mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
117 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5a8eea668f | ||
![]() |
777143e502 | ||
![]() |
0d8c9a82fe | ||
![]() |
d10ab1cce3 | ||
![]() |
ec25e09d73 | ||
![]() |
cba9c78ab1 | ||
![]() |
c32db4a881 | ||
![]() |
871add3071 | ||
![]() |
e661c617a3 | ||
![]() |
d4bf721540 | ||
![]() |
d91b55faed | ||
![]() |
9687832d4d | ||
![]() |
fc3e436744 | ||
![]() |
da90245f7b | ||
![]() |
410d6a85d7 | ||
![]() |
b693342e4f | ||
![]() |
acca361f2e | ||
![]() |
b663f47713 | ||
![]() |
d332b199b5 | ||
![]() |
78bac1dbd1 | ||
![]() |
724ff215f9 | ||
![]() |
68ea146469 | ||
![]() |
82583e616f | ||
![]() |
bfc339c58d | ||
![]() |
fe4427c076 | ||
![]() |
5745f388a9 | ||
![]() |
377e3c253f | ||
![]() |
3007a0c00e | ||
![]() |
f51ffc091d | ||
![]() |
c37c364a08 | ||
![]() |
331a106e9a | ||
![]() |
cd74687b7b | ||
![]() |
b3e145c1e6 | ||
![]() |
d8e1547736 | ||
![]() |
8617f01924 | ||
![]() |
55f9e75e6a | ||
![]() |
b93e7b7ed1 | ||
![]() |
89cc79ad60 | ||
![]() |
8dd0e60eea | ||
![]() |
df6113fdf6 | ||
![]() |
3a3095d15a | ||
![]() |
fb4d07391e | ||
![]() |
9bef9c85cf | ||
![]() |
b77b3f227f | ||
![]() |
6a065f0a34 | ||
![]() |
4e1e190797 | ||
![]() |
1ce8cd2100 | ||
![]() |
c03af6b9ad | ||
![]() |
adca850075 | ||
![]() |
e3616b484e | ||
![]() |
cfd7808169 | ||
![]() |
addcedc588 | ||
![]() |
bfea786088 | ||
![]() |
50e84c3c9e | ||
![]() |
dc92ace85e | ||
![]() |
1a543928b1 | ||
![]() |
652fe8d21e | ||
![]() |
199690f45f | ||
![]() |
37a4dd4b00 | ||
![]() |
34d4358bfc | ||
![]() |
90906b9019 | ||
![]() |
1c212ff2b4 | ||
![]() |
7d709f44a8 | ||
![]() |
ea9e88a18a | ||
![]() |
0be8a9c805 | ||
![]() |
fcf8139afe | ||
![]() |
62f969b50b | ||
![]() |
6726062500 | ||
![]() |
cf1f4bdcaf | ||
![]() |
b09a14ad4e | ||
![]() |
1dc62c9ca3 | ||
![]() |
beaa89a2dc | ||
![]() |
f39a000b49 | ||
![]() |
013a74fb14 | ||
![]() |
7c4964753b | ||
![]() |
8353533d60 | ||
![]() |
c06df27424 | ||
![]() |
ad82919ddf | ||
![]() |
44dbba17e1 | ||
![]() |
5ba110e1da | ||
![]() |
b6e392fdb2 | ||
![]() |
2280e83aa2 | ||
![]() |
f49b94edb9 | ||
![]() |
2428a12221 | ||
![]() |
9c353f3760 | ||
![]() |
5b86d25d7f | ||
![]() |
2b168e8bbc | ||
![]() |
537db32847 | ||
![]() |
498b7f9f2b | ||
![]() |
9935568597 | ||
![]() |
467003af8c | ||
![]() |
4c9edcc47b | ||
![]() |
24bf9cf121 | ||
![]() |
e06f6f39a9 | ||
![]() |
98ee0c307b | ||
![]() |
5e53ea0bc3 | ||
![]() |
847d88ea77 | ||
![]() |
d5046cc2b3 | ||
![]() |
3ad64b7cbb | ||
![]() |
0dbfe8ca55 | ||
![]() |
91b794d66d | ||
![]() |
0d65e1e314 | ||
![]() |
2d8f58c6d8 | ||
![]() |
65888fa816 | ||
![]() |
857e882c6e | ||
![]() |
add2931834 | ||
![]() |
cdda5f45ee | ||
![]() |
5f73d6a913 | ||
![]() |
0637882fbc | ||
![]() |
3f785bab20 | ||
![]() |
a4ca89bdd6 | ||
![]() |
1a64e796bd | ||
![]() |
a8b85a34f7 | ||
![]() |
e7bec7d6b0 | ||
![]() |
a582026037 | ||
![]() |
1a67a001c5 | ||
![]() |
406deac592 |
28
.github/workflows/release.yml
vendored
28
.github/workflows/release.yml
vendored
@@ -93,15 +93,18 @@ jobs:
|
|||||||
needs: [Build-LiteLoader,Build-Shell]
|
needs: [Build-LiteLoader,Build-Shell]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
|
- name: Clone Main Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: 'NapNeko/NapCatQQ'
|
||||||
|
submodules: true
|
||||||
|
ref: main
|
||||||
|
token: ${{ secrets.NAPCAT_BUILD }}
|
||||||
|
|
||||||
- name: Download All Artifact
|
- name: Download All Artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
# - name: Compress subdirectories
|
|
||||||
# run: |
|
|
||||||
# cd ./NapCat.Shell/
|
|
||||||
# zip -q -r NapCat.Shell.zip *
|
|
||||||
# cd ..
|
|
||||||
# rm ./NapCat.Shell.zip -rf
|
|
||||||
# mv ./NapCat.Shell/NapCat.Shell.zip ./
|
|
||||||
- name: Compress subdirectories
|
- name: Compress subdirectories
|
||||||
run: |
|
run: |
|
||||||
cd ./NapCat.Shell/
|
cd ./NapCat.Shell/
|
||||||
@@ -114,6 +117,16 @@ jobs:
|
|||||||
rm ./NapCat.Framework.zip -rf
|
rm ./NapCat.Framework.zip -rf
|
||||||
mv ./NapCat.Shell/NapCat.Shell.zip ./
|
mv ./NapCat.Shell/NapCat.Shell.zip ./
|
||||||
mv ./NapCat.Framework/NapCat.Framework.zip ./
|
mv ./NapCat.Framework/NapCat.Framework.zip ./
|
||||||
|
|
||||||
|
mkdir ./NapCat.Framework.Windows.Once
|
||||||
|
unzip -q ./external/LiteLoaderWrapper.zip -d ./NapCat.Framework.Windows.Once
|
||||||
|
cd ./NapCat.Framework.Windows.Once
|
||||||
|
ls
|
||||||
|
mkdir -p ./LL/plugins/NapCatQQ
|
||||||
|
unzip -q ../NapCat.Framework.zip -d ./LL/plugins/NapCatQQ
|
||||||
|
zip -q -r NapCat.Framework.Windows.Once.zip *
|
||||||
|
cd ..
|
||||||
|
mv ./NapCat.Framework.Windows.Once/NapCat.Framework.Windows.Once.zip ./
|
||||||
- name: Extract version from tag
|
- name: Extract version from tag
|
||||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
|
||||||
@@ -129,4 +142,5 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
NapCat.Framework.zip
|
NapCat.Framework.zip
|
||||||
NapCat.Shell.zip
|
NapCat.Shell.zip
|
||||||
|
NapCat.Framework.Windows.Once.zip
|
||||||
draft: true
|
draft: true
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
## 欢迎回来
|
## 欢迎回来
|
||||||
NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现。
|
NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
|
||||||
|
|
||||||
## 猫猫技能
|
## 猫猫技能
|
||||||
- [x] **高性能**:1K+ 群聊数目、20 线程并行发送消息毫无压力
|
- [x] **高性能**:1K+ 群聊数目、20 线程并行发送消息毫无压力
|
||||||
@@ -20,7 +20,7 @@ NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
|
|||||||
|
|
||||||
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
|
||||||
|
|
||||||
**首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程。
|
**首次使用**请务必前往[官方文档](https://napneko.github.io/)查看使用教程
|
||||||
|
|
||||||
## 回家旅途
|
## 回家旅途
|
||||||
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
|
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
|
||||||
@@ -40,4 +40,4 @@ NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
|
|||||||
> [!CAUTION]\
|
> [!CAUTION]\
|
||||||
> **请不要在 QQ 官方群聊和任何影响力较大的简中互联网平台(包括但不限于: 哔哩哔哩,微博,知乎,抖音等)发布和讨论*任何*与本项目存在相关性的信息**
|
> **请不要在 QQ 官方群聊和任何影响力较大的简中互联网平台(包括但不限于: 哔哩哔哩,微博,知乎,抖音等)发布和讨论*任何*与本项目存在相关性的信息**
|
||||||
|
|
||||||
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 NapCat 代码开发。**
|
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 NapCat 代码开发。**
|
||||||
|
BIN
external/LiteLoaderWrapper.zip
vendored
Normal file
BIN
external/LiteLoaderWrapper.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -33,7 +33,7 @@ if not exist "%QQpath%" (
|
|||||||
exit /b
|
exit /b
|
||||||
)
|
)
|
||||||
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
|
||||||
|
|
||||||
|
@@ -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
|
@@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "qq-chat",
|
"name": "qq-chat",
|
||||||
"version": "9.9.15-28060",
|
"version": "9.9.15-28418",
|
||||||
|
"verHash": "206bfa62",
|
||||||
|
"linuxVersion": "3.2.12-28418",
|
||||||
|
"linuxVerHash": "0256c948",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "QQ",
|
"description": "QQ",
|
||||||
@@ -15,7 +18,7 @@
|
|||||||
"qd": "externals/devtools/cli/index.js"
|
"qd": "externals/devtools/cli/index.js"
|
||||||
},
|
},
|
||||||
"main": "./loadNapCat.js",
|
"main": "./loadNapCat.js",
|
||||||
"buildVersion": "28060",
|
"buildVersion": "28418",
|
||||||
"isPureShell": true,
|
"isPureShell": true,
|
||||||
"isByteCodeShell": true,
|
"isByteCodeShell": true,
|
||||||
"platform": "win32",
|
"platform": "win32",
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "2.6.2",
|
"version": "2.6.23",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
107
package.json
107
package.json
@@ -1,55 +1,52 @@
|
|||||||
{
|
{
|
||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.6.2",
|
"version": "2.6.23",
|
||||||
"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",
|
||||||
"build:webui": "cd ./src/webui && vite build",
|
"build:webui": "cd ./src/webui && vite build",
|
||||||
"lint": "eslint --fix src/**/*.{js,ts}",
|
"lint": "eslint --fix src/**/*.{js,ts}",
|
||||||
"depend": "cd dist && npm install --omit=dev"
|
"depend": "cd dist && npm install --omit=dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@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",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-typescript": "^11.1.6",
|
||||||
"@rollup/plugin-typescript": "^11.1.6",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/express": "^5.0.0",
|
||||||
"@types/express": "^4.17.21",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@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",
|
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
"@typescript-eslint/parser": "^8.3.0",
|
||||||
"@typescript-eslint/parser": "^8.3.0",
|
"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",
|
"typescript": "^5.3.3",
|
||||||
"typescript": "^5.3.3",
|
"vite": "^5.2.6",
|
||||||
"vite": "^5.2.6",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vite-plugin-dts": "^3.8.2",
|
"@protobuf-ts/runtime": "^2.9.4",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"ajv": "^8.13.0",
|
||||||
},
|
"fast-xml-parser": "^4.3.6",
|
||||||
"dependencies": {
|
"chalk": "^5.3.0",
|
||||||
"ajv": "^8.13.0",
|
"commander": "^12.1.0",
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
"chalk": "^5.3.0",
|
"file-type": "^19.0.0",
|
||||||
"commander": "^12.1.0",
|
"json-schema-to-ts": "^3.1.0",
|
||||||
"cors": "^2.8.5",
|
"image-size": "^1.1.1",
|
||||||
"express": "^5.0.0-beta.2",
|
"cors": "^2.8.5"
|
||||||
"fast-xml-parser": "^4.3.6",
|
},
|
||||||
"file-type": "^19.0.0",
|
"dependencies": {
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"image-size": "^1.1.1",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"json-schema-to-ts": "^3.1.0",
|
"express": "^5.0.0-beta.2",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"protobufjs": "~7.4.0",
|
"silk-wasm": "^3.6.1",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"ws": "^8.18.0"
|
||||||
"silk-wasm": "^3.6.1",
|
}
|
||||||
"strtok3": "8.0.1",
|
}
|
||||||
"ws": "^8.18.0"
|
|
||||||
}
|
|
||||||
}
|
|
@@ -45,9 +45,9 @@ async function handleWavFile(
|
|||||||
): Promise<{input: Buffer, sampleRate: number}> {
|
): 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 {input: await convert(filePath, pcmPath, logger), sampleRate: 24000};
|
return { input: await convert(filePath, pcmPath, logger), sampleRate: 24000 };
|
||||||
}
|
}
|
||||||
return {input: file, sampleRate: fmt.sampleRate};
|
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) {
|
||||||
@@ -59,7 +59,7 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
|
|||||||
const pcmPath = `${pttPath}.pcm`;
|
const pcmPath = `${pttPath}.pcm`;
|
||||||
const { input, sampleRate } = isWav(file)
|
const { input, sampleRate } = isWav(file)
|
||||||
? (await handleWavFile(file, filePath, pcmPath, logger))
|
? (await handleWavFile(file, filePath, pcmPath, logger))
|
||||||
: {input: await convert(filePath, pcmPath, logger), sampleRate: 24000};
|
: { input: await convert(filePath, pcmPath, logger), sampleRate: 24000 };
|
||||||
const silk = await encode(input, sampleRate);
|
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);
|
||||||
@@ -83,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 {};
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -183,7 +183,7 @@ export class NTEventWrapper {
|
|||||||
timeout = 5000,
|
timeout = 5000,
|
||||||
) {
|
) {
|
||||||
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
|
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
|
||||||
(resolve, reject) => {
|
async (resolve, reject) => {
|
||||||
const id = randomUUID();
|
const id = randomUUID();
|
||||||
let complete = 0;
|
let complete = 0;
|
||||||
let retData: Parameters<ListenerType> | undefined = undefined;
|
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||||
@@ -235,22 +235,22 @@ export class NTEventWrapper {
|
|||||||
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback);
|
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback);
|
||||||
this.createListenerFunction(ListenerMainName);
|
this.createListenerFunction(ListenerMainName);
|
||||||
const eventFunction = this.createEventFunction(serviceAndMethod);
|
const eventFunction = this.createEventFunction(serviceAndMethod);
|
||||||
if (eventFunction) eventFunction(...(args)).then((retEvent: Awaited<ReturnType<EventType>>) => {
|
retEvent = await eventFunction!(...(args));
|
||||||
if (!checkerEvent(retEvent) && timeoutRef.hasRef()) {
|
if (!checkerEvent(retEvent) && timeoutRef.hasRef()) {
|
||||||
clearTimeout(timeoutRef);
|
clearTimeout(timeoutRef);
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new Error(
|
||||||
'EventChecker Failed: NTEvent serviceAndMethod:' +
|
'EventChecker Failed: NTEvent serviceAndMethod:' +
|
||||||
serviceAndMethod +
|
serviceAndMethod +
|
||||||
' ListenerName:' +
|
' ListenerName:' +
|
||||||
listenerAndMethod +
|
listenerAndMethod +
|
||||||
' EventRet:\n' +
|
' EventRet:\n' +
|
||||||
JSON.stringify(retEvent, null, 4) +
|
JSON.stringify(retEvent, null, 4) +
|
||||||
'\n',
|
'\n',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -144,7 +144,7 @@ async function tryDownload(options: string | HttpDownloadOptions, useReferer: bo
|
|||||||
}
|
}
|
||||||
if (useReferer && !headers['Referer']) {
|
if (useReferer && !headers['Referer']) {
|
||||||
headers['Referer'] = url;
|
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;
|
||||||
|
@@ -180,11 +180,11 @@ export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
baseVersion: '9.9.15-28060',
|
baseVersion: '9.9.15-28131',
|
||||||
curVersion: '9.9.15-28060',
|
curVersion: '9.9.15-28131',
|
||||||
prevVersion: '',
|
prevVersion: '',
|
||||||
onErrorVersions: [],
|
onErrorVersions: [],
|
||||||
buildId: '28060',
|
buildId: '28131',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -139,9 +139,13 @@ export class LogWrapper {
|
|||||||
|
|
||||||
logMessage(msg: RawMessage, selfInfo: SelfInfo) {
|
logMessage(msg: RawMessage, selfInfo: SelfInfo) {
|
||||||
const isSelfSent = msg.senderUin === selfInfo.uin;
|
const isSelfSent = msg.senderUin === selfInfo.uin;
|
||||||
this.log(`${
|
|
||||||
isSelfSent ? '发送 ->' : '接收 <-'
|
// Intercept grey tip
|
||||||
} ${rawMessageToText(msg)}`);
|
if (msg.elements[0]?.elementType === ElementType.GreyTip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log(`${isSelfSent ? '发送 ->' : '接收 <-' } ${rawMessageToText(msg)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +159,12 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
|||||||
if (msg.chatType == ChatType.KCHATTYPEC2C) {
|
if (msg.chatType == ChatType.KCHATTYPEC2C) {
|
||||||
tokens.push(`私聊 (${msg.peerUin})`);
|
tokens.push(`私聊 (${msg.peerUin})`);
|
||||||
} else if (msg.chatType == ChatType.KCHATTYPEGROUP) {
|
} else if (msg.chatType == ChatType.KCHATTYPEGROUP) {
|
||||||
tokens.push(`群聊 (群 ${msg.peerUin} 的 ${msg.senderUin})`);
|
if (recursiveLevel < 1) {
|
||||||
|
tokens.push(`群聊 [${msg.peerName}(${msg.peerUin})]`);
|
||||||
|
}
|
||||||
|
if (msg.senderUin !== '0') {
|
||||||
|
tokens.push(`[${msg.sendMemberName || msg.sendRemarkName || msg.sendNickName}(${msg.senderUin})]`);
|
||||||
|
}
|
||||||
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
|
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
|
||||||
tokens.push('移动设备');
|
tokens.push('移动设备');
|
||||||
} else /* temp */ {
|
} else /* temp */ {
|
||||||
@@ -180,12 +189,11 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
|||||||
const recordMsgOrNull = msg.records.find(
|
const recordMsgOrNull = msg.records.find(
|
||||||
record => element.replyElement!.sourceMsgIdInRecords === record.msgId,
|
record => element.replyElement!.sourceMsgIdInRecords === record.msgId,
|
||||||
);
|
);
|
||||||
return `[回复消息 ${
|
return `[回复消息 ${recordMsgOrNull &&
|
||||||
recordMsgOrNull &&
|
recordMsgOrNull.peerUin != '284840486' && recordMsgOrNull.peerUin != '1094950020'// 非转发消息; 否则定位不到
|
||||||
recordMsgOrNull.peerUin != '284840486' // 非转发消息; 否则定位不到
|
?
|
||||||
?
|
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
|
||||||
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
|
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
|
||||||
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
|
|
||||||
}]`;
|
}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -55,23 +55,23 @@ export class QQBasicInfoWrapper {
|
|||||||
//此方法不要直接使用
|
//此方法不要直接使用
|
||||||
getQUAInternal() {
|
getQUAInternal() {
|
||||||
switch (systemPlatform) {
|
switch (systemPlatform) {
|
||||||
case 'linux':
|
case 'linux':
|
||||||
return `V1_LNX_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
|
return `V1_LNX_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
return `V1_MAC_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
|
return `V1_MAC_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
|
||||||
default:
|
default:
|
||||||
return `V1_WIN_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
|
return `V1_WIN_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAppidInternal() {
|
getAppidInternal() {
|
||||||
switch (systemPlatform) {
|
switch (systemPlatform) {
|
||||||
case 'linux':
|
case 'linux':
|
||||||
return '537246140';
|
return '537246140';
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
return '537246140';
|
return '537246140';
|
||||||
default:
|
default:
|
||||||
return '537246092';
|
return '537246092';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '2.6.2';
|
export const napCatVersion = '2.6.23';
|
||||||
|
@@ -145,7 +145,7 @@ 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 fileExt = 'mp4';
|
let fileExt = 'mp4';
|
||||||
@@ -153,7 +153,7 @@ export class NTQQFileApi {
|
|||||||
const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext;
|
const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext;
|
||||||
if (tempExt) fileExt = tempExt;
|
if (tempExt) fileExt = tempExt;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.context.logger.logError('获取文件类型失败', e);
|
this.context.logger.logError.bind(logger)('获取文件类型失败', e);
|
||||||
}
|
}
|
||||||
const newFilePath = filePath + '.' + fileExt;
|
const newFilePath = filePath + '.' + fileExt;
|
||||||
fs.copyFileSync(filePath, newFilePath);
|
fs.copyFileSync(filePath, newFilePath);
|
||||||
@@ -227,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 {
|
||||||
@@ -365,22 +365,41 @@ export class NTQQFileApi {
|
|||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
const parsedUrl = new URL(IMAGE_HTTP_HOST + url);
|
const parsedUrl = new URL(IMAGE_HTTP_HOST + url);
|
||||||
|
const urlRkey = parsedUrl.searchParams.get('rkey');
|
||||||
const imageAppid = parsedUrl.searchParams.get('appid');
|
const imageAppid = parsedUrl.searchParams.get('appid');
|
||||||
const isNTV2 = imageAppid && ['1406', '1407'].includes(imageAppid);
|
const isNTV2 = imageAppid && ['1406', '1407'].includes(imageAppid);
|
||||||
if (isNTV2) {
|
const imageFileId = parsedUrl.searchParams.get('fileid');
|
||||||
let rkey = parsedUrl.searchParams.get('rkey');
|
|
||||||
if (rkey) {
|
let rkeyData = {
|
||||||
return IMAGE_HTTP_HOST_NT + url;
|
private_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qEc3Rbib9LP4',
|
||||||
}
|
group_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qffcqm614gds',
|
||||||
const rkeyData = await this.rkeyManager.getRkey();
|
online_rkey: false
|
||||||
rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
|
};
|
||||||
return IMAGE_HTTP_HOST_NT + url + `${rkey}`;
|
|
||||||
} else {
|
try {
|
||||||
return IMAGE_HTTP_HOST + url;
|
let tempRkeyData = await this.rkeyManager.getRkey();
|
||||||
|
rkeyData.group_rkey = tempRkeyData.group_rkey;
|
||||||
|
rkeyData.private_rkey = tempRkeyData.private_rkey;
|
||||||
|
rkeyData.online_rkey = tempRkeyData.expired_time > Date.now() / 1000;
|
||||||
|
} catch (e) {
|
||||||
|
this.context.logger.logError.bind(this.context.logger)('获取rkey失败 Fallback Old Mode', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (isNTV2 && urlRkey) {
|
||||||
|
return IMAGE_HTTP_HOST_NT + urlRkey;
|
||||||
|
} else if (isNTV2 && rkeyData.online_rkey) {
|
||||||
|
let rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
|
||||||
|
return IMAGE_HTTP_HOST_NT + url + `&rkey=${rkey}`;
|
||||||
|
} else if (isNTV2 && imageFileId) {
|
||||||
|
let rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
|
||||||
|
return IMAGE_HTTP_HOST + `/download?appid=${imageAppid}&fileid=${imageFileId}&rkey=${rkey}`;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (fileMd5 || md5HexStr) {
|
} else if (fileMd5 || md5HexStr) {
|
||||||
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr)!.toUpperCase()}/0`;
|
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr)!.toUpperCase()}/0`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.context.logger.logDebug('图片url获取失败', element);
|
this.context.logger.logDebug('图片url获取失败', element);
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@@ -120,12 +120,20 @@ export class NTQQUserApi {
|
|||||||
return this.context.session.getProfileService().modifyDesktopMiniProfile(param);
|
return this.context.session.getProfileService().modifyDesktopMiniProfile(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
//需要异常处理
|
|
||||||
async getCookies(domain: string) {
|
async getCookies(domain: string) {
|
||||||
const ClientKeyData = await this.forceFetchClientKey();
|
const ClientKeyData = await this.forceFetchClientKey();
|
||||||
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin +
|
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin +
|
||||||
'&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + this.core.selfInfo.uin + '%2Finfocenter&keyindex=19%27';
|
'&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + this.core.selfInfo.uin + '%2Finfocenter&keyindex=19%27';
|
||||||
return await RequestUtil.HttpsGetCookies(requestUrl);
|
let data = await RequestUtil.HttpsGetCookies(requestUrl);
|
||||||
|
if (!data.p_skey || data.p_skey.length == 0) {
|
||||||
|
try {
|
||||||
|
let pskey = (await this.getPSkey([domain])).domainPskeyMap.get(domain);
|
||||||
|
if (pskey) data.p_skey = pskey;
|
||||||
|
} catch {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPSkey(domainList: string[]) {
|
async getPSkey(domainList: string[]) {
|
||||||
|
@@ -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)('获取快乐源泉失败');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -659,7 +659,8 @@ export interface GrayTipElement {
|
|||||||
export enum FaceType {
|
export enum FaceType {
|
||||||
normal = 1, // 小黄脸
|
normal = 1, // 小黄脸
|
||||||
normal2 = 2, // 新小黄脸, 从faceIndex 222开始?
|
normal2 = 2, // 新小黄脸, 从faceIndex 222开始?
|
||||||
dice = 3 // 骰子
|
dice = 3, // 骰子
|
||||||
|
poke = 5 // 拍一拍
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FaceIndex {
|
export enum FaceIndex {
|
||||||
@@ -908,11 +909,26 @@ export interface RawMessage {
|
|||||||
*/
|
*/
|
||||||
peerUin: string;
|
peerUin: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 好友备注(如果是好友消息)
|
||||||
|
*/
|
||||||
|
remark?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群名(如果是群消息)
|
||||||
|
*/
|
||||||
|
peerName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送者昵称(如果是好友消息)
|
* 发送者昵称(如果是好友消息)
|
||||||
*/
|
*/
|
||||||
sendNickName: string;
|
sendNickName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送者好友备注(如果是群消息并且有发送者好友)
|
||||||
|
*/
|
||||||
|
sendRemarkName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送者群名片(如果是群消息)
|
* 发送者群名片(如果是群消息)
|
||||||
*/
|
*/
|
||||||
@@ -973,4 +989,4 @@ export interface MsgReqType {
|
|||||||
extraCnt: number
|
extraCnt: number
|
||||||
}
|
}
|
||||||
//getMsgsIncludeSelf Peer必须 byType 1
|
//getMsgsIncludeSelf Peer必须 byType 1
|
||||||
//getMsgsWithMsgTimeAndClientSeqForC2C Peer必须 byType 3
|
//getMsgsWithMsgTimeAndClientSeqForC2C Peer必须 byType 3
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
src/core/external/appid.json
vendored
36
src/core/external/appid.json
vendored
@@ -1,10 +1,42 @@
|
|||||||
{
|
{
|
||||||
"9.9.15-28060":{
|
"9.9.15-28060": {
|
||||||
"appid": 537246092,
|
"appid": 537246092,
|
||||||
"qua": "V1_WIN_NQ_9.9.15_28060_GW_B"
|
"qua": "V1_WIN_NQ_9.9.15_28060_GW_B"
|
||||||
},
|
},
|
||||||
"3.2.12-28060":{
|
"9.9.15-28131": {
|
||||||
|
"appid": 537246092,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_28131_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.12-28060": {
|
||||||
"appid": 537246140,
|
"appid": 537246140,
|
||||||
"qua": "V1_LNX_NQ_3.2.12_28060_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"
|
||||||
|
},
|
||||||
|
"6.9.55-28131": {
|
||||||
|
"appid": 537246115,
|
||||||
|
"qua": "V1_MAC_NQ_6.9.55_28131_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.15-28327":{
|
||||||
|
"appid": 537249321,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_28327_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.12-28327":{
|
||||||
|
"appid": 537249393,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.12_28327_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.15-28418":{
|
||||||
|
"appid": 537249321,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.15_28418_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.12-28418":{
|
||||||
|
"appid": 537249393,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.12_28418_GW_B"
|
||||||
|
},
|
||||||
|
"6.9.56-28418": {
|
||||||
|
"appid": 537249367,
|
||||||
|
"qua": "V1_MAC_NQ_6.9.56_28418_GW_B"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,8 @@ export class RkeyManager {
|
|||||||
try {
|
try {
|
||||||
await this.refreshRkey();
|
await this.refreshRkey();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.logError('获取rkey失败', e);
|
throw new Error(`获取rkey失败: ${e}`);//外抛
|
||||||
|
//this.logger.logError.bind(this.logger)('获取rkey失败', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.rkeyData;
|
return this.rkeyData;
|
||||||
@@ -42,9 +43,18 @@ export class RkeyManager {
|
|||||||
//刷新rkey
|
//刷新rkey
|
||||||
for (const url of this.serverUrl) {
|
for (const url of this.serverUrl) {
|
||||||
try {
|
try {
|
||||||
this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET');
|
let temp = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET');
|
||||||
|
this.rkeyData = {
|
||||||
|
group_rkey: temp.group_rkey.slice(6),
|
||||||
|
private_rkey: temp.private_rkey.slice(6),
|
||||||
|
expired_time: temp.expired_time
|
||||||
|
};
|
||||||
} 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);
|
||||||
|
//是否为最后一个url
|
||||||
|
if (url === this.serverUrl[this.serverUrl.length - 1]) {
|
||||||
|
throw new Error(`获取rkey失败: ${e}`);//外抛
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,16 +20,15 @@ import { LogLevel, LogWrapper } from '@/common/log';
|
|||||||
import { NodeIKernelLoginService } from '@/core/services';
|
import { NodeIKernelLoginService } from '@/core/services';
|
||||||
import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
|
import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
|
||||||
import { NapCatPathWrapper } from '@/common/path';
|
import { NapCatPathWrapper } from '@/common/path';
|
||||||
import path from 'node:path';
|
import path, { resolve } 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 { ChatType, DataSource, GroupMember, KickedOffLineInfo, Peer, SelfInfo, SelfStatusInfo } from '@/core/entities';
|
||||||
import { NapCatConfigLoader } from '@/core/helper/config';
|
import { NapCatConfigLoader } from '@/core/helper/config';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
|
import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
|
||||||
import { proxiedListenerOf } from '@/common/proxy-handler';
|
import { proxiedListenerOf } from '@/common/proxy-handler';
|
||||||
|
|
||||||
export * from './wrapper';
|
export * from './wrapper';
|
||||||
export * from './entities';
|
export * from './entities';
|
||||||
export * from './services';
|
export * from './services';
|
||||||
@@ -99,7 +98,8 @@ 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,
|
||||||
@@ -127,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) => {
|
||||||
@@ -247,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: {
|
||||||
@@ -270,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: '',
|
||||||
|
5
src/core/listeners/NodeIO3MiscListener.ts
Normal file
5
src/core/listeners/NodeIO3MiscListener.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export class NodeIO3MiscListener {
|
||||||
|
getOnAmgomDataPiece(...arg: unknown[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
21
src/core/proto/ImageFileId.ts
Normal file
21
src/core/proto/ImageFileId.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { MessageType, BinaryReader, ScalarType, BinaryWriter } from '@protobuf-ts/runtime';
|
||||||
|
|
||||||
|
export const FileId = new MessageType("FileId", [
|
||||||
|
{ no: 2, name: "sha1", kind: "scalar", T: ScalarType.BYTES },
|
||||||
|
{ no: 4, name: "appid", kind: "scalar", T: ScalarType.UINT32 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function encodePBFileId(message: any) {
|
||||||
|
return FileId.internalBinaryWrite(message, new BinaryWriter(), {
|
||||||
|
writerFactory: () => new BinaryWriter(),
|
||||||
|
writeUnknownFields: false
|
||||||
|
}).finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodePBFileId(buffer: Uint8Array): any {
|
||||||
|
const reader = new BinaryReader(buffer);
|
||||||
|
return FileId.internalBinaryRead(reader, reader.len, {
|
||||||
|
readUnknownField: true,
|
||||||
|
readerFactory: () => new BinaryReader(buffer)
|
||||||
|
});
|
||||||
|
}
|
48
src/core/proto/Message.ts
Normal file
48
src/core/proto/Message.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { MessageType, BinaryReader, ScalarType } from '@protobuf-ts/runtime';
|
||||||
|
|
||||||
|
export const BodyInner = new MessageType("BodyInner", [
|
||||||
|
{ no: 1, name: "msgType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */, opt: true },
|
||||||
|
{ no: 2, name: "subType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */, opt: true }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const NoifyData = new MessageType("NoifyData", [
|
||||||
|
{ no: 1, name: "skip", kind: "scalar", T: ScalarType.BYTES /* bytes */, opt: true },
|
||||||
|
{ no: 2, name: "innerData", kind: "scalar", T: ScalarType.BYTES /* bytes */, opt: true }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const MsgHead = new MessageType("MsgHead", [
|
||||||
|
{ no: 2, name: "bodyInner", kind: "message", T: () => BodyInner, opt: true },
|
||||||
|
{ no: 3, name: "noifyData", kind: "message", T: () => NoifyData, opt: true }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const Message = new MessageType("Message", [
|
||||||
|
{ no: 1, name: "msgHead", kind: "message", T: () => MsgHead }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const SubDetail = new MessageType("SubDetail", [
|
||||||
|
{ no: 1, name: "msgSeq", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 2, name: "msgTime", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 6, name: "senderUid", kind: "scalar", T: ScalarType.STRING /* string */ }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const RecallDetails = new MessageType("RecallDetails", [
|
||||||
|
{ no: 1, name: "operatorUid", kind: "scalar", T: ScalarType.STRING /* string */ },
|
||||||
|
{ no: 3, name: "subDetail", kind: "message", T: () => SubDetail }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const RecallGroup = new MessageType("RecallGroup", [
|
||||||
|
{ no: 1, name: "type", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
|
||||||
|
{ no: 4, name: "peerUid", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 11, name: "recallDetails", kind: "message", T: () => RecallDetails },
|
||||||
|
{ no: 37, name: "grayTipsSeq", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function decodeMessage(buffer: Uint8Array): any {
|
||||||
|
const reader = new BinaryReader(buffer);
|
||||||
|
return Message.internalBinaryRead(reader, reader.len, { readUnknownField: true, readerFactory: () => new BinaryReader(buffer) });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeRecallGroup(buffer: Uint8Array): any {
|
||||||
|
const reader = new BinaryReader(buffer);
|
||||||
|
return RecallGroup.internalBinaryRead(reader, reader.len, { readUnknownField: true, readerFactory: () => new BinaryReader(buffer) });
|
||||||
|
}
|
@@ -1,95 +1,58 @@
|
|||||||
import * as pb from 'protobufjs';
|
import { MessageType, BinaryReader, ScalarType, RepeatType } from '@protobuf-ts/runtime';
|
||||||
|
|
||||||
// Proto: from src/core/proto/ProfileLike.proto
|
export const LikeDetail = new MessageType("likeDetail", [
|
||||||
// Author: Mlikiowa
|
{ no: 1, name: "txt", kind: "scalar", T: ScalarType.STRING /* string */ },
|
||||||
|
{ no: 3, name: "uin", kind: "scalar", T: ScalarType.INT64 /* int64 */ },
|
||||||
|
{ no: 5, name: "nickname", kind: "scalar", T: ScalarType.STRING /* string */ }
|
||||||
|
]);
|
||||||
|
|
||||||
export interface LikeDetailType {
|
export const LikeMsg = new MessageType("likeMsg", [
|
||||||
txt: string;
|
{ no: 1, name: "times", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
|
||||||
uin: pb.Long;
|
{ no: 2, name: "time", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
|
||||||
nickname: string;
|
{ no: 3, name: "detail", kind: "message", T: () => LikeDetail }
|
||||||
}
|
]);
|
||||||
export interface LikeMsgType {
|
|
||||||
times: number;
|
export const ProfileLikeSubTip = new MessageType("profileLikeSubTip", [
|
||||||
time: number;
|
{ no: 14, name: "msg", kind: "message", T: () => LikeMsg }
|
||||||
detail: LikeDetailType;
|
]);
|
||||||
|
export const ProfileLikeTip = new MessageType("profileLikeTip", [
|
||||||
|
{ no: 1, name: "msgType", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
|
||||||
|
{ no: 2, name: "subType", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
|
||||||
|
{ no: 203, name: "content", kind: "message", T: () => ProfileLikeSubTip }
|
||||||
|
]);
|
||||||
|
export const SysMessageHeader = new MessageType("SysMessageHeader", [
|
||||||
|
{ no: 1, name: "PeerNumber", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 2, name: "PeerString", kind: "scalar", T: ScalarType.STRING /* string */ },
|
||||||
|
{ no: 5, name: "Uin", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 6, name: "Uid", kind: "scalar", T: ScalarType.STRING /* string */, opt: true }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const SysMessageMsgSpec = new MessageType("SysMessageMsgSpec", [
|
||||||
|
{ no: 1, name: "msgType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 2, name: "subType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 3, name: "subSubType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 5, name: "msgSeq", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 6, name: "time", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
|
||||||
|
{ no: 12, name: "msgId", kind: "scalar", T: ScalarType.UINT64 /* uint64 */ },
|
||||||
|
{ no: 13, name: "other", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const SysMessageBodyWrapper = new MessageType("SysMessageBodyWrapper", [
|
||||||
|
{ no: 2, name: "wrappedBody", kind: "scalar", T: ScalarType.BYTES /* bytes */ }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const SysMessage = new MessageType("SysMessage", [
|
||||||
|
{ no: 1, name: "header", kind: "message", T: () => SysMessageHeader, repeat: RepeatType.UNPACKED },
|
||||||
|
{ no: 2, name: "msgSpec", kind: "message", T: () => SysMessageMsgSpec, repeat: RepeatType.UNPACKED },
|
||||||
|
{ no: 3, name: "bodyWrapper", kind: "message", T: () => SysMessageBodyWrapper }
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function decodeProfileLikeTip(buffer: Uint8Array): any {
|
||||||
|
const reader = new BinaryReader(buffer);
|
||||||
|
return ProfileLikeTip.internalBinaryRead(reader, reader.len, { readUnknownField: true, readerFactory: () => new BinaryReader(buffer) });
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface profileLikeSubTipType {
|
export function decodeSysMessage(buffer: Uint8Array): any {
|
||||||
msg: LikeMsgType;
|
const reader = new BinaryReader(buffer);
|
||||||
}
|
return SysMessage.internalBinaryRead(reader, reader.len, { readUnknownField: true, readerFactory: () => new BinaryReader(buffer) });
|
||||||
|
}
|
||||||
export interface ProfileLikeTipType {
|
|
||||||
msgType: number;
|
|
||||||
subType: number;
|
|
||||||
content: profileLikeSubTipType;
|
|
||||||
}
|
|
||||||
export interface SysMessageHeaderType {
|
|
||||||
id: string;
|
|
||||||
timestamp: number;
|
|
||||||
sender: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SysMessageMsgSpecType {
|
|
||||||
msgType: number;
|
|
||||||
subType: number;
|
|
||||||
subSubType: number;
|
|
||||||
msgSeq: number;
|
|
||||||
time: number;
|
|
||||||
msgId: pb.Long;
|
|
||||||
other: number;
|
|
||||||
}
|
|
||||||
export interface SysMessageBodyWrapperType {
|
|
||||||
wrappedBody: Uint8Array;
|
|
||||||
}
|
|
||||||
export interface SysMessageType {
|
|
||||||
header: SysMessageHeaderType[];
|
|
||||||
msgSpec: SysMessageMsgSpecType[];
|
|
||||||
bodyWrapper: SysMessageBodyWrapperType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SysMessageHeader = new pb.Type("SysMessageHeader")
|
|
||||||
.add(new pb.Field("PeerNumber", 1, "uint32"))
|
|
||||||
.add(new pb.Field("PeerString", 2, "string"))
|
|
||||||
.add(new pb.Field("Uin", 5, "uint32"))
|
|
||||||
.add(new pb.Field("Uid", 6, "string", "optional"));
|
|
||||||
|
|
||||||
export const SysMessageMsgSpec = new pb.Type("SysMessageMsgSpec")
|
|
||||||
.add(new pb.Field("msgType", 1, "uint32"))
|
|
||||||
.add(new pb.Field("subType", 2, "uint32"))
|
|
||||||
.add(new pb.Field("subSubType", 3, "uint32"))
|
|
||||||
.add(new pb.Field("msgSeq", 5, "uint32"))
|
|
||||||
.add(new pb.Field("time", 6, "uint32"))
|
|
||||||
.add(new pb.Field("msgId", 12, "uint64"))
|
|
||||||
.add(new pb.Field("other", 13, "uint32"));
|
|
||||||
|
|
||||||
export const SysMessageBodyWrapper = new pb.Type("SysMessageBodyWrapper")
|
|
||||||
.add(new pb.Field("wrappedBody", 2, "bytes"));
|
|
||||||
|
|
||||||
export const SysMessage = new pb.Type("SysMessage")
|
|
||||||
.add(SysMessageHeader)
|
|
||||||
.add(SysMessageMsgSpec)
|
|
||||||
.add(SysMessageBodyWrapper)
|
|
||||||
.add(new pb.Field("header", 1, "SysMessageHeader", "repeated"))
|
|
||||||
.add(new pb.Field("msgSpec", 2, "SysMessageMsgSpec", "repeated"))
|
|
||||||
.add(new pb.Field("bodyWrapper", 3, "SysMessageBodyWrapper"));
|
|
||||||
|
|
||||||
export const likeDetail = new pb.Type("likeDetail")
|
|
||||||
.add(new pb.Field("txt", 1, "string"))
|
|
||||||
.add(new pb.Field("uin", 3, "int64"))
|
|
||||||
.add(new pb.Field("nickname", 5, "string"));
|
|
||||||
|
|
||||||
export const likeMsg = new pb.Type("likeMsg")
|
|
||||||
.add(likeDetail)
|
|
||||||
.add(new pb.Field("times", 1, "int32"))
|
|
||||||
.add(new pb.Field("time", 2, "int32"))
|
|
||||||
.add(new pb.Field("detail", 3, "likeDetail"));
|
|
||||||
|
|
||||||
export const profileLikeSubTip = new pb.Type("profileLikeSubTip")
|
|
||||||
.add(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"));
|
|
@@ -240,7 +240,12 @@ export interface NodeIKernelGroupService {
|
|||||||
|
|
||||||
getGroupRecommendContactArkJson(groupCode: string): unknown;
|
getGroupRecommendContactArkJson(groupCode: string): unknown;
|
||||||
|
|
||||||
getJoinGroupLink(groupCode: string): unknown;
|
getJoinGroupLink(param: {
|
||||||
|
groupCode: string,
|
||||||
|
srcId: number,//73
|
||||||
|
needShortUrl: boolean,//true
|
||||||
|
additionalParam: string//''
|
||||||
|
}): Promise<GeneralCallResult & { url?: string }>;
|
||||||
|
|
||||||
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
modifyGroupExtInfo(groupCode: string, arg: unknown): void;
|
||||||
|
|
||||||
|
@@ -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,6 +60,9 @@ export interface QuickLoginResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeIKernelLoginService {
|
export interface NodeIKernelLoginService {
|
||||||
|
setLoginMiscData(arg0: string, value: string): unknown;
|
||||||
|
getMachineGuid(): string;
|
||||||
|
|
||||||
get(): NodeIKernelLoginService;
|
get(): NodeIKernelLoginService;
|
||||||
|
|
||||||
connect(): boolean;
|
connect(): boolean;
|
||||||
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
@@ -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>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
11
src/core/services/NodeIO3MiscService.ts
Normal file
11
src/core/services/NodeIO3MiscService.ts
Normal 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;
|
||||||
|
}
|
@@ -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;
|
||||||
@@ -144,7 +145,7 @@ export interface NodeQQNTWrapperUtil {
|
|||||||
|
|
||||||
export interface NodeIQQNTWrapperSession {
|
export interface NodeIQQNTWrapperSession {
|
||||||
create(): NodeIQQNTWrapperSession;
|
create(): NodeIQQNTWrapperSession;
|
||||||
|
|
||||||
init(
|
init(
|
||||||
wrapperSessionInitConfig: WrapperSessionInitConfig,
|
wrapperSessionInitConfig: WrapperSessionInitConfig,
|
||||||
nodeIDependsAdapter: NodeIDependsAdapter,
|
nodeIDependsAdapter: NodeIDependsAdapter,
|
||||||
@@ -249,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;
|
||||||
@@ -262,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 {
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
BIN
src/native/external/MoeHoo.win32.node
vendored
Normal file
BIN
src/native/external/MoeHoo.win32.node
vendored
Normal file
Binary file not shown.
40
src/native/index.ts
Normal file
40
src/native/index.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { constants } from "node:os";
|
||||||
|
import path from "path";
|
||||||
|
import { dlopen } from "process";
|
||||||
|
import fs from "fs";
|
||||||
|
export class Native {
|
||||||
|
platform: string;
|
||||||
|
supportedPlatforms = ['win32'];
|
||||||
|
MoeHooExport: any = { exports: {} };
|
||||||
|
recallHookEnabled: boolean = false;
|
||||||
|
inited = true;
|
||||||
|
constructor(nodePath: string, platform: string = process.platform) {
|
||||||
|
this.platform = platform;
|
||||||
|
try {
|
||||||
|
if (!this.supportedPlatforms.includes(this.platform)) {
|
||||||
|
throw new Error(`Platform ${this.platform} is not supported`);
|
||||||
|
}
|
||||||
|
let nativeNode = path.join(nodePath, './native/MoeHoo.win32.node');
|
||||||
|
if (fs.existsSync(nativeNode)) {
|
||||||
|
dlopen(this.MoeHooExport, nativeNode, constants.dlopen.RTLD_LAZY);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.inited = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
isSetReCallEnabled(): boolean {
|
||||||
|
return this.recallHookEnabled && this.inited;
|
||||||
|
}
|
||||||
|
registerRecallCallback(callback: (hex: string) => any): void {
|
||||||
|
try {
|
||||||
|
if (!this.inited) throw new Error('Native Not Init');
|
||||||
|
if (this.MoeHooExport.exports?.registMsgPush) {
|
||||||
|
this.MoeHooExport.exports.registMsgPush(callback);
|
||||||
|
this.recallHookEnabled = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.recallHookEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,16 @@
|
|||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { OB11ForwardMessage } from '@/onebot';
|
import { OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageForward, OB11MessageNode as OriginalOB11MessageNode } from '@/onebot';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { MessageUnique } from '@/common/message-unique';
|
import { MessageUnique } from '@/common/message-unique';
|
||||||
|
|
||||||
|
type OB11MessageNode = OriginalOB11MessageNode & {
|
||||||
|
data: {
|
||||||
|
content?: Array<OB11MessageData>;
|
||||||
|
message: Array<OB11MessageData>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@@ -18,36 +25,69 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
|
|||||||
actionName = ActionName.GoCQHTTP_GetForwardMsg;
|
actionName = ActionName.GoCQHTTP_GetForwardMsg;
|
||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
|
private createTemplateNode(message: OB11Message): OB11MessageNode {
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.node,
|
||||||
|
data: {
|
||||||
|
user_id: message.user_id,
|
||||||
|
nickname: message.sender.nickname,
|
||||||
|
message: [],
|
||||||
|
content: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseForward(messages: OB11Message[]): Promise<OB11MessageNode[]> {
|
||||||
|
const retMsg: OB11MessageNode[] = [];
|
||||||
|
|
||||||
|
for (const message of messages) {
|
||||||
|
const templateNode = this.createTemplateNode(message);
|
||||||
|
|
||||||
|
for (const msgdata of message.message) {
|
||||||
|
if ((msgdata as OB11MessageData).type === OB11MessageDataType.forward) {
|
||||||
|
const newNode = this.createTemplateNode(message);
|
||||||
|
newNode.data.message = await this.parseForward((msgdata as OB11MessageForward).data.content);
|
||||||
|
|
||||||
|
templateNode.data.message.push(newNode);
|
||||||
|
} else {
|
||||||
|
templateNode.data.message.push(msgdata as OB11MessageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retMsg.push(templateNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retMsg;
|
||||||
|
}
|
||||||
|
|
||||||
async _handle(payload: Payload): Promise<any> {
|
async _handle(payload: Payload): Promise<any> {
|
||||||
const msgId = payload.message_id || payload.id;
|
const msgId = payload.message_id || payload.id;
|
||||||
if (!msgId) {
|
if (!msgId) {
|
||||||
throw Error('message_id is required');
|
throw new Error('message_id is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId);
|
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId);
|
||||||
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || parseInt(msgId));
|
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || parseInt(msgId));
|
||||||
if (!rootMsg) {
|
if (!rootMsg) {
|
||||||
throw Error('msg not found');
|
throw new Error('msg not found');
|
||||||
}
|
}
|
||||||
const data = await this.core.apis.MsgApi.getMultiMsg(rootMsg.Peer, rootMsg.MsgId, rootMsg.MsgId);
|
const data = await this.core.apis.MsgApi.getMsgsByMsgId(rootMsg.Peer, [rootMsg.MsgId]);
|
||||||
|
|
||||||
if (!data || data.result !== 0) {
|
if (!data || data.result !== 0) {
|
||||||
throw Error('找不到相关的聊天记录' + data?.errMsg);
|
throw new Error('找不到相关的聊天记录' + data?.errMsg);
|
||||||
}
|
}
|
||||||
const msgList = data.msgList;
|
|
||||||
const messages = (await Promise.all(msgList.map(async msg => {
|
const singleMsg = data.msgList[0];
|
||||||
const resMsg = await this.obContext.apis.MsgApi
|
const resMsg = await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array');//强制array 以便处理
|
||||||
.parseMessage(msg);
|
if (!resMsg) {
|
||||||
if (!resMsg) return;
|
throw new Error('找不到相关的聊天记录');
|
||||||
resMsg.message_id = MessageUnique.createUniqueMsgId({
|
}
|
||||||
guildId: '',
|
//if (this.obContext.configLoader.configData.messagePostFormat === 'array') {
|
||||||
chatType: msg.chatType,
|
//提取
|
||||||
peerUid: msg.peerUid,
|
let realmsg = ((await this.parseForward([resMsg]))[0].data.message as OB11MessageNode[])[0].data.message;
|
||||||
}, msg.msgId)!;
|
//里面都是offline消息 id都是0 没得说话
|
||||||
return resMsg;
|
return { message: realmsg };
|
||||||
}))).filter(msg => !!msg);
|
//}
|
||||||
messages.map(msg => {
|
|
||||||
(<OB11ForwardMessage>msg).content = msg.message;
|
// return { message: resMsg };
|
||||||
delete (<any>msg).message;
|
|
||||||
});
|
|
||||||
return { messages };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -23,7 +23,7 @@ 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());
|
||||||
let uids = groupMembersArr.map(item => item.uid);
|
const uids = groupMembersArr.map(item => item.uid);
|
||||||
//let CoreAndBase = await this.core.apis.GroupApi.getCoreAndBaseInfo(uids)
|
//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);
|
||||||
|
@@ -3,6 +3,7 @@ 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 { MessageUnique } from '@/common/message-unique';
|
import { MessageUnique } from '@/common/message-unique';
|
||||||
|
import { RawMessage } from '@/core';
|
||||||
|
|
||||||
|
|
||||||
export type ReturnDataType = OB11Message
|
export type ReturnDataType = OB11Message
|
||||||
@@ -32,13 +33,17 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
|
|||||||
throw new Error('消息不存在');
|
throw new Error('消息不存在');
|
||||||
}
|
}
|
||||||
const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType };
|
const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType };
|
||||||
const msg = await this.core.apis.MsgApi.getMsgsByMsgId(
|
let orimsg = this.obContext.recallMsgCache.get(msgIdWithPeer.MsgId);
|
||||||
peer,
|
let msg: RawMessage;
|
||||||
[msgIdWithPeer?.MsgId || payload.message_id.toString()]);
|
if (orimsg) {
|
||||||
const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg.msgList[0]);
|
msg = orimsg;
|
||||||
|
} else {
|
||||||
|
msg = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgIdWithPeer?.MsgId || payload.message_id.toString()])).msgList[0];
|
||||||
|
}
|
||||||
|
const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg);
|
||||||
if (!retMsg) throw Error('消息为空');
|
if (!retMsg) throw Error('消息为空');
|
||||||
try {
|
try {
|
||||||
retMsg.message_id = MessageUnique.createUniqueMsgId(peer, msg.msgList[0].msgId)!;
|
retMsg.message_id = MessageUnique.createUniqueMsgId(peer, msg.msgId)!;
|
||||||
retMsg.message_seq = retMsg.message_id;
|
retMsg.message_seq = retMsg.message_id;
|
||||||
retMsg.real_id = retMsg.message_id;
|
retMsg.real_id = retMsg.message_id;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,14 +15,14 @@ export class OneBotFriendApi {
|
|||||||
//使用前预先判断 busiId 1061
|
//使用前预先判断 busiId 1061
|
||||||
async parsePrivatePokeEvent(grayTipElement: GrayTipElement) {
|
async parsePrivatePokeEvent(grayTipElement: GrayTipElement) {
|
||||||
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
|
const json = JSON.parse(grayTipElement.jsonGrayTipElement.jsonStr);
|
||||||
let pokedetail: any[] = json.items;
|
let pokedetail: Array<{ uid: string }> = json.items;
|
||||||
//筛选item带有uid的元素
|
//筛选item带有uid的元素
|
||||||
pokedetail = pokedetail.filter(item => item.uid);
|
pokedetail = pokedetail.filter(item => item.uid);
|
||||||
if (pokedetail.length == 2) {
|
if (pokedetail.length == 2) {
|
||||||
return new OB11FriendPokeEvent(
|
return new OB11FriendPokeEvent(
|
||||||
this.core,
|
this.core,
|
||||||
parseInt((await this.core.apis.UserApi.getUinByUidV2(pokedetail[0].uid))!),
|
parseInt((await this.core.apis.UserApi.getUinByUidV2(pokedetail[0].uid))),
|
||||||
parseInt((await this.core.apis.UserApi.getUinByUidV2(pokedetail[1].uid))!),
|
parseInt((await this.core.apis.UserApi.getUinByUidV2(pokedetail[1].uid))),
|
||||||
pokedetail,
|
pokedetail,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -96,7 +96,6 @@ export class OneBotGroupApi {
|
|||||||
if (GroupIncreaseEvent) return GroupIncreaseEvent;
|
if (GroupIncreaseEvent) return GroupIncreaseEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
//代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE
|
|
||||||
else if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
else if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
||||||
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr);
|
const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr);
|
||||||
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||||
@@ -139,7 +138,6 @@ export class OneBotGroupApi {
|
|||||||
// 获取MsgSeq+Peer可获取具体消息
|
// 获取MsgSeq+Peer可获取具体消息
|
||||||
}
|
}
|
||||||
if (element.grayTipElement.jsonGrayTipElement.busiId == 2407) {
|
if (element.grayTipElement.jsonGrayTipElement.busiId == 2407) {
|
||||||
//下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE
|
|
||||||
const type = json.items[json.items.length - 1]?.txt;
|
const type = json.items[json.items.length - 1]?.txt;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "头衔": {
|
case "头衔": {
|
||||||
@@ -207,7 +205,6 @@ export class OneBotGroupApi {
|
|||||||
while ((match = regex.exec(xmlElement.content)) !== null) {
|
while ((match = regex.exec(xmlElement.content)) !== null) {
|
||||||
matches.push(match[1]);
|
matches.push(match[1]);
|
||||||
}
|
}
|
||||||
// log("新人进群匹配到的QQ号", matches)
|
|
||||||
if (matches.length === 2) {
|
if (matches.length === 2) {
|
||||||
const [inviter, invitee] = matches;
|
const [inviter, invitee] = matches;
|
||||||
return new OB11GroupIncreaseEvent(
|
return new OB11GroupIncreaseEvent(
|
||||||
@@ -289,7 +286,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(
|
||||||
|
@@ -34,7 +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';
|
import { decodeSysMessage } 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]: (
|
||||||
@@ -122,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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -201,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,14 +212,14 @@ export class OneBotMsgApi {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (records.peerUin === '284840486') {
|
if (records.peerUin === '284840486' || records.peerUin === '1094950020') {
|
||||||
return createReplyData(records.msgId);
|
return createReplyData(records.msgId);
|
||||||
}
|
}
|
||||||
const replyMsg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeqV2(peer, element.replayMsgSeq, element.replyMsgTime, [element.senderUidStr]))
|
const replyMsg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeqV2(peer, element.replayMsgSeq, element.replyMsgTime, [element.senderUidStr]))
|
||||||
.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);
|
||||||
@@ -234,19 +234,29 @@ export class OneBotMsgApi {
|
|||||||
//读取视频链接并兜底
|
//读取视频链接并兜底
|
||||||
let videoUrlWrappers: Awaited<ReturnType<typeof this.core.apis.FileApi.getVideoUrl>> | undefined;
|
let videoUrlWrappers: Awaited<ReturnType<typeof this.core.apis.FileApi.getVideoUrl>> | undefined;
|
||||||
|
|
||||||
if (msg.peerUin === '284840486') {
|
if (msg.peerUin === '284840486' || msg.peerUin === '1094950020') {
|
||||||
//TODO: 合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Tagged
|
try {
|
||||||
}
|
videoUrlWrappers = await this.core.apis.FileApi.getVideoUrl({
|
||||||
try {
|
chatType: msg.chatType,
|
||||||
videoUrlWrappers = await this.core.apis.FileApi.getVideoUrl({
|
peerUid: msg.peerUid,
|
||||||
chatType: msg.chatType,
|
guildId: '0',
|
||||||
peerUid: msg.peerUid,
|
}, msg.parentMsgIdList[0] ?? msg.msgId, elementWrapper.elementId);
|
||||||
guildId: '0',
|
} catch (error) {
|
||||||
}, msg.msgId, elementWrapper.elementId);
|
this.core.context.logger.logWarn('合并获取视频 URL 失败');
|
||||||
} catch (error) {
|
}
|
||||||
this.core.context.logger.logWarn('获取视频 URL 失败');
|
} else {
|
||||||
|
try {
|
||||||
|
videoUrlWrappers = await this.core.apis.FileApi.getVideoUrl({
|
||||||
|
chatType: msg.chatType,
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
guildId: '0',
|
||||||
|
}, msg.msgId, elementWrapper.elementId);
|
||||||
|
} catch (error) {
|
||||||
|
this.core.context.logger.logWarn('获取视频 URL 失败');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//读取在线URL
|
//读取在线URL
|
||||||
let videoDownUrl: string | undefined;
|
let videoDownUrl: string | undefined;
|
||||||
|
|
||||||
@@ -421,6 +431,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) {
|
||||||
@@ -443,7 +457,6 @@ export class OneBotMsgApi {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
[OB11MessageDataType.mface]: async ({
|
[OB11MessageDataType.mface]: async ({
|
||||||
data: {
|
data: {
|
||||||
emoji_package_id, emoji_id, key, summary,
|
emoji_package_id, emoji_id, key, summary,
|
||||||
@@ -506,13 +519,12 @@ export class OneBotMsgApi {
|
|||||||
faceElement: {
|
faceElement: {
|
||||||
faceIndex: FaceIndex.dice,
|
faceIndex: FaceIndex.dice,
|
||||||
faceType: FaceType.dice,
|
faceType: FaceType.dice,
|
||||||
'faceText': '[骰子]',
|
faceText: '[骰子]',
|
||||||
'packId': '1',
|
packId: '1',
|
||||||
'stickerId': '33',
|
stickerId: '33',
|
||||||
'sourceType': 1,
|
sourceType: 1,
|
||||||
'stickerType': 2,
|
stickerType: 2,
|
||||||
// resultId: resultId.toString(),
|
surpriseId: '',
|
||||||
'surpriseId': '',
|
|
||||||
// "randomType": 1,
|
// "randomType": 1,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -521,15 +533,14 @@ export class OneBotMsgApi {
|
|||||||
elementType: ElementType.FACE,
|
elementType: ElementType.FACE,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
faceElement: {
|
faceElement: {
|
||||||
'faceIndex': FaceIndex.RPS,
|
faceIndex: FaceIndex.RPS,
|
||||||
'faceText': '[包剪锤]',
|
faceText: '[包剪锤]',
|
||||||
'faceType': 3,
|
faceType: 3,
|
||||||
'packId': '1',
|
packId: '1',
|
||||||
'stickerId': '34',
|
stickerId: '34',
|
||||||
'sourceType': 1,
|
sourceType: 1,
|
||||||
'stickerType': 2,
|
stickerType: 2,
|
||||||
// 'resultId': resultId.toString(),
|
surpriseId: '',
|
||||||
'surpriseId': '',
|
|
||||||
// "randomType": 1,
|
// "randomType": 1,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -543,40 +554,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 +593,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);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -609,7 +617,7 @@ export class OneBotMsgApi {
|
|||||||
[OB11MessageDataType.miniapp]: async () => undefined,
|
[OB11MessageDataType.miniapp]: async () => undefined,
|
||||||
|
|
||||||
[OB11MessageDataType.contact]: async ({ data }, context) => {
|
[OB11MessageDataType.contact]: async ({ data }, context) => {
|
||||||
let arkJson = await this.core.apis.UserApi.getBuddyRecommendContactArkJson(data.id.toString(), '');
|
const arkJson = await this.core.apis.UserApi.getBuddyRecommendContactArkJson(data.id.toString(), '');
|
||||||
return this.ob11ToRawConverters.json({
|
return this.ob11ToRawConverters.json({
|
||||||
data: { data: arkJson.arkMsg },
|
data: { data: arkJson.arkMsg },
|
||||||
type: OB11MessageDataType.json
|
type: OB11MessageDataType.json
|
||||||
@@ -627,18 +635,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.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
|
||||||
if (element.grayTipElement.xmlElement.templId === '10229' && msg.peerUin !== '') {
|
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
||||||
return new OB11FriendAddNoticeEvent(this.core, parseInt(msg.peerUin) || Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -700,36 +704,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;
|
||||||
@@ -786,7 +800,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('发送消息失败');
|
||||||
@@ -796,7 +810,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;
|
||||||
}
|
}
|
||||||
@@ -807,7 +821,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 {
|
||||||
@@ -818,7 +832,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,7 +841,7 @@ export class OneBotMsgApi {
|
|||||||
return { path, fileName: inputdata.name ?? fileName };
|
return { path, fileName: inputdata.name ?? fileName };
|
||||||
}
|
}
|
||||||
async parseSysMessage(msg: number[]) {
|
async parseSysMessage(msg: number[]) {
|
||||||
const sysMsg = SysMessage.decode(Uint8Array.from(msg)) as unknown as SysMessageType;
|
const sysMsg = decodeSysMessage(Uint8Array.from(msg));
|
||||||
if (sysMsg.msgSpec.length === 0) {
|
if (sysMsg.msgSpec.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { NapCatCore } from '@/core';
|
import { NapCatCore } from '@/core';
|
||||||
import { profileLikeTip, ProfileLikeTipType } from '@/core/proto/ProfileLike';
|
import { decodeProfileLikeTip } from '@/core/proto/ProfileLike';
|
||||||
|
|
||||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||||
import { OB11ProfileLikeEvent } from '../event/notice/OB11ProfileLikeEvent';
|
import { OB11ProfileLikeEvent } from '../event/notice/OB11ProfileLikeEvent';
|
||||||
@@ -13,7 +13,7 @@ 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)) as unknown as ProfileLikeTipType;
|
const likeTip = decodeProfileLikeTip(Uint8Array.from(wrappedBody));
|
||||||
if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return;
|
if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return;
|
||||||
this.core.context.logger.logDebug("收到点赞通知消息");
|
this.core.context.logger.logDebug("收到点赞通知消息");
|
||||||
const likeMsg = likeTip.content.msg;
|
const likeMsg = likeTip.content.msg;
|
||||||
|
@@ -10,6 +10,7 @@ import {
|
|||||||
NodeIKernelBuddyListener,
|
NodeIKernelBuddyListener,
|
||||||
NodeIKernelGroupListener,
|
NodeIKernelGroupListener,
|
||||||
NodeIKernelMsgListener,
|
NodeIKernelMsgListener,
|
||||||
|
Peer,
|
||||||
RawMessage,
|
RawMessage,
|
||||||
SendStatusType,
|
SendStatusType,
|
||||||
} from '@/core';
|
} from '@/core';
|
||||||
@@ -43,8 +44,9 @@ import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRec
|
|||||||
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
|
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
|
||||||
import { LRUCache } from '@/common/lru-cache';
|
import { LRUCache } from '@/common/lru-cache';
|
||||||
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
|
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
|
||||||
import { OB11ProfileLikeEvent } from './event/notice/OB11ProfileLikeEvent';
|
import { Native } from '@/native';
|
||||||
import { profileLikeTip, ProfileLikeTipType, SysMessage, SysMessageType } from '@/core/proto/ProfileLike';
|
import { decodeMessage, decodeRecallGroup, Message, RecallGroup } from '@/core/proto/Message';
|
||||||
|
|
||||||
//OneBot实现类
|
//OneBot实现类
|
||||||
export class NapCatOneBot11Adapter {
|
export class NapCatOneBot11Adapter {
|
||||||
readonly core: NapCatCore;
|
readonly core: NapCatCore;
|
||||||
@@ -54,8 +56,9 @@ export class NapCatOneBot11Adapter {
|
|||||||
apis: StableOneBotApiWrapper;
|
apis: StableOneBotApiWrapper;
|
||||||
networkManager: OB11NetworkManager;
|
networkManager: OB11NetworkManager;
|
||||||
actions: ActionMap;
|
actions: ActionMap;
|
||||||
|
nativeCore: Native | undefined;
|
||||||
private bootTime = Date.now() / 1000;
|
private bootTime = Date.now() / 1000;
|
||||||
|
recallMsgCache = new LRUCache<string, RawMessage>(100);
|
||||||
|
|
||||||
constructor(core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) {
|
constructor(core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) {
|
||||||
this.core = core;
|
this.core = core;
|
||||||
@@ -70,10 +73,41 @@ export class NapCatOneBot11Adapter {
|
|||||||
};
|
};
|
||||||
this.actions = createActionMap(this, core);
|
this.actions = createActionMap(this, core);
|
||||||
this.networkManager = new OB11NetworkManager();
|
this.networkManager = new OB11NetworkManager();
|
||||||
|
this.registerNative(core, context).catch(e => this.context.logger.logWarn.bind(this.context.logger)('初始化Native失败', e)).then();
|
||||||
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 registerNative(core: NapCatCore, context: InstanceContext) {
|
||||||
|
try {
|
||||||
|
this.nativeCore = new Native(context.pathWrapper.binaryPath);
|
||||||
|
if (!this.nativeCore.inited) throw new Error('Native Not Init');
|
||||||
|
this.nativeCore.registerRecallCallback(async (hex: string) => {
|
||||||
|
try {
|
||||||
|
let data = decodeMessage(Buffer.from(hex, 'hex')) as any;
|
||||||
|
//data.MsgHead.BodyInner.MsgType SubType
|
||||||
|
let bodyInner = data.msgHead?.bodyInner;
|
||||||
|
//context.logger.log("[appNative] Parse MsgType:" + bodyInner.msgType + " / SubType:" + bodyInner.subType);
|
||||||
|
if (bodyInner && bodyInner.msgType == 732 && bodyInner.subType == 17) {
|
||||||
|
let RecallData = Buffer.from(data.msgHead.noifyData.innerData);
|
||||||
|
//跳过 4字节 群号 + 不知道的1字节 +2字节 长度
|
||||||
|
let uid = RecallData.readUint32BE();
|
||||||
|
const buffer = Buffer.from(RecallData.toString('hex').slice(14), 'hex');
|
||||||
|
let seq: number = decodeRecallGroup(buffer).recallDetails.subDetail.msgSeq;
|
||||||
|
let peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: uid.toString() };
|
||||||
|
context.logger.log("[Native] 群消息撤回 Peer: " + uid.toString() + " / MsgSeq:" + seq);
|
||||||
|
let msgs = await core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, seq.toString());
|
||||||
|
this.recallMsgCache.put(msgs.msgList[0].msgId, msgs.msgList[0]);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
context.logger.logWarn("[Native] Error:", (error as Error).message, ' HEX:', hex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
context.logger.logWarn("[Native] Error:", (error as Error).message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
async InitOneBot() {
|
async InitOneBot() {
|
||||||
const selfInfo = this.core.selfInfo;
|
const selfInfo = this.core.selfInfo;
|
||||||
const ob11Config = this.configLoader.configData;
|
const ob11Config = this.configLoader.configData;
|
||||||
@@ -87,7 +121,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服务
|
||||||
@@ -241,7 +275,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
msgListener.onRecvSysMsg = (msg) => {
|
msgListener.onRecvSysMsg = (msg) => {
|
||||||
this.apis.MsgApi.parseSysMessage(msg).then((event) => {
|
this.apis.MsgApi.parseSysMessage(msg).then((event) => {
|
||||||
if (event) this.networkManager.emitEvent(event);
|
if (event) this.networkManager.emitEvent(event);
|
||||||
}).catch(e => this.context.logger.logError('constructSysMessage error: ', e));
|
}).catch(e => this.context.logger.logError.bind(this.context.logger)('constructSysMessage error: ', e, '\n Parse Hex:', Buffer.from(msg).toString('hex')));
|
||||||
};
|
};
|
||||||
|
|
||||||
msgListener.onInputStatusPush = async data => {
|
msgListener.onInputStatusPush = async data => {
|
||||||
@@ -270,7 +304,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));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -278,7 +312,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)) {
|
||||||
@@ -380,7 +414,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));
|
||||||
}
|
}
|
||||||
@@ -405,7 +439,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,
|
||||||
@@ -425,9 +459,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}`);
|
||||||
@@ -440,7 +474,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(
|
||||||
@@ -452,7 +486,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -474,9 +508,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)('群管理员变动处理完毕');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -499,8 +533,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;
|
||||||
@@ -509,23 +541,22 @@ 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>) {
|
||||||
for (const message of msgList) {
|
for (const message of msgList) {
|
||||||
// log("message update", message.sendStatus, message.msgId, message.msgSeq)
|
// log("message update", message.sendStatus, message.msgId, message.msgSeq)
|
||||||
@@ -543,7 +574,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) {
|
||||||
@@ -557,10 +588,10 @@ export class NapCatOneBot11Adapter {
|
|||||||
parseInt(message.peerUin),
|
parseInt(message.peerUin),
|
||||||
parseInt(message.senderUin),
|
parseInt(message.senderUin),
|
||||||
parseInt(operatorId),
|
parseInt(operatorId),
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -48,14 +48,14 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
//鉴权
|
//鉴权
|
||||||
this.authorize(token, wsClient, wsReq);
|
this.authorize(token, wsClient, wsReq);
|
||||||
const 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;
|
||||||
}
|
}
|
||||||
|
@@ -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,15 +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 = wrapper.NodeIKernelLoginService.get();
|
const loginService = wrapper.NodeIKernelLoginService.get();
|
||||||
const session = wrapper.NodeIQQNTWrapperSession.create();
|
|
||||||
|
|
||||||
|
const session = wrapper.NodeIQQNTWrapperSession.create();
|
||||||
// from get dataPath
|
// from get dataPath
|
||||||
const [dataPath, dataPathGlobal] = (() => {
|
const [dataPath, dataPathGlobal] = (() => {
|
||||||
if (os.platform() === 'darwin') {
|
if (os.platform() === 'darwin') {
|
||||||
@@ -63,17 +69,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 +101,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,13 +118,14 @@ export async function NCoreInitShell() {
|
|||||||
quickLoginUin = '';
|
quickLoginUin = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const 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;
|
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) => {
|
loginListener.onQRCodeLoginSucceed = async (loginResult) => {
|
||||||
@@ -117,7 +136,7 @@ export async function NCoreInitShell() {
|
|||||||
nick: '', // 获取不到
|
nick: '', // 获取不到
|
||||||
online: true,
|
online: true,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {
|
loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {
|
||||||
//设置WebuiQrcode
|
//设置WebuiQrcode
|
||||||
@@ -140,9 +159,8 @@ 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) {
|
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) {
|
||||||
// 二维码过期刷新
|
// 二维码过期刷新
|
||||||
}
|
}
|
||||||
@@ -150,14 +168,13 @@ export async function NCoreInitShell() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
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('核心登录服务连接成功!');
|
||||||
@@ -166,18 +183,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 {
|
||||||
@@ -193,14 +217,14 @@ 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);
|
||||||
if (!isLogined) loginService.getQRCodePicture();
|
if (!isLogined) loginService.getQRCodePicture();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch();
|
.catch();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式');
|
logger.logError.bind(logger)('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式');
|
||||||
if (!isLogined) loginService.getQRCodePicture();
|
if (!isLogined) loginService.getQRCodePicture();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -209,18 +233,30 @@ export async function NCoreInitShell() {
|
|||||||
logger.log(`可用于快速登录的 QQ:\n${historyLoginList
|
logger.log(`可用于快速登录的 QQ:\n${historyLoginList
|
||||||
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
|
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
|
||||||
.join('\n')
|
.join('\n')
|
||||||
}`);
|
}`);
|
||||||
}
|
}
|
||||||
loginService.getQRCodePicture();
|
loginService.getQRCodePicture();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// BEFORE LOGGING IN
|
// BEFORE LOGGING IN
|
||||||
|
const 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,
|
||||||
|
@@ -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}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -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.6.2', 'napcat-update-button', 'secondary'),
|
SettingButton('V2.6.23', 'napcat-update-button', 'secondary'),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -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.6.2", "napcat-update-button", "secondary")
|
SettingButton("V2.6.23", "napcat-update-button", "secondary")
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -41,6 +41,7 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
|
|||||||
const ShellBaseConfigPlugin: PluginOption[] = [
|
const ShellBaseConfigPlugin: PluginOption[] = [
|
||||||
cp({
|
cp({
|
||||||
targets: [
|
targets: [
|
||||||
|
{ src: './src/native/external', dest: 'dist/native', flatten: false },
|
||||||
{ src: './static/', dest: 'dist/static/', flatten: false },
|
{ src: './static/', dest: 'dist/static/', flatten: false },
|
||||||
{ 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/' },
|
||||||
|
Reference in New Issue
Block a user