mozi-bot 1.1.2 → 1.2.1

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.
Files changed (53) hide show
  1. package/README.md +255 -35
  2. package/dist/channels/dingtalk/index.d.ts.map +1 -1
  3. package/dist/channels/dingtalk/index.js +10 -0
  4. package/dist/channels/dingtalk/index.js.map +1 -1
  5. package/dist/channels/dingtalk/stream.d.ts.map +1 -1
  6. package/dist/channels/dingtalk/stream.js +2 -0
  7. package/dist/channels/dingtalk/stream.js.map +1 -1
  8. package/dist/channels/index.d.ts +4 -0
  9. package/dist/channels/index.d.ts.map +1 -1
  10. package/dist/channels/index.js +4 -0
  11. package/dist/channels/index.js.map +1 -1
  12. package/dist/channels/qq/api.d.ts +37 -0
  13. package/dist/channels/qq/api.d.ts.map +1 -0
  14. package/dist/channels/qq/api.js +145 -0
  15. package/dist/channels/qq/api.js.map +1 -0
  16. package/dist/channels/qq/index.d.ts +36 -0
  17. package/dist/channels/qq/index.d.ts.map +1 -0
  18. package/dist/channels/qq/index.js +145 -0
  19. package/dist/channels/qq/index.js.map +1 -0
  20. package/dist/channels/qq/websocket.d.ts +67 -0
  21. package/dist/channels/qq/websocket.d.ts.map +1 -0
  22. package/dist/channels/qq/websocket.js +372 -0
  23. package/dist/channels/qq/websocket.js.map +1 -0
  24. package/dist/channels/wecom/api.d.ts +30 -0
  25. package/dist/channels/wecom/api.d.ts.map +1 -0
  26. package/dist/channels/wecom/api.js +98 -0
  27. package/dist/channels/wecom/api.js.map +1 -0
  28. package/dist/channels/wecom/crypto.d.ts +25 -0
  29. package/dist/channels/wecom/crypto.d.ts.map +1 -0
  30. package/dist/channels/wecom/crypto.js +74 -0
  31. package/dist/channels/wecom/crypto.js.map +1 -0
  32. package/dist/channels/wecom/events.d.ts +34 -0
  33. package/dist/channels/wecom/events.d.ts.map +1 -0
  34. package/dist/channels/wecom/events.js +127 -0
  35. package/dist/channels/wecom/events.js.map +1 -0
  36. package/dist/channels/wecom/index.d.ts +39 -0
  37. package/dist/channels/wecom/index.d.ts.map +1 -0
  38. package/dist/channels/wecom/index.js +184 -0
  39. package/dist/channels/wecom/index.js.map +1 -0
  40. package/dist/cli/index.js +21 -6
  41. package/dist/cli/index.js.map +1 -1
  42. package/dist/config/index.d.ts +94 -0
  43. package/dist/config/index.d.ts.map +1 -1
  44. package/dist/config/index.js +49 -2
  45. package/dist/config/index.js.map +1 -1
  46. package/dist/gateway/server.d.ts +2 -0
  47. package/dist/gateway/server.d.ts.map +1 -1
  48. package/dist/gateway/server.js +47 -0
  49. package/dist/gateway/server.js.map +1 -1
  50. package/dist/types/index.d.ts +20 -1
  51. package/dist/types/index.d.ts.map +1 -1
  52. package/dist/types/index.js.map +1 -1
  53. package/package.json +4 -2
@@ -0,0 +1,145 @@
1
+ /**
2
+ * QQ 机器人 API 客户端
3
+ * 基于 QQ 官方机器人 API v2
4
+ */
5
+ import axios from "axios";
6
+ import { getChildLogger } from "../../utils/logger.js";
7
+ const logger = getChildLogger("qq-api");
8
+ /** QQ API 基础地址 */
9
+ const QQ_API_BASE = "https://api.sgroup.qq.com";
10
+ const QQ_SANDBOX_API_BASE = "https://sandbox.api.sgroup.qq.com";
11
+ /** Token 获取地址 */
12
+ const QQ_TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken";
13
+ export class QQApiClient {
14
+ config;
15
+ client;
16
+ tokenCache = null;
17
+ constructor(config) {
18
+ this.config = config;
19
+ const baseURL = config.sandbox ? QQ_SANDBOX_API_BASE : QQ_API_BASE;
20
+ this.client = axios.create({
21
+ baseURL,
22
+ timeout: 30000,
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ },
26
+ });
27
+ }
28
+ /** 获取 Access Token */
29
+ async getAccessToken() {
30
+ // 检查缓存
31
+ if (this.tokenCache && this.tokenCache.expiresAt > Date.now() + 60000) {
32
+ return this.tokenCache.accessToken;
33
+ }
34
+ try {
35
+ const response = await axios.post(QQ_TOKEN_URL, {
36
+ appId: this.config.appId,
37
+ clientSecret: this.config.clientSecret,
38
+ });
39
+ const { access_token, expires_in } = response.data;
40
+ this.tokenCache = {
41
+ accessToken: access_token,
42
+ expiresAt: Date.now() + expires_in * 1000,
43
+ };
44
+ logger.debug("Access token refreshed");
45
+ return access_token;
46
+ }
47
+ catch (error) {
48
+ logger.error({ error }, "Failed to get access token");
49
+ throw error;
50
+ }
51
+ }
52
+ /** 获取带鉴权的请求头 */
53
+ async getAuthHeaders() {
54
+ const token = await this.getAccessToken();
55
+ return {
56
+ Authorization: `QQBot ${token}`,
57
+ "X-Union-Appid": this.config.appId,
58
+ };
59
+ }
60
+ /** 获取 WebSocket 网关地址 */
61
+ async getGatewayUrl() {
62
+ const headers = await this.getAuthHeaders();
63
+ // 先尝试 /gateway,再尝试 /gateway/bot
64
+ try {
65
+ const response = await this.client.get("/gateway", { headers });
66
+ return response.data.url;
67
+ }
68
+ catch (error) {
69
+ // 如果 /gateway 失败,尝试 /gateway/bot
70
+ const response = await this.client.get("/gateway/bot", { headers });
71
+ return response.data.url;
72
+ }
73
+ }
74
+ /** 发送私聊消息 */
75
+ async sendDirectMessage(openId, content, msgId) {
76
+ const headers = await this.getAuthHeaders();
77
+ const data = {
78
+ content,
79
+ msg_type: 0,
80
+ };
81
+ if (msgId) {
82
+ data.msg_id = msgId;
83
+ }
84
+ const response = await this.client.post(`/v2/users/${openId}/messages`, data, {
85
+ headers,
86
+ });
87
+ logger.debug({ openId, messageId: response.data.id }, "Direct message sent");
88
+ return response.data;
89
+ }
90
+ /** 发送群聊消息 */
91
+ async sendGroupMessage(groupOpenId, content, msgId) {
92
+ const headers = await this.getAuthHeaders();
93
+ const data = {
94
+ content,
95
+ msg_type: 0,
96
+ };
97
+ if (msgId) {
98
+ data.msg_id = msgId;
99
+ }
100
+ const response = await this.client.post(`/v2/groups/${groupOpenId}/messages`, data, {
101
+ headers,
102
+ });
103
+ logger.debug({ groupOpenId, messageId: response.data.id }, "Group message sent");
104
+ return response.data;
105
+ }
106
+ /** 发送频道消息 */
107
+ async sendChannelMessage(channelId, content, msgId) {
108
+ const headers = await this.getAuthHeaders();
109
+ const data = {
110
+ content,
111
+ msg_type: 0,
112
+ };
113
+ if (msgId) {
114
+ data.msg_id = msgId;
115
+ }
116
+ const response = await this.client.post(`/channels/${channelId}/messages`, data, {
117
+ headers,
118
+ });
119
+ logger.debug({ channelId, messageId: response.data.id }, "Channel message sent");
120
+ return response.data;
121
+ }
122
+ /** 发送频道私信 */
123
+ async sendDMMessage(guildId, content, msgId) {
124
+ const headers = await this.getAuthHeaders();
125
+ const data = {
126
+ content,
127
+ msg_type: 0,
128
+ };
129
+ if (msgId) {
130
+ data.msg_id = msgId;
131
+ }
132
+ const response = await this.client.post(`/dms/${guildId}/messages`, data, {
133
+ headers,
134
+ });
135
+ logger.debug({ guildId, messageId: response.data.id }, "DM message sent");
136
+ return response.data;
137
+ }
138
+ /** 获取用户信息 */
139
+ async getUserInfo(openId) {
140
+ const headers = await this.getAuthHeaders();
141
+ const response = await this.client.get(`/users/${openId}`, { headers });
142
+ return response.data;
143
+ }
144
+ }
145
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/channels/qq/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * QQ 通道适配器
3
+ *
4
+ * 使用 WebSocket 长连接模式,无需公网部署
5
+ */
6
+ import type { QQConfig, ChannelMeta, OutboundMessage, SendResult } from "../../types/index.js";
7
+ import { BaseChannelAdapter } from "../common/base.js";
8
+ import { QQApiClient } from "./api.js";
9
+ export declare class QQChannel extends BaseChannelAdapter {
10
+ readonly id: "qq";
11
+ readonly meta: ChannelMeta;
12
+ private config;
13
+ private apiClient;
14
+ private wsClient;
15
+ private initialized;
16
+ constructor(config: QQConfig);
17
+ /** 初始化通道 */
18
+ initialize(): Promise<void>;
19
+ /** 启动 WebSocket 连接 */
20
+ private startWebSocket;
21
+ /** 关闭通道 */
22
+ shutdown(): Promise<void>;
23
+ /** 发送消息 */
24
+ sendMessage(message: OutboundMessage): Promise<SendResult>;
25
+ /** 发送文本消息 */
26
+ sendText(chatId: string, text: string, replyToId?: string): Promise<SendResult>;
27
+ /** 检查通道状态 */
28
+ isHealthy(): Promise<boolean>;
29
+ /** 获取 API 客户端 */
30
+ getApiClient(): QQApiClient;
31
+ /** 检查是否使用长连接 */
32
+ isUsingWebSocket(): boolean;
33
+ }
34
+ /** 创建 QQ 通道 */
35
+ export declare function createQQChannel(config: QQConfig): QQChannel;
36
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/qq/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,WAAW,EACX,eAAe,EACf,UAAU,EACX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAqBvC,qBAAa,SAAU,SAAQ,kBAAkB;IAC/C,QAAQ,CAAC,EAAE,EAAG,IAAI,CAAU;IAC5B,QAAQ,CAAC,IAAI,cAAW;IAExB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,QAAQ;IAO5B,YAAY;IACN,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBjC,sBAAsB;YACR,cAAc;IAa5B,WAAW;IACL,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAW/B,WAAW;IACL,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IA8BhE,aAAa;IACP,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAIrF,aAAa;IACP,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBnC,iBAAiB;IACjB,YAAY,IAAI,WAAW;IAI3B,gBAAgB;IAChB,gBAAgB,IAAI,OAAO;CAG5B;AAED,eAAe;AACf,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAE3D"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * QQ 通道适配器
3
+ *
4
+ * 使用 WebSocket 长连接模式,无需公网部署
5
+ */
6
+ import { BaseChannelAdapter } from "../common/base.js";
7
+ import { QQApiClient } from "./api.js";
8
+ import { QQWebSocketClient } from "./websocket.js";
9
+ import { getChildLogger } from "../../utils/logger.js";
10
+ /** QQ 通道元数据 */
11
+ const QQ_META = {
12
+ id: "qq",
13
+ name: "QQ",
14
+ description: "QQ 机器人 (官方 API)",
15
+ capabilities: {
16
+ chatTypes: ["direct", "group"],
17
+ supportsMedia: true,
18
+ supportsReply: true,
19
+ supportsMention: true,
20
+ supportsReaction: false,
21
+ supportsThread: false,
22
+ supportsEdit: false,
23
+ maxMessageLength: 4096,
24
+ },
25
+ };
26
+ export class QQChannel extends BaseChannelAdapter {
27
+ id = "qq";
28
+ meta = QQ_META;
29
+ config;
30
+ apiClient;
31
+ wsClient = null;
32
+ initialized = false;
33
+ constructor(config) {
34
+ super();
35
+ this.config = config;
36
+ this.apiClient = new QQApiClient(config);
37
+ this.logger = getChildLogger("qq");
38
+ }
39
+ /** 初始化通道 */
40
+ async initialize() {
41
+ if (this.initialized)
42
+ return;
43
+ this.logger.info("Initializing QQ channel");
44
+ // 验证配置
45
+ if (!this.config.appId || !this.config.clientSecret) {
46
+ throw new Error("QQ appId and clientSecret are required");
47
+ }
48
+ // 测试获取 token
49
+ try {
50
+ await this.apiClient.getAccessToken();
51
+ this.logger.info("Successfully authenticated with QQ");
52
+ }
53
+ catch (error) {
54
+ this.logger.error({ error }, "Failed to authenticate with QQ");
55
+ throw error;
56
+ }
57
+ // 启动 WebSocket 连接
58
+ await this.startWebSocket();
59
+ this.initialized = true;
60
+ }
61
+ /** 启动 WebSocket 连接 */
62
+ async startWebSocket() {
63
+ this.logger.info("Starting QQ WebSocket client");
64
+ this.wsClient = new QQWebSocketClient(this.config);
65
+ // 设置事件处理器
66
+ this.wsClient.setEventHandler(async (context) => {
67
+ await this.handleInboundMessage(context);
68
+ });
69
+ // 启动连接
70
+ await this.wsClient.start();
71
+ }
72
+ /** 关闭通道 */
73
+ async shutdown() {
74
+ this.logger.info("Shutting down QQ channel");
75
+ if (this.wsClient) {
76
+ await this.wsClient.stop();
77
+ this.wsClient = null;
78
+ }
79
+ this.initialized = false;
80
+ }
81
+ /** 发送消息 */
82
+ async sendMessage(message) {
83
+ try {
84
+ const { chatId, content, replyToId } = message;
85
+ // 根据 chatId 前缀判断消息类型
86
+ if (chatId.startsWith("group:")) {
87
+ // QQ 群消息
88
+ const groupOpenId = chatId.replace("group:", "");
89
+ await this.apiClient.sendGroupMessage(groupOpenId, content, replyToId);
90
+ }
91
+ else if (chatId.startsWith("c2c:")) {
92
+ // QQ 私聊消息
93
+ const openId = chatId.replace("c2c:", "");
94
+ await this.apiClient.sendDirectMessage(openId, content, replyToId);
95
+ }
96
+ else if (chatId.startsWith("dms:")) {
97
+ // 频道私信
98
+ const guildId = chatId.replace("dms:", "");
99
+ await this.apiClient.sendDMMessage(guildId, content, replyToId);
100
+ }
101
+ else {
102
+ // 默认作为频道消息处理
103
+ await this.apiClient.sendChannelMessage(chatId, content, replyToId);
104
+ }
105
+ return { success: true };
106
+ }
107
+ catch (error) {
108
+ const errorMessage = error instanceof Error ? error.message : String(error);
109
+ this.logger.error({ error, message }, "Failed to send message");
110
+ return { success: false, error: errorMessage };
111
+ }
112
+ }
113
+ /** 发送文本消息 */
114
+ async sendText(chatId, text, replyToId) {
115
+ return this.sendMessage({ chatId, content: text, replyToId });
116
+ }
117
+ /** 检查通道状态 */
118
+ async isHealthy() {
119
+ try {
120
+ // 检查 API 认证
121
+ await this.apiClient.getAccessToken();
122
+ // 检查 WebSocket 连接状态
123
+ if (this.wsClient) {
124
+ return this.wsClient.checkConnected();
125
+ }
126
+ return true;
127
+ }
128
+ catch {
129
+ return false;
130
+ }
131
+ }
132
+ /** 获取 API 客户端 */
133
+ getApiClient() {
134
+ return this.apiClient;
135
+ }
136
+ /** 检查是否使用长连接 */
137
+ isUsingWebSocket() {
138
+ return this.wsClient !== null;
139
+ }
140
+ }
141
+ /** 创建 QQ 通道 */
142
+ export function createQQChannel(config) {
143
+ return new QQChannel(config);
144
+ }
145
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/channels/qq/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * QQ 机器人 WebSocket 客户端
3
+ * 实现与 QQ 机器人网关的长连接
4
+ */
5
+ import type { QQConfig, InboundMessageContext } from "../../types/index.js";
6
+ /** 事件处理器类型 */
7
+ type EventHandler = (context: InboundMessageContext) => Promise<void>;
8
+ export declare class QQWebSocketClient {
9
+ private config;
10
+ private apiClient;
11
+ private ws;
12
+ private sessionId;
13
+ private sequence;
14
+ private heartbeatInterval;
15
+ private heartbeatTimer;
16
+ private reconnectAttempts;
17
+ private maxReconnectAttempts;
18
+ private eventHandler;
19
+ private isConnected;
20
+ constructor(config: QQConfig);
21
+ /** 设置事件处理器 */
22
+ setEventHandler(handler: EventHandler): void;
23
+ /** 启动连接 */
24
+ start(): Promise<void>;
25
+ /** 连接到网关 */
26
+ private connect;
27
+ /** 处理 WebSocket 消息 */
28
+ private handlePayload;
29
+ /** 处理 Hello 消息 */
30
+ private handleHello;
31
+ /** 发送鉴权请求 */
32
+ private identify;
33
+ /** 恢复连接 */
34
+ private resume;
35
+ /** 获取订阅的事件 intents */
36
+ private getIntents;
37
+ /** 处理事件分发 */
38
+ private handleDispatch;
39
+ /** 处理频道消息 */
40
+ private handleChannelMessage;
41
+ /** 处理频道私信 */
42
+ private handleDirectMessage;
43
+ /** 处理 QQ 群消息 */
44
+ private handleGroupMessage;
45
+ /** 处理 QQ 私聊消息 */
46
+ private handleC2CMessage;
47
+ /** 清理消息内容 (去除 @ 标记等) */
48
+ private cleanContent;
49
+ /** 开始心跳 */
50
+ private startHeartbeat;
51
+ /** 停止心跳 */
52
+ private stopHeartbeat;
53
+ /** 发送心跳 */
54
+ private sendHeartbeat;
55
+ /** 发送消息 */
56
+ private send;
57
+ /** 处理断开连接 */
58
+ private handleDisconnect;
59
+ /** 重新连接 */
60
+ private reconnect;
61
+ /** 停止连接 */
62
+ stop(): Promise<void>;
63
+ /** 检查是否已连接 */
64
+ checkConnected(): boolean;
65
+ }
66
+ export {};
67
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../src/channels/qq/websocket.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAgE5E,cAAc;AACd,KAAK,YAAY,GAAG,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtE,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAM;IAClC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,QAAQ;IAK5B,cAAc;IACd,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAI5C,WAAW;IACL,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,YAAY;YACE,OAAO;IA2CrB,sBAAsB;YACR,aAAa;IAgC3B,kBAAkB;YACJ,WAAW;IAezB,aAAa;YACC,QAAQ;IAsBtB,WAAW;YACG,MAAM;IAgBpB,sBAAsB;IACtB,OAAO,CAAC,UAAU;IAelB,aAAa;YACC,cAAc;IA4C5B,aAAa;YACC,oBAAoB;IAqBlC,aAAa;YACC,mBAAmB;IAkBjC,gBAAgB;YACF,kBAAkB;IAkBhC,iBAAiB;YACH,gBAAgB;IAkB9B,wBAAwB;IACxB,OAAO,CAAC,YAAY;IAKpB,WAAW;IACX,OAAO,CAAC,cAAc;IAQtB,WAAW;IACX,OAAO,CAAC,aAAa;IAOrB,WAAW;IACX,OAAO,CAAC,aAAa;IAUrB,WAAW;IACX,OAAO,CAAC,IAAI;IAMZ,aAAa;YACC,gBAAgB;IAe9B,WAAW;YACG,SAAS;IAcvB,WAAW;IACL,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,cAAc;IACd,cAAc,IAAI,OAAO;CAG1B"}