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 +9 -6
- package/lib/config.sample.yaml +2 -0
- package/lib/onebot.d.ts +5 -1
- package/lib/onebot.js +19 -2
- package/lib/server/app.d.ts +1 -0
- package/lib/server/app.js +1 -0
- package/lib/service/V11/action/common.d.ts +2 -1
- package/lib/service/V11/action/common.js +10 -2
- package/lib/service/V11/db_entities.d.ts +13 -0
- package/lib/service/V11/db_entities.js +63 -0
- package/lib/service/V11/db_sqlite.d.ts +50 -0
- package/lib/service/V11/db_sqlite.js +105 -0
- package/lib/service/V11/index.d.ts +6 -8
- package/lib/service/V11/index.js +65 -19
- package/lib/service/V11/utils.js +5 -1
- package/lib/service/V12/index.d.ts +2 -1
- package/lib/service/V12/index.js +3 -1
- package/lib/utils.d.ts +6 -0
- package/lib/utils.js +6 -0
- package/package.json +5 -2
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
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
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
|
|:--------------------|:---------|:-------|:-----------|
|
package/lib/config.sample.yaml
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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;
|
package/lib/server/app.d.ts
CHANGED
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:
|
|
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
|
-
|
|
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,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 "
|
|
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
|
-
|
|
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消息
|
package/lib/service/V11/index.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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.
|
|
215
|
-
|
|
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}`,
|
|
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.
|
|
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
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
}
|
package/lib/service/V11/utils.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/lib/service/V12/index.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
}
|