mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-03-01 08:10:25 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7aedacb27f | ||
|
|
c9b45ec1a2 | ||
|
|
54cacc30e4 | ||
|
|
9b26fc99d3 | ||
|
|
204846b404 | ||
|
|
3d654791b9 | ||
|
|
63f42f1592 | ||
|
|
0ab0b939da | ||
|
|
522a123f9a | ||
|
|
ec9f8d6e12 | ||
|
|
3c750c75a9 | ||
|
|
5b2b1f499b | ||
|
|
531ffcd55d | ||
|
|
068e4d8bb5 | ||
|
|
5dc33e78ad | ||
|
|
d76a2170a0 | ||
|
|
ec2af3120c | ||
|
|
8de49a3109 | ||
|
|
dbb5a0022e | ||
|
|
cb8c8d6b57 | ||
|
|
93c140ed4e | ||
|
|
457b072f0e | ||
|
|
1869493473 | ||
|
|
f33c66ce15 | ||
|
|
e8d6f86458 | ||
|
|
a000ffdf0d | ||
|
|
202338a160 | ||
|
|
e3eb129a52 | ||
|
|
7654e9f2bb | ||
|
|
a60c03f42f | ||
|
|
60aae228a1 | ||
|
|
b1417f9b56 | ||
|
|
eeeaddbb60 | ||
|
|
b1109022bb | ||
|
|
1807789511 | ||
|
|
49a5b631c2 | ||
|
|
e7aaec81e2 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -14,3 +14,6 @@ devconfig/*
|
|||||||
*.db
|
*.db
|
||||||
checkVersion.sh
|
checkVersion.sh
|
||||||
bun.lockb
|
bun.lockb
|
||||||
|
tests/run/
|
||||||
|
guild1.db-wal
|
||||||
|
guild1.db-shm
|
||||||
|
|||||||
31
README.md
31
README.md
@@ -45,37 +45,6 @@ _Modern protocol-side framework implemented based on NTQQ._
|
|||||||
|
|
||||||
> 项目非盈利,对接问题/基础问题/下层框架问题 请自行搜索解决,本项目社区不提供此类解答。
|
> 项目非盈利,对接问题/基础问题/下层框架问题 请自行搜索解决,本项目社区不提供此类解答。
|
||||||
|
|
||||||
## Development Guide
|
|
||||||
|
|
||||||
### 代码提交前检查
|
|
||||||
|
|
||||||
在提交代码前,**必须**执行以下命令进行代码检查:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. 代码格式化修复
|
|
||||||
npm run lint:fix
|
|
||||||
|
|
||||||
# 2. TypeScript 类型检查
|
|
||||||
npm run tsc
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 关于 TypeScript 类型检查
|
|
||||||
|
|
||||||
执行 `npm run tsc` 时,会出现 22 个已知的第三方库类型错误,**这是正常现象**:
|
|
||||||
|
|
||||||
```
|
|
||||||
Found 22 errors in 3 files.
|
|
||||||
|
|
||||||
Errors Files
|
|
||||||
3 node_modules/@homebridge/node-pty-prebuilt-multiarch/src/eventEmitter2.ts:42
|
|
||||||
2 node_modules/@homebridge/node-pty-prebuilt-multiarch/src/terminal.ts:158
|
|
||||||
17 node_modules/@napneko/nap-proto-core/NapProto.ts:94
|
|
||||||
```
|
|
||||||
|
|
||||||
这些错误是由于启用了严格类型检查模式导致的第三方库内部类型问题,**不影响项目运行**。
|
|
||||||
|
|
||||||
⚠️ **注意**:除了上述 22 个已知错误外,不应该出现其他类型错误。如果有新的错误,请在提交前修复。
|
|
||||||
|
|
||||||
## Link
|
## Link
|
||||||
|
|
||||||
| Docs | [](https://napneko.github.io/) | [](https://doc.napneko.icu/) | [](https://napcat.napneko.icu/) |
|
| Docs | [](https://napneko.github.io/) | [](https://doc.napneko.icu/) | [](https://napcat.napneko.icu/) |
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "4.9.23",
|
"version": "4.9.38",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
|||||||
30
napiloader/napiLoader-debug.bat
Normal file
30
napiloader/napiLoader-debug.bat
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
set NAPCAT_INJECT_PATH=%cd%\napiloader.dll
|
||||||
|
set NAPCAT_LAUNCHER_PATH=%cd%\napimain.exe
|
||||||
|
set NAPCAT_MAIN_PATH=%cd%\nativeLoader.cjs
|
||||||
|
set NAPCAT_DEBUG_CONSOLE=1
|
||||||
|
: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
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
||||||
|
|
||||||
|
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" "%NAPCAT_MAIN_PATH%"
|
||||||
|
|
||||||
|
pause
|
||||||
27
napiloader/napiLoader.bat
Normal file
27
napiloader/napiLoader.bat
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001
|
||||||
|
set NAPCAT_INJECT_PATH=%cd%\napiloader.dll
|
||||||
|
set NAPCAT_LAUNCHER_PATH=%cd%\napimain.exe
|
||||||
|
set NAPCAT_MAIN_PATH=%cd%\nativeLoader.cjs
|
||||||
|
: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
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
|
||||||
|
|
||||||
|
start "" "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" "%NAPCAT_MAIN_PATH%"
|
||||||
BIN
napiloader/napiloader.dll
Normal file
BIN
napiloader/napiloader.dll
Normal file
Binary file not shown.
BIN
napiloader/napimain.exe
Normal file
BIN
napiloader/napimain.exe
Normal file
Binary file not shown.
11
package-lock.json
generated
11
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"version": "4.9.21",
|
"version": "4.9.25",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"version": "4.9.21",
|
"version": "4.9.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
"silk-wasm": "^3.6.1",
|
"silk-wasm": "^3.6.1",
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
"express-rate-limit": "^7.5.0",
|
"express-rate-limit": "^7.5.0",
|
||||||
"fast-xml-parser": "^4.3.6",
|
"fast-xml-parser": "^4.3.6",
|
||||||
"file-type": "^21.0.0",
|
"file-type": "^21.0.0",
|
||||||
|
"fs-extra": "^11.3.2",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"multer": "^2.0.1",
|
"multer": "^2.0.1",
|
||||||
"napcat.protobuf": "^1.1.4",
|
"napcat.protobuf": "^1.1.4",
|
||||||
@@ -5222,9 +5223,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fs-extra": {
|
"node_modules/fs-extra": {
|
||||||
"version": "11.3.0",
|
"version": "11.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz",
|
||||||
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
"integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.9.23",
|
"version": "4.9.38",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:universal": "npm run build:webui && npm run dev:universal || exit 1",
|
"build:universal": "npm run build:webui && npm run dev:universal || exit 1",
|
||||||
"build:framework": "npm run build:webui && npm run dev:framework || exit 1",
|
"build:framework": "npm run build:webui && npm run dev:framework || exit 1",
|
||||||
@@ -23,7 +23,8 @@
|
|||||||
"lint:webui": "cd napcat.webui && eslint src/**/*",
|
"lint:webui": "cd napcat.webui && eslint src/**/*",
|
||||||
"lint:fix:webui": "cd napcat.webui && eslint --fix src/**/*",
|
"lint:fix:webui": "cd napcat.webui && eslint --fix src/**/*",
|
||||||
"depend": "cd dist && npm install --omit=dev",
|
"depend": "cd dist && npm install --omit=dev",
|
||||||
"dev:depend": "npm i && cd napcat.webui && npm i"
|
"dev:depend": "npm i && cd napcat.webui && npm i",
|
||||||
|
"test:winshell": "pwsh ./tests/nodeTest.ps1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.28.0",
|
"@babel/core": "^7.28.0",
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
"express-rate-limit": "^7.5.0",
|
"express-rate-limit": "^7.5.0",
|
||||||
"fast-xml-parser": "^4.3.6",
|
"fast-xml-parser": "^4.3.6",
|
||||||
"file-type": "^21.0.0",
|
"file-type": "^21.0.0",
|
||||||
|
"fs-extra": "^11.3.2",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"multer": "^2.0.1",
|
"multer": "^2.0.1",
|
||||||
"napcat.protobuf": "^1.1.4",
|
"napcat.protobuf": "^1.1.4",
|
||||||
|
|||||||
@@ -66,13 +66,15 @@ export class FFmpegAddonAdapter implements IFFmpegAdapter {
|
|||||||
*/
|
*/
|
||||||
async getVideoInfo (videoPath: string): Promise<VideoInfoResult> {
|
async getVideoInfo (videoPath: string): Promise<VideoInfoResult> {
|
||||||
const addon = this.ensureAddon();
|
const addon = this.ensureAddon();
|
||||||
const info = await addon.getVideoInfo(videoPath, 'bmp24');
|
const info = await addon.getVideoInfo(videoPath);
|
||||||
|
|
||||||
|
let format = info.format.includes(',') ? info.format.split(',')[0] ?? info.format : info.format;
|
||||||
|
console.log('[FFmpegAddonAdapter] Detected format:', format);
|
||||||
return {
|
return {
|
||||||
width: info.width,
|
width: info.width,
|
||||||
height: info.height,
|
height: info.height,
|
||||||
duration: info.duration,
|
duration: info.duration,
|
||||||
format: info.format,
|
format: format,
|
||||||
thumbnail: info.image,
|
thumbnail: info.image,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -88,7 +90,7 @@ export class FFmpegAddonAdapter implements IFFmpegAdapter {
|
|||||||
/**
|
/**
|
||||||
* 转换为 PCM
|
* 转换为 PCM
|
||||||
*/
|
*/
|
||||||
async convertToPCM (filePath: string, pcmPath: string): Promise<{ result: boolean, sampleRate: number }> {
|
async convertToPCM (filePath: string, pcmPath: string): Promise<{ result: boolean, sampleRate: number; }> {
|
||||||
const addon = this.ensureAddon();
|
const addon = this.ensureAddon();
|
||||||
const result = await addon.decodeAudioToPCM(filePath, pcmPath, 24000);
|
const result = await addon.decodeAudioToPCM(filePath, pcmPath, 24000);
|
||||||
|
|
||||||
@@ -100,13 +102,8 @@ export class FFmpegAddonAdapter implements IFFmpegAdapter {
|
|||||||
*/
|
*/
|
||||||
async convertFile (inputFile: string, outputFile: string, format: string): Promise<void> {
|
async convertFile (inputFile: string, outputFile: string, format: string): Promise<void> {
|
||||||
const addon = this.ensureAddon();
|
const addon = this.ensureAddon();
|
||||||
|
console.log('[FFmpegAddonAdapter] Converting file:', inputFile, 'to', outputFile, 'as', format);
|
||||||
if (format === 'silk' || format === 'ntsilk') {
|
await addon.decodeAudioToFmt(inputFile, outputFile, format);
|
||||||
// 使用 Addon 的 NTSILK 转换
|
|
||||||
await addon.convertToNTSilkTct(inputFile, outputFile);
|
|
||||||
} else {
|
|
||||||
throw new Error(`Format '${format}' is not supported by FFmpeg Addon`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -49,23 +49,25 @@ export interface AudioPCMResult {
|
|||||||
* FFmpeg interface providing all audio/video processing methods
|
* FFmpeg interface providing all audio/video processing methods
|
||||||
*/
|
*/
|
||||||
export interface FFmpeg {
|
export interface FFmpeg {
|
||||||
|
convertFile (inputFile: string, outputFile: string, format: string): Promise<{ success: boolean; }>;
|
||||||
/**
|
/**
|
||||||
* Get video information including resolution, duration, format, codec and first frame thumbnail
|
* Get video information including resolution, duration, format, codec and first frame thumbnail
|
||||||
*/
|
*/
|
||||||
getVideoInfo(filePath: string, format?: 'bmp' | 'bmp24'): Promise<VideoInfo>;
|
getVideoInfo (filePath: string, format?: 'bmp' | 'bmp24'): Promise<VideoInfo>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get duration of audio or video file in seconds
|
* Get duration of audio or video file in seconds
|
||||||
*/
|
*/
|
||||||
getDuration(filePath: string): Promise<number>;
|
getDuration (filePath: string): Promise<number>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert audio file to NTSILK format (WeChat voice message format)
|
* Convert audio file to NTSILK format (WeChat voice message format)
|
||||||
*/
|
*/
|
||||||
convertToNTSilkTct(inputPath: string, outputPath: string): Promise<void>;
|
convertToNTSilkTct (inputPath: string, outputPath: string): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode audio file to raw PCM data
|
* Decode audio file to raw PCM data
|
||||||
*/
|
*/
|
||||||
decodeAudioToPCM(filePath: string, pcmPath: string, sampleRate?: number): Promise<{ result: boolean, sampleRate: number }>;
|
decodeAudioToPCM (filePath: string, pcmPath: string, sampleRate?: number): Promise<{ result: boolean, sampleRate: number; }>;
|
||||||
|
decodeAudioToFmt (filePath: string, pcmPath: string, format: string): Promise<{ channels: number; sampleRate: number; format: string; }>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ export class FFmpegService {
|
|||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getAdapterName (): string {
|
||||||
|
if (!this.adapter) {
|
||||||
|
throw new Error('FFmpeg service not initialized. Please call FFmpegService.init() first.');
|
||||||
|
}
|
||||||
|
return this.adapter.name;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 FFmpeg 适配器
|
* 获取 FFmpeg 适配器
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -119,6 +119,9 @@ export function getDefaultQQVersionConfigInfo (): QQVersionConfigType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getQQPackageInfoPath (exePath: string = '', version?: string): string {
|
export function getQQPackageInfoPath (exePath: string = '', version?: string): string {
|
||||||
|
if (process.env['NAPCAT_QQ_PACKAGE_INFO_PATH']) {
|
||||||
|
return process.env['NAPCAT_QQ_PACKAGE_INFO_PATH'];
|
||||||
|
}
|
||||||
let packagePath;
|
let packagePath;
|
||||||
if (os.platform() === 'darwin') {
|
if (os.platform() === 'darwin') {
|
||||||
packagePath = path.join(path.dirname(exePath), '..', 'Resources', 'app', 'package.json');
|
packagePath = path.join(path.dirname(exePath), '..', 'Resources', 'app', 'package.json');
|
||||||
@@ -135,6 +138,9 @@ export function getQQPackageInfoPath (exePath: string = '', version?: string): s
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getQQVersionConfigPath (exePath: string = ''): string | undefined {
|
export function getQQVersionConfigPath (exePath: string = ''): string | undefined {
|
||||||
|
if (process.env['NAPCAT_QQ_VERSION_CONFIG_PATH']) {
|
||||||
|
return process.env['NAPCAT_QQ_VERSION_CONFIG_PATH'];
|
||||||
|
}
|
||||||
let configVersionInfoPath;
|
let configVersionInfoPath;
|
||||||
if (os.platform() === 'win32') {
|
if (os.platform() === 'win32') {
|
||||||
configVersionInfoPath = path.join(path.dirname(exePath), 'versions', 'config.json');
|
configVersionInfoPath = path.join(path.dirname(exePath), 'versions', 'config.json');
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '4.9.23';
|
export const napCatVersion = '4.9.38';
|
||||||
|
|||||||
@@ -17,8 +17,14 @@ export class NTQQFriendApi {
|
|||||||
|
|
||||||
async getBuddyV2SimpleInfoMap () {
|
async getBuddyV2SimpleInfoMap () {
|
||||||
const buddyService = this.context.session.getBuddyService();
|
const buddyService = this.context.session.getBuddyService();
|
||||||
|
let uids: string[] = [];
|
||||||
|
if (this.core.context.basicInfoWrapper.requireMinNTQQBuild('41679')) {
|
||||||
|
const buddyListV2NT = await buddyService.getBuddyListV2('0', true, BuddyListReqType.KNOMAL);
|
||||||
|
uids = buddyListV2NT.data.flatMap(item => item.buddyUids);
|
||||||
|
} else {
|
||||||
const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
|
||||||
const uids = buddyListV2.data.flatMap(item => item.buddyUids);
|
uids = buddyListV2.data.flatMap(item => item.buddyUids);
|
||||||
|
}
|
||||||
return await this.core.eventWrapper.callNoListenerEvent(
|
return await this.core.eventWrapper.callNoListenerEvent(
|
||||||
'NodeIKernelProfileService/getCoreAndBaseInfo',
|
'NodeIKernelProfileService/getCoreAndBaseInfo',
|
||||||
'nodeStore',
|
'nodeStore',
|
||||||
@@ -47,10 +53,15 @@ export class NTQQFriendApi {
|
|||||||
|
|
||||||
async getBuddyV2ExWithCate () {
|
async getBuddyV2ExWithCate () {
|
||||||
const buddyService = this.context.session.getBuddyService();
|
const buddyService = this.context.session.getBuddyService();
|
||||||
const buddyListV2 = (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data;
|
let uids: string[] = [];
|
||||||
const uids = buddyListV2.flatMap(item => {
|
let buddyListV2: Awaited<ReturnType<typeof buddyService.getBuddyListV2>>['data'];
|
||||||
return item.buddyUids;
|
if (this.core.context.basicInfoWrapper.requireMinNTQQBuild('41679')) {
|
||||||
});
|
buddyListV2 = (await buddyService.getBuddyListV2('0', true, BuddyListReqType.KNOMAL)).data;
|
||||||
|
uids = buddyListV2.flatMap(item => item.buddyUids);
|
||||||
|
} else {
|
||||||
|
buddyListV2 = (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data;
|
||||||
|
uids = buddyListV2.flatMap(item => item.buddyUids);
|
||||||
|
}
|
||||||
const data = await this.core.eventWrapper.callNoListenerEvent(
|
const data = await this.core.eventWrapper.callNoListenerEvent(
|
||||||
'NodeIKernelProfileService/getCoreAndBaseInfo',
|
'NodeIKernelProfileService/getCoreAndBaseInfo',
|
||||||
'nodeStore',
|
'nodeStore',
|
||||||
|
|||||||
16
src/core/external/appid.json
vendored
16
src/core/external/appid.json
vendored
@@ -431,7 +431,7 @@
|
|||||||
"appid": 537320110,
|
"appid": 537320110,
|
||||||
"qua": "V1_WIN_NQ_9.9.23_41679_GW_B"
|
"qua": "V1_WIN_NQ_9.9.23_41679_GW_B"
|
||||||
},
|
},
|
||||||
"6.8.83-41679": {
|
"6.9.83-41679": {
|
||||||
"appid": 537320135,
|
"appid": 537320135,
|
||||||
"qua": "V1_MAC_NQ_6.9.83_41679_GW_B"
|
"qua": "V1_MAC_NQ_6.9.83_41679_GW_B"
|
||||||
},
|
},
|
||||||
@@ -439,8 +439,20 @@
|
|||||||
"appid": 537320110,
|
"appid": 537320110,
|
||||||
"qua": "V1_WIN_NQ_9.9.23_41785_GW_B"
|
"qua": "V1_WIN_NQ_9.9.23_41785_GW_B"
|
||||||
},
|
},
|
||||||
"6.8.83-41785": {
|
"6.9.83-41785": {
|
||||||
"appid": 537320135,
|
"appid": 537320135,
|
||||||
"qua": "V1_MAC_NQ_6.9.83_41785_GW_B"
|
"qua": "V1_MAC_NQ_6.9.83_41785_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.23-41857": {
|
||||||
|
"appid": 537320161,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.23_41857_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.21-41857": {
|
||||||
|
"appid": 537320197,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.21_41857_GW_B"
|
||||||
|
},
|
||||||
|
"6.9.83-41857": {
|
||||||
|
"appid": 537320186,
|
||||||
|
"qua": "V1_MAC_NQ_6.9.83_41857_GW_B"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
16
src/core/external/napi2native.json
vendored
16
src/core/external/napi2native.json
vendored
@@ -62,5 +62,21 @@
|
|||||||
"9.9.23-41785-x64": {
|
"9.9.23-41785-x64": {
|
||||||
"send": "09FF0A4",
|
"send": "09FF0A4",
|
||||||
"recv": "1D19FF9"
|
"recv": "1D19FF9"
|
||||||
|
},
|
||||||
|
"6.9.83-41857-arm64": {
|
||||||
|
"send": "0815774",
|
||||||
|
"recv": "0958B3C"
|
||||||
|
},
|
||||||
|
"3.2.21-41857-x64": {
|
||||||
|
"send": "5B44510",
|
||||||
|
"recv": "2FDB0B0"
|
||||||
|
},
|
||||||
|
"3.2.21-41857-arm64": {
|
||||||
|
"send": "3D6EE6C",
|
||||||
|
"recv": "1479EDC"
|
||||||
|
},
|
||||||
|
"9.9.23-41857-x64": {
|
||||||
|
"send": "0A01394",
|
||||||
|
"recv": "1D1C4F9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
18
src/core/external/packet.json
vendored
18
src/core/external/packet.json
vendored
@@ -571,8 +571,24 @@
|
|||||||
"send": "2C944A0",
|
"send": "2C944A0",
|
||||||
"recv": "2C97A20"
|
"recv": "2C97A20"
|
||||||
},
|
},
|
||||||
"6.8.83-41785-arm64": {
|
"6.9.83-41785-arm64": {
|
||||||
"send": "3D6DA28",
|
"send": "3D6DA28",
|
||||||
"recv": "3D70338"
|
"recv": "3D70338"
|
||||||
|
},
|
||||||
|
"6.9.83-41857-arm64": {
|
||||||
|
"send": "3D74610",
|
||||||
|
"recv": "3D76F20"
|
||||||
|
},
|
||||||
|
"3.2.21-41857-x64": {
|
||||||
|
"send": "A7B40A0",
|
||||||
|
"recv": "A7B7B20"
|
||||||
|
},
|
||||||
|
"9.9.23-41857-x64": {
|
||||||
|
"send": "2C98F00",
|
||||||
|
"recv": "2C9C480"
|
||||||
|
},
|
||||||
|
"3.2.21-41857-arm64": {
|
||||||
|
"send": "6B159F8",
|
||||||
|
"recv": "6B19388"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,12 @@ export enum NapCatCoreWorkingEnv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function loadQQWrapper (QQVersion: string): WrapperNodeApi {
|
export function loadQQWrapper (QQVersion: string): WrapperNodeApi {
|
||||||
|
if (process.env['NAPCAT_WRAPPER_PATH']) {
|
||||||
|
const wrapperPath = process.env['NAPCAT_WRAPPER_PATH'];
|
||||||
|
const nativemodule: { exports: WrapperNodeApi; } = { exports: {} as WrapperNodeApi };
|
||||||
|
process.dlopen(nativemodule, wrapperPath);
|
||||||
|
return nativemodule.exports;
|
||||||
|
}
|
||||||
let appPath;
|
let appPath;
|
||||||
if (os.platform() === 'darwin') {
|
if (os.platform() === 'darwin') {
|
||||||
appPath = path.resolve(path.dirname(process.execPath), '../Resources/app');
|
appPath = path.resolve(path.dirname(process.execPath), '../Resources/app');
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export interface NativePacketExportType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type PacketType = 0 | 1; // 0: send, 1: recv
|
export type PacketType = 0 | 1; // 0: send, 1: recv
|
||||||
export type PacketCallback = (data: { type: PacketType, uin: string, cmd: string, seq: number, hex_data: string }) => void;
|
export type PacketCallback = (data: { type: PacketType, uin: string, cmd: string, seq: number, hex_data: string; }) => void;
|
||||||
|
|
||||||
interface ListenerEntry {
|
interface ListenerEntry {
|
||||||
callback: PacketCallback;
|
callback: PacketCallback;
|
||||||
@@ -27,14 +27,30 @@ interface ListenerEntry {
|
|||||||
|
|
||||||
export class NativePacketHandler {
|
export class NativePacketHandler {
|
||||||
private readonly supportedPlatforms = ['win32.x64', 'linux.x64', 'linux.arm64', 'darwin.x64', 'darwin.arm64'];
|
private readonly supportedPlatforms = ['win32.x64', 'linux.x64', 'linux.arm64', 'darwin.x64', 'darwin.arm64'];
|
||||||
private readonly MoeHooExport: { exports: NativePacketExportType } = { exports: {} };
|
private readonly MoeHooExport: { exports: NativePacketExportType; } = { exports: {} };
|
||||||
protected readonly logger: LogWrapper;
|
protected readonly logger: LogWrapper;
|
||||||
|
private loaded: boolean = false;
|
||||||
|
|
||||||
// 统一的监听器存储 - key: 'all' | 'type:0' | 'type:1' | 'cmd:xxx' | 'exact:type:cmd'
|
// 统一的监听器存储 - key: 'all' | 'type:0' | 'type:1' | 'cmd:xxx' | 'exact:type:cmd'
|
||||||
private readonly listeners: Map<string, Set<ListenerEntry>> = new Map();
|
private readonly listeners: Map<string, Set<ListenerEntry>> = new Map();
|
||||||
|
|
||||||
constructor ({ logger }: { logger: LogWrapper }) {
|
constructor ({ logger }: { logger: LogWrapper; }) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
try {
|
||||||
|
const platform = process.platform + '.' + process.arch;
|
||||||
|
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './native/packet/MoeHoo.' + platform + '.node');
|
||||||
|
if (!fs.existsSync(moehoo_path)) {
|
||||||
|
this.logger.logWarn(`NativePacketClient: 缺失运行时文件: ${moehoo_path}`);
|
||||||
|
this.loaded = false;
|
||||||
|
}
|
||||||
|
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
|
||||||
|
this.loaded = true;
|
||||||
|
this.logger.log('[PacketHandler] 加载成功');
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.logError('NativePacketClient 加载出错:', error);
|
||||||
|
this.loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,6 +198,10 @@ export class NativePacketHandler {
|
|||||||
async init (version: string): Promise<boolean> {
|
async init (version: string): Promise<boolean> {
|
||||||
const version_arch = version + '-' + process.arch;
|
const version_arch = version + '-' + process.arch;
|
||||||
try {
|
try {
|
||||||
|
if (!this.loaded) {
|
||||||
|
this.logger.logWarn('NativePacketClient 未成功加载,无法初始化');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const send = typedOffset[version_arch]?.send;
|
const send = typedOffset[version_arch]?.send;
|
||||||
const recv = typedOffset[version_arch]?.recv;
|
const recv = typedOffset[version_arch]?.recv;
|
||||||
if (!send || !recv) {
|
if (!send || !recv) {
|
||||||
@@ -193,16 +213,11 @@ export class NativePacketHandler {
|
|||||||
this.logger.logWarn(`NativePacketClient: 不支持的平台: ${platform}`);
|
this.logger.logWarn(`NativePacketClient: 不支持的平台: ${platform}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './native/packet/MoeHoo.' + platform + '.node');
|
|
||||||
|
|
||||||
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
|
|
||||||
if (!fs.existsSync(moehoo_path)) {
|
|
||||||
this.logger.logWarn(`NativePacketClient: 缺失运行时文件: ${moehoo_path}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.MoeHooExport.exports.initHook?.(send, recv, (type: PacketType, uin: string, cmd: string, seq: number, hex_data: string) => {
|
this.MoeHooExport.exports.initHook?.(send, recv, (type: PacketType, uin: string, cmd: string, seq: number, hex_data: string) => {
|
||||||
this.emitPacket(type, uin, cmd, seq, hex_data);
|
this.emitPacket(type, uin, cmd, seq, hex_data);
|
||||||
}, true);
|
}, true);
|
||||||
|
this.logger.log('[PacketHandler] 初始化成功');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.logError('NativePacketClient 初始化出错:', error);
|
this.logger.logError('NativePacketClient 初始化出错:', error);
|
||||||
|
|||||||
@@ -3,122 +3,133 @@ import { NodeIKernelBuddyListener } from '@/core/listeners';
|
|||||||
import { BuddyListReqType } from '@/core/types/user';
|
import { BuddyListReqType } from '@/core/types/user';
|
||||||
|
|
||||||
export interface NodeIKernelBuddyService {
|
export interface NodeIKernelBuddyService {
|
||||||
getBuddyListV2(callFrom: string, reqType: BuddyListReqType): Promise<GeneralCallResult & {
|
getBuddyListV2 (callFrom: string, reqType: BuddyListReqType): Promise<GeneralCallResult & {
|
||||||
data: Array<{
|
data: Array<{
|
||||||
categoryId: number,
|
categoryId: number,
|
||||||
categorySortId: number,
|
categorySortId: number,
|
||||||
categroyName: string,
|
categroyName: string,
|
||||||
categroyMbCount: number,
|
categroyMbCount: number,
|
||||||
onlineCount: number,
|
onlineCount: number,
|
||||||
buddyUids: Array<string>
|
buddyUids: Array<string>;
|
||||||
}>
|
}>;
|
||||||
|
}>;
|
||||||
|
getBuddyListV2 (callFrom: string, isPullRefresh: boolean, reqType: BuddyListReqType): Promise<GeneralCallResult & {
|
||||||
|
data: Array<{
|
||||||
|
categoryId: number,
|
||||||
|
categorySortId: number,
|
||||||
|
categroyName: string,
|
||||||
|
categroyMbCount: number,
|
||||||
|
onlineCount: number,
|
||||||
|
buddyUids: Array<string>;
|
||||||
|
}>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
getBuddyListFromCache(reqType: BuddyListReqType): Promise<Array<
|
|
||||||
|
getBuddyListFromCache (reqType: BuddyListReqType): Promise<Array<
|
||||||
{
|
{
|
||||||
categoryId: number, // 9999为特别关心
|
categoryId: number, // 9999为特别关心
|
||||||
categorySortId: number, // 排序方式
|
categorySortId: number, // 排序方式
|
||||||
categroyName: string, // 分类名
|
categroyName: string, // 分类名
|
||||||
categroyMbCount: number, // 不懂
|
categroyMbCount: number, // 不懂
|
||||||
onlineCount: number, // 在线数目
|
onlineCount: number, // 在线数目
|
||||||
buddyUids: Array<string>// Uids
|
buddyUids: Array<string>;// Uids
|
||||||
}>>;
|
}>>;
|
||||||
|
|
||||||
addKernelBuddyListener(listener: NodeIKernelBuddyListener): number;
|
addKernelBuddyListener (listener: NodeIKernelBuddyListener): number;
|
||||||
|
|
||||||
getAllBuddyCount(): number;
|
getAllBuddyCount (): number;
|
||||||
|
|
||||||
removeKernelBuddyListener(listenerId: number): void;
|
removeKernelBuddyListener (listenerId: number): void;
|
||||||
|
|
||||||
// getBuddyList(nocache: boolean): Promise<GeneralCallResult>;
|
// getBuddyList(nocache: boolean): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
getBuddyNick(uid: number): string;
|
getBuddyNick (uid: number): string;
|
||||||
|
|
||||||
getBuddyRemark(uid: number): string;
|
getBuddyRemark (uid: number): string;
|
||||||
|
|
||||||
setBuddyRemark(param: { uid: string, remark: string, signInfo?: unknown }): void;
|
setBuddyRemark (param: { uid: string, remark: string, signInfo?: unknown; }): void;
|
||||||
|
|
||||||
getAvatarUrl(uid: number): string;
|
getAvatarUrl (uid: number): string;
|
||||||
|
|
||||||
isBuddy(uid: string): boolean;
|
isBuddy (uid: string): boolean;
|
||||||
|
|
||||||
getCategoryNameWithUid(uid: number): string;
|
getCategoryNameWithUid (uid: number): string;
|
||||||
|
|
||||||
getTargetBuddySetting(uid: number): unknown;
|
getTargetBuddySetting (uid: number): unknown;
|
||||||
|
|
||||||
getTargetBuddySettingByType(uid: number, type: number): unknown;
|
getTargetBuddySettingByType (uid: number, type: number): unknown;
|
||||||
|
|
||||||
getBuddyReqUnreadCnt(): number;
|
getBuddyReqUnreadCnt (): number;
|
||||||
|
|
||||||
getBuddyReq(): Promise<GeneralCallResult>;
|
getBuddyReq (): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
delBuddyReq(uid: number): void;
|
delBuddyReq (uid: number): void;
|
||||||
|
|
||||||
clearBuddyReqUnreadCnt(): Promise<GeneralCallResult>;
|
clearBuddyReqUnreadCnt (): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
reqToAddFriends(uid: number, msg: string): void;
|
reqToAddFriends (uid: number, msg: string): void;
|
||||||
|
|
||||||
setSpacePermission(uid: number, permission: number): void;
|
setSpacePermission (uid: number, permission: number): void;
|
||||||
|
|
||||||
approvalFriendRequest(arg: {
|
approvalFriendRequest (arg: {
|
||||||
friendUid: string;
|
friendUid: string;
|
||||||
reqTime: string;
|
reqTime: string;
|
||||||
accept: boolean;
|
accept: boolean;
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
|
|
||||||
delBuddy(param: {
|
delBuddy (param: {
|
||||||
friendUid: string;
|
friendUid: string;
|
||||||
tempBlock: boolean;
|
tempBlock: boolean;
|
||||||
tempBothDel: boolean;
|
tempBothDel: boolean;
|
||||||
}): Promise<unknown>;
|
}): Promise<unknown>;
|
||||||
|
|
||||||
delBatchBuddy(uids: number[]): void;
|
delBatchBuddy (uids: number[]): void;
|
||||||
|
|
||||||
getSmartInfos(uid: number): unknown;
|
getSmartInfos (uid: number): unknown;
|
||||||
|
|
||||||
setBuddyCategory(uid: number, category: number): void;
|
setBuddyCategory (uid: number, category: number): void;
|
||||||
|
|
||||||
setBatchBuddyCategory(uids: number[], category: number): void;
|
setBatchBuddyCategory (uids: number[], category: number): void;
|
||||||
|
|
||||||
addCategory(category: string): void;
|
addCategory (category: string): void;
|
||||||
|
|
||||||
delCategory(category: string): void;
|
delCategory (category: string): void;
|
||||||
|
|
||||||
renameCategory(oldCategory: string, newCategory: string): void;
|
renameCategory (oldCategory: string, newCategory: string): void;
|
||||||
|
|
||||||
resortCategory(categorys: string[]): void;
|
resortCategory (categorys: string[]): void;
|
||||||
|
|
||||||
pullCategory(uid: number, category: string): void;
|
pullCategory (uid: number, category: string): void;
|
||||||
|
|
||||||
setTop(uid: number, isTop: boolean): void;
|
setTop (uid: number, isTop: boolean): void;
|
||||||
|
|
||||||
SetSpecialCare(uid: number, isSpecialCare: boolean): void;
|
SetSpecialCare (uid: number, isSpecialCare: boolean): void;
|
||||||
|
|
||||||
setMsgNotify(uid: number, isNotify: boolean): void;
|
setMsgNotify (uid: number, isNotify: boolean): void;
|
||||||
|
|
||||||
hasBuddyList(): boolean;
|
hasBuddyList (): boolean;
|
||||||
|
|
||||||
setBlock(uid: number, isBlock: boolean): void;
|
setBlock (uid: number, isBlock: boolean): void;
|
||||||
|
|
||||||
isBlocked(uid: number): boolean;
|
isBlocked (uid: number): boolean;
|
||||||
|
|
||||||
modifyAddMeSetting(setting: unknown): void;
|
modifyAddMeSetting (setting: unknown): void;
|
||||||
|
|
||||||
getAddMeSetting(): unknown;
|
getAddMeSetting (): unknown;
|
||||||
|
|
||||||
getDoubtBuddyReq(reqId: string, num: number, uk:string): Promise<GeneralCallResult>;
|
getDoubtBuddyReq (reqId: string, num: number, uk: string): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
getDoubtBuddyUnreadNum(): number;
|
getDoubtBuddyUnreadNum (): number;
|
||||||
|
|
||||||
approvalDoubtBuddyReq(uid: string, str1: string, str2: string): void;
|
approvalDoubtBuddyReq (uid: string, str1: string, str2: string): void;
|
||||||
|
|
||||||
delDoubtBuddyReq(uid: number): void;
|
delDoubtBuddyReq (uid: number): void;
|
||||||
|
|
||||||
delAllDoubtBuddyReq(): Promise<GeneralCallResult>;
|
delAllDoubtBuddyReq (): Promise<GeneralCallResult>;
|
||||||
|
|
||||||
reportDoubtBuddyReqUnread(): void;
|
reportDoubtBuddyReqUnread (): void;
|
||||||
|
|
||||||
getBuddyRecommendContactArkJson(uid: string, phoneNumber: string): Promise<GeneralCallResult & { arkMsg: string }>;
|
getBuddyRecommendContactArkJson (uid: string, phoneNumber: string): Promise<GeneralCallResult & { arkMsg: string; }>;
|
||||||
|
|
||||||
isNull(): boolean;
|
isNull (): boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,7 +7,7 @@ import { FFmpegService } from '@/common/ffmpeg';
|
|||||||
const out_format = ['mp3', 'amr', 'wma', 'm4a', 'spx', 'ogg', 'wav', 'flac'];
|
const out_format = ['mp3', 'amr', 'wma', 'm4a', 'spx', 'ogg', 'wav', 'flac'];
|
||||||
|
|
||||||
type Payload = {
|
type Payload = {
|
||||||
out_format: string
|
out_format: string;
|
||||||
} & GetFilePayload;
|
} & GetFilePayload;
|
||||||
|
|
||||||
export default class GetRecord extends GetFileBase {
|
export default class GetRecord extends GetFileBase {
|
||||||
@@ -28,9 +28,13 @@ export default class GetRecord extends GetFileBase {
|
|||||||
try {
|
try {
|
||||||
await fs.access(outputFile);
|
await fs.access(outputFile);
|
||||||
} catch {
|
} catch {
|
||||||
|
if (FFmpegService.getAdapterName() === 'FFmpegAddon') {
|
||||||
|
await FFmpegService.convertFile(inputFile, outputFile, payload.out_format);
|
||||||
|
} else {
|
||||||
await this.decodeFile(inputFile, pcmFile);
|
await this.decodeFile(inputFile, pcmFile);
|
||||||
await FFmpegService.convertFile(pcmFile, outputFile, payload.out_format);
|
await FFmpegService.convertFile(pcmFile, outputFile, payload.out_format);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const base64Data = await fs.readFile(outputFile, { encoding: 'base64' });
|
const base64Data = await fs.readFile(outputFile, { encoding: 'base64' });
|
||||||
res.file = outputFile;
|
res.file = outputFile;
|
||||||
res.url = outputFile;
|
res.url = outputFile;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export class SendGroupNotice extends OneBotAction<Payload, null> {
|
|||||||
await checkFileExist(path, 5000);
|
await checkFileExist(path, 5000);
|
||||||
const ImageUploadResult = await this.core.apis.GroupApi.uploadGroupBulletinPic(payload.group_id.toString(), path);
|
const ImageUploadResult = await this.core.apis.GroupApi.uploadGroupBulletinPic(payload.group_id.toString(), path);
|
||||||
if (ImageUploadResult.errCode !== 0) {
|
if (ImageUploadResult.errCode !== 0) {
|
||||||
throw new Error(`群公告${payload.image}设置失败,图片上传失败`);
|
throw new Error(`群公告${payload.image}设置失败,图片上传失败 , 错误信息:${ImageUploadResult.errMsg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlink(path).catch(() => { });
|
unlink(path).catch(() => { });
|
||||||
|
|||||||
@@ -47,8 +47,12 @@ export class DownloadFileRecordStream extends BaseDownloadStream<Payload, Downlo
|
|||||||
streamPath = outputFile;
|
streamPath = outputFile;
|
||||||
} catch {
|
} catch {
|
||||||
// 尝试解码 silk 到 pcm 再用 ffmpeg 转换
|
// 尝试解码 silk 到 pcm 再用 ffmpeg 转换
|
||||||
|
if (FFmpegService.getAdapterName() === 'FFmpegAddon') {
|
||||||
|
await FFmpegService.convertFile(downloadPath, outputFile, payload.out_format);
|
||||||
|
} else {
|
||||||
await this.decodeFile(downloadPath, pcmFile);
|
await this.decodeFile(downloadPath, pcmFile);
|
||||||
await FFmpegService.convertFile(pcmFile, outputFile, payload.out_format);
|
await FFmpegService.convertFile(pcmFile, outputFile, payload.out_format);
|
||||||
|
}
|
||||||
streamPath = outputFile;
|
streamPath = outputFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -907,12 +907,12 @@ export class OneBotMsgApi {
|
|||||||
|
|
||||||
async parsePrivateMsgEvent (msg: RawMessage, grayTipElement: GrayTipElement) {
|
async parsePrivateMsgEvent (msg: RawMessage, grayTipElement: GrayTipElement) {
|
||||||
if (grayTipElement.subElementType === NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
if (grayTipElement.subElementType === NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
||||||
if (grayTipElement.jsonGrayTipElement.busiId === 1061) {
|
if (grayTipElement.jsonGrayTipElement.busiId.toString() === '1061') {
|
||||||
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
||||||
if (PokeEvent) {
|
if (PokeEvent) {
|
||||||
return PokeEvent;
|
return PokeEvent;
|
||||||
}
|
}
|
||||||
} else if (grayTipElement.jsonGrayTipElement.busiId === 19324 && msg.peerUid !== '') {
|
} else if (grayTipElement.jsonGrayTipElement.busiId.toString() === '19324' && msg.peerUid !== '') {
|
||||||
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import path from 'path';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import { LoginListItem, NodeIKernelLoginService } from '@/core/services';
|
import { LoginListItem, NodeIKernelLoginService } from '@/core/services';
|
||||||
import { program } from 'commander';
|
|
||||||
import qrcode from '@/qrcode/lib/main';
|
import qrcode from '@/qrcode/lib/main';
|
||||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||||
import { InitWebUi } from '@/webui';
|
import { InitWebUi } from '@/webui';
|
||||||
@@ -322,7 +321,9 @@ export async function NCoreInitShell () {
|
|||||||
// 初始化 FFmpeg 服务
|
// 初始化 FFmpeg 服务
|
||||||
await FFmpegService.init(pathWrapper.binaryPath, logger);
|
await FFmpegService.init(pathWrapper.binaryPath, logger);
|
||||||
|
|
||||||
|
if (process.env['NAPCAT_DISABLE_PIPE'] !== '1') {
|
||||||
await connectToNamedPipe(logger).catch(e => logger.logError('命名管道连接失败', e));
|
await connectToNamedPipe(logger).catch(e => logger.logError('命名管道连接失败', e));
|
||||||
|
}
|
||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVersion());
|
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVersion());
|
||||||
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
|
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
|
||||||
@@ -362,9 +363,18 @@ export async function NCoreInitShell () {
|
|||||||
await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion);
|
await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion);
|
||||||
await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
|
await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
|
||||||
handleProxy(session, logger);
|
handleProxy(session, logger);
|
||||||
program.option('-q, --qq [number]', 'QQ号').parse(process.argv);
|
|
||||||
const cmdOptions = program.opts();
|
let quickLoginUin: string | undefined = undefined;
|
||||||
const quickLoginUin = cmdOptions['qq'];
|
try {
|
||||||
|
const args = process.argv;
|
||||||
|
const qIndex = args.findIndex(arg => arg === '-q' || arg === '--qq');
|
||||||
|
if (qIndex !== -1 && qIndex + 1 < args.length) {
|
||||||
|
quickLoginUin = args[qIndex + 1];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.logWarn('解析命令行参数失败,无法使用快速登录功能', error);
|
||||||
|
}
|
||||||
|
|
||||||
const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList;
|
const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList;
|
||||||
|
|
||||||
const dataTimestape = new Date().getTime().toString();
|
const dataTimestape = new Date().getTime().toString();
|
||||||
|
|||||||
BIN
tests/QQNT.dll
Normal file
BIN
tests/QQNT.dll
Normal file
Binary file not shown.
69
tests/loadNapCat.cjs
Normal file
69
tests/loadNapCat.cjs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
const { pathToFileURL } = require('url');
|
||||||
|
|
||||||
|
const mainPath = process.argv[2];
|
||||||
|
if (!mainPath) {
|
||||||
|
console.error('Please provide the base directory as the first argument.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态获取 versions 下唯一的版本文件夹,并拼接 resources/app 路径
|
||||||
|
const versionsDir = path.join(mainPath, 'versions');
|
||||||
|
console.log(`Looking for version folders in: ${versionsDir}`);
|
||||||
|
const versionFolders = fs.readdirSync(versionsDir).filter(f => fs.statSync(path.join(versionsDir, f)).isDirectory());
|
||||||
|
if (versionFolders.length !== 1) {
|
||||||
|
console.error('versions 文件夹下必须且只能有一个版本目录');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE_DIR = path.join(versionsDir, versionFolders[0], 'resources', 'app');
|
||||||
|
const TARGET_DIR = path.join(__dirname, 'run');
|
||||||
|
const QQNT_FILE = path.join(__dirname, 'QQNT.dll');
|
||||||
|
const NAPCAT_MJS_PATH = path.join(__dirname, '..', 'dist', 'napcat.mjs');
|
||||||
|
|
||||||
|
const itemsToCopy = [
|
||||||
|
'avif_convert.dll',
|
||||||
|
'broadcast_ipc.dll',
|
||||||
|
'libglib-2.0-0.dll',
|
||||||
|
'libgobject-2.0-0.dll',
|
||||||
|
'libvips-42.dll',
|
||||||
|
'ncnn.dll',
|
||||||
|
'opencv.dll',
|
||||||
|
'package.json',
|
||||||
|
'QBar.dll',
|
||||||
|
'wrapper.node'
|
||||||
|
];
|
||||||
|
|
||||||
|
async function copyAll () {
|
||||||
|
const qqntDllPath = path.join(TARGET_DIR, 'QQNT.dll');
|
||||||
|
const configPath = path.join(TARGET_DIR, 'config.json');
|
||||||
|
const allItemsExist = await fs.pathExists(qqntDllPath)
|
||||||
|
&& await fs.pathExists(configPath)
|
||||||
|
&& (await Promise.all(itemsToCopy.map(item => fs.pathExists(path.join(TARGET_DIR, item))))).every(exists => exists);
|
||||||
|
|
||||||
|
if (!allItemsExist) {
|
||||||
|
console.log('Copying required files...');
|
||||||
|
await fs.ensureDir(TARGET_DIR);
|
||||||
|
await fs.copy(QQNT_FILE, qqntDllPath, { overwrite: true });
|
||||||
|
await fs.copy(path.join(versionsDir, 'config.json'), configPath, { overwrite: true });
|
||||||
|
await Promise.all(itemsToCopy.map(async (item) => {
|
||||||
|
await fs.copy(path.join(BASE_DIR, item), path.join(TARGET_DIR, item), { overwrite: true });
|
||||||
|
console.log(`Copied ${item}`);
|
||||||
|
}));
|
||||||
|
console.log('All files copied successfully.');
|
||||||
|
} else {
|
||||||
|
console.log('Files already exist, skipping copy.');
|
||||||
|
}
|
||||||
|
|
||||||
|
process.env.NAPCAT_WRAPPER_PATH = path.join(TARGET_DIR, 'wrapper.node');
|
||||||
|
process.env.NAPCAT_QQ_PACKAGE_INFO_PATH = path.join(TARGET_DIR, 'package.json');
|
||||||
|
process.env.NAPCAT_QQ_VERSION_CONFIG_PATH = path.join(TARGET_DIR, 'config.json');
|
||||||
|
process.env.NAPCAT_DISABLE_PIPE = '1';
|
||||||
|
process.env.NAPCAT_WORKDIR = path.join(__dirname, 'run');
|
||||||
|
|
||||||
|
console.log('Loading NapCat module...');
|
||||||
|
await import(pathToFileURL(NAPCAT_MJS_PATH).href);
|
||||||
|
}
|
||||||
|
|
||||||
|
copyAll().catch(console.error);
|
||||||
8
tests/nodeTest.ps1
Normal file
8
tests/nodeTest.ps1
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
$regPath = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ'
|
||||||
|
$uninstall = (Get-ItemProperty -Path $regPath -Name UninstallString -ErrorAction Stop).UninstallString
|
||||||
|
$uninstall = $uninstall.Trim('"')
|
||||||
|
$qqPath = Split-Path $uninstall -Parent
|
||||||
|
|
||||||
|
Write-Host "QQPath: $qqPath"
|
||||||
|
node.exe "tests/loadNapCat.cjs" "$qqPath"
|
||||||
@@ -56,6 +56,7 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
|
|||||||
// }),
|
// }),
|
||||||
cp({
|
cp({
|
||||||
targets: [
|
targets: [
|
||||||
|
{ src: './napiloader/', dest: 'dist', flatten: true },
|
||||||
{ src: './src/native/', dest: 'dist/native', flatten: false },
|
{ src: './src/native/', dest: 'dist/native', flatten: false },
|
||||||
{ src: './manifest.json', dest: 'dist' },
|
{ src: './manifest.json', dest: 'dist' },
|
||||||
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
||||||
|
|||||||
Reference in New Issue
Block a user