16
src/api.ts
16
src/api.ts
@@ -114,3 +114,19 @@ export async function sendChannelMessage(
|
|||||||
...(msgId ? { msg_id: msgId } : {}),
|
...(msgId ? { msg_id: msgId } : {}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送群聊消息
|
||||||
|
*/
|
||||||
|
export async function sendGroupMessage(
|
||||||
|
accessToken: string,
|
||||||
|
groupOpenid: string,
|
||||||
|
content: string,
|
||||||
|
msgId?: string
|
||||||
|
): Promise<{ id: string; timestamp: string }> {
|
||||||
|
return apiRequest(accessToken, "POST", `/v2/groups/${groupOpenid}/messages`, {
|
||||||
|
content,
|
||||||
|
msg_type: 0,
|
||||||
|
...(msgId ? { msg_id: msgId } : {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
import type { ResolvedQQBotAccount, WSPayload, C2CMessageEvent, GuildMessageEvent } from "./types.js";
|
import type { ResolvedQQBotAccount, WSPayload, C2CMessageEvent, GuildMessageEvent, GroupMessageEvent } from "./types.js";
|
||||||
import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage } from "./api.js";
|
import { getAccessToken, getGatewayUrl, sendC2CMessage, sendChannelMessage, sendGroupMessage } from "./api.js";
|
||||||
import { getQQBotRuntime } from "./runtime.js";
|
import { getQQBotRuntime } from "./runtime.js";
|
||||||
|
|
||||||
// QQ Bot intents
|
// QQ Bot intents
|
||||||
const INTENTS = {
|
const INTENTS = {
|
||||||
PUBLIC_GUILD_MESSAGES: 1 << 30,
|
PUBLIC_GUILD_MESSAGES: 1 << 30, // 频道公开消息
|
||||||
DIRECT_MESSAGE: 1 << 25,
|
DIRECT_MESSAGE: 1 << 12, // 频道私信
|
||||||
|
GROUP_AND_C2C: 1 << 25, // 群聊和 C2C 私聊
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface GatewayContext {
|
export interface GatewayContext {
|
||||||
@@ -56,7 +57,7 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|||||||
|
|
||||||
// 处理收到的消息
|
// 处理收到的消息
|
||||||
const handleMessage = async (event: {
|
const handleMessage = async (event: {
|
||||||
type: "c2c" | "guild" | "dm";
|
type: "c2c" | "guild" | "dm" | "group";
|
||||||
senderId: string;
|
senderId: string;
|
||||||
senderName?: string;
|
senderName?: string;
|
||||||
content: string;
|
content: string;
|
||||||
@@ -64,6 +65,7 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|||||||
timestamp: string;
|
timestamp: string;
|
||||||
channelId?: string;
|
channelId?: string;
|
||||||
guildId?: string;
|
guildId?: string;
|
||||||
|
groupOpenid?: string;
|
||||||
}) => {
|
}) => {
|
||||||
log?.info(`[qqbot:${account.accountId}] Processing message from ${event.senderId}: ${event.content}`);
|
log?.info(`[qqbot:${account.accountId}] Processing message from ${event.senderId}: ${event.content}`);
|
||||||
|
|
||||||
@@ -73,8 +75,10 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|||||||
direction: "inbound",
|
direction: "inbound",
|
||||||
});
|
});
|
||||||
|
|
||||||
const isGroup = event.type === "guild";
|
const isGroup = event.type === "guild" || event.type === "group";
|
||||||
const peerId = isGroup ? `channel:${event.channelId}` : event.senderId;
|
const peerId = event.type === "guild" ? `channel:${event.channelId}`
|
||||||
|
: event.type === "group" ? `group:${event.groupOpenid}`
|
||||||
|
: event.senderId;
|
||||||
|
|
||||||
const route = pluginRuntime.channel.routing.resolveAgentRoute({
|
const route = pluginRuntime.channel.routing.resolveAgentRoute({
|
||||||
cfg,
|
cfg,
|
||||||
@@ -101,8 +105,8 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|||||||
envelope: envelopeOptions,
|
envelope: envelopeOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fromAddress = isGroup
|
const fromAddress = event.type === "guild" ? `qqbot:channel:${event.channelId}`
|
||||||
? `qqbot:channel:${event.channelId}`
|
: event.type === "group" ? `qqbot:group:${event.groupOpenid}`
|
||||||
: `qqbot:${event.senderId}`;
|
: `qqbot:${event.senderId}`;
|
||||||
const toAddress = fromAddress;
|
const toAddress = fromAddress;
|
||||||
|
|
||||||
@@ -126,6 +130,7 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|||||||
// QQBot 特有字段
|
// QQBot 特有字段
|
||||||
QQChannelId: event.channelId,
|
QQChannelId: event.channelId,
|
||||||
QQGuildId: event.guildId,
|
QQGuildId: event.guildId,
|
||||||
|
QQGroupOpenid: event.groupOpenid,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 分发到 AI 系统
|
// 分发到 AI 系统
|
||||||
@@ -144,6 +149,8 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
if (event.type === "c2c") {
|
if (event.type === "c2c") {
|
||||||
await sendC2CMessage(accessToken, event.senderId, replyText, event.messageId);
|
await sendC2CMessage(accessToken, event.senderId, replyText, event.messageId);
|
||||||
|
} else if (event.type === "group" && event.groupOpenid) {
|
||||||
|
await sendGroupMessage(accessToken, event.groupOpenid, replyText, event.messageId);
|
||||||
} else if (event.channelId) {
|
} else if (event.channelId) {
|
||||||
await sendChannelMessage(accessToken, event.channelId, replyText, event.messageId);
|
await sendChannelMessage(accessToken, event.channelId, replyText, event.messageId);
|
||||||
}
|
}
|
||||||
@@ -191,7 +198,7 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|||||||
op: 2,
|
op: 2,
|
||||||
d: {
|
d: {
|
||||||
token: `QQBot ${accessToken}`,
|
token: `QQBot ${accessToken}`,
|
||||||
intents: INTENTS.PUBLIC_GUILD_MESSAGES | INTENTS.DIRECT_MESSAGE,
|
intents: INTENTS.PUBLIC_GUILD_MESSAGES | INTENTS.DIRECT_MESSAGE | INTENTS.GROUP_AND_C2C,
|
||||||
shard: [0, 1],
|
shard: [0, 1],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -239,6 +246,16 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|||||||
timestamp: event.timestamp,
|
timestamp: event.timestamp,
|
||||||
guildId: event.guild_id,
|
guildId: event.guild_id,
|
||||||
});
|
});
|
||||||
|
} else if (t === "GROUP_AT_MESSAGE_CREATE") {
|
||||||
|
const event = d as GroupMessageEvent;
|
||||||
|
await handleMessage({
|
||||||
|
type: "group",
|
||||||
|
senderId: event.author.member_openid,
|
||||||
|
content: event.content,
|
||||||
|
messageId: event.id,
|
||||||
|
timestamp: event.timestamp,
|
||||||
|
groupOpenid: event.group_openid,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
15
src/types.ts
15
src/types.ts
@@ -70,6 +70,21 @@ export interface GuildMessageEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群聊 AT 消息事件
|
||||||
|
*/
|
||||||
|
export interface GroupMessageEvent {
|
||||||
|
author: {
|
||||||
|
id: string;
|
||||||
|
member_openid: string;
|
||||||
|
};
|
||||||
|
content: string;
|
||||||
|
id: string;
|
||||||
|
timestamp: string;
|
||||||
|
group_id: string;
|
||||||
|
group_openid: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSocket 事件负载
|
* WebSocket 事件负载
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user