mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
58 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3dd4b6549f | ||
![]() |
0802c35dc1 | ||
![]() |
7d9d7226ec | ||
![]() |
b5ef6ce6b0 | ||
![]() |
704ac11cbb | ||
![]() |
aa9663d85e | ||
![]() |
05291f34fb | ||
![]() |
2260fe32a1 | ||
![]() |
2c398a6832 | ||
![]() |
3e1f566699 | ||
![]() |
4f89f184b8 | ||
![]() |
787685c937 | ||
![]() |
ed9cd2fe38 | ||
![]() |
e287906a9d | ||
![]() |
8bae789020 | ||
![]() |
ce57b7b725 | ||
![]() |
1d9872195d | ||
![]() |
98d1f8e29f | ||
![]() |
221b3fb730 | ||
![]() |
90a834495a | ||
![]() |
8bfd102232 | ||
![]() |
65e784f169 | ||
![]() |
0fc81c672f | ||
![]() |
62ae0f4321 | ||
![]() |
a01a0a1a18 | ||
![]() |
4c30cc69ad | ||
![]() |
1d43b75df4 | ||
![]() |
d02afdfc3e | ||
![]() |
5d6dee9fd0 | ||
![]() |
60c67ef41c | ||
![]() |
917d7c1f19 | ||
![]() |
ad19f2c99e | ||
![]() |
8a61f5a03f | ||
![]() |
8c164910f6 | ||
![]() |
a560d3d266 | ||
![]() |
532f739272 | ||
![]() |
a120727f2d | ||
![]() |
a9bcb830a8 | ||
![]() |
56e5f0033f | ||
![]() |
101106996a | ||
![]() |
41a81534dc | ||
![]() |
1425e8f229 | ||
![]() |
75bb1d2193 | ||
![]() |
2a23820f9b | ||
![]() |
2ee0fed047 | ||
![]() |
40be6b9c43 | ||
![]() |
a06b3f0246 | ||
![]() |
4787fa53b4 | ||
![]() |
a06158bf01 | ||
![]() |
314e7485b8 | ||
![]() |
aed5d2d9f0 | ||
![]() |
f44e48a28b | ||
![]() |
38be90450c | ||
![]() |
2dd57d7676 | ||
![]() |
6b3b163fa8 | ||
![]() |
9792ebafdc | ||
![]() |
d10e7c37cb | ||
![]() |
d38f1853a4 |
@@ -1,64 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
'env': {
|
|
||||||
'browser': true,
|
|
||||||
'es2021': true,
|
|
||||||
'node': true
|
|
||||||
},
|
|
||||||
'ignorePatterns': ['src/core/proto/'],
|
|
||||||
'extends': [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended'
|
|
||||||
],
|
|
||||||
'overrides': [
|
|
||||||
{
|
|
||||||
'env': {
|
|
||||||
'node': true
|
|
||||||
},
|
|
||||||
'files': [
|
|
||||||
'.eslintrc.{js,cjs}'
|
|
||||||
],
|
|
||||||
'parserOptions': {
|
|
||||||
'sourceType': 'script'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'parser': '@typescript-eslint/parser',
|
|
||||||
'parserOptions': {
|
|
||||||
'ecmaVersion': 'latest',
|
|
||||||
'sourceType': 'module'
|
|
||||||
},
|
|
||||||
'plugins': [
|
|
||||||
'@typescript-eslint',
|
|
||||||
'import'
|
|
||||||
],
|
|
||||||
'settings': {
|
|
||||||
'import/parsers': {
|
|
||||||
'@typescript-eslint/parser': ['.ts']
|
|
||||||
},
|
|
||||||
'import/resolver': {
|
|
||||||
'typescript': {
|
|
||||||
'alwaysTryTypes': true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'rules': {
|
|
||||||
'indent': [
|
|
||||||
'error',
|
|
||||||
4
|
|
||||||
],
|
|
||||||
'linebreak-style': [
|
|
||||||
'error',
|
|
||||||
'unix'
|
|
||||||
],
|
|
||||||
'semi': [
|
|
||||||
'error',
|
|
||||||
'always'
|
|
||||||
],
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
'no-async-promise-executor': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
|
||||||
'@typescript-eslint/no-var-requires': 'off',
|
|
||||||
'object-curly-spacing': ['error', 'always'],
|
|
||||||
}
|
|
||||||
};
|
|
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -127,10 +127,6 @@ jobs:
|
|||||||
zip -q -r NapCat.Framework.Windows.Once.zip *
|
zip -q -r NapCat.Framework.Windows.Once.zip *
|
||||||
cd ..
|
cd ..
|
||||||
mv ./NapCat.Framework.Windows.Once/NapCat.Framework.Windows.Once.zip ./
|
mv ./NapCat.Framework.Windows.Once/NapCat.Framework.Windows.Once.zip ./
|
||||||
mv ./external/packet/napcat.packet.arm64 ./
|
|
||||||
mv ./external/packet/napcat.packet.exe ./
|
|
||||||
mv ./external/packet/napcat.packet.linux ./
|
|
||||||
mv ./external/packet/napcat.packet.production.py ./
|
|
||||||
- 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
|
||||||
|
|
||||||
@@ -147,8 +143,4 @@ jobs:
|
|||||||
NapCat.Framework.zip
|
NapCat.Framework.zip
|
||||||
NapCat.Shell.zip
|
NapCat.Shell.zip
|
||||||
NapCat.Framework.Windows.Once.zip
|
NapCat.Framework.Windows.Once.zip
|
||||||
napcat.packet.arm64
|
|
||||||
napcat.packet.exe
|
|
||||||
napcat.packet.linux
|
|
||||||
napcat.packet.production.py
|
|
||||||
draft: true
|
draft: true
|
||||||
|
@@ -9,14 +9,12 @@
|
|||||||
NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
|
NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
|
||||||
|
|
||||||
## 猫猫技能
|
## 猫猫技能
|
||||||
- [x] **超高性能**:轻松数千群聊 独创消息队列
|
|
||||||
- [x] **启动方式**:支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动
|
- [x] **启动方式**:支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动
|
||||||
- [x] **覆盖平台**: 覆盖 Windows / Linux (可选 Docker) / Android Termux / MacOS
|
- [x] **覆盖平台**: 覆盖 Windows / Linux (可选 Docker) / Android Termux / MacOS
|
||||||
- [x] **安装简单**: 支持一键脚本/程序自动部署/镜像部署等多种覆盖范围
|
- [x] **安装简单**: 支持一键脚本/程序自动部署/镜像部署等多种覆盖范围
|
||||||
- [x] **超低占用**:无头模式占用资源极低,适合在服务器上运行
|
- [x] **超低占用**:无头模式占用资源极低,适合在服务器上运行
|
||||||
- [x] **超多接口**:实现大部分 OneBot 和 go-cqhttp 接口,超多扩展 API
|
- [x] **超多接口**:实现大部分 OneBot 和 go-cqhttp 接口,超多扩展 API
|
||||||
- [x] **远程管理**:自带 WebUI 支持,远程管理更加便捷
|
- [x] **远程管理**:自带 WebUI 支持,远程管理更加便捷
|
||||||
- [x] **扩展支持**:基于 MoeHoo 的Native 可实现发包与收包
|
|
||||||
|
|
||||||
## 使用猫猫
|
## 使用猫猫
|
||||||
|
|
||||||
@@ -32,6 +30,10 @@ NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
|
|||||||
|
|
||||||
[Cloudflare.Pages](https://napneko.pages.dev/)
|
[Cloudflare.Pages](https://napneko.pages.dev/)
|
||||||
|
|
||||||
|
[Server.China](https://napneko.com/)
|
||||||
|
|
||||||
|
[Server.Other](https://napcat.cyou/)
|
||||||
|
|
||||||
[Github.IO](https://napneko.github.io/)
|
[Github.IO](https://napneko.github.io/)
|
||||||
## 回家旅途
|
## 回家旅途
|
||||||
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
|
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
|
||||||
|
70
eslint.config.mjs
Normal file
70
eslint.config.mjs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||||
|
import _import from "eslint-plugin-import";
|
||||||
|
import { fixupPluginRules } from "@eslint/compat";
|
||||||
|
import globals from "globals";
|
||||||
|
import tsParser from "@typescript-eslint/parser";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import js from "@eslint/js";
|
||||||
|
import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
|
||||||
|
const filename = fileURLToPath(import.meta.url);
|
||||||
|
const dirname = path.dirname(filename);
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: dirname,
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
allConfig: js.configs.all
|
||||||
|
});
|
||||||
|
|
||||||
|
export default [{
|
||||||
|
ignores: ["src/core/proto/"],
|
||||||
|
}, ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"), {
|
||||||
|
plugins: {
|
||||||
|
"@typescript-eslint": typescriptEslint,
|
||||||
|
import: fixupPluginRules(_import),
|
||||||
|
},
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node,
|
||||||
|
},
|
||||||
|
|
||||||
|
parser: tsParser,
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
|
||||||
|
settings: {
|
||||||
|
"import/parsers": {
|
||||||
|
"@typescript-eslint/parser": [".ts"],
|
||||||
|
},
|
||||||
|
|
||||||
|
"import/resolver": {
|
||||||
|
typescript: {
|
||||||
|
alwaysTryTypes: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
indent: ["error", 4],
|
||||||
|
semi: ["error", "always"],
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"no-async-promise-executor": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
files: ["**/.eslintrc.{js,cjs}"],
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
},
|
||||||
|
ecmaVersion: 5,
|
||||||
|
sourceType: "commonjs",
|
||||||
|
},
|
||||||
|
}];
|
BIN
external/LiteLoaderWrapper.zip
vendored
BIN
external/LiteLoaderWrapper.zip
vendored
Binary file not shown.
BIN
external/packet/napcat.packet.arm64
vendored
BIN
external/packet/napcat.packet.arm64
vendored
Binary file not shown.
BIN
external/packet/napcat.packet.exe
vendored
BIN
external/packet/napcat.packet.exe
vendored
Binary file not shown.
BIN
external/packet/napcat.packet.linux
vendored
BIN
external/packet/napcat.packet.linux
vendored
Binary file not shown.
102
external/packet/napcat.packet.production.py
vendored
102
external/packet/napcat.packet.production.py
vendored
File diff suppressed because one or more lines are too long
32
launcher/launcher-user.bat
Normal file
32
launcher/launcher-user.bat
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
|
||||||
|
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
|
||||||
|
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
|
||||||
|
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
|
||||||
|
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set RetString=%%b
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("%RetString%") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
SET QQPath=%pathWithoutUninstall%QQ.exe
|
||||||
|
|
||||||
|
if not exist "%QQpath%" (
|
||||||
|
echo provided QQ path is invalid: %QQpath%
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
||||||
|
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
|
||||||
|
|
||||||
|
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1
|
||||||
|
|
||||||
|
pause
|
33
launcher/launcher-win10-user.bat
Normal file
33
launcher/launcher-win10-user.bat
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
|
||||||
|
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
|
||||||
|
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
|
||||||
|
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
|
||||||
|
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
|
||||||
|
:loop_read
|
||||||
|
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
|
||||||
|
set RetString=%%b
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("%RetString%") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
SET QQPath=%pathWithoutUninstall%QQ.exe
|
||||||
|
|
||||||
|
if not exist "%QQpath%" (
|
||||||
|
echo provided QQ path is invalid: %QQpath%
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
||||||
|
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
|
||||||
|
|
||||||
|
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1
|
||||||
|
|
||||||
|
REM "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" 123456
|
||||||
|
|
||||||
|
pause
|
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "qq-chat",
|
"name": "qq-chat",
|
||||||
"version": "9.9.16-28788",
|
"version": "9.9.16-29456",
|
||||||
"verHash": "73b0c8f6",
|
"verHash": "dd395162",
|
||||||
"linuxVersion": "3.2.13-28788",
|
"linuxVersion": "3.2.13-29456",
|
||||||
"linuxVerHash": "55fb6434",
|
"linuxVerHash": "e379390a",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "QQ",
|
"description": "QQ",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"qd": "externals/devtools/cli/index.js"
|
"qd": "externals/devtools/cli/index.js"
|
||||||
},
|
},
|
||||||
"main": "./loadNapCat.js",
|
"main": "./loadNapCat.js",
|
||||||
"buildVersion": "28788",
|
"buildVersion": "29456",
|
||||||
"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": "3.6.0",
|
"version": "3.6.16",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
14
package.json
14
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "3.6.0",
|
"version": "3.6.16",
|
||||||
"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",
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-typescript": "^7.24.7",
|
"@babel/preset-typescript": "^7.24.7",
|
||||||
|
"@eslint/compat": "^1.2.2",
|
||||||
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
|
"@eslint/js": "^9.14.0",
|
||||||
"@log4js-node/log4js-api": "^1.0.2",
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
"@napneko/nap-proto-core": "^0.0.2",
|
"@napneko/nap-proto-core": "^0.0.2",
|
||||||
"@protobuf-ts/runtime": "^2.9.4",
|
"@protobuf-ts/runtime": "^2.9.4",
|
||||||
@@ -27,25 +30,26 @@
|
|||||||
"@typescript-eslint/parser": "^8.3.0",
|
"@typescript-eslint/parser": "^8.3.0",
|
||||||
"ajv": "^8.13.0",
|
"ajv": "^8.13.0",
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
"chalk": "^5.3.0",
|
|
||||||
"commander": "^12.1.0",
|
"commander": "^12.1.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.14.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",
|
||||||
"fast-xml-parser": "^4.3.6",
|
"fast-xml-parser": "^4.3.6",
|
||||||
"file-type": "^19.0.0",
|
"file-type": "^19.0.0",
|
||||||
|
"globals": "^15.12.0",
|
||||||
"image-size": "^1.1.1",
|
"image-size": "^1.1.1",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
|
"typescript-eslint": "^8.13.0",
|
||||||
"vite": "^5.2.6",
|
"vite": "^5.2.6",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-tsconfig-paths": "^5.1.0"
|
"vite-tsconfig-paths": "^5.1.0",
|
||||||
|
"winston": "^3.17.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"log4js": "^6.9.1",
|
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"silk-wasm": "^3.6.1",
|
"silk-wasm": "^3.6.1",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
|
@@ -215,7 +215,7 @@ export async function checkUriType(Uri: string) {
|
|||||||
}
|
}
|
||||||
if (uri.startsWith('file://')) {
|
if (uri.startsWith('file://')) {
|
||||||
let filePath: string;
|
let filePath: string;
|
||||||
const pathname = decodeURIComponent(new URL(uri).pathname);
|
const pathname = decodeURIComponent(new URL(uri).pathname + new URL(uri).hash);
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
filePath = pathname.slice(1);
|
filePath = pathname.slice(1);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import log4js, { Configuration } from 'log4js';
|
import winston, { format, transports } from 'winston';
|
||||||
import { truncateString } from '@/common/helper';
|
import { truncateString } from '@/common/helper';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import chalk from 'chalk';
|
import fs from 'node:fs';
|
||||||
import { AtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/core';
|
import { AtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/core';
|
||||||
|
|
||||||
export enum LogLevel {
|
export enum LogLevel {
|
||||||
@@ -27,97 +27,132 @@ function getFormattedTimestamp() {
|
|||||||
export class LogWrapper {
|
export class LogWrapper {
|
||||||
fileLogEnabled = true;
|
fileLogEnabled = true;
|
||||||
consoleLogEnabled = true;
|
consoleLogEnabled = true;
|
||||||
logConfig: Configuration;
|
logger: winston.Logger;
|
||||||
loggerConsole: log4js.Logger;
|
|
||||||
loggerFile: log4js.Logger;
|
|
||||||
loggerDefault: log4js.Logger;
|
|
||||||
// eslint-disable-next-line no-control-regex
|
|
||||||
colorEscape = /\x1B[@-_][0-?]*[ -/]*[@-~]/g;
|
|
||||||
|
|
||||||
constructor(logDir: string) {
|
constructor(logDir: string) {
|
||||||
const filename = `${getFormattedTimestamp()}.log`;
|
const filename = `${getFormattedTimestamp()}.log`;
|
||||||
const logPath = path.join(logDir, filename);
|
const logPath = path.join(logDir, filename);
|
||||||
this.logConfig = {
|
|
||||||
appenders: {
|
this.logger = winston.createLogger({
|
||||||
FileAppender: { // 输出到文件的appender
|
level: 'debug',
|
||||||
type: 'file',
|
format: format.combine(
|
||||||
filename: logPath, // 指定日志文件的位置和文件名
|
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||||
maxLogSize: 10485760, // 日志文件的最大大小(单位:字节),这里设置为10MB
|
format.printf(({ timestamp, level, message, ...meta }) => {
|
||||||
layout: {
|
const userInfo = meta.userInfo ? `${meta.userInfo} | ` : '';
|
||||||
type: 'pattern',
|
return `${timestamp} [${level}] ${userInfo}${message}`;
|
||||||
pattern: '%d{yyyy-MM-dd hh:mm:ss} [%p] %X{userInfo} | %m',
|
})
|
||||||
},
|
),
|
||||||
},
|
transports: [
|
||||||
ConsoleAppender: { // 输出到控制台的appender
|
new transports.File({
|
||||||
type: 'console',
|
filename: logPath,
|
||||||
layout: {
|
level: 'debug',
|
||||||
type: 'pattern',
|
maxsize: 5 * 1024 * 1024, // 5MB
|
||||||
pattern: `%d{yyyy-MM-dd hh:mm:ss} [%[%p%]] ${chalk.magenta('%X{userInfo}')} | %m`,
|
maxFiles: 5
|
||||||
},
|
}),
|
||||||
},
|
new transports.Console({
|
||||||
},
|
format: format.combine(
|
||||||
categories: {
|
format.colorize(),
|
||||||
default: { appenders: ['FileAppender', 'ConsoleAppender'], level: 'debug' }, // 默认情况下同时输出到文件和控制台
|
format.printf(({ timestamp, level, message, ...meta }) => {
|
||||||
file: { appenders: ['FileAppender'], level: 'debug' },
|
const userInfo = meta.userInfo ? `${meta.userInfo} | ` : '';
|
||||||
console: { appenders: ['ConsoleAppender'], level: 'debug' },
|
return `${timestamp} [${level}] ${userInfo}${message}`;
|
||||||
},
|
})
|
||||||
};
|
)
|
||||||
log4js.configure(this.logConfig);
|
})
|
||||||
this.loggerConsole = log4js.getLogger('console');
|
]
|
||||||
this.loggerFile = log4js.getLogger('file');
|
});
|
||||||
this.loggerDefault = log4js.getLogger('default');
|
|
||||||
this.setLogSelfInfo({ nick: '', uin: '', uid: '' });
|
this.setLogSelfInfo({ nick: '', uin: '', uid: '' });
|
||||||
|
this.cleanOldLogs(logDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanOldLogs(logDir: string) {
|
||||||
|
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
||||||
|
fs.readdir(logDir, (err, files) => {
|
||||||
|
if (err) {
|
||||||
|
this.logger.error('Failed to read log directory', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
files.forEach(file => {
|
||||||
|
const filePath = path.join(logDir, file);
|
||||||
|
fs.stat(filePath, (err, stats) => {
|
||||||
|
if (err) {
|
||||||
|
this.logger.error('Failed to get file stats', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stats.mtime.getTime() < oneWeekAgo) {
|
||||||
|
fs.unlink(filePath, err => {
|
||||||
|
if (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
this.logger.warn(`File already deleted: ${file}`);
|
||||||
|
} else {
|
||||||
|
this.logger.error('Failed to delete old log file', err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.logger.info(`Deleted old log file: ${file}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileAndConsoleLogLevel(fileLogLevel: LogLevel, consoleLogLevel: LogLevel) {
|
setFileAndConsoleLogLevel(fileLogLevel: LogLevel, consoleLogLevel: LogLevel) {
|
||||||
this.logConfig.categories.file.level = fileLogLevel;
|
this.logger.transports.forEach((transport) => {
|
||||||
this.logConfig.categories.console.level = consoleLogLevel;
|
if (transport instanceof transports.File) {
|
||||||
log4js.configure(this.logConfig);
|
transport.level = fileLogLevel;
|
||||||
|
} else if (transport instanceof transports.Console) {
|
||||||
|
transport.level = consoleLogLevel;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setLogSelfInfo(selfInfo: { nick: string, uin: string, uid: string }) {
|
setLogSelfInfo(selfInfo: { nick: string, uin: string, uid: string }) {
|
||||||
const userInfo = `${selfInfo.nick}(${selfInfo.uin})`;
|
const userInfo = `${selfInfo.nick}(${selfInfo.uin})`;
|
||||||
this.loggerConsole.addContext('userInfo', userInfo);
|
this.logger.defaultMeta = { userInfo };
|
||||||
this.loggerFile.addContext('userInfo', userInfo);
|
|
||||||
this.loggerDefault.addContext('userInfo', userInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileLogEnabled(isEnabled: boolean) {
|
setFileLogEnabled(isEnabled: boolean) {
|
||||||
this.fileLogEnabled = isEnabled;
|
this.fileLogEnabled = isEnabled;
|
||||||
|
this.logger.transports.forEach((transport) => {
|
||||||
|
if (transport instanceof transports.File) {
|
||||||
|
transport.silent = !isEnabled;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setConsoleLogEnabled(isEnabled: boolean) {
|
setConsoleLogEnabled(isEnabled: boolean) {
|
||||||
this.consoleLogEnabled = isEnabled;
|
this.consoleLogEnabled = isEnabled;
|
||||||
|
this.logger.transports.forEach((transport) => {
|
||||||
|
if (transport instanceof transports.Console) {
|
||||||
|
transport.silent = !isEnabled;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
formatMsg(msg: any[]) {
|
formatMsg(msg: any[]) {
|
||||||
let logMsg = '';
|
return msg.map(msgItem => {
|
||||||
for (const msgItem of msg) {
|
if (msgItem instanceof Error) {
|
||||||
if (msgItem instanceof Error) { // 判断是否是错误
|
return msgItem.stack;
|
||||||
logMsg += msgItem.stack + ' ';
|
} else if (typeof msgItem === 'object') {
|
||||||
continue;
|
return JSON.stringify(truncateString(JSON.parse(JSON.stringify(msgItem, null, 2))));
|
||||||
} else if (typeof msgItem === 'object') { // 判断是否是对象
|
|
||||||
const obj = JSON.parse(JSON.stringify(msgItem, null, 2));
|
|
||||||
logMsg += JSON.stringify(truncateString(obj)) + ' ';
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
logMsg += msgItem + ' ';
|
return msgItem;
|
||||||
}
|
}).join(' ');
|
||||||
return logMsg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_log(level: LogLevel, ...args: any[]) {
|
_log(level: LogLevel, ...args: any[]) {
|
||||||
if (this.consoleLogEnabled) {
|
const message = this.formatMsg(args);
|
||||||
this.loggerConsole[level](this.formatMsg(args));
|
if (this.consoleLogEnabled && this.fileLogEnabled) {
|
||||||
}
|
this.logger.log(level, message);
|
||||||
if (this.fileLogEnabled) {
|
} else if (this.consoleLogEnabled) {
|
||||||
this.loggerFile[level](this.formatMsg(args).replace(this.colorEscape, ''));
|
this.logger.log(level, message);
|
||||||
|
} else if (this.fileLogEnabled) {
|
||||||
|
this.logger.log(level, message.replace(/\x1B[@-_][0-?]*[ -/]*[@-~]/g, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log(...args: any[]) {
|
log(...args: any[]) {
|
||||||
// info 等级
|
|
||||||
this._log(LogLevel.INFO, ...args);
|
this._log(LogLevel.INFO, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,12 +175,11 @@ 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;
|
||||||
|
|
||||||
// Intercept grey tip
|
|
||||||
if (msg.elements[0]?.elementType === ElementType.GreyTip) {
|
if (msg.elements[0]?.elementType === ElementType.GreyTip) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log(`${isSelfSent ? '发送 ->' : '接收 <-' } ${rawMessageToText(msg)}`);
|
this.log(`${isSelfSent ? '发送 ->' : '接收 <-'} ${rawMessageToText(msg)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,12 +201,10 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
|||||||
}
|
}
|
||||||
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
|
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
|
||||||
tokens.push('移动设备');
|
tokens.push('移动设备');
|
||||||
} else /* temp */ {
|
} else {
|
||||||
tokens.push(`临时消息 (${msg.peerUin})`);
|
tokens.push(`临时消息 (${msg.peerUin})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// message content
|
|
||||||
|
|
||||||
function msgElementToText(element: MessageElement) {
|
function msgElementToText(element: MessageElement) {
|
||||||
if (element.textElement) {
|
if (element.textElement) {
|
||||||
if (element.textElement.atType === AtType.notAt) {
|
if (element.textElement.atType === AtType.notAt) {
|
||||||
@@ -190,11 +222,11 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
|||||||
record => element.replyElement!.sourceMsgIdInRecords === record.msgId,
|
record => element.replyElement!.sourceMsgIdInRecords === record.msgId,
|
||||||
);
|
);
|
||||||
return `[回复消息 ${recordMsgOrNull &&
|
return `[回复消息 ${recordMsgOrNull &&
|
||||||
recordMsgOrNull.peerUin != '284840486' && recordMsgOrNull.peerUin != '1094950020'// 非转发消息; 否则定位不到
|
recordMsgOrNull.peerUin != '284840486' && recordMsgOrNull.peerUin != '1094950020'
|
||||||
?
|
?
|
||||||
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
|
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
|
||||||
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
|
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
|
||||||
}]`;
|
}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.picElement) {
|
if (element.picElement) {
|
||||||
@@ -245,4 +277,4 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return tokens.join(' ');
|
return tokens.join(' ');
|
||||||
}
|
}
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '3.6.0';
|
export const napCatVersion = '3.6.16';
|
||||||
|
@@ -175,14 +175,18 @@ export class NTQQFileApi {
|
|||||||
const thumbPath = pathLib.join(thumb, thumbFileName);
|
const thumbPath = pathLib.join(thumb, thumbFileName);
|
||||||
ffmpeg(filePath)
|
ffmpeg(filePath)
|
||||||
.on('error', (err) => {
|
.on('error', (err) => {
|
||||||
logger.logDebug('获取视频封面失败,使用默认封面', err);
|
try {
|
||||||
if (diyThumbPath) {
|
logger.logDebug('获取视频封面失败,使用默认封面', err);
|
||||||
fsPromises.copyFile(diyThumbPath, thumbPath).then(() => {
|
if (diyThumbPath) {
|
||||||
|
fsPromises.copyFile(diyThumbPath, thumbPath).then(() => {
|
||||||
|
resolve(thumbPath);
|
||||||
|
}).catch(reject);
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
|
||||||
resolve(thumbPath);
|
resolve(thumbPath);
|
||||||
}).catch(reject);
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
fs.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
|
logger.logError.bind(logger)('获取视频封面失败,使用默认封面失败', error);
|
||||||
resolve(thumbPath);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.screenshots({
|
.screenshots({
|
||||||
|
@@ -375,8 +375,10 @@ export class NTQQGroupApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGroupMembersV2(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
async getGroupMembersV2(groupQQ: string, num = 3000, no_cache: boolean = false): Promise<Map<string, GroupMember>> {
|
||||||
//console.log('getGroupMembers -->', groupQQ);
|
if (no_cache) {
|
||||||
|
return (await this.getGroupMemberAll(groupQQ, true)).result.infos;
|
||||||
|
}
|
||||||
let res = await this.GetGroupMembersV3(groupQQ, num);
|
let res = await this.GetGroupMembersV3(groupQQ, num);
|
||||||
let ret = res.infos;
|
let ret = res.infos;
|
||||||
if (res.infos.size === 0 && !res.listenerMode) {
|
if (res.infos.size === 0 && !res.listenerMode) {
|
||||||
@@ -386,14 +388,13 @@ export class NTQQGroupApi {
|
|||||||
if (res.infos.size === 0) {
|
if (res.infos.size === 0) {
|
||||||
ret = (await this.getGroupMemberAll(groupQQ)).result.infos;
|
ret = (await this.getGroupMemberAll(groupQQ)).result.infos;
|
||||||
}
|
}
|
||||||
//console.log("<---------------")
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
|
||||||
const groupService = this.context.session.getGroupService();
|
const groupService = this.context.session.getGroupService();
|
||||||
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
|
||||||
const result = await groupService.getNextMemberList(sceneId!, undefined, num);
|
const result = await groupService.getNextMemberList(sceneId, undefined, num);
|
||||||
if (result.errCode !== 0) {
|
if (result.errCode !== 0) {
|
||||||
throw new Error('获取群成员列表出错,' + result.errMsg);
|
throw new Error('获取群成员列表出错,' + result.errMsg);
|
||||||
}
|
}
|
||||||
@@ -401,8 +402,8 @@ export class NTQQGroupApi {
|
|||||||
return result.result.infos;
|
return result.result.infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGroupFileCount(Gids: Array<string>) {
|
async getGroupFileCount(group_ids: Array<string>) {
|
||||||
return this.context.session.getRichMediaService().batchGetGroupFileCount(Gids);
|
return this.context.session.getRichMediaService().batchGetGroupFileCount(group_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getArkJsonGroupShare(GroupCode: string) {
|
async getArkJsonGroupShare(GroupCode: string) {
|
||||||
|
@@ -25,6 +25,7 @@ import { OidbSvcTrpcTcp0X929B_0Resp, OidbSvcTrpcTcp0X929D_0Resp } from "@/core/p
|
|||||||
import { IndexNode, MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
import { IndexNode, MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||||
import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
||||||
import { RecvPacketData } from "@/core/packet/client/client";
|
import { RecvPacketData } from "@/core/packet/client/client";
|
||||||
|
import { napCatVersion } from "@/common/version";
|
||||||
|
|
||||||
|
|
||||||
interface OffsetType {
|
interface OffsetType {
|
||||||
@@ -61,11 +62,12 @@ export class NTQQPacketApi {
|
|||||||
this.qqVersion = qqversion;
|
this.qqVersion = qqversion;
|
||||||
const table = typedOffset[qqversion + '-' + os.arch()];
|
const table = typedOffset[qqversion + '-' + os.arch()];
|
||||||
if (!table) {
|
if (!table) {
|
||||||
this.logger.logError('[Core] [Packet] PacketServer Offset table not found for QQVersion: ', qqversion + '-' + os.arch());
|
this.logger.logError(`[Core] [Packet] PacketBackend 不支持当前QQ版本架构:${qqversion}-${os.arch()},
|
||||||
|
请参照 https://github.com/NapNeko/NapCatQQ/releases/tag/v${napCatVersion} 配置正确的QQ版本!`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.core.configLoader.configData.packetBackend === 'disable') {
|
if (this.core.configLoader.configData.packetBackend === 'disable') {
|
||||||
this.logger.logWarn('[Core] [Packet] 已禁用Packet后端,NapCat.Packet将不会加载!');
|
this.logger.logWarn('[Core] [Packet] 已禁用PacketBackend,NapCat.Packet将不会加载!');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.packetSession = new PacketSession(this.core);
|
this.packetSession = new PacketSession(this.core);
|
||||||
|
@@ -283,7 +283,7 @@ export class NTQQWebApi {
|
|||||||
this.context.logger.logError.bind(this.context.logger)('获取群聊之火失败');
|
this.context.logger.logError.bind(this.context.logger)('获取群聊之火失败');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
|
if (getType === WebHonorType.LEGEND || getType === WebHonorType.ALL) {
|
||||||
const RetInternal = await getDataInternal(groupCode, 3);
|
const RetInternal = await getDataInternal(groupCode, 3);
|
||||||
if (RetInternal) {
|
if (RetInternal) {
|
||||||
HonorInfo.legend_list = [];
|
HonorInfo.legend_list = [];
|
||||||
|
14
src/core/external/appid.json
vendored
14
src/core/external/appid.json
vendored
@@ -74,5 +74,17 @@
|
|||||||
"6.9.59-29271": {
|
"6.9.59-29271": {
|
||||||
"appid": 537249863,
|
"appid": 537249863,
|
||||||
"qua": "V1_MAC_NQ_6.9.59_29271_GW_B"
|
"qua": "V1_MAC_NQ_6.9.59_29271_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.16-29456": {
|
||||||
|
"appid": 537249875,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.16_29456_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.13-29456": {
|
||||||
|
"appid": 537249996,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.13_29456_GW_B"
|
||||||
|
},
|
||||||
|
"6.9.59-29456": {
|
||||||
|
"appid": 537249961,
|
||||||
|
"qua": "V1_MAC_NQ_6.9.59_29456_GW_B"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
src/core/external/offset.json
vendored
40
src/core/external/offset.json
vendored
@@ -1,8 +1,4 @@
|
|||||||
{
|
{
|
||||||
"6.9.56-28418-arm64": {
|
|
||||||
"send": "4471360",
|
|
||||||
"recv": "4473BCC"
|
|
||||||
},
|
|
||||||
"3.2.12-28418-x64": {
|
"3.2.12-28418-x64": {
|
||||||
"recv": "A0723E0",
|
"recv": "A0723E0",
|
||||||
"send": "A06EAE0"
|
"send": "A06EAE0"
|
||||||
@@ -11,6 +7,14 @@
|
|||||||
"recv": "37A9004",
|
"recv": "37A9004",
|
||||||
"send": "37A4BD0"
|
"send": "37A4BD0"
|
||||||
},
|
},
|
||||||
|
"6.9.56-28418-x64": {
|
||||||
|
"send": "4471360",
|
||||||
|
"recv": "4473BCC"
|
||||||
|
},
|
||||||
|
"6.9.56-28418-arm64": {
|
||||||
|
"send": "3FBDBF8",
|
||||||
|
"recv": "3FC0410"
|
||||||
|
},
|
||||||
"9.9.15-28498-x64": {
|
"9.9.15-28498-x64": {
|
||||||
"recv": "37A9004",
|
"recv": "37A9004",
|
||||||
"send": "37A4BD0"
|
"send": "37A4BD0"
|
||||||
@@ -39,10 +43,14 @@
|
|||||||
"send": "6E91318",
|
"send": "6E91318",
|
||||||
"recv": "6E94B50"
|
"recv": "6E94B50"
|
||||||
},
|
},
|
||||||
"6.9.58-28971-arm64": {
|
"6.9.58-28971-x64": {
|
||||||
"send": "449ACA0",
|
"send": "449ACA0",
|
||||||
"recv": "449D50C"
|
"recv": "449D50C"
|
||||||
},
|
},
|
||||||
|
"6.9.58-28971-arm64": {
|
||||||
|
"send": "3FE0DB0",
|
||||||
|
"recv": "3FE35C8"
|
||||||
|
},
|
||||||
"9.9.16-29271-x64": {
|
"9.9.16-29271-x64": {
|
||||||
"send": "3833510",
|
"send": "3833510",
|
||||||
"recv": "3837944"
|
"recv": "3837944"
|
||||||
@@ -54,5 +62,25 @@
|
|||||||
"3.2.13-29271-arm64": {
|
"3.2.13-29271-arm64": {
|
||||||
"send": "6ECA098",
|
"send": "6ECA098",
|
||||||
"recv": "6ECD8D0"
|
"recv": "6ECD8D0"
|
||||||
|
},
|
||||||
|
"9.9.16-29456-x64": {
|
||||||
|
"send": "3835CD0",
|
||||||
|
"recv": "383A104"
|
||||||
|
},
|
||||||
|
"3.2.13-29456-x64": {
|
||||||
|
"send": "A11E820",
|
||||||
|
"recv": "A122120"
|
||||||
|
},
|
||||||
|
"3.2.13-29456-arm64": {
|
||||||
|
"send": "6ECA130",
|
||||||
|
"recv": "6ECD968"
|
||||||
|
},
|
||||||
|
"6.9.59-29456-x64": {
|
||||||
|
"send": "44C57A0",
|
||||||
|
"recv": "44C800C"
|
||||||
|
},
|
||||||
|
"6.9.59-29456-arm64": {
|
||||||
|
"send": "4005FE8",
|
||||||
|
"recv": "4008800"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChatType, RawMessage } from '@/core/entities';
|
import { ChatType, KickedOffLineInfo, RawMessage } from '@/core/entities';
|
||||||
import { CommonFileInfo } from '@/core';
|
import { CommonFileInfo } from '@/core';
|
||||||
|
|
||||||
export interface OnRichMediaDownloadCompleteParams {
|
export interface OnRichMediaDownloadCompleteParams {
|
||||||
@@ -212,7 +212,7 @@ export class NodeIKernelMsgListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onKickedOffLine(kickedInfo: unknown) {
|
onKickedOffLine(kickedInfo: KickedOffLineInfo) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -45,7 +45,7 @@ export class PacketSession {
|
|||||||
client = this.judgeClient(core);
|
client = this.judgeClient(core);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.logger.logError(`[Core] [Packet] 未知的Packet后端类型 ${prefer},请检查配置文件!`);
|
this.logger.logError(`[Core] [Packet] 未知的PacketBackend ${prefer},请检查配置文件!`);
|
||||||
client = null;
|
client = null;
|
||||||
}
|
}
|
||||||
if (!(client && client.check(core))) {
|
if (!(client && client.check(core))) {
|
||||||
|
@@ -23,6 +23,14 @@ export async function NCoreInitFramework(
|
|||||||
) {
|
) {
|
||||||
//在进入本层前是否登录未进行判断
|
//在进入本层前是否登录未进行判断
|
||||||
console.log('NapCat Framework App Loading...');
|
console.log('NapCat Framework App Loading...');
|
||||||
|
|
||||||
|
process.on('uncaughtException', (err) => {
|
||||||
|
console.log('[NapCat] [Error] Unhandled Exception:', err.message);
|
||||||
|
});
|
||||||
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
|
console.log('[NapCat] [Error] unhandledRejection:', reason);
|
||||||
|
});
|
||||||
|
|
||||||
const pathWrapper = new NapCatPathWrapper();
|
const pathWrapper = new NapCatPathWrapper();
|
||||||
const logger = new LogWrapper(pathWrapper.logsPath);
|
const logger = new LogWrapper(pathWrapper.logsPath);
|
||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
|
BIN
src/native/packet/MoeHoo.darwin.arm64.node
Normal file
BIN
src/native/packet/MoeHoo.darwin.arm64.node
Normal file
Binary file not shown.
Binary file not shown.
@@ -5,7 +5,7 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
|||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
group_id: { type: 'string' },
|
group_id: { type: ['string', 'number'] },
|
||||||
},
|
},
|
||||||
required: ['group_id'],
|
required: ['group_id'],
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@@ -17,6 +17,9 @@ export class SetGroupSign extends BaseAction<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
return await this.core.apis.PacketApi.sendGroupSignPacket(payload.group_id);
|
return await this.core.apis.PacketApi.sendGroupSignPacket(payload.group_id.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export class SendGroupSign extends SetGroupSign {
|
||||||
|
actionName = ActionName.SendGroupSign;
|
||||||
|
}
|
@@ -42,7 +42,7 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
|
|||||||
deleteAfterSentFiles: []
|
deleteAfterSentFiles: []
|
||||||
};
|
};
|
||||||
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id);
|
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id);
|
||||||
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], [], true);
|
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], msgContext.deleteAfterSentFiles, true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -53,7 +53,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
|
|||||||
deleteAfterSentFiles: []
|
deleteAfterSentFiles: []
|
||||||
};
|
};
|
||||||
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
|
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
|
||||||
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], [], true);
|
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], msgContext.deleteAfterSentFiles, true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,6 @@ import { ActionName } from '../types';
|
|||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
|
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
|
||||||
import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
|
import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
|
||||||
import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto";
|
|
||||||
import { IndexNode } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@@ -24,12 +24,11 @@ export class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
|
|||||||
const noCache = payload.no_cache ? this.stringToBoolean(payload.no_cache) : false;
|
const noCache = payload.no_cache ? this.stringToBoolean(payload.no_cache) : false;
|
||||||
const memberCache = this.core.apis.GroupApi.groupMemberCache;
|
const memberCache = this.core.apis.GroupApi.groupMemberCache;
|
||||||
let groupMembers;
|
let groupMembers;
|
||||||
if (noCache) {
|
try {
|
||||||
groupMembers = await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr);
|
groupMembers = await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr, 3000, noCache);
|
||||||
} else {
|
} catch (error) {
|
||||||
groupMembers = memberCache.get(groupIdStr) ?? await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr);
|
groupMembers = memberCache.get(groupIdStr) ?? await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
const memberPromises = Array.from(groupMembers.values()).map(item =>
|
const memberPromises = Array.from(groupMembers.values()).map(item =>
|
||||||
OB11Entities.groupMember(groupIdStr, item)
|
OB11Entities.groupMember(groupIdStr, item)
|
||||||
);
|
);
|
||||||
|
@@ -92,7 +92,7 @@ import { GetGroupFileUrl } from "@/onebot/action/file/GetGroupFileUrl";
|
|||||||
import { GetPacketStatus } from "@/onebot/action/packet/GetPacketStatus";
|
import { GetPacketStatus } from "@/onebot/action/packet/GetPacketStatus";
|
||||||
import { FriendPoke } from "@/onebot/action/user/FriendPoke";
|
import { FriendPoke } from "@/onebot/action/user/FriendPoke";
|
||||||
import { GetCredentials } from './system/GetCredentials';
|
import { GetCredentials } from './system/GetCredentials';
|
||||||
import { SetGroupSign } from './extends/SetGroupSign';
|
import { SendGroupSign, SetGroupSign } from './extends/SetGroupSign';
|
||||||
import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain';
|
import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain';
|
||||||
import { GoCQHTTPCheckUrlSafely } from './go-cqhttp/GoCQHTTPCheckUrlSafely';
|
import { GoCQHTTPCheckUrlSafely } from './go-cqhttp/GoCQHTTPCheckUrlSafely';
|
||||||
import { GoCQHTTPGetModelShow } from './go-cqhttp/GoCQHTTPGetModelShow';
|
import { GoCQHTTPGetModelShow } from './go-cqhttp/GoCQHTTPGetModelShow';
|
||||||
@@ -125,6 +125,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
|||||||
new TranslateEnWordToZn(obContext, core),
|
new TranslateEnWordToZn(obContext, core),
|
||||||
new GetGroupRootFiles(obContext, core),
|
new GetGroupRootFiles(obContext, core),
|
||||||
new SetGroupSign(obContext, core),
|
new SetGroupSign(obContext, core),
|
||||||
|
new SendGroupSign(obContext, core),
|
||||||
// onebot11
|
// onebot11
|
||||||
new SendLike(obContext, core),
|
new SendLike(obContext, core),
|
||||||
new GetMsg(obContext, core),
|
new GetMsg(obContext, core),
|
||||||
|
@@ -2,12 +2,14 @@ import { ChatType, Peer } from '@/core/entities';
|
|||||||
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
import BaseAction from '../BaseAction';
|
import BaseAction from '../BaseAction';
|
||||||
import { ActionName } from '../types';
|
import { ActionName } from '../types';
|
||||||
|
import { MessageUnique } from '@/common/message-unique';
|
||||||
|
|
||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
user_id: { type: ['number', 'string'] },
|
user_id: { type: ['number', 'string'] },
|
||||||
group_id: { type: ['number', 'string'] },
|
group_id: { type: ['number', 'string'] },
|
||||||
|
message_id: { type: ['number', 'string'] },
|
||||||
},
|
},
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
@@ -15,6 +17,16 @@ type PlayloadType = FromSchema<typeof SchemaData>;
|
|||||||
|
|
||||||
class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
|
class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
|
||||||
async getPeer(payload: PlayloadType): Promise<Peer> {
|
async getPeer(payload: PlayloadType): Promise<Peer> {
|
||||||
|
if (payload.message_id) {
|
||||||
|
let s_peer = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id)?.Peer;
|
||||||
|
if (s_peer) {
|
||||||
|
return s_peer;
|
||||||
|
}
|
||||||
|
let l_peer = MessageUnique.getPeerByMsgId(payload.message_id.toString())?.Peer;
|
||||||
|
if (l_peer) {
|
||||||
|
return l_peer;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (payload.user_id) {
|
if (payload.user_id) {
|
||||||
const peerUid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
const peerUid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
if (!peerUid) {
|
if (!peerUid) {
|
||||||
|
@@ -136,6 +136,8 @@ export enum ActionName {
|
|||||||
GetGroupIgnoredNotifies = 'get_group_ignored_notifies',
|
GetGroupIgnoredNotifies = 'get_group_ignored_notifies',
|
||||||
|
|
||||||
SetGroupSign = "set_group_sign",
|
SetGroupSign = "set_group_sign",
|
||||||
|
SendGroupSign = "send_group_sign",
|
||||||
|
|
||||||
GetMiniAppArk = "get_mini_app_ark",
|
GetMiniAppArk = "get_mini_app_ark",
|
||||||
// UploadForwardMsg = "upload_forward_msg",
|
// UploadForwardMsg = "upload_forward_msg",
|
||||||
GetAiRecord = "get_ai_record",
|
GetAiRecord = "get_ai_record",
|
||||||
|
@@ -190,6 +190,8 @@ export class OneBotMsgApi {
|
|||||||
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, "", "." + _.key + ".jpg"),
|
file_id: FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, "", "." + _.key + ".jpg"),
|
||||||
path: url,
|
path: url,
|
||||||
url: url,
|
url: url,
|
||||||
|
key: _.key,
|
||||||
|
emoji_id: _.emojiId,
|
||||||
file_unique: _.key
|
file_unique: _.key
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
16
src/onebot/event/notice/BotOfflineEvent.ts
Normal file
16
src/onebot/event/notice/BotOfflineEvent.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
|
||||||
|
import { NapCatCore } from '@/core';
|
||||||
|
|
||||||
|
export class BotOfflineEvent extends OB11BaseNoticeEvent {
|
||||||
|
notice_type = 'bot_offline';
|
||||||
|
user_id: number;
|
||||||
|
tag: string = 'BotOfflineEvent';
|
||||||
|
message: string = 'BotOfflineEvent';
|
||||||
|
|
||||||
|
public constructor(core: NapCatCore, tag: string, message: string) {
|
||||||
|
super(core);
|
||||||
|
this.user_id = +core.selfInfo.uin;
|
||||||
|
this.tag = tag;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
@@ -47,6 +47,7 @@ import { LRUCache } from '@/common/lru-cache';
|
|||||||
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
|
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
|
||||||
import { Native } from '@/native';
|
import { Native } from '@/native';
|
||||||
import { decodeMessage, decodeRecallGroup } from '@/core/packet/proto/old/Message';
|
import { decodeMessage, decodeRecallGroup } from '@/core/packet/proto/old/Message';
|
||||||
|
import { BotOfflineEvent } from './event/notice/BotOfflineEvent';
|
||||||
|
|
||||||
//OneBot实现类
|
//OneBot实现类
|
||||||
export class NapCatOneBot11Adapter {
|
export class NapCatOneBot11Adapter {
|
||||||
@@ -343,7 +344,11 @@ export class NapCatOneBot11Adapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
msgListener.onKickedOffLine = async (kick) => {
|
||||||
|
let event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc);
|
||||||
|
this.networkManager.emitEvent(event)
|
||||||
|
.catch(e => this.context.logger.logError.bind(this.context.logger)('处理Bot掉线失败', e));
|
||||||
|
}
|
||||||
this.context.session.getMsgService().addKernelMsgListener(
|
this.context.session.getMsgService().addKernelMsgListener(
|
||||||
proxiedListenerOf(msgListener, this.context.logger),
|
proxiedListenerOf(msgListener, this.context.logger),
|
||||||
);
|
);
|
||||||
|
@@ -36,7 +36,12 @@ const cmdOptions = program.opts();
|
|||||||
// NapCat Shell App ES 入口文件
|
// NapCat Shell App ES 入口文件
|
||||||
export async function NCoreInitShell() {
|
export async function NCoreInitShell() {
|
||||||
console.log('NapCat Shell App Loading...');
|
console.log('NapCat Shell App Loading...');
|
||||||
|
process.on('uncaughtException', (err) => {
|
||||||
|
console.log('[NapCat] [Error] Unhandled Exception:', err.message);
|
||||||
|
});
|
||||||
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
|
console.log('[NapCat] [Error] unhandledRejection:', reason);
|
||||||
|
});
|
||||||
const pathWrapper = new NapCatPathWrapper();
|
const pathWrapper = new NapCatPathWrapper();
|
||||||
const logger = new LogWrapper(pathWrapper.logsPath);
|
const logger = new LogWrapper(pathWrapper.logsPath);
|
||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
@@ -227,7 +232,7 @@ 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();
|
||||||
}
|
}
|
||||||
|
@@ -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("V3.6.0", "napcat-update-button", "secondary")
|
SettingButton("V3.6.16", "napcat-update-button", "secondary")
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -10,6 +10,20 @@
|
|||||||
<link rel="stylesheet" href="./assets/webcomponents.css" />
|
<link rel="stylesheet" href="./assets/webcomponents.css" />
|
||||||
<link rel="stylesheet" href="./assets/style.css" />
|
<link rel="stylesheet" href="./assets/style.css" />
|
||||||
<link rel="stylesheet" href="./assets/color.css" />
|
<link rel="stylesheet" href="./assets/color.css" />
|
||||||
|
<style>
|
||||||
|
body > div {
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ob-setting-select {
|
||||||
|
width: 210px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<!-- 脚手架 -->
|
<!-- 脚手架 -->
|
||||||
<!-- 渲染脚本 -->
|
<!-- 渲染脚本 -->
|
||||||
<script>
|
<script>
|
||||||
|
@@ -4,7 +4,7 @@ import { resolve } from 'path';
|
|||||||
import nodeResolve from '@rollup/plugin-node-resolve';
|
import nodeResolve from '@rollup/plugin-node-resolve';
|
||||||
import { builtinModules } from 'module';
|
import { builtinModules } from 'module';
|
||||||
//依赖排除
|
//依赖排除
|
||||||
const external = ['silk-wasm', 'ws', 'express', 'fluent-ffmpeg', 'log4js', 'qrcode-terminal'];
|
const external = ['silk-wasm', 'ws', 'express', 'fluent-ffmpeg', 'qrcode-terminal'];
|
||||||
const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat();
|
const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat();
|
||||||
function genCpModule(module: string) {
|
function genCpModule(module: string) {
|
||||||
return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false };
|
return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false };
|
||||||
|
Reference in New Issue
Block a user