onebots 0.0.13 → 0.0.15

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 (46) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +63 -63
  3. package/lib/bin.d.ts +2 -2
  4. package/lib/bin.js +5 -5
  5. package/lib/db.d.ts +19 -19
  6. package/lib/db.js +91 -91
  7. package/lib/index.d.ts +5 -5
  8. package/lib/index.js +21 -21
  9. package/lib/onebot.d.ts +44 -44
  10. package/lib/onebot.js +86 -86
  11. package/lib/server/app.d.ts +52 -52
  12. package/lib/server/app.js +232 -232
  13. package/lib/server/router.d.ts +9 -9
  14. package/lib/server/router.js +32 -32
  15. package/lib/service/V11/action/common.d.ts +65 -66
  16. package/lib/service/V11/action/common.js +142 -142
  17. package/lib/service/V11/action/friend.d.ts +38 -38
  18. package/lib/service/V11/action/friend.js +44 -44
  19. package/lib/service/V11/action/group.d.ts +104 -104
  20. package/lib/service/V11/action/group.js +138 -138
  21. package/lib/service/V11/action/index.d.ts +9 -9
  22. package/lib/service/V11/action/index.js +10 -10
  23. package/lib/service/V11/config.d.ts +10 -10
  24. package/lib/service/V11/config.js +2 -2
  25. package/lib/service/V11/index.d.ts +95 -95
  26. package/lib/service/V11/index.js +499 -488
  27. package/lib/service/V12/action/common.d.ts +33 -33
  28. package/lib/service/V12/action/common.js +108 -108
  29. package/lib/service/V12/action/friend.d.ts +13 -13
  30. package/lib/service/V12/action/friend.js +15 -15
  31. package/lib/service/V12/action/group.d.ts +104 -104
  32. package/lib/service/V12/action/group.js +138 -138
  33. package/lib/service/V12/action/guild.d.ts +2 -2
  34. package/lib/service/V12/action/guild.js +6 -6
  35. package/lib/service/V12/action/index.d.ts +10 -10
  36. package/lib/service/V12/action/index.js +11 -11
  37. package/lib/service/V12/config.d.ts +17 -17
  38. package/lib/service/V12/config.js +2 -2
  39. package/lib/service/V12/index.d.ts +102 -102
  40. package/lib/service/V12/index.js +538 -537
  41. package/lib/types.d.ts +3 -3
  42. package/lib/types.js +2 -2
  43. package/lib/utils.d.ts +14 -14
  44. package/lib/utils.js +150 -150
  45. package/package.json +58 -58
  46. package/lib/config.sample.yaml +0 -31
@@ -1,537 +1,538 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.V12 = void 0;
7
- const oicq_1 = require("oicq");
8
- const path_1 = require("path");
9
- const onebot_1 = require("../../onebot");
10
- const action_1 = require("./action");
11
- const events_1 = require("events");
12
- const url_1 = require("url");
13
- const http_1 = __importDefault(require("http"));
14
- const https_1 = __importDefault(require("https"));
15
- const ws_1 = require("ws");
16
- const oicq2_cq_enable_1 = require("oicq2-cq-enable");
17
- const utils_1 = require("../../utils");
18
- const db_1 = require("../../db");
19
- const app_1 = require("../../server/app");
20
- const fs_1 = require("fs");
21
- class V12 extends events_1.EventEmitter {
22
- constructor(oneBot, client, config) {
23
- super();
24
- this.oneBot = oneBot;
25
- this.client = client;
26
- this.config = config;
27
- this.version = 'V12';
28
- this.timestamp = Date.now();
29
- this.wsr = new Set();
30
- this.db = new db_1.Db((0, path_1.join)(app_1.App.configDir, 'data', this.client.uin + '.json'));
31
- if (!this.history)
32
- this.db.set('eventBuffer', []);
33
- this.action = new action_1.Action();
34
- this.logger = this.oneBot.app.getLogger(this.client.uin, this.version);
35
- }
36
- get history() {
37
- return this.db.get('eventBuffer');
38
- }
39
- start(path) {
40
- this.path = `/${this.client.uin}`;
41
- if (path)
42
- this.path += path;
43
- if (this.config.use_http) {
44
- const config = typeof this.config.use_http === 'boolean' ? {} : this.config.use_http || {};
45
- this.startHttp({
46
- access_token: this.config.access_token,
47
- event_enabled: true,
48
- event_buffer_size: 50,
49
- ...config
50
- });
51
- }
52
- if (this.config.use_ws) {
53
- const config = typeof this.config.use_ws === 'boolean' ? {} : this.config.use_ws || {};
54
- this.startWs({
55
- access_token: this.config.access_token,
56
- ...config
57
- });
58
- }
59
- this.config.webhook.forEach(config => {
60
- if (typeof config === 'string') {
61
- config = {
62
- url: config,
63
- access_token: this.config.access_token,
64
- };
65
- }
66
- else {
67
- config = {
68
- access_token: this.config.access_token,
69
- ...config
70
- };
71
- }
72
- this.startWebhook(config);
73
- });
74
- this.config.ws_reverse.forEach(config => {
75
- if (typeof config === 'string') {
76
- config = {
77
- url: config,
78
- access_token: this.config.access_token,
79
- };
80
- }
81
- else {
82
- config = {
83
- access_token: this.config.access_token,
84
- ...config
85
- };
86
- }
87
- this.startWsReverse(config);
88
- });
89
- if (this.config.heartbeat) {
90
- this.heartbeat = setInterval(() => {
91
- this.dispatch({
92
- self_id: this.client.uin,
93
- time: Math.floor(Date.now() / 1000),
94
- type: "meta",
95
- detail_type: "heartbeat",
96
- interval: this.config.heartbeat * 1000,
97
- status: this.action.getStatus.apply(this)
98
- });
99
- }, this.config.heartbeat * 1000);
100
- }
101
- }
102
- startHttp(config) {
103
- this.oneBot.app.router.all(new RegExp(`^${this.path}/(.*)$`), (ctx) => this._httpRequestHandler(ctx, config));
104
- this.logger.mark(`开启http服务器成功,监听:http://127.0.0.1:${this.oneBot.app.config.port}${this.path}`);
105
- this.on('dispatch', (payload) => {
106
- if (!['message', 'notice', 'request', 'system'].includes(payload.type))
107
- return;
108
- if (config.event_enabled) {
109
- this.history.push(payload);
110
- if (config.event_buffer_size !== 0 && this.history.length > config.event_buffer_size)
111
- this.history.shift();
112
- }
113
- });
114
- }
115
- startWebhook(config) {
116
- this.on('dispatch', (unserialized) => {
117
- const serialized = JSON.stringify(unserialized);
118
- const options = {
119
- method: "POST",
120
- timeout: this.config.request_timeout * 1000,
121
- headers: {
122
- "Content-Type": "application/json",
123
- "Content-Length": Buffer.byteLength(serialized),
124
- "X-Self-ID": String(this.client.uin),
125
- "User-Agent": "OneBot",
126
- },
127
- };
128
- const protocol = config.url.startsWith("https") ? https_1.default : http_1.default;
129
- try {
130
- protocol.request(config.url, options, (res) => {
131
- if (res.statusCode !== 200)
132
- return this.logger.warn(`POST(${config.url})上报事件收到非200响应:` + res.statusCode);
133
- let data = "";
134
- res.setEncoding("utf-8");
135
- res.on("data", (chunk) => data += chunk);
136
- res.on("end", () => {
137
- this.logger.debug(`收到HTTP响应 ${res.statusCode} :` + data);
138
- if (!data)
139
- return;
140
- try {
141
- this._quickOperate(unserialized, JSON.parse(data));
142
- }
143
- catch (e) {
144
- this.logger.error(`快速操作遇到错误:` + e.message);
145
- }
146
- });
147
- }).on("error", (err) => {
148
- this.logger.error(`POST(${config.url})上报事件失败:` + err.message);
149
- }).end(serialized, () => {
150
- this.logger.debug(`POST(${config.url})上报事件成功: ` + serialized);
151
- });
152
- }
153
- catch (e) {
154
- this.logger.error(`POST(${config.url})上报失败:` + e.message);
155
- }
156
- });
157
- }
158
- startWs(config) {
159
- this.wss = this.oneBot.app.router.ws(this.path, this.oneBot.app.httpServer);
160
- this.logger.mark(`开启ws服务器成功,监听:ws://127.0.0.1:${this.oneBot.app.config.port}${this.path}`);
161
- this.wss.on("error", (err) => {
162
- this.logger.error(err.message);
163
- });
164
- this.wss.on("connection", (ws, req) => {
165
- this.logger.info(`ws客户端(${req.url})已连接`);
166
- ws.on("error", (err) => {
167
- this.logger.error(`ws客户端(${req.url})报错:${err.message}`);
168
- });
169
- ws.on("close", (code, reason) => {
170
- this.logger.warn(`ws客户端(${req.url})连接关闭,关闭码${code},关闭理由:` + reason);
171
- });
172
- if (config.access_token) {
173
- const url = new url_1.URL(req.url, "http://127.0.0.1");
174
- const token = url.searchParams.get('access_token');
175
- if (token)
176
- req.headers["authorization"] = `Bearer ${token}`;
177
- if (!req.headers["authorization"] || req.headers["authorization"] !== `Bearer ${config.access_token}`)
178
- return ws.close(1002, "wrong access token");
179
- }
180
- this._webSocketHandler(ws);
181
- });
182
- this.on('dispatch', (unserialized) => {
183
- const serialized = JSON.stringify(unserialized);
184
- for (const ws of this.wss.clients) {
185
- ws.send(serialized, (err) => {
186
- if (err)
187
- this.logger.error(`正向WS(${ws.url})上报事件失败: ` + err.message);
188
- else
189
- this.logger.debug(`正向WS(${ws.url})上报事件成功: ` + serialized);
190
- });
191
- }
192
- });
193
- }
194
- startWsReverse(config) {
195
- const ws = this._createWsr(config.url, config);
196
- this.on('dispatch', (unserialized) => {
197
- if (this.wsr.has(ws)) {
198
- ws.send(JSON.stringify(unserialized));
199
- }
200
- });
201
- }
202
- async stop(force) {
203
- if (this.client.status === oicq_1.OnlineStatus.Online) {
204
- await this.client.terminate();
205
- }
206
- if (force) {
207
- (0, fs_1.rmSync)(this.client.dir, { force: true, recursive: true });
208
- }
209
- }
210
- dispatch(data = {}) {
211
- if (!data)
212
- data = {};
213
- if (!data.post_type) {
214
- // @ts-ignore
215
- data.sub_type = 'online';
216
- if (data.image) {
217
- // @ts-ignore
218
- data.system_type = 'login';
219
- // @ts-ignore
220
- data.sub_type = 'qrcode';
221
- }
222
- else if (data.url) {
223
- // @ts-ignore
224
- data.system_type = 'login';
225
- // @ts-ignore
226
- data.sub_type = 'slider';
227
- if (data.phone) {
228
- // @ts-ignore
229
- data.sub_type = 'device';
230
- }
231
- }
232
- else if (data.message) {
233
- // @ts-ignore
234
- data.system_type = 'login';
235
- // @ts-ignore
236
- data.sub_type = 'error';
237
- }
238
- }
239
- const payload = {
240
- id: (0, utils_1.uuid)(),
241
- impl: 'oicq_onebot',
242
- platform: 'qq',
243
- self_id: `${this.client.uin}`,
244
- type: data.post_type || 'meta',
245
- detail_type: data.message_type || data.notice_type || data.request_type || data.system_type,
246
- ...data,
247
- };
248
- if (payload.type === 'notice') {
249
- switch (payload.detail_type) {
250
- case 'friend':
251
- payload.detail_type += payload.sub_type;
252
- break;
253
- case 'group':
254
- if (['increase', 'decrease'].includes(payload.sub_type))
255
- payload.detail_type = 'group_member_' + payload.sub_type;
256
- else if (payload.sub_type === 'recall')
257
- payload.detail_type = 'group_message_delete';
258
- }
259
- }
260
- this.emit('dispatch', payload);
261
- }
262
- async apply(req) {
263
- let { action, params, echo } = req;
264
- action = (0, utils_1.toLine)(action);
265
- let is_async = action.includes("_async");
266
- if (is_async)
267
- action = action.replace("_async", "");
268
- if (action === 'send_msg') {
269
- if (["private", "group", "discuss", 'channel'].includes(params.detail_type)) {
270
- action = "send_" + params.detail_type + "_msg";
271
- }
272
- else if (params.user_id)
273
- action = "send_private_msg";
274
- else if (params.group_id)
275
- action = "send_group_msg";
276
- else if (params.discuss_id)
277
- action = "send_discuss_msg";
278
- else if (params.channel_id && params.guild_id)
279
- action = "send_guild_msg";
280
- else
281
- throw new Error('required detail_type or input (user_id/group_id/(guild_id and channel_id))');
282
- }
283
- const method = (0, utils_1.toHump)(action);
284
- if (Reflect.has(this.action, method)) {
285
- const ARGS = String(Reflect.get(this.action, method)).match(/\(.*\)/)?.[0]
286
- .replace("(", "")
287
- .replace(")", "")
288
- .split(",")
289
- .filter(Boolean).map(v => v.replace(/=.+/, "").trim());
290
- const args = [];
291
- for (let k of ARGS) {
292
- if (Reflect.has(params, k)) {
293
- if (onebot_1.BOOLS.includes(k))
294
- params[k] = (0, utils_1.toBool)(params[k]);
295
- if (k === 'message') {
296
- if (typeof params[k] === 'string') {
297
- params[k] = (0, oicq2_cq_enable_1.fromCqcode)(params[k]);
298
- }
299
- else {
300
- params[k] = (0, oicq2_cq_enable_1.fromSegment)(params[k]);
301
- }
302
- }
303
- args.push(params[k]);
304
- }
305
- }
306
- let ret, result;
307
- try {
308
- ret = this.action[method].apply(this, args);
309
- }
310
- catch (e) {
311
- return JSON.stringify(V12.error(e.message));
312
- }
313
- if (ret instanceof Promise) {
314
- if (is_async) {
315
- result = V12.success(null, 0);
316
- }
317
- else {
318
- result = V12.success(await ret, 0);
319
- }
320
- }
321
- else {
322
- result = V12.success(await ret, 0);
323
- }
324
- if (result.data instanceof Map)
325
- result.data = [...result.data.values()];
326
- if (echo) {
327
- result.echo = echo;
328
- }
329
- return JSON.stringify(result);
330
- }
331
- else
332
- throw new onebot_1.NotFoundError();
333
- }
334
- async _httpRequestHandler(ctx, config) {
335
- if (ctx.method === 'OPTIONS') {
336
- return ctx.writeHead(200, {
337
- 'Access-Control-Allow-Origin': '*',
338
- 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
339
- 'Access-Control-Allow-Headers': 'Content-Type, authorization'
340
- }).end();
341
- }
342
- const url = new url_1.URL(ctx.url, `http://127.0.0.1`);
343
- if (config.access_token) {
344
- if (ctx.headers["authorization"]) {
345
- if (ctx.headers["authorization"] !== `Bearer ${config.access_token}`)
346
- return ctx.res.writeHead(403).end();
347
- }
348
- else {
349
- const access_token = url.searchParams.get("access_token");
350
- if (!access_token)
351
- return ctx.res.writeHead(401).end();
352
- else if (access_token !== this.config.access_token)
353
- return ctx.res.writeHead(403).end();
354
- }
355
- }
356
- ctx.res.setHeader("Content-Type", "application/json; charset=utf-8");
357
- if (this.config.enable_cors)
358
- ctx.res.setHeader("Access-Control-Allow-Origin", "*");
359
- const action = url.pathname.replace(`${this.path}`, '').slice(1);
360
- if (ctx.method === "GET") {
361
- try {
362
- const ret = await this.apply({ action, params: ctx.query });
363
- ctx.res.writeHead(200).end(ret);
364
- }
365
- catch (e) {
366
- ctx.res.writeHead(500).end(e.message);
367
- }
368
- }
369
- else if (ctx.method === "POST") {
370
- try {
371
- const params = { ...ctx.query, ...ctx.request.body };
372
- const ret = await this.apply({ action, params });
373
- ctx.res.writeHead(200).end(ret);
374
- }
375
- catch (e) {
376
- ctx.res.writeHead(500).end(e.message);
377
- }
378
- }
379
- else {
380
- ctx.res.writeHead(405).end();
381
- }
382
- }
383
- /**
384
- * 快速操作
385
- */
386
- _quickOperate(event, res) {
387
- if (event.type === "message") {
388
- if (res.reply) {
389
- if (event.detail_type === "discuss")
390
- return;
391
- const action = event.detail_type === "private" ? "sendPrivateMsg" : "sendGroupMsg";
392
- const id = event.detail_type === "private" ? event.user_id : event.group_id;
393
- this.client[action](id, res.reply, res.auto_escape);
394
- }
395
- if (event.detail_type === "group") {
396
- if (res.delete)
397
- this.client.deleteMsg(event.message_id);
398
- if (res.kick && !event.anonymous)
399
- this.client.setGroupKick(event.group_id, event.user_id, res.reject_add_request);
400
- if (res.ban)
401
- this.client.setGroupBan(event.group_id, event.user_id, res.ban_duration > 0 ? res.ban_duration : 1800);
402
- }
403
- }
404
- if (event.type === "request" && "approve" in res) {
405
- const action = event.detail_type === "friend" ? "setFriendAddRequest" : "setGroupAddRequest";
406
- this.client[action](event.flag, res.approve, res.reason ? res.reason : "", !!res.block);
407
- }
408
- }
409
- /**
410
- * 创建反向ws
411
- */
412
- _createWsr(url, config) {
413
- const timestmap = Date.now();
414
- const headers = {
415
- "X-Self-ID": String(this.client.uin),
416
- "X-Client-Role": "Universal",
417
- "User-Agent": "OneBot",
418
- };
419
- if (config.access_token)
420
- headers.Authorization = "Bearer " + config.access_token;
421
- const ws = new ws_1.WebSocket(url, { headers });
422
- ws.on("error", (err) => {
423
- this.logger.error(err.message);
424
- });
425
- ws.on("open", () => {
426
- this.logger.info(`反向ws(${url})连接成功。`);
427
- this.wsr.add(ws);
428
- this._webSocketHandler(ws);
429
- });
430
- ws.on("close", (code) => {
431
- this.wsr.delete(ws);
432
- if (timestmap < this.timestamp)
433
- return;
434
- this.logger.warn(`反向ws(${url})被关闭,关闭码${code},将在${this.config.reconnect_interval}秒后尝试重连。`);
435
- setTimeout(() => {
436
- if (timestmap < this.timestamp)
437
- return;
438
- this._createWsr(url, config);
439
- }, this.config.reconnect_interval * 1000);
440
- });
441
- return ws;
442
- }
443
- /**
444
- * 处理ws消息
445
- */
446
- _webSocketHandler(ws) {
447
- ws.on("message", async (msg) => {
448
- this.logger.debug(" 收到ws消息:" + msg);
449
- var data;
450
- try {
451
- data = JSON.parse(String(msg));
452
- let ret;
453
- if (data.action.startsWith(".handle_quick_operation")) {
454
- const event = data.params.context, res = data.params.operation;
455
- this._quickOperate(event, res);
456
- ret = JSON.stringify({
457
- retcode: 0,
458
- status: "ok",
459
- data: null,
460
- message: null,
461
- echo: data.echo
462
- });
463
- }
464
- else {
465
- ret = await this.apply(data);
466
- }
467
- ws.send(ret);
468
- }
469
- catch (e) {
470
- let code, message;
471
- if (e instanceof onebot_1.NotFoundError) {
472
- code = 10002;
473
- message = "不支持的api";
474
- }
475
- else {
476
- code = 10003;
477
- message = "请求格式错误";
478
- }
479
- ws.send(JSON.stringify({
480
- retcode: code,
481
- status: "failed",
482
- data: null,
483
- error: {
484
- code, message
485
- },
486
- echo: data?.echo
487
- }));
488
- }
489
- });
490
- this.dispatch(V12.genMetaEvent(this.client.uin, "connect"));
491
- this.dispatch(V12.genMetaEvent(this.client.uin, "enable"));
492
- }
493
- }
494
- exports.V12 = V12;
495
- (function (V12) {
496
- V12.defaultConfig = {
497
- heartbeat: 3,
498
- access_token: '',
499
- request_timeout: 15,
500
- reconnect_interval: 3,
501
- enable_cors: true,
502
- use_http: true,
503
- use_ws: true,
504
- webhook: [],
505
- ws_reverse: []
506
- };
507
- function success(data, retcode = 0, echo) {
508
- return {
509
- retcode,
510
- status: retcode === 0 ? 'ok' : 'failed',
511
- data,
512
- message: '',
513
- echo
514
- };
515
- }
516
- V12.success = success;
517
- function error(message, retcode = 10001, echo) {
518
- return {
519
- retcode,
520
- status: 'failed',
521
- data: null,
522
- message,
523
- echo
524
- };
525
- }
526
- V12.error = error;
527
- function genMetaEvent(uin, type) {
528
- return {
529
- self_id: uin,
530
- time: Math.floor(Date.now() / 1000),
531
- type: "meta",
532
- detail_type: "lifecycle",
533
- sub_type: type,
534
- };
535
- }
536
- V12.genMetaEvent = genMetaEvent;
537
- })(V12 = exports.V12 || (exports.V12 = {}));
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.V12 = void 0;
7
+ const oicq_1 = require("oicq");
8
+ const path_1 = require("path");
9
+ const onebot_1 = require("../../onebot");
10
+ const action_1 = require("./action");
11
+ const events_1 = require("events");
12
+ const url_1 = require("url");
13
+ const http_1 = __importDefault(require("http"));
14
+ const https_1 = __importDefault(require("https"));
15
+ const ws_1 = require("ws");
16
+ const oicq2_cq_enable_1 = require("oicq2-cq-enable");
17
+ const utils_1 = require("../../utils");
18
+ const db_1 = require("../../db");
19
+ const app_1 = require("../../server/app");
20
+ const fs_1 = require("fs");
21
+ class V12 extends events_1.EventEmitter {
22
+ constructor(oneBot, client, config) {
23
+ super();
24
+ this.oneBot = oneBot;
25
+ this.client = client;
26
+ this.config = config;
27
+ this.version = 'V12';
28
+ this.timestamp = Date.now();
29
+ this.wsr = new Set();
30
+ this.db = new db_1.Db((0, path_1.join)(app_1.App.configDir, 'data', this.client.uin + '.json'));
31
+ if (!this.history)
32
+ this.db.set('eventBuffer', []);
33
+ this.action = new action_1.Action();
34
+ this.logger = this.oneBot.app.getLogger(this.client.uin, this.version);
35
+ }
36
+ get history() {
37
+ return this.db.get('eventBuffer');
38
+ }
39
+ start(path) {
40
+ this.path = `/${this.client.uin}`;
41
+ if (path)
42
+ this.path += path;
43
+ if (this.config.use_http) {
44
+ const config = typeof this.config.use_http === 'boolean' ? {} : this.config.use_http || {};
45
+ this.startHttp({
46
+ access_token: this.config.access_token,
47
+ event_enabled: true,
48
+ event_buffer_size: 50,
49
+ ...config
50
+ });
51
+ }
52
+ if (this.config.use_ws) {
53
+ const config = typeof this.config.use_ws === 'boolean' ? {} : this.config.use_ws || {};
54
+ this.startWs({
55
+ access_token: this.config.access_token,
56
+ ...config
57
+ });
58
+ }
59
+ this.config.webhook.forEach(config => {
60
+ if (typeof config === 'string') {
61
+ config = {
62
+ url: config,
63
+ access_token: this.config.access_token,
64
+ };
65
+ }
66
+ else {
67
+ config = {
68
+ access_token: this.config.access_token,
69
+ ...config
70
+ };
71
+ }
72
+ this.startWebhook(config);
73
+ });
74
+ this.config.ws_reverse.forEach(config => {
75
+ if (typeof config === 'string') {
76
+ config = {
77
+ url: config,
78
+ access_token: this.config.access_token,
79
+ };
80
+ }
81
+ else {
82
+ config = {
83
+ access_token: this.config.access_token,
84
+ ...config
85
+ };
86
+ }
87
+ this.startWsReverse(config);
88
+ });
89
+ if (this.config.heartbeat) {
90
+ this.heartbeat = setInterval(() => {
91
+ this.dispatch({
92
+ self_id: this.client.uin,
93
+ time: Math.floor(Date.now() / 1000),
94
+ type: "meta",
95
+ detail_type: "heartbeat",
96
+ interval: this.config.heartbeat * 1000,
97
+ status: this.action.getStatus.apply(this)
98
+ });
99
+ }, this.config.heartbeat * 1000);
100
+ }
101
+ }
102
+ startHttp(config) {
103
+ this.oneBot.app.router.all(new RegExp(`^${this.path}/(.*)$`), (ctx) => this._httpRequestHandler(ctx, config));
104
+ this.logger.mark(`开启http服务器成功,监听:http://127.0.0.1:${this.oneBot.app.config.port}${this.path}`);
105
+ this.on('dispatch', (payload) => {
106
+ if (!['message', 'notice', 'request', 'system'].includes(payload.type))
107
+ return;
108
+ if (config.event_enabled) {
109
+ this.history.push(payload);
110
+ if (config.event_buffer_size !== 0 && this.history.length > config.event_buffer_size)
111
+ this.history.shift();
112
+ }
113
+ });
114
+ }
115
+ startWebhook(config) {
116
+ this.on('dispatch', (unserialized) => {
117
+ const serialized = JSON.stringify(unserialized);
118
+ const options = {
119
+ method: "POST",
120
+ timeout: this.config.request_timeout * 1000,
121
+ headers: {
122
+ "Content-Type": "application/json",
123
+ "Content-Length": Buffer.byteLength(serialized),
124
+ "X-Self-ID": String(this.client.uin),
125
+ "User-Agent": "OneBot",
126
+ },
127
+ };
128
+ const protocol = config.url.startsWith("https") ? https_1.default : http_1.default;
129
+ try {
130
+ protocol.request(config.url, options, (res) => {
131
+ if (res.statusCode !== 200)
132
+ return this.logger.warn(`POST(${config.url})上报事件收到非200响应:` + res.statusCode);
133
+ let data = "";
134
+ res.setEncoding("utf-8");
135
+ res.on("data", (chunk) => data += chunk);
136
+ res.on("end", () => {
137
+ this.logger.debug(`收到HTTP响应 ${res.statusCode} :` + data);
138
+ if (!data)
139
+ return;
140
+ try {
141
+ this._quickOperate(unserialized, JSON.parse(data));
142
+ }
143
+ catch (e) {
144
+ this.logger.error(`快速操作遇到错误:` + e.message);
145
+ }
146
+ });
147
+ }).on("error", (err) => {
148
+ this.logger.error(`POST(${config.url})上报事件失败:` + err.message);
149
+ }).end(serialized, () => {
150
+ this.logger.debug(`POST(${config.url})上报事件成功: ` + serialized);
151
+ });
152
+ }
153
+ catch (e) {
154
+ this.logger.error(`POST(${config.url})上报失败:` + e.message);
155
+ }
156
+ });
157
+ }
158
+ startWs(config) {
159
+ this.wss = this.oneBot.app.router.ws(this.path, this.oneBot.app.httpServer);
160
+ this.logger.mark(`开启ws服务器成功,监听:ws://127.0.0.1:${this.oneBot.app.config.port}${this.path}`);
161
+ this.wss.on("error", (err) => {
162
+ this.logger.error(err.message);
163
+ });
164
+ this.wss.on("connection", (ws, req) => {
165
+ this.logger.info(`ws客户端(${req.url})已连接`);
166
+ ws.on("error", (err) => {
167
+ this.logger.error(`ws客户端(${req.url})报错:${err.message}`);
168
+ });
169
+ ws.on("close", (code, reason) => {
170
+ this.logger.warn(`ws客户端(${req.url})连接关闭,关闭码${code},关闭理由:` + reason);
171
+ });
172
+ if (config.access_token) {
173
+ const url = new url_1.URL(req.url, "http://127.0.0.1");
174
+ const token = url.searchParams.get('access_token');
175
+ if (token)
176
+ req.headers["authorization"] = `Bearer ${token}`;
177
+ if (!req.headers["authorization"] || req.headers["authorization"] !== `Bearer ${config.access_token}`)
178
+ return ws.close(1002, "wrong access token");
179
+ }
180
+ this._webSocketHandler(ws);
181
+ });
182
+ this.on('dispatch', (unserialized) => {
183
+ const serialized = JSON.stringify(unserialized);
184
+ for (const ws of this.wss.clients) {
185
+ ws.send(serialized, (err) => {
186
+ if (err)
187
+ this.logger.error(`正向WS(${ws.url})上报事件失败: ` + err.message);
188
+ else
189
+ this.logger.debug(`正向WS(${ws.url})上报事件成功: ` + serialized);
190
+ });
191
+ }
192
+ });
193
+ }
194
+ startWsReverse(config) {
195
+ const ws = this._createWsr(config.url, config);
196
+ this.on('dispatch', (unserialized) => {
197
+ if (this.wsr.has(ws)) {
198
+ ws.send(JSON.stringify(unserialized));
199
+ }
200
+ });
201
+ }
202
+ async stop(force) {
203
+ if (this.client.status === oicq_1.OnlineStatus.Online) {
204
+ await this.client.terminate();
205
+ }
206
+ if (force) {
207
+ (0, fs_1.rmSync)(this.client.dir, { force: true, recursive: true });
208
+ }
209
+ }
210
+ dispatch(data = {}) {
211
+ if (!data)
212
+ data = {};
213
+ if (!data.post_type) {
214
+ // @ts-ignore
215
+ data.sub_type = 'online';
216
+ if (data.image) {
217
+ // @ts-ignore
218
+ data.system_type = 'login';
219
+ // @ts-ignore
220
+ data.sub_type = 'qrcode';
221
+ }
222
+ else if (data.url) {
223
+ // @ts-ignore
224
+ data.system_type = 'login';
225
+ // @ts-ignore
226
+ data.sub_type = 'slider';
227
+ if (data.phone) {
228
+ // @ts-ignore
229
+ data.sub_type = 'device';
230
+ }
231
+ }
232
+ else if (data.message) {
233
+ // @ts-ignore
234
+ data.system_type = 'login';
235
+ // @ts-ignore
236
+ data.sub_type = 'error';
237
+ }
238
+ }
239
+ const payload = {
240
+ id: (0, utils_1.uuid)(),
241
+ impl: 'oicq_onebot',
242
+ platform: 'qq',
243
+ self_id: `${this.client.uin}`,
244
+ type: data.post_type || 'meta',
245
+ detail_type: data.message_type || data.notice_type || data.request_type || data.system_type,
246
+ ...data,
247
+ };
248
+ if (payload.type === 'notice') {
249
+ switch (payload.detail_type) {
250
+ case 'friend':
251
+ payload.detail_type += payload.sub_type;
252
+ break;
253
+ case 'group':
254
+ if (['increase', 'decrease'].includes(payload.sub_type))
255
+ payload.detail_type = 'group_member_' + payload.sub_type;
256
+ else if (payload.sub_type === 'recall')
257
+ payload.detail_type = 'group_message_delete';
258
+ }
259
+ }
260
+ this.emit('dispatch', payload);
261
+ }
262
+ async apply(req) {
263
+ let { action, params, echo } = req;
264
+ action = (0, utils_1.toLine)(action);
265
+ let is_async = action.includes("_async");
266
+ if (is_async)
267
+ action = action.replace("_async", "");
268
+ if (action === 'send_msg') {
269
+ if (["private", "group", "discuss", 'channel'].includes(params.detail_type)) {
270
+ action = "send_" + params.detail_type + "_msg";
271
+ }
272
+ else if (params.user_id)
273
+ action = "send_private_msg";
274
+ else if (params.group_id)
275
+ action = "send_group_msg";
276
+ else if (params.discuss_id)
277
+ action = "send_discuss_msg";
278
+ else if (params.channel_id && params.guild_id)
279
+ action = "send_guild_msg";
280
+ else
281
+ throw new Error('required detail_type or input (user_id/group_id/(guild_id and channel_id))');
282
+ }
283
+ const method = (0, utils_1.toHump)(action);
284
+ if (Reflect.has(this.action, method)) {
285
+ const ARGS = String(Reflect.get(this.action, method)).match(/\(.*\)/)?.[0]
286
+ .replace("(", "")
287
+ .replace(")", "")
288
+ .split(",")
289
+ .filter(Boolean).map(v => v.replace(/=.+/, "").trim());
290
+ const args = [];
291
+ for (let k of ARGS) {
292
+ if (Reflect.has(params, k)) {
293
+ if (onebot_1.BOOLS.includes(k))
294
+ params[k] = (0, utils_1.toBool)(params[k]);
295
+ if (k === 'message') {
296
+ if (typeof params[k] === 'string') {
297
+ params[k] = (0, oicq2_cq_enable_1.fromCqcode)(params[k]);
298
+ }
299
+ else {
300
+ params[k] = (0, oicq2_cq_enable_1.fromSegment)(params[k]);
301
+ }
302
+ }
303
+ args.push(params[k]);
304
+ }
305
+ }
306
+ let ret, result;
307
+ try {
308
+ ret = this.action[method].apply(this, args);
309
+ }
310
+ catch (e) {
311
+ return JSON.stringify(V12.error(e.message));
312
+ }
313
+ if (ret instanceof Promise) {
314
+ if (is_async) {
315
+ result = V12.success(null, 0);
316
+ }
317
+ else {
318
+ result = V12.success(await ret, 0);
319
+ }
320
+ }
321
+ else {
322
+ result = V12.success(await ret, 0);
323
+ }
324
+ if (result.data instanceof Map)
325
+ result.data = [...result.data.values()];
326
+ if (echo) {
327
+ result.echo = echo;
328
+ }
329
+ return JSON.stringify(result);
330
+ }
331
+ else
332
+ throw new onebot_1.NotFoundError();
333
+ }
334
+ async _httpRequestHandler(ctx, config) {
335
+ if (ctx.method === 'OPTIONS') {
336
+ return ctx.writeHead(200, {
337
+ 'Access-Control-Allow-Origin': '*',
338
+ 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
339
+ 'Access-Control-Allow-Headers': 'Content-Type, authorization'
340
+ }).end();
341
+ }
342
+ const url = new url_1.URL(ctx.url, `http://127.0.0.1`);
343
+ if (config.access_token) {
344
+ if (ctx.headers["authorization"]) {
345
+ if (ctx.headers["authorization"] !== `Bearer ${config.access_token}`)
346
+ return ctx.res.writeHead(403).end();
347
+ }
348
+ else {
349
+ const access_token = url.searchParams.get("access_token");
350
+ if (!access_token)
351
+ return ctx.res.writeHead(401).end();
352
+ else if (access_token !== this.config.access_token)
353
+ return ctx.res.writeHead(403).end();
354
+ }
355
+ }
356
+ ctx.res.setHeader("Content-Type", "application/json; charset=utf-8");
357
+ if (this.config.enable_cors)
358
+ ctx.res.setHeader("Access-Control-Allow-Origin", "*");
359
+ const action = url.pathname.replace(`${this.path}`, '').slice(1);
360
+ if (ctx.method === "GET") {
361
+ try {
362
+ const ret = await this.apply({ action, params: ctx.query });
363
+ ctx.res.writeHead(200).end(ret);
364
+ }
365
+ catch (e) {
366
+ ctx.res.writeHead(500).end(e.message);
367
+ }
368
+ }
369
+ else if (ctx.method === "POST") {
370
+ try {
371
+ const params = { ...(ctx.request.query || {}), ...(ctx.request.body || {}) };
372
+ const ret = await this.apply({ action, params });
373
+ ctx.res.writeHead(200).end(ret);
374
+ }
375
+ catch (e) {
376
+ ctx.res.writeHead(500).end(e.message);
377
+ }
378
+ }
379
+ else {
380
+ ctx.res.writeHead(405).end();
381
+ }
382
+ }
383
+ /**
384
+ * 快速操作
385
+ */
386
+ _quickOperate(event, res) {
387
+ if (event.type === "message") {
388
+ if (res.reply) {
389
+ if (event.detail_type === "discuss")
390
+ return;
391
+ const action = event.detail_type === "private" ? "sendPrivateMsg" : "sendGroupMsg";
392
+ const id = event.detail_type === "private" ? event.user_id : event.group_id;
393
+ this.client[action](id, res.reply, res.auto_escape);
394
+ }
395
+ if (event.detail_type === "group") {
396
+ if (res.delete)
397
+ this.client.deleteMsg(event.message_id);
398
+ if (res.kick && !event.anonymous)
399
+ this.client.setGroupKick(event.group_id, event.user_id, res.reject_add_request);
400
+ if (res.ban)
401
+ this.client.setGroupBan(event.group_id, event.user_id, res.ban_duration > 0 ? res.ban_duration : 1800);
402
+ }
403
+ }
404
+ if (event.type === "request" && "approve" in res) {
405
+ const action = event.detail_type === "friend" ? "setFriendAddRequest" : "setGroupAddRequest";
406
+ this.client[action](event.flag, res.approve, res.reason ? res.reason : "", !!res.block);
407
+ }
408
+ }
409
+ /**
410
+ * 创建反向ws
411
+ */
412
+ _createWsr(url, config) {
413
+ const timestmap = Date.now();
414
+ const headers = {
415
+ "X-Self-ID": String(this.client.uin),
416
+ "X-Client-Role": "Universal",
417
+ "User-Agent": "OneBot/12 (qq) node-Onebots/0.0.15",
418
+ "Sec-WebSocket-Protocol": "12.onebots.v0.0.15"
419
+ };
420
+ if (config.access_token)
421
+ headers.Authorization = "Bearer " + config.access_token;
422
+ const ws = new ws_1.WebSocket(url, { headers });
423
+ ws.on("error", (err) => {
424
+ this.logger.error(err.message);
425
+ });
426
+ ws.on("open", () => {
427
+ this.logger.info(`反向ws(${url})连接成功。`);
428
+ this.wsr.add(ws);
429
+ this._webSocketHandler(ws);
430
+ });
431
+ ws.on("close", (code) => {
432
+ this.wsr.delete(ws);
433
+ if (timestmap < this.timestamp)
434
+ return;
435
+ this.logger.warn(`反向ws(${url})被关闭,关闭码${code},将在${this.config.reconnect_interval}秒后尝试重连。`);
436
+ setTimeout(() => {
437
+ if (timestmap < this.timestamp)
438
+ return;
439
+ this._createWsr(url, config);
440
+ }, this.config.reconnect_interval * 1000);
441
+ });
442
+ return ws;
443
+ }
444
+ /**
445
+ * 处理ws消息
446
+ */
447
+ _webSocketHandler(ws) {
448
+ ws.on("message", async (msg) => {
449
+ this.logger.debug(" 收到ws消息:" + msg);
450
+ var data;
451
+ try {
452
+ data = JSON.parse(String(msg));
453
+ let ret;
454
+ if (data.action.startsWith(".handle_quick_operation")) {
455
+ const event = data.params.context, res = data.params.operation;
456
+ this._quickOperate(event, res);
457
+ ret = JSON.stringify({
458
+ retcode: 0,
459
+ status: "success",
460
+ data: null,
461
+ message: null,
462
+ echo: data.echo
463
+ });
464
+ }
465
+ else {
466
+ ret = await this.apply(data);
467
+ }
468
+ ws.send(ret);
469
+ }
470
+ catch (e) {
471
+ let code, message;
472
+ if (e instanceof onebot_1.NotFoundError) {
473
+ code = 10002;
474
+ message = "不支持的api";
475
+ }
476
+ else {
477
+ code = 10003;
478
+ message = "请求格式错误";
479
+ }
480
+ ws.send(JSON.stringify({
481
+ retcode: code,
482
+ status: "failed",
483
+ data: null,
484
+ error: {
485
+ code, message
486
+ },
487
+ echo: data?.echo
488
+ }));
489
+ }
490
+ });
491
+ this.dispatch(V12.genMetaEvent(this.client.uin, "connect"));
492
+ this.dispatch(V12.genMetaEvent(this.client.uin, "enable"));
493
+ }
494
+ }
495
+ exports.V12 = V12;
496
+ (function (V12) {
497
+ V12.defaultConfig = {
498
+ heartbeat: 3,
499
+ access_token: '',
500
+ request_timeout: 15,
501
+ reconnect_interval: 3,
502
+ enable_cors: true,
503
+ use_http: true,
504
+ use_ws: true,
505
+ webhook: [],
506
+ ws_reverse: []
507
+ };
508
+ function success(data, retcode = 0, echo) {
509
+ return {
510
+ retcode,
511
+ status: retcode === 0 ? 'success' : 'failed',
512
+ data,
513
+ message: '',
514
+ echo
515
+ };
516
+ }
517
+ V12.success = success;
518
+ function error(message, retcode = 10001, echo) {
519
+ return {
520
+ retcode,
521
+ status: 'failed',
522
+ data: null,
523
+ message,
524
+ echo
525
+ };
526
+ }
527
+ V12.error = error;
528
+ function genMetaEvent(uin, type) {
529
+ return {
530
+ self_id: uin,
531
+ time: Math.floor(Date.now() / 1000),
532
+ type: "meta",
533
+ detail_type: "lifecycle",
534
+ sub_type: type,
535
+ };
536
+ }
537
+ V12.genMetaEvent = genMetaEvent;
538
+ })(V12 = exports.V12 || (exports.V12 = {}));