mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
146 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
18892379de | ||
![]() |
620d61c8dc | ||
![]() |
9f91398875 | ||
![]() |
34d19a471a | ||
![]() |
2ef6477d7c | ||
![]() |
26e6800836 | ||
![]() |
9dbbcf3872 | ||
![]() |
6b3343e1e4 | ||
![]() |
ef48f754a5 | ||
![]() |
3be1ede847 | ||
![]() |
7bff1b61e8 | ||
![]() |
6affd0eb68 | ||
![]() |
0a112d15e0 | ||
![]() |
4e03f582bb | ||
![]() |
8f186c1c5e | ||
![]() |
cd1bae9a1f | ||
![]() |
60796c26ca | ||
![]() |
28927f950d | ||
![]() |
95f16ebc8c | ||
![]() |
25bca8385d | ||
![]() |
965c7f23b4 | ||
![]() |
33082af9cc | ||
![]() |
1f7f3565b0 | ||
![]() |
f784363696 | ||
![]() |
37bd51e138 | ||
![]() |
c367728c43 | ||
![]() |
61f0f5d884 | ||
![]() |
5f2ebeead7 | ||
![]() |
7646037fc7 | ||
![]() |
eac6d285ff | ||
![]() |
b921d5e734 | ||
![]() |
831d808e63 | ||
![]() |
451b88d7e3 | ||
![]() |
f916682a71 | ||
![]() |
2d76bcf0cf | ||
![]() |
8db294efe6 | ||
![]() |
5cc5149aed | ||
![]() |
7ecb01dc9f | ||
![]() |
8bf1a545d9 | ||
![]() |
efb2be2f94 | ||
![]() |
bd2edda494 | ||
![]() |
991172eae4 | ||
![]() |
fc7631f9aa | ||
![]() |
a21efb7d2f | ||
![]() |
03bc844ad0 | ||
![]() |
f7bdc35ed6 | ||
![]() |
0efdffd857 | ||
![]() |
6a16a42d0c | ||
![]() |
e9c00c72b1 | ||
![]() |
ab22f36b8a | ||
![]() |
c57497cd91 | ||
![]() |
ba123236e5 | ||
![]() |
338b6e4607 | ||
![]() |
f88c717560 | ||
![]() |
f8ffc92db5 | ||
![]() |
98c23c172c | ||
![]() |
781c107d8c | ||
![]() |
186668c075 | ||
![]() |
cf9f785193 | ||
![]() |
72d2d3f224 | ||
![]() |
087c76b394 | ||
![]() |
4f9fb2c8c3 | ||
![]() |
334e43e764 | ||
![]() |
7843256402 | ||
![]() |
0522ba35fe | ||
![]() |
24d3b52e0b | ||
![]() |
3177110f0f | ||
![]() |
e1b8243a67 | ||
![]() |
b1c6ce3885 | ||
![]() |
0b4b25a11e | ||
![]() |
1176fe984a | ||
![]() |
6ca768c3ee | ||
![]() |
3da1659c8d | ||
![]() |
9aa4cd319c | ||
![]() |
5af866cdca | ||
![]() |
2b421fa447 | ||
![]() |
30ec964325 | ||
![]() |
714d7d72eb | ||
![]() |
687aa0f363 | ||
![]() |
8363ab07a7 | ||
![]() |
c46a757339 | ||
![]() |
557864395b | ||
![]() |
3f7a85d80b | ||
![]() |
8d18d2ce1f | ||
![]() |
7141ba1587 | ||
![]() |
44d350a225 | ||
![]() |
239b8e72d9 | ||
![]() |
279bdb6fb1 | ||
![]() |
a0cea819da | ||
![]() |
9ab7f60544 | ||
![]() |
aaf7191bf3 | ||
![]() |
628c9be0c8 | ||
![]() |
733052720c | ||
![]() |
a10f007194 | ||
![]() |
6fa50c58d3 | ||
![]() |
c54a58d6e4 | ||
![]() |
34cb1ea3fd | ||
![]() |
f640b0ca91 | ||
![]() |
60e16da42e | ||
![]() |
0cdceb95d6 | ||
![]() |
70bd22d925 | ||
![]() |
82462dd647 | ||
![]() |
c0466e943d | ||
![]() |
b187b4695d | ||
![]() |
b1956d2a37 | ||
![]() |
590b622e5f | ||
![]() |
3d8174396a | ||
![]() |
b8dc6e9bd9 | ||
![]() |
8ee99109dc | ||
![]() |
902041d4ee | ||
![]() |
cc34aef47e | ||
![]() |
0afbbe7c7a | ||
![]() |
8004553ba7 | ||
![]() |
0023b2846a | ||
![]() |
34775c1816 | ||
![]() |
e0759e704b | ||
![]() |
0aa225ca78 | ||
![]() |
b43b4ee5c0 | ||
![]() |
0cdb8cecbf | ||
![]() |
fd6a306742 | ||
![]() |
7f3b3d2277 | ||
![]() |
8be5b977bf | ||
![]() |
d7ddb15f9c | ||
![]() |
9a6a1798d0 | ||
![]() |
14196fd349 | ||
![]() |
941b89a523 | ||
![]() |
a5f9e5f8c0 | ||
![]() |
80c3356c8f | ||
![]() |
914136b750 | ||
![]() |
f9a60795f5 | ||
![]() |
19640927c7 | ||
![]() |
22faac7e36 | ||
![]() |
30d260ab32 | ||
![]() |
115120d066 | ||
![]() |
1327844736 | ||
![]() |
29904f3cb7 | ||
![]() |
50395594b7 | ||
![]() |
9360af88b3 | ||
![]() |
376370336c | ||
![]() |
70df6e3302 | ||
![]() |
0a1fc2dc12 | ||
![]() |
9857f6e437 | ||
![]() |
56d6ebe916 | ||
![]() |
81134ea2d4 | ||
![]() |
a9f3e7fc54 | ||
![]() |
eb84e2f8c9 |
@@ -1,21 +1,21 @@
|
|||||||
# EditorConfig is awesome: https://EditorConfig.org
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
# top-most EditorConfig file
|
# top-most EditorConfig file
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
# Unix-style newlines with a newline ending every file
|
# Unix-style newlines with a newline ending every file
|
||||||
[*]
|
[*]
|
||||||
end_of_line = lf|crlf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
# Matches multiple files with brace expansion notation
|
# Matches multiple files with brace expansion notation
|
||||||
# Set default charset
|
# Set default charset
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
# 2 space indentation
|
# 2 space indentation
|
||||||
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
|
[*.{cjs,mjs,js,jsx,ts,tsx,css,scss,sass,html,json}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
# Unfortunately, EditorConfig doesn't support space configuration inside import braces directly.
|
# Unfortunately, EditorConfig doesn't support space configuration inside import braces directly.
|
||||||
# You'll need to rely on your linter/formatter like ESLint or Prettier for that.
|
# You'll need to rely on your linter/formatter like ESLint or Prettier for that.
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -14,4 +14,5 @@ dist/
|
|||||||
|
|
||||||
# Build
|
# Build
|
||||||
*.db
|
*.db
|
||||||
checkVersion.sh
|
checkVersion.sh
|
||||||
|
bun.lockb
|
||||||
|
20
docs/changelogs/CHANGELOG.v1.6.8.md
Normal file
20
docs/changelogs/CHANGELOG.v1.6.8.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# v1.6.8
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 移除数据库文件读写 ~ 优化性能
|
||||||
|
2. 重构消息发送 极限速度优化 ~ 优化性能
|
||||||
|
3. WebUi配置热重载优化 ~ 修复问题
|
||||||
|
4. 修复偶现崩溃问题 ~ 修复问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
1. 最后发言时间重构 入群时间失效 ~ 替换功能
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
18
docs/changelogs/old/CHANGELOG.v1.6.5.md
Normal file
18
docs/changelogs/old/CHANGELOG.v1.6.5.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# v1.6.5
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 优化了WrapperNative载入代码
|
||||||
|
2. 优化缓存
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
没有哦
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
17
docs/changelogs/old/CHANGELOG.v1.6.6.md
Normal file
17
docs/changelogs/old/CHANGELOG.v1.6.6.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# v1.6.6
|
||||||
|
|
||||||
|
QQ Version: Windows 9.9.12-26000 / Linux 3.2.9-26000
|
||||||
|
## 使用前警告
|
||||||
|
1. 在最近版本由于QQ本体大幅变动,为了保证NapCat可用性,NapCat近期启动与安装方式将将大幅变动,请关注文档和社群获取。
|
||||||
|
2. 在Core上完全执行开源,请不要用于违法用途,如此可能造成NapCat完全停止更新。
|
||||||
|
3. 针对原启动方式的围堵,NapCat研发了多种方式,除此其余理论与扩展的分析和思路将部分展示于Docs,以便各位参与开发与维护NapCat。
|
||||||
|
## 其余·备注
|
||||||
|
启动方式: WayBoot.03 (Electron Main进程为Node 直接注入代码 同理项目: LiteLoader)
|
||||||
|
|
||||||
|
## 修复与优化
|
||||||
|
1. 修复了一些问题
|
||||||
|
|
||||||
|
## 新增与调整
|
||||||
|
没有哦
|
||||||
|
|
||||||
|
新增的 API 详细见[API文档](https://napneko.github.io/zh-CN/develop/extends_api)
|
@@ -1,2 +0,0 @@
|
|||||||
# 开始
|
|
||||||
jadx 跳转于 `com.tencent.qqnt.kernel.*`
|
|
@@ -1,42 +0,0 @@
|
|||||||
# Android
|
|
||||||
```java
|
|
||||||
GroupMemberExtReq groupMemberExtReq = new GroupMemberExtReq();
|
|
||||||
groupMemberExtReq.sourceType = MemberExtSourceType.TITLETYPE.ordinal();
|
|
||||||
groupMemberExtReq.groupCode = longOrNull.longValue();
|
|
||||||
groupMemberExtReq.beginUin = "0";
|
|
||||||
groupMemberExtReq.dataTime = "0";
|
|
||||||
Long[] lArr = new Long[1];
|
|
||||||
AppInterface a2 = dVar.a();
|
|
||||||
lArr[0] = Long.valueOf(a2 != null ? a2.getLongAccountUin() : 0L);
|
|
||||||
arrayListOf = CollectionsKt__CollectionsKt.arrayListOf(lArr);
|
|
||||||
groupMemberExtReq.uinList = arrayListOf;
|
|
||||||
MemberExtInfoFilter memberExtInfoFilter = new MemberExtInfoFilter();
|
|
||||||
memberExtInfoFilter.memberLevelInfoUin = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoPoint = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoActiveDay = 1;
|
|
||||||
memberExtInfoFilter.memberLevelInfoLevel = 1;
|
|
||||||
memberExtInfoFilter.levelName = 1;
|
|
||||||
memberExtInfoFilter.dataTime = 1;
|
|
||||||
memberExtInfoFilter.sysShowFlag = 1;
|
|
||||||
memberExtInfoFilter.userShowFlag = 1;
|
|
||||||
memberExtInfoFilter.userShowFlagNew = 1;
|
|
||||||
memberExtInfoFilter.levelNameNew = 1;
|
|
||||||
Unit unit = Unit.INSTANCE;
|
|
||||||
groupMemberExtReq.memberExtFilter = memberExtInfoFilter;
|
|
||||||
troopLevelFrequencyControl.f(troopUin, new TroopListRepo$fetchTroopLevelInfo$2(b2, groupMemberExtReq, troopUin, new com.tencent.qqnt.troopmemberlist.report.c("fetchTroopLevelInfo")));
|
|
||||||
```
|
|
||||||
# Win
|
|
||||||
参数解析位于 sub_181456A10(24108) -> wrapper.node(24108)+1456A10
|
|
||||||
IGroupService.GetMemberExt(param: object);
|
|
||||||
param展开如下
|
|
||||||
```
|
|
||||||
groupCode string
|
|
||||||
beginUin string
|
|
||||||
dataTime string
|
|
||||||
uinList Array<string>
|
|
||||||
uinNum string
|
|
||||||
groupType string
|
|
||||||
richCardNameVer string
|
|
||||||
sourceType number
|
|
||||||
memberExtFilter object// 参数解析位于 sub_18145A6D0(24108) -> wrapper.node(24108)+145A6D0
|
|
||||||
```
|
|
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
public static final int C2C_PIC_DOWNLOAD = 1004;
|
public static final int C2C_PIC_DOWNLOAD = 1004;
|
||||||
public static final String C2C_PIC_DOWNLOAD_DOMAIN = "c2cpicdw.qpic.cn";
|
public static final String C2C_PIC_DOWNLOAD_DOMAIN = "c2cpicdw.qpic.cn";
|
||||||
public static final String C2C_PIC_DOWNLOAD_QUIC_DOMAIN = "c2cpicdw.quic.qpic.cn";
|
public static final String C2C_PIC_DOWNLOAD_QUIC_DOMAIN = "c2cpicdw.quic.qpic.cn";
|
||||||
|
16
docs/develop/NC 1.6.X的计划.md
Normal file
16
docs/develop/NC 1.6.X的计划.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 开发方向
|
||||||
|
方向一 NativeCall/Hook:
|
||||||
|
1. 崩溃检测机制的实现
|
||||||
|
2. Api_Caller 的Hook 可以拿到Event/Handler 进一步提升NC 即时的拦截与处理一些事件比如ReCall拦截
|
||||||
|
3. Node包装层 进一步分析,拿到脱离自带Listener/Adapter,可以拿到一些更加底层的数据变动 或许包括更多二进制数据
|
||||||
|
|
||||||
|
方向二 全新的无头启动 Way01
|
||||||
|
1. 基于Node启动原理,借助导出符号获取函数地址 再次还原NodeMain
|
||||||
|
|
||||||
|
方向三 发包与收包
|
||||||
|
1. 参考 方向一/3 大概可以收包
|
||||||
|
2. 发包 (暂时没有计划)
|
||||||
|
|
||||||
|
方向四 版本控制
|
||||||
|
1. 根据不同版本进行逻辑控制
|
||||||
|
2. 某些参数的自动提取
|
@@ -1,24 +0,0 @@
|
|||||||
# 前排提示
|
|
||||||
由于Core未处于开源,非组织人员无法参与Core开发,此处为Core开发提示
|
|
||||||
|
|
||||||
# 准备工具
|
|
||||||
frida ida-pro jadx x64dbg ce 内部调试脚本
|
|
||||||
|
|
||||||
## ida-pro
|
|
||||||
1. 用于快速分析入参和返回类型
|
|
||||||
2. 通过静态QLog推测语义
|
|
||||||
3. 提取Listener与Service (常用)
|
|
||||||
## frida
|
|
||||||
1. 用于动态获取QLog推测语义
|
|
||||||
2. 捕捉Native函数 实际入参与数据 分析中间流程
|
|
||||||
|
|
||||||
## jadx
|
|
||||||
1. 通过其它平台实现 静态获取QLog推测语义
|
|
||||||
2. 提供部分未调用代码 参考
|
|
||||||
|
|
||||||
## x64dbg
|
|
||||||
1. 验证IDA的Hook点
|
|
||||||
|
|
||||||
## 内部脚本
|
|
||||||
1. 提取Listener与Service (不调用无类型 不推荐)
|
|
||||||
2. 获取NT调用流程
|
|
@@ -1 +1,8 @@
|
|||||||
|
# Api方向
|
||||||
|
## getMsgUniqueId √ 已应用
|
||||||
getMsgUniqueId 传入时间 产出一个唯一ID 发送消息作为一个参数
|
getMsgUniqueId 传入时间 产出一个唯一ID 发送消息作为一个参数
|
||||||
|
|
||||||
|
# Native方向
|
||||||
|
## magic_load
|
||||||
|
## api_caller
|
||||||
|
## NodeMain
|
||||||
|
12
package.json
12
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.6.4",
|
"version": "1.6.8",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch:dev": "vite --mode development",
|
"watch:dev": "vite --mode development",
|
||||||
"watch:prod": "vite --mode production",
|
"watch:prod": "vite --mode production",
|
||||||
@@ -19,10 +19,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.7",
|
"@babel/core": "^7.24.7",
|
||||||
"@babel/preset-typescript": "^7.24.7",
|
|
||||||
"vite-plugin-babel": "^1.2.0",
|
|
||||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/plugin-proposal-decorators": "^7.24.7",
|
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||||
|
"@babel/preset-typescript": "^7.24.7",
|
||||||
"@log4js-node/log4js-api": "^1.0.2",
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
"@protobuf-ts/plugin": "^2.9.4",
|
"@protobuf-ts/plugin": "^2.9.4",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
@@ -31,9 +30,9 @@
|
|||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/figlet": "^1.5.8",
|
"@types/figlet": "^1.5.8",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20.11.30",
|
"@types/node": "^20.11.30",
|
||||||
"@types/qrcode-terminal": "^0.12.2",
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
"@types/uuid": "^10.0.0",
|
|
||||||
"@types/ws": "^8.5.10",
|
"@types/ws": "^8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@typescript-eslint/parser": "^7.4.0",
|
||||||
@@ -47,6 +46,7 @@
|
|||||||
"rollup-plugin-obfuscator": "^1.1.0",
|
"rollup-plugin-obfuscator": "^1.1.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.2.6",
|
"vite": "^5.2.6",
|
||||||
|
"vite-plugin-babel": "^1.2.0",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-plugin-dts": "^3.8.2",
|
"vite-plugin-dts": "^3.8.2",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-tsconfig-paths": "^4.3.2"
|
||||||
@@ -64,9 +64,7 @@
|
|||||||
"json-schema-to-ts": "^3.1.0",
|
"json-schema-to-ts": "^3.1.0",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"silk-wasm": "^3.3.4",
|
"silk-wasm": "^3.6.1",
|
||||||
"sqlite3": "^5.1.7",
|
|
||||||
"uuid": "^10.0.0",
|
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
script/NapCat.164.bat
Normal file
28
script/NapCat.164.bat
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
:: 检查是否有管理员权限
|
||||||
|
net session >nul 2>&1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo 请求管理员权限...
|
||||||
|
powershell -Command "Start-Process '%~f0' -Verb runAs"
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
:: 如果有管理员权限,继续执行
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "QQPath=!pathWithoutUninstall!QQ.exe"
|
||||||
|
|
||||||
|
echo !QQPath!
|
||||||
|
"!QQPath!" --enable-logging %*
|
||||||
|
|
||||||
|
pause
|
3
script/NapCat.Way01.bat
Normal file
3
script/NapCat.Way01.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
REM 全新启动脚本 基于 Hook Native 预计版本1.6.0左右发布
|
||||||
|
@echo off
|
||||||
|
pause
|
18
script/napcat-9912-utf8.bat
Normal file
18
script/napcat-9912-utf8.bat
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
chcp 65001
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "QQPath=!pathWithoutUninstall!"
|
||||||
|
cd /d !QQPath!
|
||||||
|
echo !QQPath!
|
||||||
|
QQ.exe --enable-logging %*
|
41
script/napcat-9912-utf8.ps1
Normal file
41
script/napcat-9912-utf8.ps1
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location -Path $QQpath
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& ./QQ.exe --enable-logging $params}"
|
17
script/napcat-9912.bat
Normal file
17
script/napcat-9912.bat
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set "RetString=%%b"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("!RetString!") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set QQPath=!pathWithoutUninstall!
|
||||||
|
cd /d !QQPath!
|
||||||
|
echo !QQPath!
|
||||||
|
QQ.exe --enable-logging %*
|
41
script/napcat-9912.ps1
Normal file
41
script/napcat-9912.ps1
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
function Get-QQpath {
|
||||||
|
try {
|
||||||
|
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
|
||||||
|
$uninstallString = $key.UninstallString
|
||||||
|
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "get QQ path error: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Select-QQPath {
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||||||
|
|
||||||
|
$dialogTitle = "Select QQ.exe"
|
||||||
|
|
||||||
|
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
|
||||||
|
$filePicker.Title = $dialogTitle
|
||||||
|
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
|
||||||
|
$filePicker.FilterIndex = 1
|
||||||
|
$null = $filePicker.ShowDialog()
|
||||||
|
if (-not ($filePicker.FileName)) {
|
||||||
|
throw "User did not select an .exe file."
|
||||||
|
}
|
||||||
|
return $filePicker.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = $args -join " "
|
||||||
|
Try {
|
||||||
|
$QQpath = Get-QQpath
|
||||||
|
}
|
||||||
|
Catch {
|
||||||
|
$QQpath = Select-QQPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(Test-Path $QQpath)) {
|
||||||
|
throw "provided QQ path is invalid: $QQpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location -Path $QQpath
|
||||||
|
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& ./QQ.exe --enable-logging $params}"
|
@@ -9,7 +9,15 @@ type RegisterHandler = (res: Response, payload: any) => Promise<any>
|
|||||||
export abstract class HttpServerBase {
|
export abstract class HttpServerBase {
|
||||||
name: string = 'NapCatQQ';
|
name: string = 'NapCatQQ';
|
||||||
private readonly expressAPP: Express;
|
private readonly expressAPP: Express;
|
||||||
private server: http.Server | null = null;
|
private _server: http.Server | null = null;
|
||||||
|
|
||||||
|
public get server(): http.Server | null {
|
||||||
|
return this._server;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set server(value: http.Server | null) {
|
||||||
|
this._server = value;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.expressAPP = express();
|
this.expressAPP = express();
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { WebSocket, WebSocketServer } from 'ws';
|
import { WebSocket, WebSocketServer } from 'ws';
|
||||||
|
import http from 'http';
|
||||||
import urlParse from 'url';
|
import urlParse from 'url';
|
||||||
import { IncomingMessage } from 'node:http';
|
import { IncomingMessage } from 'node:http';
|
||||||
import { log } from '@/common/utils/log';
|
import { log } from '@/common/utils/log';
|
||||||
@@ -27,17 +28,36 @@ export class WebsocketServerBase {
|
|||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
start(port: number, host: string = '') {
|
start(port: number | http.Server, host: string = '') {
|
||||||
try {
|
if (port instanceof http.Server) {
|
||||||
this.ws = new WebSocketServer({
|
try {
|
||||||
port,
|
const wss = new WebSocketServer({
|
||||||
host: '',
|
noServer: true,
|
||||||
maxPayload: 1024 * 1024 * 1024
|
maxPayload: 1024 * 1024 * 1024
|
||||||
}).on('error', () => {
|
}).on('error', () => {
|
||||||
});
|
});
|
||||||
log(`ws服务启动成功, ${host}:${port}`);
|
this.ws = wss;
|
||||||
} catch (e: any) {
|
port.on('upgrade', function upgrade(request, socket, head) {
|
||||||
throw Error('ws服务启动失败, 请检查监听的ip和端口' + e.toString());
|
wss.handleUpgrade(request, socket, head, function done(ws) {
|
||||||
|
wss.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
log(`ws服务启动成功, 绑定到HTTP服务`);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw Error('ws服务启动失败, 可能是绑定的HTTP服务异常' + e.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
this.ws = new WebSocketServer({
|
||||||
|
port,
|
||||||
|
host: '',
|
||||||
|
maxPayload: 1024 * 1024 * 1024
|
||||||
|
}).on('error', () => {
|
||||||
|
});
|
||||||
|
log(`ws服务启动成功, ${host}:${port}`);
|
||||||
|
} catch (e: any) {
|
||||||
|
throw Error('ws服务启动失败, 请检查监听的ip和端口' + e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.ws.on('connection', (wsClient, req) => {
|
this.ws.on('connection', (wsClient, req) => {
|
||||||
const url: string = req.url!.split('?').shift() || '/';
|
const url: string = req.url!.split('?').shift() || '/';
|
||||||
@@ -50,10 +70,12 @@ export class WebsocketServerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
this.ws && this.ws.close((err) => {
|
if (this.ws) {
|
||||||
log('ws server close failed!', err);
|
this.ws.close((err) => {
|
||||||
});
|
if (err) log('ws server close failed!', err);
|
||||||
this.ws = null;
|
});
|
||||||
|
this.ws = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restart(port: number) {
|
restart(port: number) {
|
||||||
|
@@ -1,36 +0,0 @@
|
|||||||
import { sleep } from '@/common/utils/helper';
|
|
||||||
import { logError } from './log';
|
|
||||||
type AsyncQueueTask = (() => void) | (()=>Promise<void>);
|
|
||||||
|
|
||||||
|
|
||||||
export class AsyncQueue {
|
|
||||||
private tasks: (AsyncQueueTask)[] = [];
|
|
||||||
|
|
||||||
public addTask(task: AsyncQueueTask) {
|
|
||||||
this.tasks.push(task);
|
|
||||||
// console.log('addTask', this.tasks.length);
|
|
||||||
if (this.tasks.length === 1) {
|
|
||||||
this.runQueue().then().catch(()=>{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async runQueue() {
|
|
||||||
// console.log('runQueue', this.tasks.length);
|
|
||||||
while (this.tasks.length > 0) {
|
|
||||||
const task = this.tasks[0];
|
|
||||||
// console.log('typeof task', typeof task);
|
|
||||||
try {
|
|
||||||
const taskRet = task();
|
|
||||||
// console.log('type of taskRet', typeof taskRet, taskRet);
|
|
||||||
if (taskRet instanceof Promise) {
|
|
||||||
await taskRet;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// console.error(e);
|
|
||||||
logError(e);
|
|
||||||
}
|
|
||||||
this.tasks.shift();
|
|
||||||
await sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|||||||
import { log, logDebug, logError } from '@/common/utils/log';
|
import { log, logDebug, logError } from '@/common/utils/log';
|
||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -12,8 +13,9 @@ const configDir = path.resolve(__dirname, 'config');
|
|||||||
fs.mkdirSync(configDir, { recursive: true });
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
|
|
||||||
|
|
||||||
export class ConfigBase<T>{
|
export class ConfigBase<T> {
|
||||||
|
public name: string = 'default_config'
|
||||||
|
private pathName: string | null = null // 本次读取的文件路径
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,19 +24,28 @@ export class ConfigBase<T>{
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigDir(){
|
getConfigDir() {
|
||||||
const configDir = path.resolve(__dirname, 'config');
|
const configDir = path.resolve(__dirname, 'config');
|
||||||
fs.mkdirSync(configDir, { recursive: true });
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
return configDir;
|
return configDir;
|
||||||
}
|
}
|
||||||
getConfigPath(): string {
|
getConfigPath(pathName: string | null): string {
|
||||||
throw new Error('Method not implemented.');
|
const suffix = pathName ? `_${pathName}` : ''
|
||||||
|
const filename = `${this.name}${suffix}.json`
|
||||||
|
return path.join(this.getConfigDir(), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
read() {
|
read() {
|
||||||
const configPath = this.getConfigPath();
|
// 尝试加载当前账号配置
|
||||||
|
if (this.read_from_file(selfInfo.uin, false)) return this
|
||||||
|
// 尝试加载默认配置
|
||||||
|
return this.read_from_file('', true)
|
||||||
|
}
|
||||||
|
read_from_file(pathName: string, createIfNotExist: boolean) {
|
||||||
|
const configPath = this.getConfigPath(pathName);
|
||||||
if (!fs.existsSync(configPath)) {
|
if (!fs.existsSync(configPath)) {
|
||||||
try{
|
if (!createIfNotExist) return null
|
||||||
|
this.pathName = pathName // 记录有效的设置文件
|
||||||
|
try {
|
||||||
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
||||||
log(`配置文件${configPath}已创建\n如果修改此文件后需要重启 NapCat 生效`);
|
log(`配置文件${configPath}已创建\n如果修改此文件后需要重启 NapCat 生效`);
|
||||||
}
|
}
|
||||||
@@ -43,6 +54,7 @@ export class ConfigBase<T>{
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
const data = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||||
logDebug(`配置文件${configPath}已加载`, data);
|
logDebug(`配置文件${configPath}已加载`, data);
|
||||||
@@ -61,9 +73,13 @@ export class ConfigBase<T>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
save(config: T) {
|
save(config: T, overwrite: boolean = false) {
|
||||||
Object.assign(this, config);
|
Object.assign(this, config);
|
||||||
const configPath = this.getConfigPath();
|
if (overwrite) {
|
||||||
|
// 用户要求强制写入,则变更当前文件为目标文件
|
||||||
|
this.pathName = `${selfInfo.uin}`
|
||||||
|
}
|
||||||
|
const configPath = this.getConfigPath(this.pathName);
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { NodeIKernelMsgListener } from '@/core';
|
|
||||||
import { NodeIQQNTWrapperSession } from '@/core/wrapper';
|
import { NodeIQQNTWrapperSession } from '@/core/wrapper';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
@@ -6,6 +5,7 @@ interface Internal_MapKey {
|
|||||||
timeout: number,
|
timeout: number,
|
||||||
createtime: number,
|
createtime: number,
|
||||||
func: (...arg: any[]) => any,
|
func: (...arg: any[]) => any,
|
||||||
|
checker: ((...args: any[]) => boolean) | undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ListenerClassBase {
|
export class ListenerClassBase {
|
||||||
@@ -84,17 +84,19 @@ export class NTEventWrapper {
|
|||||||
}
|
}
|
||||||
//统一回调清理事件
|
//统一回调清理事件
|
||||||
async DispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
async DispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
|
||||||
//console.log(ListenerMainName, this.EventTask.get(ListenerMainName), ListenerSubName, this.EventTask.get(ListenerMainName)?.get(ListenerSubName));
|
//console.log(ListenerMainName, ListenerSubName, ...args,this.EventTask.get(ListenerMainName)?.get(ListenerSubName));
|
||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.forEach((task, uuid) => {
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.forEach((task, uuid) => {
|
||||||
//console.log(task.func, uuid, task.createtime, task.timeout);
|
//console.log(task.func, uuid, task.createtime, task.timeout);
|
||||||
if (task.createtime + task.timeout < Date.now()) {
|
if (task.createtime + task.timeout < Date.now()) {
|
||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
task.func(...args);
|
if (task.checker && task.checker(...args)) {
|
||||||
|
task.func(...args);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any>,>(EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
async CallNoListenerEvent<EventType extends (...args: any[]) => Promise<any>>(EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
|
||||||
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
|
||||||
const EventFunc = this.CreatEventFunction<EventType>(EventName);
|
const EventFunc = this.CreatEventFunction<EventType>(EventName);
|
||||||
let complete = false;
|
let complete = false;
|
||||||
@@ -108,14 +110,15 @@ export class NTEventWrapper {
|
|||||||
resolve(retData);
|
resolve(retData);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async CallNormalEvent<EventType extends (...args: any[]) => Promise<any>, ListenerType extends (...args: any[]) => void>(EventName = '', ListenerName = '', waitTimes = 1, timeout: number = 3000, ...args: Parameters<EventType>) {
|
async CallNormalEvent<EventType extends (...args: any[]) => Promise<any>, ListenerType extends (...args: any[]) => void>
|
||||||
|
(EventName = '', ListenerName = '', waitTimes = 1, timeout: number = 3000, checker: (...args: Parameters<ListenerType>) => boolean, ...args: Parameters<EventType>) {
|
||||||
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
|
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
|
||||||
const id = randomUUID();
|
const id = randomUUID();
|
||||||
let complete = 0;
|
let complete = 0;
|
||||||
let retData: Parameters<ListenerType> | undefined = undefined;
|
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||||
let retEvent: any = {};
|
let retEvent: any = {};
|
||||||
const databack = () => {
|
const databack = () => {
|
||||||
if (complete < waitTimes) {
|
if (complete == 0) {
|
||||||
reject(new Error('NTEvent EventName:' + EventName + ' ListenerName:' + ListenerName + ' timeout'));
|
reject(new Error('NTEvent EventName:' + EventName + ' ListenerName:' + ListenerName + ' timeout'));
|
||||||
} else {
|
} else {
|
||||||
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
||||||
@@ -129,6 +132,7 @@ export class NTEventWrapper {
|
|||||||
const eventCallbak = {
|
const eventCallbak = {
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
createtime: Date.now(),
|
createtime: Date.now(),
|
||||||
|
checker: checker,
|
||||||
func: (...args: any[]) => {
|
func: (...args: any[]) => {
|
||||||
complete++;
|
complete++;
|
||||||
//console.log('func', ...args);
|
//console.log('func', ...args);
|
||||||
|
@@ -1,145 +0,0 @@
|
|||||||
import { logError, logDebug } from '@/common/utils/log';
|
|
||||||
|
|
||||||
type group_id = number;
|
|
||||||
type user_id = number;
|
|
||||||
|
|
||||||
class cacheNode<T> {
|
|
||||||
value: T;
|
|
||||||
groupId: group_id;
|
|
||||||
userId: user_id;
|
|
||||||
prev: cacheNode<T> | null;
|
|
||||||
next: cacheNode<T> | null;
|
|
||||||
timestamp: number;
|
|
||||||
|
|
||||||
constructor(groupId: group_id, userId: user_id, value: T) {
|
|
||||||
this.groupId = groupId;
|
|
||||||
this.userId = userId;
|
|
||||||
this.value = value;
|
|
||||||
this.prev = null;
|
|
||||||
this.next = null;
|
|
||||||
this.timestamp = Date.now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type cache<T> = { [key: group_id]: { [key: user_id]: cacheNode<T> } };
|
|
||||||
class LRU<T> {
|
|
||||||
private maxAge: number;
|
|
||||||
private maxSize: number;
|
|
||||||
private currentSize: number;
|
|
||||||
private cache: cache<T>;
|
|
||||||
private head: cacheNode<T> | null = null;
|
|
||||||
private tail: cacheNode<T> | null = null;
|
|
||||||
private onFuncs: ((node: cacheNode<T>) => void)[] = [];
|
|
||||||
|
|
||||||
constructor(maxAge: number = 2e4, maxSize: number = 5e3) {
|
|
||||||
this.maxAge = maxAge;
|
|
||||||
this.maxSize = maxSize;
|
|
||||||
this.cache = Object.create(null);
|
|
||||||
this.currentSize = 0;
|
|
||||||
|
|
||||||
if (maxSize == 0) return;
|
|
||||||
setInterval(() => this.removeExpired(), this.maxAge);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除LRU节点
|
|
||||||
private removeLRUNode(node: cacheNode<T>) {
|
|
||||||
logDebug(
|
|
||||||
'removeLRUNode',
|
|
||||||
node.groupId,
|
|
||||||
node.userId,
|
|
||||||
node.value,
|
|
||||||
this.currentSize
|
|
||||||
);
|
|
||||||
node.prev = node.next = null;
|
|
||||||
delete this.cache[node.groupId][node.userId];
|
|
||||||
this.removeNode(node);
|
|
||||||
this.onFuncs.forEach((func) => func(node));
|
|
||||||
this.currentSize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
public on(func: (node: cacheNode<T>) => void) {
|
|
||||||
this.onFuncs.push(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
private removeExpired() {
|
|
||||||
const now = Date.now();
|
|
||||||
let current = this.tail;
|
|
||||||
const nodesToRemove: cacheNode<T>[] = [];
|
|
||||||
let removedCount = 0;
|
|
||||||
|
|
||||||
// 收集需要删除的节点
|
|
||||||
while (current && now - current.timestamp > this.maxAge) {
|
|
||||||
nodesToRemove.push(current);
|
|
||||||
current = current.prev;
|
|
||||||
removedCount++;
|
|
||||||
if (removedCount >= 100) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新链表指向
|
|
||||||
if (nodesToRemove.length > 0) {
|
|
||||||
const newTail = nodesToRemove[nodesToRemove.length - 1].prev;
|
|
||||||
if (newTail) {
|
|
||||||
newTail.next = null;
|
|
||||||
} else {
|
|
||||||
this.head = null;
|
|
||||||
}
|
|
||||||
this.tail = newTail;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesToRemove.forEach((node) => {
|
|
||||||
node.prev = node.next = null;
|
|
||||||
delete this.cache[node.groupId][node.userId];
|
|
||||||
|
|
||||||
this.currentSize--;
|
|
||||||
this.onFuncs.forEach((func) => func(node));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private addNode(node: cacheNode<T>) {
|
|
||||||
node.next = this.head;
|
|
||||||
if (this.head) this.head.prev = node;
|
|
||||||
if (!this.tail) this.tail = node;
|
|
||||||
this.head = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private removeNode(node: cacheNode<T>) {
|
|
||||||
if (node.prev) node.prev.next = node.next;
|
|
||||||
if (node.next) node.next.prev = node.prev;
|
|
||||||
if (node === this.head) this.head = node.next;
|
|
||||||
if (node === this.tail) this.tail = node.prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
private moveToHead(node: cacheNode<T>) {
|
|
||||||
if (this.head === node) return;
|
|
||||||
|
|
||||||
this.removeNode(node);
|
|
||||||
this.addNode(node);
|
|
||||||
node.prev = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public set(groupId: group_id, userId: user_id, value: T) {
|
|
||||||
if (!this.cache[groupId]) {
|
|
||||||
this.cache[groupId] = Object.create(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupObject = this.cache[groupId];
|
|
||||||
|
|
||||||
if (groupObject[userId]) {
|
|
||||||
const node = groupObject[userId];
|
|
||||||
node.value = value;
|
|
||||||
node.timestamp = Date.now();
|
|
||||||
this.moveToHead(node);
|
|
||||||
} else {
|
|
||||||
const node = new cacheNode(groupId, userId, value);
|
|
||||||
groupObject[userId] = node;
|
|
||||||
this.currentSize++;
|
|
||||||
this.addNode(node);
|
|
||||||
if (this.currentSize > this.maxSize) {
|
|
||||||
const tail = this.tail!;
|
|
||||||
this.removeLRUNode(tail);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LRU;
|
|
@@ -1,20 +1,40 @@
|
|||||||
import crypto from 'crypto';
|
import { Peer } from '@/core';
|
||||||
|
import crypto, { randomInt, randomUUID } from 'crypto';
|
||||||
|
import { logError } from './log';
|
||||||
|
|
||||||
class LimitedHashTable<K, V> {
|
class LimitedHashTable<K, V> {
|
||||||
private keyToValue: Map<K, V> = new Map();
|
private keyToValue: Map<K, V> = new Map();
|
||||||
private valueToKey: Map<V, K> = new Map();
|
private valueToKey: Map<V, K> = new Map();
|
||||||
private maxSize: number;
|
private maxSize: number;
|
||||||
private KeyQueneList: K[] = [];
|
|
||||||
private ValueQueneList: V[] = [];
|
|
||||||
constructor(maxSize: number) {
|
constructor(maxSize: number) {
|
||||||
this.maxSize = maxSize;
|
this.maxSize = maxSize;
|
||||||
}
|
}
|
||||||
|
resize(count: number) {
|
||||||
|
this.maxSize = count;
|
||||||
|
}
|
||||||
|
|
||||||
set(key: K, value: V): void {
|
set(key: K, value: V): void {
|
||||||
|
const isExist = this.keyToValue.get(key);
|
||||||
|
if (isExist && isExist === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.keyToValue.set(key, value);
|
this.keyToValue.set(key, value);
|
||||||
this.valueToKey.set(value, key);
|
this.valueToKey.set(value, key);
|
||||||
if (this.KeyQueneList.length >= this.maxSize || this.ValueQueneList.length >= this.maxSize) {
|
while (this.keyToValue.size !== this.valueToKey.size) {
|
||||||
this.KeyQueneList.shift();
|
console.log('keyToValue.size !== valueToKey.size Error Atom');
|
||||||
this.ValueQueneList.shift();
|
this.keyToValue.clear();
|
||||||
|
this.valueToKey.clear();
|
||||||
|
}
|
||||||
|
// console.log('---------------');
|
||||||
|
// console.log(this.keyToValue);
|
||||||
|
// console.log(this.valueToKey);
|
||||||
|
// console.log('---------------');
|
||||||
|
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
|
||||||
|
//console.log(this.keyToValue.size > this.maxSize, this.valueToKey.size > this.maxSize);
|
||||||
|
const oldestKey = this.keyToValue.keys().next().value;
|
||||||
|
this.valueToKey.delete(this.keyToValue.get(oldestKey)!);
|
||||||
|
this.keyToValue.delete(oldestKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +46,15 @@ class LimitedHashTable<K, V> {
|
|||||||
return this.valueToKey.get(value);
|
return this.valueToKey.get(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(key: K): void {
|
deleteByValue(value: V): void {
|
||||||
|
const key = this.valueToKey.get(value);
|
||||||
|
if (key !== undefined) {
|
||||||
|
this.keyToValue.delete(key);
|
||||||
|
this.valueToKey.delete(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteByKey(key: K): void {
|
||||||
const value = this.keyToValue.get(key);
|
const value = this.keyToValue.get(key);
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
this.keyToValue.delete(key);
|
this.keyToValue.delete(key);
|
||||||
@@ -36,18 +64,53 @@ class LimitedHashTable<K, V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MessageUniqueWrapper {
|
class MessageUniqueWrapper {
|
||||||
private msgIdMap: LimitedHashTable<number, string> = new LimitedHashTable(1000);
|
private msgDataMap: LimitedHashTable<string, number>;
|
||||||
createMsg(MsgId: string) {
|
private msgIdMap: LimitedHashTable<string, number>;
|
||||||
const ShortId = parseInt(crypto.createHash('sha1').update('2345').digest('hex').slice(0, 8), 16);
|
constructor(maxMap: number = 1000) {
|
||||||
this.msgIdMap.set(ShortId, MsgId);
|
this.msgIdMap = new LimitedHashTable<string, number>(maxMap);
|
||||||
return ShortId;
|
this.msgDataMap = new LimitedHashTable<string, number>(maxMap);
|
||||||
}
|
}
|
||||||
getMsgIdByShortId(ShortId: number) {
|
|
||||||
return this.msgIdMap.getValue(ShortId);
|
createMsg(peer: Peer, msgId: string): number | undefined {
|
||||||
|
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||||
|
const hash = crypto.createHash('sha1').update(key);
|
||||||
|
const shortId = parseInt(hash.digest('hex').slice(0, 8), 16);
|
||||||
|
const isExist = this.msgIdMap.getKey(shortId);
|
||||||
|
//console.log(`${peer.peerUid} ${msgId} ------- ${shortId}`);
|
||||||
|
if (isExist && isExist === msgId) {
|
||||||
|
return shortId;
|
||||||
|
}
|
||||||
|
this.msgIdMap.set(msgId, shortId);
|
||||||
|
this.msgDataMap.set(key, shortId);
|
||||||
|
return shortId;
|
||||||
}
|
}
|
||||||
getShortIdByMsgId(MsgId: string) {
|
|
||||||
return this.msgIdMap.getKey(MsgId);
|
getMsgIdAndPeerByShortId(shortId: number): { MsgId: string; Peer: Peer } | undefined {
|
||||||
|
const data = this.msgDataMap.getKey(shortId);
|
||||||
|
if (data) {
|
||||||
|
const [msgId, chatTypeStr, peerUid] = data.split('|');
|
||||||
|
const peer: Peer = {
|
||||||
|
chatType: parseInt(chatTypeStr),
|
||||||
|
peerUid,
|
||||||
|
guildId: '',
|
||||||
|
};
|
||||||
|
return { MsgId: msgId, Peer: peer };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShortIdByMsgId(msgId: string): number | undefined {
|
||||||
|
return this.msgIdMap.getValue(msgId);
|
||||||
|
}
|
||||||
|
getPeerByMsgId(msgId: string) {
|
||||||
|
const shortId = this.msgIdMap.getValue(msgId);
|
||||||
|
if (!shortId) return undefined;
|
||||||
|
return this.getMsgIdAndPeerByShortId(shortId);
|
||||||
|
}
|
||||||
|
resize(maxSize: number): void {
|
||||||
|
this.msgIdMap.resize(maxSize);
|
||||||
|
this.msgDataMap.resize(maxSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MessageUnique = new MessageUniqueWrapper();
|
export const MessageUnique: MessageUniqueWrapper = new MessageUniqueWrapper();
|
@@ -38,11 +38,11 @@ type QQVersionConfigInfo = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
let _qqVersionConfigInfo: QQVersionConfigInfo = {
|
||||||
'baseVersion': '9.9.11-24568',
|
'baseVersion': '9.9.12-25765',
|
||||||
'curVersion': '9.9.11-24568',
|
'curVersion': '9.9.12-25765',
|
||||||
'prevVersion': '',
|
'prevVersion': '',
|
||||||
'onErrorVersions': [],
|
'onErrorVersions': [],
|
||||||
'buildId': '24568'
|
'buildId': '25765'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fs.existsSync(configVersionInfoPath)) {
|
if (fs.existsSync(configVersionInfoPath)) {
|
||||||
@@ -55,23 +55,23 @@ if (fs.existsSync(configVersionInfoPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
export const qqVersionConfigInfo: QQVersionConfigInfo = _qqVersionConfigInfo;
|
||||||
//V1_WIN_NQ_9.9.11_24568_GW_B
|
//V1_WIN_NQ_9.9.12_25765_GW_B
|
||||||
export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toString());
|
export const qqPkgInfo: QQPkgInfo = JSON.parse(fs.readFileSync(pkgInfoPath).toString());
|
||||||
// platform_type: 3,
|
// platform_type: 3,
|
||||||
// app_type: 4,
|
// app_type: 4,
|
||||||
// app_version: '9.9.9-23159',
|
// app_version: '9.9.12-25765',
|
||||||
// qua: 'V1_WIN_NQ_9.9.9_23159_GW_B',
|
// qua: 'V1_WIN_NQ_9.9.12_25765_GW_B',
|
||||||
// appid: '537213764',
|
// appid: '537234702',
|
||||||
// platVer: '10.0.26100',
|
// platVer: '10.0.26100',
|
||||||
// clientVer: '9.9.9-23159',
|
// clientVer: '9.9.9-25765',
|
||||||
|
|
||||||
// Linux
|
// Linux
|
||||||
// app_version: '3.2.9-24568',
|
// app_version: '3.2.9-25765',
|
||||||
// qua: 'V1_LNX_NQ_3.2.9_24568_GW_B',
|
// qua: 'V1_LNX_NQ_3.2.10_25765_GW_B',
|
||||||
|
|
||||||
let _appid: string = '537226369'; // 默认为 Windows 平台的 appid
|
let _appid: string = '537234702'; // 默认为 Windows 平台的 appid
|
||||||
if (systemPlatform === 'linux') {
|
if (systemPlatform === 'linux') {
|
||||||
_appid = '537226441';
|
_appid = '537234773';
|
||||||
}
|
}
|
||||||
// todo: mac 平台的 appid
|
// todo: mac 平台的 appid
|
||||||
export const appid = _appid;
|
export const appid = _appid;
|
@@ -1,9 +1,9 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { encode, getDuration, getWavFileInfo, isWav } from 'silk-wasm';
|
import { encode, getDuration, getWavFileInfo, isWav, isSilk } from 'silk-wasm';
|
||||||
import fsPromise from 'fs/promises';
|
import fsPromise from 'fs/promises';
|
||||||
import { log, logError } from './log';
|
import { log, logError } from './log';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
import { getTempDir } from '@/common/utils/file';
|
import { getTempDir } from '@/common/utils/file';
|
||||||
|
|
||||||
@@ -63,10 +63,11 @@ export async function encodeSilk(filePath: string) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pttPath = path.join(TEMP_DIR, uuidv4());
|
const file = await fsPromise.readFile(filePath);
|
||||||
if (getFileHeader(filePath) !== '02232153494c4b') {
|
const pttPath = path.join(TEMP_DIR, randomUUID());
|
||||||
|
if (!isSilk(file)) {
|
||||||
log(`语音文件${filePath}需要转换成silk`);
|
log(`语音文件${filePath}需要转换成silk`);
|
||||||
const _isWav = await isWavFile(filePath);
|
const _isWav = isWav(file);
|
||||||
const pcmPath = pttPath + '.pcm';
|
const pcmPath = pttPath + '.pcm';
|
||||||
let sampleRate = 0;
|
let sampleRate = 0;
|
||||||
const convert = () => {
|
const convert = () => {
|
||||||
@@ -96,7 +97,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
if (!_isWav) {
|
if (!_isWav) {
|
||||||
input = await convert();
|
input = await convert();
|
||||||
} else {
|
} else {
|
||||||
input = fs.readFileSync(filePath);
|
input = file;
|
||||||
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
const allowSampleRate = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
||||||
const { fmt } = getWavFileInfo(input);
|
const { fmt } = getWavFileInfo(input);
|
||||||
// log(`wav文件信息`, fmt)
|
// log(`wav文件信息`, fmt)
|
||||||
@@ -113,7 +114,7 @@ export async function encodeSilk(filePath: string) {
|
|||||||
duration: silk.duration / 1000
|
duration: silk.duration / 1000
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const silk = fs.readFileSync(filePath);
|
const silk = file;
|
||||||
let duration = 0;
|
let duration = 0;
|
||||||
try {
|
try {
|
||||||
duration = getDuration(silk) / 1000;
|
duration = getDuration(silk) / 1000;
|
||||||
|
@@ -1,449 +0,0 @@
|
|||||||
import { ElementType, FileElement, PicElement, PttElement, RawMessage, VideoElement } from '../../core/src/entities';
|
|
||||||
|
|
||||||
import sqlite3 from 'sqlite3';
|
|
||||||
import { log, logDebug, logError } from '@/common/utils/log';
|
|
||||||
import { NTQQMsgApi } from '@/core';
|
|
||||||
import LRU from '@/common/utils/LRUCache';
|
|
||||||
|
|
||||||
export interface IRember {
|
|
||||||
last_sent_time: number;
|
|
||||||
join_time: number;
|
|
||||||
user_id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type DBMsg = {
|
|
||||||
id: number,
|
|
||||||
shortId: number,
|
|
||||||
longId: string,
|
|
||||||
seq: number,
|
|
||||||
peerUid: string,
|
|
||||||
chatType: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
type DBFile = {
|
|
||||||
name: string; // 文件名
|
|
||||||
path: string;
|
|
||||||
url: string;
|
|
||||||
size: number;
|
|
||||||
uuid: string;
|
|
||||||
msgId: string;
|
|
||||||
elementId: string;
|
|
||||||
element: PicElement | VideoElement | FileElement | PttElement;
|
|
||||||
elementType: ElementType.PIC | ElementType.VIDEO | ElementType.FILE | ElementType.PTT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DBUtilBase {
|
|
||||||
protected db: sqlite3.Database | undefined;
|
|
||||||
|
|
||||||
async init(dbPath: string) {
|
|
||||||
if (this.db) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
this.db = new sqlite3.Database(dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('Could not connect to database', err);
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.createTable();
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createTable() {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.db?.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DBUtil extends DBUtilBase {
|
|
||||||
private msgCache: Map<string | number, RawMessage> = new Map<string | number, RawMessage>();
|
|
||||||
private globalMsgShortId = -2147483640;
|
|
||||||
private groupIds: number[] = [];
|
|
||||||
private LURCache = new LRU<number>();
|
|
||||||
private LastSentCache = new (class {
|
|
||||||
private cache: { gid: number; uid: number }[] = [];
|
|
||||||
private maxSize: number;
|
|
||||||
|
|
||||||
constructor(maxSize: number = 5000) {
|
|
||||||
this.maxSize = maxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
get(gid: number, uid: number): boolean {
|
|
||||||
const exists = this.cache.some(
|
|
||||||
(entry) => entry.gid === gid && entry.uid === uid
|
|
||||||
);
|
|
||||||
if (!exists) {
|
|
||||||
this.cache.push({ gid, uid });
|
|
||||||
if (this.cache.length > this.maxSize) {
|
|
||||||
this.cache.shift();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return exists;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const interval = 1000 * 60 * 10; // 10分钟清理一次缓存
|
|
||||||
setInterval(() => {
|
|
||||||
logDebug('清理消息缓存');
|
|
||||||
this.msgCache.forEach((msg, key) => {
|
|
||||||
if ((Date.now() - parseInt(msg.msgTime) * 1000) > interval) {
|
|
||||||
this.msgCache.delete(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
async init(dbPath: string) {
|
|
||||||
await super.init(dbPath);
|
|
||||||
this.globalMsgShortId = await this.getCurrentMaxShortId();
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化群缓存列表
|
|
||||||
this.db!.serialize(() => {
|
|
||||||
const sql = 'SELECT * FROM sqlite_master WHERE type=\'table\'';
|
|
||||||
this.db!.all(sql, [], (err, rows: { name: string }[]) => {
|
|
||||||
if (err) return logError(err);
|
|
||||||
rows.forEach((row) => this.groupIds.push(parseInt(row.name)));
|
|
||||||
//logDebug(`已加载 ${groupIds.length} 个群`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
this.LURCache.on(async (node) => {
|
|
||||||
const { value: time, groupId, userId } = node;
|
|
||||||
|
|
||||||
logDebug('插入发言时间', userId, groupId);
|
|
||||||
await this.createGroupInfoTimeTableIfNotExist(groupId);
|
|
||||||
|
|
||||||
const method = await this.getDataSetMethod(groupId, userId);
|
|
||||||
logDebug('插入发言时间方法判断', userId, groupId, method);
|
|
||||||
|
|
||||||
const sql =
|
|
||||||
method == 'update'
|
|
||||||
? `UPDATE "${groupId}" SET last_sent_time = ? WHERE user_id = ?`
|
|
||||||
: `INSERT INTO "${groupId}" (last_sent_time, user_id) VALUES (?, ?)`;
|
|
||||||
|
|
||||||
this.db!.all(sql, [time, userId], (err) => {
|
|
||||||
if (err) {
|
|
||||||
return logError('插入/更新发言时间失败', userId, groupId);
|
|
||||||
}
|
|
||||||
logDebug('插入/更新发言时间成功', userId, groupId);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async getDataSetMethod(groupId: number, userId: number) {
|
|
||||||
// 缓存记录
|
|
||||||
if (this.LastSentCache.get(groupId, userId)) {
|
|
||||||
logDebug('缓存命中', userId, groupId);
|
|
||||||
return 'update';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据库判断
|
|
||||||
return new Promise<'insert' | 'update'>((resolve, reject) => {
|
|
||||||
this.db!.all(
|
|
||||||
`SELECT * FROM "${groupId}" WHERE user_id = ?`,
|
|
||||||
[userId],
|
|
||||||
(err, rows) => {
|
|
||||||
if (err) {
|
|
||||||
logError('查询发言时间存在失败', userId, groupId, err);
|
|
||||||
return logError('插入发言时间失败', userId, groupId, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows.length === 0) {
|
|
||||||
logDebug('查询发言时间不存在', userId, groupId);
|
|
||||||
return resolve('insert');
|
|
||||||
}
|
|
||||||
|
|
||||||
logDebug('查询发言时间存在', userId, groupId);
|
|
||||||
resolve('update');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async createGroupInfoTimeTableIfNotExist(groupId: number) {
|
|
||||||
const createTableSQL = (groupId: number) =>
|
|
||||||
`CREATE TABLE IF NOT EXISTS "${groupId}" (
|
|
||||||
user_id INTEGER,
|
|
||||||
last_sent_time INTEGER,
|
|
||||||
join_time INTEGER,
|
|
||||||
PRIMARY KEY (user_id)
|
|
||||||
);`;
|
|
||||||
|
|
||||||
if (this.groupIds.includes(groupId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const sql = createTableSQL(groupId);
|
|
||||||
this.db!.all(sql, (err) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.groupIds.push(groupId);
|
|
||||||
resolve(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
protected createTable() {
|
|
||||||
// 消息记录
|
|
||||||
const createTableSQL = `
|
|
||||||
CREATE TABLE IF NOT EXISTS msgs (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
shortId INTEGER NOT NULL UNIQUE,
|
|
||||||
longId TEXT NOT NULL UNIQUE,
|
|
||||||
seq INTEGER NOT NULL,
|
|
||||||
peerUid TEXT NOT NULL,
|
|
||||||
chatType INTEGER NOT NULL
|
|
||||||
)`;
|
|
||||||
this.db!.run(createTableSQL, function (err) {
|
|
||||||
if (err) {
|
|
||||||
logError('Could not create table msgs', err.stack);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 文件缓存
|
|
||||||
const createFileTableSQL = `
|
|
||||||
CREATE TABLE IF NOT EXISTS files (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
path TEXT NOT NULL,
|
|
||||||
url TEXT,
|
|
||||||
size INTEGER NOT NULL,
|
|
||||||
uuid TEXT,
|
|
||||||
elementType INTEGER,
|
|
||||||
element TEXT NOT NULL,
|
|
||||||
elementId TEXT NOT NULL,
|
|
||||||
msgId TEXT NOT NULL
|
|
||||||
)`;
|
|
||||||
this.db!.run(createFileTableSQL, function (err) {
|
|
||||||
if (err) {
|
|
||||||
logError('Could not create table files', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getCurrentMaxShortId() {
|
|
||||||
return new Promise<number>((resolve, reject) => {
|
|
||||||
this.db!.get('SELECT MAX(shortId) as maxId FROM msgs', (err, row: { maxId: number }) => {
|
|
||||||
if (err) {
|
|
||||||
logDebug('Could not get max short id, Use default -2147483640', err);
|
|
||||||
return resolve(-2147483640);
|
|
||||||
}
|
|
||||||
logDebug('数据库中消息最大短id', row?.maxId);
|
|
||||||
resolve(row?.maxId ?? -2147483640);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getMsg(query: string, params: any[]) {
|
|
||||||
const stmt = this.db!.prepare(query);
|
|
||||||
return new Promise<RawMessage | null>((resolve, reject) => {
|
|
||||||
stmt.get(...params, (err: any, row: DBMsg) => {
|
|
||||||
// log("getMsg", row, err);
|
|
||||||
if (err) {
|
|
||||||
logError('Could not get msg', err, query, params);
|
|
||||||
return resolve(null);
|
|
||||||
}
|
|
||||||
if (!row) {
|
|
||||||
// logDebug('不存在数据库中的消息,不进行处理', query, params);
|
|
||||||
resolve(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const msgId = row.longId;
|
|
||||||
NTQQMsgApi.getMsgsByMsgId({ peerUid: row.peerUid, chatType: row.chatType }, [msgId]).then(res => {
|
|
||||||
const msg = res.msgList[0];
|
|
||||||
if (!msg) {
|
|
||||||
resolve(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
msg.id = row.shortId;
|
|
||||||
resolve(msg);
|
|
||||||
}).catch(e => {
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgByShortId(shortId: number): Promise<RawMessage | null> {
|
|
||||||
if (this.msgCache.has(shortId)) {
|
|
||||||
return this.msgCache.get(shortId)!;
|
|
||||||
}
|
|
||||||
const getStmt = 'SELECT * FROM msgs WHERE shortId = ?';
|
|
||||||
return this.getMsg(getStmt, [shortId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgByLongId(longId: string): Promise<RawMessage | null> {
|
|
||||||
if (this.msgCache.has(longId)) {
|
|
||||||
return this.msgCache.get(longId)!;
|
|
||||||
}
|
|
||||||
return this.getMsg('SELECT * FROM msgs WHERE longId = ?', [longId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMsgBySeq(peerUid: string, seq: string): Promise<RawMessage | null> {
|
|
||||||
const stmt = 'SELECT * FROM msgs WHERE peerUid = ? AND seq = ?';
|
|
||||||
return this.getMsg(stmt, [peerUid, seq]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async addMsg(msg: RawMessage, update = true): Promise<number> {
|
|
||||||
const existMsg = await this.getMsgByLongId(msg.msgId);
|
|
||||||
if (existMsg) {
|
|
||||||
// logDebug('消息已存在,更新数据库', msg.msgId);
|
|
||||||
if (update) this.updateMsg(msg).then();
|
|
||||||
return existMsg.id!;
|
|
||||||
}
|
|
||||||
const stmt = this.db!.prepare('INSERT INTO msgs (shortId, longId, seq, peerUid, chatType) VALUES (?, ?, ?, ?, ?)');
|
|
||||||
// const runAsync = promisify(stmt.run.bind(stmt));
|
|
||||||
const shortId = ++this.globalMsgShortId;
|
|
||||||
msg.id = shortId;
|
|
||||||
//logDebug(`记录消息到数据库, 消息长id: ${msg.msgId}, 短id: ${msg.id}`);
|
|
||||||
this.msgCache.set(shortId, msg);
|
|
||||||
this.msgCache.set(msg.msgId, msg);
|
|
||||||
stmt.run(this.globalMsgShortId, msg.msgId, msg.msgSeq.toString(), msg.peerUid, msg.chatType, (err: any) => {
|
|
||||||
if (err) {
|
|
||||||
if (err.errno === 19) {
|
|
||||||
this.getMsgByLongId(msg.msgId).then((msg: RawMessage | null) => {
|
|
||||||
if (msg) {
|
|
||||||
this.msgCache.set(shortId, msg);
|
|
||||||
this.msgCache.set(msg.msgId, msg);
|
|
||||||
// logDebug('获取消息短id成功', msg.id);
|
|
||||||
} else {
|
|
||||||
logError('db could not get msg by long id', err);
|
|
||||||
}
|
|
||||||
}).catch(e => logError('db getMsgByLongId error', e));
|
|
||||||
} else {
|
|
||||||
logError('db could not add msg', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return shortId;
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateMsg(msg: RawMessage) {
|
|
||||||
const existMsg = this.msgCache.get(msg.msgId);
|
|
||||||
if (existMsg) {
|
|
||||||
Object.assign(existMsg, msg);
|
|
||||||
}
|
|
||||||
//logDebug(`更新消息, shortId:${msg.id}, seq: ${msg.msgSeq}, msgId: ${msg.msgId}`);
|
|
||||||
const stmt = this.db!.prepare('UPDATE msgs SET seq=? WHERE longId=?');
|
|
||||||
stmt.run(msg.msgSeq, msg.msgId, (err: any) => {
|
|
||||||
if (err) {
|
|
||||||
logError('updateMsg db error', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async addFileCache(file: DBFile) {
|
|
||||||
const stmt = this.db!.prepare('INSERT INTO files (name, path, url, size, uuid, elementType ,element, elementId, msgId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
stmt.run(file.name, file.path, file.url, file.size, file.uuid,
|
|
||||||
file.elementType,
|
|
||||||
JSON.stringify(file.element),
|
|
||||||
file.elementId,
|
|
||||||
file.msgId,
|
|
||||||
function (err: any) {
|
|
||||||
if (err) {
|
|
||||||
logError('db could not add file', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getFileCache(query: string, params: any[]) {
|
|
||||||
const stmt = this.db!.prepare(query);
|
|
||||||
return new Promise<DBFile | null>((resolve, reject) => {
|
|
||||||
stmt.get(...params, (err: any, row: DBFile & { element: string }) => {
|
|
||||||
if (err) {
|
|
||||||
logError('db could not get file cache', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
if (row) {
|
|
||||||
row.element = JSON.parse(row.element);
|
|
||||||
}
|
|
||||||
resolve(row);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFileCacheByName(name: string): Promise<DBFile | null> {
|
|
||||||
return this.getFileCache('SELECT * FROM files WHERE name = ?', [name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFileCacheByUuid(uuid: string): Promise<DBFile | null> {
|
|
||||||
return this.getFileCache('SELECT * FROM files WHERE uuid = ?', [uuid]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: 是否所有的文件都有uuid?语音消息有没有uuid?
|
|
||||||
async updateFileCache(file: DBFile) {
|
|
||||||
const stmt = this.db!.prepare('UPDATE files SET path = ?, url = ? WHERE uuid = ?');
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
stmt.run(file.path, file.url, file.uuid, function (err: any) {
|
|
||||||
if (err) {
|
|
||||||
logError('db could not update file cache', err);
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getLastSentTimeAndJoinTime(
|
|
||||||
groupId: number
|
|
||||||
): Promise<IRember[]> {
|
|
||||||
logDebug('读取发言时间', groupId);
|
|
||||||
return new Promise<IRember[]>((resolve, reject) => {
|
|
||||||
this.db!.all(`SELECT * FROM "${groupId}" `, (err, rows: IRember[]) => {
|
|
||||||
if (err) {
|
|
||||||
logError('查询发言时间失败', groupId);
|
|
||||||
return resolve([]);
|
|
||||||
}
|
|
||||||
logDebug('查询发言时间成功', groupId, rows);
|
|
||||||
resolve(rows);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
insertLastSentTime(
|
|
||||||
groupId: number,
|
|
||||||
userId: number,
|
|
||||||
time: number
|
|
||||||
) {
|
|
||||||
this.LURCache.set(groupId, userId, time);
|
|
||||||
}
|
|
||||||
async insertJoinTime(
|
|
||||||
groupId: number,
|
|
||||||
userId: number,
|
|
||||||
time: number
|
|
||||||
) {
|
|
||||||
await this.createGroupInfoTimeTableIfNotExist(groupId);
|
|
||||||
this.db!.all(
|
|
||||||
`INSERT OR REPLACE INTO "${groupId}" (user_id, last_sent_time, join_time) VALUES (?,?,?)`,
|
|
||||||
[userId, time, time],
|
|
||||||
(err) => {
|
|
||||||
if (err)
|
|
||||||
logError(err),
|
|
||||||
Promise.reject(),
|
|
||||||
logError('插入入群时间失败', userId, groupId);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const dbUtil = new DBUtil();
|
|
@@ -4,9 +4,8 @@ import crypto from 'crypto';
|
|||||||
import util from 'util';
|
import util from 'util';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { log, logError } from './log';
|
import { log, logError } from './log';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import * as fileType from 'file-type';
|
import * as fileType from 'file-type';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
import { napCatCore } from '@/core';
|
import { napCatCore } from '@/core';
|
||||||
|
|
||||||
export const getNapCatDir = () => {
|
export const getNapCatDir = () => {
|
||||||
@@ -160,7 +159,12 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const fetchRes = await fetch(url, { headers });
|
const fetchRes = await fetch(url, { headers }).catch((err) => {
|
||||||
|
if (err.cause) {
|
||||||
|
throw err.cause;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
if (!fetchRes.ok) throw new Error(`下载文件失败: ${fetchRes.statusText}`);
|
if (!fetchRes.ok) throw new Error(`下载文件失败: ${fetchRes.statusText}`);
|
||||||
|
|
||||||
const blob = await fetchRes.blob();
|
const blob = await fetchRes.blob();
|
||||||
@@ -187,7 +191,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
isLocal: false
|
isLocal: false
|
||||||
};
|
};
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
fileName = uuidv4();
|
fileName = randomUUID();
|
||||||
}
|
}
|
||||||
let filePath = path.join(getTempDir(), fileName);
|
let filePath = path.join(getTempDir(), fileName);
|
||||||
let url = null;
|
let url = null;
|
||||||
@@ -230,7 +234,7 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
}
|
}
|
||||||
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
|
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
|
||||||
res.fileName = fileName;
|
res.fileName = fileName;
|
||||||
filePath = path.join(getTempDir(), uuidv4() + fileName);
|
filePath = path.join(getTempDir(), randomUUID() + fileName);
|
||||||
fs.writeFileSync(filePath, buffer);
|
fs.writeFileSync(filePath, buffer);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
res.errMsg = `${url}下载失败,` + e.toString();
|
res.errMsg = `${url}下载失败,` + e.toString();
|
||||||
@@ -246,13 +250,14 @@ export async function uri2local(uri: string, fileName: string | null = null): Pr
|
|||||||
} else {
|
} else {
|
||||||
filePath = pathname;
|
filePath = pathname;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
const cache = await dbUtil.getFileCacheByName(uri);
|
else {
|
||||||
if (cache) {
|
// const cache = await dbUtil.getFileCacheByName(uri);
|
||||||
filePath = cache.path;
|
// if (cache) {
|
||||||
} else {
|
// filePath = cache.path;
|
||||||
filePath = uri;
|
// } else {
|
||||||
}
|
// filePath = uri;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
res.isLocal = true;
|
res.isLocal = true;
|
||||||
|
@@ -12,6 +12,25 @@ export function sleep(ms: number): Promise<void> {
|
|||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PromiseTimer<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||||
|
const timeoutPromise = new Promise<T>((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error("PromiseTimer: Operation timed out")), ms)
|
||||||
|
);
|
||||||
|
return Promise.race([promise, timeoutPromise]);
|
||||||
|
}
|
||||||
|
export async function runAllWithTimeout<T>(tasks: Promise<T>[], timeout: number): Promise<T[]> {
|
||||||
|
const wrappedTasks = tasks.map(task =>
|
||||||
|
PromiseTimer(task, timeout).then(
|
||||||
|
result => ({ status: 'fulfilled', value: result }),
|
||||||
|
error => ({ status: 'rejected', reason: error })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const results = await Promise.all(wrappedTasks);
|
||||||
|
return results
|
||||||
|
.filter(result => result.status === 'fulfilled')
|
||||||
|
.map(result => (result as { status: 'fulfilled'; value: T }).value);
|
||||||
|
}
|
||||||
|
|
||||||
export function getMd5(s: string) {
|
export function getMd5(s: string) {
|
||||||
|
|
||||||
const h = crypto.createHash('md5');
|
const h = crypto.createHash('md5');
|
||||||
@@ -89,7 +108,36 @@ export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string
|
|||||||
}
|
}
|
||||||
return logExecutionTime;
|
return logExecutionTime;
|
||||||
}
|
}
|
||||||
|
export function CacheClassFuncAsyncExtend(ttl: number = 3600 * 1000, customKey: string = '', checker: any = (...data: any[]) => { return true; }) {
|
||||||
|
//console.log('CacheClassFuncAsync', ttl, customKey);
|
||||||
|
function logExecutionTime(target: any, methodName: string, descriptor: PropertyDescriptor) {
|
||||||
|
//console.log('logExecutionTime', target, methodName, descriptor);
|
||||||
|
const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
const originalMethod = descriptor.value;
|
||||||
|
descriptor.value = async function (...args: any[]) {
|
||||||
|
const key = `${customKey}${String(methodName)}.(${args.map(arg => JSON.stringify(arg)).join(', ')})`;
|
||||||
|
cache.forEach((value, key) => {
|
||||||
|
if (value.expiry < Date.now()) {
|
||||||
|
cache.delete(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const cachedValue = cache.get(key);
|
||||||
|
if (cachedValue && cachedValue.expiry > Date.now()) {
|
||||||
|
return cachedValue.value;
|
||||||
|
}
|
||||||
|
// const start = Date.now();
|
||||||
|
const result = await originalMethod.apply(this, args);
|
||||||
|
if (!checker(...args, result)) {
|
||||||
|
return result;//丢弃缓存
|
||||||
|
}
|
||||||
|
// const end = Date.now();
|
||||||
|
// console.log(`Method ${methodName} executed in ${end - start} ms.`);
|
||||||
|
cache.set(key, { expiry: Date.now() + ttl, value: result });
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return logExecutionTime;
|
||||||
|
}
|
||||||
// export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string = ''): any {
|
// export function CacheClassFuncAsync(ttl: number = 3600 * 1000, customKey: string = ''): any {
|
||||||
// const cache = new Map<string, { expiry: number; value: any }>();
|
// const cache = new Map<string, { expiry: number; value: any }>();
|
||||||
|
|
||||||
|
@@ -91,8 +91,10 @@ export function enableConsoleLog(enable: boolean) {
|
|||||||
function formatMsg(msg: any[]) {
|
function formatMsg(msg: any[]) {
|
||||||
let logMsg = '';
|
let logMsg = '';
|
||||||
for (const msgItem of msg) {
|
for (const msgItem of msg) {
|
||||||
// 判断是否是对象
|
if (msgItem instanceof Error) { // 判断是否是错误
|
||||||
if (typeof msgItem === 'object') {
|
logMsg += msgItem.stack + ' ';
|
||||||
|
continue;
|
||||||
|
} else if (typeof msgItem === 'object') { // 判断是否是对象
|
||||||
const obj = JSON.parse(JSON.stringify(msgItem, null, 2));
|
const obj = JSON.parse(JSON.stringify(msgItem, null, 2));
|
||||||
logMsg += JSON.stringify(truncateString(obj)) + ' ';
|
logMsg += JSON.stringify(truncateString(obj)) + ' ';
|
||||||
continue;
|
continue;
|
||||||
|
@@ -7,7 +7,7 @@ export class RequestUtil {
|
|||||||
static async HttpsGetCookies(url: string): Promise<{ [key: string]: string }> {
|
static async HttpsGetCookies(url: string): Promise<{ [key: string]: string }> {
|
||||||
const client = url.startsWith('https') ? https : http;
|
const client = url.startsWith('https') ? https : http;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let req = client.get(url, (res) => {
|
const req = client.get(url, (res) => {
|
||||||
let cookies: { [key: string]: string } = {};
|
let cookies: { [key: string]: string } = {};
|
||||||
const handleRedirect = (res: http.IncomingMessage) => {
|
const handleRedirect = (res: http.IncomingMessage) => {
|
||||||
//console.log(res.headers.location);
|
//console.log(res.headers.location);
|
||||||
@@ -44,7 +44,7 @@ export class RequestUtil {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
req.on('error', (error: any) => {
|
req.on('error', (error: any) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { networkInterfaces } from 'os';
|
import { networkInterfaces } from 'os';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
// 缓解Win7设备兼容性问题
|
// 缓解Win7设备兼容性问题
|
||||||
let osName: string;
|
let osName: string;
|
||||||
@@ -30,7 +30,7 @@ export async function getMachineId(): Promise<string> {
|
|||||||
if (!machineId) {
|
if (!machineId) {
|
||||||
machineId = (async () => {
|
machineId = (async () => {
|
||||||
const id = await getMacMachineId();
|
const id = await getMacMachineId();
|
||||||
return id || uuidv4(); // fallback, generate a UUID
|
return id || randomUUID(); // fallback, generate a UUID
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -1,9 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
CacheFileList,
|
|
||||||
CacheFileListItem,
|
CacheFileListItem,
|
||||||
CacheFileType,
|
CacheFileType,
|
||||||
CacheScanResult,
|
|
||||||
ChatCacheList,
|
|
||||||
ChatCacheListItemBasic,
|
ChatCacheListItemBasic,
|
||||||
ChatType,
|
ChatType,
|
||||||
ElementType, IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, RawMessage
|
ElementType, IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, RawMessage
|
||||||
@@ -15,31 +12,13 @@ import { log, logDebug, logError } from '@/common/utils/log';
|
|||||||
import { GeneralCallResult, napCatCore, OnRichMediaDownloadCompleteParams } from '@/core';
|
import { GeneralCallResult, napCatCore, OnRichMediaDownloadCompleteParams } from '@/core';
|
||||||
import { calculateFileMD5 } from '@/common/utils/file';
|
import { calculateFileMD5 } from '@/common/utils/file';
|
||||||
import * as fileType from 'file-type';
|
import * as fileType from 'file-type';
|
||||||
import { MsgListener } from '@/core/listeners';
|
|
||||||
import imageSize from 'image-size';
|
import imageSize from 'image-size';
|
||||||
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
||||||
import { sessionConfig } from '@/core/sessionConfig';
|
import { sessionConfig } from '@/core/sessionConfig';
|
||||||
import { randomUUID } from 'crypto';
|
|
||||||
import { rkeyManager } from '../utils/rkey';
|
import { rkeyManager } from '../utils/rkey';
|
||||||
import { AsyncQueue } from '@/common/utils/AsyncQueue';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
// const rkeyExpireTime = 1000;
|
import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService';
|
||||||
|
|
||||||
const getRKeyTaskQueue = new AsyncQueue();
|
|
||||||
|
|
||||||
const downloadMediaTasks: Map<string, (arg: OnRichMediaDownloadCompleteParams) => void> = new Map<string, (arg: OnRichMediaDownloadCompleteParams) => void>();
|
|
||||||
|
|
||||||
const downloadMediaListener = new MsgListener();
|
|
||||||
downloadMediaListener.onRichMediaDownloadComplete = arg => {
|
|
||||||
for (const [uuid, cb] of downloadMediaTasks) {
|
|
||||||
cb(arg);
|
|
||||||
downloadMediaTasks.delete(uuid);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
setTimeout(() => {
|
|
||||||
napCatCore.onLoginSuccess(() => {
|
|
||||||
napCatCore.addListener(downloadMediaListener);
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
export class NTQQFileApi {
|
export class NTQQFileApi {
|
||||||
static async getFileType(filePath: string) {
|
static async getFileType(filePath: string) {
|
||||||
return fileType.fileTypeFromFile(filePath);
|
return fileType.fileTypeFromFile(filePath);
|
||||||
@@ -106,31 +85,33 @@ export class NTQQFileApi {
|
|||||||
return sourcePath;
|
return sourcePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//logDebug('start downloadMedia', msgId, chatType, peerUid, elementId, thumbPath, sourcePath, timeout, force);
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
return new Promise<string>((resolve, reject) => {
|
(
|
||||||
let completed = false;
|
params: {
|
||||||
const cb = (arg: OnRichMediaDownloadCompleteParams) => {
|
fileModelId: string,
|
||||||
//logDebug('downloadMedia complete', arg, msgId);
|
downloadSourceType: number,
|
||||||
|
triggerType: number,
|
||||||
|
msgId: string,
|
||||||
|
chatType: ChatType,
|
||||||
|
peerUid: string,
|
||||||
|
elementId: string,
|
||||||
|
thumbSize: number,
|
||||||
|
downloadType: number,
|
||||||
|
filePath: string
|
||||||
|
}) => Promise<unknown>,
|
||||||
|
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
|
||||||
|
>(
|
||||||
|
'NodeIKernelMsgService/downloadRichMedia',
|
||||||
|
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
|
||||||
|
1,
|
||||||
|
timeout,
|
||||||
|
(arg: OnRichMediaDownloadCompleteParams) => {
|
||||||
if (arg.msgId === msgId) {
|
if (arg.msgId === msgId) {
|
||||||
completed = true;
|
return true;
|
||||||
let filePath = arg.filePath;
|
|
||||||
if (filePath.startsWith('\\')) {
|
|
||||||
// log('filePath start with \\');
|
|
||||||
const downloadPath = sessionConfig.defaultFileDownloadPath;
|
|
||||||
//logDebug('downloadPath', downloadPath);
|
|
||||||
filePath = path.join(downloadPath, filePath);
|
|
||||||
// 下载路径是下载文件夹的相对路径
|
|
||||||
}
|
|
||||||
resolve(filePath);
|
|
||||||
}
|
}
|
||||||
};
|
return false;
|
||||||
downloadMediaTasks.set(randomUUID(), cb);
|
},
|
||||||
setTimeout(() => {
|
{
|
||||||
if (!completed) {
|
|
||||||
reject('下载超时');
|
|
||||||
}
|
|
||||||
}, timeout);
|
|
||||||
napCatCore.session.getMsgService().downloadRichMedia({
|
|
||||||
fileModelId: '0',
|
fileModelId: '0',
|
||||||
downloadSourceType: 0,
|
downloadSourceType: 0,
|
||||||
triggerType: 1,
|
triggerType: 1,
|
||||||
@@ -140,9 +121,18 @@ export class NTQQFileApi {
|
|||||||
elementId: elementId,
|
elementId: elementId,
|
||||||
thumbSize: 0,
|
thumbSize: 0,
|
||||||
downloadType: 1,
|
downloadType: 1,
|
||||||
filePath: thumbPath,
|
filePath: thumbPath
|
||||||
});
|
}
|
||||||
});
|
);
|
||||||
|
let filePath = data[1].filePath;
|
||||||
|
if (filePath.startsWith('\\')) {
|
||||||
|
// log('filePath start with \\');
|
||||||
|
const downloadPath = sessionConfig.defaultFileDownloadPath;
|
||||||
|
//logDebug('downloadPath', downloadPath);
|
||||||
|
filePath = path.join(downloadPath, filePath);
|
||||||
|
// 下载路径是下载文件夹的相对路径
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
|
static async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
|
||||||
@@ -156,27 +146,79 @@ export class NTQQFileApi {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
static async searchfile(keys: string[]) {
|
||||||
|
|
||||||
static async getImageUrl(element: { originImageUrl: any; md5HexStr?: any; fileUuid: any; }, isPrivateImage: boolean) {
|
type EventType = NodeIKernelSearchService['searchFileWithKeywords'];
|
||||||
|
interface OnListener {
|
||||||
|
searchId: string,
|
||||||
|
hasMore: boolean,
|
||||||
|
resultItems: {
|
||||||
|
chatType: ChatType,
|
||||||
|
buddyChatInfo: any[],
|
||||||
|
discussChatInfo: any[],
|
||||||
|
groupChatInfo:
|
||||||
|
{
|
||||||
|
groupCode: string,
|
||||||
|
isConf: boolean,
|
||||||
|
hasModifyConfGroupFace: boolean,
|
||||||
|
hasModifyConfGroupName: boolean,
|
||||||
|
groupName: string,
|
||||||
|
remark: string
|
||||||
|
}[]
|
||||||
|
,
|
||||||
|
dataLineChatInfo: any[],
|
||||||
|
tmpChatInfo: any[],
|
||||||
|
msgId: string,
|
||||||
|
msgSeq: string,
|
||||||
|
msgTime: string,
|
||||||
|
senderUid: string,
|
||||||
|
senderNick: string,
|
||||||
|
senderRemark: string,
|
||||||
|
senderCard: string,
|
||||||
|
elemId: string,
|
||||||
|
elemType: number,
|
||||||
|
fileSize: string,
|
||||||
|
filePath: string,
|
||||||
|
fileName: string,
|
||||||
|
hits:
|
||||||
|
{
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
}[]
|
||||||
|
}[]
|
||||||
|
};
|
||||||
|
const [id, data] = await NTEventDispatch.CallNormalEvent<EventType, (params: OnListener) => void>(
|
||||||
|
'NodeIKernelSearchService/searchFileWithKeywords',
|
||||||
|
'NodeIKernelSearchListener/onSearchFileKeywordsResult',
|
||||||
|
1,
|
||||||
|
10000,
|
||||||
|
(arg): boolean => { return id == data.searchId },
|
||||||
|
keys,
|
||||||
|
12
|
||||||
|
);
|
||||||
|
return data.resultItems[0];
|
||||||
|
}
|
||||||
|
static async getImageUrl(element: { originImageUrl: any; md5HexStr?: any; fileUuid: any; }) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const url = element.originImageUrl; // 没有域名
|
const url: string = element.originImageUrl; // 没有域名
|
||||||
const md5HexStr = element.md5HexStr;
|
const md5HexStr = element.md5HexStr;
|
||||||
const fileMd5 = element.md5HexStr;
|
const fileMd5 = element.md5HexStr;
|
||||||
const fileUuid = element.fileUuid;
|
const fileUuid = element.fileUuid;
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
if (url.startsWith('/download')) {
|
let UrlParse = new URL(IMAGE_HTTP_HOST + url);//临时解析拼接
|
||||||
if (url.includes('&rkey=')) {
|
let imageAppid = UrlParse.searchParams.get('appid');
|
||||||
|
let isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid);
|
||||||
|
if (isNewPic) {
|
||||||
|
let UrlRkey = UrlParse.searchParams.get('rkey');
|
||||||
|
if (UrlRkey) {
|
||||||
return IMAGE_HTTP_HOST_NT + url;
|
return IMAGE_HTTP_HOST_NT + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rkeyData = await rkeyManager.getRkey();
|
const rkeyData = await rkeyManager.getRkey();
|
||||||
|
UrlRkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
|
||||||
const existsRKey = isPrivateImage ? rkeyData.private_rkey : rkeyData.group_rkey;
|
return IMAGE_HTTP_HOST_NT + url + `${UrlRkey}`;
|
||||||
return IMAGE_HTTP_HOST_NT + url + `${existsRKey}`;
|
|
||||||
} else {
|
} else {
|
||||||
// 老的图片url,不需要rkey
|
// 老的图片url,不需要rkey
|
||||||
return IMAGE_HTTP_HOST + url;
|
return IMAGE_HTTP_HOST + url;
|
||||||
|
@@ -14,6 +14,7 @@ export class NTQQFriendApi {
|
|||||||
'NodeIKernelBuddyListener/onBuddyListChange',
|
'NodeIKernelBuddyListener/onBuddyListChange',
|
||||||
1,
|
1,
|
||||||
5000,
|
5000,
|
||||||
|
() => true,
|
||||||
forced
|
forced
|
||||||
);
|
);
|
||||||
const friends: User[] = [];
|
const friends: User[] = [];
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType } from '../entities';
|
import { GroupMember, GroupRequestOperateTypes, GroupMemberRole, GroupNotify, Group, MemberExtSourceType, GroupNotifyTypes, ChatType } from '../entities';
|
||||||
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
|
import { GeneralCallResult, NTQQUserApi, napCatCore } from '@/core';
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
import { logDebug } from '@/common/utils/log';
|
import { log } from '@/common/utils/log';
|
||||||
// console.log(process.pid);
|
import { groupMembers } from '../data';
|
||||||
// setTimeout(async () => {
|
import { CacheClassFuncAsyncExtend, runAllWithTimeout } from '@/common/utils/helper';
|
||||||
// console.log(JSON.stringify(await NTQQGroupApi.getMemberExtInfo(), null, 2));
|
|
||||||
// }, 20000);
|
|
||||||
export class NTQQGroupApi {
|
export class NTQQGroupApi {
|
||||||
|
static async setGroupAvatar(gc: string, filePath: string) {
|
||||||
|
return napCatCore.session.getGroupService().setHeader(gc, filePath);
|
||||||
|
}
|
||||||
static async getGroups(forced = false) {
|
static async getGroups(forced = false) {
|
||||||
let [_retData, _updateType, groupList] = await NTEventDispatch.CallNormalEvent
|
let [_retData, _updateType, groupList] = await NTEventDispatch.CallNormalEvent
|
||||||
<(force: boolean) => Promise<any>, (updateType: number, groupList: Group[]) => void>
|
<(force: boolean) => Promise<any>, (updateType: number, groupList: Group[]) => void>
|
||||||
@@ -15,10 +16,99 @@ export class NTQQGroupApi {
|
|||||||
'NodeIKernelGroupListener/onGroupListUpdate',
|
'NodeIKernelGroupListener/onGroupListUpdate',
|
||||||
1,
|
1,
|
||||||
5000,
|
5000,
|
||||||
|
() => true,
|
||||||
forced
|
forced
|
||||||
);
|
);
|
||||||
return groupList;
|
return groupList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CacheClassFuncAsyncExtend(600, "LastestSendTime", () => true)
|
||||||
|
static async getGroupMemberLastestSendTimeCache(GroupCode: string) {
|
||||||
|
return NTQQGroupApi.getGroupMemberLastestSendTime(GroupCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过QQ自带数据库获取群成员最后发言时间(仅返回有效数据 且消耗延迟大 需要进行缓存)
|
||||||
|
* @param GroupCode 群号
|
||||||
|
* @returns Map<string, string> key: uin value: sendTime
|
||||||
|
* @example
|
||||||
|
* let ret = await NTQQGroupApi.getGroupMemberLastestSendTime('123456');
|
||||||
|
* for (let [uin, sendTime] of ret) {
|
||||||
|
* console.log(uin, sendTime);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
static async getGroupMemberLastestSendTime(GroupCode: string) {
|
||||||
|
async function getdata(uid: string) {
|
||||||
|
let NTRet = await NTQQGroupApi.getLastestMsgByUids(GroupCode, [uid]);
|
||||||
|
if (NTRet.result != 0 && NTRet.msgList.length < 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return { sendUin: NTRet.msgList[0].senderUin, sendTime: NTRet.msgList[0].msgTime }
|
||||||
|
}
|
||||||
|
let currentGroupMembers = groupMembers.get(GroupCode);
|
||||||
|
let PromiseData: Promise<({
|
||||||
|
sendUin: string;
|
||||||
|
sendTime: string;
|
||||||
|
} | undefined)>[] = [];
|
||||||
|
let ret: Map<string, string> = new Map();
|
||||||
|
if (!currentGroupMembers) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
for (let member of currentGroupMembers.values()) {
|
||||||
|
PromiseData.push(getdata(member.uid).catch(() => undefined));
|
||||||
|
}
|
||||||
|
let allRet = await runAllWithTimeout(PromiseData, 2500);
|
||||||
|
for (let PromiseDo of allRet) {
|
||||||
|
if (PromiseDo) {
|
||||||
|
ret.set(PromiseDo.sendUin, PromiseDo.sendTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static async getLastestMsgByUids(GroupCode: string, uids: string[]) {
|
||||||
|
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: {
|
||||||
|
peerUid: GroupCode,
|
||||||
|
chatType: ChatType.group,
|
||||||
|
},
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: uids,
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static async getLastestMsg(GroupCode: string, uins: string[]) {
|
||||||
|
let uids: Array<string> = [];
|
||||||
|
for (let uin of uins) {
|
||||||
|
try {
|
||||||
|
let uid = await NTQQUserApi.getUidByUin(uin)
|
||||||
|
if (uid) {
|
||||||
|
uids.push(uid);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log("getLastestMsg--->", error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ret = await napCatCore.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
|
||||||
|
chatInfo: {
|
||||||
|
peerUid: GroupCode,
|
||||||
|
chatType: ChatType.group,
|
||||||
|
},
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: uids,
|
||||||
|
filterMsgToTime: '0',
|
||||||
|
filterMsgFromTime: '0',
|
||||||
|
isReverseOrder: false,
|
||||||
|
isIncludeCurrent: true,
|
||||||
|
pageLimit: 1,
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
static async getGroupRecommendContactArkJson(GroupCode: string) {
|
static async getGroupRecommendContactArkJson(GroupCode: string) {
|
||||||
return napCatCore.session.getGroupService().getGroupRecommendContactArkJson(GroupCode);
|
return napCatCore.session.getGroupService().getGroupRecommendContactArkJson(GroupCode);
|
||||||
}
|
}
|
||||||
@@ -31,6 +121,30 @@ export class NTQQGroupApi {
|
|||||||
static async DelGroupFileFolder(groupCode: string, folderId: string) {
|
static async DelGroupFileFolder(groupCode: string, folderId: string) {
|
||||||
return napCatCore.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
|
return napCatCore.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
|
||||||
}
|
}
|
||||||
|
static async addGroupEssence(GroupCode: string, msgId: string) {
|
||||||
|
// 代码没测过
|
||||||
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
let MsgData = await napCatCore.session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: GroupCode }, msgId, 1, false);
|
||||||
|
let param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||||
|
msgSeq: parseInt(MsgData.msgList[0].msgSeq)
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return napCatCore.session.getGroupService().addGroupEssence(param);
|
||||||
|
}
|
||||||
|
static async removeGroupEssence(GroupCode: string, msgId: string) {
|
||||||
|
// 代码没测过
|
||||||
|
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
|
||||||
|
let MsgData = await napCatCore.session.getMsgService().getMsgsIncludeSelf({ chatType: 2, guildId: '', peerUid: GroupCode }, msgId, 1, false);
|
||||||
|
let param = {
|
||||||
|
groupCode: GroupCode,
|
||||||
|
msgRandom: parseInt(MsgData.msgList[0].msgRandom),
|
||||||
|
msgSeq: parseInt(MsgData.msgList[0].msgSeq)
|
||||||
|
};
|
||||||
|
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
|
||||||
|
return napCatCore.session.getGroupService().removeGroupEssence(param);
|
||||||
|
}
|
||||||
static async getSingleScreenNotifies(num: number) {
|
static async getSingleScreenNotifies(num: number) {
|
||||||
let [_retData, _doubt, _seq, notifies] = await NTEventDispatch.CallNormalEvent
|
let [_retData, _doubt, _seq, notifies] = await NTEventDispatch.CallNormalEvent
|
||||||
<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
|
||||||
@@ -39,6 +153,7 @@ export class NTQQGroupApi {
|
|||||||
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
|
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
|
||||||
1,
|
1,
|
||||||
5000,
|
5000,
|
||||||
|
() => true,
|
||||||
false,
|
false,
|
||||||
'',
|
'',
|
||||||
num
|
num
|
||||||
@@ -87,16 +202,21 @@ export class NTQQGroupApi {
|
|||||||
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
const _Pskey = (await NTQQUserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
|
||||||
return napCatCore.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
|
return napCatCore.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl);
|
||||||
}
|
}
|
||||||
static async handleGroupRequest(notify: GroupNotify, operateType: GroupRequestOperateTypes, reason?: string) {
|
static async handleGroupRequest(flag: string, operateType: GroupRequestOperateTypes, reason?: string) {
|
||||||
|
let flagitem = flag.split('|');
|
||||||
|
let groupCode = flagitem[0];
|
||||||
|
let seq = flagitem[1];
|
||||||
|
let type = parseInt(flagitem[2]);
|
||||||
|
|
||||||
return napCatCore.session.getGroupService().operateSysNotify(
|
return napCatCore.session.getGroupService().operateSysNotify(
|
||||||
false,
|
false,
|
||||||
{
|
{
|
||||||
'operateType': operateType, // 2 拒绝
|
'operateType': operateType, // 2 拒绝
|
||||||
'targetMsg': {
|
'targetMsg': {
|
||||||
'seq': notify.seq, // 通知序列号
|
'seq': seq, // 通知序列号
|
||||||
'type': notify.type,
|
'type': type,
|
||||||
'groupCode': notify.group.groupCode,
|
'groupCode': groupCode,
|
||||||
'postscript': reason || ''
|
'postscript': reason || ' ' // 仅传空值可能导致处理失败,故默认给个空格
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,83 +1,56 @@
|
|||||||
import { GetFileListParam, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
import { GetFileListParam, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
||||||
import { selfInfo } from '@/core/data';
|
import { friends, groups, selfInfo } from '@/core/data';
|
||||||
import { log, logError } from '@/common/utils/log';
|
import { log, logWarn } from '@/common/utils/log';
|
||||||
import { sleep } from '@/common/utils/helper';
|
import { sleep } from '@/common/utils/helper';
|
||||||
import { napCatCore } from '@/core';
|
import { napCatCore, NTQQUserApi } from '@/core';
|
||||||
import { MsgListener, onGroupFileInfoUpdateParamType } from '@/core/listeners';
|
import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
|
||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
import { randomUUID } from 'crypto';
|
import { MessageUnique } from '../../../common/utils/MessageUnique';
|
||||||
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
|
async function LoadMessageIdList(Peer: Peer, msgId: string) {
|
||||||
const sendMessagePool: Record<string, ((sendSuccessMsg: RawMessage) => void | Promise<void>) | null> = {};// peerUid: callbackFunc
|
let msgList = await NTQQMsgApi.getMsgHistory(Peer, msgId, 50);
|
||||||
|
for (let j = 0; j < msgList.msgList.length; j++) {
|
||||||
const sendSuccessCBMap: Record<string, ((sendSuccessMsg: RawMessage) => boolean | Promise<boolean>) | null> = {};// uuid: callbackFunc
|
let shortId = MessageUnique.createMsg(Peer, msgList.msgList[j].msgId);
|
||||||
|
|
||||||
const GroupFileInfoUpdateTasks: Map<string, ((groupFileListResult: onGroupFileInfoUpdateParamType) => void)> = new Map();
|
|
||||||
|
|
||||||
const sentMsgTasks: Map<string, (msg: RawMessage) => void> = new Map();
|
|
||||||
|
|
||||||
const msgListener = new MsgListener();
|
|
||||||
|
|
||||||
msgListener.onGroupFileInfoUpdate = (groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
|
||||||
for (const [uuid, cb] of GroupFileInfoUpdateTasks) {
|
|
||||||
cb(groupFileListResult);
|
|
||||||
GroupFileInfoUpdateTasks.delete(uuid);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
async function loadMessageUnique() {
|
||||||
msgListener.onAddSendMsg = (msgRecord: RawMessage) => {
|
if (groups.size > 100) {
|
||||||
// console.log("sent msg", msgRecord, sendMessagePool);
|
logWarn('群数量大于100,可能会导致性能问题');
|
||||||
for (const [uuid, cb] of sentMsgTasks) {
|
|
||||||
cb(msgRecord);
|
|
||||||
sentMsgTasks.delete(uuid);
|
|
||||||
}
|
}
|
||||||
if (sendMessagePool[msgRecord.peerUid]) {
|
let predict = (groups.size + friends.size) / 5;
|
||||||
const r = sendMessagePool[msgRecord.peerUid]?.(msgRecord);
|
predict = predict < 20 ? 20 : predict;
|
||||||
if (r instanceof Promise) {
|
predict = predict > 50 ? 50 : predict;
|
||||||
r.then().catch(logError);
|
//let waitpromise: Array<Promise<{ msgList: RawMessage[]; }>> = [];
|
||||||
|
MessageUnique.resize(predict * 50);
|
||||||
|
let RecentContact = await NTQQUserApi.getRecentContactListSnapShot(predict);
|
||||||
|
let LoadMessageIdDo: Array<Promise<void>> = new Array<Promise<void>>();
|
||||||
|
if (RecentContact?.info?.changedList && RecentContact?.info?.changedList?.length > 0) {
|
||||||
|
for (let i = 0; i < RecentContact.info.changedList.length; i++) {
|
||||||
|
let Peer: Peer = { chatType: RecentContact.info.changedList[i].chatType, peerUid: RecentContact.info.changedList[i].peerUid, guildId: '' };
|
||||||
|
LoadMessageIdDo.push(LoadMessageIdList(Peer, RecentContact.info.changedList[i].msgId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
await Promise.all(LoadMessageIdDo).then(() => {
|
||||||
|
log(`[消息序列] 加载 ${predict * 50} 条历史消息记录完成`);
|
||||||
msgListener.onMsgInfoListUpdate = (msgInfoList: RawMessage[]) => {
|
|
||||||
msgInfoList.forEach(msg => {
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
for (const cbId in sendSuccessCBMap) {
|
|
||||||
const cb = sendSuccessCBMap[cbId]!;
|
|
||||||
const cbResult = cb(msg);
|
|
||||||
const checkResult = (result: boolean) => {
|
|
||||||
if (result) {
|
|
||||||
delete sendSuccessCBMap[cbId];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (cbResult instanceof Promise) {
|
|
||||||
cbResult.then(checkResult);
|
|
||||||
} else {
|
|
||||||
checkResult(cbResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then().catch(log);
|
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
napCatCore.onLoginSuccess(() => {
|
napCatCore.onLoginSuccess(async () => {
|
||||||
napCatCore.addListener(msgListener);
|
await sleep(100);
|
||||||
|
loadMessageUnique().then().catch();
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
|
||||||
export class NTQQMsgApi {
|
export class NTQQMsgApi {
|
||||||
// static napCatCore: NapCatCore | null = null;
|
// static napCatCore: NapCatCore | null = null;
|
||||||
// enum BaseEmojiType {
|
// enum BaseEmojiType {
|
||||||
// NORMAL_EMOJI,
|
// NORMAL_EMOJI,
|
||||||
// SUPER_EMOJI,
|
// SUPER_EMOJI,
|
||||||
// RANDOM_SUPER_EMOJI,
|
// RANDOM_SUPER_EMOJI,
|
||||||
// CHAIN_SUPER_EMOJI,
|
// CHAIN_SUPER_EMOJI,
|
||||||
// EMOJI_EMOJI
|
// EMOJI_EMOJI
|
||||||
// }
|
// }
|
||||||
static async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
static async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, set: boolean = true) {
|
||||||
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
|
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
|
||||||
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
|
||||||
@@ -97,135 +70,121 @@ export class NTQQMsgApi {
|
|||||||
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
static async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
|
||||||
return await napCatCore.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
|
return await napCatCore.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async activateChat(peer: Peer) {
|
|
||||||
// await this.fetchRecentContact();
|
|
||||||
// await sleep(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async activateChatAndGetHistory(peer: Peer) {
|
|
||||||
|
|
||||||
}
|
|
||||||
static async setMsgRead(peer: Peer) {
|
static async setMsgRead(peer: Peer) {
|
||||||
return napCatCore.session.getMsgService().setMsgRead(peer);
|
return napCatCore.session.getMsgService().setMsgRead(peer);
|
||||||
}
|
}
|
||||||
static async getGroupFileList(GroupCode: string, params: GetFileListParam) {
|
static async getGroupFileList(GroupCode: string, params: GetFileListParam) {
|
||||||
return new Promise<Array<any>>(async (resolve, reject) => {
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
let complete = false;
|
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
|
||||||
setTimeout(() => {
|
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
|
||||||
if (!complete) {
|
>(
|
||||||
reject('获取群文件列表超时');
|
'NodeIKernelRichMediaService/sendMsg',
|
||||||
}
|
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
|
||||||
}, 5000);
|
1,
|
||||||
const GroupFileInfoUpdateCB = (groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
5000,
|
||||||
complete = true;
|
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
|
||||||
resolve(groupFileListResult.item);
|
return true;
|
||||||
};
|
},
|
||||||
GroupFileInfoUpdateTasks.set(randomUUID(), GroupFileInfoUpdateCB);
|
GroupCode,
|
||||||
await napCatCore.session.getRichMediaService().getGroupFileList(GroupCode, params);
|
params
|
||||||
});
|
);
|
||||||
|
return data[1].item;
|
||||||
}
|
}
|
||||||
static async getMsgHistory(peer: Peer, msgId: string, count: number) {
|
static async getMsgHistory(peer: Peer, msgId: string, count: number) {
|
||||||
// 消息时间从旧到新
|
// 消息时间从旧到新
|
||||||
return napCatCore.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, true);
|
return napCatCore.session.getMsgService().getMsgsIncludeSelf(peer, msgId, count, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fetchRecentContact() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static async recallMsg(peer: Peer, msgIds: string[]) {
|
static async recallMsg(peer: Peer, msgIds: string[]) {
|
||||||
await napCatCore.session.getMsgService().recallMsg({
|
await napCatCore.session.getMsgService().recallMsg({
|
||||||
chatType: peer.chatType,
|
chatType: peer.chatType,
|
||||||
peerUid: peer.peerUid
|
peerUid: peer.peerUid
|
||||||
}, msgIds);
|
}, msgIds);
|
||||||
}
|
}
|
||||||
|
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
|
||||||
static async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000): Promise<RawMessage> {
|
let msgId = await NTQQMsgApi.getMsgUnique(await NTQQMsgApi.getServerTime());
|
||||||
const peerUid = peer.peerUid;
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
// 等待上一个相同的peer发送完
|
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
|
||||||
let checkLastSendUsingTime = 0;
|
(msgList: RawMessage[]) => void
|
||||||
const waitLastSend: () => Promise<void> = async () => {
|
>(
|
||||||
if (checkLastSendUsingTime > timeout) {
|
'NodeIKernelMsgService/sendMsg',
|
||||||
throw ('发送超时');
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
}
|
1,
|
||||||
const lastSending = sendMessagePool[peer.peerUid];
|
timeout,
|
||||||
if (lastSending) {
|
(msgRecords: RawMessage[]) => {
|
||||||
// log("有正在发送的消息,等待中...")
|
for (let msgRecord of msgRecords) {
|
||||||
await sleep(500);
|
if (msgRecord.msgId === msgId && msgRecord.sendStatus === 2) {
|
||||||
checkLastSendUsingTime += 500;
|
|
||||||
return await waitLastSend();
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
await waitLastSend();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let completed = false;
|
|
||||||
let sentMessage: RawMessage | null = null;
|
|
||||||
const sendSuccessCBId = randomUUID() as string;
|
|
||||||
sendSuccessCBMap[sendSuccessCBId] = (msgRecord: RawMessage) => {
|
|
||||||
if (msgRecord.msgId === sentMessage?.msgId) {
|
|
||||||
if (msgRecord.sendStatus === 2) {
|
|
||||||
delete sendSuccessCBMap[sendSuccessCBId];
|
|
||||||
completed = true;
|
|
||||||
resolve(msgRecord);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
},
|
||||||
sendMessagePool[peerUid] = async (rawMessage: RawMessage) => {
|
msgId,
|
||||||
// console.log('收到sent 消息', rawMessage.msgId);
|
peer,
|
||||||
delete sendMessagePool[peerUid];
|
msgElements,
|
||||||
sentMessage = rawMessage;
|
new Map()
|
||||||
};
|
);
|
||||||
setTimeout(() => {
|
let retMsg = data[1].find(msgRecord => {
|
||||||
if (completed) return;
|
if (msgRecord.msgId === msgId) {
|
||||||
delete sendMessagePool[peerUid];
|
return true;
|
||||||
delete sendSuccessCBMap[sendSuccessCBId];
|
}
|
||||||
reject('发送超时');
|
|
||||||
}, timeout);
|
|
||||||
const result = napCatCore.session.getMsgService().sendMsg('0', peer, msgElements, new Map());
|
|
||||||
});
|
});
|
||||||
|
return retMsg;
|
||||||
|
}
|
||||||
|
static async getMsgUniqueEx(){
|
||||||
|
let msgId = await NTQQMsgApi.getMsgUnique(await NTQQMsgApi.getServerTime());
|
||||||
|
return msgId;
|
||||||
|
}
|
||||||
|
static async getMsgUnique(time: string) {
|
||||||
|
return napCatCore.session.getMsgService().getMsgUniqueId(time);
|
||||||
|
}
|
||||||
|
static async getServerTime() {
|
||||||
|
return napCatCore.session.getMSFService().getServerTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
static async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
|
||||||
return napCatCore.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
|
return napCatCore.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
|
||||||
}
|
}
|
||||||
|
|
||||||
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
static async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise<RawMessage> {
|
||||||
const msgInfos = msgIds.map(id => {
|
const msgInfos = msgIds.map(id => {
|
||||||
return { msgId: id, senderShowName: selfInfo.nick };
|
return { msgId: id, senderShowName: selfInfo.nick };
|
||||||
});
|
});
|
||||||
|
let data = await NTEventDispatch.CallNormalEvent<
|
||||||
return new Promise((resolve, reject) => {
|
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>,) => Promise<unknown>,
|
||||||
let complete = false;
|
(msgList: RawMessage[]) => void
|
||||||
const onSentCB = (msg: RawMessage) => {
|
>(
|
||||||
const arkElement = msg.elements.find(ele => ele.arkElement);
|
'NodeIKernelMsgService/multiForwardMsgWithComment',
|
||||||
if (!arkElement) {
|
'NodeIKernelMsgListener/onMsgInfoListUpdate',
|
||||||
// log("收到的不是转发消息")
|
1,
|
||||||
return;
|
5000,
|
||||||
|
(msgRecords: RawMessage[]) => {
|
||||||
|
for (let msgRecord of msgRecords) {
|
||||||
|
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == selfInfo.uid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
|
return false;
|
||||||
if (forwardData.app != 'com.tencent.multimsg') {
|
},
|
||||||
return;
|
msgInfos,
|
||||||
}
|
srcPeer,
|
||||||
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
destPeer,
|
||||||
complete = true;
|
[],
|
||||||
resolve(msg);
|
new Map()
|
||||||
}
|
|
||||||
};
|
|
||||||
sentMsgTasks.set(randomUUID(), onSentCB);
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!complete) {
|
|
||||||
reject('转发消息超时');
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
napCatCore.session.getMsgService().multiForwardMsgWithComment(msgInfos, srcPeer, destPeer, [], new Map());
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
for (let msg of data[1]) {
|
||||||
|
const arkElement = msg.elements.find(ele => ele.arkElement);
|
||||||
|
if (!arkElement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
|
||||||
|
if (forwardData.app != 'com.tencent.multimsg') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfInfo.uid) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('转发消息超时');
|
||||||
|
}
|
||||||
|
static async markallMsgAsRead() {
|
||||||
|
return napCatCore.session.getMsgService().setAllC2CAndGroupMsgRead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ export class NTQQSystemApi {
|
|||||||
static async translateEnWordToZn(words: string[]) {
|
static async translateEnWordToZn(words: string[]) {
|
||||||
return napCatCore.session.getRichMediaService().translateEnWordToZn(words);
|
return napCatCore.session.getRichMediaService().translateEnWordToZn(words);
|
||||||
}
|
}
|
||||||
//调用会超时 没灯用
|
//调用会超时 没灯用 (好像是通知listener的) onLineDev
|
||||||
static async getOnlineDev() {
|
static async getOnlineDev() {
|
||||||
return napCatCore.session.getMsgService().getOnLineDev();
|
return napCatCore.session.getMsgService().getOnLineDev();
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ export class NTQQSystemApi {
|
|||||||
static async BootMiniApp(appfile: string, params: string) {
|
static async BootMiniApp(appfile: string, params: string) {
|
||||||
await napCatCore.session.getNodeMiscService().setMiniAppVersion('2.16.4');
|
await napCatCore.session.getNodeMiscService().setMiniAppVersion('2.16.4');
|
||||||
let c = await napCatCore.session.getNodeMiscService().getMiniAppPath();
|
let c = await napCatCore.session.getNodeMiscService().getMiniAppPath();
|
||||||
console.log(c);
|
|
||||||
return napCatCore.session.getNodeMiscService().startNewMiniApp(appfile, params);
|
return napCatCore.session.getNodeMiscService().startNewMiniApp(appfile, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,40 +1,28 @@
|
|||||||
import { ModifyProfileParams, SelfInfo, User, UserDetailInfoByUin } from '@/core/entities';
|
import { ModifyProfileParams, SelfInfo, User, UserDetailInfoByUin } from '@/core/entities';
|
||||||
import { selfInfo } from '@/core/data';
|
import { friends, selfInfo } from '@/core/data';
|
||||||
import { CacheClassFuncAsync } from '@/common/utils/helper';
|
import { CacheClassFuncAsync, CacheClassFuncAsyncExtend } from '@/common/utils/helper';
|
||||||
import { GeneralCallResult, napCatCore } from '@/core';
|
import { napCatCore } from '@/core';
|
||||||
import { ProfileListener } from '@/core/listeners';
|
import { NodeIKernelProfileListener, ProfileListener } from '@/core/listeners';
|
||||||
import { rejects } from 'assert';
|
|
||||||
import { randomUUID } from 'crypto';
|
|
||||||
import { RequestUtil } from '@/common/utils/request';
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
import { logDebug, logError } from '@/common/utils/log';
|
import { logWarn } from '@/common/utils/log';
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
const userInfoCache: Record<string, User> = {}; // uid: User
|
import { NodeIKernelProfileService } from '@/core/services';
|
||||||
|
|
||||||
const profileListener = new ProfileListener();
|
|
||||||
|
|
||||||
const userDetailHandlers: Map<string, ((profile: User) => void)> = new Map();
|
|
||||||
profileListener.onProfileDetailInfoChanged = (profile) => {
|
|
||||||
userInfoCache[profile.uid] = profile;
|
|
||||||
userDetailHandlers.forEach(handler => handler(profile));
|
|
||||||
};
|
|
||||||
setTimeout(() => {
|
|
||||||
napCatCore.onLoginSuccess(() => {
|
|
||||||
napCatCore.addListener(profileListener);
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
// 老版本逻辑现已移除
|
|
||||||
// console.log('onProfileDetailInfoChanged', profile);
|
|
||||||
// recevCount++;
|
|
||||||
// firstProfile = profile;
|
|
||||||
// if (recevCount === 2) {
|
|
||||||
// profileService.removeKernelProfileListener(listenerId);
|
|
||||||
// // if (!completed) {
|
|
||||||
// completed = true;
|
|
||||||
// resolve(profile);
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
export class NTQQUserApi {
|
export class NTQQUserApi {
|
||||||
|
static async getProfileLike(uid: string) {
|
||||||
|
return napCatCore.session.getProfileLikeService().getBuddyProfileLike({
|
||||||
|
friendUids: [
|
||||||
|
uid
|
||||||
|
],
|
||||||
|
basic: 1,
|
||||||
|
vote: 1,
|
||||||
|
favorite: 0,
|
||||||
|
userProfile: 1,
|
||||||
|
type: 2,
|
||||||
|
start: 0,
|
||||||
|
limit: 20
|
||||||
|
});
|
||||||
|
}
|
||||||
static async setLongNick(longNick: string) {
|
static async setLongNick(longNick: string) {
|
||||||
return napCatCore.session.getProfileService().setLongNick(longNick);
|
return napCatCore.session.getProfileService().setLongNick(longNick);
|
||||||
}
|
}
|
||||||
@@ -58,6 +46,9 @@ export class NTQQUserApi {
|
|||||||
const ret = await napCatCore.session.getProfileService().setHeader(filePath) as setQQAvatarRet;
|
const ret = await napCatCore.session.getProfileService().setHeader(filePath) as setQQAvatarRet;
|
||||||
return { result: ret?.result, errMsg: ret?.errMsg };
|
return { result: ret?.result, errMsg: ret?.errMsg };
|
||||||
}
|
}
|
||||||
|
static async setGroupAvatar(gc: string, filePath: string) {
|
||||||
|
return napCatCore.session.getGroupService().setHeader(gc, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
static async getSelfInfo() {
|
static async getSelfInfo() {
|
||||||
|
|
||||||
@@ -73,45 +64,26 @@ export class NTQQUserApi {
|
|||||||
// KQZONE,
|
// KQZONE,
|
||||||
// KOTHER
|
// KOTHER
|
||||||
// }
|
// }
|
||||||
static async getUserDetailInfo(uid: string): Promise<User> {
|
static async getUserDetailInfo(uid: string) {
|
||||||
// const existUser = userInfoCache[uid];
|
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo'];
|
||||||
// if (existUser) {
|
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged'];
|
||||||
// return existUser;
|
let [_retData, profile] = await NTEventDispatch.CallNormalEvent
|
||||||
// }
|
<EventService, EventListener>
|
||||||
const profileService = napCatCore.session.getProfileService();
|
(
|
||||||
// console.log('getUserDetailInfo', result);
|
'NodeIKernelProfileService/getUserDetailInfoWithBizInfo',
|
||||||
return new Promise((resolve, reject) => {
|
'NodeIKernelProfileListener/onProfileDetailInfoChanged',
|
||||||
const uuid = randomUUID();
|
2,
|
||||||
let completed = false;
|
5000,
|
||||||
let retData: User | undefined = undefined;
|
(profile: User) => {
|
||||||
let isFirst = true;
|
if (profile.uid === uid) {
|
||||||
// 不管返回几次 超时有数据就该返回 兼容就好了
|
return true;
|
||||||
setTimeout(() => {
|
|
||||||
if (!completed) {
|
|
||||||
if (retData) {
|
|
||||||
resolve(retData);
|
|
||||||
} else {
|
|
||||||
reject('getUserDetailInfo timeout');
|
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
userDetailHandlers.delete(uuid);
|
},
|
||||||
}, 5000);
|
uid,
|
||||||
userDetailHandlers.set(uuid, (profile) => {
|
[0]
|
||||||
if (profile.uid === uid) {
|
);
|
||||||
if (isFirst) {
|
return profile;
|
||||||
retData = profile;
|
|
||||||
isFirst = false;
|
|
||||||
// console.log('getUserDetailInfo', profile);
|
|
||||||
} else {
|
|
||||||
completed = true;
|
|
||||||
resolve(profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
profileService.getUserDetailInfoWithBizInfo(uid, [0]).then(result => {
|
|
||||||
// console.log('getUserDetailInfo', result);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
static async modifySelfProfile(param: ModifyProfileParams) {
|
static async modifySelfProfile(param: ModifyProfileParams) {
|
||||||
return napCatCore.session.getProfileService().modifyDesktopMiniProfile(param);
|
return napCatCore.session.getProfileService().modifyDesktopMiniProfile(param);
|
||||||
@@ -163,6 +135,13 @@ export class NTQQUserApi {
|
|||||||
}
|
}
|
||||||
return skey;
|
return skey;
|
||||||
}
|
}
|
||||||
|
@CacheClassFuncAsyncExtend(3600, 'Uin2Uid', (Uin: string, Uid: string | undefined) => {
|
||||||
|
if (Uid && Uid.indexOf('u_') != -1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
logWarn("uin转换到uid时异常", Uin);
|
||||||
|
return false;
|
||||||
|
})
|
||||||
static async getUidByUin(Uin: string) {
|
static async getUidByUin(Uin: string) {
|
||||||
let ret = await NTEventDispatch.CallNoListenerEvent
|
let ret = await NTEventDispatch.CallNoListenerEvent
|
||||||
<(Uin: string[]) => Promise<{ uidInfo: Map<string, string> }>>(
|
<(Uin: string[]) => Promise<{ uidInfo: Map<string, string> }>>(
|
||||||
@@ -170,8 +149,39 @@ export class NTQQUserApi {
|
|||||||
5000,
|
5000,
|
||||||
[Uin]
|
[Uin]
|
||||||
);
|
);
|
||||||
return ret.uidInfo.get(Uin);
|
let uid = ret.uidInfo.get(Uin); //通过QQ默认方式转换
|
||||||
|
if (!uid) {
|
||||||
|
Array.from(friends.values()).forEach((t) => {
|
||||||
|
if (t.uin == Uin) {
|
||||||
|
//logDebug('getUidByUin', t.uid, t.uin, Uin);
|
||||||
|
uid = t.uid;
|
||||||
|
}
|
||||||
|
//console.log(t.uid, t.uin, Uin);
|
||||||
|
});
|
||||||
|
//uid = Array.from(friends.values()).find((t) => { t.uin == Uin })?.uid; // 从NC维护的QQ Buddy缓存 转换
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!uid) {
|
||||||
|
// uid = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uin == Uin })?.uid; //从QQ Native 缓存转换 方法一
|
||||||
|
// }
|
||||||
|
// if (!uid) {
|
||||||
|
// uid = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uin == Uin })?.uid; //从QQ Native 非缓存转换 方法二
|
||||||
|
// }
|
||||||
|
if (!uid) {
|
||||||
|
let unveifyUid = (await NTQQUserApi.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
|
||||||
|
if (unveifyUid.indexOf("*") == -1) {
|
||||||
|
uid = unveifyUid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uid;
|
||||||
}
|
}
|
||||||
|
@CacheClassFuncAsyncExtend(3600, 'Uid2Uin', (Uid: string | undefined, Uin: number | undefined) => {
|
||||||
|
if (Uin && Uin != 0 && !isNaN(Uin)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
logWarn("uid转换到uin时异常", Uid);
|
||||||
|
return false;
|
||||||
|
})
|
||||||
static async getUinByUid(Uid: string | undefined) {
|
static async getUinByUid(Uid: string | undefined) {
|
||||||
if (!Uid) {
|
if (!Uid) {
|
||||||
return '';
|
return '';
|
||||||
@@ -182,7 +192,38 @@ export class NTQQUserApi {
|
|||||||
5000,
|
5000,
|
||||||
[Uid]
|
[Uid]
|
||||||
);
|
);
|
||||||
return ret.uinInfo.get(Uid);
|
let uin = ret.uinInfo.get(Uid);
|
||||||
|
if (!uin) {
|
||||||
|
//从Buddy缓存获取Uin
|
||||||
|
Array.from(friends.values()).forEach((t) => {
|
||||||
|
if (t.uid == Uid) {
|
||||||
|
uin = t.uin;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!uin) {
|
||||||
|
uin = (await NTQQUserApi.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!uin) {
|
||||||
|
// uin = (await NTQQFriendApi.getFriends(false)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 缓存转换
|
||||||
|
// }
|
||||||
|
// if (!uin) {
|
||||||
|
// uin = (await NTQQFriendApi.getFriends(true)).find((t) => { t.uid == Uid })?.uin; //从QQ Native 非缓存转换
|
||||||
|
// }
|
||||||
|
return uin;
|
||||||
|
}
|
||||||
|
static async getRecentContactListSnapShot(count: number) {
|
||||||
|
return await napCatCore.session.getRecentContactService().getRecentContactListSnapShot(count);
|
||||||
|
}
|
||||||
|
static async getRecentContactListSyncLimit(count: number) {
|
||||||
|
return await napCatCore.session.getRecentContactService().getRecentContactListSyncLimit(count);
|
||||||
|
}
|
||||||
|
static async getRecentContactListSync() {
|
||||||
|
return await napCatCore.session.getRecentContactService().getRecentContactListSync();
|
||||||
|
}
|
||||||
|
static async getRecentContactList() {
|
||||||
|
return await napCatCore.session.getRecentContactService().getRecentContactList();
|
||||||
}
|
}
|
||||||
static async getUserDetailInfoByUin(Uin: string) {
|
static async getUserDetailInfoByUin(Uin: string) {
|
||||||
return NTEventDispatch.CallNoListenerEvent
|
return NTEventDispatch.CallNoListenerEvent
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import QQWrapper, { NodeIQQNTWrapperEngine, NodeIQQNTWrapperSession, NodeQQNTWrapperUtil } from '@/core/wrapper';
|
import QQWrapper, { NodeIQQNTWrapperEngine, NodeIQQNTWrapperSession, NodeQQNTWrapperUtil } from '@/core/wrapper';
|
||||||
|
import { DeviceList } from '@/onebot11/main';
|
||||||
import {
|
import {
|
||||||
NodeIKernelLoginService,
|
NodeIKernelLoginService,
|
||||||
NodeIKernelBuddyService,
|
NodeIKernelBuddyService,
|
||||||
@@ -17,11 +18,10 @@ import fs from 'node:fs';
|
|||||||
import { appid, qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
|
import { appid, qqVersionConfigInfo } from '@/common/utils/QQBasicInfo';
|
||||||
import { hostname, systemVersion } from '@/common/utils/system';
|
import { hostname, systemVersion } from '@/common/utils/system';
|
||||||
import { genSessionConfig } from '@/core/sessionConfig';
|
import { genSessionConfig } from '@/core/sessionConfig';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { sleep } from '@/common/utils/helper';
|
import { sleep } from '@/common/utils/helper';
|
||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
import { rawFriends, friends, groupMembers, groups, selfInfo, stat } from '@/core/data';
|
import { rawFriends, friends, groupMembers, groups, selfInfo, stat } from '@/core/data';
|
||||||
import { RawMessage } from '@/core/entities';
|
import { GroupMember, RawMessage } from '@/core/entities';
|
||||||
import { NTEventDispatch } from '@/common/utils/EventTask';
|
import { NTEventDispatch } from '@/common/utils/EventTask';
|
||||||
import {
|
import {
|
||||||
enableConsoleLog,
|
enableConsoleLog,
|
||||||
@@ -84,20 +84,21 @@ export class NapCatCore {
|
|||||||
const dataPath = path.resolve(this.dataPath, './NapCat/data');
|
const dataPath = path.resolve(this.dataPath, './NapCat/data');
|
||||||
fs.mkdirSync(dataPath, { recursive: true });
|
fs.mkdirSync(dataPath, { recursive: true });
|
||||||
logDebug('本账号数据/缓存目录:', dataPath);
|
logDebug('本账号数据/缓存目录:', dataPath);
|
||||||
dbUtil.init(path.resolve(dataPath, `./${arg.uin}-v2.db`)).then(() => {
|
// dbUtil.init(path.resolve(dataPath, `./${arg.uin}-v2.db`)).then(() => {
|
||||||
this.initDataListener();
|
this.initDataListener();
|
||||||
this.onLoginSuccessFuncList.map(cb => {
|
this.onLoginSuccessFuncList.map(cb => {
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const result = cb(arg.uin, arg.uid);
|
const result = cb(arg.uin, arg.uid);
|
||||||
if (result instanceof Promise) {
|
if (result instanceof Promise) {
|
||||||
result.then(resolve).catch(reject);
|
result.then(resolve).catch(reject);
|
||||||
}
|
}
|
||||||
}).then();
|
}).then();
|
||||||
});
|
|
||||||
}).catch((e) => {
|
|
||||||
logError('数据库初始化失败', e);
|
|
||||||
});
|
});
|
||||||
|
// }).catch((e) => {
|
||||||
|
// logError('数据库初始化失败', e);
|
||||||
|
// });
|
||||||
// this.initDataListener();
|
// this.initDataListener();
|
||||||
|
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
logError('initSession failed', e);
|
logError('initSession failed', e);
|
||||||
throw new Error(`启动失败: ${JSON.stringify(e)}`);
|
throw new Error(`启动失败: ${JSON.stringify(e)}`);
|
||||||
@@ -108,8 +109,8 @@ export class NapCatCore {
|
|||||||
logError('登录失败(onQRCodeSessionFailed)', errMsg);
|
logError('登录失败(onQRCodeSessionFailed)', errMsg);
|
||||||
if (errType == 1 && errCode == 3) {
|
if (errType == 1 && errCode == 3) {
|
||||||
// 二维码过期刷新
|
// 二维码过期刷新
|
||||||
this.loginService.getQRCodePicture();
|
|
||||||
}
|
}
|
||||||
|
this.loginService.getQRCodePicture();
|
||||||
};
|
};
|
||||||
this.loginListener.onLoginFailed = (args) => {
|
this.loginListener.onLoginFailed = (args) => {
|
||||||
logError('登录失败(onLoginFailed)', args);
|
logError('登录失败(onLoginFailed)', args);
|
||||||
@@ -189,7 +190,6 @@ export class NapCatCore {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private initDataListener() {
|
private initDataListener() {
|
||||||
// 消息相关
|
// 消息相关
|
||||||
interface LineDevice {
|
interface LineDevice {
|
||||||
@@ -208,10 +208,17 @@ export class NapCatCore {
|
|||||||
}
|
}
|
||||||
const msgListener = new MsgListener();
|
const msgListener = new MsgListener();
|
||||||
msgListener.onLineDev = (Devices: LineDevice[]) => {
|
msgListener.onLineDev = (Devices: LineDevice[]) => {
|
||||||
|
DeviceList.splice(0, DeviceList.length);
|
||||||
Devices.map((Device: LineDevice) => {
|
Devices.map((Device: LineDevice) => {
|
||||||
if (Device.clientType === 2) {
|
let DeviceData = {
|
||||||
log('账号设备(' + Device.devUid + ') 在线状态变更');
|
app_id: Device.devUid,
|
||||||
}
|
device_name: Device.clientType.toString(),
|
||||||
|
device_kind: Device.clientType.toString(),
|
||||||
|
};
|
||||||
|
DeviceList.push(DeviceData);
|
||||||
|
// if (Device.clientType === 2) {
|
||||||
|
// log('账号设备(' + Device.devUid + ') 在线状态变更');
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
||||||
@@ -242,6 +249,7 @@ export class NapCatCore {
|
|||||||
stat.last_message_time = Math.floor(Date.now() / 1000);
|
stat.last_message_time = Math.floor(Date.now() / 1000);
|
||||||
};
|
};
|
||||||
msgListener.onRecvMsg = (msgList: RawMessage[]) => {
|
msgListener.onRecvMsg = (msgList: RawMessage[]) => {
|
||||||
|
// console.log(JSON.stringify(msgList[0],null,2));
|
||||||
stat.packet_received += 1;
|
stat.packet_received += 1;
|
||||||
stat.message_received += msgList.length;
|
stat.message_received += msgList.length;
|
||||||
stat.last_message_time = Math.floor(Date.now() / 1000);
|
stat.last_message_time = Math.floor(Date.now() / 1000);
|
||||||
@@ -346,7 +354,7 @@ export class NapCatCore {
|
|||||||
// console.log('onMemberListChange', groupCode, arg);
|
// console.log('onMemberListChange', groupCode, arg);
|
||||||
};
|
};
|
||||||
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
|
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
|
||||||
// console.log('onMemberInfoChange', arg);
|
//console.log('onMemberInfoChange', groupCode, changeType, members);
|
||||||
if (changeType === 0 && members.get(selfInfo.uid)?.isDelete) {
|
if (changeType === 0 && members.get(selfInfo.uid)?.isDelete) {
|
||||||
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
|
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -359,6 +367,9 @@ export class NapCatCore {
|
|||||||
members.forEach((member, uid) => {
|
members.forEach((member, uid) => {
|
||||||
const existMember = existMembers.get(uid);
|
const existMember = existMembers.get(uid);
|
||||||
if (existMember) {
|
if (existMember) {
|
||||||
|
// 检查管理变动
|
||||||
|
member.isChangeRole = this.checkAdminEvent(groupCode, member, existMember);
|
||||||
|
// 更新成员信息
|
||||||
Object.assign(existMember, member);
|
Object.assign(existMember, member);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -471,6 +482,13 @@ export class NapCatCore {
|
|||||||
const loginList = await this.loginService.getLoginList();
|
const loginList = await this.loginService.getLoginList();
|
||||||
return loginList;
|
return loginList;
|
||||||
}
|
}
|
||||||
|
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
|
||||||
|
if (memberNew.role !== memberOld?.role) {
|
||||||
|
log(`群 ${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const napCatCore = new NapCatCore();
|
export const napCatCore = new NapCatCore();
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
type Friend,
|
type Friend,
|
||||||
type Group,
|
type Group,
|
||||||
type GroupMember, GroupNotify,
|
type GroupMember,
|
||||||
type SelfInfo,
|
type SelfInfo,
|
||||||
BuddyCategoryType
|
type BuddyCategoryType
|
||||||
} from './entities';
|
} from './entities';
|
||||||
import { isNumeric } from '@/common/utils/helper';
|
import { isNumeric } from '@/common/utils/helper';
|
||||||
import { NTQQGroupApi } from '@/core/apis';
|
import { NTQQGroupApi } from '@/core/apis';
|
||||||
@@ -30,7 +30,6 @@ export const groupMembers: Map<string, Map<string, GroupMember>> = new Map<strin
|
|||||||
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
export const friends: Map<string, Friend> = new Map<string, Friend>();
|
||||||
export const rawFriends: Array<BuddyCategoryType> = []; // 带分组的好友列表
|
export const rawFriends: Array<BuddyCategoryType> = []; // 带分组的好友列表
|
||||||
|
|
||||||
export const groupNotifies: Record<string, GroupNotify> = {}; // flag->GroupNotify
|
|
||||||
export async function getGroup(qq: string | number): Promise<Group | undefined> {
|
export async function getGroup(qq: string | number): Promise<Group | undefined> {
|
||||||
let group = groups.get(qq.toString());
|
let group = groups.get(qq.toString());
|
||||||
if (!group) {
|
if (!group) {
|
||||||
|
@@ -8,8 +8,10 @@ import {
|
|||||||
SendPicElement,
|
SendPicElement,
|
||||||
SendPttElement,
|
SendPttElement,
|
||||||
SendReplyElement,
|
SendReplyElement,
|
||||||
|
sendShareLocationElement,
|
||||||
SendTextElement,
|
SendTextElement,
|
||||||
SendVideoElement
|
SendVideoElement,
|
||||||
|
viedo_type
|
||||||
} from './index';
|
} from './index';
|
||||||
import { promises as fs } from 'node:fs';
|
import { promises as fs } from 'node:fs';
|
||||||
import ffmpeg from 'fluent-ffmpeg';
|
import ffmpeg from 'fluent-ffmpeg';
|
||||||
@@ -27,6 +29,16 @@ export const mFaceCache = new Map<string, string>(); // emojiId -> faceName
|
|||||||
|
|
||||||
|
|
||||||
export class SendMsgElementConstructor {
|
export class SendMsgElementConstructor {
|
||||||
|
static location(): sendShareLocationElement {
|
||||||
|
return {
|
||||||
|
elementType: ElementType.SHARELOCATION,
|
||||||
|
elementId: '',
|
||||||
|
shareLocationElement: {
|
||||||
|
text: "测试",
|
||||||
|
ext: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
static text(content: string): SendTextElement {
|
static text(content: string): SendTextElement {
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.TEXT,
|
elementType: ElementType.TEXT,
|
||||||
@@ -116,7 +128,7 @@ export class SendMsgElementConstructor {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async video(filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
|
static async video(filePath: string, fileName: string = '', diyThumbPath: string = '', videotype: viedo_type = viedo_type.VIDEO_FORMAT_MP4): Promise<SendVideoElement> {
|
||||||
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
|
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
|
||||||
if (fileSize === 0) {
|
if (fileSize === 0) {
|
||||||
throw '文件异常,大小为0';
|
throw '文件异常,大小为0';
|
||||||
@@ -184,6 +196,7 @@ export class SendMsgElementConstructor {
|
|||||||
thumbWidth: videoInfo.width,
|
thumbWidth: videoInfo.width,
|
||||||
thumbHeight: videoInfo.height,
|
thumbHeight: videoInfo.height,
|
||||||
fileSize: '' + fileSize,
|
fileSize: '' + fileSize,
|
||||||
|
//fileFormat: videotype
|
||||||
// fileUuid: "",
|
// fileUuid: "",
|
||||||
// transferStatus: 0,
|
// transferStatus: 0,
|
||||||
// progress: 0,
|
// progress: 0,
|
||||||
@@ -235,7 +248,28 @@ export class SendMsgElementConstructor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// NodeIQQNTWrapperSession sendMsg [
|
||||||
|
// "0",
|
||||||
|
// {
|
||||||
|
// "peerUid": "u_e_RIxgTs2NaJ68h0PwOPSg",
|
||||||
|
// "chatType": 1,
|
||||||
|
// "guildId": ""
|
||||||
|
// },
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// "elementId": "0",
|
||||||
|
// "elementType": 6,
|
||||||
|
// "faceElement": {
|
||||||
|
// "faceIndex": 0,
|
||||||
|
// "faceType": 5,
|
||||||
|
// "msgType": 0,
|
||||||
|
// "pokeType": 1,
|
||||||
|
// "pokeStrength": 0
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
// {}
|
||||||
|
// ]
|
||||||
static face(faceId: number): SendFaceElement {
|
static face(faceId: number): SendFaceElement {
|
||||||
// 从face_config.json中获取表情名称
|
// 从face_config.json中获取表情名称
|
||||||
const sysFaces = faceConfig.sysface;
|
const sysFaces = faceConfig.sysface;
|
||||||
|
@@ -52,4 +52,5 @@ export interface GroupMember {
|
|||||||
isRobot: boolean;
|
isRobot: boolean;
|
||||||
sex?: Sex
|
sex?: Sex
|
||||||
qqLevel?: QQLevel
|
qqLevel?: QQLevel
|
||||||
|
isChangeRole: boolean;
|
||||||
}
|
}
|
@@ -23,6 +23,7 @@ export interface GetFileListParam {
|
|||||||
showOnlinedocFolder: number
|
showOnlinedocFolder: number
|
||||||
}
|
}
|
||||||
export enum ElementType {
|
export enum ElementType {
|
||||||
|
UNKNOWN = 0,
|
||||||
TEXT = 1,
|
TEXT = 1,
|
||||||
PIC = 2,
|
PIC = 2,
|
||||||
FILE = 3,
|
FILE = 3,
|
||||||
@@ -30,11 +31,182 @@ export enum ElementType {
|
|||||||
VIDEO = 5,
|
VIDEO = 5,
|
||||||
FACE = 6,
|
FACE = 6,
|
||||||
REPLY = 7,
|
REPLY = 7,
|
||||||
|
WALLET = 9,
|
||||||
|
GreyTip = 8,//Poke别叫戳一搓了 官方名字拍一拍 戳一戳是另一个名字
|
||||||
ARK = 10,
|
ARK = 10,
|
||||||
MFACE = 11,
|
MFACE = 11,
|
||||||
MARKDOWN = 14
|
LIVEGIFT = 12,
|
||||||
|
STRUCTLONGMSG = 13,
|
||||||
|
MARKDOWN = 14,
|
||||||
|
GIPHY = 15,
|
||||||
|
MULTIFORWARD = 16,
|
||||||
|
INLINEKEYBOARD = 17,
|
||||||
|
INTEXTGIFT = 18,
|
||||||
|
CALENDAR = 19,
|
||||||
|
YOLOGAMERESULT = 20,
|
||||||
|
AVRECORD = 21,
|
||||||
|
FEED = 22,
|
||||||
|
TOFURECORD = 23,
|
||||||
|
ACEBUBBLE = 24,
|
||||||
|
ACTIVITY = 25,
|
||||||
|
TOFU = 26,
|
||||||
|
FACEBUBBLE = 27,
|
||||||
|
SHARELOCATION = 28,
|
||||||
|
TASKTOPMSG = 29,
|
||||||
|
RECOMMENDEDMSG = 43,
|
||||||
|
ACTIONBAR = 44
|
||||||
}
|
}
|
||||||
|
export interface SendActionBarElement {
|
||||||
|
elementType: ElementType.ACTIONBAR;
|
||||||
|
elementId: string;
|
||||||
|
actionBarElement: {
|
||||||
|
rows: InlineKeyboardRow[];
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendRecommendedMsgElement {
|
||||||
|
elementType: ElementType.RECOMMENDEDMSG;
|
||||||
|
elementId: string;
|
||||||
|
recommendedMsgElement: {
|
||||||
|
rows: InlineKeyboardRow[];
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface InlineKeyboardButton {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
visitedLabel: string;
|
||||||
|
unsupportTips: string;
|
||||||
|
data: string;
|
||||||
|
specifyRoleIds: string[];
|
||||||
|
specifyTinyids: string[];
|
||||||
|
style: number;
|
||||||
|
type: number;
|
||||||
|
clickLimit: number;
|
||||||
|
atBotShowChannelList: boolean;
|
||||||
|
permissionType: number;
|
||||||
|
}
|
||||||
|
export interface InlineKeyboardRow {
|
||||||
|
buttons: InlineKeyboardButton[];
|
||||||
|
}
|
||||||
|
export interface TofuElementContent {
|
||||||
|
color: string;
|
||||||
|
tittle: string;
|
||||||
|
}
|
||||||
|
export interface SendTaskTopMsgElement {
|
||||||
|
elementType: ElementType.TASKTOPMSG;
|
||||||
|
elementId: string;
|
||||||
|
taskTopMsgElement: {
|
||||||
|
msgTitle: string;
|
||||||
|
msgSummary: string;
|
||||||
|
iconUrl: string;
|
||||||
|
topMsgType: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendTofuRecordElement {
|
||||||
|
elementType: ElementType.TOFURECORD;
|
||||||
|
elementId: string;
|
||||||
|
tofuRecordElement: {
|
||||||
|
type: number;
|
||||||
|
busiid: string;
|
||||||
|
busiuuid: string;
|
||||||
|
descriptionContent: string;
|
||||||
|
contentlist: TofuElementContent[],
|
||||||
|
background: string;
|
||||||
|
icon: string;
|
||||||
|
uinlist: string[],
|
||||||
|
uidlist: string[],
|
||||||
|
busiExtra: string;
|
||||||
|
updateTime: string;
|
||||||
|
dependedmsgid: string;
|
||||||
|
msgtime: string;
|
||||||
|
onscreennotify: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendFaceBubbleElement {
|
||||||
|
elementType: ElementType.FACEBUBBLE;
|
||||||
|
elementId: string;
|
||||||
|
faceBubbleElement: {
|
||||||
|
faceCount: number;
|
||||||
|
faceSummary: string;
|
||||||
|
faceFlag: number;
|
||||||
|
content: string;
|
||||||
|
oldVersionStr: string;
|
||||||
|
faceType: number;
|
||||||
|
others: string;
|
||||||
|
yellowFaceInfo: {
|
||||||
|
index: number;
|
||||||
|
buf: string;
|
||||||
|
compatibleText: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface SendavRecordElement {
|
||||||
|
elementType: ElementType.AVRECORD;
|
||||||
|
elementId: string;
|
||||||
|
avRecordElement: {
|
||||||
|
type: number;
|
||||||
|
time: string;
|
||||||
|
text: string;
|
||||||
|
mainType: number;
|
||||||
|
hasRead: boolean;
|
||||||
|
extraType: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface YoloUserInfo {
|
||||||
|
uid: string;
|
||||||
|
result: number;
|
||||||
|
rank: number;
|
||||||
|
bizId: string
|
||||||
|
}
|
||||||
|
export interface SendInlineKeyboardElement {
|
||||||
|
elementType: ElementType.INLINEKEYBOARD;
|
||||||
|
elementId: string;
|
||||||
|
inlineKeyboardElement: {
|
||||||
|
rows: number;
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface SendYoloGameResultElement {
|
||||||
|
elementType: ElementType.YOLOGAMERESULT;
|
||||||
|
yoloGameResultElement: {
|
||||||
|
UserInfo: YoloUserInfo[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendGiphyElement {
|
||||||
|
elementType: ElementType.GIPHY;
|
||||||
|
elementId: string;
|
||||||
|
giphyElement: {
|
||||||
|
id: string;
|
||||||
|
isClip: boolean;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendWalletElement {
|
||||||
|
elementType: ElementType.UNKNOWN;//不做 设置位置
|
||||||
|
elementId: string;
|
||||||
|
walletElement: {}
|
||||||
|
}
|
||||||
|
export interface SendCalendarElement {
|
||||||
|
elementType: ElementType.CALENDAR;
|
||||||
|
elementId: string;
|
||||||
|
calendarElement: {
|
||||||
|
summary: string;
|
||||||
|
msg: string;
|
||||||
|
expireTimeMs: string;
|
||||||
|
schemaType: number;
|
||||||
|
schema: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendliveGiftElement {
|
||||||
|
elementType: ElementType.LIVEGIFT;
|
||||||
|
elementId: string;
|
||||||
|
liveGiftElement: {}
|
||||||
|
}
|
||||||
export interface SendTextElement {
|
export interface SendTextElement {
|
||||||
elementType: ElementType.TEXT;
|
elementType: ElementType.TEXT;
|
||||||
elementId: string;
|
elementId: string;
|
||||||
@@ -118,6 +290,30 @@ export interface SendMarketFaceElement {
|
|||||||
elementType: ElementType.MFACE;
|
elementType: ElementType.MFACE;
|
||||||
marketFaceElement: MarketFaceElement;
|
marketFaceElement: MarketFaceElement;
|
||||||
}
|
}
|
||||||
|
export interface SendstructLongMsgElement {
|
||||||
|
elementType: ElementType.STRUCTLONGMSG;
|
||||||
|
elementId: string;
|
||||||
|
structLongMsgElement: {
|
||||||
|
xmlContent: string;
|
||||||
|
resId: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface SendactionBarElement {
|
||||||
|
elementType: ElementType.ACTIONBAR;
|
||||||
|
elementId: string;
|
||||||
|
actionBarElement: {
|
||||||
|
rows: number;
|
||||||
|
botAppid: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface sendShareLocationElement {
|
||||||
|
elementType: ElementType.SHARELOCATION;
|
||||||
|
elementId: string;
|
||||||
|
shareLocationElement: {
|
||||||
|
text: string;
|
||||||
|
ext: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface FileElement {
|
export interface FileElement {
|
||||||
fileMd5?: string;
|
fileMd5?: string;
|
||||||
@@ -162,7 +358,7 @@ export interface SendMarkdownElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type SendMessageElement = SendTextElement | SendPttElement |
|
export type SendMessageElement = SendTextElement | SendPttElement |
|
||||||
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement | SendVideoElement | SendArkElement | SendMarkdownElement
|
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement | SendVideoElement | SendArkElement | SendMarkdownElement | sendShareLocationElement
|
||||||
|
|
||||||
export enum AtType {
|
export enum AtType {
|
||||||
notAt = 0,
|
notAt = 0,
|
||||||
@@ -289,6 +485,7 @@ export interface GrayTipElement {
|
|||||||
templId: string;
|
templId: string;
|
||||||
};
|
};
|
||||||
jsonGrayTipElement: {
|
jsonGrayTipElement: {
|
||||||
|
busiId?: number;
|
||||||
jsonStr: string;
|
jsonStr: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -329,7 +526,7 @@ export interface VideoElement {
|
|||||||
thumbMd5?: string
|
thumbMd5?: string
|
||||||
fileTime?: number; // second
|
fileTime?: number; // second
|
||||||
thumbSize?: number; // byte
|
thumbSize?: number; // byte
|
||||||
fileFormat?: number; // 2表示mp4 参考下面条目
|
fileFormat?: viedo_type; // 2表示mp4 参考下面条目
|
||||||
fileSize?: string; // byte
|
fileSize?: string; // byte
|
||||||
thumbWidth?: number;
|
thumbWidth?: number;
|
||||||
thumbHeight?: number;
|
thumbHeight?: number;
|
||||||
@@ -490,6 +687,7 @@ export interface MultiForwardMsgElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RawMessage {
|
export interface RawMessage {
|
||||||
|
msgRandom: string;
|
||||||
// int32, 自己维护的消息id
|
// int32, 自己维护的消息id
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
|
@@ -40,14 +40,14 @@ export interface onGroupFileInfoUpdateParamType {
|
|||||||
// fromNick: '拾xxxx,
|
// fromNick: '拾xxxx,
|
||||||
// sig: '0x'
|
// sig: '0x'
|
||||||
// }
|
// }
|
||||||
export interface TempOnRecvParams{
|
export interface TempOnRecvParams {
|
||||||
sessionType: number,//1
|
sessionType: number,//1
|
||||||
chatType: ChatType,//100
|
chatType: ChatType,//100
|
||||||
peerUid: string,//uid
|
peerUid: string,//uid
|
||||||
groupCode: string,//gc
|
groupCode: string,//gc
|
||||||
fromNick: string,//gc name
|
fromNick: string,//gc name
|
||||||
sig: string,
|
sig: string,
|
||||||
|
|
||||||
}
|
}
|
||||||
export interface IKernelMsgListener {
|
export interface IKernelMsgListener {
|
||||||
onAddSendMsg(msgRecord: RawMessage): void;
|
onAddSendMsg(msgRecord: RawMessage): void;
|
||||||
@@ -158,7 +158,41 @@ export interface IKernelMsgListener {
|
|||||||
|
|
||||||
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void;
|
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void;
|
||||||
|
|
||||||
onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown): void;
|
onSearchGroupFileInfoUpdate(searchGroupFileResult:
|
||||||
|
{
|
||||||
|
result: {
|
||||||
|
retCode: number,
|
||||||
|
retMsg: string,
|
||||||
|
clientWording: string
|
||||||
|
},
|
||||||
|
syncCookie: string,
|
||||||
|
totalMatchCount: number,
|
||||||
|
ownerMatchCount: number,
|
||||||
|
isEnd: boolean,
|
||||||
|
reqId: number,
|
||||||
|
item: Array<{
|
||||||
|
groupCode: string,
|
||||||
|
groupName: string,
|
||||||
|
uploaderUin: string,
|
||||||
|
uploaderName: string,
|
||||||
|
matchUin: string,
|
||||||
|
matchWords: Array<unknown>,
|
||||||
|
fileNameHits: Array<{
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
}>,
|
||||||
|
fileModelId: string,
|
||||||
|
fileId: string,
|
||||||
|
fileName: string,
|
||||||
|
fileSize: string,
|
||||||
|
busId: number,
|
||||||
|
uploadTime: number,
|
||||||
|
modifyTime: number,
|
||||||
|
deadTime: number,
|
||||||
|
downloadTimes: number,
|
||||||
|
localPath: string
|
||||||
|
}>
|
||||||
|
}): void;
|
||||||
|
|
||||||
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void;
|
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void;
|
||||||
|
|
||||||
|
44
src/core/src/listeners/NodeIKernelRecentContactListener.ts
Normal file
44
src/core/src/listeners/NodeIKernelRecentContactListener.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
interface IKernelRecentContactListener {
|
||||||
|
onDeletedContactsNotify(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactNotification(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onMsgUnreadCountUpdate(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onGuildDisplayRecentContactListChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactListChanged(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
onRecentContactListChangedVer2(...args: unknown[]): unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeIKernelRecentContactListener extends IKernelRecentContactListener {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-new
|
||||||
|
new(listener: IKernelRecentContactListener): NodeIKernelRecentContactListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KernelRecentContactListener implements IKernelRecentContactListener {
|
||||||
|
onDeletedContactsNotify(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactNotification(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMsgUnreadCountUpdate(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onGuildDisplayRecentContactListChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactListChanged(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecentContactListChangedVer2(...args: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ import {
|
|||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
export interface NodeIKernelGroupService {
|
export interface NodeIKernelGroupService {
|
||||||
|
setHeader(uid:string,path:string): unknown;
|
||||||
|
|
||||||
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
|
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
|
||||||
|
|
||||||
@@ -149,5 +150,18 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
//需要提前判断是否存在 高版本新增
|
||||||
|
addGroupEssence(param: {
|
||||||
|
groupCode: string
|
||||||
|
msgRandom: number,
|
||||||
|
msgSeq: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
//需要提前判断是否存在 高版本新增
|
||||||
|
removeGroupEssence(param: {
|
||||||
|
groupCode: string
|
||||||
|
msgRandom: number,
|
||||||
|
msgSeq: number
|
||||||
|
}): Promise<unknown>;
|
||||||
|
|
||||||
isNull(): boolean;
|
isNull(): boolean;
|
||||||
}
|
}
|
||||||
|
3
src/core/src/services/NodeIKernelMSFService.ts
Normal file
3
src/core/src/services/NodeIKernelMSFService.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface NodeIKernelMSFService {
|
||||||
|
getServerTime(): string;
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChatType, ElementType, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
import { ElementType, Peer, RawMessage, SendMessageElement } from '@/core/entities';
|
||||||
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
|
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
|
||||||
import { GeneralCallResult } from '@/core/services/common';
|
import { GeneralCallResult } from '@/core/services/common';
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getAutoReplyTextList(...args: unknown[]): unknown;
|
getAutoReplyTextList(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getOnLineDev(): Promise<any>;
|
getOnLineDev(): void;
|
||||||
|
|
||||||
kickOffLine(DevInfo: Object): unknown;
|
kickOffLine(DevInfo: Object): unknown;
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
addLocalTofuRecordMsg(...args: unknown[]): unknown;
|
addLocalTofuRecordMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
addLocalRecordMsg(...args: unknown[]): unknown;
|
addLocalRecordMsg(Peer: Peer, msgId: string, rawMessage: RawMessage, attr: Array<any> | number, front: boolean): Promise<unknown>;
|
||||||
|
|
||||||
deleteMsg(...args: unknown[]): unknown;
|
deleteMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getLastMessageList(peer: Peer[]): Promise<unknown>;
|
getLastMessageList(peer: Peer[]): Promise<unknown>;
|
||||||
|
|
||||||
getAioFirstViewLatestMsgs(...args: unknown[]): unknown;
|
getAioFirstViewLatestMsgs(peer: Peer, unknown: number): unknown;
|
||||||
|
|
||||||
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
|
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
|
||||||
|
|
||||||
@@ -186,27 +186,48 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getSourceOfReplyMsgByClientSeqAndTime(...args: unknown[]): unknown;
|
getSourceOfReplyMsgByClientSeqAndTime(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getMsgsByTypeFilter(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilters: unknown): unknown;
|
getMsgsByTypeFilter(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilter: { type: number, subtype: Array<number> }): unknown;
|
||||||
|
|
||||||
getMsgsByTypeFilters(...args: unknown[]): unknown;
|
getMsgsByTypeFilters(peer: Peer, msgId: string, cnt: unknown, queryOrder: boolean, typeFilters: Array<{ type: number, subtype: Array<number> }>): unknown;
|
||||||
|
|
||||||
getMsgWithAbstractByFilterParam(...args: unknown[]): unknown;
|
getMsgWithAbstractByFilterParam(...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryMsgsWithFilter(...args: unknown[]): unknown;
|
queryMsgsWithFilter(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated 该函数已被标记为废弃,请使用新的替代方法。
|
||||||
|
* 使用过滤条件查询消息列表的版本2接口。
|
||||||
|
*
|
||||||
|
* 该函数通过一系列过滤条件来查询特定聊天中的消息列表。这些条件包括消息类型、发送者、时间范围等。
|
||||||
|
* 函数返回一个Promise,解析为查询结果的未知类型对象。
|
||||||
|
*
|
||||||
|
* @param MsgId 消息ID,用于特定消息的查询。
|
||||||
|
* @param MsgTime 消息时间,用于指定消息的时间范围。
|
||||||
|
* @param param 查询参数对象,包含详细的过滤条件和分页信息。
|
||||||
|
* @param param.chatInfo 聊天信息,包括聊天类型和对方用户ID。
|
||||||
|
* @param param.filterMsgType 需要过滤的消息类型数组,留空表示不过滤。
|
||||||
|
* @param param.filterSendersUid 需要过滤的发送者用户ID数组。
|
||||||
|
* @param param.filterMsgFromTime 查询消息的起始时间。
|
||||||
|
* @param param.filterMsgToTime 查询消息的结束时间。
|
||||||
|
* @param param.pageLimit 每页的消息数量限制。
|
||||||
|
* @param param.isReverseOrder 是否按时间顺序倒序返回消息。
|
||||||
|
* @param param.isIncludeCurrent 是否包含当前页码。
|
||||||
|
* @returns 返回一个Promise,解析为查询结果的未知类型对象。
|
||||||
|
*/
|
||||||
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: {
|
queryMsgsWithFilterVer2(MsgId: string, MsgTime: string, param: {
|
||||||
chatInfo: {
|
chatInfo: {
|
||||||
chatType: number,
|
chatType: number,
|
||||||
peerUid: string
|
peerUid: string
|
||||||
},
|
},
|
||||||
filterMsgType: [],
|
filterMsgType: [],
|
||||||
filterSendersUid: [],
|
filterSendersUid: Array<string>,
|
||||||
filterMsgFromTime: string,
|
filterMsgFromTime: string,
|
||||||
filterMsgToTime: string,
|
filterMsgToTime: string,
|
||||||
pageLimit: number,
|
pageLimit: number,
|
||||||
isReverseOrder: boolean,
|
isReverseOrder: boolean,
|
||||||
isIncludeCurrent: boolean
|
isIncludeCurrent: boolean
|
||||||
}): Promise<unknown>;
|
}): Promise<unknown>;
|
||||||
|
|
||||||
// this.chatType = i2;
|
// this.chatType = i2;
|
||||||
// this.peerUid = str;
|
// this.peerUid = str;
|
||||||
|
|
||||||
@@ -228,14 +249,15 @@ export interface NodeIKernelMsgService {
|
|||||||
peerUid: string
|
peerUid: string
|
||||||
},
|
},
|
||||||
filterMsgType: [],
|
filterMsgType: [],
|
||||||
filterSendersUid: [],
|
filterSendersUid: string[],
|
||||||
filterMsgFromTime: string,
|
filterMsgFromTime: string,
|
||||||
filterMsgToTime: string,
|
filterMsgToTime: string,
|
||||||
pageLimit: number,
|
pageLimit: number,
|
||||||
isReverseOrder: boolean,
|
isReverseOrder: boolean,
|
||||||
isIncludeCurrent: boolean
|
isIncludeCurrent: boolean
|
||||||
|
}): Promise<GeneralCallResult & {
|
||||||
}): Promise<unknown>;
|
msgList: RawMessage[]
|
||||||
|
}>;
|
||||||
//queryMsgsWithFilterEx(this.$msgId, this.$msgTime, this.$msgSeq, this.$param)
|
//queryMsgsWithFilterEx(this.$msgId, this.$msgTime, this.$msgSeq, this.$param)
|
||||||
queryFileMsgsDesktop(...args: unknown[]): unknown;
|
queryFileMsgsDesktop(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -253,7 +275,7 @@ export interface NodeIKernelMsgService {
|
|||||||
pageLimit: number,
|
pageLimit: number,
|
||||||
isReverseOrder: boolean,
|
isReverseOrder: boolean,
|
||||||
isIncludeCurrent: boolean
|
isIncludeCurrent: boolean
|
||||||
}):Promise<unknown>;
|
}): Promise<unknown>;
|
||||||
|
|
||||||
queryPicOrVideoMsgsDesktop(...args: unknown[]): unknown;
|
queryPicOrVideoMsgsDesktop(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -261,7 +283,19 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
queryTroopEmoticonMsgs(...args: unknown[]): unknown;
|
queryTroopEmoticonMsgs(...args: unknown[]): unknown;
|
||||||
|
|
||||||
queryMsgsAndAbstractsWithFilter(...args: unknown[]): unknown;
|
queryMsgsAndAbstractsWithFilter(msgId: string, msgTime: string, megSeq: string, param: {
|
||||||
|
chatInfo: {
|
||||||
|
chatType: number,
|
||||||
|
peerUid: string
|
||||||
|
},
|
||||||
|
filterMsgType: [],
|
||||||
|
filterSendersUid: [],
|
||||||
|
filterMsgFromTime: string,
|
||||||
|
filterMsgToTime: string,
|
||||||
|
pageLimit: number,
|
||||||
|
isReverseOrder: boolean,
|
||||||
|
isIncludeCurrent: boolean
|
||||||
|
}): unknown;
|
||||||
|
|
||||||
setFocusOnGuild(...args: unknown[]): unknown;
|
setFocusOnGuild(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -299,7 +333,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
setMsgRead(peer: Peer): Promise<GeneralCallResult>;
|
setMsgRead(peer: Peer): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
setAllC2CAndGroupMsgRead(...args: unknown[]): unknown;
|
setAllC2CAndGroupMsgRead(): Promise<unknown>;
|
||||||
|
|
||||||
setGuildMsgRead(...args: unknown[]): unknown;
|
setGuildMsgRead(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -355,8 +389,8 @@ export interface NodeIKernelMsgService {
|
|||||||
getFileThumbSavePathForSend(...args: unknown[]): unknown;
|
getFileThumbSavePathForSend(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getFileThumbSavePath(...args: unknown[]): unknown;
|
getFileThumbSavePath(...args: unknown[]): unknown;
|
||||||
|
//猜测居多
|
||||||
translatePtt2Text(...args: unknown[]): unknown;
|
translatePtt2Text(MsgId: string, Peer: {}, MsgElement: {}): unknown;
|
||||||
|
|
||||||
setPttPlayedState(...args: unknown[]): unknown;
|
setPttPlayedState(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -406,7 +440,7 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
getEmojiResourcePath(...args: unknown[]): unknown;
|
getEmojiResourcePath(...args: unknown[]): unknown;
|
||||||
|
|
||||||
JoinDragonGroupEmoji(...args: unknown[]): unknown;
|
JoinDragonGroupEmoji(JoinDragonGroupEmojiReq: any/*joinDragonGroupEmojiReq*/): unknown;
|
||||||
|
|
||||||
getMsgAbstracts(...args: unknown[]): unknown;
|
getMsgAbstracts(...args: unknown[]): unknown;
|
||||||
|
|
||||||
@@ -430,7 +464,10 @@ export interface NodeIKernelMsgService {
|
|||||||
|
|
||||||
downloadRichMedia(...args: unknown[]): unknown;
|
downloadRichMedia(...args: unknown[]): unknown;
|
||||||
|
|
||||||
getFirstUnreadMsgSeq(...args: unknown[]): unknown;
|
getFirstUnreadMsgSeq(args: {
|
||||||
|
peerUid: string
|
||||||
|
guildId: string
|
||||||
|
}): unknown;
|
||||||
|
|
||||||
getFirstUnreadCommonMsg(...args: unknown[]): unknown;
|
getFirstUnreadCommonMsg(...args: unknown[]): unknown;
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { BuddyProfileLikeReq } from "../entities/user";
|
import { BuddyProfileLikeReq } from "../entities/user";
|
||||||
|
import { GeneralCallResult } from "./common";
|
||||||
|
|
||||||
export interface NodeIKernelProfileLikeService {
|
export interface NodeIKernelProfileLikeService {
|
||||||
addKernelProfileLikeListener(listener: NodeIKernelProfileLikeService): void;
|
addKernelProfileLikeListener(listener: NodeIKernelProfileLikeService): void;
|
||||||
@@ -7,7 +8,13 @@ export interface NodeIKernelProfileLikeService {
|
|||||||
|
|
||||||
setBuddyProfileLike(...args: unknown[]): { result: number, errMsg: string, succCounts: number };
|
setBuddyProfileLike(...args: unknown[]): { result: number, errMsg: string, succCounts: number };
|
||||||
|
|
||||||
getBuddyProfileLike(req: BuddyProfileLikeReq): void;
|
getBuddyProfileLike(req: BuddyProfileLikeReq): Promise<GeneralCallResult & {
|
||||||
|
"info": {
|
||||||
|
"userLikeInfos": Array<any>,
|
||||||
|
"friendMaxVotes": number,
|
||||||
|
"start": number
|
||||||
|
}
|
||||||
|
}>;
|
||||||
|
|
||||||
getProfileLikeScidResourceInfo(...args: unknown[]): void;
|
getProfileLikeScidResourceInfo(...args: unknown[]): void;
|
||||||
|
|
||||||
|
@@ -1,5 +1,28 @@
|
|||||||
import { Peer } from "../entities";
|
import { Peer } from "../entities";
|
||||||
|
import { NodeIKernelRecentContactListener } from "../listeners/NodeIKernelRecentContactListener";
|
||||||
|
import { GeneralCallResult } from "./common";
|
||||||
|
export interface FSABRecentContactParams {
|
||||||
|
anchorPointContact: {
|
||||||
|
contactId: string;
|
||||||
|
sortField: string;
|
||||||
|
pos: number;
|
||||||
|
},
|
||||||
|
relativeMoveCount: number;
|
||||||
|
listType: number;
|
||||||
|
count: number;
|
||||||
|
fetchOld: boolean;
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// "anchorPointContact": {
|
||||||
|
// "contactId": "",
|
||||||
|
// "sortField": "",
|
||||||
|
// "pos": 0
|
||||||
|
// },
|
||||||
|
// "relativeMoveCount": 0,
|
||||||
|
// "listType": 1,
|
||||||
|
// "count": 200,
|
||||||
|
// "fetchOld": true
|
||||||
|
// }
|
||||||
export interface NodeIKernelRecentContactService {
|
export interface NodeIKernelRecentContactService {
|
||||||
setGuildDisplayStatus(...args: unknown[]): unknown; // 2 arguments
|
setGuildDisplayStatus(...args: unknown[]): unknown; // 2 arguments
|
||||||
|
|
||||||
@@ -11,7 +34,14 @@ export interface NodeIKernelRecentContactService {
|
|||||||
|
|
||||||
enterOrExitMsgList(...args: unknown[]): unknown; // 1 arguments
|
enterOrExitMsgList(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
getRecentContactListSnapShot(...args: unknown[]): unknown; // 1 arguments
|
/*!---!*/getRecentContactListSnapShot(count: number): Promise<GeneralCallResult & {
|
||||||
|
info: {
|
||||||
|
errCode: number,
|
||||||
|
errMsg: string,
|
||||||
|
sortedContactList: Array<number>,
|
||||||
|
changedList: Array<any>
|
||||||
|
}
|
||||||
|
}>; // 1 arguments
|
||||||
|
|
||||||
clearMsgUnreadCount(...args: unknown[]): unknown; // 1 arguments
|
clearMsgUnreadCount(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
@@ -19,7 +49,7 @@ export interface NodeIKernelRecentContactService {
|
|||||||
|
|
||||||
jumpToSpecifyRecentContact(...args: unknown[]): unknown; // 1 arguments
|
jumpToSpecifyRecentContact(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
fetchAndSubscribeABatchOfRecentContact(...args: unknown[]): unknown; // 1 arguments
|
/*!---!*/fetchAndSubscribeABatchOfRecentContact(params: FSABRecentContactParams): unknown; // 1 arguments
|
||||||
|
|
||||||
addRecentContact(peer: Peer): unknown;
|
addRecentContact(peer: Peer): unknown;
|
||||||
|
|
||||||
@@ -31,9 +61,9 @@ export interface NodeIKernelRecentContactService {
|
|||||||
|
|
||||||
updateGameMsgConfigs(...args: unknown[]): unknown; // 1 arguments
|
updateGameMsgConfigs(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
removeKernelRecentContactListener(...args: unknown[]): unknown; // 1 arguments
|
removeKernelRecentContactListener(listenerid: number): unknown; // 1 arguments
|
||||||
|
|
||||||
addKernelRecentContactListener(...args: unknown[]): unknown; // 1 arguments
|
addKernelRecentContactListener(listener: NodeIKernelRecentContactListener): void;
|
||||||
|
|
||||||
clearRecentContactsByChatType(...args: unknown[]): unknown; // 1 arguments
|
clearRecentContactsByChatType(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
@@ -43,7 +73,7 @@ export interface NodeIKernelRecentContactService {
|
|||||||
|
|
||||||
deleteRecentContactsVer2(...args: unknown[]): unknown; // 1 arguments
|
deleteRecentContactsVer2(...args: unknown[]): unknown; // 1 arguments
|
||||||
|
|
||||||
getRecentContactList(): unknown;
|
getRecentContactList(): Promise<any>;
|
||||||
|
|
||||||
getMsgUnreadCount(): unknown;
|
getMsgUnreadCount(): unknown;
|
||||||
|
|
||||||
|
@@ -85,23 +85,23 @@ export interface NodeIKernelRichMediaService {
|
|||||||
|
|
||||||
downloadFileForFileUuid(peer: Peer, arg1: string, arg3: unknown[]): unknown;
|
downloadFileForFileUuid(peer: Peer, arg1: string, arg3: unknown[]): unknown;
|
||||||
|
|
||||||
downloadFileByUrlListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
downloadFileByUrlList(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
downloadFileForFileInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
downloadFileForFileInfo(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
createGroupFolder(GroupCode: string, FolderName: string): Promise<GeneralCallResult & { resultWithGroupItem: { result: any, groupItem: Array<any> } }>
|
createGroupFolder(GroupCode: string, FolderName: string): Promise<GeneralCallResult & { resultWithGroupItem: { result: any, groupItem: Array<any> } }>
|
||||||
|
|
||||||
downloadFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown): unknown;
|
downloadFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown): unknown;
|
||||||
|
|
||||||
createGroupFoldertransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
createGroupFolder(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
downloadGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
downloadGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
renameGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
renameGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
deleteGroupFoldertransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
deleteGroupFolder(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
deleteTransferInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
deleteTransferInfo(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
cancelTransferTask(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
cancelTransferTask(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
@@ -122,18 +122,28 @@ export interface NodeIKernelRichMediaService {
|
|||||||
}
|
}
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
getGroupFileInfotransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
getGroupFileInfo(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
getGroupFileListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
getGroupFileList(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
getGroupTransferListtransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
getGroupTransferList(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
renameGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
renameGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
||||||
|
|
||||||
moveGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
moveGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
||||||
|
|
||||||
transGroupFile(arg1: unknown, arg2: unknown): unknown;
|
transGroupFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
|
searchGroupFile(
|
||||||
|
keywords: Array<string>,
|
||||||
|
param: {
|
||||||
|
groupIds: Array<string>,
|
||||||
|
fileType: number,
|
||||||
|
context: string,
|
||||||
|
count: number,
|
||||||
|
sortType: number,
|
||||||
|
groupNames: Array<string>
|
||||||
|
}): Promise<unknown>;
|
||||||
searchGroupFileByWord(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
searchGroupFileByWord(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown;
|
||||||
|
|
||||||
deleteGroupFile(GroupCode: string, params: Array<number>, Files: Array<string>): Promise<GeneralCallResult & {
|
deleteGroupFile(GroupCode: string, params: Array<number>, Files: Array<string>): Promise<GeneralCallResult & {
|
||||||
@@ -152,7 +162,7 @@ export interface NodeIKernelRichMediaService {
|
|||||||
|
|
||||||
queryPicDownloadSize(arg: unknown): unknown;
|
queryPicDownloadSize(arg: unknown): unknown;
|
||||||
|
|
||||||
searchGroupFiletransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
searchGroupFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
searchMoreGroupFile(arg: unknown): unknown;
|
searchMoreGroupFile(arg: unknown): unknown;
|
||||||
|
|
||||||
@@ -160,7 +170,7 @@ export interface NodeIKernelRichMediaService {
|
|||||||
|
|
||||||
onlyDownloadFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
onlyDownloadFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
onlyUploadFiletransgroupfile(arg1: unknown, arg2: unknown): unknown;
|
onlyUploadFile(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
isExtraLargePic(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
isExtraLargePic(arg1: unknown, arg2: unknown, arg3: unknown): unknown;
|
||||||
|
|
||||||
|
@@ -30,5 +30,6 @@ export interface NodeIKernelRobotService {
|
|||||||
setRobotPickTts(arg1: unknown, arg2: unknown): unknown;
|
setRobotPickTts(arg1: unknown, arg2: unknown): unknown;
|
||||||
|
|
||||||
getRobotUinRange(data: any): Promise<{ response: { robotUinRanges: any } }>
|
getRobotUinRange(data: any): Promise<{ response: { robotUinRanges: any } }>
|
||||||
|
|
||||||
isNull(): boolean;
|
isNull(): boolean;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
export interface NodeIKernelSearchService{
|
export interface NodeIKernelSearchService {
|
||||||
addKernelSearchListenerr(...args: any[]): unknown;// needs 1 arguments
|
addKernelSearchListenerr(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
removeKernelSearchListenerr(...args: any[]): unknown;// needs 1 arguments
|
removeKernelSearchListenerr(...args: any[]): unknown;// needs 1 arguments
|
||||||
@@ -48,10 +48,10 @@ export interface NodeIKernelSearchService{
|
|||||||
searchMsgWithKeywordsr(...args: any[]): unknown;// needs 2 arguments
|
searchMsgWithKeywordsr(...args: any[]): unknown;// needs 2 arguments
|
||||||
|
|
||||||
searchMoreMsgWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
|
searchMoreMsgWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
cancelSearchMsgWithKeywordsr(...args: any[]): unknown;// needs 3 arguments
|
|
||||||
|
|
||||||
searchFileWithKeywordsr(...args: any[]): unknown;// needs 2 arguments
|
cancelSearchMsgWithKeywords(...args: any[]): unknown;// needs 3 arguments
|
||||||
|
|
||||||
|
searchFileWithKeywords(keywords: string[], source: number): Promise<unknown>;// needs 2 arguments
|
||||||
|
|
||||||
searchMoreFileWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
|
searchMoreFileWithKeywordsr(...args: any[]): unknown;// needs 1 arguments
|
||||||
|
|
||||||
|
@@ -12,18 +12,16 @@ export interface NapCatConfig {
|
|||||||
consoleLogLevel: LogLevel,
|
consoleLogLevel: LogLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
class Config extends ConfigBase<NapCatConfig> implements NapCatConfig{
|
class Config extends ConfigBase<NapCatConfig> implements NapCatConfig {
|
||||||
|
name: string = 'napcat'
|
||||||
fileLog = true;
|
fileLog = true;
|
||||||
consoleLog = true;
|
consoleLog = true;
|
||||||
fileLogLevel = LogLevel.DEBUG;
|
fileLogLevel = LogLevel.DEBUG;
|
||||||
consoleLogLevel = LogLevel.INFO;
|
consoleLogLevel = LogLevel.INFO;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
getConfigPath() {
|
|
||||||
return path.join(this.getConfigDir(), `napcat_${selfInfo.uin}.json`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const napCatConfig = new Config();
|
export const napCatConfig = new Config();
|
||||||
|
@@ -39,6 +39,8 @@ import { NodeIKernelTianShuService } from './services/NodeIKernelTianShuService'
|
|||||||
import { NodeIKernelUnitedConfigService } from './services/NodeIKernelUnitedConfigService';
|
import { NodeIKernelUnitedConfigService } from './services/NodeIKernelUnitedConfigService';
|
||||||
import { NodeIKernelSearchService } from './services/NodeIKernelSearchService';
|
import { NodeIKernelSearchService } from './services/NodeIKernelSearchService';
|
||||||
import { NodeIKernelCollectionService } from './services/NodeIKernelCollectionService';
|
import { NodeIKernelCollectionService } from './services/NodeIKernelCollectionService';
|
||||||
|
import { NodeIKernelRecentContactService } from './services/NodeIKernelRecentContactService';
|
||||||
|
import { NodeIKernelMSFService } from './services/NodeIKernelMSFService';
|
||||||
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -171,7 +173,7 @@ export interface NodeIQQNTWrapperSession {
|
|||||||
getTicketService(): NodeIKernelTicketService;
|
getTicketService(): NodeIKernelTicketService;
|
||||||
|
|
||||||
getTipOffService(): NodeIKernelTipOffService;
|
getTipOffService(): NodeIKernelTipOffService;
|
||||||
|
|
||||||
getNodeMiscService(): NodeIKernelNodeMiscService;
|
getNodeMiscService(): NodeIKernelNodeMiscService;
|
||||||
|
|
||||||
getRichMediaService(): NodeIKernelRichMediaService;
|
getRichMediaService(): NodeIKernelRichMediaService;
|
||||||
@@ -230,13 +232,13 @@ export interface NodeIQQNTWrapperSession {
|
|||||||
|
|
||||||
getLockService(): unknown;
|
getLockService(): unknown;
|
||||||
|
|
||||||
getMSFService(): unknown
|
getMSFService(): NodeIKernelMSFService;
|
||||||
|
|
||||||
getGuildHotUpdateService(): unknown;
|
getGuildHotUpdateService(): unknown;
|
||||||
|
|
||||||
getAVSDKService(): unknown;
|
getAVSDKService(): unknown;
|
||||||
|
|
||||||
getRecentContactService(): unknown;
|
getRecentContactService(): NodeIKernelRecentContactService;
|
||||||
|
|
||||||
getConfigMgrService(): unknown;
|
getConfigMgrService(): unknown;
|
||||||
}
|
}
|
||||||
@@ -286,11 +288,7 @@ let wrapperNodePath = path.resolve(path.dirname(process.execPath), './resources/
|
|||||||
if (!fs.existsSync(wrapperNodePath)) {
|
if (!fs.existsSync(wrapperNodePath)) {
|
||||||
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${qqVersionConfigInfo.curVersion}/wrapper.node`);
|
wrapperNodePath = path.join(path.dirname(process.execPath), `resources/app/versions/${qqVersionConfigInfo.curVersion}/wrapper.node`);
|
||||||
}
|
}
|
||||||
let WrapperLoader = path.join(__dirname, "WrapperLoader.cjs");
|
const nativemodule: any = { exports: {} };
|
||||||
//此处待优化
|
process.dlopen(nativemodule, wrapperNodePath);
|
||||||
fs.writeFileSync(WrapperLoader, `
|
const QQWrapper: WrapperNodeApi = nativemodule.exports;
|
||||||
module.exports = require("${wrapperNodePath.replace(/\\/g, "\\\\")}");
|
|
||||||
exports = module.exports;
|
|
||||||
`)
|
|
||||||
const QQWrapper: WrapperNodeApi = (await import("file://" + WrapperLoader)).default;
|
|
||||||
export default QQWrapper;
|
export default QQWrapper;
|
||||||
|
24
src/index.ts
24
src/index.ts
@@ -13,13 +13,15 @@ import { deleteOldFiles, UpdateConfig } from './common/utils/helper';
|
|||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import { randomInt } from 'crypto';
|
||||||
|
import { MessageUnique } from './common/utils/MessageUnique';
|
||||||
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
const tagColor = chalk.cyan;
|
const tagColor = chalk.cyan;
|
||||||
program
|
program
|
||||||
.option('-q, --qq <type>', 'QQ号')
|
.option('-q, --qq [type]', 'QQ号')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
//deleteOldFiles(path.join(__dirname, 'logs'), 3).then().catch();
|
//deleteOldFiles(path.join(__dirname, 'logs'), 3).then().catch();
|
||||||
@@ -67,7 +69,18 @@ const showQRCode = async (url: string, base64: string, buffer: Buffer) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const quickLoginQQ = cmdOptions.qq;
|
|
||||||
|
let quickLoginQQ = cmdOptions.qq; // undefine、true、string
|
||||||
|
const QuickLoginList = await napCatCore.getQuickLoginList();
|
||||||
|
if (quickLoginQQ == true) {
|
||||||
|
if (QuickLoginList.LocalLoginInfoList.length > 0) {
|
||||||
|
quickLoginQQ = QuickLoginList.LocalLoginInfoList[0].uin;
|
||||||
|
log('-q 指令指定使用最近的QQ进行快速登录');
|
||||||
|
} else {
|
||||||
|
quickLoginQQ = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// napCatCore.on('system.login.error', (result) => {
|
// napCatCore.on('system.login.error', (result) => {
|
||||||
// console.error('登录失败', result);
|
// console.error('登录失败', result);
|
||||||
// napCatCore.qrLogin().then().catch(console.error);
|
// napCatCore.qrLogin().then().catch(console.error);
|
||||||
@@ -105,11 +118,14 @@ if (quickLoginQQ) {
|
|||||||
logError('快速登录错误:', res.loginErrorInfo.errMsg);
|
logError('快速登录错误:', res.loginErrorInfo.errMsg);
|
||||||
}
|
}
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
logError(e);
|
logError('快速登录错误:', e);
|
||||||
napCatCore.qrLogin(showQRCode);
|
napCatCore.qrLogin(showQRCode);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
log('没有 -q 参数指定快速登录的QQ,将使用二维码登录方式');
|
log('没有 -q 指令指定快速登录,将使用二维码登录方式');
|
||||||
|
if (QuickLoginList.LocalLoginInfoList.length > 0) {
|
||||||
|
log(`可用于快速登录的QQ:${QuickLoginList.LocalLoginInfoList.map((u, index) => `\n${index}: ${u.uin} ${u.nickName}`)}`);
|
||||||
|
}
|
||||||
napCatCore.qrLogin(showQRCode);
|
napCatCore.qrLogin(showQRCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
//统一到处入口
|
|
||||||
export * from './main';
|
|
@@ -1,29 +0,0 @@
|
|||||||
enum NapCatCorePlatform {
|
|
||||||
Node = 'Node',//命令行模式加载
|
|
||||||
LiteLoader = 'LiteLoader',//LL插件模式加载
|
|
||||||
}
|
|
||||||
class NewNapCatCore {
|
|
||||||
platform: NapCatCorePlatform; // 平台
|
|
||||||
constructor(platform: NapCatCorePlatform) {
|
|
||||||
this.platform = platform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class NapCatCoreManger {
|
|
||||||
static core: NewNapCatCore | undefined = undefined;
|
|
||||||
static defaultPlatform: NapCatCorePlatform = NapCatCorePlatform.Node;
|
|
||||||
static SetDefaultCore(platform: NapCatCorePlatform) {
|
|
||||||
if (this.core !== undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.defaultPlatform = platform;
|
|
||||||
}
|
|
||||||
static GetPlatform(): NapCatCorePlatform {
|
|
||||||
return NapCatCoreManger.defaultPlatform;
|
|
||||||
}
|
|
||||||
static GetInstance(): NewNapCatCore {
|
|
||||||
if (this.core === undefined) {
|
|
||||||
this.core = new NewNapCatCore(NapCatCoreManger.defaultPlatform);
|
|
||||||
}
|
|
||||||
return this.core;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1 +0,0 @@
|
|||||||
//拦截proxy到会话
|
|
@@ -1 +0,0 @@
|
|||||||
//初始化跟之前一样
|
|
@@ -1 +0,0 @@
|
|||||||
//proxy封装工具类
|
|
@@ -15,6 +15,6 @@ export class SetConfigAction extends BaseAction<OB11Config, void> {
|
|||||||
actionName = ActionName.SetConfig;
|
actionName = ActionName.SetConfig;
|
||||||
|
|
||||||
protected async _handle(payload: OB11Config): Promise<void> {
|
protected async _handle(payload: OB11Config): Promise<void> {
|
||||||
ob11Config.save(payload);
|
ob11Config.save(payload, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
src/onebot11/action/extends/GetProfileLike.ts
Normal file
16
src/onebot11/action/extends/GetProfileLike.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { selfInfo } from '@/core/data';
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core/apis';
|
||||||
|
|
||||||
|
export class GetProfileLike extends BaseAction<void, any> {
|
||||||
|
actionName = ActionName.GetProfileLike;
|
||||||
|
protected async _handle(payload: void) {
|
||||||
|
const ret = await NTQQUserApi.getProfileLike(selfInfo.uid);
|
||||||
|
const listdata: any[] = ret.info.userLikeInfos[0].favoriteInfo.userInfos;
|
||||||
|
for (let i = 0; i < listdata.length; i++) {
|
||||||
|
listdata[i].uin = parseInt((await NTQQUserApi.getUinByUid(listdata[i].uid)) || '');
|
||||||
|
}
|
||||||
|
return listdata;
|
||||||
|
}
|
||||||
|
}
|
57
src/onebot11/action/extends/SetGroupHeader.ts
Normal file
57
src/onebot11/action/extends/SetGroupHeader.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
|
import { checkFileReceived, uri2local } from '@/common/utils/file';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
// import { log } from "../../../common/utils";
|
||||||
|
|
||||||
|
interface Payload {
|
||||||
|
file: string,
|
||||||
|
groupCode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SetGroupHeader extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetGroupHeader;
|
||||||
|
// 用不着复杂检测
|
||||||
|
protected async check(payload: Payload): Promise<BaseCheckResult> {
|
||||||
|
if (!payload.file || typeof payload.file != 'string' || !payload.groupCode || typeof payload.groupCode != 'string') {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: 'file和groupCode字段不能为空或者类型错误',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const { path, isLocal, errMsg } = (await uri2local(payload.file));
|
||||||
|
if (errMsg) {
|
||||||
|
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
|
||||||
|
}
|
||||||
|
if (path) {
|
||||||
|
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃,需要提前判断
|
||||||
|
const ret = await NTQQGroupApi.setGroupAvatar(payload.groupCode,path);
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
throw `头像${payload.file}设置失败,api无返回`;
|
||||||
|
}
|
||||||
|
// log(`头像设置返回:${JSON.stringify(ret)}`)
|
||||||
|
// if (ret['result'] == 1004022) {
|
||||||
|
// throw `头像${payload.file}设置失败,文件可能不是图片格式`;
|
||||||
|
// } else if (ret['result'] != 0) {
|
||||||
|
// throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
|
||||||
|
// }
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
if (!isLocal) {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
|
}
|
||||||
|
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,15 +1,15 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import fs from 'fs/promises';
|
import fs from 'fs/promises';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { ob11Config } from '@/onebot11/config';
|
import { ob11Config } from '@/onebot11/config';
|
||||||
import { log, logDebug } from '@/common/utils/log';
|
import { log, logDebug } from '@/common/utils/log';
|
||||||
import { sleep } from '@/common/utils/helper';
|
import { sleep } from '@/common/utils/helper';
|
||||||
import { uri2local } from '@/common/utils/file';
|
import { uri2local } from '@/common/utils/file';
|
||||||
import { ActionName, BaseCheckResult } from '../types';
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
import { FileElement, RawMessage, VideoElement } from '@/core/entities';
|
import { FileElement, RawMessage, VideoElement } from '@/core/entities';
|
||||||
import { NTQQFileApi } from '@/core/apis';
|
import { NTQQFileApi, NTQQMsgApi } from '@/core/apis';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import Ajv from 'ajv';
|
import Ajv from 'ajv';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
export interface GetFilePayload {
|
export interface GetFilePayload {
|
||||||
file: string; // 文件名或者fileUuid
|
file: string; // 文件名或者fileUuid
|
||||||
@@ -45,69 +45,67 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
|
|||||||
return { id: element.elementId, element: element.fileElement };
|
return { id: element.elementId, element: element.fileElement };
|
||||||
}
|
}
|
||||||
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
|
protected async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
|
||||||
let cache = await dbUtil.getFileCacheByName(payload.file);
|
throw new Error('file not found');
|
||||||
if (!cache) {
|
// let cache = await dbUtil.getFileCacheByName(payload.file);
|
||||||
cache = await dbUtil.getFileCacheByUuid(payload.file);
|
// if (!cache) {
|
||||||
}
|
// cache = await dbUtil.getFileCacheByUuid(payload.file);
|
||||||
if (!cache) {
|
|
||||||
throw new Error('file not found');
|
|
||||||
}
|
|
||||||
const { enableLocalFile2Url } = ob11Config;
|
|
||||||
try {
|
|
||||||
await fs.access(cache.path, fs.constants.F_OK);
|
|
||||||
} catch (e) {
|
|
||||||
logDebug('local file not found, start download...');
|
|
||||||
// if (cache.url) {
|
|
||||||
// const downloadResult = await uri2local(cache.url);
|
|
||||||
// if (downloadResult.success) {
|
|
||||||
// cache.path = downloadResult.path;
|
|
||||||
// dbUtil.updateFileCache(cache).then();
|
|
||||||
// } else {
|
|
||||||
// throw new Error('file download failed. ' + downloadResult.errMsg);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// // 没有url的可能是私聊文件或者群文件,需要自己下载
|
|
||||||
// log('需要调用 NTQQ 下载文件api');
|
|
||||||
let msg = await dbUtil.getMsgByLongId(cache.msgId);
|
|
||||||
// log('文件 msg', msg);
|
|
||||||
if (msg) {
|
|
||||||
// 构建下载函数
|
|
||||||
const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
|
|
||||||
cache.elementId, '', '');
|
|
||||||
// await sleep(1000);
|
|
||||||
|
|
||||||
// log('download result', downloadPath);
|
|
||||||
msg = await dbUtil.getMsgByLongId(cache.msgId);
|
|
||||||
// log('下载完成后的msg', msg);
|
|
||||||
cache.path = downloadPath!;
|
|
||||||
dbUtil.updateFileCache(cache).then();
|
|
||||||
// log('下载完成后的msg', msg);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// log('file found', cache);
|
|
||||||
const res: GetFileResponse = {
|
|
||||||
file: cache.path,
|
|
||||||
url: cache.url,
|
|
||||||
file_size: cache.size.toString(),
|
|
||||||
file_name: cache.name
|
|
||||||
};
|
|
||||||
if (enableLocalFile2Url) {
|
|
||||||
if (!cache.url) {
|
|
||||||
try {
|
|
||||||
res.base64 = await fs.readFile(cache.path, 'base64');
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error('文件下载失败. ' + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if (autoDeleteFile) {
|
|
||||||
// setTimeout(() => {
|
|
||||||
// fs.unlink(cache.filePath)
|
|
||||||
// }, autoDeleteFileSecond * 1000)
|
|
||||||
// }
|
// }
|
||||||
return res;
|
// if (!cache) {
|
||||||
|
// throw new Error('file not found');
|
||||||
|
// }
|
||||||
|
// const { enableLocalFile2Url } = ob11Config;
|
||||||
|
// try {
|
||||||
|
// await fs.access(cache.path, fs.constants.F_OK);
|
||||||
|
// } catch (e) {
|
||||||
|
// logDebug('local file not found, start download...');
|
||||||
|
// // if (cache.url) {
|
||||||
|
// // const downloadResult = await uri2local(cache.url);
|
||||||
|
// // if (downloadResult.success) {
|
||||||
|
// // cache.path = downloadResult.path;
|
||||||
|
// // dbUtil.updateFileCache(cache).then();
|
||||||
|
// // } else {
|
||||||
|
// // throw new Error('file download failed. ' + downloadResult.errMsg);
|
||||||
|
// // }
|
||||||
|
// // } else {
|
||||||
|
// // // 没有url的可能是私聊文件或者群文件,需要自己下载
|
||||||
|
// // log('需要调用 NTQQ 下载文件api');
|
||||||
|
// let peer = MessageUnique.getPeerByMsgId(cache.msgId);
|
||||||
|
// let msg = await NTQQMsgApi.getMsgsByMsgId(peer?.Peer!,cache.msgId);
|
||||||
|
// // log('文件 msg', msg);
|
||||||
|
// if (msg) {
|
||||||
|
// // 构建下载函数
|
||||||
|
// const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
|
||||||
|
// cache.elementId, '', '');
|
||||||
|
// // await sleep(1000);
|
||||||
|
|
||||||
|
// // log('download result', downloadPath);
|
||||||
|
// let peer = MessageUnique.getPeerByMsgId(cache.msgId);
|
||||||
|
// msg = await NTQQMsgApi.getMsgsByMsgId(peer?.Peer!,cache.msgId);
|
||||||
|
// // log('下载完成后的msg', msg);
|
||||||
|
// cache.path = downloadPath!;
|
||||||
|
// dbUtil.updateFileCache(cache).then();
|
||||||
|
// // log('下载完成后的msg', msg);
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// // log('file found', cache);
|
||||||
|
// const res: GetFileResponse = {
|
||||||
|
// file: cache.path,
|
||||||
|
// url: cache.url,
|
||||||
|
// file_size: cache.size.toString(),
|
||||||
|
// file_name: cache.name
|
||||||
|
// };
|
||||||
|
// if (enableLocalFile2Url) {
|
||||||
|
// if (!cache.url) {
|
||||||
|
// try {
|
||||||
|
// res.base64 = await fs.readFile(cache.path, 'base64');
|
||||||
|
// } catch (e) {
|
||||||
|
// throw new Error('文件下载失败. ' + e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName, BaseCheckResult } from '../types';
|
import { ActionName } from '../types';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { join as joinPath } from 'node:path';
|
import { join as joinPath } from 'node:path';
|
||||||
import { calculateFileMD5, getTempDir, httpDownload } from '@/common/utils/file';
|
import { calculateFileMD5, getTempDir, httpDownload } from '@/common/utils/file';
|
||||||
import { v4 as uuid4 } from 'uuid';
|
import { randomUUID } from 'crypto';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import Ajv from 'ajv';
|
|
||||||
interface FileResponse {
|
interface FileResponse {
|
||||||
file: string;
|
file: string;
|
||||||
}
|
}
|
||||||
@@ -32,7 +31,7 @@ export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileRespon
|
|||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload): Promise<FileResponse> {
|
protected async _handle(payload: Payload): Promise<FileResponse> {
|
||||||
const isRandomName = !payload.name;
|
const isRandomName = !payload.name;
|
||||||
const name = payload.name || uuid4();
|
const name = payload.name || randomUUID();
|
||||||
const filePath = joinPath(getTempDir(), name);
|
const filePath = joinPath(getTempDir(), name);
|
||||||
|
|
||||||
if (payload.base64) {
|
if (payload.base64) {
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
|
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
|
||||||
import { NTQQMsgApi } from '@/core/apis';
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import { ActionName, BaseCheckResult } from '../types';
|
import { ActionName, BaseCheckResult } from '../types';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import Ajv from 'ajv';
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -29,24 +28,19 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
|
|||||||
if (!msgId) {
|
if (!msgId) {
|
||||||
throw Error('message_id is required');
|
throw Error('message_id is required');
|
||||||
}
|
}
|
||||||
let rootMsg = await dbUtil.getMsgByLongId(msgId);
|
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId);
|
||||||
|
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || parseInt(msgId));
|
||||||
if (!rootMsg) {
|
if (!rootMsg) {
|
||||||
rootMsg = await dbUtil.getMsgByShortId(parseInt(msgId));
|
throw Error('msg not found');
|
||||||
if (!rootMsg) {
|
|
||||||
throw Error('msg not found');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const data = await NTQQMsgApi.getMultiMsg({
|
const data = await NTQQMsgApi.getMultiMsg(rootMsg.Peer, rootMsg.MsgId, rootMsg.MsgId);
|
||||||
chatType: rootMsg.chatType,
|
|
||||||
peerUid: rootMsg.peerUid
|
|
||||||
}, rootMsg.msgId, rootMsg.msgId);
|
|
||||||
if (!data || data.result !== 0) {
|
if (!data || data.result !== 0) {
|
||||||
throw Error('找不到相关的聊天记录' + data?.errMsg);
|
throw Error('找不到相关的聊天记录' + data?.errMsg);
|
||||||
}
|
}
|
||||||
const msgList = data.msgList;
|
const msgList = data.msgList;
|
||||||
const messages = await Promise.all(msgList.map(async msg => {
|
const messages = await Promise.all(msgList.map(async msg => {
|
||||||
const resMsg = await OB11Constructor.message(msg);
|
const resMsg = await OB11Constructor.message(msg);
|
||||||
resMsg.message_id = await dbUtil.addMsg(msg);
|
resMsg.message_id = await MessageUnique.createMsg({ guildId:'',chatType:msg.chatType,peerUid:msg.peerUid },msg.msgId)!;
|
||||||
return resMsg;
|
return resMsg;
|
||||||
}));
|
}));
|
||||||
messages.map(msg => {
|
messages.map(msg => {
|
||||||
|
@@ -2,11 +2,11 @@ import BaseAction from '../BaseAction';
|
|||||||
import { OB11Message, OB11User } from '../../types';
|
import { OB11Message, OB11User } from '../../types';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { ChatType } from '@/core/entities';
|
import { ChatType } from '@/core/entities';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { NTQQMsgApi } from '@/core/apis/msg';
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { NTQQFriendApi, NTQQUserApi } from '@/core';
|
import { NTQQFriendApi, NTQQUserApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
interface Response {
|
interface Response {
|
||||||
messages: OB11Message[];
|
messages: OB11Message[];
|
||||||
@@ -32,7 +32,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
|||||||
if (!uid) {
|
if (!uid) {
|
||||||
throw `记录${payload.user_id}不存在`;
|
throw `记录${payload.user_id}不存在`;
|
||||||
}
|
}
|
||||||
const startMsgId = (await dbUtil.getMsgByShortId(payload.message_seq))?.msgId || '0';
|
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq))?.MsgId || '0';
|
||||||
const friend = await NTQQFriendApi.isBuddy(uid);
|
const friend = await NTQQFriendApi.isBuddy(uid);
|
||||||
const historyResult = (await NTQQMsgApi.getMsgHistory({
|
const historyResult = (await NTQQMsgApi.getMsgHistory({
|
||||||
chatType: friend ? ChatType.friend : ChatType.temp,
|
chatType: friend ? ChatType.friend : ChatType.temp,
|
||||||
@@ -41,7 +41,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
|||||||
//logDebug(historyResult);
|
//logDebug(historyResult);
|
||||||
const msgList = historyResult.msgList;
|
const msgList = historyResult.msgList;
|
||||||
await Promise.all(msgList.map(async msg => {
|
await Promise.all(msgList.map(async msg => {
|
||||||
msg.id = await dbUtil.addMsg(msg);
|
msg.id = await MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
}));
|
}));
|
||||||
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
||||||
return { 'messages': ob11MsgList };
|
return { 'messages': ob11MsgList };
|
||||||
|
@@ -3,11 +3,10 @@ import { OB11Message, OB11User } from '../../types';
|
|||||||
import { getGroup, groups } from '@/core/data';
|
import { getGroup, groups } from '@/core/data';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { ChatType } from '@/core/entities';
|
import { ChatType } from '@/core/entities';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { NTQQMsgApi } from '@/core/apis/msg';
|
import { NTQQMsgApi } from '@/core/apis/msg';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import { logDebug } from '@/common/utils/log';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
interface Response {
|
interface Response {
|
||||||
messages: OB11Message[];
|
messages: OB11Message[];
|
||||||
}
|
}
|
||||||
@@ -15,7 +14,7 @@ interface Response {
|
|||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
group_id: { type: [ 'number' , 'string' ] },
|
group_id: { type: ['number', 'string'] },
|
||||||
message_seq: { type: 'number' },
|
message_seq: { type: 'number' },
|
||||||
count: { type: 'number' }
|
count: { type: 'number' }
|
||||||
},
|
},
|
||||||
@@ -32,7 +31,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
|
|||||||
if (!group) {
|
if (!group) {
|
||||||
throw `群${payload.group_id}不存在`;
|
throw `群${payload.group_id}不存在`;
|
||||||
}
|
}
|
||||||
const startMsgId = (await dbUtil.getMsgByShortId(payload.message_seq))?.msgId || '0';
|
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq))?.MsgId || '0';
|
||||||
// log("startMsgId", startMsgId)
|
// log("startMsgId", startMsgId)
|
||||||
const historyResult = (await NTQQMsgApi.getMsgHistory({
|
const historyResult = (await NTQQMsgApi.getMsgHistory({
|
||||||
chatType: ChatType.group,
|
chatType: ChatType.group,
|
||||||
@@ -41,7 +40,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
|
|||||||
//logDebug(historyResult);
|
//logDebug(historyResult);
|
||||||
const msgList = historyResult.msgList;
|
const msgList = historyResult.msgList;
|
||||||
await Promise.all(msgList.map(async msg => {
|
await Promise.all(msgList.map(async msg => {
|
||||||
msg.id = await dbUtil.addMsg(msg);
|
msg.id = await MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
}));
|
}));
|
||||||
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(msg)));
|
||||||
return { 'messages': ob11MsgList };
|
return { 'messages': ob11MsgList };
|
||||||
|
@@ -3,6 +3,7 @@ import BaseAction from '../BaseAction';
|
|||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { JSONSchema } from 'json-schema-to-ts';
|
import { JSONSchema } from 'json-schema-to-ts';
|
||||||
import { NTQQSystemApi } from '@/core';
|
import { NTQQSystemApi } from '@/core';
|
||||||
|
import { sleep } from '@/common/utils/helper';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -15,7 +16,8 @@ export class GetOnlineClient extends BaseAction<void, Array<any>> {
|
|||||||
actionName = ActionName.GetOnlineClient;
|
actionName = ActionName.GetOnlineClient;
|
||||||
|
|
||||||
protected async _handle(payload: void) {
|
protected async _handle(payload: void) {
|
||||||
//console.log(await NTQQSystemApi.getOnlineDev());
|
NTQQSystemApi.getOnlineDev();
|
||||||
|
await sleep(500);
|
||||||
return DeviceList;
|
return DeviceList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
src/onebot11/action/group/DelEssenceMsg.ts
Normal file
31
src/onebot11/action/group/DelEssenceMsg.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQGroupApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class DelEssenceMsg extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.DelEssenceMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if (!msg) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
return await NTQQGroupApi.removeGroupEssence(
|
||||||
|
msg.Peer.peerUid,
|
||||||
|
msg.MsgId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,11 +1,10 @@
|
|||||||
import { OB11GroupMember } from '../../types';
|
import { OB11GroupMember } from '../../types';
|
||||||
import { getGroup, getGroupMember, groupMembers } from '@/core/data';
|
import { getGroup, getGroupMember, groupMembers, selfInfo } from '@/core/data';
|
||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { NTQQUserApi } from '@/core/apis/user';
|
import { NTQQUserApi } from '@/core/apis/user';
|
||||||
import { log, logDebug } from '@/common/utils/log';
|
import { logDebug } from '@/common/utils/log';
|
||||||
import { isNull } from '../../../common/utils/helper';
|
|
||||||
import { WebApi } from '@/core/apis/webapi';
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
import { NTQQGroupApi } from '@/core';
|
import { NTQQGroupApi } from '@/core';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
@@ -28,25 +27,29 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
|||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
const group = await getGroup(payload.group_id.toString());
|
const group = await getGroup(payload.group_id.toString());
|
||||||
|
const role = (await getGroupMember(payload.group_id, selfInfo.uin))?.role;
|
||||||
|
const isPrivilege = role === 3 || role === 4;
|
||||||
if (!group) {
|
if (!group) {
|
||||||
throw (`群(${payload.group_id})不存在`);
|
throw (`群(${payload.group_id})不存在`);
|
||||||
}
|
}
|
||||||
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
|
||||||
if (payload.no_cache == true || payload.no_cache === 'true') {
|
if (payload.no_cache == true || payload.no_cache === 'true') {
|
||||||
groupMembers.set(group.groupCode, await NTQQGroupApi.getGroupMembers(payload.group_id.toString()));
|
groupMembers.set(group.groupCode, await NTQQGroupApi.getGroupMembers(payload.group_id.toString()));
|
||||||
}
|
}
|
||||||
const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString());
|
const member = await getGroupMember(payload.group_id.toString(), payload.user_id.toString());
|
||||||
// log(member);
|
//早返回
|
||||||
if (member) {
|
if (!member) {
|
||||||
logDebug('获取群成员详细信息');
|
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
try {
|
}
|
||||||
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
try {
|
||||||
logDebug('群成员详细信息结果', info);
|
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
|
||||||
Object.assign(member, info);
|
logDebug('群成员详细信息结果', info);
|
||||||
} catch (e) {
|
Object.assign(member, info);
|
||||||
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
} catch (e) {
|
||||||
}
|
logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
|
||||||
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
}
|
||||||
|
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
|
||||||
|
if (isPrivilege) {
|
||||||
|
const webGroupMembers = await WebApi.getGroupMembers(payload.group_id.toString());
|
||||||
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
|
||||||
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
|
||||||
retMember.join_time = webGroupMembers[i]?.join_time;
|
retMember.join_time = webGroupMembers[i]?.join_time;
|
||||||
@@ -55,11 +58,17 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
|
|||||||
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
retMember.level = webGroupMembers[i]?.lv.level.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retMember;
|
|
||||||
} else {
|
} else {
|
||||||
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
|
let LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
|
||||||
|
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
|
||||||
|
let last_send_time = LastestMsgList.msgList[0].msgTime;
|
||||||
|
if (last_send_time && last_send_time != '0' && last_send_time != '') {
|
||||||
|
retMember.last_sent_time = parseInt(last_send_time);
|
||||||
|
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return retMember;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export default GetGroupMemberInfo;
|
||||||
export default GetGroupMemberInfo;
|
|
@@ -3,13 +3,9 @@ import { OB11GroupMember } from '../../types';
|
|||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { napCatCore, NTQQGroupApi, NTQQUserApi } from '@/core';
|
import { NTQQGroupApi } from '@/core';
|
||||||
import { WebApi } from '@/core/apis/webapi';
|
import { WebApi } from '@/core/apis/webapi';
|
||||||
import { logDebug } from '@/common/utils/log';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { ob11Config } from '@/onebot11/config';
|
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { TypeConvert } from '@/common/utils/type';
|
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -43,7 +39,11 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
|
|
||||||
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
|
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
|
||||||
// 转为Map 方便索引
|
// 转为Map 方便索引
|
||||||
|
let date = Math.round(Date.now() / 1000);
|
||||||
for (let i = 0, len = _groupMembers.length; i < len; i++) {
|
for (let i = 0, len = _groupMembers.length; i < len; i++) {
|
||||||
|
// 保证基础数据有这个 同时避免群管插件过于依赖这个杀了
|
||||||
|
_groupMembers[i].join_time = date;
|
||||||
|
_groupMembers[i].last_sent_time = date;
|
||||||
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,15 +63,15 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ob11Config.GroupLocalTime.Record && ob11Config.GroupLocalTime.RecordList[0] === '-1' || ob11Config.GroupLocalTime.RecordList.includes(payload.group_id.toString())) {
|
} else {
|
||||||
const _sendAndJoinRember = await dbUtil.getLastSentTimeAndJoinTime(TypeConvert.toNumber(payload.group_id));
|
const DateMap = await NTQQGroupApi.getGroupMemberLastestSendTimeCache(payload.group_id.toString());//开始从本地拉取
|
||||||
_sendAndJoinRember.forEach((element) => {
|
for (let DateUin of DateMap.keys()) {
|
||||||
const MemberData = MemberMap.get(element.user_id);
|
const MemberData = MemberMap.get(parseInt(DateUin));
|
||||||
if (MemberData) {
|
if (MemberData) {
|
||||||
MemberData.join_time = element.join_time;
|
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);
|
||||||
MemberData.last_sent_time = element.last_sent_time;
|
//join_time 有基础数据兜底
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
// 还原索引到Array 一同返回
|
// 还原索引到Array 一同返回
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// _groupMembers = Array.from(retData);
|
// _groupMembers = Array.from(retData);
|
||||||
|
|
||||||
_groupMembers = Array.from(MemberMap.values());
|
_groupMembers = Array.from(MemberMap.values());
|
||||||
return _groupMembers;
|
return _groupMembers;
|
||||||
}
|
}
|
||||||
|
30
src/onebot11/action/group/SetEssenceMsg.ts
Normal file
30
src/onebot11/action/group/SetEssenceMsg.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { NTQQGroupApi, NTQQMsgApi } from '@/core';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
message_id: { type: ['number', 'string'] }
|
||||||
|
},
|
||||||
|
required: ['message_id']
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export default class SetEssenceMsg extends BaseAction<Payload, any> {
|
||||||
|
actionName = ActionName.SetEssenceMsg;
|
||||||
|
PayloadSchema = SchemaData;
|
||||||
|
protected async _handle(payload: Payload): Promise<any> {
|
||||||
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
|
if (!msg) {
|
||||||
|
throw new Error('msg not found');
|
||||||
|
}
|
||||||
|
return await NTQQGroupApi.addGroupEssence(
|
||||||
|
msg.Peer.peerUid,
|
||||||
|
msg.MsgId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,6 @@ import BaseAction from '../BaseAction';
|
|||||||
import { GroupRequestOperateTypes } from '@/core/entities';
|
import { GroupRequestOperateTypes } from '@/core/entities';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { NTQQGroupApi } from '@/core/apis/group';
|
import { NTQQGroupApi } from '@/core/apis/group';
|
||||||
import { groupNotifies } from '@/core/data';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
@@ -10,7 +9,7 @@ const SchemaData = {
|
|||||||
properties: {
|
properties: {
|
||||||
flag: { type: 'string' },
|
flag: { type: 'string' },
|
||||||
approve: { type: ['string', 'boolean'] },
|
approve: { type: ['string', 'boolean'] },
|
||||||
reason: { type: 'string' }
|
reason: { type: 'string', nullable: true, }
|
||||||
},
|
},
|
||||||
required: ['flag'],
|
required: ['flag'],
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@@ -23,11 +22,7 @@ export default class SetGroupAddRequest extends BaseAction<Payload, null> {
|
|||||||
protected async _handle(payload: Payload): Promise<null> {
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
const flag = payload.flag.toString();
|
const flag = payload.flag.toString();
|
||||||
const approve = payload.approve?.toString() !== 'false';
|
const approve = payload.approve?.toString() !== 'false';
|
||||||
const notify = groupNotifies[flag];
|
await NTQQGroupApi.handleGroupRequest(flag,
|
||||||
if (!notify) {
|
|
||||||
throw `${flag}对应的加群通知不存在`;
|
|
||||||
}
|
|
||||||
await NTQQGroupApi.handleGroupRequest(notify,
|
|
||||||
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
|
||||||
payload.reason
|
payload.reason
|
||||||
);
|
);
|
||||||
|
@@ -34,7 +34,7 @@ import SetGroupAdmin from './group/SetGroupAdmin';
|
|||||||
import SetGroupCard from './group/SetGroupCard';
|
import SetGroupCard from './group/SetGroupCard';
|
||||||
import GetImage from './file/GetImage';
|
import GetImage from './file/GetImage';
|
||||||
import GetRecord from './file/GetRecord';
|
import GetRecord from './file/GetRecord';
|
||||||
import { GoCQHTTPMarkMsgAsRead, MarkGroupMsgAsRead, MarkPrivateMsgAsRead } from './msg/MarkMsgAsRead';
|
import { GoCQHTTPMarkMsgAsRead, MarkAllMsgAsRead, MarkGroupMsgAsRead, MarkPrivateMsgAsRead } from './msg/MarkMsgAsRead';
|
||||||
import CleanCache from './system/CleanCache';
|
import CleanCache from './system/CleanCache';
|
||||||
import GoCQHTTPUploadGroupFile from './go-cqhttp/UploadGroupFile';
|
import GoCQHTTPUploadGroupFile from './go-cqhttp/UploadGroupFile';
|
||||||
import { GetConfigAction, SetConfigAction } from '@/onebot11/action/extends/Config';
|
import { GetConfigAction, SetConfigAction } from '@/onebot11/action/extends/Config';
|
||||||
@@ -70,6 +70,11 @@ import { SetSelfProfile } from './extends/SetSelfProfile';
|
|||||||
import { shareGroupEx, sharePeer } from './extends/sharePeer';
|
import { shareGroupEx, sharePeer } from './extends/sharePeer';
|
||||||
import { CreateCollection } from './extends/CreateCollection';
|
import { CreateCollection } from './extends/CreateCollection';
|
||||||
import { SetLongNick } from './extends/SetLongNick';
|
import { SetLongNick } from './extends/SetLongNick';
|
||||||
|
import DelEssenceMsg from './group/DelEssenceMsg';
|
||||||
|
import SetEssenceMsg from './group/SetEssenceMsg';
|
||||||
|
import GetRecentContact from './user/GetRecentContact';
|
||||||
|
import { GetProfileLike } from './extends/GetProfileLike';
|
||||||
|
import SetGroupHeader from './extends/SetGroupHeader';
|
||||||
|
|
||||||
export const actionHandlers = [
|
export const actionHandlers = [
|
||||||
new RebootNormal(),
|
new RebootNormal(),
|
||||||
@@ -81,10 +86,6 @@ export const actionHandlers = [
|
|||||||
new sharePeer(),
|
new sharePeer(),
|
||||||
new CreateCollection(),
|
new CreateCollection(),
|
||||||
new SetLongNick(),
|
new SetLongNick(),
|
||||||
// new GetConfigAction(),
|
|
||||||
// new SetConfigAction(),
|
|
||||||
// new GetGroupAddRequest(),
|
|
||||||
// TranslateEnWordToZn = "translate_en2zh",
|
|
||||||
new ForwardFriendSingleMsg(),
|
new ForwardFriendSingleMsg(),
|
||||||
new ForwardGroupSingleMsg(),
|
new ForwardGroupSingleMsg(),
|
||||||
new MarkGroupMsgAsRead(),
|
new MarkGroupMsgAsRead(),
|
||||||
@@ -101,9 +102,13 @@ export const actionHandlers = [
|
|||||||
new GetMsg(),
|
new GetMsg(),
|
||||||
new GetLoginInfo(),
|
new GetLoginInfo(),
|
||||||
new GetFriendList(),
|
new GetFriendList(),
|
||||||
new GetGroupList(), new GetGroupInfo(),
|
new GetGroupList(),
|
||||||
new GetGroupMemberList(), new GetGroupMemberInfo(),
|
new GetGroupInfo(),
|
||||||
new SendGroupMsg(), new SendPrivateMsg(), new SendMsg(),
|
new GetGroupMemberList(),
|
||||||
|
new GetGroupMemberInfo(),
|
||||||
|
new SendGroupMsg(),
|
||||||
|
new SendPrivateMsg(),
|
||||||
|
new SendMsg(),
|
||||||
new DeleteMsg(),
|
new DeleteMsg(),
|
||||||
new SetGroupAddRequest(),
|
new SetGroupAddRequest(),
|
||||||
new SetFriendAddRequest(),
|
new SetFriendAddRequest(),
|
||||||
@@ -121,9 +126,7 @@ export const actionHandlers = [
|
|||||||
new GetImage(),
|
new GetImage(),
|
||||||
new GetRecord(),
|
new GetRecord(),
|
||||||
new SetMsgEmojiLike(),
|
new SetMsgEmojiLike(),
|
||||||
// new CleanCache(),
|
|
||||||
new GetCookies(),
|
new GetCookies(),
|
||||||
//
|
|
||||||
new SetOnlineStatus(),
|
new SetOnlineStatus(),
|
||||||
new GetRobotUinRange(),
|
new GetRobotUinRange(),
|
||||||
new GetFriendWithCategory(),
|
new GetFriendWithCategory(),
|
||||||
@@ -147,7 +150,13 @@ export const actionHandlers = [
|
|||||||
new GoCQHTTPGetForwardMsgAction(),
|
new GoCQHTTPGetForwardMsgAction(),
|
||||||
new GetFriendMsgHistory(),
|
new GetFriendMsgHistory(),
|
||||||
new GoCQHTTPHandleQuickAction(),
|
new GoCQHTTPHandleQuickAction(),
|
||||||
new GetGroupSystemMsg()
|
new GetGroupSystemMsg(),
|
||||||
|
new DelEssenceMsg(),
|
||||||
|
new SetEssenceMsg(),
|
||||||
|
new GetRecentContact(),
|
||||||
|
new MarkAllMsgAsRead(),
|
||||||
|
new GetProfileLike(),
|
||||||
|
new SetGroupHeader()
|
||||||
];
|
];
|
||||||
|
|
||||||
function initActionMap() {
|
function initActionMap() {
|
||||||
|
@@ -1,13 +1,18 @@
|
|||||||
import { NTQQMsgApi } from '@/core/apis';
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
message_id: { type: 'number' },
|
message_id: {
|
||||||
|
oneOf:[
|
||||||
|
{ type: 'number' },
|
||||||
|
{ type: 'string' }
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
required: ['message_id']
|
required: ['message_id']
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@@ -18,9 +23,9 @@ class DeleteMsg extends BaseAction<Payload, void> {
|
|||||||
actionName = ActionName.DeleteMsg;
|
actionName = ActionName.DeleteMsg;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
const msg = await dbUtil.getMsgByShortId(payload.message_id);
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(Number(payload.message_id));
|
||||||
if (msg) {
|
if (msg) {
|
||||||
await NTQQMsgApi.recallMsg({ peerUid: msg.peerUid, chatType: msg.chatType }, [msg.msgId]);
|
await NTQQMsgApi.recallMsg(msg.Peer, [msg.MsgId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
import { NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
import { ChatType, Peer } from '@/core/entities';
|
import { ChatType, Peer } from '@/core/entities';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
message_id: { type: 'number' },
|
message_id: { type: 'number' },
|
||||||
group_id: { type: [ 'number' , 'string' ] },
|
group_id: { type: ['number', 'string'] },
|
||||||
user_id: { type: [ 'number' , 'string' ] }
|
user_id: { type: ['number', 'string'] }
|
||||||
},
|
},
|
||||||
required: ['message_id']
|
required: ['message_id']
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@@ -30,18 +30,14 @@ class ForwardSingleMsg extends BaseAction<Payload, null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async _handle(payload: Payload): Promise<null> {
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
const msg = await dbUtil.getMsgByShortId(payload.message_id);
|
const msg = await MessageUnique.getMsgIdAndPeerByShortId(payload.message_id);
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
throw new Error(`无法找到消息${payload.message_id}`);
|
throw new Error(`无法找到消息${payload.message_id}`);
|
||||||
}
|
}
|
||||||
const peer = await this.getTargetPeer(payload);
|
const peer = await this.getTargetPeer(payload);
|
||||||
const ret = await NTQQMsgApi.forwardMsg(
|
const ret = await NTQQMsgApi.forwardMsg(msg.Peer,
|
||||||
{
|
|
||||||
chatType: msg.chatType,
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
},
|
|
||||||
peer,
|
peer,
|
||||||
[msg.msgId],
|
[msg.MsgId],
|
||||||
);
|
);
|
||||||
if (ret.result !== 0) {
|
if (ret.result !== 0) {
|
||||||
throw new Error(`转发消息失败 ${ret.errMsg}`);
|
throw new Error(`转发消息失败 ${ret.errMsg}`);
|
||||||
|
@@ -2,8 +2,9 @@ import { OB11Message } from '../../types';
|
|||||||
import { OB11Constructor } from '../../constructor';
|
import { OB11Constructor } from '../../constructor';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
import { NTQQMsgApi } from '@/core';
|
||||||
|
|
||||||
|
|
||||||
export type ReturnDataType = OB11Message
|
export type ReturnDataType = OB11Message
|
||||||
@@ -11,7 +12,7 @@ export type ReturnDataType = OB11Message
|
|||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
message_id: { type: ['number','string'] },
|
message_id: { type: ['number', 'string'] },
|
||||||
},
|
},
|
||||||
required: ['message_id']
|
required: ['message_id']
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@@ -26,14 +27,15 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
|
|||||||
if (!payload.message_id) {
|
if (!payload.message_id) {
|
||||||
throw Error('参数message_id不能为空');
|
throw Error('参数message_id不能为空');
|
||||||
}
|
}
|
||||||
let msg = await dbUtil.getMsgByShortId(parseInt(payload.message_id.toString()));
|
const MsgShortId = await MessageUnique.getShortIdByMsgId(payload.message_id.toString());
|
||||||
if (!msg) {
|
const msgIdWithPeer = await MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString()));
|
||||||
msg = await dbUtil.getMsgByLongId(payload.message_id.toString());
|
if (!msgIdWithPeer) {
|
||||||
}
|
|
||||||
if (!msg) {
|
|
||||||
throw ('消息不存在');
|
throw ('消息不存在');
|
||||||
}
|
}
|
||||||
return await OB11Constructor.message(msg);
|
const msg = await NTQQMsgApi.getMsgsByMsgId(
|
||||||
|
{ guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType },
|
||||||
|
[msgIdWithPeer?.MsgId || payload.message_id.toString()]);
|
||||||
|
return await OB11Constructor.message(msg.msgList[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -60,3 +60,12 @@ export class GoCQHTTPMarkMsgAsRead extends BaseAction<Payload, null> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MarkAllMsgAsRead extends BaseAction<Payload, null> {
|
||||||
|
actionName = ActionName._MarkAllMsgAsRead;
|
||||||
|
|
||||||
|
protected async _handle(payload: Payload): Promise<null> {
|
||||||
|
await NTQQMsgApi.markallMsgAsRead();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -5,18 +5,19 @@ import {
|
|||||||
Group,
|
Group,
|
||||||
IdMusicSignPostData,
|
IdMusicSignPostData,
|
||||||
NTQQFileApi,
|
NTQQFileApi,
|
||||||
|
NTQQMsgApi,
|
||||||
SendArkElement,
|
SendArkElement,
|
||||||
SendMessageElement,
|
SendMessageElement,
|
||||||
SendMsgElementConstructor,
|
SendMsgElementConstructor,
|
||||||
SignMusicWrapper
|
SignMusicWrapper
|
||||||
} from '@/core';
|
} from '@/core';
|
||||||
import { getGroupMember } from '@/core/data';
|
import { getGroupMember } from '@/core/data';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { logDebug, logError } from '@/common/utils/log';
|
import { logDebug, logError } from '@/common/utils/log';
|
||||||
import { uri2local } from '@/common/utils/file';
|
import { uri2local } from '@/common/utils/file';
|
||||||
import { ob11Config } from '@/onebot11/config';
|
import { ob11Config } from '@/onebot11/config';
|
||||||
import { RequestUtil } from '@/common/utils/request';
|
import { RequestUtil } from '@/common/utils/request';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
export type MessageContext = {
|
export type MessageContext = {
|
||||||
group?: Group,
|
group?: Group,
|
||||||
@@ -27,27 +28,27 @@ async function handleOb11FileLikeMessage(
|
|||||||
{ data: { file, name: payloadFileName } }: OB11MessageFileBase,
|
{ data: { file, name: payloadFileName } }: OB11MessageFileBase,
|
||||||
{ deleteAfterSentFiles }: MessageContext
|
{ deleteAfterSentFiles }: MessageContext
|
||||||
) {
|
) {
|
||||||
let uri = file;
|
const uri = file;
|
||||||
|
|
||||||
const cache = await dbUtil.getFileCacheByName(file);
|
// const cache = await dbUtil.getFileCacheByName(file);
|
||||||
if (cache) {
|
// if (cache) {
|
||||||
if (fs.existsSync(cache.path)) {
|
// if (fs.existsSync(cache.path)) {
|
||||||
uri = 'file://' + cache.path;
|
// uri = 'file://' + cache.path;
|
||||||
} else if (cache.url) {
|
// } else if (cache.url) {
|
||||||
uri = cache.url;
|
// uri = cache.url;
|
||||||
} else {
|
// } else {
|
||||||
const fileMsg = await dbUtil.getMsgByLongId(cache.msgId);
|
// const fileMsgPeer = MessageUnique.getPeerByMsgId(cache.msgId);
|
||||||
if (fileMsg) {
|
// if (fileMsgPeer) {
|
||||||
cache.path = await NTQQFileApi.downloadMedia(
|
// cache.path = await NTQQFileApi.downloadMedia(
|
||||||
fileMsg.msgId, fileMsg.chatType, fileMsg.peerUid,
|
// fileMsgPeer.MsgId, fileMsgPeer.Peer.chatType, fileMsgPeer.Peer.peerUid,
|
||||||
cache.elementId, '', ''
|
// cache.elementId, '', ''
|
||||||
);
|
// );
|
||||||
uri = 'file://' + cache.path;
|
// uri = 'file://' + cache.path;
|
||||||
dbUtil.updateFileCache(cache);
|
// dbUtil.updateFileCache(cache);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
logDebug('找到文件缓存', uri);
|
// logDebug('找到文件缓存', uri);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const { path, isLocal, fileName, errMsg } = (await uri2local(uri));
|
const { path, isLocal, fileName, errMsg } = (await uri2local(uri));
|
||||||
|
|
||||||
@@ -69,9 +70,9 @@ const _handlers: {
|
|||||||
// This picks the correct message type out
|
// This picks the correct message type out
|
||||||
// How great the type system of TypeScript is!
|
// How great the type system of TypeScript is!
|
||||||
context: MessageContext
|
context: MessageContext
|
||||||
) => SendMessageElement | undefined | Promise<SendMessageElement | undefined>
|
) => Promise<SendMessageElement | undefined>
|
||||||
} = {
|
} = {
|
||||||
[OB11MessageDataType.text]: ({ data: { text } }) => SendMsgElementConstructor.text(text),
|
[OB11MessageDataType.text]: async ({ data: { text } }) => SendMsgElementConstructor.text(text),
|
||||||
|
|
||||||
[OB11MessageDataType.at]: async ({ data: { qq: atQQ } }, context) => {
|
[OB11MessageDataType.at]: async ({ data: { qq: atQQ } }, context) => {
|
||||||
if (!context.group) return undefined;
|
if (!context.group) return undefined;
|
||||||
@@ -86,15 +87,16 @@ const _handlers: {
|
|||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.reply]: async ({ data: { id } }) => {
|
[OB11MessageDataType.reply]: async ({ data: { id } }) => {
|
||||||
const replyMsg = await dbUtil.getMsgByShortId(parseInt(id));
|
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
|
||||||
|
const replyMsg = (await NTQQMsgApi.getMsgsByMsgId(replyMsgM?.Peer!, [replyMsgM?.MsgId!])).msgList[0];
|
||||||
return replyMsg ?
|
return replyMsg ?
|
||||||
SendMsgElementConstructor.reply(replyMsg.msgSeq, replyMsg.msgId, replyMsg.senderUin!, replyMsg.senderUin!) :
|
SendMsgElementConstructor.reply(replyMsg.msgSeq, replyMsg.msgId, replyMsg.senderUin!, replyMsg.senderUin!) :
|
||||||
undefined;
|
undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.face]: ({ data: { id } }) => SendMsgElementConstructor.face(parseInt(id)),
|
[OB11MessageDataType.face]: async ({ data: { id } }) => SendMsgElementConstructor.face(parseInt(id)),
|
||||||
|
|
||||||
[OB11MessageDataType.mface]: ({
|
[OB11MessageDataType.mface]: async ({
|
||||||
data: {
|
data: {
|
||||||
emoji_package_id,
|
emoji_package_id,
|
||||||
emoji_id,
|
emoji_id,
|
||||||
@@ -144,13 +146,13 @@ const _handlers: {
|
|||||||
[OB11MessageDataType.voice]: async (sendMsg, context) =>
|
[OB11MessageDataType.voice]: async (sendMsg, context) =>
|
||||||
SendMsgElementConstructor.ptt((await handleOb11FileLikeMessage(sendMsg, context)).path),
|
SendMsgElementConstructor.ptt((await handleOb11FileLikeMessage(sendMsg, context)).path),
|
||||||
|
|
||||||
[OB11MessageDataType.json]: ({ data: { data } }) => SendMsgElementConstructor.ark(data),
|
[OB11MessageDataType.json]: async ({ data: { data } }) => SendMsgElementConstructor.ark(data),
|
||||||
|
|
||||||
[OB11MessageDataType.dice]: ({ data: { result } }) => SendMsgElementConstructor.dice(result),
|
[OB11MessageDataType.dice]: async ({ data: { result } }) => SendMsgElementConstructor.dice(result),
|
||||||
|
|
||||||
[OB11MessageDataType.RPS]: ({ data: { result } }) => SendMsgElementConstructor.rps(result),
|
[OB11MessageDataType.RPS]: async ({ data: { result } }) => SendMsgElementConstructor.rps(result),
|
||||||
|
|
||||||
[OB11MessageDataType.markdown]: ({ data: { content } }) => SendMsgElementConstructor.markdown(content),
|
[OB11MessageDataType.markdown]: async ({ data: { content } }) => SendMsgElementConstructor.markdown(content),
|
||||||
|
|
||||||
[OB11MessageDataType.music]: async ({ data }) => {
|
[OB11MessageDataType.music]: async ({ data }) => {
|
||||||
// 保留, 直到...找到更好的解决方案
|
// 保留, 直到...找到更好的解决方案
|
||||||
@@ -202,20 +204,24 @@ const _handlers: {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.node]: () => undefined,
|
[OB11MessageDataType.node]: async () => undefined,
|
||||||
|
|
||||||
[OB11MessageDataType.forward]: () => undefined,
|
[OB11MessageDataType.forward]: async () => undefined,
|
||||||
|
|
||||||
[OB11MessageDataType.xml]: () => undefined,
|
[OB11MessageDataType.xml]: async () => undefined,
|
||||||
|
|
||||||
[OB11MessageDataType.poke]: () => undefined,
|
[OB11MessageDataType.poke]: async () => undefined,
|
||||||
|
|
||||||
|
[OB11MessageDataType.Location]: async () => {
|
||||||
|
return SendMsgElementConstructor.location();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlers = <{
|
const handlers = <{
|
||||||
[Key in OB11MessageDataType]: (
|
[Key in OB11MessageDataType]: (
|
||||||
sendMsg: OB11MessageData,
|
sendMsg: OB11MessageData,
|
||||||
context: MessageContext
|
context: MessageContext
|
||||||
) => SendMessageElement | undefined | Promise<SendMessageElement | undefined>
|
) => Promise<SendMessageElement | undefined>
|
||||||
}>_handlers;
|
}>_handlers;
|
||||||
|
|
||||||
export default async function createSendElements(
|
export default async function createSendElements(
|
||||||
@@ -223,18 +229,20 @@ export default async function createSendElements(
|
|||||||
group?: Group,
|
group?: Group,
|
||||||
ignoreTypes: OB11MessageDataType[] = []
|
ignoreTypes: OB11MessageDataType[] = []
|
||||||
) {
|
) {
|
||||||
const sendElements: SendMessageElement[] = [];
|
|
||||||
const deleteAfterSentFiles: string[] = [];
|
const deleteAfterSentFiles: string[] = [];
|
||||||
|
let callResultList: Array<Promise<SendMessageElement | undefined>> = [];
|
||||||
for (const sendMsg of messageData) {
|
for (const sendMsg of messageData) {
|
||||||
if (ignoreTypes.includes(sendMsg.type)) {
|
if (ignoreTypes.includes(sendMsg.type)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const callResult = await handlers[sendMsg.type](
|
const callResult = handlers[sendMsg.type](
|
||||||
sendMsg,
|
sendMsg,
|
||||||
{ group, deleteAfterSentFiles }
|
{ group, deleteAfterSentFiles }
|
||||||
);
|
)?.catch(undefined);
|
||||||
if (callResult) sendElements.push(callResult);
|
callResultList.push(callResult);
|
||||||
}
|
}
|
||||||
|
let ret = await Promise.all(callResultList);
|
||||||
|
const sendElements: SendMessageElement[] = ret.filter(ele => ele) as SendMessageElement[];
|
||||||
return { sendElements, deleteAfterSentFiles };
|
return { sendElements, deleteAfterSentFiles };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
import { ChatType, ElementType, Group, NTQQMsgApi, Peer, RawMessage, SendMessageElement } from '@/core';
|
import { ChatType, ElementType, Group, NTQQMsgApi, Peer, RawMessage, SendMessageElement } from '@/core';
|
||||||
import { OB11MessageNode } from '@/onebot11/types';
|
import { OB11MessageNode } from '@/onebot11/types';
|
||||||
import { selfInfo } from '@/core/data';
|
import { selfInfo } from '@/core/data';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import createSendElements from '@/onebot11/action/msg/SendMsg/create-send-elements';
|
import createSendElements from '@/onebot11/action/msg/SendMsg/create-send-elements';
|
||||||
import { logDebug, logError } from '@/common/utils/log';
|
import { logDebug, logError } from '@/common/utils/log';
|
||||||
import { sleep } from '@/common/utils/helper';
|
import { sleep } from '@/common/utils/helper';
|
||||||
import fs from 'node:fs';
|
|
||||||
import { normalize, sendMsg } from '@/onebot11/action/msg/SendMsg/index';
|
import { normalize, sendMsg } from '@/onebot11/action/msg/SendMsg/index';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
async function cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
|
async function cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
|
||||||
const selfPeer = {
|
const selfPeer = {
|
||||||
@@ -14,7 +13,7 @@ async function cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
|
|||||||
peerUid: selfInfo.uid
|
peerUid: selfInfo.uid
|
||||||
};
|
};
|
||||||
|
|
||||||
// logDebug('克隆的目标消息', msg);
|
//logDebug('克隆的目标消息', msg);
|
||||||
|
|
||||||
const sendElements: SendMessageElement[] = [];
|
const sendElements: SendMessageElement[] = [];
|
||||||
|
|
||||||
@@ -54,13 +53,14 @@ export async function handleForwardNode(destPeer: Peer, messageNodes: OB11Messag
|
|||||||
const nodeId = messageNode.data.id;
|
const nodeId = messageNode.data.id;
|
||||||
// 有nodeId表示一个子转发消息卡片
|
// 有nodeId表示一个子转发消息卡片
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
const nodeMsg = await dbUtil.getMsgByShortId(parseInt(nodeId));
|
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId));
|
||||||
if (!needClone) {
|
if (!needClone) {
|
||||||
nodeMsgIds.push(nodeMsg!.msgId);
|
nodeMsgIds.push(nodeMsg!.MsgId);
|
||||||
} else {
|
} else {
|
||||||
if (nodeMsg!.peerUid !== selfInfo.uid) {
|
if (nodeMsg!.Peer.peerUid !== selfInfo.uid) {
|
||||||
// need cloning
|
// need cloning
|
||||||
const clonedMsg = await cloneMsg(nodeMsg!);
|
const rawClone = await NTQQMsgApi.getMsgsByMsgId(nodeMsg?.Peer!, [nodeMsg?.MsgId!]);
|
||||||
|
const clonedMsg = await cloneMsg(rawClone.msgList[0]);
|
||||||
if (clonedMsg) {
|
if (clonedMsg) {
|
||||||
nodeMsgIds.push(clonedMsg.msgId);
|
nodeMsgIds.push(clonedMsg.msgId);
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ export async function handleForwardNode(destPeer: Peer, messageNodes: OB11Messag
|
|||||||
//logDebug(sendElementsSplit);
|
//logDebug(sendElementsSplit);
|
||||||
}
|
}
|
||||||
// log("分割后的转发节点", sendElementsSplit)
|
// log("分割后的转发节点", sendElementsSplit)
|
||||||
const MsgNodeList: Promise<RawMessage>[] = [];
|
const MsgNodeList: Promise<RawMessage | undefined>[] = [];
|
||||||
for (const sendElementsSplitElement of sendElementsSplit) {
|
for (const sendElementsSplitElement of sendElementsSplit) {
|
||||||
MsgNodeList.push(sendMsg(selfPeer, sendElementsSplitElement, [], true));
|
MsgNodeList.push(sendMsg(selfPeer, sendElementsSplitElement, [], true));
|
||||||
await sleep(Math.trunc(sendElementsSplit.length / 10) * 100);
|
await sleep(Math.trunc(sendElementsSplit.length / 10) * 100);
|
||||||
@@ -99,7 +99,9 @@ export async function handleForwardNode(destPeer: Peer, messageNodes: OB11Messag
|
|||||||
}
|
}
|
||||||
for (const msgNode of MsgNodeList) {
|
for (const msgNode of MsgNodeList) {
|
||||||
const result = await msgNode;
|
const result = await msgNode;
|
||||||
nodeMsgIds.push(result.msgId);
|
if (result) {
|
||||||
|
nodeMsgIds.push(result.msgId);
|
||||||
|
}
|
||||||
//logDebug('转发节点生成成功', result.msgId);
|
//logDebug('转发节点生成成功', result.msgId);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -107,13 +109,14 @@ export async function handleForwardNode(destPeer: Peer, messageNodes: OB11Messag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查srcPeer是否一致,不一致则需要克隆成自己的消息, 让所有srcPeer都变成自己的,使其保持一致才能够转发
|
// 检查srcPeer是否一致,不一致则需要克隆成自己的消息, 让所有srcPeer都变成自己的,使其保持一致才能够转发
|
||||||
const nodeMsgArray: Array<RawMessage> = [];
|
const nodeMsgArray: Array<RawMessage> = [];
|
||||||
|
|
||||||
let srcPeer: Peer | undefined = undefined;
|
let srcPeer: Peer | undefined = undefined;
|
||||||
let needSendSelf = false;
|
let needSendSelf = false;
|
||||||
for (const msgId of nodeMsgIds) {
|
for (const msgId of nodeMsgIds) {
|
||||||
const nodeMsg = await dbUtil.getMsgByLongId(msgId);
|
const nodeMsgPeer = await MessageUnique.getPeerByMsgId(msgId);
|
||||||
|
const nodeMsg = (await NTQQMsgApi.getMsgsByMsgId(nodeMsgPeer?.Peer!, [msgId])).msgList[0];
|
||||||
if (nodeMsg) {
|
if (nodeMsg) {
|
||||||
nodeMsgArray.push(nodeMsg);
|
nodeMsgArray.push(nodeMsg);
|
||||||
if (!srcPeer) {
|
if (!srcPeer) {
|
||||||
@@ -124,7 +127,7 @@ export async function handleForwardNode(destPeer: Peer, messageNodes: OB11Messag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//logDebug('nodeMsgArray', nodeMsgArray);
|
// logDebug('nodeMsgArray', nodeMsgArray);
|
||||||
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
|
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
|
||||||
if (needSendSelf) {
|
if (needSendSelf) {
|
||||||
//logDebug('需要克隆转发消息');
|
//logDebug('需要克隆转发消息');
|
||||||
|
@@ -8,13 +8,13 @@ import {
|
|||||||
} from '@/onebot11/types';
|
} from '@/onebot11/types';
|
||||||
import { ActionName, BaseCheckResult } from '@/onebot11/action/types';
|
import { ActionName, BaseCheckResult } from '@/onebot11/action/types';
|
||||||
import { getGroup } from '@/core/data';
|
import { getGroup } from '@/core/data';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { ChatType, ElementType, Group, NTQQFileApi, NTQQFriendApi, NTQQMsgApi, NTQQUserApi, Peer, SendMessageElement, } from '@/core';
|
import { ChatType, ElementType, Group, NTQQFileApi, NTQQFriendApi, NTQQMsgApi, NTQQUserApi, Peer, SendMessageElement, } from '@/core';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import { logDebug, logError } from '@/common/utils/log';
|
import { logDebug, logError } from '@/common/utils/log';
|
||||||
import { decodeCQCode } from '@/onebot11/cqcode';
|
import { decodeCQCode } from '@/onebot11/cqcode';
|
||||||
import createSendElements from './create-send-elements';
|
import createSendElements from './create-send-elements';
|
||||||
import { handleForwardNode } from '@/onebot11/action/msg/SendMsg/handle-forward-node';
|
import { handleForwardNode } from '@/onebot11/action/msg/SendMsg/handle-forward-node';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
export interface ReturnDataType {
|
export interface ReturnDataType {
|
||||||
message_id: number;
|
message_id: number;
|
||||||
@@ -66,10 +66,10 @@ export async function sendMsg(peer: Peer, sendElements: SendMessageElement[], de
|
|||||||
}
|
}
|
||||||
const returnMsg = await NTQQMsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
|
const returnMsg = await NTQQMsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
|
||||||
try {
|
try {
|
||||||
returnMsg.id = await dbUtil.addMsg(returnMsg, false);
|
returnMsg!.id = await MessageUnique.createMsg({ chatType: peer.chatType, guildId: '', peerUid: peer.peerUid }, returnMsg!.msgId);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logDebug('发送消息id获取失败', e);
|
logDebug('发送消息id获取失败', e);
|
||||||
returnMsg.id = 0;
|
returnMsg!.id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAfterSentFiles.map((f) => {
|
deleteAfterSentFiles.map((f) => {
|
||||||
@@ -102,6 +102,7 @@ async function createContext(payload: OB11PostSendMsg, contextMode: ContextMode)
|
|||||||
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
|
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
|
||||||
const Uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
const Uid = await NTQQUserApi.getUidByUin(payload.user_id.toString());
|
||||||
const isBuddy = await NTQQFriendApi.isBuddy(Uid!);
|
const isBuddy = await NTQQFriendApi.isBuddy(Uid!);
|
||||||
|
//console.log("[调试代码] UIN:", payload.user_id, " UID:", Uid, " IsBuddy:", isBuddy);
|
||||||
return {
|
return {
|
||||||
peer: {
|
peer: {
|
||||||
chatType: isBuddy ? ChatType.friend : ChatType.temp,
|
chatType: isBuddy ? ChatType.friend : ChatType.temp,
|
||||||
@@ -144,7 +145,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
protected async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
|
||||||
let { peer, group } = await createContext(payload, this.contextMode);
|
const { peer, group } = await createContext(payload, this.contextMode);
|
||||||
|
|
||||||
const messages = normalize(
|
const messages = normalize(
|
||||||
payload.message,
|
payload.message,
|
||||||
@@ -154,8 +155,8 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
|
||||||
const returnMsg = await handleForwardNode(peer, messages as OB11MessageNode[], group);
|
const returnMsg = await handleForwardNode(peer, messages as OB11MessageNode[], group);
|
||||||
if (returnMsg) {
|
if (returnMsg) {
|
||||||
const msgShortId = await dbUtil.addMsg(returnMsg!, false);
|
const msgShortId = await MessageUnique.createMsg({ guildId: '', peerUid: peer.peerUid, chatType: peer.chatType }, returnMsg!.msgId);
|
||||||
return { message_id: msgShortId };
|
return { message_id: msgShortId! };
|
||||||
} else {
|
} else {
|
||||||
throw Error('发送转发消息失败');
|
throw Error('发送转发消息失败');
|
||||||
}
|
}
|
||||||
@@ -171,7 +172,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
const { sendElements, deleteAfterSentFiles } = await createSendElements(messages, group);
|
const { sendElements, deleteAfterSentFiles } = await createSendElements(messages, group);
|
||||||
//console.log(peer, JSON.stringify(sendElements,null,2));
|
//console.log(peer, JSON.stringify(sendElements,null,2));
|
||||||
const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles);
|
const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles);
|
||||||
return { message_id: returnMsg.id! };
|
return { message_id: returnMsg!.id! };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { NTQQMsgApi } from '@/core/apis';
|
import { NTQQMsgApi } from '@/core/apis';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -19,16 +19,14 @@ export class SetMsgEmojiLike extends BaseAction<Payload, any> {
|
|||||||
actionName = ActionName.SetMsgEmojiLike;
|
actionName = ActionName.SetMsgEmojiLike;
|
||||||
PayloadSchema = SchemaData;
|
PayloadSchema = SchemaData;
|
||||||
protected async _handle(payload: Payload) {
|
protected async _handle(payload: Payload) {
|
||||||
const msg = await dbUtil.getMsgByShortId(parseInt(payload.message_id.toString()));
|
const msg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
|
||||||
if (!msg) {
|
if (!msg) {
|
||||||
throw new Error('msg not found');
|
throw new Error('msg not found');
|
||||||
}
|
}
|
||||||
if (!payload.emoji_id){
|
if (!payload.emoji_id){
|
||||||
throw new Error('emojiId not found');
|
throw new Error('emojiId not found');
|
||||||
}
|
}
|
||||||
return await NTQQMsgApi.setEmojiLike({
|
const msgSeq = (await NTQQMsgApi.getMsgsByMsgId(msg.Peer, [msg.MsgId])).msgList[0].msgSeq;
|
||||||
chatType: msg.chatType,
|
return await NTQQMsgApi.setEmojiLike(msg.Peer, msgSeq, payload.emoji_id.toString(), true);
|
||||||
peerUid: msg.peerUid
|
|
||||||
}, msg.msgSeq, payload.emoji_id.toString(), true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,6 @@ import {
|
|||||||
ChatCacheListItemBasic,
|
ChatCacheListItemBasic,
|
||||||
CacheFileType
|
CacheFileType
|
||||||
} from '@/core/entities';
|
} from '@/core/entities';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { NTQQFileApi, NTQQFileCacheApi } from '@/core/apis/file';
|
import { NTQQFileApi, NTQQFileCacheApi } from '@/core/apis/file';
|
||||||
import { logError } from '@/common/utils/log';
|
import { logError } from '@/common/utils/log';
|
||||||
|
|
||||||
|
@@ -93,5 +93,11 @@ export enum ActionName {
|
|||||||
SetSelfProfile = 'set_self_profile',
|
SetSelfProfile = 'set_self_profile',
|
||||||
CreateCollection = 'create_collection',
|
CreateCollection = 'create_collection',
|
||||||
GetCollectionList = 'get_collection_list',
|
GetCollectionList = 'get_collection_list',
|
||||||
SetLongNick = 'set_self_longnick'
|
SetLongNick = 'set_self_longnick',
|
||||||
|
SetEssenceMsg = 'set_essence_msg',
|
||||||
|
DelEssenceMsg = 'delete_essence_msg',
|
||||||
|
GetRecentContact = 'get_recent_contact',
|
||||||
|
_MarkAllMsgAsRead = '_mark_all_as_read',
|
||||||
|
GetProfileLike = 'get_profile_like',
|
||||||
|
SetGroupHeader = "set_group_head"
|
||||||
}
|
}
|
||||||
|
12
src/onebot11/action/user/GetRecentContact.ts
Normal file
12
src/onebot11/action/user/GetRecentContact.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
import BaseAction from '../BaseAction';
|
||||||
|
import { ActionName } from '../types';
|
||||||
|
import { NTQQUserApi } from '@/core';
|
||||||
|
|
||||||
|
export default class GetRecentContact extends BaseAction<void, any> {
|
||||||
|
actionName = ActionName.GetRecentContact;
|
||||||
|
protected async _handle(payload: void) {
|
||||||
|
//没有效果
|
||||||
|
return await NTQQUserApi.getRecentContactListSnapShot(10);
|
||||||
|
}
|
||||||
|
}
|
@@ -42,6 +42,7 @@ export interface OB11Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Config extends ConfigBase<OB11Config> implements OB11Config {
|
class Config extends ConfigBase<OB11Config> implements OB11Config {
|
||||||
|
name: string = 'onebot11'
|
||||||
http = {
|
http = {
|
||||||
enable: false,
|
enable: false,
|
||||||
host: '',
|
host: '',
|
||||||
@@ -72,10 +73,6 @@ class Config extends ConfigBase<OB11Config> implements OB11Config {
|
|||||||
RecordList: [] as Array<string>
|
RecordList: [] as Array<string>
|
||||||
};
|
};
|
||||||
|
|
||||||
getConfigPath() {
|
|
||||||
return path.join(this.getConfigDir(), `onebot11_${selfInfo.uin}.json`);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getKeys(): string[] | null {
|
protected getKeys(): string[] | null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ import {
|
|||||||
Group,
|
Group,
|
||||||
GroupMember,
|
GroupMember,
|
||||||
IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, mFaceCache,
|
IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, mFaceCache,
|
||||||
|
Peer,
|
||||||
RawMessage,
|
RawMessage,
|
||||||
SelfInfo,
|
SelfInfo,
|
||||||
Sex,
|
Sex,
|
||||||
@@ -26,7 +27,6 @@ import {
|
|||||||
} from '@/core/entities';
|
} from '@/core/entities';
|
||||||
import { EventType } from './event/OB11BaseEvent';
|
import { EventType } from './event/OB11BaseEvent';
|
||||||
import { encodeCQCode } from './cqcode';
|
import { encodeCQCode } from './cqcode';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { OB11GroupIncreaseEvent } from './event/notice/OB11GroupIncreaseEvent';
|
import { OB11GroupIncreaseEvent } from './event/notice/OB11GroupIncreaseEvent';
|
||||||
import { OB11GroupBanEvent } from './event/notice/OB11GroupBanEvent';
|
import { OB11GroupBanEvent } from './event/notice/OB11GroupBanEvent';
|
||||||
import { OB11GroupUploadNoticeEvent } from './event/notice/OB11GroupUploadNoticeEvent';
|
import { OB11GroupUploadNoticeEvent } from './event/notice/OB11GroupUploadNoticeEvent';
|
||||||
@@ -42,7 +42,10 @@ import { ob11Config } from '@/onebot11/config';
|
|||||||
import { deleteGroup, getGroupMember, groupMembers, selfInfo, tempGroupCodeMap } from '@/core/data';
|
import { deleteGroup, getGroupMember, groupMembers, selfInfo, tempGroupCodeMap } from '@/core/data';
|
||||||
import { NTQQFileApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
import { NTQQFileApi, NTQQGroupApi, NTQQMsgApi, NTQQUserApi } from '@/core/apis';
|
||||||
import { OB11GroupMsgEmojiLikeEvent } from '@/onebot11/event/notice/OB11MsgEmojiLikeEvent';
|
import { OB11GroupMsgEmojiLikeEvent } from '@/onebot11/event/notice/OB11MsgEmojiLikeEvent';
|
||||||
import { napCatCore } from '@/core';
|
import { OB11FriendPokeEvent, OB11GroupPokeEvent } from './event/notice/OB11PokeEvent';
|
||||||
|
import { OB11BaseNoticeEvent } from './event/notice/OB11BaseNoticeEvent';
|
||||||
|
import { OB11GroupEssenceEvent } from './event/notice/OB11GroupEssenceEvent';
|
||||||
|
import { MessageUnique } from '@/common/utils/MessageUnique';
|
||||||
|
|
||||||
|
|
||||||
export class OB11Constructor {
|
export class OB11Constructor {
|
||||||
@@ -133,26 +136,26 @@ export class OB11Constructor {
|
|||||||
message_data['type'] = 'reply';
|
message_data['type'] = 'reply';
|
||||||
// log("收到回复消息", element.replyElement.replayMsgSeq)
|
// log("收到回复消息", element.replyElement.replayMsgSeq)
|
||||||
try {
|
try {
|
||||||
// let retData = await NTQQMsgApi.getMsgsBySeqAndCount(
|
let replyMsg = await NTQQMsgApi.getMsgsBySeqAndCount(
|
||||||
// {
|
{
|
||||||
// chatType: msg.chatType,
|
chatType: msg.chatType,
|
||||||
// peerUid: msg.peerUid,
|
peerUid: msg.peerUid,
|
||||||
// guildId: '',
|
guildId: '',
|
||||||
// },
|
},
|
||||||
// element.replyElement.replayMsgSeq,
|
element.replyElement.replayMsgSeq,
|
||||||
// 1,
|
1,
|
||||||
// false,
|
true,
|
||||||
// true
|
true
|
||||||
// );
|
);
|
||||||
// console.log(JSON.stringify(retData, null, 2));
|
// console.log(JSON.stringify(retData, null, 2));
|
||||||
const replyMsg = await dbUtil.getMsgBySeq(msg.peerUid, element.replyElement.replayMsgSeq);
|
// const replyMsg = await NTQQMsgApi.getMsgsBySeqAndCount({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, element.replyElement.replayMsgSeq, 1, true, true);
|
||||||
// log("找到回复消息", replyMsg.msgShortId, replyMsg.msgId)
|
if (replyMsg) {
|
||||||
if (replyMsg && replyMsg.id) {
|
message_data['data']['id'] = MessageUnique.createMsg({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, replyMsg.msgList[0].msgId)?.toString();
|
||||||
message_data['data']['id'] = replyMsg.id!.toString();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
//log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logError('获取不到引用的消息', e.stack, element.replyElement.replayMsgSeq);
|
logError('获取不到引用的消息', e.stack, element.replyElement.replayMsgSeq);
|
||||||
}
|
}
|
||||||
@@ -162,29 +165,29 @@ export class OB11Constructor {
|
|||||||
message_data['type'] = 'image';
|
message_data['type'] = 'image';
|
||||||
// message_data["data"]["file"] = element.picElement.sourcePath
|
// message_data["data"]["file"] = element.picElement.sourcePath
|
||||||
message_data['data']['file'] = element.picElement.fileName;
|
message_data['data']['file'] = element.picElement.fileName;
|
||||||
message_data['subType']= element.picElement.picSubType;
|
message_data['subType'] = element.picElement.picSubType;
|
||||||
// message_data["data"]["path"] = element.picElement.sourcePath
|
// message_data["data"]["path"] = element.picElement.sourcePath
|
||||||
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
// let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement, msg.chatType !== ChatType.group);
|
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logError('获取图片url失败', e.stack);
|
logError('获取图片url失败', e.stack);
|
||||||
}
|
}
|
||||||
//console.log(message_data['data']['url'])
|
//console.log(message_data['data']['url'])
|
||||||
// message_data["data"]["file_id"] = element.picElement.fileUuid
|
// message_data["data"]["file_id"] = element.picElement.fileUuid
|
||||||
message_data['data']['file_size'] = element.picElement.fileSize;
|
message_data['data']['file_size'] = element.picElement.fileSize;
|
||||||
dbUtil.addFileCache({
|
// dbUtil.addFileCache({
|
||||||
name: element.picElement.fileName,
|
// name: element.picElement.fileName,
|
||||||
path: element.picElement.sourcePath,
|
// path: element.picElement.sourcePath,
|
||||||
size: element.picElement.fileSize,
|
// size: element.picElement.fileSize,
|
||||||
url: message_data['data']['url'],
|
// url: message_data['data']['url'],
|
||||||
uuid: element.picElement.fileUuid || '',
|
// uuid: element.picElement.fileUuid || '',
|
||||||
msgId: msg.msgId,
|
// msgId: msg.msgId,
|
||||||
element: element.picElement,
|
// element: element.picElement,
|
||||||
elementType: ElementType.PIC,
|
// elementType: ElementType.PIC,
|
||||||
elementId: element.elementId
|
// elementId: element.elementId
|
||||||
}).then();
|
// }).then();
|
||||||
// 不自动下载图片
|
// 不自动下载图片
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -199,17 +202,18 @@ export class OB11Constructor {
|
|||||||
message_data['data']['file_id'] = videoOrFileElement.fileUuid;
|
message_data['data']['file_id'] = videoOrFileElement.fileUuid;
|
||||||
message_data['data']['file_size'] = videoOrFileElement.fileSize;
|
message_data['data']['file_size'] = videoOrFileElement.fileSize;
|
||||||
if (!element.videoElement) {
|
if (!element.videoElement) {
|
||||||
dbUtil.addFileCache({
|
// dbUtil.addFileCache({
|
||||||
msgId: msg.msgId,
|
// msgId: msg.msgId,
|
||||||
name: videoOrFileElement.fileName,
|
// name: videoOrFileElement.fileName,
|
||||||
path: videoOrFileElement.filePath,
|
// path: videoOrFileElement.filePath,
|
||||||
size: parseInt(videoOrFileElement.fileSize || '0'),
|
// size: parseInt(videoOrFileElement.fileSize || '0'),
|
||||||
uuid: videoOrFileElement.fileUuid || '',
|
// uuid: videoOrFileElement.fileUuid || '',
|
||||||
url: '',
|
// url: '',
|
||||||
element: element.videoElement || element.fileElement,
|
// element: element.videoElement || element.fileElement,
|
||||||
elementType: element.videoElement ? ElementType.VIDEO : ElementType.FILE,
|
// elementType: element.videoElement ? ElementType.VIDEO : ElementType.FILE,
|
||||||
elementId: element.elementId
|
// elementId: element.elementId
|
||||||
}).then();
|
// }).then();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (element.pttElement) {
|
else if (element.pttElement) {
|
||||||
@@ -218,17 +222,17 @@ export class OB11Constructor {
|
|||||||
message_data['data']['path'] = element.pttElement.filePath;
|
message_data['data']['path'] = element.pttElement.filePath;
|
||||||
// message_data["data"]["file_id"] = element.pttElement.fileUuid
|
// message_data["data"]["file_id"] = element.pttElement.fileUuid
|
||||||
message_data['data']['file_size'] = element.pttElement.fileSize;
|
message_data['data']['file_size'] = element.pttElement.fileSize;
|
||||||
dbUtil.addFileCache({
|
// dbUtil.addFileCache({
|
||||||
name: element.pttElement.fileName,
|
// name: element.pttElement.fileName,
|
||||||
path: element.pttElement.filePath,
|
// path: element.pttElement.filePath,
|
||||||
size: parseInt(element.pttElement.fileSize) || 0,
|
// size: parseInt(element.pttElement.fileSize) || 0,
|
||||||
url: '',
|
// url: '',
|
||||||
uuid: element.pttElement.fileUuid || '',
|
// uuid: element.pttElement.fileUuid || '',
|
||||||
msgId: msg.msgId,
|
// msgId: msg.msgId,
|
||||||
element: element.pttElement,
|
// element: element.pttElement,
|
||||||
elementType: ElementType.PTT,
|
// elementType: ElementType.PTT,
|
||||||
elementId: element.elementId
|
// elementId: element.elementId
|
||||||
}).then();
|
// }).then();
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (element.arkElement) {
|
else if (element.arkElement) {
|
||||||
@@ -281,16 +285,41 @@ export class OB11Constructor {
|
|||||||
else (resMsg.message as OB11MessageData[]).push(message_data);
|
else (resMsg.message as OB11MessageData[]).push(message_data);
|
||||||
resMsg.raw_message += cqCode;
|
resMsg.raw_message += cqCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
resMsg.raw_message = resMsg.raw_message.trim();
|
resMsg.raw_message = resMsg.raw_message.trim();
|
||||||
return resMsg;
|
return resMsg;
|
||||||
}
|
}
|
||||||
|
static async PrivateEvent(msg: RawMessage): Promise<OB11BaseNoticeEvent | undefined> {
|
||||||
|
if (msg.chatType !== ChatType.friend) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const element of msg.elements) {
|
||||||
|
if (element.grayTipElement) {
|
||||||
|
if (element.grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
|
||||||
|
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr);
|
||||||
|
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||||
|
//判断业务类型
|
||||||
|
//Poke事件
|
||||||
|
let pokedetail: any[] = json.items;
|
||||||
|
//筛选item带有uid的元素
|
||||||
|
pokedetail = pokedetail.filter(item => item.uid);
|
||||||
|
//console.log("[NapCat] 群拍一拍 群:", pokedetail, parseInt(msg.peerUid), " ", await NTQQUserApi.getUinByUid(pokedetail[0].uid), "拍了拍", await NTQQUserApi.getUinByUid(pokedetail[1].uid));
|
||||||
|
if (pokedetail.length == 2) {
|
||||||
|
return new OB11FriendPokeEvent(parseInt((await NTQQUserApi.getUinByUid(pokedetail[0].uid))!), parseInt((await NTQQUserApi.getUinByUid(pokedetail[1].uid))!));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
static async GroupEvent(msg: RawMessage): Promise<OB11GroupNoticeEvent | undefined> {
|
static async GroupEvent(msg: RawMessage): Promise<OB11GroupNoticeEvent | undefined> {
|
||||||
if (msg.chatType !== ChatType.group) {
|
if (msg.chatType !== ChatType.group) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.senderUin) {
|
//log("group msg", msg);
|
||||||
|
if (msg.senderUin && msg.senderUin !== '0') {
|
||||||
const member = await getGroupMember(msg.peerUid, msg.senderUin);
|
const member = await getGroupMember(msg.peerUid, msg.senderUin);
|
||||||
if (member && member.cardName !== msg.sendMemberName) {
|
if (member && member.cardName !== msg.sendMemberName) {
|
||||||
const newCardName = msg.sendMemberName || '';
|
const newCardName = msg.sendMemberName || '';
|
||||||
@@ -299,7 +328,7 @@ export class OB11Constructor {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// log("group msg", msg);
|
|
||||||
for (const element of msg.elements) {
|
for (const element of msg.elements) {
|
||||||
const grayTipElement = element.grayTipElement;
|
const grayTipElement = element.grayTipElement;
|
||||||
const groupElement = grayTipElement?.groupElement;
|
const groupElement = grayTipElement?.groupElement;
|
||||||
@@ -369,33 +398,23 @@ export class OB11Constructor {
|
|||||||
busid: element.fileElement.fileBizId || 0
|
busid: element.fileElement.fileBizId || 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grayTipElement) {
|
if (grayTipElement) {
|
||||||
const xmlElement = grayTipElement.xmlElement;
|
if (grayTipElement.xmlElement?.templId === '10382') {
|
||||||
|
|
||||||
if (xmlElement?.templId === '10382') {
|
|
||||||
// 表情回应消息
|
|
||||||
// "content":
|
|
||||||
// "<gtip align=\"center\">
|
|
||||||
// <qq uin=\"u_snYxnEfja-Po_\" col=\"3\" jp=\"3794\"/>
|
|
||||||
// <nor txt=\"回应了你的\"/>
|
|
||||||
// <url jp= \"\" msgseq=\"74711\" col=\"3\" txt=\"消息:\"/>
|
|
||||||
// <face type=\"1\" id=\"76\"> </face>
|
|
||||||
// </gtip>",
|
|
||||||
const emojiLikeData = new fastXmlParser.XMLParser({
|
const emojiLikeData = new fastXmlParser.XMLParser({
|
||||||
ignoreAttributes: false,
|
ignoreAttributes: false,
|
||||||
attributeNamePrefix: ''
|
attributeNamePrefix: ''
|
||||||
}).parse(xmlElement.content);
|
}).parse(grayTipElement.xmlElement.content);
|
||||||
logDebug('收到表情回应我的消息', emojiLikeData);
|
logDebug('收到表情回应我的消息', emojiLikeData);
|
||||||
try {
|
try {
|
||||||
const senderUin = emojiLikeData.gtip.qq.jp;
|
const senderUin = emojiLikeData.gtip.qq.jp;
|
||||||
const msgSeq = emojiLikeData.gtip.url.msgseq;
|
const msgSeq = emojiLikeData.gtip.url.msgseq;
|
||||||
const emojiId = emojiLikeData.gtip.face.id;
|
const emojiId = emojiLikeData.gtip.face.id;
|
||||||
const replyMsg = await dbUtil.getMsgBySeq(msg.peerUid, msgSeq);
|
const replyMsgList = (await NTQQMsgApi.getMsgsBySeqAndCount({ chatType: ChatType.group, guildId: '', peerUid: msg.peerUid }, msgSeq, 1, true, true)).msgList;
|
||||||
if (!replyMsg) {
|
if (replyMsgList.length < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return new OB11GroupMsgEmojiLikeEvent(parseInt(msg.peerUid), parseInt(senderUin), replyMsg.id!, [{
|
const replyMsg = replyMsgList[0];
|
||||||
|
return new OB11GroupMsgEmojiLikeEvent(parseInt(msg.peerUid), parseInt(senderUin), MessageUnique.getShortIdByMsgId(replyMsg?.msgId!)!, [{
|
||||||
emoji_id: emojiId,
|
emoji_id: emojiId,
|
||||||
count: 1
|
count: 1
|
||||||
}]);
|
}]);
|
||||||
@@ -422,36 +441,40 @@ export class OB11Constructor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE
|
||||||
else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
|
else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
|
||||||
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
|
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
|
||||||
/*
|
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||||
{
|
//判断业务类型
|
||||||
align: 'center',
|
//Poke事件
|
||||||
items: [
|
const pokedetail: any[] = json.items;
|
||||||
{ txt: '恭喜', type: 'nor' },
|
//筛选item带有uid的元素
|
||||||
{
|
const poke_uid = pokedetail.filter(item => item.uid);
|
||||||
col: '3',
|
if (poke_uid.length == 2) {
|
||||||
jp: '5',
|
return new OB11GroupPokeEvent(parseInt(msg.peerUid), parseInt((await NTQQUserApi.getUinByUid(poke_uid[0].uid))!), parseInt((await NTQQUserApi.getUinByUid(poke_uid[1].uid))!), pokedetail);
|
||||||
param: ["QQ号"],
|
|
||||||
txt: '林雨辰',
|
|
||||||
type: 'url'
|
|
||||||
},
|
|
||||||
{ txt: '获得群主授予的', type: 'nor' },
|
|
||||||
{
|
|
||||||
col: '3',
|
|
||||||
jp: '',
|
|
||||||
txt: '好好好',
|
|
||||||
type: 'url'
|
|
||||||
},
|
|
||||||
{ txt: '头衔', type: 'nor' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
* */
|
if (grayTipElement.jsonGrayTipElement.busiId == 2401) {
|
||||||
const memberUin = json.items[1].param[0];
|
const searchParams = new URL(json.items[0].jp).searchParams;
|
||||||
const title = json.items[3].txt;
|
const msgSeq = searchParams.get('msgSeq')!;
|
||||||
logDebug('收到群成员新头衔消息', json);
|
const Group = searchParams.get('groupCode');
|
||||||
return new OB11GroupTitleEvent(parseInt(msg.peerUid), parseInt(memberUin), title);
|
const Businessid = searchParams.get('businessid');
|
||||||
|
const Peer: Peer = {
|
||||||
|
guildId: '',
|
||||||
|
chatType: ChatType.group,
|
||||||
|
peerUid: Group!
|
||||||
|
};
|
||||||
|
const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true);
|
||||||
|
return new OB11GroupEssenceEvent(parseInt(msg.peerUid), MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!, parseInt(msgData.msgList[0].senderUin));
|
||||||
|
// 获取MsgSeq+Peer可获取具体消息
|
||||||
|
}
|
||||||
|
if (grayTipElement.jsonGrayTipElement.busiId == 2407) {
|
||||||
|
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
|
||||||
|
const memberUin = json.items[1].param[0];
|
||||||
|
const title = json.items[3].txt;
|
||||||
|
logDebug('收到群成员新头衔消息', json);
|
||||||
|
return new OB11GroupTitleEvent(parseInt(msg.peerUid), parseInt(memberUin), title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -67,6 +67,14 @@ export function encodeCQCode(data: OB11MessageData) {
|
|||||||
let result = '[CQ:' + data.type;
|
let result = '[CQ:' + data.type;
|
||||||
for (const name in data.data) {
|
for (const name in data.data) {
|
||||||
const value = data.data[name];
|
const value = data.data[name];
|
||||||
|
try {
|
||||||
|
// Check if the value can be converted to a string
|
||||||
|
value.toString();
|
||||||
|
} catch (error) {
|
||||||
|
// If it can't be converted, skip this name-value pair
|
||||||
|
// console.warn(`Skipping problematic name-value pair. Name: ${name}, Value: ${value}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
result += `,${name}=${CQCodeEscape(value)}`;
|
result += `,${name}=${CQCodeEscape(value)}`;
|
||||||
}
|
}
|
||||||
result += ']';
|
result += ']';
|
||||||
|
14
src/onebot11/event/notice/OB11GroupEssenceEvent.ts
Normal file
14
src/onebot11/event/notice/OB11GroupEssenceEvent.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
||||||
|
export class OB11GroupEssenceEvent extends OB11GroupNoticeEvent {
|
||||||
|
notice_type = 'essence';
|
||||||
|
message_id: number;
|
||||||
|
sender_id: number;
|
||||||
|
sub_type: 'add' | 'delete' = 'add';
|
||||||
|
|
||||||
|
constructor(groupId: number, message_id: number, sender_id: number) {
|
||||||
|
super();
|
||||||
|
this.group_id = groupId;
|
||||||
|
this.message_id = message_id;
|
||||||
|
this.sender_id = sender_id;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,4 @@
|
|||||||
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
import { OB11GroupNoticeEvent } from './OB11GroupNoticeEvent';
|
||||||
import { dbUtil } from '@/common/utils/db';
|
|
||||||
import { ob11Config } from '@/onebot11/config';
|
|
||||||
|
|
||||||
type GroupIncreaseSubType = 'approve' | 'invite';
|
type GroupIncreaseSubType = 'approve' | 'invite';
|
||||||
export class OB11GroupIncreaseEvent extends OB11GroupNoticeEvent {
|
export class OB11GroupIncreaseEvent extends OB11GroupNoticeEvent {
|
||||||
@@ -13,8 +11,5 @@ export class OB11GroupIncreaseEvent extends OB11GroupNoticeEvent {
|
|||||||
this.operator_id = operatorId;
|
this.operator_id = operatorId;
|
||||||
this.user_id = userId;
|
this.user_id = userId;
|
||||||
this.sub_type = subType;
|
this.sub_type = subType;
|
||||||
|
|
||||||
if(ob11Config.GroupLocalTime.Record && (ob11Config.GroupLocalTime.RecordList[0] == '-1' || ob11Config.GroupLocalTime.RecordList.includes(groupId.toString())))
|
|
||||||
dbUtil.insertJoinTime(groupId, userId, Math.floor(Date.now() / 1000));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
||||||
|
|
||||||
export abstract class OB11GroupNoticeEvent extends OB11BaseNoticeEvent {
|
export abstract class OB11GroupNoticeEvent extends OB11BaseNoticeEvent {
|
||||||
group_id: number;
|
group_id: number = 0;
|
||||||
user_id: number;
|
user_id: number = 0;
|
||||||
}
|
}
|
@@ -2,29 +2,30 @@ import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
|||||||
import { selfInfo } from '@/core/data';
|
import { selfInfo } from '@/core/data';
|
||||||
import { OB11BaseEvent } from '../OB11BaseEvent';
|
import { OB11BaseEvent } from '../OB11BaseEvent';
|
||||||
|
|
||||||
class OB11PokeEvent extends OB11BaseNoticeEvent{
|
class OB11PokeEvent extends OB11BaseNoticeEvent {
|
||||||
notice_type = 'notify';
|
notice_type = 'notify';
|
||||||
sub_type = 'poke';
|
sub_type = 'poke';
|
||||||
target_id = parseInt(selfInfo.uin);
|
target_id = 0;
|
||||||
user_id: number;
|
user_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OB11FriendPokeEvent extends OB11PokeEvent{
|
export class OB11FriendPokeEvent extends OB11PokeEvent {
|
||||||
sender_id: number;
|
constructor(user_id: number, target_id: number) {
|
||||||
constructor(user_id: number) {
|
|
||||||
super();
|
super();
|
||||||
|
this.target_id = target_id;
|
||||||
this.user_id = user_id;
|
this.user_id = user_id;
|
||||||
this.sender_id = user_id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OB11GroupPokeEvent extends OB11PokeEvent{
|
export class OB11GroupPokeEvent extends OB11PokeEvent {
|
||||||
group_id: number;
|
group_id: number;
|
||||||
|
raw_message: any;
|
||||||
|
|
||||||
constructor(group_id: number, user_id: number=0) {
|
constructor(group_id: number, user_id: number = 0, target_id: number = 0, raw_message: any) {
|
||||||
super();
|
super();
|
||||||
this.group_id = group_id;
|
this.group_id = group_id;
|
||||||
this.target_id = user_id;
|
this.target_id = target_id;
|
||||||
this.user_id = user_id;
|
this.user_id = user_id;
|
||||||
|
this.raw_message = raw_message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -63,6 +63,9 @@ export async function logMessage(ob11Message: OB11Message) {
|
|||||||
else if (segment.type === 'markdown') {
|
else if (segment.type === 'markdown') {
|
||||||
msgParts.push(spSegColor(`[markdown|${segment.data.content}]`));
|
msgParts.push(spSegColor(`[markdown|${segment.data.content}]`));
|
||||||
}
|
}
|
||||||
|
else if (segment.type === 'video') {
|
||||||
|
msgParts.push(spSegColor(`[视频|${segment.data.url}]`));
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
msgParts.push(spSegColor(`[未实现|${JSON.stringify(segment)}]`));
|
msgParts.push(spSegColor(`[未实现|${JSON.stringify(segment)}]`));
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user