@zhin.js/adapter-qq 1.1.17 → 1.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/README.md +75 -21
- package/adapters/qq/index.js +4 -33
- package/adapters/qq/index.ts +3 -41
- package/agents/qq/boundaries.md +2 -0
- package/agents/qq/conversation.js +92 -0
- package/agents/qq/conversation.ts +99 -0
- package/agents/qq/messages.js +60 -0
- package/agents/qq/messages.ts +60 -0
- package/agents/qq/skills/qq/SKILL.md +8 -2
- package/agents/qq/skills/qq/tools/add_role/index.js +2 -1
- package/agents/qq/skills/qq/tools/add_role/index.ts +2 -1
- package/agents/qq/skills/qq/tools/channel_info/index.js +1 -1
- package/agents/qq/skills/qq/tools/channel_info/index.ts +1 -1
- package/agents/qq/skills/qq/tools/create_role/index.js +1 -1
- package/agents/qq/skills/qq/tools/create_role/index.ts +1 -1
- package/agents/qq/skills/qq/tools/guild_kick_member/index.js +30 -0
- package/agents/qq/skills/qq/tools/guild_kick_member/index.ts +29 -0
- package/agents/qq/skills/qq/tools/guild_mute_member/index.js +31 -0
- package/agents/qq/skills/qq/tools/guild_mute_member/index.ts +30 -0
- package/agents/qq/skills/qq/tools/list_channels/index.js +1 -1
- package/agents/qq/skills/qq/tools/list_channels/index.ts +1 -1
- package/agents/qq/skills/qq/tools/list_roles/index.js +1 -1
- package/agents/qq/skills/qq/tools/list_roles/index.ts +1 -1
- package/agents/qq/skills/qq/tools/member_detail/index.js +1 -1
- package/agents/qq/skills/qq/tools/member_detail/index.ts +1 -1
- package/agents/qq/skills/qq/tools/remove_role/index.js +2 -1
- package/agents/qq/skills/qq/tools/remove_role/index.ts +2 -1
- package/agents/qq/skills/qq-conversation/SKILL.md +24 -0
- package/agents/qq/skills/qq-conversation/tools/conversation_info/index.js +78 -0
- package/agents/qq/skills/qq-conversation/tools/conversation_info/index.ts +76 -0
- package/agents/qq/skills/qq-directory/tools/list_guilds/index.js +1 -1
- package/agents/qq/skills/qq-directory/tools/list_guilds/index.ts +1 -1
- package/agents/qq/skills/qq-group-admin/SKILL.md +34 -0
- package/agents/qq/skills/qq-group-admin/tools/group_kick/index.js +39 -0
- package/agents/qq/skills/qq-group-admin/tools/group_kick/index.ts +38 -0
- package/agents/qq/skills/qq-group-admin/tools/handle_join_request/index.js +33 -0
- package/agents/qq/skills/qq-group-admin/tools/handle_join_request/index.ts +32 -0
- package/agents/qq/skills/qq-group-admin/tools/recall_member_message/index.js +25 -0
- package/agents/qq/skills/qq-group-admin/tools/recall_member_message/index.ts +25 -0
- package/agents/qq/skills/qq-group-admin/tools/set_mute/index.js +37 -0
- package/agents/qq/skills/qq-group-admin/tools/set_mute/index.ts +36 -0
- package/agents/qq/skills/qq-group-directory/SKILL.md +24 -0
- package/agents/qq/skills/qq-group-directory/tools/group_info/index.js +16 -0
- package/agents/qq/skills/qq-group-directory/tools/group_info/index.ts +16 -0
- package/agents/qq/skills/qq-group-directory/tools/group_member_info/index.js +19 -0
- package/agents/qq/skills/qq-group-directory/tools/group_member_info/index.ts +19 -0
- package/agents/qq/skills/qq-group-directory/tools/group_members/index.js +19 -0
- package/agents/qq/skills/qq-group-directory/tools/group_members/index.ts +19 -0
- package/agents/qq/skills/qq-group-state/SKILL.md +21 -0
- package/agents/qq/skills/qq-group-state/tools/group_blacklist/index.js +19 -0
- package/agents/qq/skills/qq-group-state/tools/group_blacklist/index.ts +19 -0
- package/agents/qq/skills/qq-group-state/tools/list_join_requests/index.js +19 -0
- package/agents/qq/skills/qq-group-state/tools/list_join_requests/index.ts +19 -0
- package/agents/qq/skills/qq-group-state/tools/list_muted/index.js +21 -0
- package/agents/qq/skills/qq-group-state/tools/list_muted/index.ts +21 -0
- package/agents/qq/skills/qq-messages/SKILL.md +27 -0
- package/agents/qq/skills/qq-messages/tools/quote_reply/index.js +21 -0
- package/agents/qq/skills/qq-messages/tools/quote_reply/index.ts +21 -0
- package/agents/qq/skills/qq-messages/tools/recall_own_message/index.js +18 -0
- package/agents/qq/skills/qq-messages/tools/recall_own_message/index.ts +17 -0
- package/commands/qq/endpoint/definition.js +73 -55
- package/commands/qq/endpoint/definition.ts +75 -86
- package/lib/adapter.d.ts +13 -0
- package/lib/adapter.js +27 -0
- package/lib/client.d.ts +7 -9
- package/lib/client.js +1 -2
- package/lib/console.d.ts +3 -0
- package/lib/console.js +93 -0
- package/lib/endpoint.d.ts +17 -27
- package/lib/endpoint.js +325 -146
- package/lib/group-admin.d.ts +41 -0
- package/lib/group-admin.js +114 -0
- package/lib/i18n.js +4 -4
- package/lib/image-size.d.ts +6 -1
- package/lib/image-size.js +2 -3
- package/lib/index.d.ts +5 -4
- package/lib/index.js +3 -3
- package/lib/interaction.js +1 -1
- package/lib/outbound.d.ts +4 -1
- package/lib/outbound.js +3 -2
- package/lib/protocol.d.ts +7 -0
- package/lib/protocol.js +4 -0
- package/lib/qq-console-spec.d.ts +17 -0
- package/lib/qq-console-spec.js +21 -0
- package/lib/qq-console.d.ts +10 -0
- package/lib/qq-console.js +126 -0
- package/lib/qq-endpoint-write.d.ts +18 -0
- package/lib/qq-endpoint-write.js +7 -0
- package/lib/qq-runtime-state.d.ts +29 -9
- package/lib/qq-runtime-state.js +5 -5
- package/lib/qq-send.d.ts +7 -14
- package/lib/qq-send.js +20 -6
- package/lib/webhook.d.ts +3 -6
- package/lib/webhook.js +6 -49
- package/lib/ws.d.ts +10 -33
- package/lib/ws.js +21 -70
- package/middlewares/bot-kind-reply/index.js +1 -2
- package/middlewares/bot-kind-reply/index.ts +0 -2
- package/package.json +26 -18
- package/pages/index/index.js +15 -0
- package/pages/index/index.ts +16 -0
- package/plugin.js +9 -6
- package/schema.json +174 -0
- package/src/adapter.ts +23 -0
- package/src/client.ts +4 -12
- package/src/console.ts +103 -0
- package/src/endpoint.ts +328 -141
- package/src/group-admin.ts +155 -0
- package/src/i18n.ts +4 -4
- package/src/image-size.ts +7 -3
- package/src/index.ts +5 -5
- package/src/interaction.ts +1 -1
- package/src/outbound.ts +3 -2
- package/src/protocol.ts +10 -0
- package/src/qq-console-spec.ts +29 -0
- package/src/qq-console.ts +130 -0
- package/src/qq-endpoint-write.ts +24 -0
- package/src/qq-runtime-state.ts +31 -15
- package/src/qq-send.ts +28 -12
- package/src/webhook.ts +7 -57
- package/src/ws.ts +27 -105
package/src/qq-send.ts
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* 4. Active message refused (no permission, quota, user switched it off): remember the conversation
|
|
11
11
|
* for a while and fail fast instead of calling the platform again.
|
|
12
12
|
*/
|
|
13
|
-
import type
|
|
14
|
-
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
13
|
+
import { createEndpointOperation, type EndpointOperation } from 'zhin.js/adapter';
|
|
14
|
+
import { EndpointDeliveryError, type ConversationRef } from '@zhin.js/im-contract';
|
|
15
|
+
import type { Sendable } from 'qq-official-bot';
|
|
15
16
|
import type { QqOutboundElem, QqOutboundMessage } from './outbound.js';
|
|
16
17
|
import {
|
|
17
18
|
QQ_ACTIVE_UNAVAILABLE_CODES,
|
|
@@ -34,6 +35,20 @@ export interface QqSendContext {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
type Kind = ReturnType<typeof qqOutboundKind>;
|
|
38
|
+
interface QqSendExecution extends QqSendContext { readonly operation: EndpointOperation; }
|
|
39
|
+
interface QqMessageDispatch {
|
|
40
|
+
readonly kind: Kind;
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly message: QqOutboundMessage;
|
|
43
|
+
readonly source?: QqReplySource;
|
|
44
|
+
readonly options?: QqSendOptions;
|
|
45
|
+
}
|
|
46
|
+
export interface QqMessagesRequest {
|
|
47
|
+
readonly conversation: ConversationRef;
|
|
48
|
+
readonly messages: readonly QqOutboundMessage[];
|
|
49
|
+
readonly signal?: AbortSignal;
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
|
|
38
53
|
function elements(message: QqOutboundMessage): Array<string | QqOutboundElem> {
|
|
39
54
|
return Array.isArray(message) ? [...message] : [message as string | QqOutboundElem];
|
|
@@ -69,14 +84,14 @@ export function markdownAsText(message: QqOutboundMessage): QqOutboundMessage |
|
|
|
69
84
|
return text ? [...kept, text] : undefined;
|
|
70
85
|
}
|
|
71
86
|
|
|
72
|
-
function dispatch(bot: QqBotTransport, kind
|
|
87
|
+
function dispatch(bot: QqBotTransport, { kind, id, message, source, options }: QqMessageDispatch): Promise<unknown> {
|
|
73
88
|
switch (kind) {
|
|
74
|
-
case 'private': return bot.sendPrivateMessage(id, message, source, options);
|
|
75
|
-
case 'group': return bot.sendGroupMessage(id, message, source, options);
|
|
76
|
-
case 'channel': return bot.sendGuildMessage(id, message, source, options);
|
|
89
|
+
case 'private': return bot.sendPrivateMessage(id, message as Sendable, source, options);
|
|
90
|
+
case 'group': return bot.sendGroupMessage(id, message as Sendable, source, options);
|
|
91
|
+
case 'channel': return bot.sendGuildMessage(id, message as Sendable, source, options);
|
|
77
92
|
case 'direct':
|
|
78
93
|
if (!bot.sendDirectMessage) throw new Error('QQ direct message not supported by transport');
|
|
79
|
-
return bot.sendDirectMessage(id, message, source, options);
|
|
94
|
+
return bot.sendDirectMessage(id, message as Sendable, source, options);
|
|
80
95
|
}
|
|
81
96
|
}
|
|
82
97
|
|
|
@@ -85,15 +100,15 @@ function platformCode(error: unknown): number | undefined {
|
|
|
85
100
|
}
|
|
86
101
|
|
|
87
102
|
/** Sends one message, passive if possible; returns the platform result. */
|
|
88
|
-
async function sendOne(context:
|
|
103
|
+
async function sendOne(context: QqSendExecution, { kind, id, message }: QqMessageDispatch): Promise<unknown> {
|
|
89
104
|
const passive = (body: QqOutboundMessage, grant: PassiveReplyGrant) =>
|
|
90
|
-
dispatch(context.bot, kind, id, body, grant.source, { msgSeq: grant.msgSeq });
|
|
105
|
+
context.operation.run(() => dispatch(context.bot, { kind, id, message: body, source: grant.source, options: { msgSeq: grant.msgSeq } }));
|
|
91
106
|
const active = async (body: QqOutboundMessage): Promise<unknown> => {
|
|
92
107
|
if (context.activeGate.blocked(kind, id)) {
|
|
93
108
|
throw new EndpointDeliveryError('platform_rejected', 'QQ active messages are unavailable for this conversation', 'not_sent');
|
|
94
109
|
}
|
|
95
110
|
try {
|
|
96
|
-
return await dispatch(context.bot, kind, id, body);
|
|
111
|
+
return await context.operation.run(() => dispatch(context.bot, { kind, id, message: body }));
|
|
97
112
|
} catch (error) {
|
|
98
113
|
const code = platformCode(error);
|
|
99
114
|
if (code !== undefined && QQ_ACTIVE_UNAVAILABLE_CODES.has(code) && context.activeGate.block(kind, id)) {
|
|
@@ -130,9 +145,10 @@ async function sendOne(context: QqSendContext, kind: Kind, id: string, message:
|
|
|
130
145
|
}
|
|
131
146
|
|
|
132
147
|
/** Sends every message of one delivery in order; returns the last platform result. */
|
|
133
|
-
export async function sendQqMessages(context: QqSendContext, conversation
|
|
148
|
+
export async function sendQqMessages(context: QqSendContext, { conversation, messages, signal }: QqMessagesRequest): Promise<unknown> {
|
|
134
149
|
const kind = qqOutboundKind(conversation);
|
|
150
|
+
const execution = { ...context, operation: createEndpointOperation({ signal }) };
|
|
135
151
|
let result: unknown;
|
|
136
|
-
for (const message of messages) result = await sendOne(
|
|
152
|
+
for (const message of messages) result = await sendOne(execution, { kind, id: conversation.id, message });
|
|
137
153
|
return result;
|
|
138
154
|
}
|
package/src/webhook.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
6
|
-
import { Bot, ReceiverMode
|
|
6
|
+
import { Bot, ReceiverMode } from 'qq-official-bot';
|
|
7
7
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
8
8
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
9
9
|
import type { ResolvedQqHttpConfig } from './protocol.js';
|
|
@@ -11,11 +11,7 @@ import type { QqBotTransport } from './ws.js';
|
|
|
11
11
|
|
|
12
12
|
const logger = getLogger('qq');
|
|
13
13
|
|
|
14
|
-
export type QqHttpBotTransport =
|
|
15
|
-
middleware: (ctx: unknown, next: () => Promise<void>) => Promise<unknown>;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type CreateQqHttpBot = (config: ResolvedQqHttpConfig) => QqHttpBotTransport;
|
|
14
|
+
export type QqHttpBotTransport = Bot<ReceiverMode.MIDDLEWARE, 'koa'>;
|
|
19
15
|
|
|
20
16
|
export interface QqWebhookHandler {
|
|
21
17
|
readonly config: ResolvedQqHttpConfig;
|
|
@@ -53,6 +49,7 @@ export async function handleQqWebhookRequest(
|
|
|
53
49
|
req: request,
|
|
54
50
|
res: response,
|
|
55
51
|
request: { body: undefined },
|
|
52
|
+
response: {},
|
|
56
53
|
}, async () => undefined);
|
|
57
54
|
} catch (error) {
|
|
58
55
|
logger.error('QQ webhook error:', error);
|
|
@@ -78,55 +75,8 @@ export async function readRequestBodyText(request: IncomingMessage): Promise<str
|
|
|
78
75
|
return Buffer.concat(chunks).toString('utf8');
|
|
79
76
|
}
|
|
80
77
|
|
|
81
|
-
export function
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
mode: ReceiverMode.MIDDLEWARE,
|
|
86
|
-
platform: 'koa',
|
|
87
|
-
sandbox: config.sandbox,
|
|
88
|
-
dataDir: path.join(process.cwd(), 'data', 'qq'),
|
|
89
|
-
// 关闭内置 log4js,与 WebSocket 模式一致走 zhin adapter logger
|
|
90
|
-
logLevel: 'off',
|
|
91
|
-
} as never) as Bot<ReceiverMode.MIDDLEWARE, 'koa'> & QqBotTransport;
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
api: bot,
|
|
95
|
-
start: () => bot.start().then(() => undefined),
|
|
96
|
-
stop: () => bot.stop().then(() => undefined),
|
|
97
|
-
on: (event, listener) => {
|
|
98
|
-
bot.on(event as never, listener as never);
|
|
99
|
-
},
|
|
100
|
-
removeAllListeners: () => {
|
|
101
|
-
bot.removeAllListeners(undefined as never);
|
|
102
|
-
},
|
|
103
|
-
sendPrivateMessage: (userId, message, source, options) => bot.sendPrivateMessage(userId, message as Sendable, source, options),
|
|
104
|
-
sendGroupMessage: (groupId, message, source, options) => bot.sendGroupMessage(groupId, message as Sendable, source, options),
|
|
105
|
-
sendGuildMessage: (channelId, message, source, options) => bot.sendGuildMessage(channelId, message as Sendable, source, options),
|
|
106
|
-
sendDirectMessage: (guildId, message, source, options) => bot.sendDirectMessage(guildId, message as Sendable, source, options),
|
|
107
|
-
recallPrivateMessage: (userId, messageId) => bot.recallPrivateMessage(userId, messageId),
|
|
108
|
-
recallGroupMessage: (groupId, messageId) => bot.recallGroupMessage(groupId, messageId),
|
|
109
|
-
recallGuildMessage: (channelId, messageId) => bot.recallGuildMessage(channelId, messageId),
|
|
110
|
-
recallDirectMessage: (guildId, messageId) => bot.recallDirectMessage(guildId, messageId),
|
|
111
|
-
getGuilds: () => bot.guildService.getList(),
|
|
112
|
-
getChannels: (guildId) => bot.channelService.getList(guildId),
|
|
113
|
-
getChannelInfo: (channelId) => bot.channelService.getInfo(channelId),
|
|
114
|
-
getGuildMember: (guildId, userId) => bot.memberService.getGuildMemberInfo(guildId, userId),
|
|
115
|
-
getGuildRoles: (guildId) => bot.guildService.getRoles(guildId),
|
|
116
|
-
createGuildRole: (guildId, name, color) =>
|
|
117
|
-
bot.guildService.createRole(guildId, { name, color: color || 0, hoist: 0 }),
|
|
118
|
-
addMemberRole: async (guildId, channelId, userId, roleId) => {
|
|
119
|
-
await bot.memberService.addMemberRole(guildId, channelId, userId, roleId);
|
|
120
|
-
return true;
|
|
121
|
-
},
|
|
122
|
-
removeMemberRole: async (guildId, channelId, userId, roleId) => {
|
|
123
|
-
await bot.memberService.removeMemberRole(guildId, channelId, userId, roleId);
|
|
124
|
-
return true;
|
|
125
|
-
},
|
|
126
|
-
approveGroupJoinRequest: (groupId, userId, options) =>
|
|
127
|
-
(bot as { approveGroupJoinRequest?: QqBotTransport['approveGroupJoinRequest'] })
|
|
128
|
-
.approveGroupJoinRequest?.(groupId, userId, options)
|
|
129
|
-
?? Promise.reject(new Error('approveGroupJoinRequest is not available')),
|
|
130
|
-
middleware: (ctx, next) => bot.middleware(ctx as never, next as never),
|
|
131
|
-
};
|
|
78
|
+
export function qqHttpBotOptions(config: ResolvedQqHttpConfig): ConstructorParameters<typeof Bot<ReceiverMode.MIDDLEWARE, 'koa'>>[0] {
|
|
79
|
+
return { handleProcessErrors: false, appid: config.appid, secret: config.secret,
|
|
80
|
+
mode: ReceiverMode.MIDDLEWARE, platform: 'koa', sandbox: config.sandbox,
|
|
81
|
+
dataDir: path.join(process.cwd(), 'data', 'qq'), logLevel: 'off' };
|
|
132
82
|
}
|
package/src/ws.ts
CHANGED
|
@@ -42,50 +42,7 @@ export type QqOfficialApi = Pick<Bot,
|
|
|
42
42
|
| 'guild'
|
|
43
43
|
>;
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
export interface QqBotTransport {
|
|
47
|
-
readonly transportState?: EndpointTransportState;
|
|
48
|
-
/** Full qq-official-bot business API; lifecycle remains owned by the Endpoint. */
|
|
49
|
-
readonly api: QqOfficialApi;
|
|
50
|
-
start(): Promise<void>;
|
|
51
|
-
stop(): Promise<void>;
|
|
52
|
-
on(event: string, listener: (...args: unknown[]) => void): void;
|
|
53
|
-
removeAllListeners(): void;
|
|
54
|
-
/** `source` makes it a passive reply; without it the platform treats the message as active. */
|
|
55
|
-
sendPrivateMessage(userId: string, message: QqOutboundMessage, source?: QqReplySource, options?: QqSendOptions): Promise<unknown>;
|
|
56
|
-
sendGroupMessage(groupId: string, message: QqOutboundMessage, source?: QqReplySource, options?: QqSendOptions): Promise<unknown>;
|
|
57
|
-
sendGuildMessage(channelId: string, message: QqOutboundMessage, source?: QqReplySource, options?: QqSendOptions): Promise<unknown>;
|
|
58
|
-
sendDirectMessage?(guildId: string, message: QqOutboundMessage, source?: QqReplySource, options?: QqSendOptions): Promise<unknown>;
|
|
59
|
-
recallPrivateMessage?(userId: string, messageId: string): Promise<unknown>;
|
|
60
|
-
recallGroupMessage?(groupId: string, messageId: string): Promise<unknown>;
|
|
61
|
-
recallGuildMessage?(channelId: string, messageId: string): Promise<unknown>;
|
|
62
|
-
recallDirectMessage?(guildId: string, messageId: string): Promise<unknown>;
|
|
63
|
-
getGuilds(): Promise<unknown[]>;
|
|
64
|
-
getChannels(guildId: string): Promise<unknown[]>;
|
|
65
|
-
getChannelInfo(channelId: string): Promise<unknown>;
|
|
66
|
-
getGuildMember(guildId: string, userId: string): Promise<unknown>;
|
|
67
|
-
getGuildRoles(guildId: string): Promise<unknown[]>;
|
|
68
|
-
createGuildRole(guildId: string, name: string, color?: number): Promise<unknown>;
|
|
69
|
-
addMemberRole(
|
|
70
|
-
guildId: string,
|
|
71
|
-
channelId: string,
|
|
72
|
-
userId: string,
|
|
73
|
-
roleId: string,
|
|
74
|
-
): Promise<boolean>;
|
|
75
|
-
removeMemberRole(
|
|
76
|
-
guildId: string,
|
|
77
|
-
channelId: string,
|
|
78
|
-
userId: string,
|
|
79
|
-
roleId: string,
|
|
80
|
-
): Promise<boolean>;
|
|
81
|
-
approveGroupJoinRequest?(
|
|
82
|
-
groupId: string,
|
|
83
|
-
userId: string,
|
|
84
|
-
options: ApproveJoinRequestOptions,
|
|
85
|
-
): Promise<unknown>;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export type CreateQqBot = (config: ResolvedQqWebsocketConfig) => QqBotTransport;
|
|
45
|
+
export type QqBotTransport = Bot;
|
|
89
46
|
|
|
90
47
|
interface QqOfficialBotLifecycle {
|
|
91
48
|
stop(): Promise<unknown>;
|
|
@@ -134,6 +91,7 @@ export function normalizeQqMessage(raw: unknown): QqInboundMessage | null {
|
|
|
134
91
|
channel_id?: string | number;
|
|
135
92
|
guild_id?: string | number;
|
|
136
93
|
raw_message?: string;
|
|
94
|
+
timestamp?: number;
|
|
137
95
|
message?: unknown;
|
|
138
96
|
sender?: { user_id?: string | number; user_name?: string; roles?: (string | number)[] };
|
|
139
97
|
member?: { roles?: (string | number)[] };
|
|
@@ -186,6 +144,8 @@ export function normalizeQqMessage(raw: unknown): QqInboundMessage | null {
|
|
|
186
144
|
|
|
187
145
|
return {
|
|
188
146
|
id: String(msg.message_id),
|
|
147
|
+
event: raw,
|
|
148
|
+
...(typeof msg.timestamp === 'number' && Number.isFinite(msg.timestamp) ? { platformSentAt: msg.timestamp * 1_000 } : {}),
|
|
189
149
|
content,
|
|
190
150
|
channelKind,
|
|
191
151
|
channelId,
|
|
@@ -286,78 +246,40 @@ function extractTextContent(message: unknown): string {
|
|
|
286
246
|
.join('');
|
|
287
247
|
}
|
|
288
248
|
|
|
289
|
-
export function
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
secret: config.secret,
|
|
294
|
-
mode: ReceiverMode.WEBSOCKET,
|
|
295
|
-
sandbox: config.sandbox,
|
|
296
|
-
dataDir: path.join(process.cwd(), 'data', 'qq'),
|
|
297
|
-
// 关闭 qq-official-bot 内置 log4js,入/出站由 getAdapterLogger('qq', …) 统一格式
|
|
298
|
-
logLevel: 'off',
|
|
249
|
+
export function qqWebsocketBotOptions(config: ResolvedQqWebsocketConfig): ConstructorParameters<typeof Bot<ReceiverMode.WEBSOCKET>>[0] {
|
|
250
|
+
return { handleProcessErrors: false, appid: config.appid, secret: config.secret,
|
|
251
|
+
mode: ReceiverMode.WEBSOCKET, sandbox: config.sandbox,
|
|
252
|
+
dataDir: path.join(process.cwd(), 'data', 'qq'), logLevel: 'off',
|
|
299
253
|
...(config.intents ? { intents: config.intents as never } : {}),
|
|
300
254
|
...(config.accessTokenUrl ? { accessTokenUrl: config.accessTokenUrl } : {}),
|
|
301
255
|
...(config.gatewayUrl ? { gatewayUrl: config.gatewayUrl } : {}),
|
|
302
|
-
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
303
258
|
|
|
304
|
-
|
|
259
|
+
/** SDK 1.3.3 has no public receiver configuration/state accessor. This seam only reads its verified native fields. */
|
|
260
|
+
export function configureQqWebsocketReceiver(bot: Bot<ReceiverMode.WEBSOCKET>, config: ResolvedQqWebsocketConfig) {
|
|
261
|
+
const receiver = bot.receiver as unknown as {
|
|
262
|
+
config: { autoReconnect: boolean; agent?: import('node:https').Agent };
|
|
263
|
+
isReady?: boolean; isStarted?: boolean; isClosed?: boolean; handler?: { ws?: { readyState: number } };
|
|
264
|
+
};
|
|
305
265
|
receiver.config.autoReconnect = false;
|
|
306
266
|
const agent = config.streamProxy ? createQqStreamAgent(config.streamProxy) : undefined;
|
|
307
267
|
receiver.config.agent = agent;
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
const receiverError = () => {};
|
|
311
|
-
receiver.on('error', receiverError);
|
|
268
|
+
const observeError = () => undefined;
|
|
269
|
+
bot.receiver.on('error', observeError);
|
|
312
270
|
return {
|
|
313
|
-
|
|
314
|
-
const receiver = bot.receiver as unknown as { isReady?: boolean; isStarted?: boolean; isClosed?: boolean; handler?: { ws?: { readyState: number } } };
|
|
271
|
+
state(): EndpointTransportState {
|
|
315
272
|
const state = receiver.handler?.ws?.readyState;
|
|
316
273
|
if (state === 1 && receiver.isReady && receiver.isClosed === false) return 'open';
|
|
317
274
|
if (state === 0) return 'connecting';
|
|
318
|
-
|
|
319
|
-
return 'closed';
|
|
320
|
-
},
|
|
321
|
-
api: bot,
|
|
322
|
-
start: () => bot.start().then(() => undefined),
|
|
323
|
-
stop: async () => {
|
|
324
|
-
try { await stopQqOfficialBot(bot as unknown as QqOfficialBotLifecycle); }
|
|
325
|
-
finally { agent?.destroy(); }
|
|
275
|
+
return receiver.isStarted ? 'reconnecting' : 'closed';
|
|
326
276
|
},
|
|
327
|
-
|
|
328
|
-
if (event === 'transport.close') { receiver.on('close', listener); receiverListeners.push({ event: 'close', listener }); }
|
|
329
|
-
else bot.on(event as never, listener as never);
|
|
330
|
-
},
|
|
331
|
-
removeAllListeners: () => {
|
|
332
|
-
for (const entry of receiverListeners.splice(0)) receiver.removeListener(entry.event, entry.listener);
|
|
333
|
-
bot.removeAllListeners(undefined as never);
|
|
334
|
-
},
|
|
335
|
-
sendPrivateMessage: (userId, message, source, options) => bot.sendPrivateMessage(userId, message as Sendable, source, options),
|
|
336
|
-
sendGroupMessage: (groupId, message, source, options) => bot.sendGroupMessage(groupId, message as Sendable, source, options),
|
|
337
|
-
sendGuildMessage: (channelId, message, source, options) => bot.sendGuildMessage(channelId, message as Sendable, source, options),
|
|
338
|
-
sendDirectMessage: (guildId, message, source, options) => bot.sendDirectMessage(guildId, message as Sendable, source, options),
|
|
339
|
-
recallPrivateMessage: (userId, messageId) => bot.recallPrivateMessage(userId, messageId),
|
|
340
|
-
recallGroupMessage: (groupId, messageId) => bot.recallGroupMessage(groupId, messageId),
|
|
341
|
-
recallGuildMessage: (channelId, messageId) => bot.recallGuildMessage(channelId, messageId),
|
|
342
|
-
recallDirectMessage: (guildId, messageId) => bot.recallDirectMessage(guildId, messageId),
|
|
343
|
-
getGuilds: () => bot.guildService.getList(),
|
|
344
|
-
getChannels: (guildId) => bot.channelService.getList(guildId),
|
|
345
|
-
getChannelInfo: (channelId) => bot.channelService.getInfo(channelId),
|
|
346
|
-
getGuildMember: (guildId, userId) => bot.memberService.getGuildMemberInfo(guildId, userId),
|
|
347
|
-
getGuildRoles: (guildId) => bot.guildService.getRoles(guildId),
|
|
348
|
-
createGuildRole: (guildId, name, color) =>
|
|
349
|
-
bot.guildService.createRole(guildId, { name, color: color || 0, hoist: 0 }),
|
|
350
|
-
addMemberRole: async (guildId, channelId, userId, roleId) => {
|
|
351
|
-
await bot.memberService.addMemberRole(guildId, channelId, userId, roleId);
|
|
352
|
-
return true;
|
|
353
|
-
},
|
|
354
|
-
removeMemberRole: async (guildId, channelId, userId, roleId) => {
|
|
355
|
-
await bot.memberService.removeMemberRole(guildId, channelId, userId, roleId);
|
|
356
|
-
return true;
|
|
357
|
-
},
|
|
358
|
-
approveGroupJoinRequest: (groupId, userId, options) =>
|
|
359
|
-
(bot as { approveGroupJoinRequest?: QqBotTransport['approveGroupJoinRequest'] })
|
|
360
|
-
.approveGroupJoinRequest?.(groupId, userId, options)
|
|
361
|
-
?? Promise.reject(new Error('approveGroupJoinRequest is not available')),
|
|
277
|
+
dispose(): void { bot.receiver.off('error', observeError); agent?.destroy(); },
|
|
362
278
|
};
|
|
363
279
|
}
|
|
280
|
+
|
|
281
|
+
/** Session.stop leaves Auth refresh alive for reconnect; actual instance retirement destroys it. */
|
|
282
|
+
export function disposeQqAuthorization(bot: Bot): void {
|
|
283
|
+
const session = bot.sessionManager as unknown as { authManager: { destroy(): void } };
|
|
284
|
+
session.authManager.destroy();
|
|
285
|
+
}
|