@zhin.js/adapter-icqq 2.0.5 → 2.0.7
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 +18 -0
- package/lib/adapter.d.ts +7 -2
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +78 -33
- package/lib/adapter.js.map +1 -1
- package/lib/bot.d.ts +29 -29
- package/lib/bot.d.ts.map +1 -1
- package/lib/bot.js +226 -405
- package/lib/bot.js.map +1 -1
- package/lib/commands/index.d.ts +7 -0
- package/lib/commands/index.d.ts.map +1 -0
- package/lib/commands/index.js +30 -0
- package/lib/commands/index.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +13 -297
- package/lib/index.js.map +1 -1
- package/lib/ipc-client.d.ts +60 -0
- package/lib/ipc-client.d.ts.map +1 -0
- package/lib/ipc-client.js +272 -0
- package/lib/ipc-client.js.map +1 -0
- package/lib/protocol.d.ts +174 -0
- package/lib/protocol.d.ts.map +1 -0
- package/lib/protocol.js +162 -0
- package/lib/protocol.js.map +1 -0
- package/lib/routes.d.ts +8 -0
- package/lib/routes.d.ts.map +1 -0
- package/lib/routes.js +67 -0
- package/lib/routes.js.map +1 -0
- package/lib/tools/index.d.ts +10 -0
- package/lib/tools/index.d.ts.map +1 -0
- package/lib/tools/index.js +336 -0
- package/lib/tools/index.js.map +1 -0
- package/lib/types.d.ts +45 -7
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +5 -0
- package/lib/types.js.map +1 -1
- package/package.json +11 -12
- package/plugin.yml +3 -0
- package/skills/icqq/SKILL.md +31 -64
- package/skills/icqq/references/friends.md +54 -0
- package/skills/icqq/references/general.md +145 -0
- package/skills/icqq/references/gfs.md +49 -0
- package/skills/icqq/references/groups.md +71 -0
- package/skills/icqq/references/messaging.md +66 -0
- package/skills/icqq/references/requests.md +27 -0
- package/skills/icqq/references/settings.md +38 -0
- package/src/adapter.ts +73 -35
- package/src/bot.ts +272 -443
- package/src/commands/index.ts +32 -0
- package/src/index.ts +14 -305
- package/src/ipc-client.ts +326 -0
- package/src/protocol.ts +242 -0
- package/src/routes.ts +83 -0
- package/src/tools/index.ts +407 -0
- package/src/types.ts +47 -7
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ICQQ 命令注册 — 通过 IPC 调用守护进程
|
|
3
|
+
*/
|
|
4
|
+
import { MessageCommand } from "zhin.js";
|
|
5
|
+
import type { IcqqAdapter } from "../adapter.js";
|
|
6
|
+
import { Actions } from "../protocol.js";
|
|
7
|
+
|
|
8
|
+
export function registerCommands(
|
|
9
|
+
addCommand: (cmd: MessageCommand<"icqq">) => void,
|
|
10
|
+
icqq: IcqqAdapter,
|
|
11
|
+
) {
|
|
12
|
+
addCommand(
|
|
13
|
+
new MessageCommand<"icqq">("赞我 <times:number>")
|
|
14
|
+
.permit("adapter(icqq)")
|
|
15
|
+
.action(async (message, result) => {
|
|
16
|
+
const bot = icqq.bots.get(message.$bot);
|
|
17
|
+
if (!bot) return "Bot 不在线";
|
|
18
|
+
const userId = Number(message.$sender.id);
|
|
19
|
+
// 每次最多 20 赞,分三次请求共 50 赞
|
|
20
|
+
const results = await Promise.all([
|
|
21
|
+
bot.ipc.request(Actions.FRIEND_LIKE, { user_id: userId, times: 20 }),
|
|
22
|
+
bot.ipc.request(Actions.FRIEND_LIKE, { user_id: userId, times: 20 }),
|
|
23
|
+
bot.ipc.request(Actions.FRIEND_LIKE, { user_id: userId, times: 10 }),
|
|
24
|
+
]);
|
|
25
|
+
let times = 0;
|
|
26
|
+
if (results[0].ok) times += 20;
|
|
27
|
+
if (results[1].ok) times += 20;
|
|
28
|
+
if (results[2].ok) times += 10;
|
|
29
|
+
return `给你赞好啦,你已经获得了${times}个赞`;
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
32
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* ICQQ 适配器入口:类型扩展、导出、注册
|
|
3
3
|
*/
|
|
4
4
|
import path from "path";
|
|
5
|
-
import { usePlugin, type Plugin, type
|
|
5
|
+
import { usePlugin, type Plugin, type ToolFeature } from "zhin.js";
|
|
6
6
|
import type { Router } from "@zhin.js/http";
|
|
7
|
-
import { MessageCommand } from "zhin.js";
|
|
8
|
-
import { IcqqAdapter } from "./adapter.js";
|
|
9
7
|
import type { WebServer } from "@zhin.js/console";
|
|
8
|
+
import { IcqqAdapter } from "./adapter.js";
|
|
9
|
+
import { registerCommands } from "./commands/index.js";
|
|
10
|
+
import { registerTools } from "./tools/index.js";
|
|
11
|
+
import { registerRoutes } from "./routes.js";
|
|
10
12
|
|
|
11
13
|
declare module "zhin.js" {
|
|
12
14
|
namespace Plugin {
|
|
@@ -27,6 +29,7 @@ export { IcqqAdapter } from "./adapter.js";
|
|
|
27
29
|
const plugin = usePlugin();
|
|
28
30
|
const { provide, useContext, addCommand, root } = plugin;
|
|
29
31
|
|
|
32
|
+
// ── 适配器注册 ─────────────────────────────────────────────────────
|
|
30
33
|
provide({
|
|
31
34
|
name: "icqq",
|
|
32
35
|
description: "ICQQ Adapter",
|
|
@@ -40,318 +43,24 @@ provide({
|
|
|
40
43
|
},
|
|
41
44
|
} as any);
|
|
42
45
|
|
|
46
|
+
// ── 命令注册 ───────────────────────────────────────────────────────
|
|
43
47
|
useContext("icqq", (icqq: IcqqAdapter) => {
|
|
44
|
-
addCommand
|
|
45
|
-
new MessageCommand<"icqq">("赞我 <times:number>")
|
|
46
|
-
.permit("adapter(icqq)")
|
|
47
|
-
.action(async (message, result) => {
|
|
48
|
-
const bot = icqq.bots.get(message.$bot);
|
|
49
|
-
const send1 = bot?.sendLike(Number(message.$sender.id), 20);
|
|
50
|
-
const send2 = bot?.sendLike(Number(message.$sender.id), 20);
|
|
51
|
-
const send3 = bot?.sendLike(Number(message.$sender.id), 10);
|
|
52
|
-
const [send1Result, send2Result, send3Result] = await Promise.all([send1, send2, send3]);
|
|
53
|
-
let times = 0;
|
|
54
|
-
if (send1Result) times += 20;
|
|
55
|
-
if (send2Result) times += 20;
|
|
56
|
-
if (send3Result) times += 10;
|
|
57
|
-
return `给你赞好啦,你已经获得了${times}个赞`;
|
|
58
|
-
}),
|
|
59
|
-
);
|
|
48
|
+
registerCommands(addCommand, icqq);
|
|
60
49
|
});
|
|
61
50
|
|
|
51
|
+
// ── AI 工具注册 ────────────────────────────────────────────────────
|
|
62
52
|
useContext("tool", "icqq", (toolService: ToolFeature, icqq: IcqqAdapter) => {
|
|
63
|
-
|
|
64
|
-
icqq,
|
|
65
|
-
"icqq",
|
|
66
|
-
);
|
|
67
|
-
const disposers: (() => void)[] = groupTools.map(t => toolService.addTool(t, "icqq"));
|
|
68
|
-
|
|
69
|
-
// 设置头衔
|
|
70
|
-
disposers.push(toolService.addTool({
|
|
71
|
-
name: "icqq_set_title",
|
|
72
|
-
description: "设置 QQ 群成员的专属头衔",
|
|
73
|
-
parameters: {
|
|
74
|
-
type: "object",
|
|
75
|
-
properties: {
|
|
76
|
-
bot: { type: "string", description: "Bot QQ号" },
|
|
77
|
-
group_id: { type: "number", description: "目标群号" },
|
|
78
|
-
user_id: { type: "number", description: "目标成员 QQ号" },
|
|
79
|
-
title: { type: "string", description: "头衔文字" },
|
|
80
|
-
duration: { type: "number", description: "持续时间(秒),-1永久" },
|
|
81
|
-
},
|
|
82
|
-
required: ["bot", "group_id", "user_id", "title"],
|
|
83
|
-
},
|
|
84
|
-
platforms: ["icqq"],
|
|
85
|
-
tags: ["icqq"],
|
|
86
|
-
execute: async (args: Record<string, any>) => {
|
|
87
|
-
const bot = icqq.bots.get(args.bot);
|
|
88
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
89
|
-
const success = await bot.setTitle(args.group_id, args.user_id, args.title, args.duration ?? -1);
|
|
90
|
-
return { success, message: success ? `已将 ${args.user_id} 的头衔设为 "${args.title}"` : "设置失败" };
|
|
91
|
-
},
|
|
92
|
-
}, "icqq"));
|
|
93
|
-
|
|
94
|
-
// 发送群公告
|
|
95
|
-
disposers.push(toolService.addTool({
|
|
96
|
-
name: "icqq_announce",
|
|
97
|
-
description: "发送 QQ 群公告(需要管理员权限)",
|
|
98
|
-
parameters: {
|
|
99
|
-
type: "object",
|
|
100
|
-
properties: {
|
|
101
|
-
bot: { type: "string", description: "Bot QQ号" },
|
|
102
|
-
group_id: { type: "number", description: "目标群号" },
|
|
103
|
-
content: { type: "string", description: "公告内容" },
|
|
104
|
-
},
|
|
105
|
-
required: ["bot", "group_id", "content"],
|
|
106
|
-
},
|
|
107
|
-
platforms: ["icqq"],
|
|
108
|
-
tags: ["icqq"],
|
|
109
|
-
execute: async (args: Record<string, any>) => {
|
|
110
|
-
const bot = icqq.bots.get(args.bot);
|
|
111
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
112
|
-
const success = await bot.sendAnnounce(args.group_id, args.content);
|
|
113
|
-
return { success, message: success ? "群公告已发送" : "发送失败" };
|
|
114
|
-
},
|
|
115
|
-
}, "icqq"));
|
|
116
|
-
|
|
117
|
-
// 戳一戳
|
|
118
|
-
disposers.push(toolService.addTool({
|
|
119
|
-
name: "icqq_poke",
|
|
120
|
-
description: "在 QQ 群中对某个成员执行戳一戳互动操作",
|
|
121
|
-
parameters: {
|
|
122
|
-
type: "object",
|
|
123
|
-
properties: {
|
|
124
|
-
bot: { type: "string", description: "Bot QQ号" },
|
|
125
|
-
group_id: { type: "number", description: "目标群号" },
|
|
126
|
-
user_id: { type: "number", description: "要戳的目标成员 QQ号" },
|
|
127
|
-
},
|
|
128
|
-
required: ["bot", "group_id", "user_id"],
|
|
129
|
-
},
|
|
130
|
-
platforms: ["icqq"],
|
|
131
|
-
tags: ["icqq"],
|
|
132
|
-
execute: async (args: Record<string, any>) => {
|
|
133
|
-
const bot = icqq.bots.get(args.bot);
|
|
134
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
135
|
-
const success = await bot.pokeMember(args.group_id, args.user_id);
|
|
136
|
-
return { success, message: success ? `已戳了戳 ${args.user_id}` : "戳一戳失败" };
|
|
137
|
-
},
|
|
138
|
-
}, "icqq"));
|
|
139
|
-
|
|
140
|
-
// 获取被禁言列表
|
|
141
|
-
disposers.push(toolService.addTool({
|
|
142
|
-
name: "icqq_list_muted",
|
|
143
|
-
description: "查询 QQ 群中当前被禁言的成员列表",
|
|
144
|
-
parameters: {
|
|
145
|
-
type: "object",
|
|
146
|
-
properties: {
|
|
147
|
-
bot: { type: "string", description: "Bot QQ号" },
|
|
148
|
-
group_id: { type: "number", description: "目标群号" },
|
|
149
|
-
},
|
|
150
|
-
required: ["bot", "group_id"],
|
|
151
|
-
},
|
|
152
|
-
platforms: ["icqq"],
|
|
153
|
-
tags: ["icqq"],
|
|
154
|
-
execute: async (args: Record<string, any>) => {
|
|
155
|
-
const bot = icqq.bots.get(args.bot);
|
|
156
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
157
|
-
const mutedList = await bot.getMutedMembers(args.group_id);
|
|
158
|
-
const filtered = mutedList.filter((m: any) => m !== null);
|
|
159
|
-
return { muted_members: filtered, count: filtered.length };
|
|
160
|
-
},
|
|
161
|
-
}, "icqq"));
|
|
162
|
-
|
|
163
|
-
// 给用户点赞
|
|
164
|
-
disposers.push(toolService.addTool({
|
|
165
|
-
name: "icqq_send_user_like",
|
|
166
|
-
description: "给用户点赞(竖大拇指),每人每天最多 20 次",
|
|
167
|
-
parameters: {
|
|
168
|
-
type: "object",
|
|
169
|
-
properties: {
|
|
170
|
-
bot: { type: "string", description: "Bot QQ号" },
|
|
171
|
-
user_id: { type: "number", description: "要点赞的目标用户 QQ号" },
|
|
172
|
-
times: { type: "number", description: "点赞次数(1-20),默认 1" },
|
|
173
|
-
},
|
|
174
|
-
required: ["bot", "user_id"],
|
|
175
|
-
},
|
|
176
|
-
platforms: ["icqq"],
|
|
177
|
-
tags: ["icqq"],
|
|
178
|
-
execute: async (args: Record<string, any>) => {
|
|
179
|
-
const bot = icqq.bots.get(args.bot);
|
|
180
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
181
|
-
const success = await bot.sendLike(args.user_id, Math.min(args.times ?? 1, 20));
|
|
182
|
-
return { success, message: success ? `已给 ${args.user_id} 点赞` : "发送失败" };
|
|
183
|
-
},
|
|
184
|
-
}, "icqq"));
|
|
185
|
-
|
|
186
|
-
// 设置匿名聊天
|
|
187
|
-
disposers.push(toolService.addTool({
|
|
188
|
-
name: "icqq_set_anonymous",
|
|
189
|
-
description: "开启或关闭 QQ 群的匿名聊天功能",
|
|
190
|
-
parameters: {
|
|
191
|
-
type: "object",
|
|
192
|
-
properties: {
|
|
193
|
-
bot: { type: "string", description: "Bot QQ号" },
|
|
194
|
-
group_id: { type: "number", description: "目标群号" },
|
|
195
|
-
enable: { type: "boolean", description: "true=开启,false=关闭,默认 true" },
|
|
196
|
-
},
|
|
197
|
-
required: ["bot", "group_id"],
|
|
198
|
-
},
|
|
199
|
-
platforms: ["icqq"],
|
|
200
|
-
tags: ["icqq"],
|
|
201
|
-
execute: async (args: Record<string, any>) => {
|
|
202
|
-
const bot = icqq.bots.get(args.bot);
|
|
203
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
204
|
-
const enable = args.enable ?? true;
|
|
205
|
-
const success = await bot.setAnonymous(args.group_id, enable);
|
|
206
|
-
return { success, message: success ? (enable ? "已开启匿名聊天" : "已关闭匿名聊天") : "操作失败" };
|
|
207
|
-
},
|
|
208
|
-
}, "icqq"));
|
|
209
|
-
|
|
210
|
-
// 群文件列表
|
|
211
|
-
disposers.push(toolService.addTool({
|
|
212
|
-
name: "icqq_group_files",
|
|
213
|
-
description: "获取 QQ 群的群文件列表",
|
|
214
|
-
parameters: {
|
|
215
|
-
type: "object",
|
|
216
|
-
properties: {
|
|
217
|
-
bot: { type: "string", description: "Bot 名称" },
|
|
218
|
-
group_id: { type: "number", description: "群号" },
|
|
219
|
-
},
|
|
220
|
-
required: ["bot", "group_id"],
|
|
221
|
-
},
|
|
222
|
-
platforms: ["icqq"],
|
|
223
|
-
tags: ["icqq"],
|
|
224
|
-
execute: async (args: Record<string, any>) => {
|
|
225
|
-
const bot = icqq.bots.get(args.bot);
|
|
226
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
227
|
-
const files = await bot.getGroupFiles(args.group_id);
|
|
228
|
-
if (!files?.length) return { files: [], message: "群文件为空" };
|
|
229
|
-
return {
|
|
230
|
-
files: files.slice(0, 30).map((f: any) => ({
|
|
231
|
-
name: f.name,
|
|
232
|
-
size: f.size,
|
|
233
|
-
uploader: f.uploader_uin,
|
|
234
|
-
upload_time: f.upload_time,
|
|
235
|
-
})),
|
|
236
|
-
count: files.length,
|
|
237
|
-
};
|
|
238
|
-
},
|
|
239
|
-
}, "icqq"));
|
|
240
|
-
|
|
241
|
-
// 好友列表
|
|
242
|
-
disposers.push(toolService.addTool({
|
|
243
|
-
name: "icqq_friend_list",
|
|
244
|
-
description: "获取 QQ 好友列表",
|
|
245
|
-
parameters: {
|
|
246
|
-
type: "object",
|
|
247
|
-
properties: {
|
|
248
|
-
bot: { type: "string", description: "Bot 名称" },
|
|
249
|
-
},
|
|
250
|
-
required: ["bot"],
|
|
251
|
-
},
|
|
252
|
-
platforms: ["icqq"],
|
|
253
|
-
tags: ["icqq"],
|
|
254
|
-
execute: async (args: Record<string, any>) => {
|
|
255
|
-
const bot = icqq.bots.get(args.bot);
|
|
256
|
-
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
257
|
-
const fl = bot.fl;
|
|
258
|
-
const friends = Array.from(fl.values()).map((f: any) => ({
|
|
259
|
-
user_id: f.user_id,
|
|
260
|
-
nickname: f.nickname,
|
|
261
|
-
remark: f.remark,
|
|
262
|
-
}));
|
|
263
|
-
return { friends: friends.slice(0, 50), count: fl.size };
|
|
264
|
-
},
|
|
265
|
-
}, "icqq"));
|
|
266
|
-
|
|
267
|
-
return () => disposers.forEach(d => d());
|
|
53
|
+
return registerTools(toolService, icqq, plugin.name);
|
|
268
54
|
});
|
|
269
55
|
|
|
56
|
+
// ── Web 控制台入口 ─────────────────────────────────────────────────
|
|
270
57
|
useContext("web", (web: WebServer) => {
|
|
271
|
-
|
|
58
|
+
return web.addEntry(
|
|
272
59
|
path.resolve(import.meta.dirname, "../client/index.tsx"),
|
|
273
60
|
);
|
|
274
|
-
return dispose;
|
|
275
61
|
});
|
|
276
62
|
|
|
63
|
+
// ── HTTP 路由 ──────────────────────────────────────────────────────
|
|
277
64
|
useContext("router", "icqq", async (router: Router, icqq: IcqqAdapter) => {
|
|
278
|
-
|
|
279
|
-
| {
|
|
280
|
-
listPending: () => unknown[];
|
|
281
|
-
submit: (id: string, value: string | Record<string, unknown>) => boolean;
|
|
282
|
-
cancel: (id: string, reason?: string) => boolean;
|
|
283
|
-
}
|
|
284
|
-
| undefined;
|
|
285
|
-
if (loginAssist) {
|
|
286
|
-
router.get("/api/login-assist/pending", (ctx: any) => {
|
|
287
|
-
ctx.body = loginAssist.listPending();
|
|
288
|
-
});
|
|
289
|
-
router.post("/api/login-assist/submit", async (ctx: any) => {
|
|
290
|
-
const body = ctx.request?.body as { id: string; value?: string | Record<string, unknown> };
|
|
291
|
-
if (!body?.id) {
|
|
292
|
-
ctx.status = 400;
|
|
293
|
-
ctx.body = { error: "missing id" };
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
const ok = loginAssist.submit(body.id, body.value ?? "");
|
|
297
|
-
ctx.body = { ok };
|
|
298
|
-
});
|
|
299
|
-
router.post("/api/login-assist/cancel", async (ctx: any) => {
|
|
300
|
-
const body = ctx.request?.body as { id: string; reason?: string };
|
|
301
|
-
if (!body?.id) {
|
|
302
|
-
ctx.status = 400;
|
|
303
|
-
ctx.body = { error: "missing id" };
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
const ok = loginAssist.cancel(body.id, body.reason);
|
|
307
|
-
ctx.body = { ok };
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
router.get("/api/icqq/bots", async (ctx) => {
|
|
312
|
-
try {
|
|
313
|
-
const bots = Array.from(icqq.bots.values());
|
|
314
|
-
if (bots.length === 0) {
|
|
315
|
-
ctx.body = { success: true, data: [], message: "暂无ICQQ机器人实例" };
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
const result = bots.map((bot) => {
|
|
319
|
-
try {
|
|
320
|
-
return {
|
|
321
|
-
name: bot.$config.name,
|
|
322
|
-
connected: bot.$connected || false,
|
|
323
|
-
groupCount: bot.gl?.size || 0,
|
|
324
|
-
friendCount: bot.fl?.size || 0,
|
|
325
|
-
receiveCount: bot.stat?.recv_msg_cnt || 0,
|
|
326
|
-
sendCount: bot.stat?.sent_msg_cnt || 0,
|
|
327
|
-
loginMode: bot.$config.password ? "password" : "qrcode",
|
|
328
|
-
status: bot.$connected ? "online" : "offline",
|
|
329
|
-
lastActivity: new Date().toISOString(),
|
|
330
|
-
};
|
|
331
|
-
} catch {
|
|
332
|
-
return {
|
|
333
|
-
name: bot.$config.name,
|
|
334
|
-
connected: false,
|
|
335
|
-
groupCount: 0,
|
|
336
|
-
friendCount: 0,
|
|
337
|
-
receiveCount: 0,
|
|
338
|
-
sendCount: 0,
|
|
339
|
-
loginMode: "unknown",
|
|
340
|
-
status: "error",
|
|
341
|
-
error: "数据获取失败",
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
ctx.body = { success: true, data: result, timestamp: new Date().toISOString() };
|
|
346
|
-
} catch (error) {
|
|
347
|
-
ctx.status = 500;
|
|
348
|
-
ctx.body = {
|
|
349
|
-
success: false,
|
|
350
|
-
error: "ICQQ_API_ERROR",
|
|
351
|
-
message: "获取机器人数据失败",
|
|
352
|
-
details: process.env.NODE_ENV === "development" ? (error as Error).message : undefined,
|
|
353
|
-
timestamp: new Date().toISOString(),
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
});
|
|
65
|
+
registerRoutes(router, icqq, root);
|
|
357
66
|
});
|