Compare commits

...

37 Commits

Author SHA1 Message Date
手瓜一十雪
d35a19b4fd release: olpush remove 2024-10-11 23:06:18 +08:00
手瓜一十雪
39c4473367 feat: poke oidb.0xed3_1 2024-10-10 19:04:29 +08:00
手瓜一十雪
b882bc721d release: v2.6.24 2024-10-09 20:50:00 +08:00
手瓜一十雪
405cace489 feat: 9.9.15-28498 2024-10-09 20:14:42 +08:00
手瓜一十雪
402a7b7fc9 docs: 移除误导语句 2024-10-09 14:12:13 +08:00
手瓜一十雪
8ad805e654 docs: 修正 2024-10-08 14:26:03 +08:00
手瓜一十雪
b23c357f73 release: 2.6.24 2024-10-06 10:07:39 +08:00
Wesley F. Young
f561c2b0fa refactor: rewrite switch with object mapping or if-else 2024-10-05 17:03:02 +08:00
手瓜一十雪
5a8eea668f release: 2.6.23 2024-10-02 13:23:07 +08:00
手瓜一十雪
777143e502 feat: new log 2024-10-02 13:13:55 +08:00
手瓜一十雪
0d8c9a82fe fix: 空格目录 2024-10-02 11:45:19 +08:00
手瓜一十雪
d10ab1cce3 feat: 依赖调整 2024-10-02 11:29:05 +08:00
手瓜一十雪
ec25e09d73 release: 2.6.22 2024-10-02 10:10:01 +08:00
手瓜一十雪
cba9c78ab1 release: v2.6.21 2024-10-02 10:05:01 +08:00
手瓜一十雪
c32db4a881 fix: rkey v2 2024-10-02 10:03:48 +08:00
手瓜一十雪
871add3071 Merge pull request #419 from hguandl/macos
Fix Protobuf Dependencies & macOS Support
2024-10-01 22:59:07 +08:00
Hao Guan
e661c617a3 update: macOS support 2024-10-01 22:48:51 +08:00
Hao Guan
d4bf721540 fix: protobuf dependencies 2024-10-01 22:48:31 +08:00
Wesley F. Young
d91b55faed update: log group name & sender nickname onto console 2024-10-01 11:01:32 +08:00
Alen
9687832d4d chore: 拉高linuxQQ版本 2024-10-01 02:37:44 +08:00
手瓜一十雪
fc3e436744 fix: log 2024-09-30 17:07:30 +08:00
手瓜一十雪
da90245f7b release: v2.6.20 2024-09-30 17:05:02 +08:00
手瓜一十雪
410d6a85d7 fix: protobuf #417 2024-09-30 08:30:39 +08:00
手瓜一十雪
b693342e4f fix 2024-09-30 08:18:02 +08:00
手瓜一十雪
acca361f2e release: v2.6.18 2024-09-29 20:11:30 +08:00
手瓜一十雪
b663f47713 style: ScalarType 2024-09-29 20:10:14 +08:00
手瓜一十雪
d332b199b5 refactor: protobufjs给我似! 2024-09-29 20:06:11 +08:00
Alen
78bac1dbd1 Merge pull request #416 from cnxysoft/upmain
fix: 28418下载HASH
2024-09-29 16:21:52 +08:00
Alen
724ff215f9 fix: 28418下载HASH 2024-09-29 16:20:01 +08:00
手瓜一十雪
68ea146469 release: v2.6.17 2024-09-29 13:07:52 +08:00
手瓜一十雪
82583e616f fix: #415 2024-09-29 12:57:50 +08:00
手瓜一十雪
bfc339c58d refactor: #415 2024-09-29 12:53:18 +08:00
手瓜一十雪
fe4427c076 feat: message字段返回 #415 2024-09-29 12:30:29 +08:00
手瓜一十雪
5745f388a9 feat: 简化代码 #415 2024-09-29 12:19:04 +08:00
手瓜一十雪
377e3c253f feat: parseForward for array 2024-09-29 11:26:45 +08:00
手瓜一十雪
3007a0c00e feat: nativeNode 2024-09-28 23:00:47 +08:00
手瓜一十雪
f51ffc091d Merge pull request #410 from NapNeko/hook
[Hook] NapcatNative
2024-09-28 21:38:04 +08:00
26 changed files with 450 additions and 333 deletions

View File

@@ -7,14 +7,14 @@
NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现 NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
## 猫猫技能 ## 猫猫技能
- [x] **高性能**1K+ 群聊数目、20 线程并行发送消息毫无压力 - [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**:自带 WebUI 支持,远程管理更加便捷 - [x] **远程管理**:自带 WebUI 支持,远程管理更加便捷
- [x] **低故障率**:快速适配最新版本,日常保证 0 Issue - [x] **扩展支持**:基于 MoeHoo 的Native 可实现发包与收包
## 使用猫猫 ## 使用猫猫
@@ -28,7 +28,7 @@ NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
[Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl) [Telegram Link](https://t.me/+nLZEnpne-pQ1OWFl)
## 猫猫朋友 ## 猫猫朋友
感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot) 提供部分参考 感谢 [LLOneBot](https://github.com/LLOneBot/LLOneBot)
感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持

View File

@@ -33,7 +33,7 @@ if not exist "%QQpath%" (
exit /b exit /b
) )
set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/% set NAPCAT_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%
echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > %NAPCAT_LOAD_PATH% echo (async () =^> {await import("file:///%NAPCAT_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1 "%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %1

View File

@@ -1,9 +1,9 @@
{ {
"name": "qq-chat", "name": "qq-chat",
"version": "9.9.15-28418", "version": "9.9.15-28418",
"verHash": "512caf78", "verHash": "206bfa62",
"linuxVersion": "3.2.12-28327", "linuxVersion": "3.2.12-28418",
"linuxVerHash": "f60e8252", "linuxVerHash": "0256c948",
"type": "module", "type": "module",
"private": true, "private": true,
"description": "QQ", "description": "QQ",

View File

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

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "2.6.16", "version": "2.6.26",
"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",
@@ -13,7 +13,6 @@
"devDependencies": { "devDependencies": {
"@babel/preset-typescript": "^7.24.7", "@babel/preset-typescript": "^7.24.7",
"@log4js-node/log4js-api": "^1.0.2", "@log4js-node/log4js-api": "^1.0.2",
"@protobuf-ts/plugin": "^2.9.4",
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6", "@rollup/plugin-typescript": "^11.1.6",
"@types/cors": "^2.8.17", "@types/cors": "^2.8.17",
@@ -30,23 +29,23 @@
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vite": "^5.2.6", "vite": "^5.2.6",
"vite-plugin-cp": "^4.0.8", "vite-plugin-cp": "^4.0.8",
"vite-tsconfig-paths": "^4.3.2" "vite-tsconfig-paths": "^4.3.2",
}, "@protobuf-ts/runtime": "^2.9.4",
"dependencies": {
"ajv": "^8.13.0", "ajv": "^8.13.0",
"async-mutex": "^0.5.0", "fast-xml-parser": "^4.3.6",
"chalk": "^5.3.0", "chalk": "^5.3.0",
"commander": "^12.1.0", "commander": "^12.1.0",
"cors": "^2.8.5", "async-mutex": "^0.5.0",
"express": "^5.0.0-beta.2",
"fast-xml-parser": "^4.3.6",
"file-type": "^19.0.0", "file-type": "^19.0.0",
"fluent-ffmpeg": "^2.1.2",
"image-size": "^1.1.1",
"json-schema-to-ts": "^3.1.0", "json-schema-to-ts": "^3.1.0",
"log4js": "^6.9.1", "image-size": "^1.1.1",
"protobufjs": "~7.4.0", "cors": "^2.8.5"
},
"dependencies": {
"qrcode-terminal": "^0.12.0", "qrcode-terminal": "^0.12.0",
"fluent-ffmpeg": "^2.1.2",
"express": "^5.0.0-beta.2",
"log4js": "^6.9.1",
"silk-wasm": "^3.6.1", "silk-wasm": "^3.6.1",
"ws": "^8.18.0" "ws": "^8.18.0"
} }

View File

@@ -139,8 +139,13 @@ export class LogWrapper {
logMessage(msg: RawMessage, selfInfo: SelfInfo) { logMessage(msg: RawMessage, selfInfo: SelfInfo) {
const isSelfSent = msg.senderUin === selfInfo.uin; const isSelfSent = msg.senderUin === selfInfo.uin;
this.log(`${isSelfSent ? '发送 ->' : '接收 <-'
} ${rawMessageToText(msg)}`); // Intercept grey tip
if (msg.elements[0]?.elementType === ElementType.GreyTip) {
return;
}
this.log(`${isSelfSent ? '发送 ->' : '接收 <-' } ${rawMessageToText(msg)}`);
} }
} }
@@ -154,7 +159,12 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
if (msg.chatType == ChatType.KCHATTYPEC2C) { if (msg.chatType == ChatType.KCHATTYPEC2C) {
tokens.push(`私聊 (${msg.peerUin})`); tokens.push(`私聊 (${msg.peerUin})`);
} else if (msg.chatType == ChatType.KCHATTYPEGROUP) { } else if (msg.chatType == ChatType.KCHATTYPEGROUP) {
tokens.push(`群聊 (群 ${msg.peerUin}${msg.senderUin})`); if (recursiveLevel < 1) {
tokens.push(`群聊 [${msg.peerName}(${msg.peerUin})]`);
}
if (msg.senderUin !== '0') {
tokens.push(`[${msg.sendMemberName || msg.sendRemarkName || msg.sendNickName}(${msg.senderUin})]`);
}
} else if (msg.chatType == ChatType.KCHATTYPEDATALINE) { } else if (msg.chatType == ChatType.KCHATTYPEDATALINE) {
tokens.push('移动设备'); tokens.push('移动设备');
} else /* temp */ { } else /* temp */ {

View File

@@ -53,26 +53,22 @@ export class QQBasicInfoWrapper {
} }
//此方法不要直接使用 //此方法不要直接使用
getQUAInternal() { getQUAFallback() {
switch (systemPlatform) { const platformMapping: Partial<Record<NodeJS.Platform, string>> = {
case 'linux': win32: `V1_WIN_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`,
return `V1_LNX_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`; darwin: `V1_MAC_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`,
case 'darwin': linux: `V1_LNX_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`,
return `V1_MAC_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`; };
default: return platformMapping[systemPlatform] ?? (platformMapping.win32)!;
return `V1_WIN_${this.getFullQQVesion()}_${this.getQQBuildStr()}_GW_B`;
}
} }
getAppidInternal() { getAppIdFallback() {
switch (systemPlatform) { const platformMapping: Partial<Record<NodeJS.Platform, string>> = {
case 'linux': win32: '537246092',
return '537246140'; darwin: '537246140',
case 'darwin': linux: '537246140',
return '537246140'; };
default: return platformMapping[systemPlatform] ?? '537246092';
return '537246092';
}
} }
getAppidV2(): { appid: string; qua: string } { getAppidV2(): { appid: string; qua: string } {
@@ -88,6 +84,6 @@ export class QQBasicInfoWrapper {
// else // else
this.context.logger.log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`); this.context.logger.log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`);
this.context.logger.log(`[QQ版本兼容性检测] ${fullVersion} 版本兼容性不佳,可能会导致一些功能无法正常使用`,); this.context.logger.log(`[QQ版本兼容性检测] ${fullVersion} 版本兼容性不佳,可能会导致一些功能无法正常使用`,);
return { appid: this.getAppidInternal(), qua: this.getQUAInternal() }; return { appid: this.getAppIdFallback(), qua: this.getQUAFallback() };
} }
} }

View File

@@ -1 +1 @@
export const napCatVersion = '2.6.16'; export const napCatVersion = '2.6.26';

View File

@@ -365,22 +365,43 @@ export class NTQQFileApi {
if (url) { if (url) {
const parsedUrl = new URL(IMAGE_HTTP_HOST + url); const parsedUrl = new URL(IMAGE_HTTP_HOST + url);
const urlRkey = parsedUrl.searchParams.get('rkey');
const imageAppid = parsedUrl.searchParams.get('appid'); const imageAppid = parsedUrl.searchParams.get('appid');
const isNTV2 = imageAppid && ['1406', '1407'].includes(imageAppid); const isNTV2 = imageAppid && ['1406', '1407'].includes(imageAppid);
if (isNTV2) { const imageFileId = parsedUrl.searchParams.get('fileid');
let rkey = parsedUrl.searchParams.get('rkey');
if (rkey) { let rkeyData = {
return IMAGE_HTTP_HOST_NT + url; private_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qEc3Rbib9LP4',
group_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qffcqm614gds',
online_rkey: false
};
try {
let tempRkeyData = await this.rkeyManager.getRkey();
rkeyData.group_rkey = tempRkeyData.group_rkey;
rkeyData.private_rkey = tempRkeyData.private_rkey;
rkeyData.online_rkey = tempRkeyData.expired_time > Date.now() / 1000;
} catch (e) {
this.context.logger.logError.bind(this.context.logger)('获取rkey失败 Fallback Old Mode', e);
} }
const rkeyData = await this.rkeyManager.getRkey();
rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST_NT + url + `${rkey}`; if (isNTV2 && urlRkey) {
} else { return IMAGE_HTTP_HOST_NT + urlRkey;
return IMAGE_HTTP_HOST + url; } else if (isNTV2 && rkeyData.online_rkey) {
let rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST_NT + url + `&rkey=${rkey}`;
} else if (isNTV2 && imageFileId) {
let rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST + `/download?appid=${imageAppid}&fileid=${imageFileId}&rkey=${rkey}`;
} }
} else if (fileMd5 || md5HexStr) {
}
//到这里说明可能是旧客户端
if (fileMd5 || md5HexStr) {
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr)!.toUpperCase()}/0`; return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr)!.toUpperCase()}/0`;
} }
this.context.logger.logDebug('图片url获取失败', element); this.context.logger.logDebug('图片url获取失败', element);
return ''; return '';
} }

View File

@@ -909,11 +909,26 @@ export interface RawMessage {
*/ */
peerUin: string; peerUin: string;
/**
* 好友备注(如果是好友消息)
*/
remark?: string;
/**
* 群名(如果是群消息)
*/
peerName: string;
/** /**
* 发送者昵称(如果是好友消息) * 发送者昵称(如果是好友消息)
*/ */
sendNickName: string; sendNickName: string;
/**
* 发送者好友备注(如果是群消息并且有发送者好友)
*/
sendRemarkName: string;
/** /**
* 发送者群名片(如果是群消息) * 发送者群名片(如果是群消息)
*/ */

View File

@@ -15,6 +15,10 @@
"appid": 537246140, "appid": 537246140,
"qua": "V1_LNX_NQ_3.2.12_28131_GW_B" "qua": "V1_LNX_NQ_3.2.12_28131_GW_B"
}, },
"6.9.55-28131": {
"appid": 537246115,
"qua": "V1_MAC_NQ_6.9.55_28131_GW_B"
},
"9.9.15-28327":{ "9.9.15-28327":{
"appid": 537249321, "appid": 537249321,
"qua": "V1_WIN_NQ_9.9.15_28327_GW_B" "qua": "V1_WIN_NQ_9.9.15_28327_GW_B"
@@ -30,5 +34,13 @@
"3.2.12-28418":{ "3.2.12-28418":{
"appid": 537249393, "appid": 537249393,
"qua": "V1_LNX_NQ_3.2.12_28418_GW_B" "qua": "V1_LNX_NQ_3.2.12_28418_GW_B"
},
"6.9.56-28418": {
"appid": 537249367,
"qua": "V1_MAC_NQ_6.9.56_28418_GW_B"
},
"9.9.15-28498":{
"appid": 537249321,
"qua": "V1_WIN_NQ_9.9.15_28498_GW_B"
} }
} }

View File

@@ -26,7 +26,8 @@ export class RkeyManager {
try { try {
await this.refreshRkey(); await this.refreshRkey();
} catch (e) { } catch (e) {
this.logger.logError.bind(this.logger)('获取rkey失败', e); throw new Error(`获取rkey失败: ${e}`);//外抛
//this.logger.logError.bind(this.logger)('获取rkey失败', e);
} }
} }
return this.rkeyData; return this.rkeyData;
@@ -42,9 +43,18 @@ export class RkeyManager {
//刷新rkey //刷新rkey
for (const url of this.serverUrl) { for (const url of this.serverUrl) {
try { try {
this.rkeyData = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET'); let temp = await RequestUtil.HttpGetJson<ServerRkeyData>(url, 'GET');
this.rkeyData = {
group_rkey: temp.group_rkey.slice(6),
private_rkey: temp.private_rkey.slice(6),
expired_time: temp.expired_time
};
} catch (e) { } catch (e) {
this.logger.logError.bind(this.logger)(`[Rkey] Get Rkey ${url} Error `, e); this.logger.logError.bind(this.logger)(`[Rkey] Get Rkey ${url} Error `, e);
//是否为最后一个url
if (url === this.serverUrl[this.serverUrl.length - 1]) {
throw new Error(`获取rkey失败: ${e}`);//外抛
}
} }
} }

View File

@@ -20,11 +20,11 @@ import { LogLevel, LogWrapper } from '@/common/log';
import { NodeIKernelLoginService } from '@/core/services'; import { NodeIKernelLoginService } from '@/core/services';
import { QQBasicInfoWrapper } from '@/common/qq-basic-info'; import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
import { NapCatPathWrapper } from '@/common/path'; import { NapCatPathWrapper } from '@/common/path';
import path, { resolve } from 'node:path'; import path from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import { hostname, systemName, systemVersion } from '@/common/system'; import { hostname, systemName, systemVersion } from '@/common/system';
import { NTEventWrapper } from '@/common/event'; import { NTEventWrapper } from '@/common/event';
import { ChatType, DataSource, GroupMember, KickedOffLineInfo, Peer, SelfInfo, SelfStatusInfo } from '@/core/entities'; import { DataSource, GroupMember, KickedOffLineInfo, SelfInfo, SelfStatusInfo } from '@/core/entities';
import { NapCatConfigLoader } from '@/core/helper/config'; import { NapCatConfigLoader } from '@/core/helper/config';
import os from 'node:os'; import os from 'node:os';
import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners'; import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
@@ -257,19 +257,12 @@ export async function genSessionConfig(
): Promise<WrapperSessionInitConfig> { ): Promise<WrapperSessionInitConfig> {
const downloadPath = path.join(account_path, 'NapCat', 'temp'); const downloadPath = path.join(account_path, 'NapCat', 'temp');
fs.mkdirSync(downloadPath, { recursive: true }); fs.mkdirSync(downloadPath, { recursive: true });
//os.platform() const platformMapping: Partial<Record<NodeJS.Platform, PlatformType>> = {
let systemPlatform = PlatformType.KWINDOWS; win32: PlatformType.KWINDOWS,
switch (os.platform()) { darwin: PlatformType.KMAC,
case 'win32': linux: PlatformType.KLINUX,
systemPlatform = PlatformType.KWINDOWS; };
break; const systemPlatform = platformMapping[os.platform()] ?? PlatformType.KWINDOWS;
case 'darwin':
systemPlatform = PlatformType.KMAC;
break;
case 'linux':
systemPlatform = PlatformType.KLINUX;
break;
}
return { return {
selfUin, selfUin,
selfUid, selfUid,

View File

@@ -0,0 +1,21 @@
import { MessageType, BinaryReader, ScalarType, BinaryWriter } from '@protobuf-ts/runtime';
export const FileId = new MessageType("FileId", [
{ no: 2, name: "sha1", kind: "scalar", T: ScalarType.BYTES },
{ no: 4, name: "appid", kind: "scalar", T: ScalarType.UINT32 },
]);
export function encodePBFileId(message: any) {
return FileId.internalBinaryWrite(message, new BinaryWriter(), {
writerFactory: () => new BinaryWriter(),
writeUnknownFields: false
}).finish();
}
export function decodePBFileId(buffer: Uint8Array): any {
const reader = new BinaryReader(buffer);
return FileId.internalBinaryRead(reader, reader.len, {
readUnknownField: true,
readerFactory: () => new BinaryReader(buffer)
});
}

View File

@@ -1,37 +1,48 @@
import * as pb from 'protobufjs'; import { MessageType, BinaryReader, ScalarType } from '@protobuf-ts/runtime';
export const BodyInner = new MessageType("BodyInner", [
{ no: 1, name: "msgType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */, opt: true },
{ no: 2, name: "subType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */, opt: true }
]);
export const BodyInner = new pb.Type("BodyInner") export const NoifyData = new MessageType("NoifyData", [
.add(new pb.Field("msgType", 1, "uint32", "optional")) { no: 1, name: "skip", kind: "scalar", T: ScalarType.BYTES /* bytes */, opt: true },
.add(new pb.Field("subType", 2, "uint32", "optional")) { no: 2, name: "innerData", kind: "scalar", T: ScalarType.BYTES /* bytes */, opt: true }
]);
export const NoifyData = new pb.Type("NoifyData") export const MsgHead = new MessageType("MsgHead", [
.add(new pb.Field("skip", 1, "bytes", "optional")) { no: 2, name: "bodyInner", kind: "message", T: () => BodyInner, opt: true },
.add(new pb.Field("innerData", 2, "bytes", "optional")) { no: 3, name: "noifyData", kind: "message", T: () => NoifyData, opt: true }
]);
export const MsgHead = new pb.Type("MsgHead") export const Message = new MessageType("Message", [
.add(BodyInner) { no: 1, name: "msgHead", kind: "message", T: () => MsgHead }
.add(NoifyData) ]);
.add(new pb.Field("bodyInner", 2, "BodyInner", "optional"))
.add(new pb.Field("noifyData", 3, "NoifyData", "optional"));
export const Message = new pb.Type("Message") export const SubDetail = new MessageType("SubDetail", [
.add(MsgHead) { no: 1, name: "msgSeq", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
.add(new pb.Field("msgHead", 1, "MsgHead")) { no: 2, name: "msgTime", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
{ no: 6, name: "senderUid", kind: "scalar", T: ScalarType.STRING /* string */ }
]);
export const SubDetail = new pb.Type("SubDetail") export const RecallDetails = new MessageType("RecallDetails", [
.add(new pb.Field("msgSeq", 1, "uint32")) { no: 1, name: "operatorUid", kind: "scalar", T: ScalarType.STRING /* string */ },
.add(new pb.Field("msgTime", 2, "uint32")) { no: 3, name: "subDetail", kind: "message", T: () => SubDetail }
.add(new pb.Field("senderUid", 6, "string")) ]);
export const RecallDetails = new pb.Type("RecallDetails") export const RecallGroup = new MessageType("RecallGroup", [
.add(SubDetail) { no: 1, name: "type", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
.add(new pb.Field("operatorUid", 1, "string")) { no: 4, name: "peerUid", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
.add(new pb.Field("subDetail", 3, "SubDetail")) { no: 11, name: "recallDetails", kind: "message", T: () => RecallDetails },
{ no: 37, name: "grayTipsSeq", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ }
]);
export const RecallGroup = new pb.Type("RecallGroup") export function decodeMessage(buffer: Uint8Array): any {
.add(RecallDetails) const reader = new BinaryReader(buffer);
.add(new pb.Field("type", 1, "int32")) return Message.internalBinaryRead(reader, reader.len, { readUnknownField: true, readerFactory: () => new BinaryReader(buffer) });
.add(new pb.Field("peerUid", 4, "uint32")) }
.add(new pb.Field("recallDetails", 11, "RecallDetails"))
.add(new pb.Field("grayTipsSeq", 37, "uint32")) export function decodeRecallGroup(buffer: Uint8Array): any {
const reader = new BinaryReader(buffer);
return RecallGroup.internalBinaryRead(reader, reader.len, { readUnknownField: true, readerFactory: () => new BinaryReader(buffer) });
}

30
src/core/proto/Poke.ts Normal file
View File

@@ -0,0 +1,30 @@
import { MessageType, ScalarType, BinaryWriter } from '@protobuf-ts/runtime';
export const OidbSvcTrpcTcpBase = new MessageType("oidb_svc_trpctcp_base", [
{ no: 1, name: "command", kind: "scalar", T: ScalarType.UINT32 },
{ no: 2, name: "subcommand", kind: "scalar", T: ScalarType.UINT32, opt: true },
{ no: 4, name: "body", kind: "scalar", T: ScalarType.BYTES, opt: true }
]);
export const OidbSvcTrpcTcp0XED3_1 = new MessageType("oidb_svc_trpctcp_0xed3_1", [
{ no: 1, name: "uin", kind: "scalar", T: ScalarType.UINT32 },
{ no: 2, name: "groupuin", kind: "scalar", T: ScalarType.UINT32, opt: true },
{ no: 5, name: "frienduin", kind: "scalar", T: ScalarType.UINT32, opt: true },
{ no: 6, name: "ext", kind: "scalar", T: ScalarType.UINT32 }
]);
export function encodeGroupPoke(groupUin: string, PeerUin: string) {
let Body = OidbSvcTrpcTcp0XED3_1.toBinary
({
uin: parseInt(PeerUin),
groupuin: parseInt(groupUin),
ext: 0
});
//console.log(Body)
return OidbSvcTrpcTcpBase.toBinary
({
command: 0xed3,
subcommand: 1,
body: Body
});
}

View File

@@ -1,95 +1,58 @@
import * as pb from 'protobufjs'; import { MessageType, BinaryReader, ScalarType, RepeatType } from '@protobuf-ts/runtime';
// Proto: from src/core/proto/ProfileLike.proto export const LikeDetail = new MessageType("likeDetail", [
// Author: Mlikiowa { no: 1, name: "txt", kind: "scalar", T: ScalarType.STRING /* string */ },
{ no: 3, name: "uin", kind: "scalar", T: ScalarType.INT64 /* int64 */ },
{ no: 5, name: "nickname", kind: "scalar", T: ScalarType.STRING /* string */ }
]);
export interface LikeDetailType { export const LikeMsg = new MessageType("likeMsg", [
txt: string; { no: 1, name: "times", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
uin: pb.Long; { no: 2, name: "time", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
nickname: string; { no: 3, name: "detail", kind: "message", T: () => LikeDetail }
} ]);
export interface LikeMsgType {
times: number; export const ProfileLikeSubTip = new MessageType("profileLikeSubTip", [
time: number; { no: 14, name: "msg", kind: "message", T: () => LikeMsg }
detail: LikeDetailType; ]);
export const ProfileLikeTip = new MessageType("profileLikeTip", [
{ no: 1, name: "msgType", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
{ no: 2, name: "subType", kind: "scalar", T: ScalarType.INT32 /* int32 */ },
{ no: 203, name: "content", kind: "message", T: () => ProfileLikeSubTip }
]);
export const SysMessageHeader = new MessageType("SysMessageHeader", [
{ no: 1, name: "PeerNumber", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
{ no: 2, name: "PeerString", kind: "scalar", T: ScalarType.STRING /* string */ },
{ no: 5, name: "Uin", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
{ no: 6, name: "Uid", kind: "scalar", T: ScalarType.STRING /* string */, opt: true }
]);
export const SysMessageMsgSpec = new MessageType("SysMessageMsgSpec", [
{ no: 1, name: "msgType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
{ no: 2, name: "subType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
{ no: 3, name: "subSubType", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
{ no: 5, name: "msgSeq", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
{ no: 6, name: "time", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ },
{ no: 12, name: "msgId", kind: "scalar", T: ScalarType.UINT64 /* uint64 */ },
{ no: 13, name: "other", kind: "scalar", T: ScalarType.UINT32 /* uint32 */ }
]);
export const SysMessageBodyWrapper = new MessageType("SysMessageBodyWrapper", [
{ no: 2, name: "wrappedBody", kind: "scalar", T: ScalarType.BYTES /* bytes */ }
]);
export const SysMessage = new MessageType("SysMessage", [
{ no: 1, name: "header", kind: "message", T: () => SysMessageHeader, repeat: RepeatType.UNPACKED },
{ no: 2, name: "msgSpec", kind: "message", T: () => SysMessageMsgSpec, repeat: RepeatType.UNPACKED },
{ no: 3, name: "bodyWrapper", kind: "message", T: () => SysMessageBodyWrapper }
]);
export function decodeProfileLikeTip(buffer: Uint8Array): any {
const reader = new BinaryReader(buffer);
return ProfileLikeTip.internalBinaryRead(reader, reader.len, { readUnknownField: true, readerFactory: () => new BinaryReader(buffer) });
} }
export interface profileLikeSubTipType { export function decodeSysMessage(buffer: Uint8Array): any {
msg: LikeMsgType; const reader = new BinaryReader(buffer);
return SysMessage.internalBinaryRead(reader, reader.len, { readUnknownField: true, readerFactory: () => new BinaryReader(buffer) });
} }
export interface ProfileLikeTipType {
msgType: number;
subType: number;
content: profileLikeSubTipType;
}
export interface SysMessageHeaderType {
id: string;
timestamp: number;
sender: string;
}
export interface SysMessageMsgSpecType {
msgType: number;
subType: number;
subSubType: number;
msgSeq: number;
time: number;
msgId: pb.Long;
other: number;
}
export interface SysMessageBodyWrapperType {
wrappedBody: Uint8Array;
}
export interface SysMessageType {
header: SysMessageHeaderType[];
msgSpec: SysMessageMsgSpecType[];
bodyWrapper: SysMessageBodyWrapperType;
}
export const SysMessageHeader = new pb.Type("SysMessageHeader")
.add(new pb.Field("PeerNumber", 1, "uint32"))
.add(new pb.Field("PeerString", 2, "string"))
.add(new pb.Field("Uin", 5, "uint32"))
.add(new pb.Field("Uid", 6, "string", "optional"));
export const SysMessageMsgSpec = new pb.Type("SysMessageMsgSpec")
.add(new pb.Field("msgType", 1, "uint32"))
.add(new pb.Field("subType", 2, "uint32"))
.add(new pb.Field("subSubType", 3, "uint32"))
.add(new pb.Field("msgSeq", 5, "uint32"))
.add(new pb.Field("time", 6, "uint32"))
.add(new pb.Field("msgId", 12, "uint64"))
.add(new pb.Field("other", 13, "uint32"));
export const SysMessageBodyWrapper = new pb.Type("SysMessageBodyWrapper")
.add(new pb.Field("wrappedBody", 2, "bytes"));
export const SysMessage = new pb.Type("SysMessage")
.add(SysMessageHeader)
.add(SysMessageMsgSpec)
.add(SysMessageBodyWrapper)
.add(new pb.Field("header", 1, "SysMessageHeader", "repeated"))
.add(new pb.Field("msgSpec", 2, "SysMessageMsgSpec", "repeated"))
.add(new pb.Field("bodyWrapper", 3, "SysMessageBodyWrapper"));
export const likeDetail = new pb.Type("likeDetail")
.add(new pb.Field("txt", 1, "string"))
.add(new pb.Field("uin", 3, "int64"))
.add(new pb.Field("nickname", 5, "string"));
export const likeMsg = new pb.Type("likeMsg")
.add(likeDetail)
.add(new pb.Field("times", 1, "int32"))
.add(new pb.Field("time", 2, "int32"))
.add(new pb.Field("detail", 3, "likeDetail"));
export const profileLikeSubTip = new pb.Type("profileLikeSubTip")
.add(likeMsg)
.add(new pb.Field("msg", 14, "likeMsg"))
export const profileLikeTip = new pb.Type("profileLikeTip")
.add(profileLikeSubTip)
.add(new pb.Field("msgType", 1, "int32"))
.add(new pb.Field("subType", 2, "int32"))
.add(new pb.Field("content", 203, "profileLikeSubTip"));

View File

@@ -1,23 +1,40 @@
import { constants } from "node:os"; import { constants } from "node:os";
import path from "path"; import path from "path";
import { dlopen } from "process"; import { dlopen } from "process";
import fs from "fs";
export class Native { export class Native {
platform: string; platform: string;
supportedPlatforms = ['win32']; supportedPlatforms = [];
MoeHooExport: any = { exports: {} }; MoeHooExport: any = { exports: {} };
recallHookEnabled: boolean = false; recallHookEnabled: boolean = false;
inited = true;
constructor(nodePath: string, platform: string = process.platform) { constructor(nodePath: string, platform: string = process.platform) {
this.platform = platform; this.platform = platform;
try {
if (!this.supportedPlatforms.includes(this.platform)) { if (!this.supportedPlatforms.includes(this.platform)) {
throw new Error(`Platform ${this.platform} is not supported`); throw new Error(`Platform ${this.platform} is not supported`);
} }
dlopen(this.MoeHooExport, path.join(nodePath, './native/MoeHoo.win32.node'), constants.dlopen.RTLD_LAZY); let nativeNode = path.join(nodePath, './native/MoeHoo.win32.node');
if (fs.existsSync(nativeNode)) {
dlopen(this.MoeHooExport, nativeNode, constants.dlopen.RTLD_LAZY);
}
} catch (error) {
this.inited = false;
}
} }
isSetReCallEnabled(): boolean { isSetReCallEnabled(): boolean {
return this.recallHookEnabled; return this.recallHookEnabled && this.inited;
} }
registerRecallCallback(callback: (hex: string) => any): void { registerRecallCallback(callback: (hex: string) => any): void {
try {
if (!this.inited) throw new Error('Native Not Init');
if (this.MoeHooExport.exports?.registMsgPush) {
this.MoeHooExport.exports.registMsgPush(callback);
this.recallHookEnabled = true; this.recallHookEnabled = true;
return this.MoeHooExport.exports.registMsgPush(callback); }
} catch (error) {
this.recallHookEnabled = false;
}
} }
} }

View File

@@ -1,9 +1,16 @@
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { OB11ForwardMessage } from '@/onebot'; import { OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageForward, OB11MessageNode as OriginalOB11MessageNode } from '@/onebot';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/message-unique'; import { MessageUnique } from '@/common/message-unique';
type OB11MessageNode = OriginalOB11MessageNode & {
data: {
content?: Array<OB11MessageData>;
message: Array<OB11MessageData>;
};
};
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
properties: { properties: {
@@ -18,36 +25,69 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
actionName = ActionName.GoCQHTTP_GetForwardMsg; actionName = ActionName.GoCQHTTP_GetForwardMsg;
payloadSchema = SchemaData; payloadSchema = SchemaData;
private createTemplateNode(message: OB11Message): OB11MessageNode {
return {
type: OB11MessageDataType.node,
data: {
user_id: message.user_id,
nickname: message.sender.nickname,
message: [],
content: []
}
};
}
async parseForward(messages: OB11Message[]): Promise<OB11MessageNode[]> {
const retMsg: OB11MessageNode[] = [];
for (const message of messages) {
const templateNode = this.createTemplateNode(message);
for (const msgdata of message.message) {
if ((msgdata as OB11MessageData).type === OB11MessageDataType.forward) {
const newNode = this.createTemplateNode(message);
newNode.data.message = await this.parseForward((msgdata as OB11MessageForward).data.content);
templateNode.data.message.push(newNode);
} else {
templateNode.data.message.push(msgdata as OB11MessageData);
}
}
retMsg.push(templateNode);
}
return retMsg;
}
async _handle(payload: Payload): Promise<any> { async _handle(payload: Payload): Promise<any> {
const msgId = payload.message_id || payload.id; const msgId = payload.message_id || payload.id;
if (!msgId) { if (!msgId) {
throw Error('message_id is required'); throw new Error('message_id is required');
} }
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId); const rootMsgId = MessageUnique.getShortIdByMsgId(msgId);
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || parseInt(msgId)); const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || parseInt(msgId));
if (!rootMsg) { if (!rootMsg) {
throw Error('msg not found'); throw new Error('msg not found');
} }
const data = await this.core.apis.MsgApi.getMultiMsg(rootMsg.Peer, rootMsg.MsgId, rootMsg.MsgId); const data = await this.core.apis.MsgApi.getMsgsByMsgId(rootMsg.Peer, [rootMsg.MsgId]);
if (!data || data.result !== 0) { if (!data || data.result !== 0) {
throw Error('找不到相关的聊天记录' + data?.errMsg); throw new Error('找不到相关的聊天记录' + data?.errMsg);
} }
const msgList = data.msgList;
const messages = (await Promise.all(msgList.map(async msg => { const singleMsg = data.msgList[0];
const resMsg = await this.obContext.apis.MsgApi const resMsg = await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array');//强制array 以便处理
.parseMessage(msg); if (!resMsg) {
if (!resMsg) return; throw new Error('找不到相关的聊天记录');
resMsg.message_id = MessageUnique.createUniqueMsgId({ }
guildId: '', //if (this.obContext.configLoader.configData.messagePostFormat === 'array') {
chatType: msg.chatType, //提取
peerUid: msg.peerUid, let realmsg = ((await this.parseForward([resMsg]))[0].data.message as OB11MessageNode[])[0].data.message;
}, msg.msgId)!; //里面都是offline消息 id都是0 没得说话
return resMsg; return { message: realmsg };
}))).filter(msg => !!msg); //}
messages.map(msg => {
(<OB11ForwardMessage>msg).content = msg.message; // return { message: resMsg };
delete (<any>msg).message;
});
return { messages };
} }
} }

View File

@@ -139,8 +139,7 @@ export class OneBotGroupApi {
} }
if (element.grayTipElement.jsonGrayTipElement.busiId == 2407) { if (element.grayTipElement.jsonGrayTipElement.busiId == 2407) {
const type = json.items[json.items.length - 1]?.txt; const type = json.items[json.items.length - 1]?.txt;
switch (type) { if (type === "头衔") {
case "头衔": {
const memberUin = json.items[1].param[0]; const memberUin = json.items[1].param[0];
const title = json.items[3].txt; const title = json.items[3].txt;
logger.logDebug('收到群成员新头衔消息', json); logger.logDebug('收到群成员新头衔消息', json);
@@ -150,11 +149,10 @@ export class OneBotGroupApi {
parseInt(memberUin), parseInt(memberUin),
title, title,
); );
} } else if (type === "移出") {
case "移出":
logger.logDebug('收到机器人被踢消息', json); logger.logDebug('收到机器人被踢消息', json);
return; return;
default: } else {
logger.logWarn('收到未知的灰条消息', json); logger.logWarn('收到未知的灰条消息', json);
} }
} }

View File

@@ -34,7 +34,7 @@ import { RequestUtil } from '@/common/request';
import fs from 'node:fs'; import fs from 'node:fs';
import fsPromise from 'node:fs/promises'; import fsPromise from 'node:fs/promises';
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent'; import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
import { SysMessage, SysMessageType } from '@/core/proto/ProfileLike'; import { decodeSysMessage } from '@/core/proto/ProfileLike';
type RawToOb11Converters = { type RawToOb11Converters = {
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: ( [Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
@@ -841,7 +841,7 @@ export class OneBotMsgApi {
return { path, fileName: inputdata.name ?? fileName }; return { path, fileName: inputdata.name ?? fileName };
} }
async parseSysMessage(msg: number[]) { async parseSysMessage(msg: number[]) {
const sysMsg = SysMessage.decode(Uint8Array.from(msg)) as unknown as SysMessageType; const sysMsg = decodeSysMessage(Uint8Array.from(msg));
if (sysMsg.msgSpec.length === 0) { if (sysMsg.msgSpec.length === 0) {
return; return;
} }

View File

@@ -1,5 +1,5 @@
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
import { profileLikeTip, ProfileLikeTipType } from '@/core/proto/ProfileLike'; import { decodeProfileLikeTip } from '@/core/proto/ProfileLike';
import { NapCatOneBot11Adapter } from '@/onebot'; import { NapCatOneBot11Adapter } from '@/onebot';
import { OB11ProfileLikeEvent } from '../event/notice/OB11ProfileLikeEvent'; import { OB11ProfileLikeEvent } from '../event/notice/OB11ProfileLikeEvent';
@@ -13,7 +13,7 @@ export class OneBotUserApi {
this.core = core; this.core = core;
} }
async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> { async parseLikeEvent(wrappedBody: Uint8Array): Promise<OB11ProfileLikeEvent | undefined> {
const likeTip = profileLikeTip.decode(Uint8Array.from(wrappedBody)) as unknown as ProfileLikeTipType; const likeTip = decodeProfileLikeTip(Uint8Array.from(wrappedBody));
if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return; if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return;
this.core.context.logger.logDebug("收到点赞通知消息"); this.core.context.logger.logDebug("收到点赞通知消息");
const likeMsg = likeTip.content.msg; const likeMsg = likeTip.content.msg;

View File

@@ -45,7 +45,7 @@ import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecal
import { LRUCache } from '@/common/lru-cache'; import { LRUCache } from '@/common/lru-cache';
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener'; import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
import { Native } from '@/native'; import { Native } from '@/native';
import { Message, RecallGroup } from '@/core/proto/Message'; import { decodeMessage, decodeRecallGroup, Message, RecallGroup } from '@/core/proto/Message';
//OneBot实现类 //OneBot实现类
export class NapCatOneBot11Adapter { export class NapCatOneBot11Adapter {
@@ -73,16 +73,18 @@ export class NapCatOneBot11Adapter {
}; };
this.actions = createActionMap(this, core); this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager(); this.networkManager = new OB11NetworkManager();
this.registerNative(core, context).then().catch(); this.registerNative(core, context).catch(e => this.context.logger.logWarn.bind(this.context.logger)('初始化Native失败', e)).then();
this.InitOneBot() this.InitOneBot()
.catch(e => this.context.logger.logError.bind(this.context.logger)('初始化OneBot失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('初始化OneBot失败', e));
} }
async registerNative(core: NapCatCore, context: InstanceContext) { async registerNative(core: NapCatCore, context: InstanceContext) {
try {
this.nativeCore = new Native(context.pathWrapper.binaryPath); this.nativeCore = new Native(context.pathWrapper.binaryPath);
if (!this.nativeCore.inited) throw new Error('Native Not Init');
this.nativeCore.registerRecallCallback(async (hex: string) => { this.nativeCore.registerRecallCallback(async (hex: string) => {
try { try {
let data = Message.decode(Buffer.from(hex, 'hex')) as any; let data = decodeMessage(Buffer.from(hex, 'hex')) as any;
//data.MsgHead.BodyInner.MsgType SubType //data.MsgHead.BodyInner.MsgType SubType
let bodyInner = data.msgHead?.bodyInner; let bodyInner = data.msgHead?.bodyInner;
//context.logger.log("[appNative] Parse MsgType:" + bodyInner.msgType + " / SubType:" + bodyInner.subType); //context.logger.log("[appNative] Parse MsgType:" + bodyInner.msgType + " / SubType:" + bodyInner.subType);
@@ -91,35 +93,20 @@ export class NapCatOneBot11Adapter {
//跳过 4字节 群号 + 不知道的1字节 +2字节 长度 //跳过 4字节 群号 + 不知道的1字节 +2字节 长度
let uid = RecallData.readUint32BE(); let uid = RecallData.readUint32BE();
const buffer = Buffer.from(RecallData.toString('hex').slice(14), 'hex'); const buffer = Buffer.from(RecallData.toString('hex').slice(14), 'hex');
let seq: number = (RecallGroup.decode(buffer) as any).recallDetails.subDetail.msgSeq; let seq: number = decodeRecallGroup(buffer).recallDetails.subDetail.msgSeq;
let peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: uid.toString() }; let peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: uid.toString() };
context.logger.log("[Native] 群消息撤回 Peer: " + uid.toString() + " / MsgSeq:" + seq); context.logger.log("[Native] 群消息撤回 Peer: " + uid.toString() + " / MsgSeq:" + seq);
let msgs = await core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, seq.toString()); let msgs = await core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, seq.toString());
this.recallMsgCache.put(msgs.msgList[0].msgId, msgs.msgList[0]); this.recallMsgCache.put(msgs.msgList[0].msgId, msgs.msgList[0]);
// let ob11 = await this.apis.MsgApi.parseMessage(msgs.msgList[0], 'array')
// .catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e));
// if (ob11) {
// const { sendElements, deleteAfterSentFiles } = await this.apis.MsgApi.createSendElements(ob11.message as OB11MessageData[], peer);
// this.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles);
// }
// this.apis.MsgApi.sendMsg(peer, [{
// elementType: 1,
// elementId: '',
// textElement: {
// content: "[Native] 群消息撤回 Peer: " + uid.toString() + " / MsgSeq:" + seq,
// atType: 0,
// atUid: '',
// atTinyId: '',
// atNtUid: '',
// },
// }]);
} }
} catch (error: any) { } catch (error: any) {
context.logger.logWarn("[Native] Error:", (error as Error).message, ' HEX:', hex); context.logger.logWarn("[Native] Error:", (error as Error).message, ' HEX:', hex);
} }
}); });
} catch (error) {
context.logger.logWarn("[Native] Error:", (error as Error).message);
return;
}
} }
async InitOneBot() { async InitOneBot() {
const selfInfo = this.core.selfInfo; const selfInfo = this.core.selfInfo;
@@ -288,7 +275,7 @@ export class NapCatOneBot11Adapter {
msgListener.onRecvSysMsg = (msg) => { msgListener.onRecvSysMsg = (msg) => {
this.apis.MsgApi.parseSysMessage(msg).then((event) => { this.apis.MsgApi.parseSysMessage(msg).then((event) => {
if (event) this.networkManager.emitEvent(event); if (event) this.networkManager.emitEvent(event);
}).catch(e => this.context.logger.logError.bind(this.context.logger)('constructSysMessage error: ', e)); }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructSysMessage error: ', e, '\n Parse Hex:', Buffer.from(msg).toString('hex')));
}; };
msgListener.onInputStatusPush = async data => { msgListener.onInputStatusPush = async data => {

View File

@@ -69,18 +69,12 @@ export async function NCoreInitShell() {
const dataPathGlobal = path.resolve(dataPath, './nt_qq/global'); const dataPathGlobal = path.resolve(dataPath, './nt_qq/global');
return [dataPath, dataPathGlobal]; return [dataPath, dataPathGlobal];
})(); })();
let systemPlatform = PlatformType.KWINDOWS; const platformMapping: Partial<Record<NodeJS.Platform, PlatformType>> = {
switch (os.platform()) { win32: PlatformType.KWINDOWS,
case 'win32': darwin: PlatformType.KMAC,
systemPlatform = PlatformType.KWINDOWS; linux: PlatformType.KLINUX,
break; };
case 'darwin': const systemPlatform = platformMapping[os.platform()] ?? PlatformType.KWINDOWS;
systemPlatform = PlatformType.KMAC;
break;
case 'linux':
systemPlatform = PlatformType.KLINUX;
break;
}
if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined'); if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined');
// from initConfig // from initConfig
engine.initWithDeskTopConfig( engine.initWithDeskTopConfig(

View File

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

View File

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