onebots 0.4.18 → 0.4.20

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/README.md CHANGED
@@ -83,15 +83,17 @@ general: # 通用配置,在单个配置省略时的默认值
83
83
  # ...其他配置项参考icqq的Config配置
84
84
  # 每个账号的单独配置(用于覆盖通用配置)
85
85
  123456789:
86
+ password: '' # 账号密码,未配置则扫码登陆
86
87
  version: V11 # 使用的oneBot版本
87
88
  # ...其他配置项参见上方对应oneBot版本的通用配置
88
89
  protocol:
89
90
  platform: 2
90
91
  sign_api_addr: '' #你的签名地址
91
- password: '' # 账号密码,未配置则扫码登陆
92
92
  # ...其他配置项参考icqq的Config配置
93
93
  ```
94
+
94
95
  # 配置解释
96
+
95
97
  ## Config
96
98
  | 配置项 | 类型 | 默认值 | desc |
97
99
  |:---------|:-----------------------------|:--------|:-------|
@@ -100,11 +102,12 @@ general: # 通用配置,在单个配置省略时的默认值
100
102
  | general | OneBotConfig | general | 通用配置 |
101
103
  | [number] | OneBotConfig\|OneBotConfig[] | - | 机器人配置 |
102
104
  ## OneBotConfig
103
- | 配置项 | 类型 | 默认值 | desc |
104
- |:---------|:----------|:----------|:-------|
105
- | V11 | ConfigV11 | configV11 | V11配置 |
106
- | V12 | ConfigV12 | configV12 | V12配置 |
107
- | protocol | Config | {} | icqq配置 |
105
+ | 配置项 | 类型 | 默认值 | desc |
106
+ |:---------|:----------|:----------|:-------------------|
107
+ | password | string | - | 账号密码 未填写或填写为空则扫码登陆 |
108
+ | V11 | ConfigV11 | configV11 | V11配置 |
109
+ | V12 | ConfigV12 | configV12 | V12配置 |
110
+ | protocol | Config | {} | icqq配置 |
108
111
  ## ConfigV11
109
112
  | 配置项 | 类型 | 默认值 | desc |
110
113
  |:--------------------|:---------|:-------|:-----------|
@@ -31,6 +31,8 @@ general: # 通用配置,在单个配置省略时的默认值
31
31
  # 每个账号的单独配置(用于覆盖通用配置)
32
32
  123456789:
33
33
  version: V11 # 使用的oneBot版本
34
+ password:'' # 账号密码,未配置则扫码登陆
35
+ group_whitelist: [] # 群消息派发白名单,只有数组中的群号才派发,为空则全部派发
34
36
  protocol: # 将会覆盖通用配置中的protocol
35
37
  platform: 1
36
38
  # 。。。其他配置项参见上方对应oneBot版本的通用配置
package/lib/onebot.d.ts CHANGED
@@ -22,7 +22,8 @@ export declare class OneBot<V extends OneBot.Version> extends EventEmitter {
22
22
  start(): Promise<[boolean, any]>;
23
23
  startListen(): void;
24
24
  stop(force?: boolean): Promise<void>;
25
- dispatch(event: any, data: any): void;
25
+ system_online(event: any, data: any): void;
26
+ dispatch(event: any, data: any): Promise<void>;
26
27
  }
27
28
  export declare enum OneBotStatus {
28
29
  Good = 0,
@@ -33,11 +34,14 @@ export declare namespace OneBot {
33
34
  type Version = 'V11' | 'V12';
34
35
  type Config<V extends Version = 'V11'> = ({
35
36
  version?: V;
37
+ password?: string;
38
+ group_whitelist?: number[];
36
39
  protocol?: IcqqConfig;
37
40
  } & (V extends 'V11' ? V11.Config : V12.Config));
38
41
  interface Base {
39
42
  start(path?: string): any;
40
43
  stop(): any;
44
+ system_online(...args: any[]): any;
41
45
  dispatch(...args: any[]): any;
42
46
  apply(...args: any[]): any;
43
47
  }
package/lib/onebot.js CHANGED
@@ -56,6 +56,8 @@ class OneBot extends events_1.EventEmitter {
56
56
  c.version = 'V11';
57
57
  if (!c.protocol)
58
58
  c.protocol = {};
59
+ if (c.password)
60
+ this.password = c.password;
59
61
  Object.assign(protocolConfig, c.protocol);
60
62
  switch (c.version) {
61
63
  case 'V11':
@@ -160,6 +162,7 @@ class OneBot extends events_1.EventEmitter {
160
162
  });
161
163
  }
162
164
  startListen() {
165
+ this.client.on('system.online', this.system_online.bind(this, "system.online"));
163
166
  this.client.trap('system', this.dispatch.bind(this, 'system'));
164
167
  this.client.trap('notice', this.dispatch.bind(this, 'notice'));
165
168
  this.client.trap('request', this.dispatch.bind(this, 'request'));
@@ -174,20 +177,34 @@ class OneBot extends events_1.EventEmitter {
174
177
  }
175
178
  this.client.logout(force);
176
179
  }
177
- dispatch(event, data) {
180
+ system_online(event, data) {
178
181
  for (const instance of this.instances) {
182
+ instance.system_online(data);
183
+ }
184
+ }
185
+ async dispatch(event, data) {
186
+ let group_id = data["group_id"];
187
+ for (const instance of this.instances) {
188
+ if (group_id) {
189
+ // 群消息派发白名单
190
+ let lst = instance.config.group_whitelist;
191
+ if (lst && lst.length > 0 && !lst.includes(group_id))
192
+ continue;
193
+ }
179
194
  const result = instance.format(event, data);
180
- if (data.source) {
195
+ if (data.source) { // 有 data.source 字段代表这是个回复
181
196
  switch (data.message_type) {
182
197
  case 'group':
183
198
  data.message.unshift({
184
199
  type: 'reply',
200
+ seq: data.source.seq,
185
201
  id: (0, icqq_2.genGroupMessageId)(data.group_id, data.source.user_id, data.source.seq, data.source.rand, data.source.time)
186
202
  });
187
203
  break;
188
204
  case 'private':
189
205
  data.message.unshift({
190
206
  type: 'reply',
207
+ seq: data.source.seq,
191
208
  id: (0, icqq_2.genDmMessageId)(data.source.user_id, data.source.seq, data.source.rand, data.source.time)
192
209
  });
193
210
  break;
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="koa-bodyparser" />
3
3
  import Koa from 'koa';
4
+ import "reflect-metadata";
4
5
  import { Logger } from "log4js";
5
6
  import { Server } from "http";
6
7
  import { Config as IcqqConfig } from "icqq";
package/lib/server/app.js CHANGED
@@ -30,6 +30,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
30
30
  exports.defineConfig = exports.createApp = exports.App = void 0;
31
31
  const koa_1 = __importDefault(require("koa"));
32
32
  const os = __importStar(require("os"));
33
+ require("reflect-metadata");
33
34
  const fs_1 = require("fs");
34
35
  const log4js_1 = require("log4js");
35
36
  const http_1 = require("http");
@@ -1,3 +1,4 @@
1
+ import { Message } from "icqq";
1
2
  import { V11 } from "../../../service/V11";
2
3
  export declare class CommonAction {
3
4
  /**
@@ -16,7 +17,7 @@ export declare class CommonAction {
16
17
  * 获取消息
17
18
  * @param message_id {string} 消息id
18
19
  */
19
- getMsg(this: V11, message_id: string): Promise<import("icqq").GroupMessage | import("icqq").PrivateMessage>;
20
+ getMsg(this: V11, message_id: number): Promise<Message>;
20
21
  /**
21
22
  * 获取合并消息
22
23
  * @param id {string} 合并id
@@ -24,8 +24,16 @@ class CommonAction {
24
24
  * 获取消息
25
25
  * @param message_id {string} 消息id
26
26
  */
27
- getMsg(message_id) {
28
- return this.client.getMsg(message_id);
27
+ async getMsg(message_id) {
28
+ if (message_id == 0)
29
+ throw new Error('getMsg: message_id[0] is invalid');
30
+ let msg_entry = await this.db.getMsgById(message_id);
31
+ if (!msg_entry)
32
+ throw new Error(`getMsg: can not find msg[${message_id}] in db`);
33
+ let msg = await this.client.getMsg(msg_entry.base64_id);
34
+ msg.message_id = String(message_id); // nonebot v11 要求 message_id 是 number 类型
35
+ msg["real_id"] = msg.message_id; // nonebot 的reply类型会检测real_id是否存在,虽然它从未使用
36
+ return msg;
29
37
  }
30
38
  /**
31
39
  * 获取合并消息
@@ -0,0 +1,13 @@
1
+ export declare class MsgEntry {
2
+ id?: number;
3
+ base64_id: string;
4
+ seq: number;
5
+ user_id: number;
6
+ nickname: string;
7
+ group_id: number;
8
+ group_name: string;
9
+ content: string;
10
+ recalled: boolean;
11
+ create_time: Date;
12
+ recall_time?: Date;
13
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.MsgEntry = void 0;
13
+ const typeorm_1 = require("typeorm");
14
+ let MsgEntry = class MsgEntry {
15
+ };
16
+ exports.MsgEntry = MsgEntry;
17
+ __decorate([
18
+ (0, typeorm_1.PrimaryGeneratedColumn)(),
19
+ __metadata("design:type", Number)
20
+ ], MsgEntry.prototype, "id", void 0);
21
+ __decorate([
22
+ (0, typeorm_1.Column)(),
23
+ __metadata("design:type", String)
24
+ ], MsgEntry.prototype, "base64_id", void 0);
25
+ __decorate([
26
+ (0, typeorm_1.Column)(),
27
+ __metadata("design:type", Number)
28
+ ], MsgEntry.prototype, "seq", void 0);
29
+ __decorate([
30
+ (0, typeorm_1.Column)(),
31
+ __metadata("design:type", Number)
32
+ ], MsgEntry.prototype, "user_id", void 0);
33
+ __decorate([
34
+ (0, typeorm_1.Column)(),
35
+ __metadata("design:type", String)
36
+ ], MsgEntry.prototype, "nickname", void 0);
37
+ __decorate([
38
+ (0, typeorm_1.Column)(),
39
+ __metadata("design:type", Number)
40
+ ], MsgEntry.prototype, "group_id", void 0);
41
+ __decorate([
42
+ (0, typeorm_1.Column)(),
43
+ __metadata("design:type", String)
44
+ ], MsgEntry.prototype, "group_name", void 0);
45
+ __decorate([
46
+ (0, typeorm_1.Column)({ length: 1024 }),
47
+ __metadata("design:type", String)
48
+ ], MsgEntry.prototype, "content", void 0);
49
+ __decorate([
50
+ (0, typeorm_1.Column)({ default: false }),
51
+ __metadata("design:type", Boolean)
52
+ ], MsgEntry.prototype, "recalled", void 0);
53
+ __decorate([
54
+ (0, typeorm_1.CreateDateColumn)(),
55
+ __metadata("design:type", Date)
56
+ ], MsgEntry.prototype, "create_time", void 0);
57
+ __decorate([
58
+ (0, typeorm_1.Column)({ nullable: true }),
59
+ __metadata("design:type", Date)
60
+ ], MsgEntry.prototype, "recall_time", void 0);
61
+ exports.MsgEntry = MsgEntry = __decorate([
62
+ (0, typeorm_1.Entity)()
63
+ ], MsgEntry);
@@ -0,0 +1,50 @@
1
+ import { MsgEntry } from "./db_entities";
2
+ import { DataSource, Repository } from "typeorm";
3
+ import { Logger } from "log4js";
4
+ export declare class Database {
5
+ logger: Logger;
6
+ dbPath: string;
7
+ dataSource: DataSource;
8
+ /**
9
+ * 消息在数据库中的保留时间
10
+ */
11
+ msgHistoryPreserveDays: number;
12
+ msgHistoryCheckInterval: number;
13
+ msgRepo: Repository<MsgEntry>;
14
+ constructor(dbPath: string, logger: Logger);
15
+ initDB(): Promise<void>;
16
+ /**
17
+ * 增加或更新一条消息到数据库
18
+ * @param msgData
19
+ */
20
+ addOrUpdateMsg(msgData: MsgEntry): Promise<number>;
21
+ /**
22
+ * 通过 icqq 的 base64 格式的 message_id 获取一个 MsgData 对象
23
+ * @param base64_id
24
+ * @returns
25
+ */
26
+ getMsgByBase64Id(base64_id: string): Promise<MsgEntry | null>;
27
+ /**
28
+ * 通过 number 类型的 id 自增主键获取一个 MsgData 对象
29
+ * @param id
30
+ * @returns
31
+ */
32
+ getMsgById(id: number): Promise<MsgEntry | null>;
33
+ /**
34
+ * 通过参数从数据库中查找消息
35
+ * @param user_id
36
+ * @param group_id
37
+ * @param seq
38
+ */
39
+ getMsgByParams(user_id: number, group_id: number, seq: number): Promise<MsgEntry | null>;
40
+ /**
41
+ * 将一条消息标记为 recalled
42
+ * @param base64_id
43
+ * @param id
44
+ */
45
+ markMsgAsRecalled(base64_id?: string, id?: number): Promise<void>;
46
+ /**
47
+ * 根据 `msgPreserveDays` 变量定义的保留期限收缩数据库
48
+ */
49
+ shrinkDB(): Promise<void>;
50
+ }
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Database = void 0;
4
+ const db_entities_1 = require("./db_entities");
5
+ const typeorm_1 = require("typeorm");
6
+ class Database {
7
+ constructor(dbPath, logger) {
8
+ /**
9
+ * 消息在数据库中的保留时间
10
+ */
11
+ this.msgHistoryPreserveDays = 14; // 历史消息默认存储2周
12
+ this.msgHistoryCheckInterval = 1 * 24 * 3600 * 1000; // 历史记录检查间隔
13
+ this.dbPath = dbPath;
14
+ this.logger = logger;
15
+ this.dataSource = new typeorm_1.DataSource({
16
+ type: "better-sqlite3",
17
+ database: dbPath,
18
+ entities: [db_entities_1.MsgEntry],
19
+ });
20
+ this.initDB();
21
+ }
22
+ async initDB() {
23
+ try {
24
+ await this.dataSource.initialize();
25
+ await this.dataSource.synchronize(false);
26
+ }
27
+ catch (err) {
28
+ this.logger.error(`sqlite [${this.dbPath}] open fail!`, err);
29
+ return;
30
+ }
31
+ this.msgRepo = this.dataSource.getRepository(db_entities_1.MsgEntry);
32
+ this.logger.debug(`sqlite [${this.dbPath}] open success`);
33
+ setInterval(() => {
34
+ this.shrinkDB();
35
+ }, this.msgHistoryCheckInterval);
36
+ }
37
+ /**
38
+ * 增加或更新一条消息到数据库
39
+ * @param msgData
40
+ */
41
+ async addOrUpdateMsg(msgData) {
42
+ let msgDataExists = await this.getMsgByBase64Id(msgData.base64_id);
43
+ if (msgDataExists) {
44
+ msgData.id = msgDataExists.id;
45
+ await this.msgRepo.update({ id: msgData.id }, msgData);
46
+ return msgDataExists.id;
47
+ }
48
+ msgData = await this.msgRepo.save(msgData);
49
+ this.logger.debug(`addMsg with id:${msgData.id}`);
50
+ return msgData.id;
51
+ }
52
+ /**
53
+ * 通过 icqq 的 base64 格式的 message_id 获取一个 MsgData 对象
54
+ * @param base64_id
55
+ * @returns
56
+ */
57
+ async getMsgByBase64Id(base64_id) {
58
+ let ret = await this.msgRepo.findOneBy({ base64_id: base64_id });
59
+ return ret;
60
+ }
61
+ /**
62
+ * 通过 number 类型的 id 自增主键获取一个 MsgData 对象
63
+ * @param id
64
+ * @returns
65
+ */
66
+ async getMsgById(id) {
67
+ let ret = await this.msgRepo.findOneBy({ id: id });
68
+ return ret;
69
+ }
70
+ /**
71
+ * 通过参数从数据库中查找消息
72
+ * @param user_id
73
+ * @param group_id
74
+ * @param seq
75
+ */
76
+ async getMsgByParams(user_id, group_id, seq) {
77
+ let ret = await this.msgRepo.findOneBy({ user_id: user_id, group_id: group_id, seq: seq });
78
+ return ret;
79
+ }
80
+ /**
81
+ * 将一条消息标记为 recalled
82
+ * @param base64_id
83
+ * @param id
84
+ */
85
+ async markMsgAsRecalled(base64_id, id) {
86
+ if (base64_id || id)
87
+ await this.msgRepo.update(base64_id ? { base64_id: base64_id } : { id: id }, { recalled: true, recall_time: new Date() });
88
+ else
89
+ throw new Error("base64_id 或 id 参数至少一个应该被赋值");
90
+ }
91
+ /**
92
+ * 根据 `msgPreserveDays` 变量定义的保留期限收缩数据库
93
+ */
94
+ async shrinkDB() {
95
+ let dt = new Date();
96
+ dt.setDate(dt.getDate() - this.msgHistoryPreserveDays);
97
+ await this.msgRepo
98
+ .createQueryBuilder()
99
+ .delete()
100
+ .from(db_entities_1.MsgEntry)
101
+ .where("create_time < :dt", { dt: dt })
102
+ .execute();
103
+ }
104
+ }
105
+ exports.Database = Database;
@@ -8,8 +8,7 @@ import { Logger } from "log4js";
8
8
  import { WebSocket, WebSocketServer } from "ws";
9
9
  import { Dispose } from "../../types";
10
10
  import { EventEmitter } from "events";
11
- import { Database } from "../../db";
12
- import { V12 } from "../../service/V12";
11
+ import { Database } from "./db_sqlite";
13
12
  export declare class V11 extends EventEmitter implements OneBot.Base {
14
13
  oneBot: OneBot<'V11'>;
15
14
  client: Client;
@@ -19,11 +18,7 @@ export declare class V11 extends EventEmitter implements OneBot.Base {
19
18
  protected timestamp: number;
20
19
  protected heartbeat?: NodeJS.Timeout;
21
20
  private path;
22
- db: Database<{
23
- eventBuffer: V12.Payload<keyof Action>[];
24
- KVMap: Record<number, string>;
25
- files: Record<string, V12.FileInfo>;
26
- }>;
21
+ db: Database;
27
22
  disposes: Dispose[];
28
23
  protected _queue: Array<{
29
24
  method: keyof Action;
@@ -41,8 +36,11 @@ export declare class V11 extends EventEmitter implements OneBot.Base {
41
36
  private startWsReverse;
42
37
  stop(force?: boolean): Promise<void>;
43
38
  format(_: any, data: any): any;
44
- dispatch(data: any): void;
39
+ system_online(data: any): void;
40
+ dispatch(data: any): Promise<void>;
45
41
  private _formatEvent;
42
+ private addMsgIntoDB;
43
+ private getReplyMsgIdFromDB;
46
44
  private _httpRequestHandler;
47
45
  /**
48
46
  * 处理ws消息
@@ -16,9 +16,10 @@ const http_1 = __importDefault(require("http"));
16
16
  const https_1 = __importDefault(require("https"));
17
17
  const events_1 = require("events");
18
18
  const fs_1 = require("fs");
19
- const db_1 = require("../../db");
19
+ const db_sqlite_1 = require("./db_sqlite");
20
20
  const path_1 = require("path");
21
21
  const app_1 = require("../../server/app");
22
+ const db_entities_1 = require("./db_entities");
22
23
  class V11 extends events_1.EventEmitter {
23
24
  constructor(oneBot, client, config) {
24
25
  super();
@@ -31,9 +32,8 @@ class V11 extends events_1.EventEmitter {
31
32
  this.queue_running = false;
32
33
  this.wsr = new Set();
33
34
  this.action = new action_1.Action();
34
- this.db = new db_1.Database((0, path_1.join)(app_1.App.configDir, 'data', this.oneBot.uin + '.json'));
35
- this.db.sync({ eventBuffer: [], KVMap: {}, files: {} });
36
35
  this.logger = this.oneBot.app.getLogger(this.oneBot.uin, this.version);
36
+ this.db = new db_sqlite_1.Database((0, path_1.join)(app_1.App.configDir, 'data', this.oneBot.uin + '.db'), this.logger);
37
37
  }
38
38
  start(path) {
39
39
  this.path = `/${this.oneBot.uin}`;
@@ -190,19 +190,27 @@ class V11 extends events_1.EventEmitter {
190
190
  format(_, data) {
191
191
  return data;
192
192
  }
193
- dispatch(data) {
194
- if (!data.post_type)
195
- data.post_type = 'system';
196
- if (data.post_type === 'system') {
197
- }
193
+ system_online(data) {
194
+ this.logger.info("【好友列表】");
195
+ this.client.fl.forEach(item => this.logger.info(`\t${item.nickname}(${item.user_id})`));
196
+ this.logger.info("【群列表】");
197
+ this.client.gl.forEach(item => this.logger.info(`\t${item.group_name}(${item.group_id})`));
198
+ this.logger.info('');
199
+ }
200
+ async dispatch(data) {
201
+ data.post_type = data.post_type || 'system';
198
202
  if (data.message && data.post_type === 'message') {
199
203
  if (this.config.post_message_format === 'array') {
200
204
  data.message = (0, icqq_cq_enable_1.toSegment)(data.message);
205
+ if (data.source) { // reply
206
+ let msg0 = data.message[0];
207
+ msg0.data['id'] = await this.getReplyMsgIdFromDB(data);
208
+ }
201
209
  }
202
210
  else {
203
211
  if (data.source) {
204
- this.db.set(`KVMap.${data.source.seq}`, data.message[0].id);
205
212
  data.message.shift();
213
+ // segment 更好用, cq 一般只用来显示,就不存储真实id了, 有需求的自己去改
206
214
  data.message = (0, icqq_cq_enable_1.toCqcode)(data).replace(/^(\[CQ:reply,id=)(.+?)\]/, `$1${data.source.seq}]`);
207
215
  }
208
216
  else {
@@ -211,12 +219,14 @@ class V11 extends events_1.EventEmitter {
211
219
  }
212
220
  }
213
221
  if (data.message_id) {
214
- this.db.set(`KVMap.${data.seq}`, data.message_id);
215
- data.message_id = data.seq;
222
+ data.message_id = await this.addMsgIntoDB(data);
223
+ }
224
+ if (data.post_type == 'notice' && String(data.notice_type).endsWith('_recall')) {
225
+ this.db.markMsgAsRecalled(data.base64_id);
216
226
  }
217
227
  if (data.font) {
218
228
  const fontNo = Buffer.from(data.font).readUInt32BE();
219
- this.db.set(`KVMap.${data.fontNo}`, data.font);
229
+ // this.db.set(`KVMap.${data.fontNo}`,data.font)
220
230
  data.font = fontNo;
221
231
  }
222
232
  data.time = Math.floor(Date.now() / 1000);
@@ -282,6 +292,31 @@ class V11 extends events_1.EventEmitter {
282
292
  return JSON.stringify(data);
283
293
  }
284
294
  }
295
+ async addMsgIntoDB(data) {
296
+ if (!data.sender || !('user_id' in data.sender)) { // eg. notice
297
+ return;
298
+ }
299
+ let msg = new db_entities_1.MsgEntry();
300
+ msg.base64_id = data.message_id;
301
+ msg.seq = data.seq;
302
+ msg.user_id = data.sender.user_id;
303
+ msg.nickname = data.sender.nickname;
304
+ if (data.message_type === 'group') {
305
+ msg.group_id = data.group_id;
306
+ msg.group_name = data["group_name"] || ''; // 可能不存在(gocq默认不发)
307
+ }
308
+ else {
309
+ msg.group_id = 0;
310
+ msg.group_name = '';
311
+ }
312
+ msg.content = data.cqCode;
313
+ return await this.db.addOrUpdateMsg(msg);
314
+ }
315
+ async getReplyMsgIdFromDB(data) {
316
+ let group_id = (data.message_type === 'group') ? data.group_id : 0;
317
+ let msg = await this.db.getMsgByParams(data.source.user_id, group_id, data.source.seq);
318
+ return msg ? msg.id : 0;
319
+ }
285
320
  async _httpRequestHandler(ctx) {
286
321
  if (ctx.method === 'OPTIONS') {
287
322
  return ctx.writeHead(200, {
@@ -375,7 +410,9 @@ class V11 extends events_1.EventEmitter {
375
410
  error: {
376
411
  code, message
377
412
  },
378
- echo: data?.echo
413
+ echo: data?.echo,
414
+ msg: e.message,
415
+ action: data.action
379
416
  }));
380
417
  }
381
418
  });
@@ -447,7 +484,7 @@ class V11 extends events_1.EventEmitter {
447
484
  async apply(req) {
448
485
  let { action, params, echo } = req;
449
486
  if (typeof params.message_id == 'number' || /^\d+$/.test(params.message_id)) {
450
- params.message_id = this.db.get(`KVMap.${params.message_id}`);
487
+ params.message_id = (await this.db.getMsgById(params.message_id)).id; // 调用api时把本地的数字id转为base64发给icqq
451
488
  }
452
489
  action = (0, utils_1.toLine)(action);
453
490
  let is_async = action.includes("_async");
@@ -510,7 +547,7 @@ class V11 extends events_1.EventEmitter {
510
547
  }
511
548
  else {
512
549
  try {
513
- ret = this.action[method].apply(this, args);
550
+ ret = await this.action[method].apply(this, args);
514
551
  }
515
552
  catch (e) {
516
553
  return JSON.stringify(V11.error(e.message));
@@ -531,10 +568,19 @@ class V11 extends events_1.EventEmitter {
531
568
  result.data = [...result.data.values()];
532
569
  if (result.data?.message)
533
570
  result.data.message = (0, icqq_cq_enable_1.toSegment)(result.data.message);
534
- if (result.data?.message_id && result.data?.seq) {
535
- this.db.set(`KVMap.${result.data.seq}`, result.data.message_id);
536
- result.data.message_id = result.data.seq;
537
- }
571
+ /**
572
+ * > send_msg 时返回的result也有 message_id 字段并且是base64格式,但由于base64与特定消息并不是一对一关系,因此转换成number类型的msgid并无意义
573
+ * 如需判断发送的消息和接收到的消息是否是同一个消息,请使用 group_id + seq 对比
574
+ *
575
+ * > 但 get_msg 返回的是从数据库获取的message_id,因此是有效的
576
+ */
577
+ // if (result.data?.message_id && result.data?.seq) {
578
+ // let message_id = result.data?.message_id
579
+ // if(!(typeof message_id == 'number' || /^\d+$/.test(message_id))) {
580
+ // // this.db.set(`KVMap.${result.data.seq}`,result.data.message_id )
581
+ // result.data.message_id = result.data.seq
582
+ // }
583
+ // }
538
584
  if (echo) {
539
585
  result.echo = echo;
540
586
  }
@@ -9,8 +9,12 @@ async function processMessage(message, source) {
9
9
  if (quote)
10
10
  (0, utils_1.remove)(elements, quote);
11
11
  let music = elements.find(e => e.type === 'music');
12
- if (music)
12
+ if (music) {
13
13
  (0, utils_1.remove)(elements, music);
14
+ if (String(music.platform) === 'custom') {
15
+ music.platform = music['subtype']; // gocq 的平台数据存储在 subtype 内,兼容 icqq 时要求前端必须发送 id 字段
16
+ }
17
+ }
14
18
  let share = elements.find(e => e.type === 'share');
15
19
  if (share)
16
20
  (0, utils_1.remove)(elements, share);
@@ -47,7 +47,8 @@ export declare class V12 extends EventEmitter implements OneBot.Base {
47
47
  friend: any;
48
48
  member: any;
49
49
  };
50
- dispatch(data: Record<string, any>): void;
50
+ system_online(data: any): void;
51
+ dispatch(data: Record<string, any>): Promise<void>;
51
52
  apply(req: V12.RequestAction): Promise<string>;
52
53
  private httpAuth;
53
54
  private httpRequestHandler;
@@ -351,7 +351,9 @@ class V12 extends events_1.EventEmitter {
351
351
  };
352
352
  return V12.formatPayload(this.oneBot.uin, event, data);
353
353
  }
354
- dispatch(data) {
354
+ system_online(data) {
355
+ }
356
+ async dispatch(data) {
355
357
  const payload = {
356
358
  id: (0, utils_2.uuid)(),
357
359
  impl: 'onebots',
package/lib/utils.d.ts CHANGED
@@ -4,6 +4,12 @@ export declare function transformObj(obj: any, callback: any): any;
4
4
  export declare function deepClone<T extends any>(obj: T): T;
5
5
  export declare function pick<T extends object, K extends keyof T>(source: T, keys?: Iterable<K>, forced?: boolean): Pick<T, K>;
6
6
  export declare function omit<T, K extends keyof T>(source: T, keys?: Iterable<K>): Omit<T, K>;
7
+ /**
8
+ * 将驼峰命名替换为下划线分割命名
9
+ * @param name
10
+ * @returns
11
+ * @todo 是否应该改名 ToUnderLine()?
12
+ */
7
13
  export declare function toLine<T extends string>(name: T): string;
8
14
  export interface Class {
9
15
  new (...args: any[]): any;
package/lib/utils.js CHANGED
@@ -114,6 +114,12 @@ function omit(source, keys) {
114
114
  return result;
115
115
  }
116
116
  exports.omit = omit;
117
+ /**
118
+ * 将驼峰命名替换为下划线分割命名
119
+ * @param name
120
+ * @returns
121
+ * @todo 是否应该改名 ToUnderLine()?
122
+ */
117
123
  function toLine(name) {
118
124
  return name.replace(/([A-Z])/g, "_$1").toLowerCase();
119
125
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onebots",
3
- "version": "0.4.18",
3
+ "version": "0.4.20",
4
4
  "description": "基于icqq的多例oneBot实现",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -64,6 +64,9 @@
64
64
  "koa-bodyparser": "^4.3.0",
65
65
  "log4js": "^6.5.2",
66
66
  "mime-types": "^2.1.35",
67
- "ws": "^8.8.0"
67
+ "ws": "^8.8.0",
68
+ "better-sqlite3": "^8.6.0",
69
+ "reflect-metadata": "^0.1.13",
70
+ "typeorm": "^0.3.17"
68
71
  }
69
72
  }