Compare commits

...

201 Commits

Author SHA1 Message Date
手瓜一十雪
99ca79ac7d chore: 去掉无用注释 2024-08-24 12:07:40 +08:00
手瓜一十雪
24564f4c74 release: 2.2.5 2024-08-24 12:05:07 +08:00
手瓜一十雪
212c802a1e fix: BuddyReq 2024-08-24 11:52:50 +08:00
手瓜一十雪
984b5d6c40 fix: 好友申请重复推送 2024-08-24 11:26:05 +08:00
手瓜一十雪
0e3a4191a9 Merge pull request #298 from shengwang52005/readme
docs: 增强胡言乱语水平
2024-08-24 01:03:21 +08:00
Miaowing
570a34bca5 docs: 增强胡言乱语水平
增强胡言乱语水平
2024-08-24 00:16:16 +08:00
手瓜一十雪
c9a0c29286 build: 2.2.0 2024-08-23 20:46:45 +08:00
手瓜一十雪
b5f804ec22 build: CQ码回滚 提高兼容 2024-08-23 20:46:23 +08:00
手瓜一十雪
dadbb83271 docs: 提高胡言乱语水平 2024-08-23 12:54:04 +08:00
手瓜一十雪
848aacdbbf releas: 2.2.4 2024-08-23 11:34:26 +08:00
手瓜一十雪
da3665a167 release: 2.2.0
release: 2.2.1

release: 2.2.2

chore

chore: 扩大范围

release: 2.2.3
2024-08-23 11:33:22 +08:00
手瓜一十雪
5e65ae76ad chore: 移除无用代码 2024-08-22 15:58:23 +08:00
手瓜一十雪
b6c364cd78 fix: 误操作 2024-08-22 15:56:01 +08:00
手瓜一十雪
e086b8707f refactor: chattype 2024-08-22 15:53:27 +08:00
手瓜一十雪
90dddd10a9 chore: 移除类型 2024-08-22 15:42:45 +08:00
手瓜一十雪
2dd0907565 fix: type 2024-08-22 15:42:07 +08:00
手瓜一十雪
f31b0d0c71 chore: 进一步拉高版本 2024-08-22 15:40:10 +08:00
手瓜一十雪
a0825b75f7 chore: parseMsg 重构 2024-08-22 15:28:54 +08:00
手瓜一十雪
a3bd4c0f73 chore: 推动重构 2024-08-22 15:06:49 +08:00
手瓜一十雪
a3e8c9b28a chore: 重构 2024-08-22 14:58:05 +08:00
手瓜一十雪
d7fb850b4a chore: 丢弃空消息 2024-08-22 14:35:48 +08:00
手瓜一十雪
d084778a6e chore: re 2024-08-22 14:34:09 +08:00
手瓜一十雪
8ca30de760 chore: 兼容性提高 2024-08-22 14:26:49 +08:00
手瓜一十雪
8a10b81bd9 chore: fix 2024-08-22 14:15:29 +08:00
手瓜一十雪
4a93c4e584 chore: 结构性调整 2024-08-22 14:13:52 +08:00
手瓜一十雪
50177cd6bd chore: style&&lint 2024-08-22 14:12:24 +08:00
手瓜一十雪
71a2e52739 chore: 解耦 2024-08-22 14:11:20 +08:00
手瓜一十雪
4fac6d5aa3 chore: 解耦 2024-08-22 14:05:01 +08:00
手瓜一十雪
37d061b602 chore: 进一步解耦 2024-08-22 13:53:07 +08:00
手瓜一十雪
40193e4edc chore: 解耦 2024-08-22 13:46:47 +08:00
手瓜一十雪
b4e9d61871 chore: 解耦代码 2024-08-22 13:39:53 +08:00
Wesley F. Young
d44b589e55 docs: update version range 2024-08-21 09:44:11 +08:00
手瓜一十雪
68216415b6 release: 2.1.0 2024-08-21 08:16:59 +08:00
手瓜一十雪
ba53da18d1 release: 2.1.0 2024-08-21 08:13:53 +08:00
手瓜一十雪
9b76fa3582 chore: LLNC Deprecated 2024-08-21 08:12:52 +08:00
手瓜一十雪
13d8d10a7f Merge pull request #289 from NapNeko/extend
Support: 9.9.15-27254
2024-08-21 08:09:37 +08:00
手瓜一十雪
5c6c1bb09d Merge branch 'main' into extend 2024-08-20 20:36:53 +08:00
手瓜一十雪
12105d96ea release: v2.0.37 2024-08-20 20:33:06 +08:00
手瓜一十雪
4054756035 Merge branch 'main' into extend 2024-08-20 20:27:22 +08:00
手瓜一十雪
16769c7838 fix: 提高兼容性 2024-08-20 20:26:49 +08:00
手瓜一十雪
cd076c5959 fix 2024-08-20 20:22:44 +08:00
手瓜一十雪
f52e1aa131 Merge branch 'main' into extend 2024-08-20 20:07:02 +08:00
手瓜一十雪
fdc1ef7e9a fix: error 2024-08-20 20:03:41 +08:00
手瓜一十雪
9cccf2d47b Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-20 20:03:20 +08:00
手瓜一十雪
0796f27f2a fix: file ext and blank data 2024-08-20 20:03:02 +08:00
手瓜一十雪
6c84014e0d doc: 2024-08-20 19:36:13 +08:00
手瓜一十雪
cd496a22bf chore: 调整appid 2024-08-20 17:47:14 +08:00
手瓜一十雪
0200343780 chore: 调整appid 2024-08-20 17:12:07 +08:00
手瓜一十雪
47fb629d26 Merge branch 'main' into extend 2024-08-20 16:59:17 +08:00
Alen
71ae08706b release: v2.0.35 2024-08-20 16:49:16 +08:00
Alen
50dd798757 Merge pull request #285 from cnxysoft/upmain
refactor: 接口兼容
2024-08-20 16:39:51 +08:00
Alen
0c8cf73746 refactor: 接口兼容
调整SetGroupHeader接口为SetGroupPortrait,使其兼容gocq标准
2024-08-20 16:35:23 +08:00
Alen
1bee811312 refactor: 接口兼容
调整SetSelfProfile接口为SetQQProfile,使其兼容gocq标准
2024-08-20 16:05:23 +08:00
Alen
b4c0068637 Merge pull request #284 from cnxysoft/upmain
fix: 多处修复
2024-08-20 14:31:02 +08:00
Alen
f484c6e5fe fix: 多处修复
1.修复group_card事件上报
2.修复group_admin事件上报
2024-08-20 14:28:32 +08:00
Alen
7a08187c5f fix: Uid转Uin
修复Uid转Uin兜底逻辑
2024-08-20 07:28:25 +08:00
Alen
c4d7d5a0d4 fix: 多处修改
1.修改(疑似)旧设备回复消息验证失败的解决方案
2.修复Base64发送文件失败
3.修复群时间监听器未注册
2024-08-20 01:02:03 +08:00
手瓜一十雪
5b75e753a7 Util 2024-08-19 21:38:31 +08:00
手瓜一十雪
326e9b86ce Merge branch 'main' into extend 2024-08-19 20:44:49 +08:00
手瓜一十雪
d22f5d369c releas: v2.0.34 2024-08-19 20:27:04 +08:00
手瓜一十雪
d76503995c fix: ws心跳实现 2024-08-19 20:24:26 +08:00
手瓜一十雪
eab930c083 doc: 小tip 2024-08-19 19:33:58 +08:00
手瓜一十雪
e430cc54f2 Merge branch 'main' into extend 2024-08-19 19:03:31 +08:00
手瓜一十雪
fd26a9c698 fix 2024-08-19 18:53:47 +08:00
手瓜一十雪
e79b608f77 support: 27206 2024-08-19 18:47:12 +08:00
Wesley F. Young
42b23a6c9c docs: clarify version range 2024-08-19 16:17:33 +08:00
Alen
8d94f24c71 release: v2.0.33 2024-08-19 15:43:41 +08:00
Alen
6ac74c39d9 Merge pull request #279 from cnxysoft/upmain
fix: 多处修复
2024-08-19 15:41:10 +08:00
Alen
836eb7b708 fix: 多处修复
1.修复部分Uin转换失败导致的相关API错误
2.继续改进get_group_member_info效率
3.增加fetchUserDetailInfo容错(暂时性/待观察)
2024-08-19 15:39:34 +08:00
Alen
698624b4dc release: v2.0.32 2024-08-19 10:09:53 +08:00
Alen
5c1df82076 Merge pull request #278 from cnxysoft/upmain
fix: 多处修改
2024-08-19 10:07:16 +08:00
Alen
5d649b3687 perf: 优化API效率
优化get_group_member_info效率
2024-08-19 00:55:32 +08:00
Alen
a6a3d71155 Revert "perf: API优化"
This reverts commit 1cc9d501ab.
2024-08-18 18:49:46 +08:00
Alen
1cc9d501ab perf: API优化
优化get_group_member_info在查询非群员时的效率
2024-08-18 14:33:35 +08:00
Alen
7a98025df8 fix: 引用消息失败
修复 (疑似)旧设备引用消息验证失败
2024-08-18 01:40:14 +08:00
Alen
44d6ed5e80 release: v2.0.31 2024-08-17 23:15:52 +08:00
手瓜一十雪
b5f2226bef Merge pull request #270 from cnxysoft/upmain
fix: Uid转Uin
2024-08-17 22:53:42 +08:00
Alen
ddbffe55d2 Merge branch 'main' into upmain 2024-08-17 22:19:49 +08:00
手瓜一十雪
9676b1d0e9 fix 2024-08-17 18:13:43 +08:00
手瓜一十雪
8142d3bfeb fix: 打错啦 2024-08-17 18:12:43 +08:00
手瓜一十雪
755ad27a0a Merge pull request #269 from gfhdhytghd/patch-1
修改许可证以禁止宣传
2024-08-17 15:45:20 +08:00
手瓜一十雪
5afa2dcdf1 chore: 复活赛打赢啦 2024-08-17 15:37:19 +08:00
手瓜一十雪
03098ee024 chore: util 2024-08-17 15:21:47 +08:00
手瓜一十雪
a2bfdd003c fix: getNTUserDataInfoConfig 2024-08-17 15:18:33 +08:00
lin
7eb80646ba Update LICENSE
修改License,从法律层面禁止在公共社交媒体宣传
2024-08-17 14:11:57 +08:00
Alen
6fd24e57d3 Merge branch 'main' into upmain 2024-08-17 13:24:36 +08:00
手瓜一十雪
22c90adb47 chore: docs 2024-08-17 12:27:53 +08:00
Alen
df0c6fafbe fix: Uid转Uin
修复设置管理、禁言等数个API失效
对查询企业陌生人信息进行容错
2024-08-17 00:17:51 +08:00
手瓜一十雪
dc30321b04 Update README.md 2024-08-17 00:16:23 +08:00
手瓜一十雪
63dd98d2df release: 2.0.30 2024-08-16 21:44:34 +08:00
手瓜一十雪
caaa6ed506 feat: support SetInputStatus 2024-08-16 20:35:05 +08:00
手瓜一十雪
caf23792cb Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-16 13:01:38 +08:00
手瓜一十雪
e430db20aa release: 2.0.29 2024-08-16 13:01:30 +08:00
手瓜一十雪
6fc5da9b67 Merge pull request #267 from Fripine/fix/OB11GroupRequestEvent
fix: wrong user_id in GroupRequestEvent
2024-08-16 13:00:32 +08:00
Fripine
f428e57724 fix: GroupRequestEvent 2024-08-16 12:57:42 +08:00
手瓜一十雪
14ab21fe9a Merge pull request #266 from Fripine/fix/OB11FriendRequestEvent
fix: wrong comment words in FriendRequestEvent
2024-08-16 12:44:33 +08:00
手瓜一十雪
85626e19da release: 2.0.28 2024-08-16 12:44:11 +08:00
Fripine
8712160fd7 fix: FriendRequestEvent 2024-08-16 12:21:33 +08:00
手瓜一十雪
75b33f5cb1 chore: fix 2024-08-16 12:16:21 +08:00
手瓜一十雪
f5e8ede847 release: 2.0.27 2024-08-16 10:49:22 +08:00
手瓜一十雪
3b3f684a8c chore: 清除废弃代码 2024-08-16 09:52:50 +08:00
手瓜一十雪
a78b60d40e chore: 进一步识别会话 2024-08-16 09:37:36 +08:00
手瓜一十雪
9ff06a3c44 release: 2.0.26 2024-08-15 23:08:31 +08:00
手瓜一十雪
8532dc486c fix: error 2024-08-15 23:07:59 +08:00
手瓜一十雪
861340f4bf release: 2.0.25 2024-08-15 22:17:58 +08:00
手瓜一十雪
cdcb51ebe4 build: v2.0.24 2024-08-15 20:11:45 +08:00
手瓜一十雪
0b11786d7d fix 2024-08-15 20:10:35 +08:00
手瓜一十雪
1742247a9a build: fix 2024-08-15 19:40:33 +08:00
手瓜一十雪
42bad123b2 chore: 优化一处逻辑 2024-08-15 19:37:06 +08:00
手瓜一十雪
2d1e87defc chore: 代码质量提高 2024-08-15 19:34:05 +08:00
手瓜一十雪
1c6f783a07 chore: 移除错误推断 2024-08-15 19:27:28 +08:00
手瓜一十雪
6aafc097d5 chore: 废弃无用代码 2024-08-15 19:26:32 +08:00
手瓜一十雪
4010f233dd chore: 移除废弃函数 2024-08-15 19:04:14 +08:00
手瓜一十雪
75f67caa1b release: v2.0.23 2024-08-15 18:46:26 +08:00
手瓜一十雪
d760ce54b7 chore: 重构文件处理 2024-08-15 18:44:29 +08:00
手瓜一十雪
956976ebd5 fix 2024-08-15 17:10:18 +08:00
手瓜一十雪
f9c2d4ca6c fix 2024-08-15 17:07:26 +08:00
手瓜一十雪
dd5cc3c38c fix 2024-08-15 17:04:34 +08:00
手瓜一十雪
daed4cc13e fix 2024-08-15 17:03:55 +08:00
手瓜一十雪
6ff614dd18 fix 2024-08-15 16:56:03 +08:00
手瓜一十雪
eb70ac4266 release 2024-08-15 16:51:31 +08:00
手瓜一十雪
a3a431adb7 fix 2024-08-15 16:45:24 +08:00
手瓜一十雪
e12c72ab98 action fix 2024-08-15 16:41:47 +08:00
手瓜一十雪
9f8549b831 build: 2.0.22 2024-08-15 16:34:50 +08:00
手瓜一十雪
b2de256f87 release: 2.0.22 2024-08-15 16:15:33 +08:00
手瓜一十雪
7f32a5cf9e build: test 2024-08-15 12:28:51 +08:00
手瓜一十雪
56f8314d29 chore: 进一步清理无用代码 2024-08-15 11:10:02 +08:00
手瓜一十雪
4ceb2a8669 release: 2.0.21 2024-08-15 09:35:38 +08:00
手瓜一十雪
c778d3b699 chore: 提高兼容性 2024-08-15 00:33:20 +08:00
手瓜一十雪
47eda9cdf2 chore: boolen值校验 2024-08-15 00:14:31 +08:00
手瓜一十雪
dcaec4d356 style: lint 2024-08-15 00:10:13 +08:00
手瓜一十雪
aee4f349c6 chore: 丢弃废弃代码 2024-08-15 00:06:41 +08:00
手瓜一十雪
daa2c39902 chore: 兼容性提高 2024-08-15 00:00:21 +08:00
手瓜一十雪
5770fc02a1 chore: extend get兼容 2024-08-14 23:56:45 +08:00
手瓜一十雪
47cafd295b chore: 清理无用代码 2024-08-14 23:46:17 +08:00
手瓜一十雪
3296f2daf8 chore: 弃用无用代码 2024-08-14 23:41:34 +08:00
手瓜一十雪
962616545c fix: 显示自身消息 2024-08-14 23:09:07 +08:00
手瓜一十雪
11ea92c078 chore: logger 2024-08-14 23:03:24 +08:00
Seijo Cecilia
1d64fa4817 Merge remote-tracking branch 'origin/main' 2024-08-14 22:04:38 +08:00
Seijo Cecilia
c46f2956c2 fix: plugin slug 2024-08-14 22:04:29 +08:00
Wesley F. Young
8f6d4298be Merge pull request #256 from canxin121/main
typo: OB11InputStatusEvent event_type
2024-08-14 21:44:21 +08:00
canxin121
3bce81326e typo: OB11InputStatusEvent event_type 2024-08-14 21:42:12 +08:00
Seijo Cecilia
2ae9f6d0fe docs: name, slug and description of plugin manifest 2024-08-14 20:11:06 +08:00
手瓜一十雪
9266828278 chore: logger 2024-08-14 19:42:22 +08:00
手瓜一十雪
a8a2ffc33e release: 2.0.20 2024-08-14 19:38:50 +08:00
手瓜一十雪
27c4543471 Merge pull request #254 from lgc2333/patch-1
fix at error
2024-08-14 19:13:48 +08:00
student_2333
50a02cb59e fix at error 2024-08-14 19:08:42 +08:00
手瓜一十雪
50579bb9e6 fix: 修复某个笨蛋写的 2024-08-14 10:46:59 +08:00
手瓜一十雪
50512ca63c Merge pull request #250 from cnxysoft/upmain
fix: 二维码登录
2024-08-14 10:42:46 +08:00
手瓜一十雪
8b3577b216 Merge pull request #249 from DiheChen/patch-1
🐛 generating QR code when quick login fails
2024-08-14 10:42:33 +08:00
Alen
7553aab932 fix: 二维码登录
修复在没找到登陆历史记录后不加载二维码的问题
2024-08-14 10:32:39 +08:00
DiheChen
5dacdcfe5e 🐛 generating QR code when quick login fails 2024-08-14 10:31:28 +08:00
手瓜一十雪
8645a412b7 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-14 09:35:14 +08:00
手瓜一十雪
acad07a588 release: 2.0.18 2024-08-14 09:33:52 +08:00
手瓜一十雪
51bbb480bb Merge pull request #248 from cnxysoft/upmain
fix: GetCookie容错
2024-08-14 01:41:38 +08:00
Alen
f0306cd10a fix: GetCookie容错
修改 API提交不可用域名或不含skey返回时的容错
2024-08-14 01:32:32 +08:00
手瓜一十雪
25253ad9e7 fix: 异常 2024-08-14 01:22:08 +08:00
手瓜一十雪
51f2fb8e8b release: 2.0.17 2024-08-14 00:57:09 +08:00
手瓜一十雪
9e8d650cbd chore: 提高对trss兼容性 2024-08-14 00:56:42 +08:00
手瓜一十雪
d222ccfa58 fix: GetGuildProfile 2024-08-14 00:54:14 +08:00
手瓜一十雪
9a05aaa906 chore: 兼容guild api 2024-08-14 00:53:52 +08:00
手瓜一十雪
00fdce8876 style&&chore: lint 2024-08-14 00:47:50 +08:00
手瓜一十雪
29b51adf7d style&&chore: 逐步清退老版本代码 2024-08-14 00:47:31 +08:00
手瓜一十雪
8e14b39969 release: 2.0.16 2024-08-14 00:38:04 +08:00
手瓜一十雪
d9fb4d6c4d release: 2.0.15 2024-08-13 23:49:05 +08:00
手瓜一十雪
fcf2f4c5f2 Merge pull request #246 from cnxysoft/upmain
BUG修复
2024-08-13 23:48:19 +08:00
Alen
4e97501690 BUG修复
1.修改快速登陆提示使其更易辨别失败原因
2.修复API: Get_Cookie执行错误的问题
2024-08-13 23:46:21 +08:00
手瓜一十雪
b0402391fb chore: link 2024-08-13 23:11:34 +08:00
手瓜一十雪
f05a862cf9 fix 2024-08-13 22:18:24 +08:00
手瓜一十雪
3ea92d57c2 chore: 排除空消息 2024-08-13 22:11:28 +08:00
手瓜一十雪
254b85fbd8 release: v2.0.14 2024-08-13 22:02:56 +08:00
手瓜一十雪
16371c0cc4 fix: 统一消息格式 2024-08-13 21:00:22 +08:00
手瓜一十雪
2256d67e2b release: 2.0.12 2024-08-13 19:34:33 +08:00
手瓜一十雪
0af0bdede6 build: 2.0.12 test 2024-08-13 19:33:29 +08:00
手瓜一十雪
379f31b9de Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-13 16:22:24 +08:00
手瓜一十雪
771a524734 add log 2024-08-13 16:21:59 +08:00
Wesley F. Young
560e18a610 fix: no longer require admin when running after initialization 2024-08-13 16:09:15 +08:00
手瓜一十雪
147bdfab95 Revert "fix: 框架启动概率性失败"
This reverts commit 9d92270931.
2024-08-13 15:14:42 +08:00
手瓜一十雪
36b1b0f663 release: 2.0.11 2024-08-13 14:53:18 +08:00
手瓜一十雪
91511e4c3f fix: 某个笨蛋弄错的地方 2024-08-13 14:52:51 +08:00
手瓜一十雪
6a72056b25 release: 2.0.10 2024-08-13 13:58:53 +08:00
手瓜一十雪
62e0c57a50 Merge pull request #243 from cnxysoft/upmain
fix: 框架启动概率性失败
2024-08-13 13:50:08 +08:00
Alen
9d92270931 fix: 框架启动概率性失败
修复框架启动有概率失败的问题
2024-08-13 13:37:19 +08:00
手瓜一十雪
f61321d5a6 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-13 13:09:16 +08:00
手瓜一十雪
08ab2f8649 chore: 切换为缓存实现 2024-08-13 13:09:14 +08:00
Wesley F. Young
82962c4b42 docs: copyright 2024-08-13 09:06:46 +08:00
Wesley F. Young
bd24e8a4ad docs: plugin description 2024-08-13 09:02:50 +08:00
手瓜一十雪
6224d9a292 fix: 不合格类型 2024-08-13 00:59:26 +08:00
手瓜一十雪
bbc58f3671 release v2.0.9 2024-08-13 00:53:21 +08:00
手瓜一十雪
fcd620283f fix: 处理边界条件 2024-08-13 00:48:51 +08:00
手瓜一十雪
a78def3d2d chore: 移除异常内容 2024-08-13 00:39:20 +08:00
手瓜一十雪
43e94a5db0 fix: 跳过空消息 2024-08-13 00:38:42 +08:00
手瓜一十雪
e77bcc1267 release: 2.0.8 2024-08-12 23:50:03 +08:00
手瓜一十雪
9b458958b8 Revert "try fix"
This reverts commit 35419ade29.
2024-08-12 23:03:58 +08:00
手瓜一十雪
35419ade29 try fix 2024-08-12 23:01:36 +08:00
手瓜一十雪
15bd2ee887 fix: 2.0.7 2024-08-12 20:51:17 +08:00
手瓜一十雪
9394bafa8e fix 2024-08-12 20:46:28 +08:00
手瓜一十雪
94150a0c48 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-12 20:42:10 +08:00
手瓜一十雪
8955fdfc23 release: 2.0.7 2024-08-12 20:41:57 +08:00
手瓜一十雪
c13aa6a545 build: 快速登录修复 2024-08-12 20:34:34 +08:00
手瓜一十雪
c73b50bd4a fix 2024-08-12 20:28:25 +08:00
102 changed files with 2055 additions and 1937 deletions

View File

@@ -95,14 +95,25 @@ jobs:
steps:
- name: Download All Artifact
uses: actions/download-artifact@v4
# - name: Compress subdirectories
# run: |
# cd ./NapCat.Shell/
# zip -q -r NapCat.Shell.zip *
# cd ..
# rm ./NapCat.Shell.zip -rf
# mv ./NapCat.Shell/NapCat.Shell.zip ./
- name: Compress subdirectories
run: |
for dir in */; do
base=$(basename "$dir")
zip -r "${base}.zip" "$dir"
done
cd ./NapCat.Shell/
zip -q -r NapCat.Shell.zip *
cd ..
cd ./NapCat.Framework/
zip -q -r NapCat.Framework.zip *
cd ..
rm ./NapCat.Shell.zip -rf
rm ./NapCat.Framework.zip -rf
mv ./NapCat.Shell/NapCat.Shell.zip ./
mv ./NapCat.Framework/NapCat.Framework.zip ./
- name: Extract version from tag
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
@@ -118,6 +129,4 @@ jobs:
files: |
NapCat.Framework.zip
NapCat.Shell.zip
# NapCat.darwin.x64.zip
# NapCat.darwin.arm64.zip
draft: true

View File

@@ -1,4 +1,4 @@
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC Without Social media promotion LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
@@ -111,6 +111,10 @@ above, provided that you also meet all of these conditions:
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
dYou may use this software in accordance with the above terms,
but you are not allowed to promote this project or your projects
based on this project on any public social media.
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in

View File

@@ -3,32 +3,33 @@
</div>
---
## 欢迎回来
NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现。
## 项目介绍
NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现。
## 项目优势
## 猫猫技能
- [x] **多种启动方式**支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动
- [x] **低占用**:无头模式占用资源极低,适合在服务器上运行
- [x] **超多接口**在实现大部分Onebot接口上扩展了一套私有API
- [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷
## 如何使用
## 使用猫猫
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
**首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程。
## 相关链接
## 回家旅途
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
## 附加协议
禁止未授权任何项目使用Core部分代码用于二次开发与分发
## 鸣谢名单
## 猫猫朋友
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供初始版本基础
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持
---
**任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**
## 约法三章
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 NapCat 代码开发。**

View File

@@ -1,10 +1,10 @@
{
"manifest_version": 4,
"type": "extension",
"name": "NapCat",
"slug": "NapCat",
"description": "OneBot v11 protocol implementation with NapCat logic",
"version": "2.0.6",
"name": "NapCatQQ",
"slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现",
"version": "2.2.5",
"icon": "./logo.png",
"authors": [
{
@@ -30,4 +30,4 @@
"main": "./liteloader.cjs",
"preload": "./preload.cjs"
}
}
}

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "2.0.6",
"version": "2.2.5",
"scripts": {
"build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell",

77
script/BootWay05_init.bat Normal file
View File

@@ -0,0 +1,77 @@
@echo off
REM Check if the script is running as administrator
openfiles >nul 2>&1
if %errorlevel% neq 0 (
REM If not, restart the script in administrator mode
echo Requesting administrator privileges...
powershell -Command "Start-Process cmd -ArgumentList '/c %~f0 %*' -Verb RunAs"
exit /b
)
cd /d %~dp0
set currentPath=%cd%
set currentPath=%currentPath:\=/%
REM Generate JavaScript code
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
REM Save JavaScript code to a file
echo %jsCode% > loadScript.js
echo JavaScript code has been generated and saved to loadScript.js
REM Set NAPCAT_PATH environment variable to the address of loadScript.js in the current directory
set NAPCAT_PATH=%cd%\loadScript.js
REM Get QQ path and cache it
: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"
)
set "pathWithoutUninstall=%RetString:Uninstall.exe=%"
SET QQPath=%pathWithoutUninstall%QQ.exe
echo %QQPath%>qq_path_cache.txt
echo QQ path %QQPath% has been cached to qq_path_cache.txt
REM Exit if QQ path is invalid
if not exist "%QQpath%" (
echo provided QQ path is invalid: %QQpath%
pause
exit /b
)
REM Collect dbghelp.dll path and HASH information
set QQdir=%~dp0
set oldDllPath=%QQdir%dbghelp.dll
set newDllPath=%currentPath%\dbghelp.dll
for /f "tokens=*" %%A in ('certutil -hashfile "%oldDllPath%" MD5') do (
if not defined oldDllHash set oldDllHash=%%A
)
for /f "tokens=*" %%A in ('certutil -hashfile "%newDllPath%" MD5') do (
if not defined newDllHash set newDllHash=%%A
)
REM Compare the HASH of the old and new dbghelp.dll, and replace the old one if they are different
if "%oldDllHash%" neq "%newDllHash%" (
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
if %errorlevel% equ 0 (
REM If the file is in use, prompt the user to close QQ
echo dbghelp.dll is in use, please close QQ first.
) else (
copy /y "%newDllPath%" "%oldDllPath%"
if %errorlevel% neq 0 (
echo Copy dbghelp.dll failed, please check and try again.
pause
exit /b
) else (
echo dbghelp.dll has been updated.
echo Please run BootWay05_run.bat to start QQ.
echo If you update QQ in the future, please run BootWay05_init.bat again.
pause
exit /b
)
)
)

10
script/BootWay05_run.bat Normal file
View File

@@ -0,0 +1,10 @@
@echo off
set /p QQPath=<qq_path_cache.txt
echo QQ path %QQPath% has been read from qq_path_cache.txt
echo If failed to start QQ, please try running this script in administrator mode.
set NAPCAT_PATH=%cd%\loadScript.js
REM Launch QQ.exe with params provided
"%QQPath%" --enable-logging %*

View File

@@ -0,0 +1,13 @@
@echo off
chcp 65001
set /p QQPath=<qq_path_cache.txt
echo QQ path %QQPath% has been read from qq_path_cache.txt
echo If failed to start QQ, please try running this script in administrator mode.
set NAPCAT_PATH=%cd%\loadScript.js
REM Launch QQ.exe with params provided
"%QQPath%" --enable-logging %*

View File

@@ -16,16 +16,13 @@ export interface ListenerIBase {
}
export class LegacyNTEventWrapper {
private listenerMapping: Record<string, ListenerIBase>; //ListenerName-Unique -> Listener构造函数
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
private listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例
private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
constructor(
listenerMapping: Record<string, ListenerIBase>,
wrapperSession: NodeIQQNTWrapperSession,
wrapperSession: NodeIQQNTWrapperSession
) {
this.listenerMapping = listenerMapping;
this.WrapperSession = wrapperSession;
}
@@ -72,18 +69,17 @@ export class LegacyNTEventWrapper {
}
createListenerFunction<T>(listenerMainName: string, uniqueCode: string = ''): T {
const ListenerType = this.listenerMapping![listenerMainName];
let Listener = this.listenerManager.get(listenerMainName + uniqueCode);
if (!Listener && ListenerType) {
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
const existListener = this.listenerManager.get(listenerMainName + uniqueCode);
if (!existListener) {
const Listener = this.createProxyDispatch(listenerMainName);
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
addfunc!(Listener as T);
//console.log(addfunc!(Listener as T));
this.listenerManager.set(listenerMainName + uniqueCode, Listener);
return Listener as T;
}
return Listener as T;
return existListener as T;
}
//统一回调清理事件
@@ -166,7 +162,89 @@ export class LegacyNTEventWrapper {
this.createListenerFunction(ListenerMainName);
});
}
async CallNormalEventV2<
EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void
>(
EventName = '',
ListenerName = '',
waitTimes = 1,
timeout: number = 3000,
checkerEvent: (ret: Awaited<ReturnType<EventType>>) => boolean = () => true,
checkerListener: (...args: Parameters<ListenerType>) => boolean = () => true,
...args: Parameters<EventType>
) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
async (resolve, reject) => {
const id = randomUUID();
let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined;
let retEvent: any = {};
const databack = () => {
if (complete == 0) {
reject(
new Error(
'Timeout: NTEvent EventName:' +
EventName +
' ListenerName:' +
ListenerName +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
),
);
} else {
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
}
};
const ListenerNameList = ListenerName.split('/');
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
const Timeouter = setTimeout(databack, timeout);
const eventCallbak = {
timeout: timeout,
createtime: Date.now(),
checker: checkerListener,
func: (...args: any[]) => {
complete++;
//console.log('func', ...args);
retData = args as Parameters<ListenerType>;
if (complete >= waitTimes) {
clearTimeout(Timeouter);
databack();
}
},
};
if (!this.EventTask.get(ListenerMainName)) {
this.EventTask.set(ListenerMainName, new Map());
}
if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) {
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
}
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
this.createListenerFunction(ListenerMainName);
const EventFunc = this.createEventFunction<EventType>(EventName);
retEvent = await EventFunc!(...(args as any[]));
if(!checkerEvent(retEvent)){
clearTimeout(Timeouter);
reject(
new Error(
'EventChecker Failed: NTEvent EventName:' +
EventName +
' ListenerName:' +
ListenerName +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
),
);
}
},
);
}
async CallNormalEvent<
EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void

View File

@@ -2,7 +2,7 @@ import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
export const napcat_version = '2.0.6';
export const napcat_version = '2.2.5';
export class NapCatPathWrapper {
binaryPath: string;

View File

@@ -74,7 +74,7 @@ export class QQBasicInfoWrapper {
this.context.logger.log(
`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`,
);
return { appid: systemPlatform === 'linux' ? '537237950' : '537237765', qua: this.getQUAInternal() };
return { appid: systemPlatform === 'linux' ? '537240795' : '537240709', qua: this.getQUAInternal() };
}
}

View File

@@ -1,10 +1,9 @@
import fs from 'fs';
import fsPromise, { stat } from 'fs/promises';
import { stat } from 'fs/promises';
import crypto, { randomUUID } from 'crypto';
import util from 'util';
import path from 'node:path';
import * as fileType from 'file-type';
import { LogWrapper } from './log';
export function isGIF(path: string) {
const buffer = Buffer.alloc(4);
@@ -13,7 +12,6 @@ export function isGIF(path: string) {
fs.closeSync(fd);
return buffer.toString() === 'GIF8';
}
// 定义一个异步函数来检查文件是否存在
export function checkFileReceived(path: string, timeout: number = 3000): Promise<void> {
return new Promise((resolve, reject) => {
@@ -94,7 +92,6 @@ export async function file2base64(path: string) {
return result;
}
export function calculateFileMD5(filePath: string): Promise<string> {
return new Promise((resolve, reject) => {
// 创建一个流式读取器
@@ -166,136 +163,105 @@ type Uri2LocalRes = {
isLocal: boolean
}
export async function uri2local(TempDir: string, UriOrPath: string, fileName: string | null = null): Promise<Uri2LocalRes> {
const res = {
success: false,
errMsg: '',
fileName: '',
ext: '',
path: '',
isLocal: false,
};
if (!fileName) fileName = randomUUID();
let filePath = path.join(TempDir, fileName);//临时目录
let url = null;
//区分path和uri
export async function checkFileV2(filePath: string) {
try {
if (fs.existsSync(UriOrPath)) url = new URL('file://' + UriOrPath);
} catch (error: any) {
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
if (ext) {
fs.renameSync(filePath, filePath + `.${ext}`);
filePath += `.${ext}`;
return { success: true, ext: ext, path: filePath };
}
} catch (e) {
// log("获取文件类型失败", filePath,e.stack)
}
return { success: false, ext: '', path: filePath };
}
export enum FileUriType {
Unknown = 0,
Local = 1,
Remote = 2,
Base64 = 3
}
export async function checkUriType(Uri: string) {
//先判断是否是本地文件
try {
if (fs.existsSync(Uri)) return { Uri: Uri, Type: FileUriType.Local };
} catch (error) {
}
try {
url = new URL(UriOrPath);
} catch (error: any) {
}
//验证url
if (!url) {
res.errMsg = `UriOrPath ${UriOrPath} 解析失败,可能${UriOrPath}不存在`;
return res;
}
if (url.protocol == 'base64:') {
// base64转成文件
const base64Data = UriOrPath.split('base64://')[1];
try {
const buffer = Buffer.from(base64Data, 'base64');
fs.writeFileSync(filePath, buffer);
} catch (e: any) {
res.errMsg = 'base64文件下载失败,' + e.toString();
return res;
//再判断是否是Http
if (Uri.startsWith('http://') || Uri.startsWith('https://')) {
return { Uri: Uri, Type: FileUriType.Remote };
}
} else if (url.protocol == 'http:' || url.protocol == 'https:') {
// 下载文件
let buffer: Buffer | null = null;
try {
buffer = await httpDownload(UriOrPath);
} catch (e: any) {
res.errMsg = `${url}下载失败,` + e.toString();
return res;
//再判断是否是Base64
if (Uri.startsWith('base64://')) {
return { Uri: Uri, Type: FileUriType.Base64 };
}
try {
const pathInfo = path.parse(decodeURIComponent(url.pathname));
if (pathInfo.name) {
fileName = pathInfo.name;
if (pathInfo.ext) {
fileName += pathInfo.ext;
// res.ext = pathInfo.ext
}
}
fileName = fileName.replace(/[/\\:*?"<>|]/g, '_');
res.fileName = fileName;
filePath = path.join(TempDir, randomUUID() + fileName);
fs.writeFileSync(filePath, buffer);
} catch (e: any) {
res.errMsg = `${url}下载失败,` + e.toString();
return res;
}
} else {
let pathname: string;
if (url.protocol === 'file:') {
if (Uri.startsWith('file://')) {
let pathname: string;
let filePath: string;
// await fs.copyFile(url.pathname, filePath);
pathname = decodeURIComponent(url.pathname);
pathname = decodeURIComponent(new URL(Uri).pathname);
if (process.platform === 'win32') {
filePath = pathname.slice(1);
} else {
filePath = pathname;
}
} else {
// 26702执行forword file文件操作 不应该在这里乱来
// const cache = await dbUtil.getFileCacheByName(uri);
// if (cache) {
// filePath = cache.path;
// } else {
// filePath = uri;
// }
}
res.isLocal = true;
}
// else{
// res.errMsg = `不支持的file协议,` + url.protocol
// return res
// }
// if (isGIF(filePath) && !res.isLocal) {
// await fs.rename(filePath, filePath + ".gif");
// filePath += ".gif";
// }
if (!res.isLocal && !res.ext) {
try {
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
if (ext) {
fs.renameSync(filePath, filePath + `.${ext}`);
filePath += `.${ext}`;
res.fileName += `.${ext}`;
res.ext = ext;
}
} catch (e) {
// log("获取文件类型失败", filePath,e.stack)
}
}
res.success = true;
res.path = filePath;
return res;
}
export async function copyFolder(sourcePath: string, destPath: string, logger: LogWrapper) {
try {
const entries = await fsPromise.readdir(sourcePath, { withFileTypes: true });
await fsPromise.mkdir(destPath, { recursive: true });
for (const entry of entries) {
const srcPath = path.join(sourcePath, entry.name);
const dstPath = path.join(destPath, entry.name);
if (entry.isDirectory()) {
await copyFolder(srcPath, dstPath, logger);
} else {
try {
await fsPromise.copyFile(srcPath, dstPath);
} catch (error) {
logger.logError(`无法复制文件 '${srcPath}' 到 '${dstPath}': ${error}`);
// 这里可以决定是否要继续复制其他文件
}
}
return { Uri: filePath, Type: FileUriType.Local };
}
} catch (error) {
logger.logError('复制文件夹时出错:', error);
}
return { Uri: Uri, Type: FileUriType.Unknown };
}
export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise<Uri2LocalRes> {
const { Uri: HandledUri, Type: UriType } = await checkUriType(uri);
//解析失败
if (UriType == FileUriType.Unknown) {
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
}
//解析File协议和本地文件
if (UriType == FileUriType.Local) {
const fileExt = path.extname(HandledUri);
const filename = path.basename(HandledUri, fileExt);
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: HandledUri, isLocal: true };
}
//接下来都要有文件名
if (!filename) filename = randomUUID();
//解析Http和Https协议
if (UriType == FileUriType.Remote) {
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
if (pathInfo.name) {
filename = pathInfo.name;
if (pathInfo.ext) {
filename += pathInfo.ext;
}
}
filename = filename.replace(/[/\\:*?"<>|]/g, '_');
const fileExt = path.extname(HandledUri);
const filePath = path.join(dir, filename);
const buffer = await httpDownload(HandledUri);
fs.writeFileSync(filePath, buffer);
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
}
//解析Base64
if (UriType == FileUriType.Base64) {
const base64 = HandledUri.replace(/^base64:\/\//, '');
const buffer = Buffer.from(base64, 'base64');
let filePath = path.join(dir, filename);
let fileExt = '';
fs.writeFileSync(filePath, buffer);
const { success, ext, path: fileTypePath } = await checkFileV2(filePath);
if (success) {
filePath = fileTypePath;
fileExt = ext;
filename = filename + '.' + ext;
}
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
}
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
}

View File

@@ -1,7 +1,5 @@
import crypto from 'node:crypto';
import path from 'node:path';
import fs from 'fs';
import * as fsPromise from 'node:fs/promises';
import os from 'node:os';
import { QQLevel } from '@/core';
@@ -53,12 +51,6 @@ export async function runAllWithTimeout<T>(tasks: Promise<T>[], timeout: number)
.map((result) => (result as { status: 'fulfilled'; value: T }).value);
}
export function getMd5(s: string) {
const h = crypto.createHash('md5');
h.update(s);
return h.digest('hex');
}
export function isNull(value: any) {
return value === undefined || value === null;
}
@@ -103,19 +95,19 @@ export function isEqual(obj1: any, obj2: any) {
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
if (os.platform() === 'linux') {
return {
baseVersion: '3.2.12-26702',
curVersion: '3.2.12-26702',
baseVersion: '3.2.12-27254',
curVersion: '3.2.12-27254',
prevVersion: '',
onErrorVersions: [],
buildId: '26702',
buildId: '27254',
};
}
return {
baseVersion: '9.9.15-26702',
curVersion: '9.9.15-26702',
baseVersion: '9.9.15-27254',
curVersion: '9.9.15-27254',
prevVersion: '',
onErrorVersions: [],
buildId: '26702',
buildId: '27254',
};
}
@@ -137,28 +129,6 @@ export function getQQVersionConfigPath(exePath: string = ''): string | undefined
return configVersionInfoPath;
}
export async function deleteOldFiles(directoryPath: string, daysThreshold: number) {
try {
const files = await fsPromise.readdir(directoryPath);
for (const file of files) {
const filePath = path.join(directoryPath, file);
const stats = await fsPromise.stat(filePath);
const lastModifiedTime = stats.mtimeMs;
const currentTime = Date.now();
const timeDifference = currentTime - lastModifiedTime;
const daysDifference = timeDifference / (1000 * 60 * 60 * 24);
if (daysDifference > daysThreshold) {
await fsPromise.unlink(filePath); // Delete the file
//console.log(`Deleted: ${filePath}`);
}
}
} catch (error) {
//console.error('Error deleting files:', error);
}
}
export function calcQQLevel(level: QQLevel) {
const { crownNum, sunNum, moonNum, starNum } = level;
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;

View File

@@ -2,7 +2,7 @@ import log4js, { Configuration } from 'log4js';
import { truncateString } from '@/common/utils/helper';
import path from 'node:path';
import chalk from 'chalk';
import { AtType, ChatType, ElementType, ElementWrapper, RawMessage, SelfInfo } from '@/core';
import { AtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/core';
export enum LogLevel {
DEBUG = 'debug',
@@ -152,11 +152,11 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
const tokens: string[] = [];
if (msg.chatType == ChatType.friend) {
if (msg.chatType == ChatType.KCHATTYPEC2C) {
tokens.push(`私聊 (${msg.peerUin})`);
} else if (msg.chatType == ChatType.group) {
} else if (msg.chatType == ChatType.KCHATTYPEGROUP) {
tokens.push(`群聊 (群 ${msg.peerUin}${msg.senderUin})`);
} else if (msg.chatType == ChatType.chatDevice) {
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
tokens.push('移动设备');
} else /* temp */ {
tokens.push(`临时消息 (${msg.peerUin})`);
@@ -164,7 +164,7 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
// message content
function msgElementToText(element: ElementWrapper) {
function msgElementToText(element: MessageElement) {
if (element.textElement) {
if (element.textElement.atType === AtType.notAt) {
return element.textElement.content;

File diff suppressed because one or more lines are too long

View File

@@ -117,31 +117,31 @@ export class NTQQFileApi {
filePath: string
}) => Promise<unknown>,
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
>(
'NodeIKernelMsgService/downloadRichMedia',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
1,
timeout,
(arg: OnRichMediaDownloadCompleteParams) => {
if (arg.msgId === msgId) {
return true;
}
return false;
},
{
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
},
);
let msg = await this.core.apis.MsgApi.getMsgsByMsgId({
>(
'NodeIKernelMsgService/downloadRichMedia',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
1,
timeout,
(arg: OnRichMediaDownloadCompleteParams) => {
if (arg.msgId === msgId) {
return true;
}
return false;
},
{
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
},
);
const msg = await this.core.apis.MsgApi.getMsgsByMsgId({
guildId: '',
chatType: chatType,
peerUid: peerUid,
@@ -150,13 +150,13 @@ export class NTQQFileApi {
return data[1].filePath;
}
//获取原始消息
let FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId);
const FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId);
if (!FileElements) {
//失败则就乱来 Todo
return data[1].filePath;
}
//从原始消息获取文件路径
let filePath =
const filePath =
FileElements?.fileElement?.filePath ||
FileElements?.pttElement?.filePath ||
FileElements?.videoElement?.filePath ||
@@ -179,7 +179,7 @@ export class NTQQFileApi {
async addFileCache(peer: Peer, msgId: string, msgSeq: string, senderUid: string, elemId: string, elemType: string, fileSize: string, fileName: string) {
let GroupData;
let BuddyData;
if (peer.chatType === ChatType.group) {
if (peer.chatType === ChatType.KCHATTYPEGROUP) {
GroupData =
[{
groupCode: peer.peerUid,
@@ -189,7 +189,7 @@ export class NTQQFileApi {
groupName: 'NapCat.Cached',
remark: 'NapCat.Cached',
}];
} else if (peer.chatType === ChatType.friend) {
} else if (peer.chatType === ChatType.KCHATTYPEC2C) {
BuddyData = [{
category_name: 'NapCat.Cached',
peerUid: peer.peerUid,
@@ -280,10 +280,10 @@ export class NTQQFileApi {
let id = '';
const Listener = this.core.eventWrapper.RegisterListen<(params: OnListener) => void>
(
'NodeIKernelSearchListener/onSearchFileKeywordsResult',
1,
20000,
(params) => id !== '' && params.searchId == id,
'NodeIKernelSearchListener/onSearchFileKeywordsResult',
1,
20000,
(params) => id !== '' && params.searchId == id,
);
id = await Event!(keys, 12);
const [ret] = (await Listener);

View File

@@ -1,5 +1,5 @@
import { Friend, FriendV2, User } from '@/core/entities';
import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelBuddyListener, NodeIKernelBuddyService, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
import { LimitedHashTable } from '@/common/utils/MessageUnique';
export class NTQQFriendApi {
@@ -26,10 +26,6 @@ export class NTQQFriendApi {
return Array.from(data.values());
}
async getBuddyIdMapCache(refresh = false): Promise<LimitedHashTable<string, string>> {
return await this.getBuddyIdMap(refresh);
}
async getBuddyIdMap(refresh = false): Promise<LimitedHashTable<string, string>> {
const uids: string[] = [];
const retMap: LimitedHashTable<string, string> = new LimitedHashTable<string, string>(5000);
@@ -74,29 +70,14 @@ export class NTQQFriendApi {
async isBuddy(uid: string) {
return this.context.session.getBuddyService().isBuddy(uid);
}
/**
* @deprecated
* @param forced
* @returns
*/
async getFriends(forced = false): Promise<User[]> {
const [_retData, _BuddyArg] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, (arg: OnBuddyChangeParams) => void>
(
'NodeIKernelBuddyService/getBuddyList',
'NodeIKernelBuddyListener/onBuddyListChange',
1,
5000,
() => true,
forced,
);
const friends: User[] = [];
for (const categoryItem of _BuddyArg) {
for (const friend of categoryItem.buddyList) {
friends.push(friend);
}
}
return friends;
async clearBuddyReqUnreadCnt() {
return this.context.session.getBuddyService().clearBuddyReqUnreadCnt();
}
async getBuddyReq() {
const [, ret] = await this.core.eventWrapper.CallNormalEventV2
<NodeIKernelBuddyService['getBuddyReq'], NodeIKernelBuddyListener['onBuddyReqChange']>
('NodeIKernelBuddyService/getBuddyReq', 'NodeIKernelBuddyListener/onBuddyReqChange', 1, 5000);
return ret;
}
async handleFriendRequest(flag: string, accept: boolean) {

View File

@@ -114,7 +114,7 @@ export class NTQQGroupApi {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
chatInfo: {
peerUid: GroupCode,
chatType: ChatType.group,
chatType: ChatType.KCHATTYPEGROUP,
},
filterMsgType: [],
filterSendersUid: uids,
@@ -175,7 +175,7 @@ export class NTQQGroupApi {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
chatInfo: {
peerUid: GroupCode,
chatType: ChatType.group,
chatType: ChatType.KCHATTYPEGROUP,
},
filterMsgType: [],
filterSendersUid: uids,
@@ -253,22 +253,45 @@ export class NTQQGroupApi {
}
async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
//type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
type EventType = NodeIKernelGroupService['getMemberInfo'];
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
const [, , , _members] = await this.core.eventWrapper.CallNormalEvent<EventType, ListenerType>
(
'NodeIKernelGroupService/getMemberInfo',
const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void>
(
'NodeIKernelGroupListener/onMemberInfoChange',
1,
5000,
(groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
return groupCode == GroupCode && members.has(uid);
forced ? 5000 : 250,
(params) => {
return params === GroupCode;
},
GroupCode, [uid], forced,
);
return _members.get(uid);
);
const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo');
const retData = await EventFunc!(GroupCode, [uid], forced);
if (retData.result !== 0) {
throw new Error(`${retData.errMsg}`);
}
const result = await Listener as unknown;
let member: GroupMember | undefined;
if (Array.isArray(result) && result?.[2] instanceof Map) {
const members = result[2] as Map<string, GroupMember>;
member = members.get(uid);
}
return member;
// 原本的方法: (no_cache 下效率很高, cache 下效率一致)
// const [, , , _members] = await this.core.eventWrapper.CallNormalEvent<EventType, ListenerType>
// (
// 'NodeIKernelGroupService/getMemberInfo',
// 'NodeIKernelGroupListener/onMemberInfoChange',
// 1,
// 5000,
// (groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
// return groupCode == GroupCode && members.has(uid);
// },
// GroupCode, [uid], forced,
// );
// return _members.get(uid);
}
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {

View File

@@ -1,4 +1,4 @@
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement } from '@/core/entities';
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement, SendStatusType } from '@/core/entities';
import { InstanceContext, NapCatCore } from '@/core';
import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
import { GeneralCallResult } from '@/core/services/common';
@@ -16,6 +16,9 @@ export class NTQQMsgApi {
return this.context.session.getMsgService().fetchLongMsg(peer, msgId);
}
async sendShowInputStatusReq(peer: Peer, eventType: number) {
return this.context.session.getMsgService().sendShowInputStatusReq(peer.chatType, eventType, peer.peerUid);
}
async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
//console.log(peer, msgSeq, emojiId, emojiType, count);
//注意此处emojiType 可选值一般为1-2 2好像是unicode表情dec值 大部分情况 Taged M likiowa
@@ -37,7 +40,6 @@ export class NTQQMsgApi {
emojiId = emojiId.toString();
return this.context.session.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set);
}
async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
msgList: RawMessage[]
} | undefined> {
@@ -94,7 +96,22 @@ export class NTQQMsgApi {
async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
}
async getMsgExBySeq(peer: Peer, msgSeq: string) {
const DateNow = Math.floor(Date.now() / 1000);
const filterMsgFromTime = (DateNow - 300).toString();
const filterMsgToTime = DateNow.toString();
const ret = await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
filterMsgType: [],
filterSendersUid: [],
filterMsgToTime: filterMsgToTime,
filterMsgFromTime: filterMsgFromTime,
isReverseOrder: false,
isIncludeCurrent: true,
pageLimit: 100,
});
return ret;
}
async setMsgRead(peer: Peer) {
return this.context.session.getMsgService().setMsgRead(peer);
}
@@ -103,18 +120,18 @@ export class NTQQMsgApi {
const data = await this.core.eventWrapper.CallNormalEvent<
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
>(
'NodeIKernelRichMediaService/getGroupFileList',
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
1,
5000,
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
>(
'NodeIKernelRichMediaService/getGroupFileList',
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
1,
5000,
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
return true;
},
GroupCode,
params,
);
return true;
},
GroupCode,
params,
);
return data[1].item;
}
@@ -129,64 +146,9 @@ export class NTQQMsgApi {
peerUid: peer.peerUid,
}, msgIds);
}
async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
function generateMsgId() {
const timestamp = Math.floor(Date.now() / 1000);
const random = Math.floor(Math.random() * Math.pow(2, 32));
const buffer = Buffer.alloc(8);
buffer.writeUInt32BE(timestamp, 0);
buffer.writeUInt32BE(random, 4);
const msgId = BigInt('0x' + buffer.toString('hex')).toString();
return msgId;
}
// 此处有采用Hack方法 利用数据返回正确得到对应消息
// 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同
// 谨慎采用 目前测试暂无问题 Developer.Mlikiowa
let msgId: string;
try {
msgId = await this.getMsgUnique(peer.chatType, await this.getServerTime());
} catch (error) {
//if (!napCatCore.session.getMsgService()['generateMsgUniqueId'])
//兜底识别策略V2
msgId = generateMsgId().toString();
}
const data = await this.core.eventWrapper.CallNormalEvent<
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void
>(
'NodeIKernelMsgService/sendMsg',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
timeout,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.msgId === msgId && msgRecord.sendStatus === 2) {
return true;
}
}
return false;
},
msgId,
peer,
msgElements,
new Map(),
);
const retMsg = data[1].find(msgRecord => {
if (msgRecord.msgId === msgId) {
return true;
}
});
return retMsg;
}
sendMsgEx(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
//return NTQQMsgApi.sendMsgV1(peer, msgElements, waitComplete, timeout);
}
async PrepareTempChat(toUserUid: string, GroupCode: string, nickname: string) {
//By Jadx/Ida Mlikiowa
let TempGameSession = {
const TempGameSession = {
nickname: "",
gameAppId: "",
selfTinyId: "",
@@ -194,7 +156,7 @@ export class NTQQMsgApi {
peerOpenId: "",
};
return this.context.session.getMsgService().prepareTempChat({
chatType: ChatType.temp,
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: toUserUid,
peerNickname: nickname,
fromGroupCode: GroupCode,
@@ -209,35 +171,35 @@ export class NTQQMsgApi {
}
async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
//唉? !我有个想法
if (peer.chatType === ChatType.temp && peer.guildId && peer.guildId !== '') {
let member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid!);
if (peer.chatType === ChatType.KCHATTYPETEMPC2CFROMGROUP && peer.guildId && peer.guildId !== '') {
const member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid!);
if (member) {
await this.PrepareTempChat(peer.peerUid, peer.guildId, member.nick);
}
}
const msgId = await this.getMsgUnique(peer.chatType, await this.getServerTime());
const msgId = await this.generateMsgUniqueId(peer.chatType, await this.getServerTime());
peer.guildId = msgId;
const data = await this.core.eventWrapper.CallNormalEvent<
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void
>(
'NodeIKernelMsgService/sendMsg',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
timeout,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.guildId === msgId && msgRecord.sendStatus === 2) {
return true;
>(
'NodeIKernelMsgService/sendMsg',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
timeout,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
return true;
}
}
}
return false;
},
'0',
peer,
msgElements,
new Map(),
);
return false;
},
'0',
peer,
msgElements,
new Map(),
);
const retMsg = data[1].find(msgRecord => {
if (msgRecord.guildId === msgId) {
return true;
@@ -246,21 +208,14 @@ export class NTQQMsgApi {
return retMsg;
}
async getMsgUnique(chatType: number, time: string) {
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
return this.context.session.getMsgService().generateMsgUniqueId(chatType, time);
}
return this.context.session.getMsgService().getMsgUniqueId(time);
async generateMsgUniqueId(chatType: number, time: string) {
return this.context.session.getMsgService().generateMsgUniqueId(chatType, time);
}
async getServerTime() {
return this.context.session.getMSFService().getServerTime();
}
async getServerTimeV2() {
return this.core.eventWrapper.callNoListenerEvent<() => string>('NodeIKernelMsgService/getServerTime', 5000);
}
async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) {
return this.context.session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], new Map());
}
@@ -272,31 +227,31 @@ export class NTQQMsgApi {
const data = await this.core.eventWrapper.CallNormalEvent<
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void
>(
'NodeIKernelMsgService/multiForwardMsgWithComment',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
5000,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
return true;
>(
'NodeIKernelMsgService/multiForwardMsgWithComment',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
5000,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
return true;
}
}
}
return false;
},
msgInfos,
srcPeer,
destPeer,
[],
new Map(),
);
return false;
},
msgInfos,
srcPeer,
destPeer,
[],
new Map(),
);
for (const msg of data[1]) {
const arkElement = msg.elements.find(ele => ele.arkElement);
if (!arkElement) {
continue;
}
const forwardData: any = JSON.parse(arkElement.arkElement.bytesData);
const forwardData: any = JSON.parse(arkElement.arkElement?.bytesData || "");
if (forwardData.app != 'com.tencent.multimsg') {
continue;
}

View File

@@ -100,7 +100,7 @@ export class NTQQUserApi {
return retData;
}
async fetchUserDetailInfo(uid: string) {
async fetchUserDetailInfo(uid: string, mode: UserDetailSource = UserDetailSource.KDB) {
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
const [_retData, profile] = await this.core.eventWrapper.CallNormalEvent<EventService, EventListener>(
@@ -111,7 +111,7 @@ export class NTQQUserApi {
(profile) => profile.uid === uid,
'BuddyProfileStore',
[uid],
UserDetailSource.KSERVER,
mode,
[ProfileBizType.KALL],
);
const RetUser: User = {
@@ -120,32 +120,23 @@ export class NTQQUserApi {
...profile.simpleInfo.vasInfo,
...profile.commonExt,
...profile.simpleInfo.baseInfo,
qqLevel: profile.commonExt.qqLevel,
qqLevel: profile.commonExt?.qqLevel,
age: profile.simpleInfo.baseInfo.age,
pendantId: '',
};
return RetUser;
}
async getUserDetailInfo(uid: string) {
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
return this.fetchUserDetailInfo(uid);
async getUserDetailInfo(uid: string): Promise<User> {
try {
const retUser = await this.fetchUserDetailInfo(uid, UserDetailSource.KDB);
if (retUser.uin !== '0') {
return retUser;
}
} catch (e) {
}
return this.getUserDetailInfoOld(uid);
}
async getUserDetailInfoOld(uid: string) {
type EventService = NodeIKernelProfileService['getUserDetailInfoWithBizInfo'];
type EventListener = NodeIKernelProfileListener['onProfileDetailInfoChanged'];
const [_retData, profile] = await this.core.eventWrapper.CallNormalEvent<EventService, EventListener>(
'NodeIKernelProfileService/getUserDetailInfoWithBizInfo',
'NodeIKernelProfileListener/onProfileDetailInfoChanged',
2,
5000,
(profile) => profile.uid === uid,
uid,
[0],
);
return profile;
this.context.logger.logDebug('[NapCat] [Mark] getUserDetailInfo Mode1 Failed.');
return this.fetchUserDetailInfo(uid, UserDetailSource.KSERVER);
}
async modifySelfProfile(param: ModifyProfileParams) {
@@ -155,16 +146,10 @@ export class NTQQUserApi {
//需要异常处理
async getCookies(domain: string) {
const ClientKeyData = await this.forceFetchClientKey();
const requestUrl = `https://ssl.ptlogin2.qq.com/jump?${
new URLSearchParams({
ptlang: '1033',
clientuin: this.core.selfInfo.uin,
clientkey: ClientKeyData.clientKey,
u1: `https://user.qzone.qq.com/${this.core.selfInfo.uin}/infocenter`,
keyindex: '19',
})
}`;
return await RequestUtil.HttpsGetCookies(requestUrl);
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin +
'&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + this.core.selfInfo.uin + '%2Finfocenter&keyindex=19%27';
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
return cookies;
}
async getPSkey(domainList: string[]) {
@@ -209,39 +194,14 @@ export class NTQQUserApi {
return skey;
}
/**
* @deprecated
*/
async getUidByUin(Uin: string) {
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
return await this.getUidByUinV2(Uin);
}
return await this.getUidByUinV1(Uin);
}
/**
* @deprecated
*/
async getUinByUid(Uid: string) {
if (this.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
return await this.getUinByUidV2(Uid);
}
return await this.getUinByUidV1(Uid);
}
//后期改成流水线处理
async getUidByUinV2(Uin: string) {
let uid = (await this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
let uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
if (uid) return uid;
uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin);
uid = (await this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin);
if (uid) return uid;
uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
if (uid) return uid;
// console.log((await this.core.getApiContext().FriendApi.getBuddyIdMapCache(true)));
uid = (await this.core.apis.FriendApi.getBuddyIdMapCache(true)).getValue(Uin);//从Buddy缓存获取Uid
if (uid) return uid;
uid = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getValue(Uin);
if (uid) return uid;
const unveifyUid = (await this.getUserDetailInfoByUinV2(Uin)).detail.uid;//从QQ Native 特殊转换
if (unveifyUid.indexOf('*') == -1) uid = unveifyUid;
//if (uid) return uid;
@@ -250,50 +210,18 @@ export class NTQQUserApi {
//后期改成流水线处理
async getUinByUidV2(Uid: string) {
let uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
let uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
if (uin) return uin;
uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid);
uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid);
if (uin) return uin;
uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid);
if (uin) return uin;
uin = (await this.core.apis.FriendApi.getBuddyIdMapCache(true)).getKey(Uid);//从Buddy缓存获取Uin
if (uin) return uin;
uin = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getKey(Uid);
if (uin) return uin;
uin = (await this.getUserDetailInfo(Uid)).uin; //从QQ Native 转换
return uin;
}
async getUidByUinV1(Uin: string) {
// 通用转换开始尝试
let uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin);
if (!uid) {
const unveifyUid = (await this.getUserDetailInfoByUin(Uin)).info.uid;//从QQ Native 特殊转换 方法三
if (unveifyUid.indexOf('*') == -1) {
uid = unveifyUid;
}
}
return uid;
}
async getUinByUidV1(Uid: string) {
const ret = await this.core.eventWrapper.callNoListenerEvent<(Uin: string[]) => Promise<{
uinInfo: Map<string, string>
}>>
('NodeIKernelUixConvertService/getUin', 5000, [Uid]);
let uin = ret.uinInfo.get(Uid);
if (!uin) {
uin = (await this.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;
}
async getRecentContactListSnapShot(count: number) {
return await this.context.session.getRecentContactService().getRecentContactListSnapShot(count);
}
@@ -315,11 +243,6 @@ export class NTQQUserApi {
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
}
async getUserDetailInfoByUin(Uin: string) {
return this.core.eventWrapper.callNoListenerEvent<(Uin: string) => Promise<UserDetailInfoByUin>>
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
}
async forceFetchClientKey() {
return await this.context.session.getTicketService().forceFetchClientKey('');
}

View File

@@ -44,8 +44,8 @@ export class NapCatCore {
constructor(context: InstanceContext, selfInfo: SelfInfo) {
this.selfInfo = selfInfo;
this.context = context;
this.util = new this.context.wrapper.NodeQQNTWrapperUtil();
this.eventWrapper = new LegacyNTEventWrapper(context.wrapper, context.session);
this.util = this.context.wrapper.NodeQQNTWrapperUtil;
this.eventWrapper = new LegacyNTEventWrapper(context.session);
this.apis = {
FileApi: new NTQQFileApi(this.context, this),
SystemApi: new NTQQSystemApi(this.context, this),
@@ -79,7 +79,7 @@ export class NapCatCore {
}
get dataPath(): string {
let result = this.util.getNTUserDataInfoConfig();
let result = this.context.wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
if (!result) {
result = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(result, { recursive: true });
@@ -93,9 +93,12 @@ export class NapCatCore {
msgListener.onRecvMsg = (msgs) => {
msgs.forEach(msg => this.context.logger.logMessage(msg, this.selfInfo));
};
msgListener.onAddSendMsg = (msg) => {
this.context.logger.logMessage(msg, this.selfInfo);
};
//await sleep(2500);
this.context.session.getMsgService().addKernelMsgListener(
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)),
proxiedListenerOf(msgListener, this.context.logger) as any
);
const profileListener = new ProfileListener();
@@ -110,7 +113,7 @@ export class NapCatCore {
// }
};
this.context.session.getProfileService().addKernelProfileListener(
new this.context.wrapper.NodeIKernelProfileListener(proxiedListenerOf(profileListener, this.context.logger)),
proxiedListenerOf(profileListener, this.context.logger),
);
// 群相关
@@ -193,6 +196,9 @@ export class NapCatCore {
this.apis.GroupApi.groupMemberCache.set(groupCode, members);
}
};
this.context.session.getGroupService().addKernelGroupListener(
proxiedListenerOf(profileListener, this.context.logger) as any
);
}
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
if (memberNew.role !== memberOld?.role) {

View File

@@ -1,4 +1,4 @@
import { QQLevel, Sex } from './user';
import { QQLevel, Sex, User } from './user';
export enum GroupListUpdateType {
REFRESHALL,
@@ -65,6 +65,7 @@ export interface GroupMember {
uin: string; // QQ号
isRobot: boolean;
sex?: Sex;
age?: number;
qqLevel?: QQLevel;
isChangeRole: boolean;
joinTime: string;

View File

@@ -362,6 +362,7 @@ export interface SendPicElement {
}
export interface ReplyElement {
sourceMsgIdInRecords?: string;
replayMsgSeq: string;
replayMsgId: string;
senderUin: string;
@@ -476,7 +477,7 @@ export interface MessageElement {
extBufForUI: string,//"0x",
textElement?: TextElement;
faceElement?: FaceElement,
marketFaceElement?: MarkdownElement,
marketFaceElement?: MarketFaceElement,
replyElement?: ReplyElement,
picElement?: PicElement,
pttElement?: PttElement,
@@ -510,17 +511,8 @@ export enum AtType {
atAll = 1,
atUser = 2
}
export enum ChatType {
friend = 1,
group = 2,
chatDevice = 8, //移动设备?
temp = 100
}
// 来自Android分析
export enum ChatType2 {
export enum ChatType {
KCHATTYPEADELIE = 42,
KCHATTYPEBUDDYNOTIFY = 5,
KCHATTYPEC2C = 1,
@@ -614,13 +606,28 @@ export interface PicElement {
originImageUrl?: string; // http url, 没有hosthost是https://gchat.qpic.cn/, 带download参数的是https://multimedia.nt.qq.com.cn
}
export enum GrayTipElementSubType {
INVITE_NEW_MEMBER = 12,
MEMBER_NEW_TITLE = 17
export enum NTGrayTipElementSubTypeV2 {
GRAYTIP_ELEMENT_SUBTYPE_AIOOP = 15,
GRAYTIP_ELEMENT_SUBTYPE_BLOCK = 14,
GRAYTIP_ELEMENT_SUBTYPE_BUDDY = 5,
GRAYTIP_ELEMENT_SUBTYPE_BUDDYNOTIFY = 9,
GRAYTIP_ELEMENT_SUBTYPE_EMOJIREPLY = 3,
GRAYTIP_ELEMENT_SUBTYPE_ESSENCE = 7,
GRAYTIP_ELEMENT_SUBTYPE_FEED = 6,
GRAYTIP_ELEMENT_SUBTYPE_FEEDCHANNELMSG = 11,
GRAYTIP_ELEMENT_SUBTYPE_FILE = 10,
GRAYTIP_ELEMENT_SUBTYPE_GROUP = 4,
GRAYTIP_ELEMENT_SUBTYPE_GROUPNOTIFY = 8,
GRAYTIP_ELEMENT_SUBTYPE_JSON = 17,
GRAYTIP_ELEMENT_SUBTYPE_LOCALMSG = 13,
GRAYTIP_ELEMENT_SUBTYPE_PROCLAMATION = 2,
GRAYTIP_ELEMENT_SUBTYPE_REVOKE = 1,
GRAYTIP_ELEMENT_SUBTYPE_UNKNOWN = 0,
GRAYTIP_ELEMENT_SUBTYPE_WALLET = 16,
GRAYTIP_ELEMENT_SUBTYPE_XMLMSG = 12,
}
export interface GrayTipElement {
subElementType: GrayTipElementSubType;
subElementType: NTGrayTipElementSubTypeV2;
revokeElement: {
operatorRole: string;
operatorUid: string;
@@ -876,6 +883,12 @@ export enum NTSubMsgType {
KMSGSUBTYPEMIXTEXT = 0,
KMSGSUBTYPETENCENTDOC = 6
}
export enum SendStatusType {
KSEND_STATUS_FAILED = 0,
KSEND_STATUS_SENDING = 1,
KSEND_STATUS_SUCCESS = 2,
KSEND_STATUS_SUCCESS_NOSEQ = 3
}
export interface RawMessage {
parentMsgPeer: Peer;
@@ -935,7 +948,7 @@ export interface RawMessage {
/**
* 消息状态,别人发的 2 是已撤回,自己发的 2 是已发送
*/
sendStatus?: number;
sendStatus?: SendStatusType;
/**
* 撤回时间,"0" 是没有撤回
@@ -944,76 +957,5 @@ export interface RawMessage {
records: RawMessage[];
elements: ElementWrapper[];
}
/**
* 并非原生接口类型,故以 type 包装
*/
export type ElementWrapper = {
elementId: string;
elementType: ElementType;
replyElement?: {
sourceMsgIdInRecords: string;
/**
* 源消息发送者 QQ 号
*/
senderUid: string;
/**
* 源消息是否有图片
*/
sourceMsgIsIncPic: boolean;
/**
* 源消息文本
*/
sourceMsgText: string;
/**
* 源消息的 msgSeq可以通过这个找到源消息的 msgId
*/
replayMsgSeq: string;
};
textElement?: {
atType: AtType;
/**
* 被 @ 的 QQ 号
*/
atUid: string;
content: string;
/**
* 被 @ 的 UID从这里可以看出来 UID 的概念是 NT 才引入的)
*/
atNtUid: string;
};
picElement?: PicElement;
pttElement?: PttElement;
arkElement?: ArkElement;
grayTipElement?: GrayTipElement;
faceElement?: FaceElement;
videoElement?: VideoElement;
fileElement?: FileElement;
marketFaceElement?: MarketFaceElement;
inlineKeyboardElement?: InlineKeyboardElement;
markdownElement?: MarkdownElement;
multiForwardMsgElement?: MultiForwardMsgElement;
}
elements: MessageElement[];
}

View File

@@ -64,6 +64,7 @@ export enum BuddyReqType {
}
export interface FriendRequest {
isBuddy?: boolean;
isInitiator?: boolean;
isDecide: boolean;
friendUid: string;

View File

@@ -231,6 +231,7 @@ export interface User {
longNick?: string; // 签名
remark?: string;
sex?: Sex;
age?: number;
qqLevel?: QQLevel;
qid?: string;
birthday_year?: number;

View File

@@ -1,58 +1,30 @@
{
"3.1.2-13107": {
"appid": 537146866,
"qua": "V1_LNX_NQ_3.1.2-13107_RDM_B"
"3.2.12-27187": {
"appid": 537240645,
"qua": "V1_LNX_NQ_3.2.12_27187_GW_B"
},
"3.2.10-25765": {
"appid": 537234773,
"qua": "V1_LNX_NQ_3.2.10_25765_GW_B"
"3.2.12-27206": {
"appid": 537240645,
"qua": "V1_LNX_NQ_3.2.12_27206_GW_B"
},
"3.2.12-26702": {
"appid": 537237950,
"qua": "V1_LNX_NQ_3.2.12_26702_GW_B"
"3.2.12-27254":{
"appid": 537240795,
"qua": "V1_LNX_NQ_3.2.12_27254_GW_B"
},
"3.2.12-26740": {
"appid": 537237950,
"qua": "V1_WIN_NQ_9.9.15_26740_GW_B"
"9.9.15-27187":{
"appid": 537240610,
"qua": "V1_WIN_NQ_9.9.15_27187_GW_B"
},
"3.2.12-26909": {
"appid": 537237923,
"qua": "V1_LNX_NQ_3.2.12_26909_GW_B"
"9.9.15-27206":{
"appid": 537240610,
"qua": "V1_WIN_NQ_9.9.15_27206_GW_B"
},
"9.9.11-24815": {
"appid": 537226656,
"qua": "V1_WIN_NQ_9.9.11_24815_GW_B"
"9.9.15-27254":{
"appid": 537240709,
"qua": "V1_WIN_NQ_9.9.15_27254_GW_B"
},
"9.9.12-25493": {
"appid": 537231759,
"qua": "V1_WIN_NQ_9.9.12_25493_GW_B"
},
"9.9.12-25765": {
"appid": 537234702,
"qua": "V1_WIN_NQ_9.9.12_25765_GW_B"
},
"9.9.12-26299": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26299_GW_B"
},
"9.9.12-26339": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26339_GW_B"
},
"9.9.12-26466": {
"appid": 537234826,
"qua": "V1_WIN_NQ_9.9.12_26466_GW_B"
},
"9.9.15-26702": {
"appid": 537237765,
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
},
"9.9.15-26740": {
"appid": 537237765,
"qua": "V1_WIN_NQ_9.9.15_26702_GW_B"
},
"9.9.15-26909": {
"appid": 537237802,
"qua": "V1_WIN_NQ_9.9.15_26909_GW_B"
"9.9.15-27333": {
"appid": 537240709,
"qua": "V1_WIN_NQ_9.9.15_27333_GW_B"
}
}

View File

@@ -1,6 +1,6 @@
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
interface IGroupListener {
export interface IGroupListener {
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
onGroupExtListUpdate(...args: unknown[]): void;

View File

@@ -61,11 +61,11 @@ export interface NodeIKernelBuddyService {
getBuddyReqUnreadCnt(): number;
getBuddyReq(): unknown;
getBuddyReq(): Promise<GeneralCallResult>;
delBuddyReq(uid: number): void;
clearBuddyReqUnreadCnt(): void;
clearBuddyReqUnreadCnt(): Promise<GeneralCallResult>;
reqToAddFriends(uid: number, msg: string): void;

View File

@@ -1,4 +1,4 @@
import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import { IGroupListener, NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import {
GroupExtParam,
GroupMember,
@@ -104,7 +104,7 @@ export interface NodeIKernelGroupService {
setHeader(uid: string, path: string): unknown;
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
addKernelGroupListener(listener: IGroupListener): number;
removeKernelGroupListener(listenerId: unknown): void;

View File

@@ -1,6 +1,6 @@
import { AnyCnameRecord } from 'node:dns';
import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
import { NodeIKernelProfileListener } from '../listeners';
import { NodeIKernelProfileListener, ProfileListener } from '../listeners';
import { GeneralCallResult } from '@/core/services/common';
export enum UserDetailSource {
@@ -35,7 +35,7 @@ export interface NodeIKernelProfileService {
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
addKernelProfileListener(listener: NodeIKernelProfileListener): number;
addKernelProfileListener(listener: ProfileListener): number;
removeKernelProfileListener(listenerId: number): void;

View File

@@ -35,6 +35,7 @@ import { NodeIkernelTestPerformanceService } from '../services/NodeIkernelTestPe
import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService';
export interface NodeQQNTWrapperUtil {
get(): unknown;
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeQQNTWrapperUtil;

File diff suppressed because one or more lines are too long

View File

@@ -13,7 +13,7 @@ import { NapCatOneBot11Adapter } from '@/onebot';
//Framework ES入口文件
export async function getWebUiUrl() {
let WebUiConfigData = (await WebUiConfig.GetWebUIConfig());
const WebUiConfigData = (await WebUiConfig.GetWebUIConfig());
return "http://127.0.0.1:" + WebUiConfigData.port + '/webui/?token=' + WebUiConfigData.token;
}
export async function NCoreInitFramework(
@@ -41,8 +41,7 @@ export async function NCoreInitFramework(
online: true,
});
};
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener(
proxiedListenerOf(loginListener, logger)));
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
});
// 过早进入会导致addKernelMsgListener等Listener添加失败
// await sleep(2500);

View File

@@ -1,14 +1,38 @@
export const onSettingWindowCreated = async (view) => {
view.style.width = "100%";
view.style.height = "100%";
//添加iframe
const iframe = document.createElement("iframe");
iframe.src = await window.napcat.getWebUiUrl();
iframe.width = "100%";
iframe.height = "100%";
iframe.style.border = "none";
//去掉iframe滚动条
//iframe.scrolling = "no";
//有滚动条何尝不是一种美
view.appendChild(iframe);
// view.style.width = "100%";
// view.style.height = "100%";
// //添加iframe
// const iframe = document.createElement("iframe");
// iframe.src = await window.napcat.getWebUiUrl();
// iframe.width = "100%";
// iframe.height = "100%";
// iframe.style.border = "none";
// //去掉iframe滚动条
// //iframe.scrolling = "no";
// //有滚动条何尝不是一种美
// view.appendChild(iframe);
let webui = await window.napcat.getWebUiUrl();
let panel = `
<setting-section data-title="">
<setting-panel>
<setting-list data-direction="column">
<setting-item>
<setting-button data-type="primary" class="nc_openwebui">打开配置页面</setting-button>
</setting-item>
<setting-item>
<div>
<setting-text class="nc_webui">WebUi</setting-text>
</div>
</setting-item>
</setting-list>
</setting-panel>
</setting-section>
`;
view.innerHTML = panel;
view.querySelector(".nc_openwebui").addEventListener("click", () => {
window.open(webui, "_blank");
});
view.querySelector(".nc_webui").innerText = webui;
};

View File

@@ -5,7 +5,7 @@ import { ActionName } from '../types';
const SchemaData = {
type: 'object',
properties: {
count: { type: 'number' },
count: { type: ['number', 'string'] },
},
} as const satisfies JSONSchema;
@@ -17,7 +17,7 @@ export class FetchCustomFace extends BaseAction<Payload, string[]> {
async _handle(payload: Payload) {
//48 可能正好是QQ需要的一个页面的数量 Tagged Mlikiowa
const ret = await this.CoreContext.apis.MsgApi.fetchFavEmojiList(payload.count || 48);
const ret = await this.CoreContext.apis.MsgApi.fetchFavEmojiList(parseInt((payload.count || '0').toString()) || 48);
return ret.emojiInfoList.map(e => e.url);
}
}

View File

@@ -12,7 +12,7 @@ const SchemaData = {
emojiId: { type: 'string' },
emojiType: { type: 'string' },
message_id: { type: ['string', 'number'] },
count: { type: 'number' },
count: { type: ['string', 'number'] },
},
required: ['emojiId', 'emojiType', 'message_id'],
} as const satisfies JSONSchema;
@@ -22,12 +22,11 @@ type Payload = FromSchema<typeof SchemaData>;
export class FetchEmojiLike extends BaseAction<Payload, any> {
actionName = ActionName.FetchEmojiLike;
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
if (!msgIdPeer) throw new Error('消息不存在');
const msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];
return await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer, msg.msgSeq, payload.emojiId, payload.emojiType, payload.count);
return await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer, msg.msgSeq, payload.emojiId, payload.emojiType, parseInt((payload.count || '0').toString()) || 20);
}
}

View File

@@ -5,8 +5,8 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
category: { type: 'number' },
count: { type: 'number' },
category: { type: ['number', 'string'] },
count: { type: ['number', 'string'] },
},
required: ['category', 'count'],
} as const satisfies JSONSchema;
@@ -16,9 +16,8 @@ type Payload = FromSchema<typeof SchemaData>;
export class GetCollectionList extends BaseAction<Payload, any> {
actionName = ActionName.GetCollectionList;
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQCollectionApi = this.CoreContext.apis.CollectionApi;
return await NTQQCollectionApi.getAllCollection(payload.category, payload.count);
return await NTQQCollectionApi.getAllCollection(parseInt(payload.category.toString()), parseInt(payload.count.toString()));
}
}

View File

@@ -1,19 +1,13 @@
import { OB11Constructor } from '@/onebot/helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
export class GetFriendWithCategory extends BaseAction<void, any> {
actionName = ActionName.GetFriendsWithCategory;
async _handle(payload: void) {
if (this.CoreContext.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
//全新逻辑
return (await this.CoreContext.apis.FriendApi.getBuddyV2ExWithCate(true)).map(category => ({
...category,
buddyList: OB11Constructor.friendsV2(category.buddyList),
}));
} else {
throw new Error('this ntqq version not support, must be 26702 or later');
}
return (await this.CoreContext.apis.FriendApi.getBuddyV2ExWithCate(true)).map(category => ({
...category,
buddyList: OB11Constructor.friendsV2(category.buddyList),
}));
}
}

View File

@@ -9,7 +9,7 @@ export class GetProfileLike extends BaseAction<void, any> {
const ret = await NTQQUserApi.getProfileLike(this.CoreContext.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)) || '');
listdata[i].uin = parseInt((await NTQQUserApi.getUinByUidV2(listdata[i].uid)) || '');
}
return listdata;
}

View File

@@ -5,7 +5,6 @@ export class GetRobotUinRange extends BaseAction<void, Array<any>> {
actionName = ActionName.GetRobotUinRange;
async _handle(payload: void) {
// console.log(await NTQQUserApi.getRobotUinRange());
const NTQQUserApi = this.CoreContext.apis.UserApi;
return await NTQQUserApi.getRobotUinRange();
}

View File

@@ -3,7 +3,6 @@ import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { checkFileReceived, uri2local } from '@/common/utils/file';
import fs from 'fs';
const SchemaData = {
type: 'object',
properties: {

View File

@@ -0,0 +1,44 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { ChatType, Peer } from '@/core';
const SchemaData = {
type: 'object',
properties: {
eventType: { type: 'string' },
group_id: { type: 'string' },
user_id: { type: 'string' }
},
required: ['eventType'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class SetInputStatus extends BaseAction<Payload, any> {
actionName = ActionName.SetInputStatus;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
let peer: Peer;
if (payload.group_id) {
peer = {
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id
};
} else if (payload.user_id) {
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id);
if (!uid) throw new Error('uid is empty');
peer = {
chatType: ChatType.KCHATTYPEC2C,
peerUid: uid
};
} else {
throw new Error('请指定 group_id 或 user_id');
}
const ret = await NTQQMsgApi.sendShowInputStatusReq(peer, parseInt(payload.eventType));
return ret;
}
}

View File

@@ -6,9 +6,9 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
status: { type: 'number' },
extStatus: { type: 'number' },
batteryStatus: { type: 'number' },
status: { type: ['number', 'string'] },
extStatus: { type: ['number', 'string'] },
batteryStatus: { type: ['number', 'string'] },
},
required: ['status', 'extStatus', 'batteryStatus'],
} as const satisfies JSONSchema;
@@ -20,14 +20,12 @@ export class SetOnlineStatus extends BaseAction<Payload, null> {
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
// 可设置状态
// { status: 10, extStatus: 1027, batteryStatus: 0 }
// { status: 30, extStatus: 0, batteryStatus: 0 }
// { status: 50, extStatus: 0, batteryStatus: 0 }
// { status: 60, extStatus: 0, batteryStatus: 0 }
// { status: 70, extStatus: 0, batteryStatus: 0 }
const NTQQUserApi = this.CoreContext.apis.UserApi;
const ret = await NTQQUserApi.setSelfOnlineStatus(payload.status, payload.extStatus, payload.batteryStatus);
const ret = await NTQQUserApi.setSelfOnlineStatus(
parseInt(payload.status.toString()),
parseInt(payload.extStatus.toString()),
parseInt(payload.batteryStatus.toString())
);
if (ret.result !== 0) {
throw new Error('设置在线状态失败');
}

View File

@@ -1,32 +0,0 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
nick: { type: 'string' },
longNick: { type: 'string' },
sex: { type: 'number' },//传Sex值建议传0
},
required: ['nick', 'longNick', 'sex'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class SetSelfProfile extends BaseAction<Payload, any | null> {
actionName = ActionName.SetSelfProfile;
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const ret = await NTQQUserApi.modifySelfProfile({
nick: payload.nick,
longNick: payload.longNick,
sex: payload.sex,
birthday: { birthday_year: '', birthday_month: '', birthday_day: '' },
location: undefined,
});
return ret;
}
}

View File

@@ -28,19 +28,6 @@ const GetFileBase_PayloadSchema = {
export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
PayloadSchema: any = GetFileBase_PayloadSchema;
private getElement(msg: RawMessage): { id: string, element: VideoElement | FileElement } {
let element = msg.elements.find(e => e.fileElement);
if (!element) {
element = msg.elements.find(e => e.videoElement);
if (element) {
return { id: element.elementId, element: element.videoElement };
} else {
throw new Error('找不到文件');
}
}
return { id: element.elementId, element: element.fileElement };
}
async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
const NTQQUserApi = this.CoreContext.apis.UserApi;
@@ -60,15 +47,15 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
let peer: Peer | undefined;
//识别Peer
if (isGroup) {
peer = { chatType: ChatType.group, peerUid: peerUin };
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: peerUin };
}
const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin);
if (PeerUid) {
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
if (isBuddy) {
peer = { chatType: ChatType.friend, peerUid: PeerUid };
peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: PeerUid };
} else {
peer = { chatType: ChatType.temp, peerUid: PeerUid };
peer = { chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: PeerUid };
}
}
if (!peer) {
@@ -92,7 +79,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
file_size: fileSize,
file_name: fileName,
};
if (true/*enableLocalFile2Url*/) {
if (true/*enableLocalFile2Url*/ && downloadPath) {
try {
res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) {
@@ -110,8 +97,8 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
if (NTSearchNameResult.length !== 0) {
const MsgId = NTSearchNameResult[0].msgId;
let peer: Peer | undefined = undefined;
if (NTSearchNameResult[0].chatType == ChatType.group) {
peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
if (NTSearchNameResult[0].chatType == ChatType.KCHATTYPEGROUP) {
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
}
if (!peer) {
throw new Error('chattype not support');
@@ -132,7 +119,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
file_size: NTSearchNameResult[0].fileSize.toString(),
file_name: NTSearchNameResult[0].fileName,
};
if (true/*enableLocalFile2Url*/) {
if (true/*enableLocalFile2Url*/ && downloadPath) {
try {
res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) {

View File

@@ -13,7 +13,7 @@ interface FileResponse {
const SchemaData = {
type: 'object',
properties: {
thread_count: { type: 'number' },
thread_count: { type: ['number', 'string'] },
url: { type: 'string' },
base64: { type: 'string' },
name: { type: 'string' },

View File

@@ -1,9 +1,10 @@
import BaseAction from '../BaseAction';
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { RawNTMsg2Onebot } from '@/onebot/helper';
const SchemaData = {
type: 'object',
@@ -39,15 +40,16 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
throw Error('找不到相关的聊天记录' + data?.errMsg);
}
const msgList = data.msgList;
const messages = await Promise.all(msgList.map(async msg => {
const resMsg = await OB11Constructor.message(this.CoreContext, msg, 'array');
const messages = (await Promise.all(msgList.map(async msg => {
const resMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg);
if (!resMsg) return;
resMsg.message_id = MessageUnique.createMsg({
guildId: '',
chatType: msg.chatType,
peerUid: msg.peerUid,
}, msg.msgId)!;
return resMsg;
}));
}))).filter(msg => !!msg);
messages.map(msg => {
(<OB11ForwardMessage>msg).content = msg.message;
delete (<any>msg).message;

View File

@@ -2,9 +2,10 @@ import BaseAction from '../BaseAction';
import { OB11Message } from '../../types';
import { ActionName } from '../types';
import { ChatType, RawMessage } from '@/core/entities';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { RawNTMsg2Onebot } from '@/onebot/helper';
interface Response {
messages: OB11Message[];
@@ -37,7 +38,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
const isReverseOrder = payload.reverseOrder || true;
if (!uid) throw `记录${payload.user_id}不存在`;
const friend = await NTQQFriendApi.isBuddy(uid);
const peer = { chatType: friend ? ChatType.friend : ChatType.temp, peerUid: uid };
const peer = { chatType: friend ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: uid };
//拉取消息
let msgList: RawMessage[];
@@ -53,7 +54,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
}));
//转换消息
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(this.CoreContext, msg, 'array')));
const ob11MsgList = (await Promise.all(msgList.map(msg => RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg);
return { 'messages': ob11MsgList };
}
}

View File

@@ -2,9 +2,10 @@ import BaseAction from '../BaseAction';
import { OB11Message } from '../../types';
import { ActionName } from '../types';
import { ChatType, Peer, RawMessage } from '@/core/entities';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { RawNTMsg2Onebot } from '@/onebot/helper';
interface Response {
messages: OB11Message[];
@@ -32,7 +33,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
//处理参数
const isReverseOrder = payload.reverseOrder || true;
const MsgCount = payload.count || 20;
const peer: Peer = { chatType: ChatType.group, peerUid: payload.group_id.toString() };
const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
//拉取消息
let msgList: RawMessage[];
if (!payload.message_seq || payload.message_seq == 0) {
@@ -48,7 +49,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
}));
//转换消息
const ob11MsgList = await Promise.all(msgList.map(msg => OB11Constructor.message(this.CoreContext, msg, 'array')));
const ob11MsgList = (await Promise.all(msgList.map(msg => RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg);
return { 'messages': ob11MsgList };
}
}

View File

@@ -1,6 +1,6 @@
import BaseAction from '../BaseAction';
import { OB11User, OB11UserSex } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { calcQQLevel } from '@/common/utils/helper';
@@ -21,19 +21,22 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
async _handle(payload: Payload): Promise<OB11User> {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const user_id = payload.user_id.toString();
const extendData = await NTQQUserApi.getUserDetailInfoByUin(user_id);
const extendData = await NTQQUserApi.getUserDetailInfoByUinV2(user_id);
const uid = (await NTQQUserApi.getUidByUinV2(user_id))!;
if (!uid || uid.indexOf('*') != -1) {
const ret = {
...extendData,
user_id: parseInt(extendData.info.uin) || 0,
nickname: extendData.info.nick,
...extendData.detail.simpleInfo.coreInfo,
...extendData.detail.commonExt,
...extendData.detail.simpleInfo.baseInfo,
...extendData.detail.simpleInfo.relationFlags,
user_id: parseInt(extendData.detail.uin) || 0,
nickname: extendData.detail.simpleInfo.coreInfo.nick,
sex: OB11UserSex.unknown,
age: (extendData.info.birthday_year == 0) ? 0 : new Date().getFullYear() - extendData.info.birthday_year,
qid: extendData.info.qid,
level: extendData.info.qqLevel && calcQQLevel(extendData.info.qqLevel) || 0,
age: extendData.detail.simpleInfo.baseInfo.age || 0,
qid: extendData.detail.simpleInfo.baseInfo.qid,
level: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? 0) || 0,
login_days: 0,
uid: '',
uid: ''
};
return ret;
}

View File

@@ -7,18 +7,18 @@ import { checkFileReceived, uri2local } from '@/common/utils/file';
interface Payload {
file: string,
groupCode: string
group_id: number
}
export default class SetGroupHeader extends BaseAction<Payload, any> {
actionName = ActionName.SetGroupHeader;
export default class SetGroupPortrait extends BaseAction<Payload, any> {
actionName = ActionName.SetGroupPortrait;
// 用不着复杂检测
protected async check(payload: Payload): Promise<BaseCheckResult> {
if (!payload.file || typeof payload.file != 'string' || !payload.groupCode || typeof payload.groupCode != 'string') {
if (!payload.file || typeof payload.file != 'string' || !payload.group_id || typeof payload.group_id != 'number') {
return {
valid: false,
message: 'file和groupCode字段不能为空或者类型错误',
message: 'file和group_id字段不能为空或者类型错误',
};
}
return {
@@ -34,7 +34,7 @@ export default class SetGroupHeader extends BaseAction<Payload, any> {
}
if (path) {
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断
const ret = await NTQQGroupApi.setGroupAvatar(payload.groupCode, path);
const ret = await NTQQGroupApi.setGroupAvatar(payload.group_id.toString(), path) as any;
if (!isLocal) {
fs.unlink(path, () => {
});
@@ -43,11 +43,11 @@ export default class SetGroupHeader extends BaseAction<Payload, any> {
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']}`;
// }
if (ret['result'] == 1004022) {
throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`;
} else if (ret['result'] != 0) {
throw `头像${payload.file}设置失败,未知的错误,${ret['result']}:${ret['errMsg']}`;
}
return ret;
} else {
if (!isLocal) {

View File

@@ -0,0 +1,10 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
export default class SetModelShow extends BaseAction<null, null> {
actionName = ActionName.SetModelShow;
async _handle(payload: null): Promise<null> {
return null;
}
}

View File

@@ -0,0 +1,34 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
nickname: { type: 'string' },
personal_note: { type: 'string' },
sex: { type: ['number', 'string'] },//传Sex值建议传0
},
required: ['nickname'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class SetQQProfile extends BaseAction<Payload, any | null> {
actionName = ActionName.SetQQProfile;
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const self = this.CoreContext.selfInfo;
const OldProfile = await NTQQUserApi.getUserDetailInfo(self.uid);
const ret = await NTQQUserApi.modifySelfProfile({
nick: payload.nickname,
longNick: payload?.personal_note ?? OldProfile?.longNick!,
sex: parseInt(payload?.sex ? payload?.sex.toString() : OldProfile?.sex!.toString()),
birthday: { birthday_year: OldProfile?.birthday_year!.toString(), birthday_month: OldProfile?.birthday_month!.toString(), birthday_day: OldProfile?.birthday_day!.toString() },
location: undefined,
});
return ret;
}
}

View File

@@ -5,7 +5,7 @@ import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
const SchemaData = {
type: 'object',
@@ -36,7 +36,7 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
}
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name, payload.folder_id);
await sendMsg(this.CoreContext, {
chatType: ChatType.group,
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),
}, [sendFileEle], [], true);
return null;

View File

@@ -5,7 +5,7 @@ import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
const SchemaData = {
type: 'object',
@@ -32,7 +32,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
throw `私聊${payload.user_id}不存在`;
}
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid };
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
}
throw '缺少参数 user_id';
}

View File

@@ -20,9 +20,7 @@ export default class DelEssenceMsg extends BaseAction<Payload, any> {
async _handle(payload: Payload): Promise<any> {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const msg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
if (!msg) {
throw new Error('msg not found');
}
if (!msg) throw new Error('msg not found');
return await NTQQGroupApi.removeGroupEssence(
msg.Peer.peerUid,
msg.MsgId,

View File

@@ -9,7 +9,7 @@ const SchemaData = {
group_id: { type: ['number', 'string'] },
pages: { type: 'number' },
},
required: ['group_id', 'pages'],
required: ['group_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
@@ -20,7 +20,7 @@ export class GetGroupEssence extends BaseAction<Payload, GroupEssenceMsgRet> {
async _handle(payload: Payload) {
const NTQQWebApi = this.CoreContext.apis.WebApi;
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString(), payload.pages.toString());
const ret = await NTQQWebApi.getGroupEssenceMsg(payload.group_id.toString(), (payload.pages || "0").toString());
if (!ret) {
throw new Error('获取失败');
}

View File

@@ -1,5 +1,5 @@
import { OB11Group } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -21,11 +21,8 @@ class GetGroupInfo extends BaseAction<Payload, OB11Group> {
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const group = (await NTQQGroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString());
if (group) {
return OB11Constructor.group(group);
} else {
throw `${payload.group_id}不存在`;
}
if (!group) throw `${payload.group_id}不存在`;
return OB11Constructor.group(group);
}
}

View File

@@ -1,5 +1,5 @@
import { OB11Group } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { Group } from '@/core/entities';
@@ -20,7 +20,7 @@ class GetGroupList extends BaseAction<Payload, OB11Group[]> {
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const groupList: Group[] = await NTQQGroupApi.getGroups(payload?.no_cache === true || payload.no_cache === 'true');
const groupList: Group[] = await NTQQGroupApi.getGroups(typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache);
return OB11Constructor.groups(groupList);
}
}

View File

@@ -1,8 +1,9 @@
import { OB11GroupMember } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { GroupMember } from '@/core';
const SchemaData = {
type: 'object',
@@ -23,56 +24,25 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQWebApi = this.CoreContext.apis.WebApi;
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
const isNocache = typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache;
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
if (!uid) {
throw (`Uin2Uid Error ${payload.user_id}不存在`);
}
const member = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache);
if (!member) {
throw (`群(${payload.group_id})成员${payload.user_id}不存在`);
}
try {
const info = (await NTQQUserApi.getUserDetailInfo(member.uid));
this.CoreContext.context.logger.logDebug('群成员详细信息结果', info);
Object.assign(member, info);
} catch (e) {
this.CoreContext.context.logger.logDebug('获取群成员详细信息失败, 只能返回基础信息', e);
if (!uid) throw (`Uin2Uid Error ${payload.user_id}不存在`);
const [member, info] = await Promise.allSettled([
NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache),
NTQQUserApi.getUserDetailInfo(uid),
]);
if (member.status !== 'fulfilled') throw (`群(${payload.group_id})成员${payload.user_id}不存在 ${member.reason}`);
if (info.status === 'fulfilled') {
this.CoreContext.context.logger.logDebug("群成员详细信息结果", info.value);
Object.assign(member, info.value);
} else {
this.CoreContext.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息 ${info.reason}`);
}
const date = Math.round(Date.now() / 1000);
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member);
if (!this.CoreContext.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
const SelfInfoInGroup = await NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), this.CoreContext.selfInfo.uid, isNocache);
let isPrivilege = false;
if (SelfInfoInGroup) {
isPrivilege = SelfInfoInGroup.role === 3 || SelfInfoInGroup.role === 4;
}
if (isPrivilege) {
const webGroupMembers = await NTQQWebApi.getGroupMembers(payload.group_id.toString());
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
if (webGroupMembers[i]?.uin && webGroupMembers[i].uin === retMember.user_id) {
retMember.join_time = webGroupMembers[i]?.join_time;
retMember.last_sent_time = webGroupMembers[i]?.last_speak_time;
retMember.qage = webGroupMembers[i]?.qage;
retMember.level = webGroupMembers[i]?.lv.level.toString();
}
}
} else {
const LastestMsgList = await NTQQGroupApi.getLatestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
const 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);//兜底数据 防止群管乱杀
}
}
}
} else {
// Mlikiowa V2.0.6 Refactor Todo
// retMember.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.lastSpeakTime || date.toString());
// retMember.join_time = parseInt((await getGroupMember(payload.group_id.toString(), retMember.user_id))?.joinTime || date.toString());
}
const retMember = OB11Constructor.groupMember(payload.group_id.toString(), member.value as GroupMember);
const Member = await this.CoreContext.apis.GroupApi.getGroupMember(payload.group_id.toString(), retMember.user_id);
retMember.last_sent_time = parseInt(Member?.lastSpeakTime || date.toString());
retMember.join_time = parseInt(Member?.joinTime || date.toString());
return retMember;
}
}

View File

@@ -1,5 +1,5 @@
import { OB11GroupMember } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -22,23 +22,16 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQWebApi = this.CoreContext.apis.WebApi;
const isNocache = payload.no_cache == true || payload.no_cache === 'true';
// const GroupList = await NTQQGroupApi.getGroups(isNocache);
// const group = GroupList.find(item => item.groupCode == payload.group_id);
// if (!group) {
// throw (`群${payload.group_id}不存在`);
// }
const groupMembers = await NTQQGroupApi.getGroupMembers(payload.group_id.toString());
const groupMembersArr = Array.from(groupMembers.values());
const groupMembersUids = groupMembersArr.map(e => e.uid);
let _groupMembers = groupMembersArr.map(item => {
return OB11Constructor.groupMember(payload.group_id.toString(), item);
});
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
// 转为Map 方便索引
const GroupMemberUids: string[] = [];
const date = Math.round(Date.now() / 1000);
for (let i = 0, len = _groupMembers.length; i < len; i++) {
// 保证基础数据有这个 同时避免群管插件过于依赖这个杀了
_groupMembers[i].join_time = date;
@@ -46,59 +39,31 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
}
if (!this.CoreContext.context.basicInfoWrapper.requireMinNTQQBuild('26702')) {
const selfRole = groupMembers.get(this.CoreContext.selfInfo.uid)?.role;
const isPrivilege = selfRole === 3 || selfRole === 4;
const selfRole = groupMembers.get(this.CoreContext.selfInfo.uid)?.role;
const isPrivilege = selfRole === 3 || selfRole === 4;
if (isPrivilege) {
const webGroupMembers = await NTQQWebApi.getGroupMembers(payload.group_id.toString());
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
if (!webGroupMembers[i]?.uin) {
continue;
}
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
if (MemberData) {
MemberData.join_time = webGroupMembers[i]?.join_time;
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
MemberData.qage = webGroupMembers[i]?.qage;
MemberData.level = webGroupMembers[i]?.lv.level.toString();
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
}
_groupMembers.forEach(item => {
item.last_sent_time = date;
item.join_time = date;
});
if (isPrivilege) {
const webGroupMembers = await NTQQWebApi.getGroupMembers(payload.group_id.toString());
for (let i = 0, len = webGroupMembers.length; i < len; i++) {
if (!webGroupMembers[i]?.uin) {
continue;
}
} else {
if (isNocache) {
const DateMap = await NTQQGroupApi.getGroupMemberLatestSendTimeCache(payload.group_id.toString(), groupMembersUids);//开始从本地拉取
for (const DateUin of DateMap.keys()) {
const MemberData = MemberMap.get(parseInt(DateUin));
if (MemberData) {
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);
//join_time 有基础数据兜底
}
}
} else {
_groupMembers.forEach(item => {
item.last_sent_time = date;
item.join_time = date;
});
const MemberData = MemberMap.get(webGroupMembers[i]?.uin);
if (MemberData) {
MemberData.join_time = webGroupMembers[i]?.join_time;
MemberData.last_sent_time = webGroupMembers[i]?.last_speak_time;
MemberData.qage = webGroupMembers[i]?.qage;
MemberData.level = webGroupMembers[i]?.lv.level.toString();
MemberMap.set(webGroupMembers[i]?.uin, MemberData);
}
}
} else {
// Mlikiowa V2.0.6 Refactor Todo
// _groupMembers.forEach(async item => {
// item.last_sent_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.lastSpeakTime || date.toString());
// item.join_time = parseInt((await getGroupMember(payload.group_id.toString(), item.user_id))?.joinTime || date.toString());
// });
}
// 还原索引到Array 一同返回
// let retData: any[] = [];
// for (let retMem of MemberMap.values()) {
// retMem.level = TypeConvert.toString(retMem.level) as any;
// retData.push(retMem)
// }
// _groupMembers = Array.from(retData);
_groupMembers = Array.from(MemberMap.values());
return _groupMembers;
}

View File

@@ -24,22 +24,22 @@ export class GetGroupSystemMsg extends BaseAction<void, any> {
if (SSNotify.type == 1) {
retData.InvitedRequest.push({
request_id: SSNotify.seq,
invitor_uin: await NTQQUserApi.getUinByUid(SSNotify.user1?.uid),
invitor_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
invitor_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName,
checked: SSNotify.status === 1 ? false : true,
actor: await NTQQUserApi.getUinByUid(SSNotify.user2?.uid) || 0,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
});
} else if (SSNotify.type == 7) {
retData.join_requests.push({
request_id: SSNotify.seq,
requester_uin: await NTQQUserApi.getUinByUid(SSNotify.user1?.uid),
requester_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid),
requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName,
checked: SSNotify.status === 1 ? false : true,
actor: await NTQQUserApi.getUinByUid(SSNotify.user2?.uid) || 0,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
});
}
}

View File

@@ -8,7 +8,7 @@ const SchemaData = {
properties: {
group_id: { type: ['number', 'string'] },
user_id: { type: ['number', 'string'] },
enable: { type: 'boolean' },
enable: { type: ['boolean', 'string'] },
},
required: ['group_id', 'user_id'],
} as const satisfies JSONSchema;
@@ -20,11 +20,12 @@ export default class SetGroupAdmin extends BaseAction<Payload, null> {
PayloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> {
const enable = typeof payload.enable === 'string' ? payload.enable === 'true' : !!payload.enable;
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQUserApi = this.CoreContext.apis.UserApi;
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
if (!uid) throw new Error('get Uid Error');
await NTQQGroupApi.setMemberRole(payload.group_id.toString(), uid, payload.enable ? GroupMemberRole.admin : GroupMemberRole.normal);
await NTQQGroupApi.setMemberRole(payload.group_id.toString(), uid, enable ? GroupMemberRole.admin : GroupMemberRole.normal);
return null;
}
}

View File

@@ -6,7 +6,7 @@ const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['number', 'string'] },
is_dismiss: { type: 'boolean' },
is_dismiss: { type: ['boolean', 'string'] },
},
required: ['group_id'],
} as const satisfies JSONSchema;

View File

@@ -0,0 +1,10 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
export default class GetGuildProfile extends BaseAction<null, null> {
actionName = ActionName.GetGuildProfile;
async _handle(payload: null): Promise<null> {
return null;
}
}

View File

@@ -23,7 +23,7 @@ import GoCQHTTPGetStrangerInfo from './go-cqhttp/GetStrangerInfo';
import SendLike from './user/SendLike';
import SetGroupAddRequest from './group/SetGroupAddRequest';
import SetGroupLeave from './group/SetGroupLeave';
import GetGuildList from './group/GetGuildList';
import GetGuildList from './guild/GetGuildList';
import SetFriendAddRequest from './user/SetFriendAddRequest';
import SetGroupWholeBan from './group/SetGroupWholeBan';
import SetGroupName from './group/SetGroupName';
@@ -61,7 +61,7 @@ import { TranslateEnWordToZn } from './extends/TranslateEnWordToZn';
import { SetGroupFileFolder } from './file/SetGroupFileFolder';
import { DelGroupFile } from './file/DelGroupFile';
import { DelGroupFileFolder } from './file/DelGroupFileFolder';
import { SetSelfProfile } from './extends/SetSelfProfile';
import { SetQQProfile } from './go-cqhttp/SetQQProfile';
import { ShareGroupEx, SharePeer } from './extends/ShareContact';
import { CreateCollection } from './extends/CreateCollection';
import { SetLongNick } from './extends/SetLongNick';
@@ -69,13 +69,17 @@ 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';
import SetGroupPortrait from './go-cqhttp/SetGroupPortrait';
import { FetchCustomFace } from './extends/FetchCustomFace';
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile';
import { FetchEmojiLike } from './extends/FetchEmojiLike';
import { NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot';
import GetGuildProfile from './guild/GetGuildProfile';
import SetModelShow from './go-cqhttp/SetModelShow';
import { SetInputStatus } from './extends/SetInputStatus';
import { GetCSRF } from './system/GetCSRF';
export type ActionMap = Map<string, BaseAction<any, any>>;
@@ -83,7 +87,7 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
const actionHandlers = [
new FetchEmojiLike(onebotContext, coreContext),
new GetFile(onebotContext, coreContext),
new SetSelfProfile(onebotContext, coreContext),
new SetQQProfile(onebotContext, coreContext),
new ShareGroupEx(onebotContext, coreContext),
new SharePeer(onebotContext, coreContext),
new CreateCollection(onebotContext, coreContext),
@@ -158,9 +162,13 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
new GetRecentContact(onebotContext, coreContext),
new MarkAllMsgAsRead(onebotContext, coreContext),
new GetProfileLike(onebotContext, coreContext),
new SetGroupHeader(onebotContext, coreContext),
new SetGroupPortrait(onebotContext, coreContext),
new FetchCustomFace(onebotContext, coreContext),
new GoCQHTTPUploadPrivateFile(onebotContext, coreContext),
new GetGuildProfile(onebotContext, coreContext),
new SetModelShow(onebotContext, coreContext),
new SetInputStatus(onebotContext, coreContext),
new GetCSRF(onebotContext, coreContext),
];
const actionMap = new Map();
for (const action of actionHandlers) {

View File

@@ -7,7 +7,7 @@ import { MessageUnique } from '@/common/utils/MessageUnique';
const SchemaData = {
type: 'object',
properties: {
message_id: { type: 'number' },
message_id: { type: ['number', 'string'] },
group_id: { type: ['number', 'string'] },
user_id: { type: ['number', 'string'] },
},
@@ -24,14 +24,14 @@ class ForwardSingleMsg extends BaseAction<Payload, null> {
if (!peerUid) {
throw new Error(`无法找到私聊对象${payload.user_id}`);
}
return { chatType: ChatType.friend, peerUid };
return { chatType: ChatType.KCHATTYPEC2C, peerUid };
}
return { chatType: ChatType.group, peerUid: payload.group_id!.toString() };
return { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id!.toString() };
}
async _handle(payload: Payload): Promise<null> {
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const msg = MessageUnique.getMsgIdAndPeerByShortId(payload.message_id);
const msg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
if (!msg) {
throw new Error(`无法找到消息${payload.message_id}`);
}

View File

@@ -1,9 +1,10 @@
import { OB11Message } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { RawNTMsg2Onebot } from '@/onebot/helper';
export type ReturnDataType = OB11Message
@@ -28,8 +29,8 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
if (!payload.message_id) {
throw Error('参数message_id不能为空');
}
const MsgShortId = await MessageUnique.getShortIdByMsgId(payload.message_id.toString());
const msgIdWithPeer = await MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString()));
const MsgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString());
const msgIdWithPeer = MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString()));
if (!msgIdWithPeer) {
throw ('消息不存在');
}
@@ -37,7 +38,8 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
const msg = await NTQQMsgApi.getMsgsByMsgId(
peer,
[msgIdWithPeer?.MsgId || payload.message_id.toString()]);
const retMsg = await OB11Constructor.message(this.CoreContext, msg.msgList[0], 'array');
const retMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg.msgList[0], 'array');
if (!retMsg) throw Error('消息为空');
try {
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!;
retMsg.message_seq = retMsg.message_id;

View File

@@ -23,12 +23,12 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
throw `私聊${payload.user_id}不存在`;
}
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid };
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
}
if (!payload.group_id) {
throw '缺少参数 group_id 或 user_id';
}
return { chatType: ChatType.group, peerUid: payload.group_id.toString() };
return { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
}
async _handle(payload: PlayloadType): Promise<null> {

View File

@@ -2,8 +2,8 @@ import { OB11MessageData, OB11MessageDataType, OB11MessageFileBase } from '@/one
import { uri2local } from '@/common/utils/file';
import { RequestUtil } from '@/common/utils/request';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { AtType, CustomMusicSignPostData, IdMusicSignPostData, NapCatCore, Peer, SendMessageElement } from '@/core';
import { SendMsgElementConstructor } from '@/onebot/helper/msg';
import { AtType, ChatType, CustomMusicSignPostData, IdMusicSignPostData, NapCatCore, Peer, SendMessageElement } from '@/core';
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
import { NapCatOneBot11Adapter } from '@/onebot';
export type MessageContext = {
@@ -17,14 +17,21 @@ async function handleOb11FileLikeMessage(
{ data: inputdata }: OB11MessageFileBase,
{ deleteAfterSentFiles }: MessageContext,
) {
//有的奇怪的框架将url作为参数 而不是file 此时优先url 同时注意可能传入的是非file://开头的目录 By Mlikiowa
//inputdata?.url || inputdata.file
const isBlankUrl = !inputdata.url || inputdata.url === '';
const isBlankFile = !inputdata.file || inputdata.file === '';
if (isBlankUrl && isBlankFile) {
coreContext.context.logger.logError('文件消息缺少参数', inputdata);
throw Error('文件消息缺少参数');
}
const fileOrUrl = (isBlankUrl ? inputdata.file : inputdata.url) || "";
const {
path,
isLocal,
fileName,
errMsg,
success,
} = (await uri2local(coreContext.NapCatTempPath, inputdata?.url || inputdata.file));
} = (await uri2local(coreContext.NapCatTempPath, fileOrUrl));
if (!success) {
coreContext.context.logger.logError('文件下载失败', errMsg);
@@ -51,15 +58,18 @@ const _handlers: {
[OB11MessageDataType.text]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { text } }) => SendMsgElementConstructor.text(coreContext, text),
[OB11MessageDataType.at]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { qq: atQQ } }, context) => {
if (!context.peer) return undefined;
if (!context.peer || context.peer.chatType == ChatType.KCHATTYPEC2C) return undefined;
if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员');
// then the qq is a group member
// Mlikiowa V2.0.6 Refactor Todo
const uid = await coreContext.apis.UserApi.getUidByUinV2(atQQ);
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQUserApi = coreContext.apis.UserApi;
const atMember = await NTQQGroupApi.getGroupMember(context.peer.peerUid, atQQ);
if (atMember) {
return SendMsgElementConstructor.at(coreContext, atQQ, atMember.uid, AtType.atUser, atMember.nick || atMember.cardName);
}
const uid = await NTQQUserApi.getUidByUinV2(`${atQQ}`);
if (!uid) throw new Error('Get Uid Error');
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, '');
let info = await NTQQUserApi.getUserDetailInfo(uid);
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, info.nick || '');
},
[OB11MessageDataType.reply]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { id } }) => {
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
@@ -161,7 +171,7 @@ const _handlers: {
} else {
postData = data;
}
// Mlikiowa V2.0.6 Refactor Todo
// Mlikiowa V2.2.5 Refactor Todo
const signUrl = obContext.configLoader.configData.musicSignUrl;
if (!signUrl) {
if (data.type === 'qq') {
@@ -225,7 +235,7 @@ export default async function createSendElements(
callResultList.push(callResult);
}
const ret = await Promise.all(callResultList);
const sendElements: SendMessageElement[] = ret.filter(ele => ele) as SendMessageElement[];
const sendElements: SendMessageElement[] = ret.filter(ele => !!ele);
return { sendElements, deleteAfterSentFiles };
}

View File

@@ -7,7 +7,7 @@ import { NapCatOneBot11Adapter } from '@/onebot';
async function cloneMsg(coreContext: NapCatCore, msg: RawMessage): Promise<RawMessage | undefined> {
const selfPeer = {
chatType: ChatType.friend,
chatType: ChatType.KCHATTYPEC2C,
peerUid: coreContext.selfInfo.uid,
};
const logger = coreContext.context.logger;
@@ -34,7 +34,7 @@ async function cloneMsg(coreContext: NapCatCore, msg: RawMessage): Promise<RawMe
export async function handleForwardNode(coreContext: NapCatCore, obContext: NapCatOneBot11Adapter, destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> {
const NTQQMsgApi = coreContext.apis.MsgApi;
const selfPeer = {
chatType: ChatType.friend,
chatType: ChatType.KCHATTYPEC2C,
peerUid: coreContext.selfInfo.uid,
};
let nodeMsgIds: string[] = [];

View File

@@ -88,23 +88,45 @@ async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg,
// This function determines the type of message by the existence of user_id / group_id,
// not message_type.
// This redundant design of Ob11 here should be blamed.
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQFriendApi = coreContext.apis.FriendApi;
const NTQQUserApi = coreContext.apis.UserApi;
const NTQQMsgApi = coreContext.apis.MsgApi;
if ((contextMode === ContextMode.Group || contextMode === ContextMode.Normal) && payload.group_id) {
const group = (await NTQQGroupApi.getGroups()).find(e => e.groupCode == payload.group_id?.toString());
return {
chatType: ChatType.group,
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),
};
}
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
const Uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const isBuddy = await NTQQFriendApi.isBuddy(Uid!);
//console.log("[调试代码] UIN:", payload.user_id, " UID:", Uid, " IsBuddy:", isBuddy);
if (!Uid) throw '无法获取用户信息';
const isBuddy = await NTQQFriendApi.isBuddy(Uid);
if (!isBuddy) {
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, Uid);
if (ret.tmpChatInfo?.groupCode) {
return {
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: Uid,
guildId: '',
};
}
if (payload.group_id) {
return {
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: Uid,
guildId: payload.group_id.toString(),
};
}
return {
chatType: ChatType.KCHATTYPEC2C,
peerUid: Uid!,
guildId: '',
};
}
return {
chatType: isBuddy ? ChatType.friend : ChatType.temp,
chatType: ChatType.KCHATTYPEC2C,
peerUid: Uid!,
guildId: '',
};
}
throw '请指定 group_id 或 user_id';
@@ -133,26 +155,22 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素',
};
}
// if (payload.message_type !== 'private' && payload.group_id && !(await getGroup(payload.group_id))) {
// return { valid: false, message: `群${payload.group_id}不存在` };
// }
if (payload.user_id && payload.message_type !== 'group') {
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const isBuddy = await NTQQFriendApi.isBuddy(uid!);
// 此处有问题
if (!isBuddy) {
//return { valid: false, message: '异常消息' };
}
if (!isBuddy) { }
}
return { valid: true };
}
async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> {
if (payload.message_type === 'group') this.contextMode = ContextMode.Group;
if (payload.message_type === 'private') this.contextMode = ContextMode.Private;
const peer = await createContext(this.CoreContext, payload, this.contextMode);
const messages = normalize(
payload.message,
payload.auto_escape === true || payload.auto_escape === 'true',
typeof payload.auto_escape === 'string' ? payload.auto_escape === 'true' : !!payload.auto_escape
);
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {

View File

@@ -0,0 +1,12 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
export class GetCSRF extends BaseAction<any, any> {
actionName = ActionName.GetCSRF;
async _handle(payload: any) {
return {
token: "",
};
}
}

View File

@@ -1,5 +1,5 @@
import { OB11User } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';

View File

@@ -91,7 +91,7 @@ export enum ActionName {
GetOnlineClient = 'get_online_clients',
OCRImage = 'ocr_image',
IOCRImage = '.ocr_image',
SetSelfProfile = 'set_self_profile',
SetQQProfile = 'set_qq_profile',
CreateCollection = 'create_collection',
GetCollectionList = 'get_collection_list',
SetLongNick = 'set_self_longnick',
@@ -100,9 +100,13 @@ export enum ActionName {
GetRecentContact = 'get_recent_contact',
_MarkAllMsgAsRead = '_mark_all_as_read',
GetProfileLike = 'get_profile_like',
SetGroupHeader = 'set_group_head',
SetGroupPortrait = 'set_group_portrait',
FetchCustomFace = 'fetch_custom_face',
GOCQHTTP_UploadPrivateFile = 'upload_private_file',
TestApi01 = 'test_api_01',
FetchEmojiLike = 'fetch_emoji_like'
FetchEmojiLike = 'fetch_emoji_like',
GetGuildProfile = "get_guild_service_profile",
SetModelShow = "_set_model_show",
SetInputStatus = "set_input_status",
GetCSRF = "get_csrf_token",
}

View File

@@ -67,7 +67,7 @@ export class GetCookies extends BaseAction<Payload, Response> {
const cookiesObject = await NTQQUserApi.getCookies(payload.domain);
//把获取到的cookiesObject转换成 k=v; 格式字符串拼接在一起
const cookies = Object.entries(cookiesObject).map(([key, value]) => `${key}=${value}`).join('; ');
const bkn = NTQQWebApi.getBknFromCookie(cookiesObject.p_skey);
const bkn = cookiesObject?.skey ? NTQQWebApi.getBknFromCookie(cookiesObject) : '';
return { cookies, bkn };
}
}

View File

@@ -1,5 +1,5 @@
import { OB11User } from '../../types';
import { OB11Constructor } from '../../helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -20,6 +20,6 @@ export default class GetFriendList extends BaseAction<Payload, OB11User[]> {
async _handle(payload: Payload) {
//全新逻辑
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
return OB11Constructor.friendsV2(await NTQQFriendApi.getBuddyV2(payload?.no_cache === true || payload?.no_cache === 'true'));
return OB11Constructor.friendsV2(await NTQQFriendApi.getBuddyV2(typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache));
}
}

View File

@@ -1,7 +1,8 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { OB11Constructor } from '@/onebot/helper/data';
import { OB11Constructor } from '@/onebot/helper/converter';
import { RawNTMsg2Onebot } from '@/onebot/helper';
const SchemaData = {
type: 'object',
@@ -24,7 +25,7 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
if (FastMsg.msgList.length > 0) {
//扩展ret.info.changedList
const lastestMsg = await OB11Constructor.message(this.CoreContext, FastMsg.msgList[0], 'array');
const lastestMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext,FastMsg.msgList[0], 'array');
return {
lastestMsg: lastestMsg,
peerUin: t.peerUin,

View File

@@ -1,6 +1,7 @@
import { NapCatCore } from '@/core';
import { GrayTipElement, NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot';
import { OB11FriendPokeEvent } from '../event/notice/OB11PokeEvent';
export class OneBotFriendApi {
obContext: NapCatOneBot11Adapter;
@@ -10,4 +11,22 @@ export class OneBotFriendApi {
this.obContext = obContext;
this.coreContext = coreContext;
}
//使用前预先判断 busiId 1061
async parsePrivatePokeEvent(grayTipElement: GrayTipElement) {
const NTQQUserApi = this.coreContext.apis.UserApi;
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
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(
this.coreContext,
parseInt((await NTQQUserApi.getUinByUidV2(pokedetail[0].uid))!),
parseInt((await NTQQUserApi.getUinByUidV2(pokedetail[1].uid))!),
pokedetail
);
}
return undefined;
}
}

View File

@@ -1,6 +1,11 @@
import { NapCatCore } from '@/core';
import { ChatType, GrayTipElement, NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot';
import { OB11GroupBanEvent } from '../event/notice/OB11GroupBanEvent';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { OB11GroupDecreaseEvent } from '../event/notice/OB11GroupDecreaseEvent';
import fastXmlParser from 'fast-xml-parser';
import { OB11GroupMsgEmojiLikeEvent } from '../event/notice/OB11MsgEmojiLikeEvent';
import { MessageUnique } from '@/common/utils/MessageUnique';
export class OneBotGroupApi {
obContext: NapCatOneBot11Adapter;
@@ -10,4 +15,133 @@ export class OneBotGroupApi {
this.obContext = obContext;
this.coreContext = coreContext;
}
async parseGroupBanEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const groupElement = grayTipElement?.groupElement;
const NTQQGroupApi = this.coreContext.apis.GroupApi;
if (!groupElement?.shutUp) return undefined;
const memberUid = groupElement.shutUp!.member.uid;
const adminUid = groupElement.shutUp!.admin.uid;
let memberUin: string = '';
let duration = parseInt(groupElement.shutUp!.duration);
const subType: 'ban' | 'lift_ban' = duration > 0 ? 'ban' : 'lift_ban';
if (memberUid) {
memberUin = (await NTQQGroupApi.getGroupMember(GroupCode, memberUid))?.uin || '';
} else {
memberUin = '0'; // 0表示全员禁言
if (duration > 0) {
duration = -1;
}
}
const adminUin = (await NTQQGroupApi.getGroupMember(GroupCode, adminUid))?.uin;
if (memberUin && adminUin) {
return new OB11GroupBanEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(memberUin),
parseInt(adminUin),
duration,
subType
);
}
return undefined;
}
async parseGroupIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
this.coreContext.context.logger.logDebug('收到新人被邀请进群消息', grayTipElement);
const xmlElement = grayTipElement.xmlElement;
if (xmlElement?.content) {
const regex = /jp="(\d+)"/g;
const matches = [];
let match = null;
while ((match = regex.exec(xmlElement.content)) !== null) {
matches.push(match[1]);
}
// log("新人进群匹配到的QQ号", matches)
if (matches.length === 2) {
const [inviter, invitee] = matches;
return new OB11GroupIncreaseEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(invitee),
parseInt(inviter),
'invite'
);
}
}
return undefined;
}
async parseGroupMemberIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const NTQQGroupApi = this.coreContext.apis.GroupApi;
const groupElement = grayTipElement?.groupElement;
if (!groupElement) return undefined;
const member = await NTQQGroupApi.getGroupMember(GroupCode, groupElement.memberUid);
const memberUin = member?.uin;
const adminMember = await NTQQGroupApi.getGroupMember(GroupCode, groupElement.adminUid);
if (memberUin) {
const operatorUin = adminMember?.uin || memberUin;
return new OB11GroupIncreaseEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(memberUin),
parseInt(operatorUin)
);
}
return undefined;
}
async parseGroupKickEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const NTQQGroupApi = this.coreContext.apis.GroupApi;
const NTQQUserApi = this.coreContext.apis.UserApi;
const groupElement = grayTipElement?.groupElement;
if (!groupElement) return undefined;
const adminUin = (await NTQQGroupApi.getGroupMember(GroupCode, groupElement.adminUid))?.uin || (await NTQQUserApi.getUidByUinV2(groupElement.adminUid));
if (adminUin) {
return new OB11GroupDecreaseEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(this.coreContext.selfInfo.uin),
parseInt(adminUin),
'kick_me'
);
}
return undefined;
}
async parseGroupEmjioLikeEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const NTQQMsgApi = this.coreContext.apis.MsgApi;
const emojiLikeData = new fastXmlParser.XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '',
}).parse(grayTipElement.xmlElement.content);
this.coreContext.context.logger.logDebug('收到表情回应我的消息', emojiLikeData);
try {
const senderUin = emojiLikeData.gtip.qq.jp;
const msgSeq = emojiLikeData.gtip.url.msgseq;
const emojiId = emojiLikeData.gtip.face.id;
const peer = {
chatType: ChatType.KCHATTYPEGROUP,
guildId: '',
peerUid: GroupCode
};
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, msgSeq)).msgList;
if (replyMsgList.length < 1) {
return;
}
const replyMsg = replyMsgList.filter(e => e.msgSeq == msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
//console.log("表情回应消息长度检测", msgSeq, replyMsg.elements);
if (!replyMsg) throw new Error('找不到回应消息');
return new OB11GroupMsgEmojiLikeEvent(
this.coreContext,
parseInt(GroupCode),
parseInt(senderUin),
MessageUnique.getShortIdByMsgId(replyMsg.msgId)!,
[{
emoji_id: emojiId,
count: 1,
}],
);
} catch (e: any) {
this.coreContext.context.logger.logError('解析表情回应消息失败', e.stack);
}
return undefined;
}
}

View File

@@ -1,3 +1,4 @@
export * from './friend';
export * from './group';
export * from './user';
export * from './user';
export * from './msg';

361
src/onebot/api/msg.ts Normal file
View File

@@ -0,0 +1,361 @@
import { UUIDConverter } from '@/common/utils/helper';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { AtType, FaceIndex, MessageElement, NapCatCore, RawMessage } from '@/core';
import { NapCatOneBot11Adapter, OB11MessageData, OB11MessageDataType } from '@/onebot';
import { RawNTMsg2Onebot } from '../helper';
export class OneBotMsgApi {
obContext: NapCatOneBot11Adapter;
coreContext: NapCatCore;
constructor(obContext: NapCatOneBot11Adapter, coreContext: NapCatCore) {
this.obContext = obContext;
this.coreContext = coreContext;
}
async parseFileElement(msg: RawMessage, element: MessageElement) {
const fileElement = element.fileElement;
if (!fileElement) return undefined;
const NTQQFileApi = this.coreContext.apis.FileApi;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.file;
message_data['data']['file'] = fileElement.fileName;
message_data['data']['path'] = fileElement.filePath;
message_data['data']['url'] = fileElement.filePath;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = fileElement.fileSize;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
fileElement.fileSize,
fileElement.fileName
);
return message_data;
}
async parseTextElemntWithAt(msg: RawMessage, element: MessageElement) {
const textElement = element.textElement;
if (!textElement) return undefined;
const NTQQUserApi = this.coreContext.apis.UserApi;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
let qq: `${number}` | 'all';
let name: string | undefined;
if (textElement.atType == AtType.atAll) {
qq = 'all';
} else {
const { atNtUid, content } = textElement;
let atQQ = textElement.atUid;
if (!atQQ || atQQ === '0') {
atQQ = await NTQQUserApi.getUinByUidV2(atNtUid);
}
if (atQQ) {
qq = atQQ as `${number}`;
name = content.replace('@', '');
}
}
message_data = {
type: OB11MessageDataType.at,
data: {
qq: qq!,
//name,
},
};
return message_data;
}
async parseTextElement(msg: RawMessage, element: MessageElement) {
const textElement = element.textElement;
if (!textElement) return undefined;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.text;
let text = textElement.content;
if (!text.trim()) {
return false;
}
// 兼容 9.7.x 换行符
if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) {
text = text.replace(/\r/g, '\n');
}
message_data['data']['text'] = text;
return message_data;
}
async parsePicElement(msg: RawMessage, element: MessageElement) {
const picElement = element.picElement;
if (!picElement) return undefined;
const NTQQFileApi = this.coreContext.apis.FileApi;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.image;
// message_data["data"]["file"] = element.picElement.sourcePath
message_data['data']['file'] = picElement.fileName;
message_data['data']['subType'] = picElement.picSubType;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
// message_data["data"]["path"] = element.picElement.sourcePath
try {
message_data['data']['url'] = await NTQQFileApi.getImageUrl(picElement);
} catch (e: any) {
this.coreContext.context.logger.logError('获取图片url失败', e.stack);
}
//console.log(message_data['data']['url'])
// message_data["data"]["file_id"] = element.picElement.fileUuid
message_data['data']['file_size'] = picElement.fileSize;
return message_data;
}
async parseMarketFaceElement(msg: RawMessage, element: MessageElement) {
const NTQQFileApi = this.coreContext.apis.FileApi;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.image;
message_data['data']['file'] = 'marketface';
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['path'] = element.elementId;
message_data['data']['url'] = element.elementId;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
'0',
'marketface'
);
return message_data;
}
async parseReplyElement(msg: RawMessage, element: MessageElement) {
const replyElement = element.replyElement;
if (!replyElement) return undefined;
const NTQQMsgApi = this.coreContext.apis.MsgApi;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.reply;
//log("收到回复消息", element.replyElement);
try {
const records = msg.records.find(msgRecord => msgRecord.msgId === replyElement?.sourceMsgIdInRecords);
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
let replyMsg: RawMessage | undefined;
if (!records) throw new Error('找不到回复消息');
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, replyElement.replayMsgSeq, 1, true, true)).msgList.find(msg => msg.msgRandom === records.msgRandom);
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, replyElement.replayMsgSeq)).msgList[0];
}
if (msg.peerUin == '284840486') {
//合并消息内侧 消息具体定位不到
}
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList;
if (replyMsgList.length < 1) {
throw new Error('回复消息消息验证失败');
}
replyMsg = replyMsgList.filter(e => e.msgSeq == records.msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
}
message_data['data']['id'] = MessageUnique.createMsg({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, replyMsg.msgId)?.toString();
//log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId)
} catch (e: any) {
message_data['type'] = 'unknown' as any;
message_data['data'] = undefined;
this.coreContext.context.logger.logError('获取不到引用的消息', e.stack, replyElement.replayMsgSeq);
return undefined;
}
return message_data;
}
async parseVideoElement(msg: RawMessage, element: MessageElement) {
const videoElement = element.videoElement;
if (!videoElement) return undefined;
const NTQQFileApi = this.coreContext.apis.FileApi;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
//读取视频链接并兜底
let videoUrl; //Array
if (msg.peerUin === '284840486') {
//合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Taged TODO
}
try {
videoUrl = await NTQQFileApi.getVideoUrl({
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '0',
}, msg.msgId, element.elementId);
} catch (error) {
videoUrl = undefined;
}
//读取在线URL
let videoDownUrl = undefined;
if (videoUrl) {
const videoDownUrlTemp = videoUrl.find((url) => {
return !!url.url;
});
if (videoDownUrlTemp) {
videoDownUrl = videoDownUrlTemp.url;
}
}
//开始兜底
if (!videoDownUrl) {
videoDownUrl = videoElement.filePath;
}
message_data['type'] = OB11MessageDataType.video;
message_data['data']['file'] = videoElement.fileName;
message_data['data']['path'] = videoDownUrl;
message_data['data']['url'] = videoDownUrl;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = videoElement.fileSize;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
videoElement.fileSize || '0',
videoElement.fileName
);
return message_data;
}
async parsePTTElement(msg: RawMessage, element: MessageElement) {
const pttElement = element.pttElement;
if (!pttElement) return undefined;
const NTQQFileApi = this.coreContext.apis.FileApi;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.voice;
message_data['data']['file'] = pttElement.fileName;
message_data['data']['path'] = pttElement.filePath;
//message_data['data']['file_id'] = element.pttElement.fileUuid;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = pttElement.fileSize;
await NTQQFileApi.addFileCache({
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
pttElement.fileSize || '0',
pttElement.fileUuid || ''
);
//以uuid作为文件名
return message_data;
}
async parseFaceElement(msg: RawMessage, element: MessageElement) {
const faceElement = element.faceElement;
if (!faceElement) return undefined;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
const faceId = faceElement.faceIndex;
if (faceId === FaceIndex.dice) {
message_data['type'] = OB11MessageDataType.dice;
message_data['data']['result'] = faceElement.resultId;
} else if (faceId === FaceIndex.RPS) {
message_data['type'] = OB11MessageDataType.RPS;
message_data['data']['result'] = faceElement.resultId;
} else {
message_data['type'] = OB11MessageDataType.face;
message_data['data']['id'] = faceElement.faceIndex.toString();
}
return message_data;
}
async parseMultForwardElement(msg: RawMessage, element: MessageElement, messagePostFormat: any) {
const NTQQMsgApi = this.coreContext.apis.MsgApi;
const faceElement = element.multiForwardMsgElement;
if (!faceElement) return undefined;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.forward;
message_data['data']['id'] = msg.msgId;
const ParentMsgPeer = msg.parentMsgPeer ?? {
chatType: msg.chatType,
guildId: '',
peerUid: msg.peerUid,
};
//判断是否在合并消息内
msg.parentMsgIdList = msg.parentMsgIdList ?? [];
//首次列表不存在则开始创建
msg.parentMsgIdList.push(msg.msgId);
//let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2];
//加入自身MsgId
const MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList;
//拉取下级消息
if (!MultiMsgs) return undefined;
//拉取失败则跳过
message_data['data']['content'] = [];
for (const MultiMsg of MultiMsgs) {
//对每条拉取的消息传递ParentMsgPeer修正Peer
MultiMsg.parentMsgPeer = ParentMsgPeer;
MultiMsg.parentMsgIdList = msg.parentMsgIdList;
MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId); //该ID仅用查看 无法调用
const msgList = await RawNTMsg2Onebot(this.coreContext, this.obContext, MultiMsg, messagePostFormat);
if (!msgList) continue;
message_data['data']['content'].push(msgList);
//console.log("合并消息", msgList);
}
return message_data;
}
async parseArkElement(msg: RawMessage, element: MessageElement) {
const arkElement = element.arkElement;
if (!arkElement) return undefined;
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
message_data['type'] = OB11MessageDataType.json;
message_data['data']['data'] = arkElement.bytesData;
return message_data;
}
}

View File

@@ -6,14 +6,14 @@ export class OB11InputStatusEvent extends OB11BaseNoticeEvent {
notice_type = 'notify';
sub_type = 'input_status';
status_text = '对方正在输入...';
eventType = 1;
event_type = 1;
user_id = 0;
group_id = 0;
constructor(core: NapCatCore, user_id: number, eventType: number, status_text: string) {
super(core);
this.user_id = user_id;
this.eventType = eventType;
this.event_type = eventType;
this.status_text = status_text;
}
}

View File

@@ -0,0 +1,113 @@
import { calcQQLevel } from '@/common/utils/helper';
import { SelfInfo, FriendV2, Friend, Sex, GroupMember, User, Group } from '@/core';
import { OB11User, OB11GroupMemberRole, OB11UserSex, OB11GroupMember, OB11Group } from '../types';
export class OB11Constructor {
static selfInfo(selfInfo: SelfInfo): OB11User {
return {
user_id: parseInt(selfInfo.uin),
nickname: selfInfo.nick,
};
}
static friendsV2(friends: FriendV2[]): OB11User[] {
const data: OB11User[] = [];
friends.forEach(friend => {
const sexValue = this.sex(friend.baseInfo.sex!);
data.push({
...friend.baseInfo,
...friend.coreInfo,
user_id: parseInt(friend.coreInfo.uin),
nickname: friend.coreInfo.nick,
remark: friend.coreInfo.nick,
sex: sexValue,
level: 0,
});
});
return data;
}
static friends(friends: Friend[]): OB11User[] {
const data: OB11User[] = [];
friends.forEach(friend => {
const sexValue = this.sex(friend.sex!);
data.push({
user_id: parseInt(friend.uin),
nickname: friend.nick,
remark: friend.remark,
sex: sexValue,
level: 0,
});
});
return data;
}
static groupMemberRole(role: number): OB11GroupMemberRole | undefined {
return {
4: OB11GroupMemberRole.owner,
3: OB11GroupMemberRole.admin,
2: OB11GroupMemberRole.member,
}[role];
}
static sex(sex: Sex): OB11UserSex {
const sexMap = {
[Sex.male]: OB11UserSex.male,
[Sex.female]: OB11UserSex.female,
[Sex.unknown]: OB11UserSex.unknown,
};
return sexMap[sex] || OB11UserSex.unknown;
}
static groupMember(group_id: string, member: GroupMember): OB11GroupMember {
return {
group_id: parseInt(group_id),
user_id: parseInt(member.uin),
nickname: member.nick,
card: member.cardName,
sex: OB11Constructor.sex(member.sex!),
age: member.age ?? 0,
area: '',
level: '0',
qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0,
join_time: 0, // 暂时没法获取
last_sent_time: 0, // 暂时没法获取
title_expire_time: 0,
unfriendly: false,
card_changeable: true,
is_robot: member.isRobot,
shut_up_timestamp: member.shutUpTime,
role: OB11Constructor.groupMemberRole(member.role),
title: member.memberSpecialTitle || '',
};
}
static stranger(user: User): OB11User {
//logDebug('construct ob11 stranger', user);
return {
...user,
user_id: parseInt(user.uin),
nickname: user.nick,
sex: OB11Constructor.sex(user.sex!),
age: 0,
qid: user.qid,
login_days: 0,
level: user.qqLevel && calcQQLevel(user.qqLevel) || 0,
};
}
static group(group: Group): OB11Group {
return {
group_id: parseInt(group.groupCode),
group_name: group.groupName,
member_count: group.memberCount,
max_member_count: group.maxMember,
};
}
static groups(groups: Group[]): OB11Group[] {
return groups.map(OB11Constructor.group);
}
}

View File

@@ -79,8 +79,4 @@ export function encodeCQCode(data: OB11MessageData) {
}
result += ']';
return result;
}
// const result = parseCQCode("[CQ:at,qq=114514]早上好啊[CQ:image,file=http://baidu.com/1.jpg,type=show,id=40004]")
// const result = parseCQCode("好好好")
// console.log(JSON.stringify(result))
}

View File

@@ -1,746 +0,0 @@
import fastXmlParser from 'fast-xml-parser';
import {
OB11Group,
OB11GroupMember,
OB11GroupMemberRole,
OB11Message,
OB11MessageData,
OB11MessageDataType,
OB11User,
OB11UserSex,
} from '../types';
import {
AtType,
ChatType,
FaceIndex,
Friend,
FriendV2,
GrayTipElementSubType,
Group,
GroupMember,
Peer,
RawMessage,
SelfInfo,
Sex,
TipGroupElementType,
User,
VideoElement,
} from '@/core/entities';
import { EventType } from '../event/OB11BaseEvent';
import { encodeCQCode } from './cqcode';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { OB11GroupBanEvent } from '../event/notice/OB11GroupBanEvent';
import { OB11GroupUploadNoticeEvent } from '../event/notice/OB11GroupUploadNoticeEvent';
import { OB11GroupNoticeEvent } from '../event/notice/OB11GroupNoticeEvent';
import { calcQQLevel, sleep, UUIDConverter } from '@/common/utils/helper';
import { OB11GroupTitleEvent } from '../event/notice/OB11GroupTitleEvent';
import { OB11GroupDecreaseEvent } from '../event/notice/OB11GroupDecreaseEvent';
import { OB11GroupMsgEmojiLikeEvent } from '@/onebot/event/notice/OB11MsgEmojiLikeEvent';
import { OB11FriendPokeEvent, OB11GroupPokeEvent } from '../event/notice/OB11PokeEvent';
import { OB11FriendAddNoticeEvent } from '../event/notice/OB11FriendAddNoticeEvent';
import { OB11BaseNoticeEvent } from '../event/notice/OB11BaseNoticeEvent';
import { OB11GroupEssenceEvent } from '../event/notice/OB11GroupEssenceEvent';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { NapCatCore } from '@/core';
export class OB11Constructor {
static async message(core: NapCatCore, msg: RawMessage, messagePostFormat: any): Promise<OB11Message> {
const NTQQGroupApi = core.apis.GroupApi;
const NTQQUserApi = core.apis.UserApi;
const NTQQFileApi = core.apis.FileApi;
const NTQQMsgApi = core.apis.MsgApi;
const logger = core.context.logger;
const resMsg: OB11Message = {
self_id: parseInt(core.selfInfo.uin),
user_id: parseInt(msg.senderUin!),
time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.id!,
message_seq: msg.id!,
real_id: msg.id!,
message_type: msg.chatType == ChatType.group ? 'group' : 'private',
sender: {
user_id: parseInt(msg.senderUin || '0'),
nickname: msg.sendNickName,
card: msg.sendMemberName || '',
},
raw_message: '',
font: 14,
sub_type: 'friend',
message: messagePostFormat === 'string' ? '' : [],
message_format: messagePostFormat === 'string' ? 'string' : 'array',
post_type: core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
};
if (msg.chatType == ChatType.group) {
resMsg.sub_type = 'normal'; // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼
resMsg.group_id = parseInt(msg.peerUin);
//直接去QQNative取
const memberList = await NTQQGroupApi.getGroupMembers(msg.peerUin);
const member = memberList.get(msg.senderUin!);
if (member) {
resMsg.sender.role = OB11Constructor.groupMemberRole(member.role);
resMsg.sender.nickname = member.nick;
}
} else if (msg.chatType == ChatType.friend) {
resMsg.sub_type = 'friend';
resMsg.sender.nickname = (await NTQQUserApi.getUserDetailInfo(msg.senderUid)).nick;
//const user = await NTQQUserApi.getUserDetailInfoByUin(msg.senderUin!);
//resMsg.sender.nickname = user.info.nick;
} else if (msg.chatType == ChatType.temp) {
resMsg.sub_type = 'group';
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.temp, msg.senderUid);
if (ret.result === 0) {
resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode);
resMsg.sender.nickname = ret.tmpChatInfo!.fromNick;
} else {
resMsg.group_id = 284840486;//兜底数据
resMsg.sender.nickname = "临时会话";
}
}
for (const element of msg.elements) {
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
if (element.textElement && element.textElement?.atType !== AtType.notAt) {
let qq: `${number}` | 'all';
let name: string | undefined;
if (element.textElement.atType == AtType.atAll) {
qq = 'all';
} else {
const { atNtUid, content } = element.textElement;
let atQQ = element.textElement.atUid;
if (!atQQ || atQQ === '0') {
atQQ = await NTQQUserApi.getUinByUid(atNtUid);
}
if (atQQ) {
qq = atQQ as `${number}`;
name = content.replace('@', '');
}
}
message_data = {
type: OB11MessageDataType.at,
data: {
qq: qq!,
name,
},
};
} else if (element.textElement) {
message_data['type'] = OB11MessageDataType.text;
let text = element.textElement.content;
if (!text.trim()) {
continue;
}
// 兼容 9.7.x 换行符
if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) {
text = text.replace(/\r/g, '\n');
}
message_data['data']['text'] = text;
} else if (element.replyElement) {
message_data['type'] = OB11MessageDataType.reply;
//log("收到回复消息", element.replyElement);
try {
const records = msg.records.find(msgRecord => msgRecord.msgId === element.replyElement.sourceMsgIdInRecords);
const peer = {
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
};
let replyMsg: RawMessage | undefined;
if (!records) throw new Error('找不到回复消息');
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, element.replyElement.replayMsgSeq, 1, true, true)).msgList[0];
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0];
}
if (msg.peerUin == '284840486') {
//合并消息内侧 消息具体定位不到
}
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
throw new Error('回复消息消息验证失败');
}
message_data['data']['id'] = MessageUnique.createMsg({
peerUid: msg.peerUid,
guildId: '',
chatType: msg.chatType,
}, replyMsg.msgId)?.toString();
//log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId)
} catch (e: any) {
message_data['type'] = 'unknown' as any;
message_data['data'] = undefined;
logger.logError('获取不到引用的消息', e.stack, element.replyElement.replayMsgSeq);
}
} else if (element.picElement) {
message_data['type'] = OB11MessageDataType.image;
// message_data["data"]["file"] = element.picElement.sourcePath
message_data['data']['file'] = element.picElement.fileName;
message_data['data']['subType'] = element.picElement.picSubType;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
// message_data["data"]["path"] = element.picElement.sourcePath
try {
message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement);
} catch (e: any) {
logger.logError('获取图片url失败', e.stack);
}
//console.log(message_data['data']['url'])
// message_data["data"]["file_id"] = element.picElement.fileUuid
message_data['data']['file_size'] = element.picElement.fileSize;
} else if (element.fileElement) {
const FileElement = element.fileElement;
message_data['type'] = OB11MessageDataType.file;
message_data['data']['file'] = FileElement.fileName;
message_data['data']['path'] = FileElement.filePath;
message_data['data']['url'] = FileElement.filePath;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = FileElement.fileSize;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
FileElement.fileSize,
FileElement.fileName,
);
} else if (element.videoElement) {
const videoElement: VideoElement = element.videoElement;
//读取视频链接并兜底
let videoUrl;//Array
if (msg.peerUin === '284840486') {
//合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Taged TODO
}
try {
videoUrl = await NTQQFileApi.getVideoUrl({
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '0',
}, msg.msgId, element.elementId);
} catch (error) {
videoUrl = undefined;
}
//读取在线URL
let videoDownUrl = undefined;
if (videoUrl) {
const videoDownUrlTemp = videoUrl.find((url) => {
return !!url.url;
});
if (videoDownUrlTemp) {
videoDownUrl = videoDownUrlTemp.url;
}
}
//开始兜底
if (!videoDownUrl) {
videoDownUrl = videoElement.filePath;
}
message_data['type'] = OB11MessageDataType.video;
message_data['data']['file'] = videoElement.fileName;
message_data['data']['path'] = videoDownUrl;
message_data['data']['url'] = videoDownUrl;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = videoElement.fileSize;
await NTQQFileApi.addFileCache(
{
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
videoElement.fileSize || '0',
videoElement.fileName,
);
} else if (element.pttElement) {
message_data['type'] = OB11MessageDataType.voice;
message_data['data']['file'] = element.pttElement.fileName;
message_data['data']['path'] = element.pttElement.filePath;
//message_data['data']['file_id'] = element.pttElement.fileUuid;
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
message_data['data']['file_size'] = element.pttElement.fileSize;
await NTQQFileApi.addFileCache({
peerUid: msg.peerUid,
chatType: msg.chatType,
guildId: '',
},
msg.msgId,
msg.msgSeq,
msg.senderUid,
element.elementId,
element.elementType.toString(),
element.pttElement.fileSize || '0',
element.pttElement.fileUuid || '',
);
//以uuid作为文件名
} else if (element.arkElement) {
message_data['type'] = OB11MessageDataType.json;
message_data['data']['data'] = element.arkElement.bytesData;
} else if (element.faceElement) {
const faceId = element.faceElement.faceIndex;
if (faceId === FaceIndex.dice) {
message_data['type'] = OB11MessageDataType.dice;
message_data['data']['result'] = element.faceElement.resultId;
} else if (faceId === FaceIndex.RPS) {
message_data['type'] = OB11MessageDataType.RPS;
message_data['data']['result'] = element.faceElement.resultId;
} else {
message_data['type'] = OB11MessageDataType.face;
message_data['data']['id'] = element.faceElement.faceIndex.toString();
}
} else if (element.marketFaceElement) {
message_data['type'] = OB11MessageDataType.mface;
message_data['data']['summary'] = element.marketFaceElement.faceName;
const md5 = element.marketFaceElement.emojiId;
// 取md5的前两位
const dir = md5.substring(0, 2);
// 获取组装url
// const url = `https://p.qpic.cn/CDN_STATIC/0/data/imgcache/htdocs/club/item/parcel/item/${dir}/${md5}/300x300.gif?max_age=31536000`;
message_data['data']['url'] = `https://gxh.vip.qq.com/club/item/parcel/item/${dir}/${md5}/raw300.gif`;
message_data['data']['emoji_id'] = element.marketFaceElement.emojiId;
message_data['data']['emoji_package_id'] = String(element.marketFaceElement.emojiPackageId);
message_data['data']['key'] = element.marketFaceElement.key;
//mFaceCache.set(md5, element.marketFaceElement.faceName);
} else if (element.markdownElement) {
message_data['type'] = OB11MessageDataType.markdown;
message_data['data']['data'] = element.markdownElement.content;
} else if (element.multiForwardMsgElement) {
message_data['type'] = OB11MessageDataType.forward;
message_data['data']['id'] = msg.msgId;
const ParentMsgPeer = msg.parentMsgPeer ?? {
chatType: msg.chatType,
guildId: '',
peerUid: msg.peerUid,
};
//判断是否在合并消息内
msg.parentMsgIdList = msg.parentMsgIdList ?? [];
//首次列表不存在则开始创建
msg.parentMsgIdList.push(msg.msgId);
//let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2];
//加入自身MsgId
const MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList;
//拉取下级消息
if (!MultiMsgs) continue;
//拉取失败则跳过
message_data['data']['content'] = [];
for (const MultiMsg of MultiMsgs) {
//对每条拉取的消息传递ParentMsgPeer修正Peer
MultiMsg.parentMsgPeer = ParentMsgPeer;
MultiMsg.parentMsgIdList = msg.parentMsgIdList;
MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId);//该ID仅用查看 无法调用
const msgList = await OB11Constructor.message(core, MultiMsg, 'array');
message_data['data']['content'].push(msgList);
//console.log("合并消息", msgList);
}
}
if ((message_data.type as string) !== 'unknown' && message_data.data) {
const cqCode = encodeCQCode(message_data);
if (messagePostFormat === 'string') {
(resMsg.message as string) += cqCode;
} else (resMsg.message as OB11MessageData[]).push(message_data);
resMsg.raw_message += cqCode;
}
}
resMsg.raw_message = resMsg.raw_message.trim();
return resMsg;
}
static async PrivateEvent(core: NapCatCore, msg: RawMessage): Promise<OB11BaseNoticeEvent | undefined> {
const NTQQUserApi = core.apis.UserApi;
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(
core,
parseInt((await NTQQUserApi.getUinByUidV2(pokedetail[0].uid))!),
parseInt((await NTQQUserApi.getUinByUidV2(pokedetail[1].uid))!),
pokedetail
);
}
}
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
}
if (element.grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER) {
//好友添加成功事件
if (element.grayTipElement.xmlElement.templId === '10229' && msg.peerUin !== '') {
return new OB11FriendAddNoticeEvent(core, parseInt(msg.peerUin));
}
}
}
}
}
static async GroupEvent(core: NapCatCore, msg: RawMessage): Promise<OB11GroupNoticeEvent | undefined> {
const NTQQGroupApi = core.apis.GroupApi;
const NTQQUserApi = core.apis.UserApi;
const NTQQMsgApi = core.apis.MsgApi;
const logger = core.context.logger;
if (msg.chatType !== ChatType.group) {
return;
}
//log("group msg", msg);
// Mlikiowa V2.0.6 Refactor Todo
// if (msg.senderUin && msg.senderUin !== '0') {
// const member = await getGroupMember(msg.peerUid, msg.senderUin);
// if (member && member.cardName !== msg.sendMemberName) {
// const newCardName = msg.sendMemberName || '';
// const event = new OB11GroupCardEvent(parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
// member.cardName = newCardName;
// return event;
// }
// }
for (const element of msg.elements) {
const grayTipElement = element.grayTipElement;
const groupElement = grayTipElement?.groupElement;
if (groupElement) {
// log("收到群提示消息", groupElement)
if (groupElement.type == TipGroupElementType.memberIncrease) {
logger.logDebug('收到群成员增加消息', groupElement);
await sleep(1000);
const member = await NTQQGroupApi.getGroupMember(msg.peerUid, groupElement.memberUid);
const memberUin = member?.uin;
// if (!memberUin) {
// memberUin = (await NTQQUserApi.getUserDetailInfo(groupElement.memberUid)).uin
// }
// log("获取新群成员QQ", memberUin)
const adminMember = await NTQQGroupApi.getGroupMember(msg.peerUid, groupElement.adminUid);
// log("获取同意新成员入群的管理员", adminMember)
if (memberUin) {
const operatorUin = adminMember?.uin || memberUin;
// log("构造群增加事件", event)
return new OB11GroupIncreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(memberUin),
parseInt(operatorUin)
);
}
} else if (groupElement.type === TipGroupElementType.ban) {
logger.logDebug('收到群群员禁言提示', groupElement);
const memberUid = groupElement.shutUp!.member.uid;
const adminUid = groupElement.shutUp!.admin.uid;
let memberUin: string = '';
let duration = parseInt(groupElement.shutUp!.duration);
const subType: 'ban' | 'lift_ban' = duration > 0 ? 'ban' : 'lift_ban';
// log('OB11被禁言事件', adminUid);
if (memberUid) {
memberUin = (await NTQQGroupApi.getGroupMember(msg.peerUid, memberUid))?.uin || ''; // || (await NTQQUserApi.getUserDetailInfo(memberUid))?.uin
} else {
memberUin = '0'; // 0表示全员禁言
if (duration > 0) {
duration = -1;
}
}
const adminUin = (await NTQQGroupApi.getGroupMember(msg.peerUid, adminUid))?.uin; // || (await NTQQUserApi.getUserDetailInfo(adminUid))?.uin
// log('OB11被禁言事件', memberUin, adminUin, duration, subType);
if (memberUin && adminUin) {
return new OB11GroupBanEvent(
core,
parseInt(msg.peerUid),
parseInt(memberUin),
parseInt(adminUin),
duration,
subType
);
}
} else if (groupElement.type == TipGroupElementType.kicked) {
logger.logDebug(`收到我被踢出或退群提示, 群${msg.peerUid}`, groupElement);
NTQQGroupApi.quitGroup(msg.peerUid).then();
try {
const adminUin = (await NTQQGroupApi.getGroupMember(msg.peerUid, groupElement.adminUid))?.uin || (await NTQQUserApi.getUidByUinV2(groupElement.adminUid));
if (adminUin) {
return new OB11GroupDecreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(core.selfInfo.uin),
parseInt(adminUin),
'kick_me'
);
}
} catch (e) {
return new OB11GroupDecreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(core.selfInfo.uin),
0,
'leave'
);
}
}
} else if (element.fileElement) {
return new OB11GroupUploadNoticeEvent(
core,
parseInt(msg.peerUid), parseInt(msg.senderUin || ''),
{
id: element.fileElement.fileUuid!,
name: element.fileElement.fileName,
size: parseInt(element.fileElement.fileSize),
busid: element.fileElement.fileBizId || 0,
}
);
}
if (grayTipElement) {
//console.log('收到群提示消息', grayTipElement);
if (grayTipElement.xmlElement?.templId === '10382') {
const emojiLikeData = new fastXmlParser.XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '',
}).parse(grayTipElement.xmlElement.content);
logger.logDebug('收到表情回应我的消息', emojiLikeData);
try {
const senderUin = emojiLikeData.gtip.qq.jp;
const msgSeq = emojiLikeData.gtip.url.msgseq;
const emojiId = emojiLikeData.gtip.face.id;
const replyMsgList = (await NTQQMsgApi.getMsgsBySeqAndCount({
chatType: ChatType.group,
guildId: '',
peerUid: msg.peerUid,
}, msgSeq, 1, true, true)).msgList;
if (replyMsgList.length < 1) {
return;
}
const replyMsg = replyMsgList[0];
console.log('表情回应消息', msgSeq, ' 结算ID', replyMsg.msgId);
return new OB11GroupMsgEmojiLikeEvent(
core,
parseInt(msg.peerUid),
parseInt(senderUin),
MessageUnique.getShortIdByMsgId(replyMsg.msgId)!,
[{
emoji_id: emojiId,
count: 1,
}],
);
} catch (e: any) {
logger.logError('解析表情回应消息失败', e.stack);
}
}
if (grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER) {
logger.logDebug('收到新人被邀请进群消息', grayTipElement);
const xmlElement = grayTipElement.xmlElement;
if (xmlElement?.content) {
const regex = /jp="(\d+)"/g;
const matches = [];
let match = null;
while ((match = regex.exec(xmlElement.content)) !== null) {
matches.push(match[1]);
}
// log("新人进群匹配到的QQ号", matches)
if (matches.length === 2) {
const [inviter, invitee] = matches;
return new OB11GroupIncreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(invitee),
parseInt(inviter),
'invite'
);
}
}
}
//代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE
else if (grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE) {
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
//判断业务类型
//Poke事件
const pokedetail: any[] = json.items;
//筛选item带有uid的元素
const poke_uid = pokedetail.filter(item => item.uid);
if (poke_uid.length == 2) {
return new OB11GroupPokeEvent(
core,
parseInt(msg.peerUid),
parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[0].uid))!),
parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[1].uid))!),
pokedetail
);
}
}
if (grayTipElement.jsonGrayTipElement.busiId == 2401) {
const searchParams = new URL(json.items[0].jp).searchParams;
const msgSeq = searchParams.get('msgSeq')!;
const Group = searchParams.get('groupCode');
// 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(
core,
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;
logger.logDebug('收到群成员新头衔消息', json);
return new OB11GroupTitleEvent(
core,
parseInt(msg.peerUid),
parseInt(memberUin),
title
);
}
}
}
}
}
static friend(friend: User): OB11User {
return {
user_id: parseInt(friend.uin),
nickname: friend.nick,
remark: friend.remark,
sex: OB11Constructor.sex(friend.sex!),
level: friend.qqLevel && calcQQLevel(friend.qqLevel) || 0,
};
}
static selfInfo(selfInfo: SelfInfo): OB11User {
return {
user_id: parseInt(selfInfo.uin),
nickname: selfInfo.nick,
};
}
static friendsV2(friends: FriendV2[]): OB11User[] {
const data: OB11User[] = [];
friends.forEach(friend => {
const sexValue = this.sex(friend.baseInfo.sex!);
data.push({
...friend.baseInfo,
...friend.coreInfo,
user_id: parseInt(friend.coreInfo.uin),
nickname: friend.coreInfo.nick,
remark: friend.coreInfo.nick,
sex: sexValue,
level: 0,
});
});
return data;
}
static friends(friends: Friend[]): OB11User[] {
const data: OB11User[] = [];
friends.forEach(friend => {
const sexValue = this.sex(friend.sex!);
data.push({
user_id: parseInt(friend.uin),
nickname: friend.nick,
remark: friend.remark,
sex: sexValue,
level: 0,
});
});
return data;
}
static groupMemberRole(role: number): OB11GroupMemberRole | undefined {
return {
4: OB11GroupMemberRole.owner,
3: OB11GroupMemberRole.admin,
2: OB11GroupMemberRole.member,
}[role];
}
static sex(sex: Sex): OB11UserSex {
const sexMap = {
[Sex.male]: OB11UserSex.male,
[Sex.female]: OB11UserSex.female,
[Sex.unknown]: OB11UserSex.unknown,
};
return sexMap[sex] || OB11UserSex.unknown;
}
static groupMember(group_id: string, member: GroupMember): OB11GroupMember {
return {
group_id: parseInt(group_id),
user_id: parseInt(member.uin),
nickname: member.nick,
card: member.cardName,
sex: OB11Constructor.sex(member.sex!),
age: 0,
area: '',
level: '0',
qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0,
join_time: 0, // 暂时没法获取
last_sent_time: 0, // 暂时没法获取
title_expire_time: 0,
unfriendly: false,
card_changeable: true,
is_robot: member.isRobot,
shut_up_timestamp: member.shutUpTime,
role: OB11Constructor.groupMemberRole(member.role),
title: member.memberSpecialTitle || '',
};
}
static stranger(user: User): OB11User {
//logDebug('construct ob11 stranger', user);
return {
...user,
user_id: parseInt(user.uin),
nickname: user.nick,
sex: OB11Constructor.sex(user.sex!),
age: 0,
qid: user.qid,
login_days: 0,
level: user.qqLevel && calcQQLevel(user.qqLevel) || 0,
};
}
static group(group: Group): OB11Group {
return {
group_id: parseInt(group.groupCode),
group_name: group.groupName,
member_count: group.memberCount,
max_member_count: group.maxMember,
};
}
static groups(groups: Group[]): OB11Group[] {
return groups.map(OB11Constructor.group);
}
}

154
src/onebot/helper/event.ts Normal file
View File

@@ -0,0 +1,154 @@
import { NapCatOneBot11Adapter } from "..";
import { OB11BaseNoticeEvent } from "../event/notice/OB11BaseNoticeEvent";
import { OB11FriendAddNoticeEvent } from "../event/notice/OB11FriendAddNoticeEvent";
import { OB11GroupNoticeEvent } from "../event/notice/OB11GroupNoticeEvent";
import { OB11GroupCardEvent } from "../event/notice/OB11GroupCardEvent";
import { OB11GroupDecreaseEvent } from "../event/notice/OB11GroupDecreaseEvent";
import { OB11GroupUploadNoticeEvent } from "../event/notice/OB11GroupUploadNoticeEvent";
import { OB11GroupPokeEvent } from "../event/notice/OB11PokeEvent";
import { OB11GroupEssenceEvent } from "../event/notice/OB11GroupEssenceEvent";
import { MessageUnique } from "@/common/utils/MessageUnique";
import { OB11GroupTitleEvent } from "../event/notice/OB11GroupTitleEvent";
import { NapCatCore, RawMessage, ChatType, NTGrayTipElementSubTypeV2, TipGroupElementType, Peer } from '@/core';
export async function NT2PrivateEvent(core: NapCatCore, obContext: NapCatOneBot11Adapter, msg: RawMessage): Promise<OB11BaseNoticeEvent | undefined> {
if (msg.chatType !== ChatType.KCHATTYPEC2C) {
return;
}
for (const element of msg.elements) {
if (element.grayTipElement) {
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
const PokeEvent = await obContext.apiContext.FriendApi.parsePrivatePokeEvent(element.grayTipElement);
if (PokeEvent) return PokeEvent;
}
}
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) {
//好友添加成功事件
if (element.grayTipElement.xmlElement.templId === '10229' && msg.peerUin !== '') {
return new OB11FriendAddNoticeEvent(core, parseInt(msg.peerUin));
}
}
}
}
}
export async function NT2GroupEvent(core: NapCatCore, obContext: NapCatOneBot11Adapter, msg: RawMessage): Promise<OB11GroupNoticeEvent | undefined> {
const NTQQGroupApi = core.apis.GroupApi;
const NTQQUserApi = core.apis.UserApi;
const NTQQMsgApi = core.apis.MsgApi;
const logger = core.context.logger;
if (msg.chatType !== ChatType.KCHATTYPEGROUP) {
return;
}
//log("group msg", msg);
if (msg.senderUin && msg.senderUin !== '0') {
const member = await NTQQGroupApi.getGroupMember(msg.peerUid, msg.senderUin);
if (member && member.cardName !== msg.sendMemberName) {
const newCardName = msg.sendMemberName || '';
const event = new OB11GroupCardEvent(core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
member.cardName = newCardName;
return event;
}
}
for (const element of msg.elements) {
if (element.grayTipElement && element.grayTipElement.groupElement) {
const groupElement = element.grayTipElement.groupElement;
if (groupElement.type == TipGroupElementType.memberIncrease) {
const MemberIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement);
if (MemberIncreaseEvent) return MemberIncreaseEvent;
} else if (groupElement.type === TipGroupElementType.ban) {
const BanEvent = await obContext.apiContext.GroupApi.parseGroupBanEvent(msg.peerUid, element.grayTipElement);
if (BanEvent) return BanEvent;
} else if (groupElement.type == TipGroupElementType.kicked) {
NTQQGroupApi.quitGroup(msg.peerUid).then();
try {
const KickEvent = await obContext.apiContext.GroupApi.parseGroupKickEvent(msg.peerUid, element.grayTipElement);
if (KickEvent) return KickEvent;
} catch (e) {
return new OB11GroupDecreaseEvent(
core,
parseInt(msg.peerUid),
parseInt(core.selfInfo.uin),
0,
'leave'
);
}
}
} else if (element.fileElement) {
return new OB11GroupUploadNoticeEvent(
core,
parseInt(msg.peerUid), parseInt(msg.senderUin || ''),
{
id: element.fileElement.fileUuid!,
name: element.fileElement.fileName,
size: parseInt(element.fileElement.fileSize),
busid: element.fileElement.fileBizId || 0,
}
);
}
if (element.grayTipElement) {
if (element.grayTipElement.xmlElement?.templId === '10382') {
const emojiLikeEvent = await obContext.apiContext.GroupApi.parseGroupEmjioLikeEvent(msg.peerUid, element.grayTipElement);
if (emojiLikeEvent) return emojiLikeEvent;
}
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) {
const GroupIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement);
if (GroupIncreaseEvent) return GroupIncreaseEvent;
}
//代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE
else if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr);
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
//判断业务类型
//Poke事件
const pokedetail: any[] = json.items;
//筛选item带有uid的元素
const poke_uid = pokedetail.filter(item => item.uid);
if (poke_uid.length == 2) {
return new OB11GroupPokeEvent(
core,
parseInt(msg.peerUid),
parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[0].uid))!),
parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[1].uid))!),
pokedetail
);
}
}
if (element.grayTipElement.jsonGrayTipElement.busiId == 2401) {
const searchParams = new URL(json.items[0].jp).searchParams;
const msgSeq = searchParams.get('msgSeq')!;
const Group = searchParams.get('groupCode');
// const businessId = searchParams.get('businessid');
const Peer: Peer = {
guildId: '',
chatType: ChatType.KCHATTYPEGROUP,
peerUid: Group!,
};
const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true);
return new OB11GroupEssenceEvent(
core,
parseInt(msg.peerUid),
MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!,
parseInt(msgData.msgList[0].senderUin)
);
// 获取MsgSeq+Peer可获取具体消息
}
if (element.grayTipElement.jsonGrayTipElement.busiId == 2407) {
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
const memberUin = json.items[1].param[0];
const title = json.items[3].txt;
logger.logDebug('收到群成员新头衔消息', json);
return new OB11GroupTitleEvent(
core,
parseInt(msg.peerUid),
parseInt(memberUin),
title
);
}
}
}
}
}

View File

@@ -18,10 +18,11 @@ import {
SendVideoElement,
viedo_type,
} from '@/core';
import * as fsnormal from 'node:fs';
import { promises as fs } from 'node:fs';
import ffmpeg from 'fluent-ffmpeg';
import { calculateFileMD5, isGIF } from '@/common/utils/file';
import { getVideoInfo } from '@/common/utils/video';
import { defaultVideoThumbB64, getVideoInfo } from '@/common/utils/video';
import { encodeSilk } from '@/common/utils/audio';
import faceConfig from '@/core/external/face_config.json';
import * as pathLib from 'node:path';
@@ -140,11 +141,7 @@ export class SendMsgElementConstructor {
}
static async video(coreContext: NapCatCore, filePath: string, fileName: string = '', diyThumbPath: string = '', videotype: viedo_type = viedo_type.VIDEO_FORMAT_MP4): Promise<SendVideoElement> {
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQUserApi = coreContext.apis.UserApi;
const NTQQFileApi = coreContext.apis.FileApi;
const NTQQMsgApi = coreContext.apis.MsgApi;
const NTQQFriendApi = coreContext.apis.FriendApi;
const logger = coreContext.context.logger;
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
if (fileSize === 0) {
@@ -179,7 +176,8 @@ export class SendMsgElementConstructor {
resolve(thumbPath);
}).catch(reject);
} else {
resolve(undefined);
fsnormal.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
resolve(thumbPath);
}
})
.screenshots({
@@ -224,6 +222,21 @@ export class SendMsgElementConstructor {
// sourceVideoCodecFormat: 2
},
};
// "fileElement": {
// "fileMd5": "",
// "fileName": "1.mp4",
// "filePath": "C:\\Users\\nanae\\OneDrive\\Desktop\\1.mp4",
// "fileSize": "1847007",
// "picHeight": 1280,
// "picWidth": 720,
// "picThumbPath": {},
// "file10MMd5": "",
// "fileSha": "",
// "fileSha3": "",
// "fileUuid": "",
// "fileSubId": "",
// "thumbFileSize": 750
// }
return element;
}

View File

@@ -0,0 +1,6 @@
export * from './config';
export * from './parseMessage';
export * from './converter';
export * from './quick';
export * from './genMessage';
export * from './event';

View File

@@ -0,0 +1,117 @@
import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataType } from '..';
import { AtType, ChatType, NapCatCore, RawMessage } from '@/core';
import { EventType } from '../event/OB11BaseEvent';
import { OB11Constructor } from './converter';
import { encodeCQCode } from './cqcode';
export async function RawNTMsg2Onebot(
core: NapCatCore,
obcore: NapCatOneBot11Adapter,
msg: RawMessage,
messagePostFormat: string = obcore.configLoader.configData.messagePostFormat
): Promise<OB11Message | undefined> {
if (msg.senderUin == "0" || msg.senderUin == "") return;
if (msg.peerUin == "0" || msg.peerUin == "") return;
//跳过空消息
const NTQQGroupApi = core.apis.GroupApi;
const NTQQUserApi = core.apis.UserApi;
const NTQQMsgApi = core.apis.MsgApi;
const resMsg: OB11Message = {
self_id: parseInt(core.selfInfo.uin),
user_id: parseInt(msg.senderUin!),
time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.id!,
message_seq: msg.id!,
real_id: msg.id!,
message_type: msg.chatType == ChatType.KCHATTYPEGROUP ? 'group' : 'private',
sender: {
user_id: parseInt(msg.senderUin || '0'),
nickname: msg.sendNickName,
card: msg.sendMemberName || '',
},
raw_message: '',
font: 14,
sub_type: 'friend',
message: messagePostFormat === 'string' ? '' : [],
message_format: messagePostFormat === 'string' ? 'string' : 'array',
post_type: core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
};
if (msg.chatType == ChatType.KCHATTYPEGROUP) {
resMsg.sub_type = 'normal'; // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼
resMsg.group_id = parseInt(msg.peerUin);
let member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (!member) member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (member) {
resMsg.sender.role = OB11Constructor.groupMemberRole(member.role);
resMsg.sender.nickname = member.nick;
}
} else if (msg.chatType == ChatType.KCHATTYPEC2C) {
resMsg.sub_type = 'friend';
resMsg.sender.nickname = (await NTQQUserApi.getUserDetailInfo(msg.senderUid)).nick;
} else if (msg.chatType == ChatType.KCHATTYPETEMPC2CFROMGROUP) {
resMsg.sub_type = 'group';
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid);
if (ret.result === 0) {
resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode);
resMsg.sender.nickname = ret.tmpChatInfo!.fromNick;
} else {
resMsg.group_id = 284840486; //兜底数据
resMsg.sender.nickname = "临时会话";
}
}
for (const element of msg.elements) {
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
if (element.textElement && element.textElement?.atType !== AtType.notAt) {
let textAtMsgData = await obcore.apiContext.MsgApi.parseTextElemntWithAt(msg, element);
if (textAtMsgData) message_data = textAtMsgData
} else if (element.textElement) {
let textMsgData = await obcore.apiContext.MsgApi.parseTextElement(msg, element);
if (textMsgData) message_data = textMsgData;
} else if (element.replyElement) {
let replyMsgData = await obcore.apiContext.MsgApi.parseReplyElement(msg, element);
if (replyMsgData) message_data = replyMsgData;
} else if (element.picElement) {
let PicMsgData = await obcore.apiContext.MsgApi.parsePicElement(msg, element);
if (PicMsgData) message_data = PicMsgData;
} else if (element.fileElement) {
let FileMsgData = await obcore.apiContext.MsgApi.parseFileElement(msg, element);
if (FileMsgData) message_data = FileMsgData;
} else if (element.videoElement) {
let videoMsgData = await obcore.apiContext.MsgApi.parseVideoElement(msg, element);
if (videoMsgData) message_data = videoMsgData;
} else if (element.pttElement) {
let pttMsgData = await obcore.apiContext.MsgApi.parsePTTElement(msg, element);
if (pttMsgData) message_data = pttMsgData;
} else if (element.arkElement) {
let arkMsgData = await obcore.apiContext.MsgApi.parseArkElement(msg, element);
if (arkMsgData) message_data = arkMsgData;
} else if (element.faceElement) {
let faceMsgData = await obcore.apiContext.MsgApi.parseFaceElement(msg, element);
if (faceMsgData) message_data = faceMsgData;
} else if (element.marketFaceElement) {
let marketFaceMsgData = await obcore.apiContext.MsgApi.parseMarketFaceElement(msg, element);
if (marketFaceMsgData) message_data = marketFaceMsgData;
} else if (element.markdownElement) {
message_data['type'] = OB11MessageDataType.markdown;
message_data['data']['data'] = element.markdownElement.content;
} else if (element.multiForwardMsgElement) {
let multiForwardMsgData = await obcore.apiContext.MsgApi.parseMultForwardElement(msg, element, messagePostFormat);
if (multiForwardMsgData) message_data = multiForwardMsgData;
}
if ((message_data.type as string) !== 'unknown' && message_data.data) {
const cqCode = encodeCQCode(message_data);
if (messagePostFormat === 'string') {
(resMsg.message as string) += cqCode;
} else (resMsg.message as OB11MessageData[]).push(message_data);
resMsg.raw_message += cqCode;
}
}
resMsg.raw_message = resMsg.raw_message.trim();
return resMsg;
}

View File

@@ -20,15 +20,15 @@ async function handleMsg(coreContext: NapCatCore, obContext: NapCatOneBot11Adapt
msg = msg as OB11Message;
const reply = quickAction.reply;
const peer: Peer = {
chatType: ChatType.friend,
chatType: ChatType.KCHATTYPEC2C,
peerUid: await coreContext.apis.UserApi.getUidByUinV2(msg.user_id.toString()) as string,
};
if (msg.message_type == 'private') {
if (msg.sub_type === 'group') {
peer.chatType = ChatType.temp;
peer.chatType = ChatType.KCHATTYPETEMPC2CFROMGROUP;
}
} else {
peer.chatType = ChatType.group;
peer.chatType = ChatType.KCHATTYPETEMPC2CFROMGROUP;
peer.peerUid = msg.group_id!.toString();
}
if (reply) {

View File

@@ -8,6 +8,8 @@ import {
MsgListener,
NapCatCore,
RawMessage,
SendStatusType,
GroupMemberRole,
} from '@/core';
import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config';
import { OneBotApiContextType } from '@/onebot/types';
@@ -19,12 +21,11 @@ import {
OB11PassiveWebSocketAdapter,
} from '@/onebot/network';
import { NapCatPathWrapper } from '@/common/framework/napcat';
import { OneBotFriendApi, OneBotGroupApi, OneBotUserApi } from '@/onebot/api';
import { OneBotFriendApi, OneBotGroupApi, OneBotMsgApi, OneBotUserApi } from '@/onebot/api';
import { ActionMap, createActionMap } from '@/onebot/action';
import { WebUiDataRuntime } from '@/webui/src/helper/Data';
import { OB11InputStatusEvent } from '@/onebot/event/notice/OB11InputStatusEvent';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { OB11Constructor } from '@/onebot/helper/data';
import { proxiedListenerOf } from '@/common/utils/proxy-handler';
import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
import { OB11GroupAdminNoticeEvent } from '@/onebot/event/notice/OB11GroupAdminNoticeEvent';
@@ -33,6 +34,7 @@ import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent';
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
import { LRUCache } from '@/common/utils/LRU';
import { NT2GroupEvent, NT2PrivateEvent, RawNTMsg2Onebot } from './helper';
//OneBot实现类
export class NapCatOneBot11Adapter {
@@ -54,6 +56,7 @@ export class NapCatOneBot11Adapter {
GroupApi: new OneBotGroupApi(this, core),
UserApi: new OneBotUserApi(this, core),
FriendApi: new OneBotFriendApi(this, core),
MsgApi: new OneBotMsgApi(this, core),
};
this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager();
@@ -250,11 +253,12 @@ export class NapCatOneBot11Adapter {
.catch(e => this.context.logger.logError('处理消息失败', e));
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
if (msg.sendStatus == 2 && !msgIdSend.get(msg.msgId)) {
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) {
msgIdSend.put(msg.msgId, true);
// 完成后再post
OB11Constructor.message(this.core, msg, this.configLoader.configData.messagePostFormat)
RawNTMsg2Onebot(this.core, this, msg)
.then((ob11Msg) => {
if (!ob11Msg) return;
ob11Msg.target_id = parseInt(msg.peerUin);
if (this.configLoader.configData.reportSelfMessage) {
msg.id = MessageUnique.createMsg({
@@ -272,15 +276,23 @@ export class NapCatOneBot11Adapter {
};
this.context.session.getMsgService().addKernelMsgListener(
new this.context.wrapper.NodeIKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger)),
proxiedListenerOf(msgListener, this.context.logger) as any
);
}
private initBuddyListener() {
const buddyListener = new BuddyListener();
buddyListener.onBuddyReqChange = reqs => {
reqs.buddyReqs.forEach(async req => {
buddyListener.onBuddyReqChange = async reqs => {
this.core.apis.FriendApi.clearBuddyReqUnreadCnt();
for (let i = 0; i < reqs.unreadNums; i++) {
const req = reqs.buddyReqs[i];
//req.isBuddy === false是单向好友 null为常规情况
// if (req.isBuddy === false && ) {
// const NTQQFriendApi = this.core.apis.FriendApi;
// await NTQQFriendApi.handleFriendRequest(req.friendUid + '|' + req.reqTime, true);
// }
if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) {
return;
}
@@ -289,17 +301,17 @@ export class NapCatOneBot11Adapter {
await this.networkManager.emitEvent(new OB11FriendRequestEvent(
this.core,
parseInt(requesterUin!),
req.friendUid + '|' + req.reqTime,
req.extWords,
req.friendUid + '|' + req.reqTime,
));
} catch (e) {
this.context.logger.logDebug('获取加好友者QQ号失败', e);
}
});
}
};
this.context.session.getBuddyService().addKernelBuddyListener(
new this.context.wrapper.NodeIKernelBuddyListener(proxiedListenerOf(buddyListener, this.context.logger)),
proxiedListenerOf(buddyListener, this.context.logger) as any
);
}
@@ -324,11 +336,6 @@ export class NapCatOneBot11Adapter {
const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type;
this.context.logger.logDebug('收到群通知', notify);
// let member2: GroupMember;
// if (notify.user2.uid) {
// member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
// }
if ([
GroupNotifyTypes.ADMIN_SET,
GroupNotifyTypes.ADMIN_UNSET,
@@ -399,12 +406,12 @@ export class NapCatOneBot11Adapter {
} catch (e) {
this.context.logger.logError('获取加群人QQ号失败 Uid:', notify.user1.uid, e);
}
} else if (notify.type == GroupNotifyTypes.INVITE_ME) {
} else if (notify.type == GroupNotifyTypes.INVITE_ME && notify.status == 1) {
this.context.logger.logDebug(`收到邀请我加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent(
this.core,
parseInt(notify.group.groupCode),
parseInt(notify.user1.uid),
parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid)),
'invite',
notify.postscript,
flag,
@@ -416,15 +423,39 @@ export class NapCatOneBot11Adapter {
}
};
groupListener.onMemberInfoChange = async (groupCode, changeType, members) => {
//this.context.logger.logDebug('收到群成员信息变动通知', groupCode, changeType);
if (changeType === 0) {
const existMembers = this.core.apis.GroupApi.groupMemberCache.get(groupCode);
if (!existMembers) return;
members.forEach((member) => {
const existMember = existMembers.get(member.uid);
if (!existMember?.isChangeRole) return;
this.context.logger.logDebug('变动管理员获取成功');
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
this.core,
parseInt(groupCode),
parseInt(member.uin),
member.role === GroupMemberRole.admin ? 'set' : 'unset',
);
this.networkManager.emitEvent(groupAdminNoticeEvent)
.catch(e => this.context.logger.logError('处理群管理员变动失败', e));
existMember.isChangeRole = false;
this.context.logger.logDebug('群管理员变动处理完毕');
});
}
};
this.context.session.getGroupService().addKernelGroupListener(
new this.context.wrapper.NodeIKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger)),
proxiedListenerOf(groupListener, this.context.logger)
);
}
private async emitMsg(message: RawMessage) {
const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData;
this.context.logger.logDebug('收到新消息 RawMessage', message);
OB11Constructor.message(this.core, message, messagePostFormat).then((ob11Msg) => {
RawNTMsg2Onebot(this.core, this, message, messagePostFormat).then((ob11Msg) => {
if (!ob11Msg) return;
this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
if (debug) {
ob11Msg.raw = message;
@@ -445,14 +476,14 @@ export class NapCatOneBot11Adapter {
this.networkManager.emitEvent(ob11Msg);
}).catch(e => this.context.logger.logError('constructMessage error: ', e));
OB11Constructor.GroupEvent(this.core, message).then(groupEvent => {
NT2GroupEvent(this.core, this, message).then(groupEvent => {
if (groupEvent) {
// log("post group event", groupEvent);
this.networkManager.emitEvent(groupEvent);
}
}).catch(e => this.context.logger.logError('constructGroupEvent error: ', e));
OB11Constructor.PrivateEvent(this.core, message).then(privateEvent => {
NT2PrivateEvent(this.core, this, message).then(privateEvent => {
if (privateEvent) {
// log("post private event", privateEvent);
this.networkManager.emitEvent(privateEvent);
@@ -469,7 +500,7 @@ export class NapCatOneBot11Adapter {
if (!oriMessageId) {
continue;
}
if (message.chatType == ChatType.friend) {
if (message.chatType == ChatType.KCHATTYPEC2C) {
const friendRecallEvent = new OB11FriendRecallNoticeEvent(
this.core,
parseInt(message!.senderUin),
@@ -477,11 +508,11 @@ export class NapCatOneBot11Adapter {
);
this.networkManager.emitEvent(friendRecallEvent)
.catch(e => this.context.logger.logError('处理好友消息撤回失败', e));
} else if (message.chatType == ChatType.group) {
} else if (message.chatType == ChatType.KCHATTYPEGROUP) {
let operatorId = message.senderUin;
for (const element of message.elements) {
const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
if(!operatorUid) return;
if (!operatorUid) return;
const operator = await this.core.apis.GroupApi.getGroupMember(message.peerUin, operatorUid);
operatorId = operator?.uin || message.senderUin;
}

View File

@@ -44,14 +44,16 @@ export class OB11ActiveHttpAdapter implements IOB11NetworkAdapter {
resJson = await res.json();
//logDebug('新消息事件HTTP上报返回快速操作: ', JSON.stringify(resJson));
} catch (e) {
this.logger.logDebug('新消息事件HTTP上报没有返回快速操作不需要处理');
this.logger.logDebug('[OneBot] [Http Client] 新消息事件HTTP上报没有返回快速操作不需要处理');
return;
}
try {
handleQuickOperation(this.coreContext, this.obContext, event as QuickActionEvent, resJson).then().catch(this.logger.logError);
} catch (e: any) {
this.logger.logError('新消息事件HTTP上报返回快速操作失败', e);
this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报返回快速操作失败', e);
}
}).catch((e) => {
this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报失败', e);
});
}

View File

@@ -6,6 +6,7 @@ import { ActionName } from '@/onebot/action/types';
import { OB11Response } from '@/onebot/action/OB11Response';
import { LogWrapper } from '@/common/utils/log';
import { ActionMap } from '@/onebot/action';
import { LifeCycleSubType, OB11LifeCycleEvent } from '../event/meta/OB11LifeCycleEvent';
export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
isClosed: boolean = false;
@@ -25,7 +26,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
}
onEvent<T extends OB11EmitEventContent>(event: T) {
if (this.connection) {
if (this.connection && this.connection.readyState === WebSocket.OPEN) {
this.connection.send(JSON.stringify(event));
}
}
@@ -69,6 +70,9 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
let isClosedByError = false;
this.connection = new WebSocket(this.url, {
maxPayload: 1024 * 1024 * 1024,
handshakeTimeout: 2000,
perMessageDeflate: false,
headers: {
'X-Self-ID': this.coreContext.selfInfo.uin,
'Authorization': `Bearer ${this.token}`,
@@ -76,14 +80,28 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
'User-Agent': 'OneBot/11',
},
});
this.connection.on('ping', () => {
this.connection?.pong();
});
this.connection.on('pong', () => {
//this.logger.logDebug('[OneBot] [WebSocket Client] 收到pong');
});
this.connection.on('open', () => {
try {
this.connectEvent(this.coreContext);
} catch (e) {
this.logger.logError('[OneBot] [WebSocket Client] 发送连接生命周期失败', e);
}
});
this.connection.on('message', (data) => {
this.handleMessage(data);
});
this.connection.once('close', () => {
if (!isClosedByError) {
this.logger.logError(`反向WebSocket (${this.url}) 连接意外关闭`);
this.logger.logError(`${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`);
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.url}) 连接意外关闭`);
this.logger.logError(`[OneBot] [WebSocket Client] ${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`);
if (!this.isClosed) {
this.connection = null;
setTimeout(() => this.tryConnect(), this.reconnectIntervalInMillis);
@@ -92,8 +110,8 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
});
this.connection.on('error', (err) => {
isClosedByError = true;
this.logger.logError(`反向WebSocket (${this.url}) 连接错误`, err);
this.logger.logError(`${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`);
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.url}) 连接错误`, err);
this.logger.logError(`[OneBot] [WebSocket Client] ${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`);
if (!this.isClosed) {
this.connection = null;
setTimeout(() => this.tryConnect(), this.reconnectIntervalInMillis);
@@ -101,7 +119,13 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
});
}
}
connectEvent(core: NapCatCore) {
try {
this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT));
} catch (e) {
this.logger.logError('[OneBot] [WebSocket Client] 发送生命周期失败', e);
}
}
private async handleMessage(message: any) {
let receiveData: { action: ActionName, params?: any, echo?: any } = { action: ActionName.Unknown, params: {} };
let echo = undefined;
@@ -109,9 +133,10 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
try {
receiveData = JSON.parse(message.toString());
echo = receiveData.echo;
this.logger.logDebug('收到正向Websocket消息', receiveData);
this.logger.logDebug('[OneBot] [WebSocket Client] 收到正向Websocket消息', receiveData);
} catch (e) {
this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo));
return;
}
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
const retdata = await this.actions.get(receiveData.action)
@@ -119,6 +144,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
const packet = Object.assign({}, retdata);
this.checkStateAndReply<any>(packet);
} catch (e) {
this.logger.logError('[OneBot] [WebSocket Client] 发生错误', e);
this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo));
}
}

View File

@@ -4,6 +4,7 @@ import http from 'http';
import { NapCatCore } from '@/core';
import { OB11Response } from '../action/OB11Response';
import { ActionMap } from '@/onebot/action';
import cors from 'cors';
export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter {
private app: Express | undefined;
@@ -43,13 +44,24 @@ export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter {
this.server?.close();
this.app = undefined;
}
private initializeServer() {
this.app = express();
this.server = http.createServer(this.app);
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: false }));
this.app.use(cors());
this.app.use(express.urlencoded({ extended: true, limit: '5000mb' }));
this.app.use((req, res, next) => {
// 兼容处理没有带content-type的请求
req.headers['content-type'] = 'application/json';
const originalJson = express.json({ limit: '5000mb' });
originalJson(req, res, (err) => {
if (err) {
return res.status(400).send('Invalid JSON');
}
next();
});
});
this.app.use((req, res, next) => this.authorize(this.token, req, res, next));
this.app.use((req, res) => this.handleRequest(req, res));

View File

@@ -11,6 +11,7 @@ import { LogWrapper } from '@/common/utils/log';
import { OB11HeartbeatEvent } from '../event/meta/OB11HeartbeatEvent';
import { IncomingMessage } from 'http';
import { ActionMap } from '@/onebot/action';
import { LifeCycleSubType, OB11LifeCycleEvent } from '../event/meta/OB11LifeCycleEvent';
export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
wsServer: WebSocketServer;
@@ -35,7 +36,12 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
this.logger = coreContext.context.logger;
this.heartbeatInterval = heartbeatInterval;
this.wsServer = new WebSocketServer({ port: port, host: ip });
this.wsServer = new WebSocketServer({
port: port,
host: ip,
maxPayload: 1024 * 1024 * 1024,
});
const core = coreContext;
this.wsServer.on('connection', async (wsClient, wsReq) => {
if (!this.isOpen) {
wsClient.close();
@@ -43,12 +49,17 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
}
//鉴权
this.authorize(token, wsClient, wsReq);
this.connectEvent(core, wsClient);
wsClient.on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Client Error:', err.message));
wsClient.on('message', (message) => {
this.handleMessage(wsClient, message).then().catch(this.logger.logError);
});
wsClient.on('ping', () => {
wsClient.pong();
});
wsClient.on('pong', () => {
//this.logger.logDebug('[OneBot] [WebSocket Server] Pong received');
});
wsClient.once('close', () => {
this.wsClientsMutex.runExclusive(async () => {
const index = this.wsClients.indexOf(wsClient);
@@ -63,6 +74,14 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
}).on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Server Error:', err.message));
}
connectEvent(core: NapCatCore, wsClient: WebSocket) {
try {
this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT), wsClient);
} catch (e) {
this.logger.logError('[OneBot] [WebSocket Server] 发送生命周期失败', e);
}
}
onEvent<T extends OB11EmitEventContent>(event: T) {
this.wsClientsMutex.runExclusive(async () => {
this.wsClients.forEach((wsClient) => {
@@ -82,7 +101,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
}
const addressInfo = this.wsServer.address();
this.logger.log('[OneBot] [WebSocket Server] Server Started', typeof (addressInfo) === 'string' ? addressInfo : addressInfo?.address + ':' + addressInfo?.port);
this.isOpen = true;
this.registerHeartBeat();
}
@@ -136,6 +155,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
//this.logger.logDebug('收到正向Websocket消息', receiveData);
} catch (e) {
this.checkStateAndReply<any>(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient);
return;
}
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
const retdata = await this.actions.get(receiveData.action)?.websocketHandle(receiveData.params, echo || '');

View File

@@ -1,7 +1,8 @@
import { OneBotFriendApi, OneBotGroupApi, OneBotUserApi } from '../api';
import { OneBotFriendApi, OneBotGroupApi, OneBotMsgApi, OneBotUserApi } from '../api';
export interface OneBotApiContextType {
FriendApi: OneBotFriendApi;
UserApi: OneBotUserApi;
GroupApi: OneBotGroupApi;
MsgApi: OneBotMsgApi;
}

View File

@@ -43,12 +43,12 @@ export async function NCoreInitShell() {
// from constructor
const engine = new wrapper.NodeIQQNTWrapperEngine();
const util = new wrapper.NodeQQNTWrapperUtil();
//const util = wrapper.NodeQQNTWrapperUtil.get();
const loginService = new wrapper.NodeIKernelLoginService();
const session = new wrapper.NodeIQQNTWrapperSession();
// from get dataPath
let dataPath = util.getNTUserDataInfoConfig();
let dataPath = wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
if (!dataPath) {
dataPath = path.resolve(os.homedir(), './.config/QQ');
fs.mkdirSync(dataPath, { recursive: true });
@@ -70,7 +70,7 @@ export async function NCoreInitShell() {
},
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },
},
new wrapper.NodeIGlobalAdapter(new GlobalAdapter()),
new GlobalAdapter() as any,
);
loginService.initConfig({
machineId: '',
@@ -140,8 +140,7 @@ export async function NCoreInitShell() {
logger.logError('[Core] [Login] Login Error , ErrInfo: ', args);
};
loginService.addKernelLoginListener(new wrapper.NodeIKernelLoginListener(
proxiedListenerOf(loginListener, logger)));
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
// 实现WebUi快速登录
loginService.getLoginList().then((res) => {
@@ -168,25 +167,29 @@ export async function NCoreInitShell() {
});
});
if (quickLoginUin && historyLoginList.some(u => u.uin === quickLoginUin)) {
logger.log('正在快速登录 ', quickLoginUin);
setTimeout(() => {
loginService.quickLoginWithUin(quickLoginUin)
.then(result => {
if (result.loginErrorInfo.errMsg) {
logger.logError('快速登录错误:', result.loginErrorInfo.errMsg);
reject();
}
})
.catch(e => reject(e));
}, 1000);
if (quickLoginUin) {
if (historyLoginList.some(u => u.uin === quickLoginUin)) {
logger.log('正在快速登录 ', quickLoginUin);
setTimeout(() => {
loginService.quickLoginWithUin(quickLoginUin)
.then(result => {
if (result.loginErrorInfo.errMsg) {
logger.logError('快速登录错误:', result.loginErrorInfo.errMsg);
loginService.getQRCodePicture();
}
})
.catch();
}, 1000);
} else {
logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式');
loginService.getQRCodePicture();
}
} else {
logger.log('没有 -q 指令指定快速登录,或未曾登录过这个 QQ将使用二维码登录方式');
logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式');
if (historyLoginList.length > 0) {
logger.log(`可用于快速登录的 QQ\n${
historyLoginList
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
.join('\n')
logger.log(`可用于快速登录的 QQ\n${historyLoginList
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
.join('\n')
}`);
}
loginService.getQRCodePicture();
@@ -215,9 +218,9 @@ export async function NCoreInitShell() {
};
session.init(
sessionConfig,
new wrapper.NodeIDependsAdapter(new DependsAdapter()),
new wrapper.NodeIDispatcherAdapter(new DispatcherAdapter()),
new wrapper.NodeIKernelSessionListener(sessionListener),
new DependsAdapter() as any,
new DispatcherAdapter() as any,
sessionListener as any,
);
try {
session.startNT(0);

View File

@@ -19,7 +19,7 @@ export let webUiPathWrapper: NapCatPathWrapper;
export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapper) {
webUiPathWrapper = pathWrapper;
WebUiConfig = new WebUiConfigWrapper();
let log = logger.log.bind(logger);
const log = logger.log.bind(logger);
const config = await WebUiConfig.GetWebUIConfig();
if (config.port == 0) {
log('[NapCat] [WebUi] Current WebUi is not run.');

Some files were not shown because too many files have changed in this diff Show More