Compare commits

...

131 Commits

Author SHA1 Message Date
手瓜一十雪
8353533d60 v2.6.13 2024-09-23 15:52:40 +08:00
手瓜一十雪
c06df27424 feat: 修复空格与中文问题 2024-09-23 15:52:16 +08:00
手瓜一十雪
ad82919ddf fix: 2.6.12 2024-09-23 09:35:08 +08:00
手瓜一十雪
44dbba17e1 rollup 2024-09-23 09:34:44 +08:00
手瓜一十雪
5ba110e1da feat: bind 2024-09-22 16:59:45 +08:00
手瓜一十雪
b6e392fdb2 release: v2.6.11 2024-09-22 11:42:13 +08:00
手瓜一十雪
2280e83aa2 fix: type 2024-09-21 17:40:42 +08:00
手瓜一十雪
f49b94edb9 Merge pull request #392 from Fripine/feat/more-music-types
feat: support more types of music cards
2024-09-21 15:00:59 +08:00
Fripine
2428a12221 chore 2024-09-21 12:30:05 +08:00
Fripine
9c353f3760 feat: support more types of music cards 2024-09-21 12:17:05 +08:00
手瓜一十雪
5b86d25d7f Merge pull request #389 from Fripine/fix/FriendAdd
fix: FriendAddNoticeEvent
2024-09-20 21:18:14 +08:00
Fripine
2b168e8bbc fix: FriendAdd 2024-09-20 15:50:31 +08:00
手瓜一十雪
537db32847 Merge pull request #388 from NapNeko/revert-387-fix/friendAddEvent
Revert "fix: 好友添加成功事件"
2024-09-20 15:36:32 +08:00
手瓜一十雪
498b7f9f2b Revert "fix: 好友添加成功事件" 2024-09-20 14:33:52 +08:00
手瓜一十雪
9935568597 Merge pull request #387 from Fripine/fix/friendAddEvent
fix: 好友添加成功事件
2024-09-20 13:00:13 +08:00
Fripine
467003af8c chore 2024-09-20 10:24:19 +08:00
Fripine
4c9edcc47b chore 2024-09-20 10:21:26 +08:00
Fripine
24bf9cf121 chore: 换一种方法 2024-09-20 10:17:30 +08:00
手瓜一十雪
e06f6f39a9 Merge pull request #386 from 123233513/main
增加处理消息段时的检查,过滤无效消息段。
2024-09-20 07:54:52 +08:00
123233513
98ee0c307b Merge branch 'main' of https://github.com/123233513/NapCatQQ 2024-09-20 04:57:41 +08:00
Fripine
5e53ea0bc3 fix: cant emit FriendAddNoticeEvent 2024-09-20 04:38:09 +08:00
123233513
847d88ea77 Update msg.ts
处理消息段时的检查,过滤无效消息段。
2024-09-19 22:14:18 +08:00
123233513
d5046cc2b3 Merge branch 'main' of https://githubfast.com/123233513/NapCatQQ 2024-09-19 21:36:52 +08:00
123233513
3ad64b7cbb 增加处理消息段时的检查,过滤无效消息段。 2024-09-19 21:31:33 +08:00
手瓜一十雪
0dbfe8ca55 feat: 拦截不合法消息 2024-09-19 20:49:33 +08:00
手瓜一十雪
91b794d66d release: 2.6.10 2024-09-19 20:45:09 +08:00
手瓜一十雪
0d65e1e314 release: 2.6.9 2024-09-18 21:52:16 +08:00
手瓜一十雪
2d8f58c6d8 feat: close xlog 2024-09-18 20:22:21 +08:00
手瓜一十雪
65888fa816 feat: close log 2024-09-18 20:18:35 +08:00
手瓜一十雪
857e882c6e release: fk tx 2024-09-18 11:20:58 +08:00
手瓜一十雪
add2931834 remove: debug 2024-09-18 11:19:59 +08:00
手瓜一十雪
cdda5f45ee refactor: guid fk tx 2024-09-18 11:19:29 +08:00
手瓜一十雪
5f73d6a913 feat: reportAmgomWeather a1 rnm tx 2024-09-18 11:01:12 +08:00
手瓜一十雪
0637882fbc release: rnm tx 2024-09-18 10:53:37 +08:00
手瓜一十雪
3f785bab20 feat: NodeIO3MiscService 2024-09-18 10:44:35 +08:00
手瓜一十雪
a4ca89bdd6 fi: 2.6.4 2024-09-17 23:40:44 +08:00
手瓜一十雪
1a64e796bd release: 2.6.4 2024-09-17 23:17:19 +08:00
手瓜一十雪
a8b85a34f7 feat: 追平NT逻辑 2024-09-17 23:17:02 +08:00
手瓜一十雪
e7bec7d6b0 feat: systemPlatform标准化 2024-09-17 22:24:09 +08:00
手瓜一十雪
a582026037 release:2.6.3 2024-09-17 13:57:54 +08:00
手瓜一十雪
1a67a001c5 style: lint 2024-09-17 13:15:12 +08:00
手瓜一十雪
406deac592 fix:api外的推送事件 2024-09-17 13:14:18 +08:00
手瓜一十雪
e719ae0676 release: 2.6.2 2024-09-17 13:01:02 +08:00
手瓜一十雪
d8b7726440 release:2.6.1 2024-09-17 11:46:20 +08:00
手瓜一十雪
49f642e712 Merge pull request #379 from NapNeko/2.6.0
推进2.6.0大幅度重写
2024-09-17 11:43:13 +08:00
手瓜一十雪
70117016ce shell: 移除旧代码实现 2024-09-17 11:23:41 +08:00
手瓜一十雪
a4738f6281 feat: 开发依赖清理 2024-09-17 11:14:23 +08:00
手瓜一十雪
b1fc72d696 chore: 移除旧版本逻辑 2024-09-17 11:08:47 +08:00
手瓜一十雪
457c2c2b50 推进2.6.0大幅度重写 2024-09-17 11:06:07 +08:00
手瓜一十雪
48848d7d1a feat: 暂时砍掉V2Event 2024-09-17 10:59:28 +08:00
手瓜一十雪
55b07ca3ab feat: 彻底移除event旧实现 2024-09-17 09:23:19 +08:00
手瓜一十雪
a1d4882e18 feat: 规范化Promise 2024-09-17 09:19:45 +08:00
手瓜一十雪
3843795d8f Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-09-17 08:56:53 +08:00
手瓜一十雪
f2bf8d42da release: 2.5.5 2024-09-17 08:56:43 +08:00
Alen
a3b244e114 Merge pull request #378 from cnxysoft/upmain
fix: 下载文件失败
2024-09-17 03:50:22 +08:00
Alen
3093bdbc68 fix: 下载失败
优化下载逻辑
2024-09-17 03:39:43 +08:00
Alen
9ab0799283 Merge branch 'main' into upmain 2024-09-16 20:59:41 +08:00
手瓜一十雪
236bec11ed release: 2.5.4 2024-09-16 20:57:56 +08:00
手瓜一十雪
de48b0f940 Merge pull request #370 from NapNeko/28060
for: 28060
2024-09-16 20:54:54 +08:00
手瓜一十雪
4885d4db86 support: linux28060 2024-09-16 20:33:27 +08:00
手瓜一十雪
0c7bbda936 feat: Linux28060Appid 2024-09-16 20:29:04 +08:00
手瓜一十雪
fa07c2c1fb update: appid 2024-09-16 19:31:24 +08:00
手瓜一十雪
5d17a191f6 Merge branch 'main' into 28060 2024-09-16 19:12:10 +08:00
手瓜一十雪
67fb74d3c2 fix 2024-09-16 19:07:05 +08:00
手瓜一十雪
dc04cfc1b3 Revert "chore: workflow"
This reverts commit 58cd38c4a8.
2024-09-16 19:03:14 +08:00
手瓜一十雪
d61d481965 Merge branch 'main' into 28060 2024-09-16 19:02:01 +08:00
手瓜一十雪
6b346ee1de fix 2024-09-16 19:01:01 +08:00
手瓜一十雪
d0f248aaf9 fix 2024-09-16 18:53:26 +08:00
手瓜一十雪
85c9227515 Merge branch 'main' into 28060 2024-09-16 18:52:32 +08:00
手瓜一十雪
73b6d3be84 release: 2.5.3 2024-09-16 18:51:26 +08:00
手瓜一十雪
1ff6ce2343 feat: FetchOtherProfileLike 2024-09-16 18:51:05 +08:00
手瓜一十雪
c145935d46 feat: contact 2024-09-16 18:47:51 +08:00
手瓜一十雪
e9ede6924e release: 2.5.2 2024-09-16 18:12:00 +08:00
手瓜一十雪
515a21761d back: linux to 27254 2024-09-16 18:11:33 +08:00
Alen
8d6397028b Revert "style"
This reverts commit 7e74578312.
2024-09-15 17:31:01 +08:00
Alen
eb4828d81f Merge branch 'main' into upmain 2024-09-15 17:24:43 +08:00
Alen
7e74578312 style 2024-09-15 17:24:02 +08:00
Alen
640e3516d4 style 2024-09-15 17:23:10 +08:00
手瓜一十雪
bd295a4632 Merge branch 'main' into 28060 2024-09-15 17:15:03 +08:00
Alen
166c30fe2c Merge pull request #375 from cnxysoft/test
fix: friend_add
2024-09-15 16:49:10 +08:00
Alen
66c1bab629 fix: friend_add
修复该事件中user_id为0的问题
2024-09-15 16:47:46 +08:00
手瓜一十雪
66656304f9 fix 2024-09-15 16:24:08 +08:00
手瓜一十雪
07f66e379d Merge branch 'main' into 28060 2024-09-15 16:20:37 +08:00
手瓜一十雪
7ae8fd60c4 release: 2.5.1 2024-09-15 16:20:26 +08:00
手瓜一十雪
7275066994 feat: skip Qrcode When Login 2024-09-15 16:18:43 +08:00
手瓜一十雪
385adec186 Merge branch 'main' into 28060 2024-09-15 16:04:19 +08:00
手瓜一十雪
96b5bec5ab feat: revert 2024-09-15 16:04:10 +08:00
手瓜一十雪
6a9ec4e5f0 Merge branch 'main' into 28060 2024-09-15 15:52:52 +08:00
手瓜一十雪
d9851493df fix: #361 2024-09-15 15:51:23 +08:00
手瓜一十雪
efdb520414 Merge branch 'main' into 28060 2024-09-15 15:38:32 +08:00
Alen
5548644aeb Merge pull request #373 from cnxysoft/test
fix: bugs
2024-09-15 15:37:09 +08:00
Alen
e3fcd91b2d Merge branch 'main' into test 2024-09-15 15:29:50 +08:00
手瓜一十雪
2cae30ba88 Merge branch 'main' into 28060 2024-09-15 15:20:52 +08:00
手瓜一十雪
58cd38c4a8 chore: workflow 2024-09-15 15:20:44 +08:00
手瓜一十雪
3300304feb Merge branch 'main' into 28060 2024-09-15 15:00:42 +08:00
手瓜一十雪
f0e376d06b fix: 移除错误action 2024-09-15 14:55:18 +08:00
手瓜一十雪
16f7bb48f2 fix: launcher 28060 2024-09-15 14:34:47 +08:00
手瓜一十雪
7f383dd29b Merge branch 'main' into 28060 2024-09-15 09:49:49 +08:00
手瓜一十雪
3dc529edf4 fix: #369 2024-09-15 09:39:17 +08:00
手瓜一十雪
45dedb4872 fix: 28060 2024-09-15 09:35:10 +08:00
手瓜一十雪
afcdd01c0d fix: typo 9.9.15-28060 2024-09-14 19:22:36 +08:00
手瓜一十雪
1164877e9a Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-09-14 19:10:34 +08:00
手瓜一十雪
fe92a449ba feat: 准备适配9.9.15-28060版本 2024-09-14 19:10:17 +08:00
Alen
401b0e2bd0 fix: 部分语音播放速率异常 2024-09-14 17:50:09 +08:00
手瓜一十雪
cf9c71fcc1 feat: 准备适配9.9.15-28606 2024-09-14 17:24:52 +08:00
Alen
15a2400069 fix: 修复文件删除失败
此处为重复插入待删列表
2024-09-14 11:12:43 +08:00
Alen
d68a39b49e fix: 定义错误 2024-09-14 10:49:43 +08:00
Alen
066ca22e24 Merge pull request #362 from cnxysoft/upmain
fix: 点赞通知解析
2024-09-14 01:24:20 +08:00
Alen
0418b926fe fix: 点赞通知解析失败 2024-09-14 00:42:10 +08:00
手瓜一十雪
be40bbdf40 release: 2.5.0 2024-09-13 17:42:11 +08:00
手瓜一十雪
df4f42e79e fix: video name 2024-09-13 17:38:11 +08:00
Alen
5f80058f70 Merge pull request #360 from cnxysoft/upmain
fix: bugs
2024-09-13 17:30:23 +08:00
Alen
0cbe59052d Merge branch 'main' into upmain 2024-09-13 17:25:59 +08:00
Alen
af28a26e37 fix: 无法发送url视频 2024-09-13 17:22:32 +08:00
Alen
70c596df93 fix: headers分割 2024-09-13 16:11:15 +08:00
手瓜一十雪
748b51428c feat: createUidFromTinyId 2024-09-13 16:05:25 +08:00
手瓜一十雪
8ad746397c feat: JoinDragonGroupEmoji 2024-09-13 15:35:05 +08:00
手瓜一十雪
45baed2f9a tag: deprecated 2024-09-13 15:30:04 +08:00
手瓜一十雪
74185f2d33 fix 2024-09-13 14:05:04 +08:00
手瓜一十雪
90a91e4105 fix 2024-09-13 13:54:12 +08:00
手瓜一十雪
11aa3a0315 feat: fetchOtherProfileLike 2024-09-12 20:05:46 +08:00
手瓜一十雪
0c2e39214f style: lint 2024-09-12 19:55:56 +08:00
手瓜一十雪
d89620d7a6 style: folder 2024-09-12 19:55:26 +08:00
手瓜一十雪
edf80775b7 release: v2.4.9 2024-09-12 19:47:57 +08:00
手瓜一十雪
46e56ac726 remove: polyFill 2024-09-12 19:35:54 +08:00
手瓜一十雪
40b2f6bfd6 release: 2.4.7 2024-09-12 18:31:11 +08:00
手瓜一十雪
911e4921e2 fix: 删除旧文件 2024-09-12 18:15:38 +08:00
手瓜一十雪
1db9bb419d fix: 一处异常字段 2024-09-12 10:55:18 +08:00
手瓜一十雪
c6241a94e3 style: lint 2024-09-12 09:28:41 +08:00
手瓜一十雪
1cbf75ca36 style: lint 2024-09-12 09:28:26 +08:00
手瓜一十雪
8f85c897c8 refactor: SysMsg 2024-09-12 09:20:10 +08:00
80 changed files with 765 additions and 1170 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -9,7 +9,7 @@ if %errorLevel% == 0 (
exit exit
) )
set NAPCAT_PATCH_PATH=%cd%\patchNapCat.js set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe

View File

@@ -9,7 +9,7 @@ if %errorLevel% == 0 (
exit exit
) )
set NAPCAT_PATCH_PATH=%cd%\patchNapCat.js set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
@@ -34,6 +34,6 @@ if not exist "%QQpath%" (
) )
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/% set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > %NAPCAT_LOAD_PATH% echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1 "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1

View File

@@ -1 +0,0 @@
require('./launcher.node').load('external_index', module);

23
launcher/qqnt.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "qq-chat",
"version": "9.9.15-28131",
"type": "module",
"private": true,
"description": "QQ",
"productName": "QQ",
"author": {
"name": "Tencent",
"email": "QQ-Team@tencent.com"
},
"homepage": "https://im.qq.com",
"sideEffects": true,
"bin": {
"qd": "externals/devtools/cli/index.js"
},
"main": "./loadNapCat.js",
"buildVersion": "28131",
"isPureShell": true,
"isByteCodeShell": true,
"platform": "win32",
"eleArch": "x64"
}

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "2.4.6", "version": "2.6.13",
"icon": "./logo.png", "icon": "./logo.png",
"authors": [ "authors": [
{ {

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "2.4.6", "version": "2.6.13",
"scripts": { "scripts": {
"build:framework": "vite build --mode framework", "build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell", "build:shell": "vite build --mode shell",
@@ -11,9 +11,6 @@
"depend": "cd dist && npm install --omit=dev" "depend": "cd dist && npm install --omit=dev"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.24.7",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-decorators": "^7.24.7",
"@babel/preset-typescript": "^7.24.7", "@babel/preset-typescript": "^7.24.7",
"@log4js-node/log4js-api": "^1.0.2", "@log4js-node/log4js-api": "^1.0.2",
"@protobuf-ts/plugin": "^2.9.4", "@protobuf-ts/plugin": "^2.9.4",
@@ -21,9 +18,7 @@
"@rollup/plugin-typescript": "^11.1.6", "@rollup/plugin-typescript": "^11.1.6",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/figlet": "^1.5.8",
"@types/fluent-ffmpeg": "^2.1.24", "@types/fluent-ffmpeg": "^2.1.24",
"@types/jest": "^29.5.12",
"@types/node": "^22.0.1", "@types/node": "^22.0.1",
"@types/qrcode-terminal": "^0.12.2", "@types/qrcode-terminal": "^0.12.2",
"@types/ws": "^8.5.12", "@types/ws": "^8.5.12",
@@ -32,14 +27,8 @@
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-import-resolver-typescript": "^3.6.1", "eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
"i": "^0.3.7",
"javascript-obfuscator": "^4.1.0",
"rollup": "^4.13.2",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-obfuscator": "^1.1.0",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite": "^5.2.6", "vite": "^5.2.6",
"vite-plugin-babel": "^1.2.0",
"vite-plugin-cp": "^4.0.8", "vite-plugin-cp": "^4.0.8",
"vite-plugin-dts": "^3.8.2", "vite-plugin-dts": "^3.8.2",
"vite-tsconfig-paths": "^4.3.2" "vite-tsconfig-paths": "^4.3.2"

View File

@@ -1,45 +0,0 @@
# Dont Use This Script
# 2024.7.3
function Get-QQpath {
try {
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
$uninstallString = $key.UninstallString
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
}
catch {
throw "get QQ path error: $_"
}
}
function Select-QQPath {
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$dialogTitle = "Select QQ.exe"
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
$filePicker.Title = $dialogTitle
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
$filePicker.FilterIndex = 1
$null = $filePicker.ShowDialog()
if (-not ($filePicker.FileName)) {
throw "User did not select an .exe file."
}
return $filePicker.FileName
}
$params = $args -join " "
Try {
$QQpath = Get-QQpath
}
Catch {
$QQpath = Select-QQPath
}
if (!(Test-Path $QQpath)) {
throw "provided QQ path is invalid: $QQpath"
}
$Bootfile = Join-Path $PSScriptRoot "napcat.mjs"
$env:ELECTRON_RUN_AS_NODE = 1
$commandInfo = Get-Command $QQpath -ErrorAction Stop
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{& chcp 65001;& '$($commandInfo.Path)' --enable-logging }"

View File

@@ -1,90 +0,0 @@
@echo off
REM 检查当前会话是否具有管理员权限
openfiles >nul 2>&1
if %errorlevel% neq 0 (
REM 如果不是管理员,则重新启动脚本以管理员模式运行
echo 请求管理员权限...
powershell -Command "Start-Process cmd -ArgumentList '/c %~f0 %*' -Verb RunAs"
exit /b
)
REM 设置当前工作目录
cd /d %~dp0
REM 获取当前目录路径
set currentPath=%cd%
set currentPath=%currentPath:\=/%
REM 生成JavaScript代码
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
REM 将JavaScript代码保存到文件中
echo %jsCode% > loadScript.js
echo JavaScript code has been generated and saved to loadScript.js
REM 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
set NAPCAT_PATH=%cd%\loadScript.js
REM 获取QQ路径
:loop_read
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
set RetString=%%b
goto :napcat_boot
)
:napcat_boot
for %%a in (%RetString%) do (
set "pathWithoutUninstall=%%~dpa"
)
SET QQPath=%pathWithoutUninstall%QQ.exe
REM 拿不到QQ路径则退出
if not exist "%QQpath%" (
echo provided QQ path is invalid: %QQpath%
pause
exit /b
)
REM 收集dbghelp.dll路径和HASH信息
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 如果文件一致则跳过
if "%oldDllHash%" neq "%newDllHash%" (
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
if %errorlevel% equ 0 (
REM 文件占用则退出
echo dbghelp.dll is in use, cannot continue.
) else (
REM 文件未占用则尝试覆盖
copy /y "%newDllPath%" "%oldDllPath%"
if %errorlevel% neq 0 (
echo Failed to copy dbghelp.dll
pause
exit /b
) else (
echo dbghelp.dll has been copied to %QQdir%
)
)
)
REM 带参数启动QQ
REM 判断wt是否存在存在则通过wt启动不存在则通过cmd启动
REM %QQPath% --enable-logging %*
where wt >nul 2>nul
if %errorlevel% equ 0 (
wt "cmd" /c "%QQPath%" --enable-logging %*
) else (
"%QQPath%" --enable-logging %*
)

View File

@@ -1,123 +0,0 @@
# 检查当前会话是否具有管理员权限
function Test-Administrator {
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
(New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if (-not (Test-Administrator)) {
# 如果不是管理员,则重新启动脚本以管理员模式运行
$scriptPath = $myInvocation.MyCommand.Path
if (-not $scriptPath) {
$scriptPath = $PSCommandPath
}
$newProcess = New-Object System.Diagnostics.ProcessStartInfo "powershell";
$newProcess.Arguments = "-File `"$scriptPath`" $args"
$newProcess.Verb = "runas";
[System.Diagnostics.Process]::Start($newProcess);
exit
}
function Get-QQpath {
try {
$key = Get-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ"
$uninstallString = $key.UninstallString
return [System.IO.Path]::GetDirectoryName($uninstallString) + "\QQ.exe"
}
catch {
throw "get QQ path error: $_"
}
}
function Select-QQPath {
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$dialogTitle = "Select QQ.exe"
$filePicker = New-Object System.Windows.Forms.OpenFileDialog
$filePicker.Title = $dialogTitle
$filePicker.Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*"
$filePicker.FilterIndex = 1
$null = $filePicker.ShowDialog()
if (-not ($filePicker.FileName)) {
throw "User did not select an .exe file."
}
return $filePicker.FileName
}
# 设置当前工作目录
$scriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
Set-Location $scriptDirectory
# 获取当前目录路径
$currentPath = Get-Location
# 替换\为/
$currentPath = $currentPath -replace '\\', '/'
# 生成JavaScript代码
$jsCode = @"
(async () => {
await import('file:///$currentPath/napcat.mjs');
})();
"@
# 将JavaScript代码保存到文件中
$jsFilePath = Join-Path $currentPath "loadScript.js"
$jsCode | Out-File -FilePath $jsFilePath -Encoding UTF8
Write-Output "JavaScript code has been generated and saved to $jsFilePath"
# 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
$env:NAPCAT_PATH = $jsFilePath
$params = $args -join " "
Try {
$QQpath = Get-QQpath
}
Catch {
$QQpath = Select-QQPath
}
# 拿不到QQ路径则退出
if (!(Test-Path $QQpath)) {
Write-Output "provided QQ path is invalid: $QQpath"
Read-Host "Press any key to continue..."
exit
}
$commandInfo = Get-Command $QQpath -ErrorAction Stop
# 收集dbghelp.dll路径和HASH信息
$QQpath = Split-Path $QQpath
$oldDllPath = Join-Path $QQpath "dbghelp.dll"
$oldDllHash = Get-FileHash $oldDllPath -Algorithm MD5
$newDllPath = Join-Path $currentPath "dbghelp.dll"
$newDllHash = Get-FileHash $newDllPath -Algorithm MD5
# 如果文件一致则跳过
if ($oldDllHash.Hash -ne $newDllHash.Hash) {
$processes = Get-Process -Name QQ -ErrorAction SilentlyContinue
if ($processes) {
# 文件占用则退出
Write-Output "dbghelp.dll is in use by the following processes:"
$processes | ForEach-Object { Write-Output "$($_.Id) $($_.Name) $($_.Path)" }
Write-Output "dbghelp.dll is in use, cannot continue."
Read-Host "Press any key to continue..."
exit
} else {
# 文件未占用则尝试覆盖
try {
Copy-Item -Path "$newDllPath" -Destination "$oldDllPath" -Force
Write-Output "dbghelp.dll has been copied to $QQpath"
} catch {
Write-Output "Failed to copy dbghelp.dll: $_"
Read-Host "Press any key to continue..."
exit
}
}
}
# 带参数启动QQ
try {
Start-Process powershell -ArgumentList '-noexit', '-noprofile', "-command &{& chcp 65001;& '$($commandInfo.Path)' --enable-logging $params}" -NoNewWindow -ErrorAction Stop
} catch {
Write-Output "Failed to start process as administrator: $_"
Read-Host "Press any key to continue..."
}

View File

@@ -1,93 +0,0 @@
@echo off
REM 检查当前会话是否具有管理员权限
openfiles >nul 2>&1
if %errorlevel% neq 0 (
REM 如果不是管理员,则重新启动脚本以管理员模式运行
echo 请求管理员权限...
where wt >nul 2>nul
if %errorlevel% equ 0 (
powershell -Command "Start-Process cmd -ArgumentList ' /c %~f0 %*' -Verb RunAs"
) else (
powershell -Command "Start-Process wt -ArgumentList 'cmd /c %~f0 %*' -Verb RunAs"
)
REM wt "cmd" /c "%~f0 %*"
exit /b
)
REM 设置当前工作目录
cd /d %~dp0
REM 获取当前目录路径
set currentPath=%cd%
set currentPath=%currentPath:\=/%
REM 生成JavaScript代码
set "jsCode=(async () =^>await import('file:///%currentPath%/napcat.mjs'))();"
REM 将JavaScript代码保存到文件中
echo %jsCode% > loadScript.js
echo JavaScript code has been generated and saved to loadScript.js
REM 设置NAPCAT_PATH环境变量为 当前目录的loadScript.js地址
set NAPCAT_PATH=%cd%\loadScript.js
REM 获取QQ路径
:loop_read
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
set RetString=%%b
goto :napcat_boot
)
:napcat_boot
for %%a in (%RetString%) do (
set "pathWithoutUninstall=%%~dpa"
)
SET QQPath=%pathWithoutUninstall%QQ.exe
REM 拿不到QQ路径则退出
if not exist "%QQpath%" (
echo provided QQ path is invalid: %QQpath%
pause
exit /b
)
REM 收集dbghelp.dll路径和HASH信息
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 如果文件一致则跳过
if "%oldDllHash%" neq "%newDllHash%" (
tasklist /fi "imagename eq QQ.exe" 2>nul | find /i "QQ.exe" >nul
if %errorlevel% equ 0 (
REM 文件占用则退出
echo dbghelp.dll is in use, cannot continue.
) else (
REM 文件未占用则尝试覆盖
copy /y "%newDllPath%" "%oldDllPath%"
if %errorlevel% neq 0 (
echo Failed to copy dbghelp.dll
pause
exit /b
) else (
echo dbghelp.dll has been copied to %QQdir%
)
)
)
REM 带参数启动QQ
REM 判断wt是否存在存在则通过wt启动不存在则通过cmd启动
REM %QQPath% --enable-logging %*
chcp 65001
"%QQPath%" --enable-logging %*

View File

@@ -1,77 +0,0 @@
@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
)
)
)

View File

@@ -1,10 +0,0 @@
@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

@@ -1,13 +0,0 @@
@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 %*

Binary file not shown.

View File

@@ -1,4 +1,3 @@
import fs from 'fs';
import fsPromise from 'fs/promises'; import fsPromise from 'fs/promises';
import path from 'node:path'; import path from 'node:path';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
@@ -12,7 +11,7 @@ const FFMPEG_PATH = process.env.FFMPEG_PATH || 'ffmpeg';
async function guessDuration(pttPath: string, logger: LogWrapper) { async function guessDuration(pttPath: string, logger: LogWrapper) {
const pttFileInfo = await fsPromise.stat(pttPath); const pttFileInfo = await fsPromise.stat(pttPath);
let duration = Math.max(1, Math.floor(pttFileInfo.size / 1024 / 3)); // 3kb/s const duration = Math.max(1, Math.floor(pttFileInfo.size / 1024 / 3)); // 3kb/s
logger.log('通过文件大小估算语音的时长:', duration); logger.log('通过文件大小估算语音的时长:', duration);
return duration; return duration;
} }
@@ -20,7 +19,7 @@ async function guessDuration(pttPath: string, logger: LogWrapper) {
async function convert(filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> { async function convert(filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> {
return new Promise<Buffer>((resolve, reject) => { return new Promise<Buffer>((resolve, reject) => {
const cp = spawn(FFMPEG_PATH, ['-y', '-i', filePath, '-ar', '24000', '-ac', '1', '-f', 's16le', pcmPath]); const cp = spawn(FFMPEG_PATH, ['-y', '-i', filePath, '-ar', '24000', '-ac', '1', '-f', 's16le', pcmPath]);
cp.on('error', err => { cp.on('error', (err: Error) => {
logger.log('FFmpeg处理转换出错: ', err.message); logger.log('FFmpeg处理转换出错: ', err.message);
reject(err); reject(err);
}); });
@@ -41,12 +40,14 @@ async function convert(filePath: string, pcmPath: string, logger: LogWrapper): P
}); });
} }
async function handleWavFile(file: Buffer, filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> { async function handleWavFile(
file: Buffer, filePath: string, pcmPath: string, logger: LogWrapper
): Promise<{input: Buffer, sampleRate: number}> {
const { fmt } = getWavFileInfo(file); const { fmt } = getWavFileInfo(file);
if (!ALLOW_SAMPLE_RATE.includes(fmt.sampleRate)) { if (!ALLOW_SAMPLE_RATE.includes(fmt.sampleRate)) {
return await convert(filePath, pcmPath, logger); return { input: await convert(filePath, pcmPath, logger), sampleRate: 24000 };
} }
return file; return { input: file, sampleRate: fmt.sampleRate };
} }
export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: LogWrapper) { export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: LogWrapper) {
@@ -56,8 +57,10 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
if (!isSilk(file)) { if (!isSilk(file)) {
logger.log(`语音文件${filePath}需要转换成silk`); logger.log(`语音文件${filePath}需要转换成silk`);
const pcmPath = `${pttPath}.pcm`; const pcmPath = `${pttPath}.pcm`;
const input = isWav(file) ? await handleWavFile(file, filePath, pcmPath, logger) : await convert(filePath, pcmPath, logger); const { input, sampleRate } = isWav(file)
const silk = await encode(input, 24000); ? (await handleWavFile(file, filePath, pcmPath, logger))
: { input: await convert(filePath, pcmPath, logger), sampleRate: 24000 };
const silk = await encode(input, sampleRate);
await fsPromise.writeFile(pttPath, silk.data); await fsPromise.writeFile(pttPath, silk.data);
logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration); logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);
return { return {
@@ -80,7 +83,7 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
}; };
} }
} catch (error: any) { } catch (error: any) {
logger.logError('convert silk failed', error.stack); logger.logError.bind(logger)('convert silk failed', error.stack);
return {}; return {};
} }
} }

View File

@@ -40,7 +40,7 @@ export abstract class ConfigBase<T> {
fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8')); fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8'));
logger.log(`[Core] [Config] 配置文件创建成功!\n`); logger.log(`[Core] [Config] 配置文件创建成功!\n`);
} catch (e: any) { } catch (e: any) {
logger.logError(`[Core] [Config] 创建配置文件时发生错误:`, e.message); logger.logError.bind(logger)(`[Core] [Config] 创建配置文件时发生错误:`, e.message);
} }
} }
try { try {
@@ -49,9 +49,9 @@ export abstract class ConfigBase<T> {
return this.configData; return this.configData;
} catch (e: any) { } catch (e: any) {
if (e instanceof SyntaxError) { if (e instanceof SyntaxError) {
logger.logError(`[Core] [Config] 配置文件格式错误,请检查配置文件:`, e.message); logger.logError.bind(logger)(`[Core] [Config] 配置文件格式错误,请检查配置文件:`, e.message);
} else { } else {
logger.logError(`[Core] [Config] 读取配置文件时发生错误:`, e.message); logger.logError.bind(logger)(`[Core] [Config] 读取配置文件时发生错误:`, e.message);
} }
return {} as T; return {} as T;
} }
@@ -66,7 +66,7 @@ export abstract class ConfigBase<T> {
try { try {
fs.writeFileSync(configPath, JSON.stringify(newConfigData, this.getKeys(), 2)); fs.writeFileSync(configPath, JSON.stringify(newConfigData, this.getKeys(), 2));
} catch (e: any) { } catch (e: any) {
logger.logError(`保存配置文件 ${configPath} 时发生错误:`, e.message); logger.logError.bind(logger)(`保存配置文件 ${configPath} 时发生错误:`, e.message);
} }
} }
} }

View File

@@ -1,133 +0,0 @@
import type { NodeIQQNTWrapperSession, WrapperNodeApi } from '@/core/wrapper';
import EventEmitter from 'node:events';
export type ListenerClassBase = Record<string, string>;
export interface ListenerIBase {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: any): ListenerClassBase;
[key: string]: any;
}
export class NTEventWrapperV2 extends EventEmitter {
private wrapperApi: WrapperNodeApi;
private wrapperSession: NodeIQQNTWrapperSession;
private listenerRefStorage = new Map<string, ListenerIBase>();
constructor(WrapperApi: WrapperNodeApi, WrapperSession: NodeIQQNTWrapperSession) {
super();
this.on('error', () => {
});
this.wrapperApi = WrapperApi;
this.wrapperSession = WrapperSession;
}
dispatcherListener(ListenerEvent: string, ...args: any[]) {
this.emit(ListenerEvent, ...args);
}
createProxyDispatch(ListenerMainName: string) {
const dispatcherListener = this.dispatcherListener.bind(this);
return new Proxy({}, {
get(_target: any, prop: any, _receiver: any) {
return (...args: any[]) => {
dispatcherListener(ListenerMainName + '/' + prop, ...args);
};
},
});
}
async getOrInitListener<T>(listenerMainName: string): Promise<T> {
const ListenerType = this.wrapperApi[listenerMainName];
//获取NTQQ 外部 Listener包装
if (!ListenerType) throw new Error('Init Listener not found');
let Listener = this.listenerRefStorage.get(listenerMainName);
//判断是否已创建 创建则跳过
if (!Listener && ListenerType) {
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
if (!Listener) throw new Error('Init Listener failed');
//实例化NTQQ Listener外包装
const ServiceSubName = /^NodeIKernel(.*?)Listener$/.exec(listenerMainName)![1];
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
//添加Listener到NTQQ
addfunc!(Listener as T);
this.listenerRefStorage.set(listenerMainName, Listener);
//保存Listener实例
}
return Listener as T;
}
async createEventWithListener<EventType extends (...args: any) => any, ListenerType extends (...args: any) => any>
(
eventName: string,
listenerName: string,
waitTimes = 1,
timeout: number = 3000,
checker: (...args: Parameters<ListenerType>) => boolean,
...eventArg: Parameters<EventType>
) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(async (resolve, reject) => {
const ListenerNameList = listenerName.split('/');
const ListenerMainName = ListenerNameList[0];
//const ListenerSubName = ListenerNameList[1];
this.getOrInitListener<ListenerType>(ListenerMainName);
let complete = 0;
const 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 Timeouter = setTimeout(databack, timeout);
const callback = (...args: Parameters<ListenerType>) => {
if (checker(...args)) {
complete++;
if (complete >= waitTimes) {
clearTimeout(Timeouter);
this.removeListener(listenerName, callback);
databack();
}
}
};
this.on(listenerName, callback);
const EventFunc = this.createEventFunction<EventType>(eventName);
retEvent = await EventFunc!(...(eventArg as any[]));
});
}
private createEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined {
const eventNameArr = eventName.split('/');
type eventType = {
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>> }
}
if (eventNameArr.length > 1) {
const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', '');
const eventName = eventNameArr[1];
//getNodeIKernelGroupListener,GroupService
//console.log('2', eventName);
const services = (this.wrapperSession as unknown as eventType)[serviceName]();
const event = services[eventName]
//重新绑定this
.bind(services);
if (event) {
return event as T;
}
return undefined;
}
}
async callEvent<EventType extends (...args: any[]) => Promise<any> | any>(
EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
return new Promise<Awaited<ReturnType<EventType>>>((resolve) => {
const EventFunc = this.createEventFunction<EventType>(EventName);
EventFunc!(...args).then((retData: Awaited<ReturnType<EventType>> | PromiseLike<Awaited<ReturnType<EventType>>>) => resolve(retData));
});
}
}
//NTEvent2.0

View File

@@ -236,7 +236,7 @@ export class NTEventWrapper {
this.createListenerFunction(ListenerMainName); this.createListenerFunction(ListenerMainName);
const eventFunction = this.createEventFunction(serviceAndMethod); const eventFunction = this.createEventFunction(serviceAndMethod);
retEvent = await eventFunction!(...(args)); retEvent = await eventFunction!(...(args));
if (!checkerEvent(retEvent)) { if (!checkerEvent(retEvent) && timeoutRef.hasRef()) {
clearTimeout(timeoutRef); clearTimeout(timeoutRef);
reject( reject(
new Error( new Error(
@@ -250,86 +250,8 @@ export class NTEventWrapper {
), ),
); );
} }
}, },
); );
} }
/*
async callNormalEvent<
Service extends keyof ServiceNamingMapping,
ServiceMethod extends Exclude<keyof ServiceNamingMapping[Service], symbol>,
Listener extends keyof ListenerNamingMapping,
ListenerMethod extends Exclude<keyof ListenerNamingMapping[Listener], symbol>,
// eslint-disable-next-line
// @ts-ignore
EventType extends (...args: any) => any = ServiceNamingMapping[Service][ServiceMethod],
// eslint-disable-next-line
// @ts-ignore
ListenerType extends (...args: any) => any = ListenerNamingMapping[Listener][ListenerMethod]
>(
serviceAndMethod: `${Service}/${ServiceMethod}`,
listenerAndMethod: `${Listener}/${ListenerMethod}`,
waitTimes = 1,
timeout: number = 3000,
checker: (...args: Parameters<ListenerType>) => boolean,
...args: Parameters<EventType>
) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
async (resolve, reject) => {
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:' +
serviceAndMethod +
' ListenerName:' +
listenerAndMethod +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
),
);
} else {
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
}
};
const ListenerNameList = listenerAndMethod.split('/');
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
const Timeouter = setTimeout(databack, timeout);
const eventCallbak = {
timeout: timeout,
createtime: Date.now(),
checker: checker,
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>(serviceAndMethod);
retEvent = await EventFunc!(...(args as any[]));
},
);
}
*/
} }

View File

@@ -123,7 +123,7 @@ export interface HttpDownloadOptions {
headers?: Record<string, string> | string; headers?: Record<string, string> | string;
} }
export async function httpDownload(options: string | HttpDownloadOptions): Promise<Buffer> { async function tryDownload(options: string | HttpDownloadOptions, useReferer: boolean = false): Promise<Response> {
// const chunks: Buffer[] = []; // const chunks: Buffer[] = [];
let url: string; let url: string;
let headers: Record<string, string> = { let headers: Record<string, string> = {
@@ -142,15 +142,26 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi
} }
} }
} }
if (useReferer && !headers['Referer']) {
headers['Referer'] = url;
}
const fetchRes = await fetch(url, { headers }).catch((err) => { const fetchRes = await fetch(url, { headers }).catch((err) => {
if (err.cause) { if (err.cause) {
throw err.cause; throw err.cause;
} }
throw err; throw err;
}); });
if (!fetchRes.ok) throw new Error(`下载文件失败: ${fetchRes.statusText}`); return fetchRes;
}
const blob = await fetchRes.blob(); export async function httpDownload(options: string | HttpDownloadOptions): Promise<Buffer> {
const useReferer = typeof options === 'string';
let resp = await tryDownload(options);
if (resp.status === 403 && useReferer) {
resp = await tryDownload(options, true);
}
if (!resp.ok) throw new Error(`下载文件失败: ${resp.statusText}`);
const blob = await resp.blob();
const buffer = await blob.arrayBuffer(); const buffer = await blob.arrayBuffer();
return Buffer.from(buffer); return Buffer.from(buffer);
} }
@@ -242,7 +253,6 @@ export async function uri2local(dir: string, uri: string, filename: string | und
const filenameTemp = tempName + fileExt; const filenameTemp = tempName + fileExt;
const filePath = path.join(dir, filenameTemp); const filePath = path.join(dir, filenameTemp);
fs.copyFileSync(HandledUri, filePath); fs.copyFileSync(HandledUri, filePath);
//console.log('复制文件到临时文件', HandledUri, filePath);
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
} }
//接下来都要有文件名 //接下来都要有文件名
@@ -250,7 +260,7 @@ export async function uri2local(dir: string, uri: string, filename: string | und
if (UriType == FileUriType.Remote) { if (UriType == FileUriType.Remote) {
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname)); const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
if (pathInfo.name) { if (pathInfo.name) {
let pathlen = 200 - dir.length - pathInfo.name.length; const pathlen = 200 - dir.length - pathInfo.name.length;
filename = pathlen > 0 ? pathInfo.name.substring(0, pathlen) : pathInfo.name.substring(pathInfo.name.length, pathInfo.name.length - 10);//过长截断 filename = pathlen > 0 ? pathInfo.name.substring(0, pathlen) : pathInfo.name.substring(pathInfo.name.length, pathInfo.name.length - 10);//过长截断
if (pathInfo.ext) { if (pathInfo.ext) {
filename += pathInfo.ext; filename += pathInfo.ext;
@@ -260,7 +270,6 @@ export async function uri2local(dir: string, uri: string, filename: string | und
const fileExt = path.extname(HandledUri).replace(/[/\\:*?"<>|]/g, '_').substring(0, 10); const fileExt = path.extname(HandledUri).replace(/[/\\:*?"<>|]/g, '_').substring(0, 10);
const filePath = path.join(dir, tempName + fileExt); const filePath = path.join(dir, tempName + fileExt);
const buffer = await httpDownload(HandledUri); const buffer = await httpDownload(HandledUri);
//fs.writeFileSync(filePath, buffer);
//没有文件就创建 //没有文件就创建
fs.writeFileSync(filePath, buffer, { flag: 'wx' }); fs.writeFileSync(filePath, buffer, { flag: 'wx' });
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };

View File

@@ -163,34 +163,51 @@ export function isEqual(obj1: any, obj2: any) {
export function getDefaultQQVersionConfigInfo(): QQVersionConfigType { export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
if (os.platform() === 'linux') { if (os.platform() === 'linux') {
return { return {
baseVersion: '3.2.12-27597', baseVersion: '3.2.12.28060',
curVersion: '3.2.12-27597', curVersion: '3.2.12.28060',
prevVersion: '', prevVersion: '',
onErrorVersions: [], onErrorVersions: [],
buildId: '27597', buildId: '27254',
};
}
if (os.platform() === 'darwin') {
return {
baseVersion: '6.9.53.28060',
curVersion: '6.9.53.28060',
prevVersion: '',
onErrorVersions: [],
buildId: '28060',
}; };
} }
return { return {
baseVersion: '9.9.15-27597', baseVersion: '9.9.15-28131',
curVersion: '9.9.15-27597', curVersion: '9.9.15-28131',
prevVersion: '', prevVersion: '',
onErrorVersions: [], onErrorVersions: [],
buildId: '27597', buildId: '28131',
}; };
} }
export function getQQPackageInfoPath(exePath: string = ''): string { export function getQQPackageInfoPath(exePath: string = '', version?: string): string {
let packagePath;
if (os.platform() === 'darwin') { if (os.platform() === 'darwin') {
return path.join(path.dirname(exePath), '..', 'Resources', 'app', 'package.json'); packagePath = path.join(path.dirname(exePath), '..', 'Resources', 'app', 'package.json');
} else if (os.platform() === 'linux') {
packagePath = path.join(path.dirname(exePath), './resources/app/package.json');
} else { } else {
return path.join(path.dirname(exePath), 'resources', 'app', 'package.json'); packagePath = path.join(path.dirname(exePath), './versions/' + version + '/resources/app/package.json');
} }
//下面是老版本兼容 未来去掉
if (!fs.existsSync(packagePath)) {
packagePath = path.join(path.dirname(exePath), './resources/app/versions/' + version + '/package.json');
}
return packagePath;
} }
export function getQQVersionConfigPath(exePath: string = ''): string | undefined { export function getQQVersionConfigPath(exePath: string = ''): string | undefined {
let configVersionInfoPath; let configVersionInfoPath;
if (os.platform() === 'win32') { if (os.platform() === 'win32') {
configVersionInfoPath = path.join(path.dirname(exePath), 'resources', 'app', 'versions', 'config.json'); configVersionInfoPath = path.join(path.dirname(exePath), 'versions', 'config.json');
} else if (os.platform() === 'darwin') { } else if (os.platform() === 'darwin') {
const userPath = os.homedir(); const userPath = os.homedir();
const appDataPath = path.resolve(userPath, './Library/Application Support/QQ'); const appDataPath = path.resolve(userPath, './Library/Application Support/QQ');
@@ -203,13 +220,18 @@ export function getQQVersionConfigPath(exePath: string = ''): string | undefined
if (typeof configVersionInfoPath !== 'string') { if (typeof configVersionInfoPath !== 'string') {
return undefined; return undefined;
} }
//老版本兼容 未来去掉
if (!fs.existsSync(configVersionInfoPath)) {
configVersionInfoPath = path.join(path.dirname(exePath), './resources/app/versions/config.json');
}
if (!fs.existsSync(configVersionInfoPath)) { if (!fs.existsSync(configVersionInfoPath)) {
return undefined; return undefined;
} }
return configVersionInfoPath; return configVersionInfoPath;
} }
export function calcQQLevel(level: QQLevel) { export function calcQQLevel(level?: QQLevel) {
if (!level) return 0;
const { crownNum, sunNum, moonNum, starNum } = level; const { crownNum, sunNum, moonNum, starNum } = level;
return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum; return crownNum * 64 + sunNum * 16 + moonNum * 4 + starNum;
} }

View File

@@ -19,14 +19,16 @@ export class QQBasicInfoWrapper {
//基础目录获取 //基础目录获取
this.context = context; this.context = context;
this.QQMainPath = process.execPath; this.QQMainPath = process.execPath;
this.QQPackageInfoPath = getQQPackageInfoPath(this.QQMainPath);
this.QQVersionConfigPath = getQQVersionConfigPath(this.QQMainPath); this.QQVersionConfigPath = getQQVersionConfigPath(this.QQMainPath);
//基础信息获取 无快更则启用默认模板填充 //基础信息获取 无快更则启用默认模板填充
this.isQuickUpdate = !!this.QQVersionConfigPath; this.isQuickUpdate = !!this.QQVersionConfigPath;
this.QQVersionConfig = this.isQuickUpdate this.QQVersionConfig = this.isQuickUpdate
? JSON.parse(fs.readFileSync(this.QQVersionConfigPath!).toString()) ? JSON.parse(fs.readFileSync(this.QQVersionConfigPath!).toString())
: getDefaultQQVersionConfigInfo(); : getDefaultQQVersionConfigInfo();
this.QQPackageInfoPath = getQQPackageInfoPath(this.QQMainPath, this.QQVersionConfig?.curVersion);
this.QQPackageInfo = JSON.parse(fs.readFileSync(this.QQPackageInfoPath).toString()); this.QQPackageInfo = JSON.parse(fs.readFileSync(this.QQPackageInfoPath).toString());
const { appid: IQQVersionAppid, qua: IQQVersionQua } = this.getAppidV2(); const { appid: IQQVersionAppid, qua: IQQVersionQua } = this.getAppidV2();
this.QQVersionAppid = IQQVersionAppid; this.QQVersionAppid = IQQVersionAppid;
@@ -65,11 +67,11 @@ export class QQBasicInfoWrapper {
getAppidInternal() { getAppidInternal() {
switch (systemPlatform) { switch (systemPlatform) {
case 'linux': case 'linux':
return '537243600'; return '537246140';
case 'darwin': case 'darwin':
return '537243441'; return '537246140';
default: default:
return '537243538'; return '537246092';
} }
} }

View File

@@ -1,12 +1,8 @@
import os from 'node:os'; import os from 'node:os';
import path from 'node:path'; import path from 'node:path';
import { networkInterfaces } from 'os';
import { randomUUID } from 'crypto';
// 缓解Win7设备兼容性问题 // 缓解Win7设备兼容性问题
let osName: string; let osName: string;
// 设备ID
let machineId: Promise<string>;
try { try {
osName = os.hostname(); osName = os.hostname();
@@ -14,54 +10,6 @@ try {
osName = 'NapCat'; // + crypto.randomUUID().substring(0, 4); osName = 'NapCat'; // + crypto.randomUUID().substring(0, 4);
} }
const invalidMacAddresses = new Set([
'00:00:00:00:00:00',
'ff:ff:ff:ff:ff:ff',
'ac:de:48:00:11:22',
]);
function validateMacAddress(candidate: string): boolean {
// eslint-disable-next-line no-useless-escape
const tempCandidate = candidate.replace(/\-/g, ':').toLowerCase();
return !invalidMacAddresses.has(tempCandidate);
}
export async function getMachineId(): Promise<string> {
if (!machineId) {
machineId = (async () => {
const id = await getMacMachineId();
return id ?? randomUUID(); // fallback, generate a UUID
})();
}
return machineId;
}
export function getMac(): string {
const ifaces = networkInterfaces();
for (const name in ifaces) {
const networkInterface = ifaces[name];
if (networkInterface) {
for (const { mac } of networkInterface) {
if (validateMacAddress(mac)) {
return mac;
}
}
}
}
throw new Error('Unable to retrieve mac address (unexpected format)');
}
async function getMacMachineId(): Promise<string | undefined> {
try {
const crypto = await import('crypto');
const macAddress = getMac();
return crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex');
} catch (err) {
return undefined;
}
}
const homeDir = os.homedir(); const homeDir = os.homedir();

View File

@@ -1 +1 @@
export const napCatVersion = '2.4.6'; export const napCatVersion = '2.6.13';

View File

@@ -145,10 +145,19 @@ export class NTQQFileApi {
try { try {
videoInfo = await getVideoInfo(filePath, logger); videoInfo = await getVideoInfo(filePath, logger);
} catch (e) { } catch (e) {
logger.logError('获取视频信息失败,将使用默认值', e); logger.logError.bind(logger)('获取视频信息失败,将使用默认值', e);
} }
let newFilePath = filePath + '.mp4';
fs.renameSync(filePath, newFilePath); let fileExt = 'mp4';
try {
const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext;
if (tempExt) fileExt = tempExt;
} catch (e) {
this.context.logger.logError.bind(logger)('获取文件类型失败', e);
}
const newFilePath = filePath + '.' + fileExt;
fs.copyFileSync(filePath, newFilePath);
context.deleteAfterSentFiles.push(newFilePath);
filePath = newFilePath; filePath = newFilePath;
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO); const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
if (fileSize === 0) { if (fileSize === 0) {
@@ -188,11 +197,12 @@ export class NTQQFileApi {
thumbPath.set(0, _thumbPath); thumbPath.set(0, _thumbPath);
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : ''; const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : '';
context.deleteAfterSentFiles.push(path); context.deleteAfterSentFiles.push(path);
const uploadName = (fileName || _fileName).toLocaleLowerCase().endsWith('.' + fileExt.toLocaleLowerCase()) ? (fileName || _fileName) : (fileName || _fileName) + '.' + fileExt;
return { return {
elementType: ElementType.VIDEO, elementType: ElementType.VIDEO,
elementId: '', elementId: '',
videoElement: { videoElement: {
fileName: fileName || _fileName, fileName: uploadName,
filePath: path, filePath: path,
videoMd5: md5, videoMd5: md5,
thumbMd5, thumbMd5,
@@ -217,7 +227,7 @@ export class NTQQFileApi {
} }
if (converted) { if (converted) {
fsPromises.unlink(silkPath).then().catch( fsPromises.unlink(silkPath).then().catch(
(e) => this.context.logger.logError('删除临时文件失败', e) (e) => this.context.logger.logError.bind(this.context.logger)('删除临时文件失败', e)
); );
} }
return { return {

View File

@@ -32,13 +32,15 @@ export class NTQQGroupApi {
for (const group of this.groups) { for (const group of this.groups) {
this.groupCache.set(group.groupCode, group); this.groupCache.set(group.groupCode, group);
} }
// let text = await this.context.session.getMsgService().sendSsoCmdReqByContend(
// 'LightAppSvc.mini_app_share.AdaptShareInfo',
// JSON.stringify({ data: 'test' }));
// console.log(text);
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`); this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
} }
async getCoreAndBaseInfo(uids: string[]) {
return await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelProfileService/getCoreAndBaseInfo',
'nodeStore',
uids,
);
}
async fetchGroupEssenceList(groupCode: string) { async fetchGroupEssenceList(groupCode: string) {
const pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!; const pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return this.context.session.getGroupService().fetchGroupEssenceList({ return this.context.session.getGroupService().fetchGroupEssenceList({
@@ -266,6 +268,23 @@ export class NTQQGroupApi {
} }
return member; return member;
} }
async searchGroup(groupCode: string) {
const [, ret] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelSearchService/searchGroup',
'NodeIKernelSearchListener/onSearchGroupResult',
[{
keyWords: groupCode,
groupNum: 25,
exactSearch: false,
penetrate: ''
}],
(ret) => ret.result === 0,
(params) => !!params.groupInfos.find(g => g.groupCode === groupCode),
1,
5000
);
return ret.groupInfos.find(g => g.groupCode === groupCode);
}
async getGroupMemberEx(GroupCode: string, uid: string, forced = false, retry = 2) { async getGroupMemberEx(GroupCode: string, uid: string, forced = false, retry = 2) {
const data = await solveAsyncProblem((eventWrapper: NTEventWrapper, GroupCode: string, uid: string, forced = false) => { const data = await solveAsyncProblem((eventWrapper: NTEventWrapper, GroupCode: string, uid: string, forced = false) => {
return eventWrapper.callNormalEventV2( return eventWrapper.callNormalEventV2(

View File

@@ -94,6 +94,7 @@ export class NTQQMsgApi {
pageLimit: 1, pageLimit: 1,
}); });
} }
//@deprecated
async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) { async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z); return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
} }

View File

@@ -11,7 +11,13 @@ export class NTQQUserApi {
this.context = context; this.context = context;
this.core = core; this.core = core;
} }
//self_tind格式
async createUidFromTinyId(tinyId: string) {
return this.context.session.getMsgService().createUidFromTinyId(this.core.selfInfo.uin, tinyId);
}
async getStatusByUid(uid: string) {
return this.context.session.getProfileService().getStatus(uid);
}
async getProfileLike(uid: string) { async getProfileLike(uid: string) {
return this.context.session.getProfileLikeService().getBuddyProfileLike({ return this.context.session.getProfileLikeService().getBuddyProfileLike({
friendUids: [uid], friendUids: [uid],
@@ -24,7 +30,18 @@ export class NTQQUserApi {
limit: 20, limit: 20,
}); });
} }
async fetchOtherProfileLike(uid: string) {
return this.context.session.getProfileLikeService().getBuddyProfileLike({
friendUids: [uid],
basic: 1,
vote: 1,
favorite: 0,
userProfile: 0,
type: 1,
start: 0,
limit: 20,
});
}
async setLongNick(longNick: string) { async setLongNick(longNick: string) {
return this.context.session.getProfileService().setLongNick(longNick); return this.context.session.getProfileService().setLongNick(longNick);
} }

View File

@@ -264,7 +264,7 @@ export class NTQQWebApi {
}); });
} }
} else { } else {
this.context.logger.logError('获取龙王信息失败'); this.context.logger.logError.bind(this.context.logger)('获取龙王信息失败');
} }
} }
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) { if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
@@ -280,7 +280,7 @@ export class NTQQWebApi {
}); });
} }
} else { } else {
this.context.logger.logError('获取群聊之火失败'); this.context.logger.logError.bind(this.context.logger)('获取群聊之火失败');
} }
} }
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) { if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
@@ -296,7 +296,7 @@ export class NTQQWebApi {
}); });
} }
} else { } else {
this.context.logger.logError('获取群聊炽焰失败'); this.context.logger.logError.bind(this.context.logger)('获取群聊炽焰失败');
} }
} }
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) { if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
@@ -312,7 +312,7 @@ export class NTQQWebApi {
}); });
} }
} else { } else {
this.context.logger.logError('获取快乐源泉失败'); this.context.logger.logError.bind(this.context.logger)('获取快乐源泉失败');
} }
} }

View File

@@ -959,3 +959,18 @@ export interface TmpChatInfo {
sessionType: number; sessionType: number;
sig: string; sig: string;
} }
export interface MsgReqType {
peer: Peer,
byType: number,
msgId: string,
msgSeq: string,
msgTime: string,
clientSeq: string,
cnt: number,
queryOrder: boolean,
includeSelf: boolean,
includeDeleteMsg: boolean,
extraCnt: number
}
//getMsgsIncludeSelf Peer必须 byType 1
//getMsgsWithMsgTimeAndClientSeqForC2C Peer必须 byType 3

View File

@@ -1,14 +1,15 @@
export interface IdMusicSignPostData { export interface IdMusicSignPostData {
type: 'qq' | '163', type: 'qq' | '163' | 'kugou' | 'migu' | 'kuwo',
id: string | number, id: string | number,
} }
export interface CustomMusicSignPostData { export interface CustomMusicSignPostData {
type: 'custom', type: 'qq' | '163' | 'kugou' | 'migu' | 'kuwo' | 'custom',
id: undefined,
url: string, url: string,
audio: string, audio?: string,
title: string, title?: string,
image?: string, image: string,
singer?: string singer?: string
} }

View File

@@ -216,7 +216,7 @@ export interface BuddyProfileLikeReq {
userProfile: number; userProfile: number;
type: number; type: number;
start: number; start: number;
limit: number; limit?: number;
} }
export interface QQLevel { export interface QQLevel {

View File

@@ -1,14 +1,18 @@
{ {
"3.2.12-27597": { "9.9.15-28060": {
"appid": 537243600, "appid": 537246092,
"qua": "V1_LNX_NQ_3.2.12_27597_GW_B" "qua": "V1_WIN_NQ_9.9.15_28060_GW_B"
}, },
"9.9.15-27597": { "9.9.15-28131": {
"appid": 537243441, "appid": 537246092,
"qua": "V1_WIN_NQ_9.9.15_27597_GW_B" "qua": "V1_WIN_NQ_9.9.15_28131_GW_B"
}, },
"6.9.53-27597": { "3.2.12-28060": {
"appid": 537243538, "appid": 537246140,
"qua": "V1_MAC_NQ_6.9.53_27597_GW_B" "qua": "V1_LNX_NQ_3.2.12_28060_GW_B"
},
"3.2.12-28131": {
"appid": 537246140,
"qua": "V1_LNX_NQ_3.2.12_28131_GW_B"
} }
} }

View File

@@ -26,7 +26,7 @@ export class RkeyManager {
try { try {
await this.refreshRkey(); await this.refreshRkey();
} catch (e) { } catch (e) {
this.logger.logError('获取rkey失败', e); this.logger.logError.bind(this.logger)('获取rkey失败', e);
} }
} }
return this.rkeyData; return this.rkeyData;
@@ -40,11 +40,11 @@ export class RkeyManager {
async refreshRkey(): Promise<any> { async refreshRkey(): Promise<any> {
//刷新rkey //刷新rkey
for (let url of this.serverUrl) { for (const url of this.serverUrl) {
try { try {
this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET'); this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET');
} catch (e) { } catch (e) {
this.logger.logError(`[Rkey] Get Rkey ${url} Error `, e); this.logger.logError.bind(this.logger)(`[Rkey] Get Rkey ${url} Error `, e);
} }
} }

View File

@@ -22,7 +22,7 @@ import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
import { NapCatPathWrapper } from '@/common/path'; import { NapCatPathWrapper } from '@/common/path';
import path from 'node:path'; import path from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import { getMachineId, hostname, systemName, systemVersion } from '@/common/system'; import { hostname, systemName, systemVersion } from '@/common/system';
import { NTEventWrapper } from '@/common/event'; import { NTEventWrapper } from '@/common/event';
import { DataSource, GroupMember, KickedOffLineInfo, SelfInfo, SelfStatusInfo } from '@/core/entities'; import { DataSource, GroupMember, KickedOffLineInfo, SelfInfo, SelfStatusInfo } from '@/core/entities';
import { NapCatConfigLoader } from '@/core/helper/config'; import { NapCatConfigLoader } from '@/core/helper/config';
@@ -45,12 +45,18 @@ export function loadQQWrapper(QQVersion: string): WrapperNodeApi {
let appPath; let appPath;
if (os.platform() === 'darwin') { if (os.platform() === 'darwin') {
appPath = path.resolve(path.dirname(process.execPath), '../Resources/app'); appPath = path.resolve(path.dirname(process.execPath), '../Resources/app');
} else { } else if (os.platform() === 'linux') {
appPath = path.resolve(path.dirname(process.execPath), './resources/app'); appPath = path.resolve(path.dirname(process.execPath), './resources/app');
} else {
appPath = path.resolve(path.dirname(process.execPath), `./versions/${QQVersion}/`);
} }
let wrapperNodePath = path.resolve(appPath, 'wrapper.node'); let wrapperNodePath = path.resolve(appPath, 'wrapper.node');
if (!fs.existsSync(wrapperNodePath)) { if (!fs.existsSync(wrapperNodePath)) {
wrapperNodePath = path.join(appPath, `versions/${QQVersion}/wrapper.node`); wrapperNodePath = path.join(appPath, `./resources/app/wrapper.node`);
}
//老版本兼容 未来去掉
if (!fs.existsSync(wrapperNodePath)) {
wrapperNodePath = path.join(path.dirname(process.execPath), `./resources/app/versions/${QQVersion}/wrapper.node`);
} }
const nativemodule: any = { exports: {} }; const nativemodule: any = { exports: {} };
process.dlopen(nativemodule, wrapperNodePath); process.dlopen(nativemodule, wrapperNodePath);
@@ -93,7 +99,7 @@ export class NapCatCore {
if (!fs.existsSync(this.NapCatTempPath)) { if (!fs.existsSync(this.NapCatTempPath)) {
fs.mkdirSync(this.NapCatTempPath, { recursive: true }); fs.mkdirSync(this.NapCatTempPath, { recursive: true });
} }
this.initNapCatCoreListeners().then().catch(this.context.logger.logError); this.initNapCatCoreListeners().then().catch(this.context.logger.logError.bind(this.context.logger));
this.context.logger.setFileLogEnabled( this.context.logger.setFileLogEnabled(
this.configLoader.configData.fileLog, this.configLoader.configData.fileLog,
@@ -121,7 +127,7 @@ export class NapCatCore {
const msgListener = new NodeIKernelMsgListener(); const msgListener = new NodeIKernelMsgListener();
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => { msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
// 下线通知 // 下线通知
this.context.logger.logError('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc); this.context.logger.logError.bind(this.context.logger)('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc);
this.selfInfo.online = false; this.selfInfo.online = false;
}; };
msgListener.onRecvMsg = (msgs) => { msgListener.onRecvMsg = (msgs) => {
@@ -145,7 +151,6 @@ export class NapCatCore {
if (Info.status == 20) { if (Info.status == 20) {
this.selfInfo.online = false; this.selfInfo.online = false;
this.context.logger.log("账号状态变更为离线"); this.context.logger.log("账号状态变更为离线");
return;
} else { } else {
this.selfInfo.online = true; this.selfInfo.online = true;
} }
@@ -242,22 +247,41 @@ export class NapCatCore {
} }
} }
export async function genSessionConfig(QQVersionAppid: string, QQVersion: string, selfUin: string, selfUid: string, account_path: string): Promise<WrapperSessionInitConfig> { export async function genSessionConfig(
guid:string,
QQVersionAppid: string,
QQVersion: string,
selfUin: string,
selfUid: string,
account_path: string
): Promise<WrapperSessionInitConfig> {
const downloadPath = path.join(account_path, 'NapCat', 'temp'); const downloadPath = path.join(account_path, 'NapCat', 'temp');
fs.mkdirSync(downloadPath, { recursive: true }); fs.mkdirSync(downloadPath, { recursive: true });
const guid: string = await getMachineId();//26702 支持JS获取guid值 在LoginService中获取 TODO mlikiow a //os.platform()
let systemPlatform = PlatformType.KWINDOWS;
switch (os.platform()) {
case 'win32':
systemPlatform = PlatformType.KWINDOWS;
break;
case 'darwin':
systemPlatform = PlatformType.KMAC;
break;
case 'linux':
systemPlatform = PlatformType.KLINUX;
break;
}
return { return {
selfUin, selfUin,
selfUid, selfUid,
desktopPathConfig: { desktopPathConfig: {
account_path, // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取 account_path, // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取
}, },
clientVer: QQVersion, // 9.9.8-22355 clientVer: QQVersion,
a2: '', a2: '',
d2: '', d2: '',
d2Key: '', d2Key: '',
machineId: '', machineId: '',
platform: PlatformType.KWINDOWS, // 3是Windows? platform: systemPlatform, // 3是Windows?
platVer: systemVersion, // 系统版本号, 应该可以固定 platVer: systemVersion, // 系统版本号, 应该可以固定
appid: QQVersionAppid, appid: QQVersionAppid,
rdeliveryConfig: { rdeliveryConfig: {
@@ -265,7 +289,7 @@ export async function genSessionConfig(QQVersionAppid: string, QQVersion: string
systemId: 0, systemId: 0,
appId: '', appId: '',
logicEnvironment: '', logicEnvironment: '',
platform: PlatformType.KWINDOWS, platform: systemPlatform,
language: '', language: '',
sdkVersion: '', sdkVersion: '',
userId: '', userId: '',

View File

@@ -0,0 +1,97 @@
import { ChatType } from '@/core';
export interface SearchGroupInfo {
groupCode: string;
ownerUid: string;
groupFlag: number;
groupFlagExt: number;
maxMemberNum: number;
memberNum: number;
groupOption: number;
classExt: number;
groupName: string;
fingerMemo: string;
groupQuestion: string;
certType: number;
shutUpAllTimestamp: number;
shutUpMeTimestamp: number;
groupTypeFlag: number;
privilegeFlag: number;
groupSecLevel: number;
groupFlagExt3: number;
isConfGroup: number;
isModifyConfGroupFace: number;
isModifyConfGroupName: number;
noFigerOpenFlag: number;
noCodeFingerOpenFlag: number;
groupFlagExt4: number;
groupMemo: string;
cmdUinMsgSeq: number;
cmdUinJoinTime: number;
cmdUinUinFlag: number;
cmdUinMsgMask: number;
groupSecLevelInfo: number;
cmdUinPrivilege: number;
cmdUinFlagEx2: number;
appealDeadline: number;
remarkName: string;
isTop: boolean;
richFingerMemo: string;
groupAnswer: string;
joinGroupAuth: string;
isAllowModifyConfGroupName: number;
}
export interface GroupInfo {
groupCode: string;
searchGroupInfo: SearchGroupInfo;
privilege: number;
}
export interface GroupSearchResult {
keyWord: string;
errorCode: number;
groupInfos: GroupInfo[];
penetrate: string;
isEnd: boolean;
nextPos: number;
}
export interface NodeIKernelSearchListener {
onSearchGroupResult(params: GroupSearchResult): void;
onSearchFileKeywordsResult(params: {
searchId: string,
hasMore: boolean,
resultItems: {
chatType: ChatType,
buddyChatInfo: any[],
discussChatInfo: any[],
groupChatInfo: {
groupCode: string,
isConf: boolean,
hasModifyConfGroupFace: boolean,
hasModifyConfGroupName: boolean,
groupName: string,
remark: string
}[],
dataLineChatInfo: any[],
tmpChatInfo: any[],
msgId: string,
msgSeq: string,
msgTime: string,
senderUid: string,
senderNick: string,
senderRemark: string,
senderCard: string,
elemId: string,
elemType: number,
fileSize: string,
filePath: string,
fileName: string,
hits: {
start: number,
end: number
}[]
}[]
}): void;
}

View File

@@ -1,39 +0,0 @@
import { ChatType } from '@/core';
export interface NodeIKernelSearchListener_Polyfill {
onSearchFileKeywordsResult(params: {
searchId: string,
hasMore: boolean,
resultItems: {
chatType: ChatType,
buddyChatInfo: any[],
discussChatInfo: any[],
groupChatInfo: {
groupCode: string,
isConf: boolean,
hasModifyConfGroupFace: boolean,
hasModifyConfGroupName: boolean,
groupName: string,
remark: string
}[],
dataLineChatInfo: any[],
tmpChatInfo: any[],
msgId: string,
msgSeq: string,
msgTime: string,
senderUid: string,
senderNick: string,
senderRemark: string,
senderCard: string,
elemId: string,
elemType: number,
fileSize: string,
filePath: string,
fileName: string,
hits: {
start: number,
end: number
}[]
}[]
}): void;
}

View File

@@ -0,0 +1,5 @@
export class NodeIO3MiscListener {
getOnAmgomDataPiece(...arg: unknown[]) {
}
}

View File

@@ -9,7 +9,7 @@ export * from './NodeIKernelProfileListener';
export * from './NodeIKernelTicketListener'; export * from './NodeIKernelTicketListener';
export * from './NodeIKernelStorageCleanListener'; export * from './NodeIKernelStorageCleanListener';
export * from './NodeIKernelFileAssistantListener'; export * from './NodeIKernelFileAssistantListener';
export * from './NodeIKernelSearchListener_Polyfill'; export * from './NodeIKernelSearchListener';
import type { import type {
NodeIKernelBuddyListener, NodeIKernelBuddyListener,
@@ -19,11 +19,11 @@ import type {
NodeIKernelMsgListener, NodeIKernelMsgListener,
NodeIKernelProfileListener, NodeIKernelProfileListener,
NodeIKernelRobotListener, NodeIKernelRobotListener,
NodeIKernelSearchListener_Polyfill,
NodeIKernelSessionListener, NodeIKernelSessionListener,
NodeIKernelStorageCleanListener, NodeIKernelStorageCleanListener,
NodeIKernelTicketListener, NodeIKernelTicketListener,
} from '.'; } from '.';
import { NodeIKernelSearchListener } from './NodeIKernelSearchListener';
export type ListenerNamingMapping = { export type ListenerNamingMapping = {
NodeIKernelSessionListener: NodeIKernelSessionListener; NodeIKernelSessionListener: NodeIKernelSessionListener;
@@ -36,5 +36,5 @@ export type ListenerNamingMapping = {
NodeIKernelTicketListener: NodeIKernelTicketListener; NodeIKernelTicketListener: NodeIKernelTicketListener;
NodeIKernelStorageCleanListener: NodeIKernelStorageCleanListener; NodeIKernelStorageCleanListener: NodeIKernelStorageCleanListener;
NodeIKernelFileAssistantListener: NodeIKernelFileAssistantListener; NodeIKernelFileAssistantListener: NodeIKernelFileAssistantListener;
NodeIKernelSearchListener: NodeIKernelSearchListener_Polyfill; NodeIKernelSearchListener: NodeIKernelSearchListener;
}; };

View File

@@ -14,9 +14,15 @@ export interface LikeMsgType {
detail: LikeDetailType; detail: LikeDetailType;
} }
export interface ProfileLikeTipType { export interface profileLikeSubTipType {
msg: LikeMsgType; msg: LikeMsgType;
} }
export interface ProfileLikeTipType {
msgType: number;
subType: number;
content: profileLikeSubTipType;
}
export interface SysMessageHeaderType { export interface SysMessageHeaderType {
id: string; id: string;
timestamp: number; timestamp: number;
@@ -78,6 +84,12 @@ export const likeMsg = new pb.Type("likeMsg")
.add(new pb.Field("time", 2, "int32")) .add(new pb.Field("time", 2, "int32"))
.add(new pb.Field("detail", 3, "likeDetail")); .add(new pb.Field("detail", 3, "likeDetail"));
export const profileLikeTip = new pb.Type("profileLikeTip") export const profileLikeSubTip = new pb.Type("profileLikeSubTip")
.add(likeMsg) .add(likeMsg)
.add(new pb.Field("msg", 14, "likeMsg")); .add(new pb.Field("msg", 14, "likeMsg"))
export const profileLikeTip = new pb.Type("profileLikeTip")
.add(profileLikeSubTip)
.add(new pb.Field("msgType", 1, "int32"))
.add(new pb.Field("subType", 2, "int32"))
.add(new pb.Field("content", 203, "profileLikeSubTip"));

View File

@@ -114,7 +114,7 @@ export interface NodeIKernelBuddyService {
reportDoubtBuddyReqUnread(): void; reportDoubtBuddyReqUnread(): void;
getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<unknown>; getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<GeneralCallResult & { arkMsg: string }>;
isNull(): boolean; isNull(): boolean;
} }

View File

@@ -1,4 +1,5 @@
import { NodeIKernelLoginListener } from '@/core/listeners/NodeIKernelLoginListener'; import { NodeIKernelLoginListener } from '@/core/listeners/NodeIKernelLoginListener';
import { GeneralCallResult } from './common';
export interface LoginInitConfig { export interface LoginInitConfig {
machineId: ''; machineId: '';
@@ -59,9 +60,12 @@ export interface QuickLoginResult {
} }
export interface NodeIKernelLoginService { export interface NodeIKernelLoginService {
setLoginMiscData(arg0: string, value: string): unknown;
getMachineGuid(): string;
get(): NodeIKernelLoginService;
connect(): boolean; connect(): boolean;
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeIKernelLoginService;
addKernelLoginListener(listener: NodeIKernelLoginListener): number; addKernelLoginListener(listener: NodeIKernelLoginListener): number;
@@ -69,7 +73,7 @@ export interface NodeIKernelLoginService {
initConfig(config: LoginInitConfig): void; initConfig(config: LoginInitConfig): void;
getLoginMiscData(cb: (r: unknown) => void): void; getLoginMiscData(data: string): Promise<GeneralCallResult & { value: string }>;
getLoginList(): Promise<{ getLoginList(): Promise<{
result: number, // 0是ok result: number, // 0是ok

View File

@@ -1,7 +1,7 @@
import { ElementType, MessageElement, Peer, RawMessage, SendMessageElement } from '@/core/entities'; import { ElementType, MessageElement, Peer, RawMessage, SendMessageElement } from '@/core/entities';
import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener'; import { NodeIKernelMsgListener } from '@/core/listeners/NodeIKernelMsgListener';
import { GeneralCallResult } from '@/core/services/common'; import { GeneralCallResult } from '@/core/services/common';
import { QueryMsgsParams, TmpChatInfoApi } from '../entities/msg'; import { MsgReqType, QueryMsgsParams, TmpChatInfoApi } from '../entities/msg';
export interface NodeIKernelMsgService { export interface NodeIKernelMsgService {
@@ -147,12 +147,15 @@ export interface NodeIKernelMsgService {
msgList: RawMessage[] msgList: RawMessage[]
}>; }>;
//@deprecated
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>; getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;
//@deprecated
getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & { getMsgsIncludeSelf(peer: Peer, msgId: string, count: number, queryOrder: boolean): Promise<GeneralCallResult & {
msgList: RawMessage[] msgList: RawMessage[]
}>; }>;
//@deprecated
getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>; getMsgsWithMsgTimeAndClientSeqForC2C(...args: unknown[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getMsgsWithStatus(params: { getMsgsWithStatus(params: {
@@ -168,7 +171,7 @@ export interface NodeIKernelMsgService {
getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<GeneralCallResult & { getMsgsBySeqRange(peer: Peer, startSeq: string, endSeq: string): Promise<GeneralCallResult & {
msgList: RawMessage[] msgList: RawMessage[]
}>; }>;
//@deprecated
getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & { getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, unknownArg: boolean): Promise<GeneralCallResult & {
msgList: RawMessage[] msgList: RawMessage[]
}>; }>;
@@ -179,6 +182,8 @@ export interface NodeIKernelMsgService {
getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>; getMsgsBySeqList(peer: Peer, seqList: string[]): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getMsgsExt(msgReq: MsgReqType): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getSingleMsg(Peer: Peer, msgSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>; getSingleMsg(Peer: Peer, msgSeq: string): Promise<GeneralCallResult & { msgList: RawMessage[] }>;
getSourceOfReplyMsg(peer: Peer, MsgId: string, SourceSeq: string): unknown; getSourceOfReplyMsg(peer: Peer, MsgId: string, SourceSeq: string): unknown;
@@ -318,7 +323,7 @@ export interface NodeIKernelMsgService {
getFileThumbSavePath(...args: unknown[]): unknown; getFileThumbSavePath(...args: unknown[]): unknown;
translatePtt2Text(msgId: string, peer: Peer, msgElement: unknown): unknown; translatePtt2Text(msgId: string, peer: Peer, msgElement: MessageElement): unknown;
setPttPlayedState(...args: unknown[]): unknown; setPttPlayedState(...args: unknown[]): unknown;
@@ -391,7 +396,12 @@ export interface NodeIKernelMsgService {
getEmojiResourcePath(...args: unknown[]): unknown; getEmojiResourcePath(...args: unknown[]): unknown;
JoinDragonGroupEmoji(JoinDragonGroupEmojiReq: any/*joinDragonGroupEmojiReq*/): unknown; JoinDragonGroupEmoji(JoinDragonGroupEmojiReq: {
latestMsgSeq: string,
manageEmojiId: number,
manageMsgSeq: string,
peerContact: Peer
}): Promise<unknown>;
getMsgAbstracts(...args: unknown[]): unknown; getMsgAbstracts(...args: unknown[]): unknown;
@@ -518,7 +528,7 @@ export interface NodeIKernelMsgService {
canImportOldDbMsg(...args: unknown[]): unknown; canImportOldDbMsg(...args: unknown[]): unknown;
setPowerStatus(z: boolean): unknown; setPowerStatus(isPowerOn: boolean): unknown;
canProcessDataMigration(...args: unknown[]): unknown; canProcessDataMigration(...args: unknown[]): unknown;
@@ -607,7 +617,7 @@ export interface NodeIKernelMsgService {
setIKernelPublicAccountAdapter(...args: unknown[]): unknown; setIKernelPublicAccountAdapter(...args: unknown[]): unknown;
//tempChatGameSession有关 //tempChatGameSession有关
createUidFromTinyId(fromTinyId: string, toTinyId: string): unknown; createUidFromTinyId(fromTinyId: string, toTinyId: string): string;
dataMigrationGetDataAvaiableContactList(...args: unknown[]): unknown; dataMigrationGetDataAvaiableContactList(...args: unknown[]): unknown;

View File

@@ -1,6 +1,8 @@
import { GeneralCallResult } from './common'; import { GeneralCallResult } from './common';
export interface NodeIKernelNodeMiscService { export interface NodeIKernelNodeMiscService {
writeVersionToRegistry(version: string): void;
getMiniAppPath(): unknown; getMiniAppPath(): unknown;
setMiniAppVersion(version: string): unknown; setMiniAppVersion(version: string): unknown;

View File

@@ -1,4 +1,5 @@
import { ChatType } from '../entities'; import { ChatType } from '../entities';
import { GeneralCallResult } from './common';
export interface NodeIKernelSearchService { export interface NodeIKernelSearchService {
@@ -8,7 +9,12 @@ export interface NodeIKernelSearchService {
searchStranger(unknown: string, searchStranger: unknown, searchParams: unknown): Promise<unknown>; searchStranger(unknown: string, searchStranger: unknown, searchParams: unknown): Promise<unknown>;
searchGroup(...args: any[]): unknown;// needs 1 arguments searchGroup(param: {
keyWords: string,
groupNum: number,
exactSearch: boolean,
penetrate: string
}): Promise<GeneralCallResult>;// needs 1 arguments
searchLocalInfo(keywords: string, unknown: number/*4*/): unknown; searchLocalInfo(keywords: string, unknown: number/*4*/): unknown;

View File

@@ -4,14 +4,14 @@ export interface NodeIKernelUnitedConfigService {
removeKernelUnitedConfigListener(listenerId:number): void; removeKernelUnitedConfigListener(listenerId:number): void;
fetchUnitedCommendConfig(...args: any[]): unknown;// needs 1 arguments
fetchUnitedSwitchConfig(...args: any[]): unknown;// needs 1 arguments fetchUnitedSwitchConfig(...args: any[]): unknown;// needs 1 arguments
loadUnitedConfig(...args: any[]): unknown;// needs 1 arguments
isUnitedConfigSwitchOn(...args: any[]): unknown;// needs 1 arguments isUnitedConfigSwitchOn(...args: any[]): unknown;// needs 1 arguments
registerUnitedConfigPushGroupList(...args: any[]): unknown;// needs 1 arguments registerUnitedConfigPushGroupList(...args: any[]): unknown;// needs 1 arguments
fetchUnitedCommendConfig(ids: `${string}`[]): void
loadUnitedConfig(id: string): Promise<unknown>
} }

View File

@@ -0,0 +1,11 @@
import { NodeIO3MiscListener } from "../listeners/NodeIO3MiscListener";
export interface NodeIO3MiscService {
get(): NodeIO3MiscService;
addO3MiscListener(listeners: NodeIO3MiscListener): number;
setAmgomDataPiece(appid: string, dataPiece: Uint8Array): void;
reportAmgomWeather(type: string, uk2: string, arg: Array<string>): void;
}

View File

@@ -26,6 +26,7 @@ import { NodeIKernelRecentContactService } from './services/NodeIKernelRecentCon
import { NodeIKernelMSFService } from './services/NodeIKernelMSFService'; import { NodeIKernelMSFService } from './services/NodeIKernelMSFService';
import { NodeIkernelTestPerformanceService } from './services/NodeIkernelTestPerformanceService'; import { NodeIkernelTestPerformanceService } from './services/NodeIkernelTestPerformanceService';
import { NodeIKernelECDHService } from './services/NodeIKernelECDHService'; import { NodeIKernelECDHService } from './services/NodeIKernelECDHService';
import { NodeIO3MiscService } from './services/NodeIO3MiscService';
export interface NodeQQNTWrapperUtil { export interface NodeQQNTWrapperUtil {
get(): unknown; get(): unknown;
@@ -143,8 +144,7 @@ export interface NodeQQNTWrapperUtil {
} }
export interface NodeIQQNTWrapperSession { export interface NodeIQQNTWrapperSession {
// eslint-disable-next-line @typescript-eslint/no-misused-new create(): NodeIQQNTWrapperSession;
new(): NodeIQQNTWrapperSession;
init( init(
wrapperSessionInitConfig: WrapperSessionInitConfig, wrapperSessionInitConfig: WrapperSessionInitConfig,
@@ -250,11 +250,11 @@ export interface NodeIQQNTWrapperSession {
export interface EnginInitDesktopConfig { export interface EnginInitDesktopConfig {
base_path_prefix: string; base_path_prefix: string;
platform_type: 3; platform_type: PlatformType;
app_type: 4; app_type: 4;
app_version: string; app_version: string;
os_version: string; os_version: string;
use_xlog: true; use_xlog: boolean;
qua: string; qua: string;
global_path_config: { global_path_config: {
desktopGlobalPath: string; desktopGlobalPath: string;
@@ -263,28 +263,26 @@ export interface EnginInitDesktopConfig {
} }
export interface NodeIQQNTWrapperEngine { export interface NodeIQQNTWrapperEngine {
// eslint-disable-next-line @typescript-eslint/no-misused-new get(): NodeIQQNTWrapperEngine;
new(): NodeIQQNTWrapperEngine;
initWithDeskTopConfig(config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void; initWithDeskTopConfig(config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void;
} }
export interface WrapperNodeApi { export interface WrapperNodeApi {
[key: string]: any; NodeIO3MiscService: NodeIO3MiscService;
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil; NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession; NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine; NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
NodeIKernelLoginService: NodeIKernelLoginService; NodeIKernelLoginService: NodeIKernelLoginService;
NodeIKernelProfileService: NodeIKernelProfileService;
}
}
export enum PlatformType { export enum PlatformType {
KUNKNOWN, KUNKNOWN,
KANDROID, KANDROID,
KIOS, KIOS,
KWINDOWS, KWINDOWS,
KMAC, KMAC,
KLINUX
} }
export enum DeviceType { export enum DeviceType {

View File

@@ -49,7 +49,7 @@ export async function NCoreInitFramework(
const loaderObject = new NapCatFramework(wrapper, session, logger, loginService, selfInfo, basicInfoWrapper, pathWrapper); const loaderObject = new NapCatFramework(wrapper, session, logger, loginService, selfInfo, basicInfoWrapper, pathWrapper);
//启动WebUi //启动WebUi
InitWebUi(logger, pathWrapper).then().catch(logger.logError); InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger));
//初始化LLNC的Onebot实现 //初始化LLNC的Onebot实现
new NapCatOneBot11Adapter(loaderObject.core, loaderObject.context, pathWrapper); new NapCatOneBot11Adapter(loaderObject.core, loaderObject.context, pathWrapper);
} }

View File

@@ -46,7 +46,7 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
const resData = await this._handle(payload); const resData = await this._handle(payload);
return OB11Response.ok(resData); return OB11Response.ok(resData);
} catch (e: any) { } catch (e: any) {
this.core.context.logger.logError('发生错误', e); this.core.context.logger.logError.bind(this.core.context.logger)('发生错误', e);
return OB11Response.error(e?.toString() || e?.stack?.toString() || '未知错误,可能操作超时', 200); return OB11Response.error(e?.toString() || e?.stack?.toString() || '未知错误,可能操作超时', 200);
} }
} }
@@ -60,7 +60,7 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
const resData = await this._handle(payload); const resData = await this._handle(payload);
return OB11Response.ok(resData, echo); return OB11Response.ok(resData, echo);
} catch (e: any) { } catch (e: any) {
this.core.context.logger.logError('发生错误', e); this.core.context.logger.logError.bind(this.core.context.logger)('发生错误', e);
return OB11Response.error(e.stack?.toString() || e.toString(), 1200, echo); return OB11Response.error(e.stack?.toString() || e.toString(), 1200, echo);
} }
} }

View File

@@ -0,0 +1,11 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
export class FetchUserProfileLike extends BaseAction<{ qq: number }, any> {
actionName = ActionName.FetchUserProfileLike;
async _handle(payload: { qq: number }) {
if (!payload.qq) throw new Error('qq is required');
return await this.core.apis.UserApi.getUidByUinV2(payload.qq.toString());
}
}

View File

@@ -7,8 +7,9 @@ const SchemaData = {
properties: { properties: {
group_id: { type: ['string', 'number'] }, group_id: { type: ['string', 'number'] },
folder_id: { type: 'string' }, folder_id: { type: 'string' },
folder: { type: 'string' }
}, },
required: ['group_id', 'folder_id'], required: ['group_id'],
} as const satisfies JSONSchema; } as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>; type Payload = FromSchema<typeof SchemaData>;
@@ -18,6 +19,6 @@ export class DeleteGroupFileFolder extends BaseAction<Payload, any> {
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
return (await this.core.apis.GroupApi.DelGroupFileFolder( return (await this.core.apis.GroupApi.DelGroupFileFolder(
payload.group_id.toString(), payload.folder_id)).groupFileCommonResult; payload.group_id.toString(), payload.folder ?? payload.folder_id ?? '')).groupFileCommonResult;
} }
} }

View File

@@ -73,7 +73,7 @@ export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileRespon
headers[headerItem] = ''; headers[headerItem] = '';
} else { } else {
const key = headerItem.substring(0, spilt); const key = headerItem.substring(0, spilt);
headers[key] = headerItem.substring(0, spilt + 1); headers[key] = headerItem.substring(spilt + 1);
} }
} }
} }

View File

@@ -8,9 +8,10 @@ const SchemaData = {
properties: { properties: {
group_id: { type: ['string', 'number'] }, group_id: { type: ['string', 'number'] },
folder_id: { type: 'string' }, folder_id: { type: 'string' },
folder: { type: 'string' },
file_count: { type: ['string', 'number'] }, file_count: { type: ['string', 'number'] },
}, },
required: ['group_id', 'folder_id'], required: ['group_id'],
} as const satisfies JSONSchema; } as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>; type Payload = FromSchema<typeof SchemaData>;
@@ -26,7 +27,7 @@ export class GetGroupFilesByFolder extends BaseAction<any, any> {
startIndex: 0, startIndex: 0,
sortOrder: 2, sortOrder: 2,
showOnlinedocFolder: 0, showOnlinedocFolder: 0,
folderId: payload.folder_id, folderId: payload.folder ?? payload.folder_id ?? '',
}).catch(() => []); }).catch(() => []);
return { return {
files: ret.filter(item => item.fileInfo) files: ret.filter(item => item.fileInfo)

View File

@@ -21,24 +21,25 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
async _handle(payload: Payload): Promise<OB11User> { async _handle(payload: Payload): Promise<OB11User> {
const user_id = payload.user_id.toString(); const user_id = payload.user_id.toString();
const extendData = await this.core.apis.UserApi.getUserDetailInfoByUin(user_id); const extendData = await this.core.apis.UserApi.getUserDetailInfoByUin(user_id);
const uid = (await this.core.apis.UserApi.getUidByUinV2(user_id))!; let uid = (await this.core.apis.UserApi.getUidByUinV2(user_id));
if (!uid || uid.indexOf('*') != -1) { if (!uid) uid = extendData.detail.uid;
const info = (await this.core.apis.UserApi.getUserDetailInfo(uid));
return { return {
...extendData.detail.simpleInfo.coreInfo, user_id: parseInt(extendData.detail.uin) ?? 0,
...extendData.detail.commonExt, uid: info.uid ?? uid,
...extendData.detail.simpleInfo.baseInfo,
...extendData.detail.simpleInfo.relationFlags,
...extendData.detail.simpleInfo.status,
user_id: parseInt(extendData.detail.uin) || 0,
nickname: extendData.detail.simpleInfo.coreInfo.nick, nickname: extendData.detail.simpleInfo.coreInfo.nick,
sex: OB11UserSex.unknown, age: extendData.detail.simpleInfo.baseInfo.age ?? info.age,
age: extendData.detail.simpleInfo.baseInfo.age || 0,
qid: extendData.detail.simpleInfo.baseInfo.qid, qid: extendData.detail.simpleInfo.baseInfo.qid,
level: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? 0) || 0, qqLevel: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? info.qqLevel),
login_days: 0, sex: OB11Entities.sex(extendData.detail.simpleInfo.baseInfo.sex) ?? OB11UserSex.unknown,
long_nick: extendData.detail.simpleInfo.baseInfo.longNick ?? info.longNick,
reg_time: extendData.detail.commonExt.regTime ?? info.regTime,
is_vip: extendData.detail.simpleInfo.vasInfo?.svipFlag,
is_years_vip: extendData.detail.simpleInfo.vasInfo?.yearVipFlag,
vip_level: extendData.detail.simpleInfo.vasInfo?.vipLevel,
remark: extendData.detail.simpleInfo.coreInfo.remark ?? info.remark,
status: extendData.detail.simpleInfo.status?.status ?? info.status,
login_days: 0,//失效
}; };
} }
const data = { ...extendData, ...(await this.core.apis.UserApi.getUserDetailInfo(uid)) };
return OB11Entities.stranger(data);
}
} }

View File

@@ -57,7 +57,6 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
const noticeShowEditCard = +(payload.is_show_edit_card ?? 0); const noticeShowEditCard = +(payload.is_show_edit_card ?? 0);
const noticeTipWindowType = +(payload.tip_window_type ?? 0); const noticeTipWindowType = +(payload.tip_window_type ?? 0);
const noticeConfirmRequired = +(payload.confirm_required ?? 1); const noticeConfirmRequired = +(payload.confirm_required ?? 1);
//const publishGroupBulletinResult = await this.core.apis.GroupApi.publishGroupBulletin(payload.group_id.toString(), payload.content, UploadImage, noticePinned, noticeConfirmRequired);
const publishGroupBulletinResult = await this.core.apis.WebApi.setGroupNotice( const publishGroupBulletinResult = await this.core.apis.WebApi.setGroupNotice(
payload.group_id.toString(), payload.group_id.toString(),
payload.content, payload.content,
@@ -71,7 +70,7 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
UploadImage?.height UploadImage?.height
); );
if (!publishGroupBulletinResult || publishGroupBulletinResult.ec != 0) { if (!publishGroupBulletinResult || publishGroupBulletinResult.ec != 0) {
throw `设置群公告失败,错误信息:${publishGroupBulletinResult?.em}`; throw new Error(`设置群公告失败,错误信息:${publishGroupBulletinResult?.em}`);
} }
return null; return null;
} }

View File

@@ -37,11 +37,11 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
if (!downloadResult.success) { if (!downloadResult.success) {
throw new Error(downloadResult.errMsg); throw new Error(downloadResult.errMsg);
} }
let msgContext: MessageContext = { const msgContext: MessageContext = {
peer: peer, peer: peer,
deleteAfterSentFiles: [] deleteAfterSentFiles: []
} };
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder_id); const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id);
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], [], true); await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], [], true);
return null; return null;
} }

View File

@@ -45,13 +45,13 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
throw new Error(downloadResult.errMsg); throw new Error(downloadResult.errMsg);
} }
let msgContext: MessageContext = { const msgContext: MessageContext = {
peer: await createContext(this.core, { peer: await createContext(this.core, {
user_id: payload.user_id.toString(), user_id: payload.user_id.toString(),
group_id: undefined, group_id: undefined,
}, ContextMode.Private), }, ContextMode.Private),
deleteAfterSentFiles: [] deleteAfterSentFiles: []
} };
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name); const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], [], true); await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], [], true);
return null; return null;

View File

@@ -20,7 +20,17 @@ class GetGroupInfo extends BaseAction<Payload, OB11Group> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const group = (await this.core.apis.GroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString()); const group = (await this.core.apis.GroupApi.getGroups()).find(e => e.groupCode == payload.group_id.toString());
if (!group) throw `${payload.group_id}不存在`; if (!group) {
const data = await this.core.apis.GroupApi.searchGroup(payload.group_id.toString());
if (!data) throw new Error('Group not found');
return {
...data.searchGroupInfo,
group_id: +payload.group_id,
group_name: data.searchGroupInfo.groupName,
member_count: data.searchGroupInfo.memberNum,
max_member_count: data.searchGroupInfo.maxMemberNum,
};
}
return OB11Entities.group(group); return OB11Entities.group(group);
} }
} }

View File

@@ -3,6 +3,7 @@ import { OB11Entities } from '@/onebot/entities';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { calcQQLevel } from '@/common/helper';
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
@@ -22,7 +23,8 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const groupMembers = await this.core.apis.GroupApi.getGroupMembersV2(payload.group_id.toString()); const groupMembers = await this.core.apis.GroupApi.getGroupMembersV2(payload.group_id.toString());
const groupMembersArr = Array.from(groupMembers.values()); const groupMembersArr = Array.from(groupMembers.values());
const uids = groupMembersArr.map(item => item.uid);
//let CoreAndBase = await this.core.apis.GroupApi.getCoreAndBaseInfo(uids)
let _groupMembers = groupMembersArr.map(item => { let _groupMembers = groupMembersArr.map(item => {
return OB11Entities.groupMember(payload.group_id.toString(), item); return OB11Entities.groupMember(payload.group_id.toString(), item);
}); });
@@ -32,20 +34,19 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
for (let i = 0, len = _groupMembers.length; i < len; i++) { for (let i = 0, len = _groupMembers.length; i < len; i++) {
// 保证基础数据有这个 同时避免群管插件过于依赖这个杀了 // 保证基础数据有这个 同时避免群管插件过于依赖这个杀了
_groupMembers[i].join_time = date; const Member = await this.core.apis.GroupApi.getGroupMember(payload.group_id.toString(), _groupMembers[i].user_id);
_groupMembers[i].last_sent_time = date; _groupMembers[i].join_time = +(Member?.joinTime ?? date);
_groupMembers[i].last_sent_time = +(Member?.lastSpeakTime ?? date);
MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]); MemberMap.set(_groupMembers[i].user_id, _groupMembers[i]);
} }
const selfRole = groupMembers.get(this.core.selfInfo.uid)?.role; const selfRole = groupMembers.get(this.core.selfInfo.uid)?.role;
const isPrivilege = selfRole === 3 || selfRole === 4; const isPrivilege = selfRole === 3 || selfRole === 4;
_groupMembers.forEach(item => {
item.last_sent_time = date;
item.join_time = date;
});
if (isPrivilege) { if (isPrivilege) {
try {
const webGroupMembers = await this.core.apis.WebApi.getGroupMembers(payload.group_id.toString()); const webGroupMembers = await this.core.apis.WebApi.getGroupMembers(payload.group_id.toString());
for (let i = 0, len = webGroupMembers.length; i < len; i++) { for (let i = 0, len = webGroupMembers.length; i < len; i++) {
if (!webGroupMembers[i]?.uin) { if (!webGroupMembers[i]?.uin) {
@@ -60,6 +61,11 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
MemberMap.set(webGroupMembers[i]?.uin, MemberData); MemberMap.set(webGroupMembers[i]?.uin, MemberData);
} }
} }
} catch (e) {
const logger = this.core.context.logger;
logger.logError.bind(logger)('GetGroupMemberList', e);
}
} }
_groupMembers = Array.from(MemberMap.values()); _groupMembers = Array.from(MemberMap.values());

View File

@@ -68,8 +68,8 @@ import SetGroupPortrait from './go-cqhttp/SetGroupPortrait';
import { FetchCustomFace } from './extends/FetchCustomFace'; import { FetchCustomFace } from './extends/FetchCustomFace';
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivateFile'; import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivateFile';
import { FetchEmojiLike } from './extends/FetchEmojiLike'; import { FetchEmojiLike } from './extends/FetchEmojiLike';
import { FetchUserProfileLike } from './extends/FetchUserProfileLike';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot'; import { NapCatOneBot11Adapter } from '@/onebot';
import GetGuildProfile from './guild/GetGuildProfile'; import GetGuildProfile from './guild/GetGuildProfile';
import SetModelShow from './go-cqhttp/SetModelShow'; import SetModelShow from './go-cqhttp/SetModelShow';
@@ -85,6 +85,7 @@ import { GetGroupRootFiles } from '@/onebot/action/go-cqhttp/GetGroupRootFiles';
import { GetGroupFilesByFolder } from '@/onebot/action/go-cqhttp/GetGroupFilesByFolder'; import { GetGroupFilesByFolder } from '@/onebot/action/go-cqhttp/GetGroupFilesByFolder';
import { GetGroupSystemMsg } from './system/GetSystemMsg'; import { GetGroupSystemMsg } from './system/GetSystemMsg';
export type ActionMap = Map<string, BaseAction<any, any>>; export type ActionMap = Map<string, BaseAction<any, any>>;
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore): ActionMap { export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore): ActionMap {
@@ -178,6 +179,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
new GetGroupFileSystemInfo(obContext, core), new GetGroupFileSystemInfo(obContext, core),
new GetGroupFilesByFolder(obContext, core), new GetGroupFilesByFolder(obContext, core),
new GetGroupSystemMsg(obContext, core), new GetGroupSystemMsg(obContext, core),
new FetchUserProfileLike(obContext, core),
]; ];
const actionMap = new Map(); const actionMap = new Map();
for (const action of actionHandlers) { for (const action of actionHandlers) {

View File

@@ -149,7 +149,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
//对Mgsid和OB11ID混用情况兜底 //对Mgsid和OB11ID混用情况兜底
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId); const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
if (!nodeMsg) { if (!nodeMsg) {
logger.logError('转发消息失败,未找到消息', nodeId); logger.logError.bind(this.core.context.logger)('转发消息失败,未找到消息', nodeId);
continue; continue;
} }
nodeMsgIds.push(nodeMsg.MsgId); nodeMsgIds.push(nodeMsg.MsgId);
@@ -161,7 +161,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
const isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息 const isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息
if (isNodeMsg !== 0) { if (isNodeMsg !== 0) {
if (isNodeMsg !== OB11Data.length) { if (isNodeMsg !== OB11Data.length) {
logger.logError('子消息中包含非node消息 跳过不合法部分'); logger.logError.bind(this.core.context.logger)('子消息中包含非node消息 跳过不合法部分');
continue; continue;
} }
const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node)); const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
@@ -209,7 +209,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
for (const msgId of nodeMsgIds) { for (const msgId of nodeMsgIds) {
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId); const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId);
if (!nodeMsgPeer) { if (!nodeMsgPeer) {
logger.logError('转发消息失败,未找到消息', msgId); logger.logError.bind(this.core.context.logger)('转发消息失败,未找到消息', msgId);
continue; continue;
} }
const nodeMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0]; const nodeMsg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
@@ -238,7 +238,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds); logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds);
return await this.core.apis.MsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds); return await this.core.apis.MsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds);
} catch (e) { } catch (e) {
logger.logError('forward failed', e); logger.logError.bind(this.core.context.logger)('forward failed', e);
return null; return null;
} }
} }
@@ -262,7 +262,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
try { try {
return await this.core.apis.MsgApi.sendMsg(selfPeer, sendElements, true); return await this.core.apis.MsgApi.sendMsg(selfPeer, sendElements, true);
} catch (e) { } catch (e) {
logger.logError(e, '克隆转发消息失败,将忽略本条消息', msg); logger.logError.bind(this.core.context.logger)(e, '克隆转发消息失败,将忽略本条消息', msg);
} }
} }
} }

View File

@@ -16,8 +16,8 @@ export interface InvalidCheckResult {
export enum ActionName { export enum ActionName {
// 以下为扩展napcat扩展 // 以下为扩展napcat扩展
Unknown = 'unknown', Unknown = 'unknown',
SharePeer = 'ArkShareGroup', SharePeer = 'ArkSharePeer',
ShareGroupEx = 'ArkSharePeer', ShareGroupEx = 'ArkShareGroup',
RebootNormal = 'reboot_normal',//无快速登录重新启动 RebootNormal = 'reboot_normal',//无快速登录重新启动
GetRobotUinRange = 'get_robot_uin_range', GetRobotUinRange = 'get_robot_uin_range',
SetOnlineStatus = 'set_online_status', SetOnlineStatus = 'set_online_status',
@@ -118,4 +118,5 @@ export enum ActionName {
DelGroupNotice = '_del_group_notice', DelGroupNotice = '_del_group_notice',
GetGroupInfoEx = "get_group_info_ex", GetGroupInfoEx = "get_group_info_ex",
GetGroupSystemMsg = 'get_group_system_msg', GetGroupSystemMsg = 'get_group_system_msg',
FetchUserProfileLike = "fetch_user_profile_like",
} }

View File

@@ -152,7 +152,7 @@ export class OneBotGroupApi {
parseInt(memberUin), parseInt(memberUin),
title, title,
); );
}; }
case "移出": case "移出":
logger.logDebug('收到机器人被踢消息', json); logger.logDebug('收到机器人被踢消息', json);
return; return;
@@ -289,7 +289,7 @@ export class OneBotGroupApi {
} }
const replyMsg = replyMsgList[0]; const replyMsg = replyMsgList[0];
if (!replyMsg) { if (!replyMsg) {
this.core.context.logger.logError('解析表情回应消息失败: 未找到回应消息'); this.core.context.logger.logError.bind(this.core.context.logger)('解析表情回应消息失败: 未找到回应消息');
return undefined; return undefined;
} }
return new OB11GroupMsgEmojiLikeEvent( return new OB11GroupMsgEmojiLikeEvent(

View File

@@ -34,6 +34,7 @@ import { RequestUtil } from '@/common/request';
import fs from 'node:fs'; import fs from 'node:fs';
import fsPromise from 'node:fs/promises'; import fsPromise from 'node:fs/promises';
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent'; import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
import { SysMessage, SysMessageType } from '@/core/proto/ProfileLike';
type RawToOb11Converters = { type RawToOb11Converters = {
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: ( [Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
@@ -121,7 +122,7 @@ export class OneBotMsgApi {
}, },
}; };
} catch (e: any) { } catch (e: any) {
this.core.context.logger.logError('获取图片url失败', e.stack); this.core.context.logger.logError.bind(this.core.context.logger)('获取图片url失败', e.stack);
return null; return null;
} }
}, },
@@ -185,7 +186,7 @@ export class OneBotMsgApi {
data: { data: {
file: 'marketface', file: 'marketface',
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, "." + _.key + ".jpg"), file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, "." + _.key + ".jpg"),
path: elementWrapper.elementId, path: url,
url: url, url: url,
file_unique: _.key file_unique: _.key
}, },
@@ -200,7 +201,7 @@ export class OneBotMsgApi {
guildId: '', guildId: '',
}; };
if (!records || !element.replyMsgTime || !element.senderUidStr) { if (!records || !element.replyMsgTime || !element.senderUidStr) {
this.core.context.logger.logError('获取不到引用的消息', element.replayMsgSeq); this.core.context.logger.logError.bind(this.core.context.logger)('获取不到引用的消息', element.replayMsgSeq);
return null; return null;
} }
@@ -218,7 +219,7 @@ export class OneBotMsgApi {
.msgList.find(msg => msg.msgRandom === records.msgRandom); .msgList.find(msg => msg.msgRandom === records.msgRandom);
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) { if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
this.core.context.logger.logError('获取不到引用的消息', element.replayMsgSeq); this.core.context.logger.logError.bind(this.core.context.logger)('获取不到引用的消息', element.replayMsgSeq);
return null; return null;
} }
return createReplyData(replyMsg.msgId); return createReplyData(replyMsg.msgId);
@@ -420,6 +421,10 @@ export class OneBotMsgApi {
// 从face_config.json中获取表情名称 // 从face_config.json中获取表情名称
const sysFaces = faceConfig.sysface; const sysFaces = faceConfig.sysface;
const face: any = sysFaces.find((systemFace) => systemFace.QSid === parsedFaceId.toString()); const face: any = sysFaces.find((systemFace) => systemFace.QSid === parsedFaceId.toString());
if (!face) {
this.core.context.logger.logError.bind(this.core.context.logger)('不支持的ID', id);
return undefined;
}
parsedFaceId = parseInt(parsedFaceId.toString()); parsedFaceId = parseInt(parsedFaceId.toString());
let faceType = 1; let faceType = 1;
if (parsedFaceId >= 222) { if (parsedFaceId >= 222) {
@@ -465,7 +470,6 @@ export class OneBotMsgApi {
sendMsg.data.summary, sendMsg.data.summary,
sendMsg.data.sub_type, sendMsg.data.sub_type,
); );
context.deleteAfterSentFiles.push(sendPicElement.picElement.sourcePath);
return sendPicElement; return sendPicElement;
}, },
@@ -543,40 +547,37 @@ export class OneBotMsgApi {
[OB11MessageDataType.music]: async ({ data }, context) => { [OB11MessageDataType.music]: async ({ data }, context) => {
// 保留, 直到...找到更好的解决方案 // 保留, 直到...找到更好的解决方案
if (data.type === 'custom') { if (data.id !== undefined) {
if (!data.url) { if (!['qq', '163', 'kugou', 'kuwo', 'migu'].includes(data.type)) {
this.core.context.logger.logError('自定义音卡缺少参数url'); this.core.context.logger.logError.bind(this.core.context.logger)('音乐卡片type错误, 只支持qq、163、kugou、kuwo、migu当前type:', data.type);
return undefined;
}
if (!data.audio) {
this.core.context.logger.logError('自定义音卡缺少参数audio');
return undefined;
}
if (!data.title) {
this.core.context.logger.logError('自定义音卡缺少参数title');
return undefined; return undefined;
} }
} else { } else {
if (!['qq', '163'].includes(data.type)) { if (!['qq', '163', 'kugou', 'kuwo', 'migu', 'custom'].includes(data.type)) {
this.core.context.logger.logError('音乐卡片type错误, 只支持qq、163、custom当前type:', data.type); this.core.context.logger.logError.bind(this.core.context.logger)('音乐卡片type错误, 只支持qq、163、kugou、kuwo、migu、custom当前type:', data.type);
return undefined; return undefined;
} }
if (!data.id) { if (!data.url) {
this.core.context.logger.logError('音乐卡片缺少参数id'); this.core.context.logger.logError.bind(this.core.context.logger)('自定义音卡缺少参数url');
return undefined;
}
if (!data.image) {
this.core.context.logger.logError.bind(this.core.context.logger)('自定义音卡缺少参数image');
return undefined; return undefined;
} }
} }
let postData: IdMusicSignPostData | CustomMusicSignPostData; let postData: IdMusicSignPostData | CustomMusicSignPostData;
if (data.type === 'custom' && data.content) { if (data.id === undefined && data.content) {
const { content, ...others } = data; const { content, ...others } = data;
postData = { singer: content, ...others }; postData = { singer: content, ...others };
} else { } else {
postData = data; postData = data;
} }
const signUrl = this.obContext.configLoader.configData.musicSignUrl; let signUrl = this.obContext.configLoader.configData.musicSignUrl;
if (!signUrl) { if (!signUrl) {
throw Error('音乐消息签名地址未配置'); signUrl = 'https://ss.xingzhige.com/music_card/card';//感谢思思!已获思思许可 其余地方使用请自行询问
//throw Error('音乐消息签名地址未配置');
} }
try { try {
const musicJson = await RequestUtil.HttpGetJson<any>(signUrl, 'POST', postData); const musicJson = await RequestUtil.HttpGetJson<any>(signUrl, 'POST', postData);
@@ -585,7 +586,7 @@ export class OneBotMsgApi {
type: OB11MessageDataType.json type: OB11MessageDataType.json
}, context); }, context);
} catch (e) { } catch (e) {
this.core.context.logger.logError('生成音乐消息失败', e); this.core.context.logger.logError.bind(this.core.context.logger)('生成音乐消息失败', e);
} }
}, },
@@ -607,6 +608,14 @@ export class OneBotMsgApi {
}), }),
[OB11MessageDataType.miniapp]: async () => undefined, [OB11MessageDataType.miniapp]: async () => undefined,
[OB11MessageDataType.contact]: async ({ data }, context) => {
const arkJson = await this.core.apis.UserApi.getBuddyRecommendContactArkJson(data.id.toString(), '');
return this.ob11ToRawConverters.json({
data: { data: arkJson.arkMsg },
type: OB11MessageDataType.json
}, context);
}
}; };
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore) { constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
@@ -619,18 +628,14 @@ export class OneBotMsgApi {
return; return;
} }
for (const element of msg.elements) { for (const element of msg.elements) {
if (element.grayTipElement) { if (element.grayTipElement && element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) { if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(element.grayTipElement); const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(element.grayTipElement);
if (PokeEvent) return PokeEvent; if (PokeEvent) return PokeEvent;
} }
}
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) {
//好友添加成功事件 //好友添加成功事件
if (element.grayTipElement.xmlElement.templId === '10229' && msg.peerUin !== '') { if (element.grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
return new OB11FriendAddNoticeEvent(this.core, parseInt(msg.peerUin)); return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
}
} }
} }
} }
@@ -692,36 +697,46 @@ export class OneBotMsgApi {
} }
} }
const msgSegments = (await Promise.allSettled(msg.elements.map( // 处理消息段
const msgSegments = await Promise.allSettled(msg.elements.map(
async (element) => { async (element) => {
for (const key in element) { for (const key in element) {
if (keyCanBeParsed(key, this.rawToOb11Converters) && element[key]) { if (keyCanBeParsed(key, this.rawToOb11Converters) && element[key]) {
return await this.rawToOb11Converters[key]?.( const parsedElement = await this.rawToOb11Converters[key]?.(
// eslint-disable-next-line // eslint-disable-next-line
// @ts-ignore // @ts-ignore
element[key], element[key],
msg, msg,
element, element,
); );
// 对于 face 类型的消息,检查是否存在
if (key === 'faceElement' && !parsedElement) {
return null; // 如果没有找到对应的表情,返回 null
}
return parsedElement;
} }
} }
}, },
))).filter(entry => { ));
// 过滤掉无效的消息段
const validSegments = msgSegments.filter(entry => {
if (entry.status === 'fulfilled') { if (entry.status === 'fulfilled') {
return !!entry.value; return !!entry.value;
} else { } else {
this.core.context.logger.logError('消息段解析失败', entry.reason); this.core.context.logger.logError.bind(this.core.context.logger)('消息段解析失败', entry.reason);
return false; return false;
} }
}).map((entry) => (<PromiseFulfilledResult<OB11MessageData>>entry).value); }).map((entry) => (<PromiseFulfilledResult<OB11MessageData>>entry).value).filter(value => value != null);
const msgAsCQCode = msgSegments.map(msg => encodeCQCode(msg)).join('').trim(); const msgAsCQCode = validSegments.map(msg => encodeCQCode(msg)).join('').trim();
if (messagePostFormat === 'string') { if (messagePostFormat === 'string') {
resMsg.message = msgAsCQCode; resMsg.message = msgAsCQCode;
resMsg.raw_message = msgAsCQCode; resMsg.raw_message = msgAsCQCode;
} else { } else {
resMsg.message = msgSegments; resMsg.message = validSegments;
resMsg.raw_message = msgAsCQCode; resMsg.raw_message = msgAsCQCode;
} }
return resMsg; return resMsg;
@@ -778,7 +793,7 @@ export class OneBotMsgApi {
timeout += PredictTime;// 10S Basic Timeout + PredictTime( For File 512kb/s ) timeout += PredictTime;// 10S Basic Timeout + PredictTime( For File 512kb/s )
} }
} catch (e) { } catch (e) {
this.core.context.logger.logError('发送消息计算预计时间异常', e); this.core.context.logger.logError.bind(this.core.context.logger)('发送消息计算预计时间异常', e);
} }
const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, waitComplete, timeout); const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, waitComplete, timeout);
if (!returnMsg) throw new Error('发送消息失败'); if (!returnMsg) throw new Error('发送消息失败');
@@ -788,7 +803,7 @@ export class OneBotMsgApi {
peerUid: peer.peerUid, peerUid: peer.peerUid,
}, returnMsg.msgId); }, returnMsg.msgId);
deleteAfterSentFiles.forEach(file => { deleteAfterSentFiles.forEach(file => {
fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError('发送消息删除文件失败', e)); fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', e));
}); });
return returnMsg; return returnMsg;
} }
@@ -799,7 +814,7 @@ export class OneBotMsgApi {
) { ) {
const realUri = inputdata.url || inputdata.file || inputdata.path || ''; const realUri = inputdata.url || inputdata.file || inputdata.path || '';
if (realUri.length === 0) { if (realUri.length === 0) {
this.core.context.logger.logError('文件消息缺少参数', inputdata); this.core.context.logger.logError.bind(this.core.context.logger)('文件消息缺少参数', inputdata);
throw Error('文件消息缺少参数'); throw Error('文件消息缺少参数');
} }
const { const {
@@ -810,7 +825,7 @@ export class OneBotMsgApi {
} = (await uri2local(this.core.NapCatTempPath, realUri)); } = (await uri2local(this.core.NapCatTempPath, realUri));
if (!success) { if (!success) {
this.core.context.logger.logError('文件下载失败', errMsg); this.core.context.logger.logError.bind(this.core.context.logger)('文件下载失败', errMsg);
throw Error('文件下载失败' + errMsg); throw Error('文件下载失败' + errMsg);
} }
@@ -818,4 +833,38 @@ export class OneBotMsgApi {
return { path, fileName: inputdata.name ?? fileName }; return { path, fileName: inputdata.name ?? fileName };
} }
async parseSysMessage(msg: number[]) {
const sysMsg = SysMessage.decode(Uint8Array.from(msg)) as unknown as SysMessageType;
if (sysMsg.msgSpec.length === 0) {
return;
}
const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
if (msgType === 528 && subType === 39 && subSubType === 39) {
if (!sysMsg.bodyWrapper) return;
const event = await this.obContext.apis.UserApi.parseLikeEvent(sysMsg.bodyWrapper.wrappedBody);
return event;
}
/*
if (msgType === 732 && subType === 16 && subSubType === 16) {
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
if (greyTip.subTypeId === 36) {
const emojiLikeToOthers = EmojiLikeToOthersWrapper1
.fromBinary(greyTip.rest)
.wrapper!
.body!;
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
return;
}
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
greyTip.groupCode.toString(),
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
emojiLikeToOthers.msgSpec!.msgSeq.toString(),
emojiLikeToOthers.attributes!.emojiId,
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
}
}
*/
}
} }

View File

@@ -47,7 +47,7 @@ export class OneBotQuickActionApi {
const peerContextMode = msg.message_type == 'private' ? ContextMode.Private : ContextMode.Group; const peerContextMode = msg.message_type == 'private' ? ContextMode.Private : ContextMode.Group;
const peer: Peer = await createContext(this.core, { const peer: Peer = await createContext(this.core, {
message: "", message_type: undefined,
group_id: msg.group_id?.toString(), group_id: msg.group_id?.toString(),
user_id: msg.user_id?.toString(), user_id: msg.user_id?.toString(),
}, peerContextMode); }, peerContextMode);

View File

@@ -13,9 +13,10 @@ export class OneBotUserApi {
this.core = core; this.core = core;
} }
async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> { async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> {
const likeTip = profileLikeTip.decode(Uint8Array.from(wrappedBody.slice(12))) as unknown as ProfileLikeTipType; const likeTip = profileLikeTip.decode(Uint8Array.from(wrappedBody)) as unknown as ProfileLikeTipType;
if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return;
this.core.context.logger.logDebug("收到点赞通知消息"); this.core.context.logger.logDebug("收到点赞通知消息");
const likeMsg = likeTip.msg; const likeMsg = likeTip.content.msg;
if (!likeMsg) return; if (!likeMsg) return;
const detail = likeMsg.detail; const detail = likeMsg.detail;
if (!detail) return; if (!detail) return;

View File

@@ -48,7 +48,8 @@ export class OB11Entities {
}[role]; }[role];
} }
static sex(sex: Sex): OB11UserSex { static sex(sex?: Sex): OB11UserSex {
if (!sex) return OB11UserSex.unknown;
return { return {
[Sex.male]: OB11UserSex.male, [Sex.male]: OB11UserSex.male,
[Sex.female]: OB11UserSex.female, [Sex.female]: OB11UserSex.female,
@@ -126,6 +127,7 @@ export class OB11Entities {
return { return {
group_id: parseInt(peerId), group_id: parseInt(peerId),
folder_id: folder.folderId, folder_id: folder.folderId,
folder: folder.folderId,
folder_name: folder.folderName, folder_name: folder.folderName,
create_time: folder.createTime, create_time: folder.createTime,
creator: parseInt(folder.createUin), creator: parseInt(folder.createUin),

View File

@@ -71,7 +71,7 @@ export class NapCatOneBot11Adapter {
this.actions = createActionMap(this, core); this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager(); this.networkManager = new OB11NetworkManager();
this.InitOneBot() this.InitOneBot()
.catch(e => this.context.logger.logError('初始化OneBot失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('初始化OneBot失败', e));
} }
async InitOneBot() { async InitOneBot() {
@@ -87,7 +87,7 @@ export class NapCatOneBot11Adapter {
this.core.apis.UserApi.getUserDetailInfo(selfInfo.uid).then(user => { this.core.apis.UserApi.getUserDetailInfo(selfInfo.uid).then(user => {
selfInfo.nick = user.nick; selfInfo.nick = user.nick;
this.context.logger.setLogSelfInfo(selfInfo); this.context.logger.setLogSelfInfo(selfInfo);
}).catch(this.context.logger.logError); }).catch(this.context.logger.logError.bind(this.context.logger));
this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`); this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`);
//创建NetWork服务 //创建NetWork服务
@@ -238,39 +238,10 @@ export class NapCatOneBot11Adapter {
private initMsgListener() { private initMsgListener() {
const msgListener = new NodeIKernelMsgListener(); const msgListener = new NodeIKernelMsgListener();
msgListener.onRecvSysMsg = async (msg) => { msgListener.onRecvSysMsg = (msg) => {
const sysMsg = SysMessage.decode(Uint8Array.from(msg)) as unknown as SysMessageType; this.apis.MsgApi.parseSysMessage(msg).then((event) => {
if (sysMsg.msgSpec.length === 0) { if (event) this.networkManager.emitEvent(event);
return; }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructSysMessage error: ', e));
}
const { msgType, subType, subSubType } = sysMsg.msgSpec[0];
if (msgType === 528 && subType === 39 && subSubType === 39) {
if (!sysMsg.bodyWrapper) return;
let event = await this.apis.UserApi.parseLikeEvent(sysMsg.bodyWrapper.wrappedBody);
if (event) await this.networkManager.emitEvent(event);
};
/*
if (msgType === 732 && subType === 16 && subSubType === 16) {
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
if (greyTip.subTypeId === 36) {
const emojiLikeToOthers = EmojiLikeToOthersWrapper1
.fromBinary(greyTip.rest)
.wrapper!
.body!;
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
return;
}
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
greyTip.groupCode.toString(),
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
emojiLikeToOthers.msgSpec!.msgSeq.toString(),
emojiLikeToOthers.attributes!.emojiId,
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
}
}
*/
}; };
msgListener.onInputStatusPush = async data => { msgListener.onInputStatusPush = async data => {
@@ -299,7 +270,7 @@ export class NapCatOneBot11Adapter {
m.msgId, m.msgId,
); );
await this.emitMsg(m) await this.emitMsg(m)
.catch(e => this.context.logger.logError('处理消息失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e));
} }
}; };
@@ -307,7 +278,7 @@ export class NapCatOneBot11Adapter {
const recallMsgs = new LRUCache<string, boolean>(100); const recallMsgs = new LRUCache<string, boolean>(100);
msgListener.onMsgInfoListUpdate = async msgList => { msgListener.onMsgInfoListUpdate = async msgList => {
this.emitRecallMsg(msgList, recallMsgs) this.emitRecallMsg(msgList, recallMsgs)
.catch(e => this.context.logger.logError('处理消息失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e));
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) { for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) {
@@ -409,7 +380,7 @@ export class NapCatOneBot11Adapter {
].includes(notify.type) ? 'unset' : 'set', ].includes(notify.type) ? 'unset' : 'set',
); );
this.networkManager.emitEvent(groupAdminNoticeEvent) this.networkManager.emitEvent(groupAdminNoticeEvent)
.catch(e => this.context.logger.logError('处理群管理员变动失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理群管理员变动失败', e));
} else { } else {
this.context.logger.logDebug('获取群通知的成员信息失败', notify, this.core.apis.GroupApi.getGroup(notify.group.groupCode)); this.context.logger.logDebug('获取群通知的成员信息失败', notify, this.core.apis.GroupApi.getGroup(notify.group.groupCode));
} }
@@ -434,7 +405,7 @@ export class NapCatOneBot11Adapter {
subType, subType,
); );
this.networkManager.emitEvent(groupDecreaseEvent) this.networkManager.emitEvent(groupDecreaseEvent)
.catch(e => this.context.logger.logError('处理群成员退出失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理群成员退出失败', e));
// notify.status == 1 表示未处理 2表示处理完成 // notify.status == 1 表示未处理 2表示处理完成
} else if ([ } else if ([
GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS, GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS,
@@ -454,9 +425,9 @@ export class NapCatOneBot11Adapter {
flag, flag,
); );
this.networkManager.emitEvent(groupRequestEvent) this.networkManager.emitEvent(groupRequestEvent)
.catch(e => this.context.logger.logError('处理加群请求失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理加群请求失败', e));
} catch (e) { } catch (e) {
this.context.logger.logError('获取加群人QQ号失败 Uid:', notify.user1.uid, e); this.context.logger.logError.bind(this.context.logger)('获取加群人QQ号失败 Uid:', notify.user1.uid, e);
} }
} else if (notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && notify.status == GroupNotifyMsgStatus.KUNHANDLE) { } else if (notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
this.context.logger.logDebug(`收到邀请我加群通知:${notify}`); this.context.logger.logDebug(`收到邀请我加群通知:${notify}`);
@@ -469,7 +440,7 @@ export class NapCatOneBot11Adapter {
flag, flag,
); );
this.networkManager.emitEvent(groupInviteEvent) this.networkManager.emitEvent(groupInviteEvent)
.catch(e => this.context.logger.logError('处理邀请本人加群失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理邀请本人加群失败', e));
} else if (notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS && notify.status == GroupNotifyMsgStatus.KUNHANDLE) { } else if (notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`); this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent( const groupInviteEvent = new OB11GroupRequestEvent(
@@ -481,7 +452,7 @@ export class NapCatOneBot11Adapter {
flag, flag,
); );
this.networkManager.emitEvent(groupInviteEvent) this.networkManager.emitEvent(groupInviteEvent)
.catch(e => this.context.logger.logError('处理邀请本人加群失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理邀请本人加群失败', e));
} }
} }
} }
@@ -503,9 +474,9 @@ export class NapCatOneBot11Adapter {
member.role === GroupMemberRole.admin ? 'set' : 'unset', member.role === GroupMemberRole.admin ? 'set' : 'unset',
); );
this.networkManager.emitEvent(groupAdminNoticeEvent) this.networkManager.emitEvent(groupAdminNoticeEvent)
.catch(e => this.context.logger.logError('处理群管理员变动失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理群管理员变动失败', e));
existMember.isChangeRole = false; existMember.isChangeRole = false;
this.context.logger.logDebug('群管理员变动处理完毕'); this.context.logger.logDebug.bind(this.context.logger)('群管理员变动处理完毕');
}); });
} }
}; };
@@ -528,8 +499,6 @@ export class NapCatOneBot11Adapter {
return; return;
} }
} }
// logOB11Message(this.core, ob11Msg)
// .catch(e => this.context.logger.logError('logMessage error: ', e));
const isSelfMsg = ob11Msg.user_id.toString() == this.core.selfInfo.uin; const isSelfMsg = ob11Msg.user_id.toString() == this.core.selfInfo.uin;
if (isSelfMsg && !reportSelfMessage) { if (isSelfMsg && !reportSelfMessage) {
return; return;
@@ -538,21 +507,21 @@ export class NapCatOneBot11Adapter {
ob11Msg.target_id = parseInt(message.peerUin); ob11Msg.target_id = parseInt(message.peerUin);
} }
this.networkManager.emitEvent(ob11Msg); this.networkManager.emitEvent(ob11Msg);
}).catch(e => this.context.logger.logError('constructMessage error: ', e)); }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructMessage error: ', e));
this.apis.GroupApi.parseGroupEvent(message).then(groupEvent => { this.apis.GroupApi.parseGroupEvent(message).then(groupEvent => {
if (groupEvent) { if (groupEvent) {
// log("post group event", groupEvent); // log("post group event", groupEvent);
this.networkManager.emitEvent(groupEvent); this.networkManager.emitEvent(groupEvent);
} }
}).catch(e => this.context.logger.logError('constructGroupEvent error: ', e)); }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructGroupEvent error: ', e));
this.apis.MsgApi.parsePrivateMsgEvent(message).then(privateEvent => { this.apis.MsgApi.parsePrivateMsgEvent(message).then(privateEvent => {
if (privateEvent) { if (privateEvent) {
// log("post private event", privateEvent); // log("post private event", privateEvent);
this.networkManager.emitEvent(privateEvent); this.networkManager.emitEvent(privateEvent);
} }
}).catch(e => this.context.logger.logError('constructPrivateEvent error: ', e)); }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructPrivateEvent error: ', e));
} }
private async emitRecallMsg(msgList: RawMessage[], cache: LRUCache<string, boolean>) { private async emitRecallMsg(msgList: RawMessage[], cache: LRUCache<string, boolean>) {
@@ -572,7 +541,7 @@ export class NapCatOneBot11Adapter {
oriMessageId, oriMessageId,
); );
this.networkManager.emitEvent(friendRecallEvent) this.networkManager.emitEvent(friendRecallEvent)
.catch(e => this.context.logger.logError('处理好友消息撤回失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理好友消息撤回失败', e));
} else if (message.chatType == ChatType.KCHATTYPEGROUP) { } else if (message.chatType == ChatType.KCHATTYPEGROUP) {
let operatorId = message.senderUin; let operatorId = message.senderUin;
for (const element of message.elements) { for (const element of message.elements) {
@@ -589,7 +558,7 @@ export class NapCatOneBot11Adapter {
oriMessageId, oriMessageId,
); );
this.networkManager.emitEvent(groupRecallEvent) this.networkManager.emitEvent(groupRecallEvent)
.catch(e => this.context.logger.logError('处理群消息撤回失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理群消息撤回失败', e));
} }
} }
} }

View File

@@ -49,12 +49,12 @@ export class OB11ActiveHttpAdapter implements IOB11NetworkAdapter {
try { try {
this.obContext.apis.QuickActionApi this.obContext.apis.QuickActionApi
.handleQuickOperation(event as QuickActionEvent, resJson) .handleQuickOperation(event as QuickActionEvent, resJson)
.catch(this.logger.logError); .catch(this.logger.logError.bind(this.logger));
} catch (e: any) { } catch (e: any) {
this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报返回快速操作失败', e); this.logger.logError.bind(this.logger)('[OneBot] [Http Client] 新消息事件HTTP上报返回快速操作失败', e);
} }
}).catch((e) => { }).catch((e) => {
this.logger.logError('[OneBot] [Http Client] 新消息事件HTTP上报失败', e); this.logger.logError.bind(this.logger)('[OneBot] [Http Client] 新消息事件HTTP上报失败', e);
}); });
} }

View File

@@ -91,7 +91,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
try { try {
this.connectEvent(this.core); this.connectEvent(this.core);
} catch (e) { } catch (e) {
this.logger.logError('[OneBot] [WebSocket Client] 发送连接生命周期失败', e); this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Client] 发送连接生命周期失败', e);
} }
}); });
@@ -100,8 +100,8 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
}); });
this.connection.once('close', () => { this.connection.once('close', () => {
if (!isClosedByError) { if (!isClosedByError) {
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.url}) 连接意外关闭`); this.logger.logError.bind(this.logger)(`[OneBot] [WebSocket Client] 反向WebSocket (${this.url}) 连接意外关闭`);
this.logger.logError(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`); this.logger.logError.bind(this.logger)(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`);
if (!this.isClosed) { if (!this.isClosed) {
this.connection = null; this.connection = null;
setTimeout(() => this.tryConnect(), this.reconnectIntervalInMillis); setTimeout(() => this.tryConnect(), this.reconnectIntervalInMillis);
@@ -110,8 +110,8 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
}); });
this.connection.on('error', (err) => { this.connection.on('error', (err) => {
isClosedByError = true; isClosedByError = true;
this.logger.logError(`[OneBot] [WebSocket Client] 反向WebSocket (${this.url}) 连接错误`, err); this.logger.logError.bind(this.logger)(`[OneBot] [WebSocket Client] 反向WebSocket (${this.url}) 连接错误`, err);
this.logger.logError(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`); this.logger.logError.bind(this.logger)(`[OneBot] [WebSocket Client] 在 ${Math.floor(this.reconnectIntervalInMillis / 1000)} 秒后尝试重新连接`);
if (!this.isClosed) { if (!this.isClosed) {
this.connection = null; this.connection = null;
setTimeout(() => this.tryConnect(), this.reconnectIntervalInMillis); setTimeout(() => this.tryConnect(), this.reconnectIntervalInMillis);
@@ -124,7 +124,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
try { try {
this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT)); this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT));
} catch (e) { } catch (e) {
this.logger.logError('[OneBot] [WebSocket Client] 发送生命周期失败', e); this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Client] 发送生命周期失败', e);
} }
} }
@@ -143,7 +143,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
const action = this.actions.get(receiveData.action); const action = this.actions.get(receiveData.action);
if (!action) { if (!action) {
this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的api ' + receiveData.action); this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Client] 发生错误', '不支持的api ' + receiveData.action);
this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo)); this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo));
return; return;
} }

View File

@@ -47,15 +47,15 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
} }
//鉴权 //鉴权
this.authorize(token, wsClient, wsReq); this.authorize(token, wsClient, wsReq);
let paramUrl = wsReq.url?.indexOf('?') !== -1 ? wsReq.url?.substring(0, wsReq.url?.indexOf('?')) : wsReq.url; const paramUrl = wsReq.url?.indexOf('?') !== -1 ? wsReq.url?.substring(0, wsReq.url?.indexOf('?')) : wsReq.url;
const isEventConnect = paramUrl === '/event' || paramUrl === '' || paramUrl === '/'; const isApiConnect = paramUrl === '/api' || paramUrl === '/api/';
if (isEventConnect) { if (!isApiConnect) {
this.connectEvent(core, wsClient); this.connectEvent(core, wsClient);
} }
wsClient.on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Client Error:', err.message)); wsClient.on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Client Error:', err.message));
wsClient.on('message', (message) => { wsClient.on('message', (message) => {
this.handleMessage(wsClient, message).then().catch(this.logger.logError); this.handleMessage(wsClient, message).then().catch(this.logger.logError.bind(this.logger));
}); });
wsClient.on('ping', () => { wsClient.on('ping', () => {
wsClient.pong(); wsClient.pong();
@@ -77,7 +77,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
}); });
}); });
await this.wsClientsMutex.runExclusive(async () => { await this.wsClientsMutex.runExclusive(async () => {
if(isEventConnect){ if (!isApiConnect) {
this.wsClientWithEvent.push(wsClient); this.wsClientWithEvent.push(wsClient);
} }
this.wsClients.push(wsClient); this.wsClients.push(wsClient);
@@ -89,7 +89,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
try { try {
this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT), wsClient); this.checkStateAndReply<any>(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT), wsClient);
} catch (e) { } catch (e) {
this.logger.logError('[OneBot] [WebSocket Server] 发送生命周期失败', e); this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Server] 发送生命周期失败', e);
} }
} }
@@ -103,11 +103,11 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
open() { open() {
if (this.isOpen) { if (this.isOpen) {
this.logger.logError('[OneBot] [WebSocket Server] Cannot open a opened WebSocket server'); this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Server] Cannot open a opened WebSocket server');
return; return;
} }
if (this.hasBeenClosed) { if (this.hasBeenClosed) {
this.logger.logError('[OneBot] [WebSocket Server] Cannot open a WebSocket server that has been closed'); this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Server] Cannot open a WebSocket server that has been closed');
return; return;
} }
const addressInfo = this.wsServer.address(); const addressInfo = this.wsServer.address();
@@ -170,7 +170,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证
const action = this.actions.get(receiveData.action); const action = this.actions.get(receiveData.action);
if (!action) { if (!action) {
this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的api ' + receiveData.action); this.logger.logError.bind(this.logger)('[OneBot] [WebSocket Client] 发生错误', '不支持的api ' + receiveData.action);
this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo), wsClient); this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo), wsClient);
return; return;
} }

View File

@@ -1,4 +1,5 @@
export interface OB11User { export interface OB11User {
[key: string]: any;
user_id: number; user_id: number;
nickname: string; nickname: string;
remark?: string; remark?: string;
@@ -81,6 +82,7 @@ export interface OB11GroupFile {
export interface OB11GroupFileFolder { export interface OB11GroupFileFolder {
group_id: number, group_id: number,
folder_id: string, folder_id: string,
folder: string,
folder_name: string, folder_name: string,
create_time: number, create_time: number,
creator: number, creator: number,

View File

@@ -62,6 +62,7 @@ export enum OB11MessageDataType {
dice = 'dice', dice = 'dice',
RPS = 'rps', RPS = 'rps',
miniapp = 'miniapp',//json类 miniapp = 'miniapp',//json类
contact = 'contact',
Location = 'location' Location = 'location'
} }
@@ -81,7 +82,12 @@ export interface OB11MessageText {
text: string, // 纯文本 text: string, // 纯文本
} }
} }
export interface OB11MessageContext {
type: OB11MessageDataType.contact,
data: {
id: string,
}
}
export interface OB11MessageFileBase { export interface OB11MessageFileBase {
data: { data: {
file_unique?: string, file_unique?: string,
@@ -198,7 +204,7 @@ export type OB11MessageData =
OB11MessageAt | OB11MessageReply | OB11MessageAt | OB11MessageReply |
OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo | OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo |
OB11MessageNode | OB11MessageIdMusic | OB11MessageCustomMusic | OB11MessageJson | OB11MessageNode | OB11MessageIdMusic | OB11MessageCustomMusic | OB11MessageJson |
OB11MessageDice | OB11MessageRPS | OB11MessageMarkdown | OB11MessageForward OB11MessageDice | OB11MessageRPS | OB11MessageMarkdown | OB11MessageForward | OB11MessageContext
export interface OB11PostSendMsg { export interface OB11PostSendMsg {
message_type?: 'private' | 'group' message_type?: 'private' | 'group'

View File

@@ -11,6 +11,7 @@ import {
NapCatCore, NapCatCore,
NapCatCoreWorkingEnv, NapCatCoreWorkingEnv,
NodeIQQNTWrapperSession, NodeIQQNTWrapperSession,
PlatformType,
WrapperNodeApi, WrapperNodeApi,
} from '@/core'; } from '@/core';
import { QQBasicInfoWrapper } from '@/common/qq-basic-info'; import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
@@ -27,6 +28,7 @@ import { NapCatOneBot11Adapter } from '@/onebot';
import { InitWebUi } from '@/webui'; import { InitWebUi } from '@/webui';
import { WebUiDataRuntime } from '@/webui/src/helper/Data'; import { WebUiDataRuntime } from '@/webui/src/helper/Data';
import { napCatVersion } from '@/common/version'; import { napCatVersion } from '@/common/version';
import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener';
program.option('-q, --qq [number]', 'QQ号').parse(process.argv); program.option('-q, --qq [number]', 'QQ号').parse(process.argv);
const cmdOptions = program.opts(); const cmdOptions = program.opts();
@@ -39,14 +41,19 @@ export async function NCoreInitShell() {
const logger = new LogWrapper(pathWrapper.logsPath); const logger = new LogWrapper(pathWrapper.logsPath);
const basicInfoWrapper = new QQBasicInfoWrapper({ logger }); const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion()); const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
const o3Service = wrapper.NodeIO3MiscService.get();
o3Service.addO3MiscListener(new NodeIO3MiscListener());
logger.log(`[NapCat] [Core] NapCat.Core Version: ` + napCatVersion); logger.log(`[NapCat] [Core] NapCat.Core Version: ` + napCatVersion);
InitWebUi(logger, pathWrapper).then().catch(logger.logError); InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger));
// from constructor // from constructor
const engine = new wrapper.NodeIQQNTWrapperEngine(); const engine = wrapper.NodeIQQNTWrapperEngine.get();
//const util = wrapper.NodeQQNTWrapperUtil.get(); //const util = wrapper.NodeQQNTWrapperUtil.get();
const loginService = new wrapper.NodeIKernelLoginService(); const loginService = wrapper.NodeIKernelLoginService.get();
const session = new wrapper.NodeIQQNTWrapperSession();
const session = wrapper.NodeIQQNTWrapperSession.create();
// from get dataPath // from get dataPath
const [dataPath, dataPathGlobal] = (() => { const [dataPath, dataPathGlobal] = (() => {
@@ -63,17 +70,29 @@ export async function NCoreInitShell() {
const dataPathGlobal = path.resolve(dataPath, './nt_qq/global'); const dataPathGlobal = path.resolve(dataPath, './nt_qq/global');
return [dataPath, dataPathGlobal]; return [dataPath, dataPathGlobal];
})(); })();
let systemPlatform = PlatformType.KWINDOWS;
switch (os.platform()) {
case 'win32':
systemPlatform = PlatformType.KWINDOWS;
break;
case 'darwin':
systemPlatform = PlatformType.KMAC;
break;
case 'linux':
systemPlatform = PlatformType.KLINUX;
break;
}
if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined');
// from initConfig // from initConfig
engine.initWithDeskTopConfig( engine.initWithDeskTopConfig(
{ {
base_path_prefix: '', base_path_prefix: '',
platform_type: 3, platform_type: systemPlatform,
app_type: 4, app_type: 4,
app_version: basicInfoWrapper.getFullQQVesion(), app_version: basicInfoWrapper.getFullQQVesion(),
os_version: 'Windows 10 Pro', os_version: systemVersion,
use_xlog: true, use_xlog: false,
qua: basicInfoWrapper.QQVersionQua!, qua: basicInfoWrapper.QQVersionQua,
global_path_config: { global_path_config: {
desktopGlobalPath: dataPathGlobal, desktopGlobalPath: dataPathGlobal,
}, },
@@ -83,7 +102,7 @@ export async function NCoreInitShell() {
); );
loginService.initConfig({ loginService.initConfig({
machineId: '', machineId: '',
appid: basicInfoWrapper.QQVersionAppid!, appid: basicInfoWrapper.QQVersionAppid,
platVer: systemVersion, platVer: systemVersion,
commonPath: dataPathGlobal, commonPath: dataPathGlobal,
clientVer: basicInfoWrapper.getFullQQVesion(), clientVer: basicInfoWrapper.getFullQQVesion(),
@@ -100,21 +119,25 @@ export async function NCoreInitShell() {
quickLoginUin = ''; quickLoginUin = '';
} }
} }
let dataTimestape = new Date().getTime().toString();
o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']);
const selfInfo = await new Promise<SelfInfo>((resolve) => { const selfInfo = await new Promise<SelfInfo>((resolve) => {
const loginListener = new NodeIKernelLoginListener(); const loginListener = new NodeIKernelLoginListener();
let isLogined = false;
// from constructor // from constructor
loginListener.onUserLoggedIn = (userid: string) => { loginListener.onUserLoggedIn = (userid: string) => {
logger.logError(`当前账号(${userid})已登录,无法重复登录`); logger.logError.bind(logger)(`当前账号(${userid})已登录,无法重复登录`);
}; };
loginListener.onQRCodeLoginSucceed = async (loginResult) => resolve({ loginListener.onQRCodeLoginSucceed = async (loginResult) => {
isLogined = true;
resolve({
uid: loginResult.uid, uid: loginResult.uid,
uin: loginResult.uin, uin: loginResult.uin,
nick: '', // 获取不到 nick: '', // 获取不到
online: true, online: true,
}); });
};
loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => { loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {
//设置WebuiQrcode //设置WebuiQrcode
@@ -137,22 +160,22 @@ export async function NCoreInitShell() {
}); });
}; };
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => { loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
//logger.logError('登录失败(onQRCodeSessionFailed)', errCode, errMsg); if (!isLogined) {
logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg); logger.logError.bind(logger)('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg);
if (errType == 1 && errCode == 3) { if (errType == 1 && errCode == 3) {
// 二维码过期刷新 // 二维码过期刷新
} }
loginService.getQRCodePicture(); loginService.getQRCodePicture();
}
}; };
loginListener.onLoginFailed = (args) => { loginListener.onLoginFailed = (args) => {
//logger.logError('登录失败(onLoginFailed)', args); logger.logError.bind(logger)('[Core] [Login] Login Error , ErrInfo: ', args);
logger.logError('[Core] [Login] Login Error , ErrInfo: ', args);
}; };
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any); loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger) as any);
const isConnect = loginService.connect(); const isConnect = loginService.connect();
if (!isConnect) { if (!isConnect) {
logger.logError('核心登录服务连接失败!'); logger.logError.bind(logger)('核心登录服务连接失败!');
return; return;
} }
logger.log('核心登录服务连接成功!'); logger.log('核心登录服务连接成功!');
@@ -161,18 +184,25 @@ export async function NCoreInitShell() {
// 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList // 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList
WebUiDataRuntime.setQQQuickLoginList(res.LocalLoginInfoList.filter((item) => item.isQuickLogin).map((item) => item.uin.toString())); WebUiDataRuntime.setQQQuickLoginList(res.LocalLoginInfoList.filter((item) => item.isQuickLogin).map((item) => item.uin.toString()));
}); });
if (basicInfoWrapper.QQVersionConfig?.curVersion) {
loginService.getLoginMiscData('hotUpdateSign').then((res) => {
if (res.result === 0) {
loginService.setLoginMiscData('hotUpdateSign', res.value);
}
});
session.getNodeMiscService().writeVersionToRegistry(basicInfoWrapper.QQVersionConfig?.curVersion);
}
WebUiDataRuntime.setQuickLoginCall(async (uin: string) => { WebUiDataRuntime.setQuickLoginCall(async (uin: string) => {
return await new Promise((resolve) => { return await new Promise((resolve) => {
if (uin) { if (uin) {
logger.log('正在快速登录 ', uin); logger.log.bind(logger)('正在快速登录 ', uin);
loginService.quickLoginWithUin(uin).then(res => { loginService.quickLoginWithUin(uin).then(res => {
if (res.loginErrorInfo.errMsg) { if (res.loginErrorInfo.errMsg) {
resolve({ result: false, message: res.loginErrorInfo.errMsg }); resolve({ result: false, message: res.loginErrorInfo.errMsg });
} }
resolve({ result: true, message: '' }); resolve({ result: true, message: '' });
}).catch((e) => { }).catch((e) => {
logger.logError(e); logger.logError.bind(logger)(e);
resolve({ result: false, message: '快速登录发生错误' }); resolve({ result: false, message: '快速登录发生错误' });
}); });
} else { } else {
@@ -188,15 +218,15 @@ export async function NCoreInitShell() {
loginService.quickLoginWithUin(quickLoginUin) loginService.quickLoginWithUin(quickLoginUin)
.then(result => { .then(result => {
if (result.loginErrorInfo.errMsg) { if (result.loginErrorInfo.errMsg) {
logger.logError('快速登录错误:', result.loginErrorInfo.errMsg); logger.logError.bind(logger)('快速登录错误:', result.loginErrorInfo.errMsg);
loginService.getQRCodePicture(); if (!isLogined) loginService.getQRCodePicture();
} }
}) })
.catch(); .catch();
}, 1000); }, 1000);
} else { } else {
logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式'); logger.logError.bind(logger)('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式');
loginService.getQRCodePicture(); if (!isLogined) loginService.getQRCodePicture();
} }
} else { } else {
logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式'); logger.log('没有 -q 指令指定快速登录,将使用二维码登录方式');
@@ -210,12 +240,24 @@ export async function NCoreInitShell() {
} }
}); });
// BEFORE LOGGING IN // BEFORE LOGGING IN
let amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));
// AFTER LOGGING IN // AFTER LOGGING IN
//99b15bdb4c984fc69d5aa1feb9aa16xx --> 99b15bdb-4c98-4fc6-9d5a-a1feb9aa16xx
//把guid从左向右转换为guid格式 loginService.getMachineGuid()
let guid = loginService.getMachineGuid();
guid = guid.slice(0, 8) + '-' + guid.slice(8, 12) + '-' + guid.slice(12, 16) + '-' + guid.slice(16, 20) + '-' + guid.slice(20);
//console.log('guid:', guid);
//NodeIO3MiscService/reportAmgomWeather login a6 [ '1726748166943', '184', '329' ]
o3Service.reportAmgomWeather('login', 'a6', [dataTimestape, '184', '329']);
// if(session.getUnitedConfigService()){
// session.getUnitedConfigService().fetchUnitedCommendConfig([]);
// }
// from initSession // from initSession
await new Promise<void>(async (resolve, reject) => { await new Promise<void>(async (resolve, reject) => {
const sessionConfig = await genSessionConfig( const sessionConfig = await genSessionConfig(
guid,
basicInfoWrapper.QQVersionAppid!, basicInfoWrapper.QQVersionAppid!,
basicInfoWrapper.getFullQQVesion(), basicInfoWrapper.getFullQQVesion(),
selfInfo.uin, selfInfo.uin,

View File

@@ -54,7 +54,7 @@ export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapp
).then((data) => { ).then((data) => {
log(`[NapCat] [WebUi] WebUi Publish Panel Url: http://${data.IP.IP}:${config.port}${config.prefix}/webui/?token=${config.token}`); log(`[NapCat] [WebUi] WebUi Publish Panel Url: http://${data.IP.IP}:${config.port}${config.prefix}/webui/?token=${config.token}`);
}).catch((err) => { }).catch((err) => {
logger.logError(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`); logger.logError.bind(logger)(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`);
}); });
}); });

View File

@@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) {
SettingItem( SettingItem(
'<span id="napcat-update-title">Napcat</span>', '<span id="napcat-update-title">Napcat</span>',
undefined, undefined,
SettingButton('V2.4.6', 'napcat-update-button', 'secondary'), SettingButton('V2.6.13', 'napcat-update-button', 'secondary'),
), ),
]), ]),
SettingList([ SettingList([

View File

@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
SettingItem( SettingItem(
'<span id="napcat-update-title">Napcat</span>', '<span id="napcat-update-title">Napcat</span>',
void 0, void 0,
SettingButton("V2.4.6", "napcat-update-button", "secondary") SettingButton("V2.6.13", "napcat-update-button", "secondary")
) )
]), ]),
SettingList([ SettingList([

View File

@@ -1,10 +1,8 @@
// import PreprocessorDirectives from 'unplugin-preprocessor-directives/vite';
import cp from 'vite-plugin-cp'; import cp from 'vite-plugin-cp';
import { defineConfig, PluginOption, UserConfig } from 'vite'; import { defineConfig, PluginOption, UserConfig } from 'vite';
import { resolve } from 'path'; import { resolve } from 'path';
import nodeResolve from '@rollup/plugin-node-resolve'; import nodeResolve from '@rollup/plugin-node-resolve';
import { builtinModules } from 'module'; import { builtinModules } from 'module';
import babel from 'vite-plugin-babel';
//依赖排除 //依赖排除
const external = ['silk-wasm', 'ws', 'express', 'fluent-ffmpeg', 'log4js', 'qrcode-terminal']; const external = ['silk-wasm', 'ws', 'express', 'fluent-ffmpeg', 'log4js', 'qrcode-terminal'];
const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat(); const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat();
@@ -15,31 +13,15 @@ let startScripts: string[] | undefined = undefined;
if (process.env.NAPCAT_BUILDSYS == 'linux') { if (process.env.NAPCAT_BUILDSYS == 'linux') {
if (process.env.NAPCAT_BUILDARCH == 'x64') { if (process.env.NAPCAT_BUILDARCH == 'x64') {
} }
startScripts = ['./script/napcat.sh']; startScripts = [];
} else if (process.env.NAPCAT_BUILDSYS == 'win32') { } else if (process.env.NAPCAT_BUILDSYS == 'win32') {
if (process.env.NAPCAT_BUILDARCH == 'x64') { if (process.env.NAPCAT_BUILDARCH == 'x64') {
} }
startScripts = ['./script/BootWay05.ps1', './script/dbghelp.dll', startScripts = ['./script/KillQQ.bat'];
'./script/BootWay05_init.bat', './script/BootWay05_run.bat', './script/BootWay05_run.utf8.bat', './script/KillQQ.bat'];
} else { } else {
startScripts = ['./script/BootWay05.ps1', './script/dbghelp.dll', startScripts = ['./script/KillQQ.bat'];
'./script/BootWay05_init.bat', './script/BootWay05_run.bat', './script/BootWay05_run.utf8.bat', './script/KillQQ.bat'];
} }
const FrameworkBaseConfigPlugin: PluginOption[] = [ const FrameworkBaseConfigPlugin: PluginOption[] = [
// PreprocessorDirectives(),
babel({
filter: /.*\.(ts|js)$/,
babelConfig: {
babelrc: false,
configFile: false,
presets: ['@babel/preset-typescript'],
plugins: [
//'2018-09', decoratorsBeforeExport: true
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-class-properties',
],
},
}),
cp({ cp({
targets: [ targets: [
{ src: './manifest.json', dest: 'dist' }, { src: './manifest.json', dest: 'dist' },
@@ -52,38 +34,18 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
{ src: './src/framework/renderer.js', dest: 'dist' }, { src: './src/framework/renderer.js', dest: 'dist' },
{ src: './package.json', dest: 'dist' }, { src: './package.json', dest: 'dist' },
{ src: './logo.png', dest: 'dist' }, { src: './logo.png', dest: 'dist' },
//...external.map(genCpModule)
], ],
}), }),
nodeResolve(), nodeResolve(),
]; ];
const ShellBaseConfigPlugin: PluginOption[] = [ const ShellBaseConfigPlugin: PluginOption[] = [
// PreprocessorDirectives(),
babel({
filter: /.*\.(ts|js)$/,
babelConfig: {
babelrc: false,
configFile: false,
presets: ['@babel/preset-typescript'],
plugins: [
//'2018-09', decoratorsBeforeExport: true
['@babel/plugin-proposal-decorators', { legacy: true }],
'@babel/plugin-proposal-class-properties',
],
},
}),
cp({ cp({
targets: [ targets: [
// ...external.map(genCpModule),
// { src: './src/napcat.json', dest: 'dist/config/' },
{ src: './static/', dest: 'dist/static/', flatten: false }, { src: './static/', dest: 'dist/static/', flatten: false },
// { src: './src/onebot11/onebot11.json', dest: 'dist/config/' },
{ src: './src/core/external/napcat.json', dest: 'dist/config/' }, { src: './src/core/external/napcat.json', dest: 'dist/config/' },
{ src: './src/onebot/config/onebot11.json', dest: 'dist/config/' }, { src: './src/onebot/config/onebot11.json', dest: 'dist/config/' },
{ src: './package.json', dest: 'dist' }, { src: './package.json', dest: 'dist' },
{ src: './launcher/', dest: 'dist', flatten: true }, { src: './launcher/', dest: 'dist', flatten: true },
// { src: './README.md', dest: 'dist' },
// { src: './logo.png', dest: 'dist/logs' },
...(startScripts.map((startScript) => { ...(startScripts.map((startScript) => {
return { src: startScript, dest: 'dist' }; return { src: startScript, dest: 'dist' };
})), })),