onebots 0.4.20 → 0.4.21

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.
@@ -8,7 +8,7 @@ export declare class FriendAction {
8
8
  * @param message {import('icqq').Sendable} 发送的消息
9
9
  * @param message_id {string} 引用的消息ID
10
10
  */
11
- sendPrivateMsg(this: V11, user_id: number, message: string | SegmentElem | SegmentElem[], message_id?: string): Promise<void | import("icqq").MessageRet>;
11
+ sendPrivateMsg(this: V11, user_id: number, message: string | SegmentElem | SegmentElem[], message_id?: string): Promise<any>;
12
12
  /**
13
13
  * 获取好友列表
14
14
  */
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FriendAction = void 0;
4
4
  const utils_1 = require("../../../service/V11/utils");
5
+ const shareMusicCustom_1 = require("../../../service/shareMusicCustom");
5
6
  class FriendAction {
6
7
  /**
7
8
  * 发送私聊消息
@@ -13,7 +14,7 @@ class FriendAction {
13
14
  const msg = message_id ? await this.client.getMsg(message_id) : undefined;
14
15
  const { element, quote, music, share } = await utils_1.processMessage.apply(this.client, [message, msg]);
15
16
  if (music)
16
- return await this.client.pickFriend(user_id).shareMusic(music.platform, music.id);
17
+ return await shareMusicCustom_1.shareMusic.call(this.client.pickFriend(user_id), music);
17
18
  if (share)
18
19
  return await this.client.pickFriend(user_id).shareUrl(music.data);
19
20
  if (element.length) {
@@ -7,7 +7,7 @@ export declare class GroupAction {
7
7
  * @param message {import('icqq').Sendable} 消息
8
8
  * @param message_id {string} 引用的消息ID
9
9
  */
10
- sendGroupMsg(this: V11, group_id: number, message: string | SegmentElem | SegmentElem[], message_id?: string): Promise<void | import("icqq").MessageRet>;
10
+ sendGroupMsg(this: V11, group_id: number, message: string | SegmentElem | SegmentElem[], message_id?: string): Promise<any>;
11
11
  /**
12
12
  * 群组踢人
13
13
  * @param group_id {number} 群id
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GroupAction = void 0;
4
4
  const utils_1 = require("../../../service/V11/utils");
5
+ const shareMusicCustom_1 = require("../../../service/shareMusicCustom");
5
6
  class GroupAction {
6
7
  /**
7
8
  * 发送群聊消息
@@ -13,7 +14,7 @@ class GroupAction {
13
14
  const msg = message_id ? await this.client.getMsg(message_id) : undefined;
14
15
  const { element, quote, music, share } = await utils_1.processMessage.apply(this.client, [message, msg]);
15
16
  if (music)
16
- return await this.client.pickGroup(group_id).shareMusic(music.platform, music.id);
17
+ return await shareMusicCustom_1.shareMusic.call(this.client.pickGroup(group_id), music);
17
18
  if (share)
18
19
  return await this.client.pickGroup(group_id).shareUrl(music.data);
19
20
  if (element.length) {
@@ -1,6 +1,7 @@
1
1
  import { MsgEntry } from "./db_entities";
2
2
  import { DataSource, Repository } from "typeorm";
3
3
  import { Logger } from "log4js";
4
+ import { AsyncLock } from "../../types";
4
5
  export declare class Database {
5
6
  logger: Logger;
6
7
  dbPath: string;
@@ -11,6 +12,7 @@ export declare class Database {
11
12
  msgHistoryPreserveDays: number;
12
13
  msgHistoryCheckInterval: number;
13
14
  msgRepo: Repository<MsgEntry>;
15
+ dbLock: AsyncLock;
14
16
  constructor(dbPath: string, logger: Logger);
15
17
  initDB(): Promise<void>;
16
18
  /**
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Database = void 0;
4
4
  const db_entities_1 = require("./db_entities");
5
5
  const typeorm_1 = require("typeorm");
6
+ const types_1 = require("../../types");
6
7
  class Database {
7
8
  constructor(dbPath, logger) {
8
9
  /**
@@ -12,6 +13,7 @@ class Database {
12
13
  this.msgHistoryCheckInterval = 1 * 24 * 3600 * 1000; // 历史记录检查间隔
13
14
  this.dbPath = dbPath;
14
15
  this.logger = logger;
16
+ this.dbLock = new types_1.AsyncLock();
15
17
  this.dataSource = new typeorm_1.DataSource({
16
18
  type: "better-sqlite3",
17
19
  database: dbPath,
@@ -39,15 +41,25 @@ class Database {
39
41
  * @param msgData
40
42
  */
41
43
  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;
44
+ await this.dbLock.lock();
45
+ try {
46
+ let msgDataExists = await this.getMsgByParams(msgData.user_id, msgData.group_id, msgData.seq);
47
+ if (msgDataExists) {
48
+ // send_msg() 返回值和同步的 message 消息哪个先来不确定,send_msg 返回值后来时不允许更新数据库
49
+ if (msgData.content.length == 0) {
50
+ return;
51
+ }
52
+ msgData.id = msgDataExists.id;
53
+ await this.msgRepo.update({ id: msgData.id }, msgData);
54
+ return msgDataExists.id;
55
+ }
56
+ msgData = await this.msgRepo.save(msgData);
57
+ this.logger.debug(`addMsg with id:${msgData.id}`);
58
+ return msgData.id;
59
+ }
60
+ finally {
61
+ this.dbLock.unlock();
47
62
  }
48
- msgData = await this.msgRepo.save(msgData);
49
- this.logger.debug(`addMsg with id:${msgData.id}`);
50
- return msgData.id;
51
63
  }
52
64
  /**
53
65
  * 通过 icqq 的 base64 格式的 message_id 获取一个 MsgData 对象
@@ -39,7 +39,16 @@ export declare class V11 extends EventEmitter implements OneBot.Base {
39
39
  system_online(data: any): void;
40
40
  dispatch(data: any): Promise<void>;
41
41
  private _formatEvent;
42
- private addMsgIntoDB;
42
+ private addMsgToDB;
43
+ /**
44
+ * 从 send_msg_xxx() 调用的返回值中提取消息存入数据库(可以让前端在没有收到同步的message数据前就有能力拿到消息对应的base64_id)
45
+ * (也有可能来的比message慢,后来的话会被数据库忽略)
46
+ * @param user_id 发送者
47
+ * @param group_id 群号,私聊为0
48
+ * @param seq 消息序号
49
+ * @param base64_id icqq返回的base64格式的消息id
50
+ */
51
+ private addMsgToDBFromSendMsgResult;
43
52
  private getReplyMsgIdFromDB;
44
53
  private _httpRequestHandler;
45
54
  /**
@@ -219,7 +219,7 @@ class V11 extends events_1.EventEmitter {
219
219
  }
220
220
  }
221
221
  if (data.message_id) {
222
- data.message_id = await this.addMsgIntoDB(data);
222
+ data.message_id = await this.addMsgToDB(data);
223
223
  }
224
224
  if (data.post_type == 'notice' && String(data.notice_type).endsWith('_recall')) {
225
225
  this.db.markMsgAsRecalled(data.base64_id);
@@ -292,7 +292,7 @@ class V11 extends events_1.EventEmitter {
292
292
  return JSON.stringify(data);
293
293
  }
294
294
  }
295
- async addMsgIntoDB(data) {
295
+ async addMsgToDB(data) {
296
296
  if (!data.sender || !('user_id' in data.sender)) { // eg. notice
297
297
  return;
298
298
  }
@@ -312,6 +312,24 @@ class V11 extends events_1.EventEmitter {
312
312
  msg.content = data.cqCode;
313
313
  return await this.db.addOrUpdateMsg(msg);
314
314
  }
315
+ /**
316
+ * 从 send_msg_xxx() 调用的返回值中提取消息存入数据库(可以让前端在没有收到同步的message数据前就有能力拿到消息对应的base64_id)
317
+ * (也有可能来的比message慢,后来的话会被数据库忽略)
318
+ * @param user_id 发送者
319
+ * @param group_id 群号,私聊为0
320
+ * @param seq 消息序号
321
+ * @param base64_id icqq返回的base64格式的消息id
322
+ */
323
+ async addMsgToDBFromSendMsgResult(user_id, group_id, seq, base64_id) {
324
+ let msg = new db_entities_1.MsgEntry();
325
+ msg.base64_id = base64_id;
326
+ msg.seq = seq;
327
+ msg.user_id = user_id;
328
+ msg.nickname = '';
329
+ msg.group_id = group_id;
330
+ msg.content = '';
331
+ return await this.db.addOrUpdateMsg(msg);
332
+ }
315
333
  async getReplyMsgIdFromDB(data) {
316
334
  let group_id = (data.message_type === 'group') ? data.group_id : 0;
317
335
  let msg = await this.db.getMsgByParams(data.source.user_id, group_id, data.source.seq);
@@ -568,19 +586,10 @@ class V11 extends events_1.EventEmitter {
568
586
  result.data = [...result.data.values()];
569
587
  if (result.data?.message)
570
588
  result.data.message = (0, icqq_cq_enable_1.toSegment)(result.data.message);
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
- // }
589
+ // send_msg_xxx 时提前把数据写入数据库(也有可能来的比message慢,后来的话会被数据库忽略)
590
+ if (result.status === 'ok' && params.user_id && result.data?.message_id && result.data?.seq) {
591
+ result.data.message_id = await this.addMsgToDBFromSendMsgResult(params.user_id, params.group_id || 0, result.data.seq, result.data.message_id);
592
+ }
584
593
  if (echo) {
585
594
  result.echo = echo;
586
595
  }
@@ -0,0 +1,19 @@
1
+ import { Contactable } from "icqq/lib/internal";
2
+ import { MusicElem } from "icqq/lib/message";
3
+ import { Encodable } from "icqq/lib/core/protobuf";
4
+ /** 发送音乐分享(允许自定义参数) */
5
+ export declare function shareMusic(this: Contactable, music: MusicElem): Promise<void>;
6
+ /**
7
+ * 构造频道b77音乐分享
8
+ * @param channel_id {string} 子频道id
9
+ * @param guild_id {string} 频道id
10
+ * @param music 音乐分享数据
11
+ */
12
+ export declare function buildMusic(channel_id: string, guild_id: string, music: MusicElem): Promise<Encodable>;
13
+ /**
14
+ * 构造b77音乐分享
15
+ * @param target {number} 群id或者好友qq
16
+ * @param bu {0|1} 类型表示:0 为好友 1 为群
17
+ * @param music 音乐分享数据
18
+ */
19
+ export declare function buildMusic(target: number, bu: 0 | 1, music: MusicElem): Promise<Encodable>;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildMusic = exports.shareMusic = void 0;
4
+ const message_1 = require("icqq/lib/message");
5
+ const core_1 = require("icqq/lib/core");
6
+ const process_1 = require("process");
7
+ /** 发送音乐分享(允许自定义参数) */
8
+ async function shareMusic(music) {
9
+ const body = await buildMusic((this.gid || this.uid), this.dm ? 0 : 1, music);
10
+ await this.c.sendOidb("OidbSvc.0xb77_9", core_1.pb.encode(body));
11
+ }
12
+ exports.shareMusic = shareMusic;
13
+ async function buildMusic(target, bu, music) {
14
+ const { appid, package_name, sign, getMusicInfo } = message_1.musicFactory[music.platform];
15
+ let style = 4;
16
+ try {
17
+ let { singer = null, title = null, jumpUrl = null, musicUrl = null, preview = null } = music.id ? await getMusicInfo(music.id) : {};
18
+ singer = music['content'] || music.singer || singer; // 自定义参数优先级高于默认值(gocq的参数名与icqq有区别,做下兼容)
19
+ title = music.title || title;
20
+ jumpUrl = music.jumpUrl || jumpUrl;
21
+ musicUrl = music['url'] || music['voice'] || music.musicUrl || musicUrl;
22
+ preview = music['image'] || music.preview || preview;
23
+ if (!musicUrl)
24
+ style = 0;
25
+ return {
26
+ 1: appid,
27
+ 2: 1,
28
+ 3: style,
29
+ 5: {
30
+ 1: 1,
31
+ 2: "0.0.0",
32
+ 3: package_name,
33
+ 4: sign
34
+ },
35
+ 10: typeof bu === 'string' ? 3 : bu,
36
+ 11: target,
37
+ 12: {
38
+ 10: title,
39
+ 11: singer,
40
+ 12: "[分享]" + title,
41
+ 13: jumpUrl,
42
+ 14: preview,
43
+ 16: musicUrl,
44
+ },
45
+ 19: typeof bu === 'string' ? Number(bu) : undefined
46
+ };
47
+ }
48
+ catch (e) {
49
+ throw new Error("unknown music id: " + music.id + ", in platform: " + music.platform + ", with title: " + process_1.title);
50
+ }
51
+ }
52
+ exports.buildMusic = buildMusic;
package/lib/types.d.ts CHANGED
@@ -1,3 +1,12 @@
1
1
  export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal" | "mark" | "off";
2
2
  export type Dispose = () => any;
3
3
  export type MayBeArray<T extends any> = T | T[];
4
+ /**
5
+ * 异步锁---
6
+ */
7
+ export declare class AsyncLock {
8
+ private _lock;
9
+ private _waitList;
10
+ lock(): Promise<void>;
11
+ unlock(): void;
12
+ }
package/lib/types.js CHANGED
@@ -1,2 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AsyncLock = void 0;
4
+ /**
5
+ * 异步锁---
6
+ */
7
+ class AsyncLock {
8
+ constructor() {
9
+ this._lock = false;
10
+ this._waitList = [];
11
+ }
12
+ async lock() {
13
+ if (this._lock) {
14
+ await new Promise((resolve) => {
15
+ this._waitList.push(resolve);
16
+ });
17
+ }
18
+ this._lock = true;
19
+ }
20
+ unlock() {
21
+ this._lock = false;
22
+ if (this._waitList.length > 0) {
23
+ let resolve = this._waitList.shift();
24
+ resolve && resolve();
25
+ }
26
+ }
27
+ }
28
+ exports.AsyncLock = AsyncLock;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onebots",
3
- "version": "0.4.20",
3
+ "version": "0.4.21",
4
4
  "description": "基于icqq的多例oneBot实现",
5
5
  "engines": {
6
6
  "node": ">=16"