Compare commits

...

51 Commits

Author SHA1 Message Date
手瓜一十雪
5e0b3b2f35 fix: fluent-ffmpeg 2024-11-14 15:43:11 +08:00
pk5ls20
6829fad5bd chore: workflow build check 2024-11-14 14:30:56 +08:00
pk5ls20
7af0d9e87b refactor: simplify code 2024-11-14 14:29:38 +08:00
Mlikiowa
c089ebea99 release: v4.0.0 2024-11-14 06:00:34 +00:00
手瓜一十雪
d2a2c1c39c fix: #50 2024-11-14 13:58:25 +08:00
手瓜一十雪
ce9b09e8d1 fix: 简化代码 2024-11-14 13:49:37 +08:00
手瓜一十雪
2f6dfe51f5 fix: error 2024-11-14 13:43:39 +08:00
手瓜一十雪
bd227cd0b8 refactor: getGroupHonorInfo 2024-11-14 13:42:03 +08:00
手瓜一十雪
96003724ab refactor: nc shell login 2024-11-14 13:40:01 +08:00
pk5ls20
6a08b15095 chore: eslint 2024-11-14 13:29:39 +08:00
手瓜一十雪
dab0f9ab45 refactor: getImageUrl 2024-11-14 13:26:12 +08:00
手瓜一十雪
e733a6b69a fix: error 2024-11-14 13:22:58 +08:00
手瓜一十雪
9aca98bf13 fix: type 2024-11-14 13:19:50 +08:00
手瓜一十雪
b7c95e53dc fix: unuse import 2024-11-14 13:14:27 +08:00
手瓜一十雪
f762c450ca fix: error 2024-11-14 13:10:52 +08:00
手瓜一十雪
d58bbe53da Merge pull request #521 from abc1763613206/main
feat: add `emoji_package_id` for MarketFace
2024-11-14 13:10:04 +08:00
abc1763613206
f32edd8af7 feat: add emoji_package_id for MarketFace 2024-11-14 13:07:57 +08:00
手瓜一十雪
c747a86e5b fix 2024-11-14 13:07:10 +08:00
手瓜一十雪
abfda0dd58 fix: || -> ?? 2024-11-14 13:00:25 +08:00
手瓜一十雪
f66d7b11a8 fix: error throw 2024-11-14 12:54:58 +08:00
手瓜一十雪
f425c9478e fix 2024-11-14 12:48:19 +08:00
手瓜一十雪
756dea71fc remove: todo -> work 2024-11-14 12:44:21 +08:00
手瓜一十雪
71a6c4ccc5 fix: error handle 2024-11-14 12:37:16 +08:00
手瓜一十雪
ae2f4777ec fix 2024-11-14 12:32:48 +08:00
手瓜一十雪
dcd9b8168a feat: any listener 2024-11-14 12:28:08 +08:00
手瓜一十雪
4bb03ae5ba fix 2024-11-14 12:22:28 +08:00
手瓜一十雪
8bd6f8397b fix: assertion is unnecessary 2024-11-14 12:07:26 +08:00
手瓜一十雪
096e52d93e fix: promise async 2024-11-14 12:06:45 +08:00
手瓜一十雪
037065291d fix: 代码精简 2024-11-14 12:02:24 +08:00
手瓜一十雪
4cf52e1b13 fix: error 2024-11-14 11:53:55 +08:00
手瓜一十雪
21b228552d fix 2024-11-14 11:46:37 +08:00
手瓜一十雪
76b404cdd8 rename: WsPacketClient 2024-11-14 11:40:16 +08:00
手瓜一十雪
937c594ff7 fix 2024-11-14 11:35:10 +08:00
手瓜一十雪
b463140de7 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-11-14 11:30:56 +08:00
手瓜一十雪
f518fb9214 fix: readonly 2024-11-14 11:28:06 +08:00
手瓜一十雪
1092831718 Merge pull request #520 from NapNeko/refactor-4.0.0
refactor: 4.0.0
2024-11-14 11:24:57 +08:00
手瓜一十雪
6b377416da refactor: fix 2024-11-14 11:24:00 +08:00
手瓜一十雪
8f5baa47ec refactor: log最佳实践 2024-11-14 11:10:26 +08:00
手瓜一十雪
5494ff0553 refactor: build 2024-11-14 11:03:11 +08:00
手瓜一十雪
7a4805b464 refactor: registerListen 2024-11-14 10:57:57 +08:00
手瓜一十雪
8435375810 style: lint 2024-11-14 10:53:50 +08:00
手瓜一十雪
c893ec6030 refactor: apiInit Refactor 2024-11-14 10:52:03 +08:00
手瓜一十雪
e8bf6fa0a6 style: lint 2024-11-14 10:45:16 +08:00
手瓜一十雪
f228129c19 refactor: Init Core 2024-11-14 10:43:37 +08:00
手瓜一十雪
cbf98ffb89 refactor: async Init 2024-11-14 10:36:51 +08:00
手瓜一十雪
f6067b002f refactor: Shell Init 2024-11-14 10:34:38 +08:00
手瓜一十雪
636d1103e3 refactor: 删除反撤回模块 未来合并到MoeHoo 2024-11-14 10:30:01 +08:00
手瓜一十雪
bede517f7e refactor: package 2024-11-14 10:27:10 +08:00
手瓜一十雪
16e4891b7d fix 2024-11-13 22:54:56 +08:00
手瓜一十雪
3bcd79fbb7 docs: 文档精简 2024-11-13 22:46:48 +08:00
Mlikiowa
aacf6c2917 release: v3.7.0 2024-11-13 09:53:56 +00:00
106 changed files with 912 additions and 1250 deletions

View File

@@ -1,5 +1,8 @@
name: "Build Action" name: "Build Action"
on: on:
push:
branches:
- main
workflow_dispatch: workflow_dispatch:
permissions: write-all permissions: write-all

View File

@@ -5,18 +5,16 @@
</div> </div>
--- ---
## 欢迎回 ## 欢迎回
NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现 NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现
## 猫猫技能 ## 碎碎叨叨
- [x] **启动方式**支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动 - [x] **安装简单**:就算是笨蛋也能使用
- [x] **覆盖平台**: 覆盖 Windows / Linux (可选 Docker) / Android Termux / MacOS - [x] **性能友好**:就算是低内存也能使用
- [x] **安装简单**: 支持一键脚本/程序自动部署/镜像部署等多种覆盖范围 - [x] **接口丰富**:就算是没有也能使用
- [x] **超低占用**无头模式占用资源极低,适合在服务器上运行 - [x] **稳定好用**就算是被捉也能使用
- [x] **超多接口**:实现大部分 OneBot 和 go-cqhttp 接口,超多扩展 API
- [x] **远程管理**:自带 WebUI 支持,远程管理更加便捷
## 使用猫猫 ## 使用框架
可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本 可前往 [Release](https://github.com/NapNeko/NapCatQQ/releases/) 页面下载最新版本
@@ -38,19 +36,15 @@ NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
## 回家旅途 ## 回家旅途
[QQ Group](https://qm.qq.com/q/VfjAq5HIMS) [QQ Group](https://qm.qq.com/q/VfjAq5HIMS)
[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) 对本项目的大力支持 参考部分代码 已获授权
不过最最重要的 还是需要感谢屏幕前的你哦~ 不过最最重要的 还是需要感谢屏幕前的你哦~
--- ---
## 约法三章 ## 开源附加
> [!CAUTION]\
> **请不要在 QQ 官方群聊和任何影响力较大的简中互联网平台(包括但不限于: 哔哩哔哩,微博,知乎,抖音等)发布和讨论*任何*与本项目存在相关性的信息**
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 NapCat 代码开发。** 任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经仓库主作者授权二次分发或基于 NapCat 代码开发。**

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "3.6.17", "version": "4.0.0",
"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": "3.6.17", "version": "4.0.0",
"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",
@@ -48,9 +48,9 @@
}, },
"dependencies": { "dependencies": {
"express": "^5.0.0", "express": "^5.0.0",
"fluent-ffmpeg": "^2.1.2",
"qrcode-terminal": "^0.12.0",
"silk-wasm": "^3.6.1", "silk-wasm": "^3.6.1",
"ws": "^8.18.0" "ws": "^8.18.0",
"qrcode-terminal": "^0.12.0",
"fluent-ffmpeg": "^2.1.2"
} }
} }

View File

@@ -21,9 +21,9 @@ type FuncKeys<T> = Extract<
export type ListenerClassBase = Record<string, string>; export type ListenerClassBase = Record<string, string>;
export class NTEventWrapper { export class NTEventWrapper {
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession private readonly WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
private listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例 private readonly listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); //ListenerName-Unique -> Listener实例
private EventTask = new Map<string, Map<string, Map<string, InternalMapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func} private readonly EventTask = new Map<string, Map<string, Map<string, InternalMapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
constructor( constructor(
wrapperSession: NodeIQQNTWrapperSession, wrapperSession: NodeIQQNTWrapperSession,
@@ -120,9 +120,9 @@ export class NTEventWrapper {
ListenerType extends (...args: any) => any = EnsureFunc<ListenerNamingMapping[Listener][ListenerMethod]>, ListenerType extends (...args: any) => any = EnsureFunc<ListenerNamingMapping[Listener][ListenerMethod]>,
>( >(
listenerAndMethod: `${Listener}/${ListenerMethod}`, listenerAndMethod: `${Listener}/${ListenerMethod}`,
checker: (...args: Parameters<ListenerType>) => boolean,
waitTimes = 1, waitTimes = 1,
timeout = 5000, timeout = 5000,
checker: (...args: Parameters<ListenerType>) => boolean,
) { ) {
return new Promise<Parameters<ListenerType>>((resolve, reject) => { return new Promise<Parameters<ListenerType>>((resolve, reject) => {
const ListenerNameList = listenerAndMethod.split('/'); const ListenerNameList = listenerAndMethod.split('/');
@@ -181,14 +181,12 @@ export class NTEventWrapper {
callbackTimesToWait = 1, callbackTimesToWait = 1,
timeout = 5000, timeout = 5000,
) { ) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
async (resolve, reject) => {
const id = randomUUID(); const id = randomUUID();
let complete = 0; let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined; let retData: Parameters<ListenerType> | undefined = undefined;
let retEvent: any = {}; let retEvent: any = {};
function sendDataCallback() { function sendDataCallback(resolve: any, reject: any) {
if (complete == 0) { if (complete == 0) {
reject( reject(
new Error( new Error(
@@ -210,7 +208,9 @@ export class NTEventWrapper {
const ListenerMainName = ListenerNameList[0]; const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1]; const ListenerSubName = ListenerNameList[1];
const timeoutRef = setTimeout(sendDataCallback, timeout); return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
(resolve, reject) => {
const timeoutRef = setTimeout(() => sendDataCallback(resolve, reject), timeout);
const eventCallback = { const eventCallback = {
timeout: timeout, timeout: timeout,
@@ -221,7 +221,7 @@ export class NTEventWrapper {
retData = args as Parameters<ListenerType>; retData = args as Parameters<ListenerType>;
if (complete >= callbackTimesToWait) { if (complete >= callbackTimesToWait) {
clearTimeout(timeoutRef); clearTimeout(timeoutRef);
sendDataCallback(); sendDataCallback(resolve, reject);
} }
}, },
}; };
@@ -233,8 +233,10 @@ export class NTEventWrapper {
} }
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback); this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback);
this.createListenerFunction(ListenerMainName); this.createListenerFunction(ListenerMainName);
const eventFunction = this.createEventFunction(serviceAndMethod);
retEvent = await eventFunction!(...(args)); this.createEventFunction(serviceAndMethod)!(...(args))
.then((eventResult: any) => {
retEvent = eventResult;
if (!checkerEvent(retEvent) && timeoutRef.hasRef()) { if (!checkerEvent(retEvent) && timeoutRef.hasRef()) {
clearTimeout(timeoutRef); clearTimeout(timeoutRef);
reject( reject(
@@ -249,7 +251,8 @@ export class NTEventWrapper {
), ),
); );
} }
})
.catch(reject);
}, },
); );
} }

View File

@@ -54,11 +54,7 @@ export class ForwardMsgBuilder {
const id = crypto.randomUUID(); const id = crypto.randomUUID();
const isGroupMsg = msg.some(m => m.isGroupMsg); const isGroupMsg = msg.some(m => m.isGroupMsg);
if (!source) { if (!source) {
source = isGroupMsg ? "群聊的聊天记录" : source = isGroupMsg ? "群聊的聊天记录" : msg.map(m => m.senderName).filter((v, i, a) => a.indexOf(v) === i).slice(0, 4).join('和') + '的聊天记录';
msg.length
? Array.from(new Set(msg.slice(0, 4).map(m => m.senderName)))
.join('和') + '的聊天记录'
: '聊天记录';
} }
if (!news) { if (!news) {
news = msg.length === 0 ? [{ news = msg.length === 0 ? [{
@@ -111,7 +107,7 @@ export class ForwardMsgBuilder {
senderName: msg.senderName, senderName: msg.senderName,
isGroupMsg: msg.groupId !== undefined, isGroupMsg: msg.groupId !== undefined,
msg: msg.msg.map(m => ({ msg: msg.msg.map(m => ({
preview: m.valid? m.toPreview() : "[该消息类型暂不支持查看]", preview: m.valid ? m.toPreview() : "[该消息类型暂不支持查看]",
})) }))
})), source, news, summary, prompt); })), source, news, summary, prompt);
} }

View File

@@ -74,6 +74,12 @@ export class LogWrapper {
} }
files.forEach(file => { files.forEach(file => {
const filePath = path.join(logDir, file); const filePath = path.join(logDir, file);
this.deleteOldLogFile(filePath, oneWeekAgo);
});
});
}
private deleteOldLogFile(filePath: string, oneWeekAgo: number) {
fs.stat(filePath, (err, stats) => { fs.stat(filePath, (err, stats) => {
if (err) { if (err) {
this.logger.error('Failed to get file stats', err); this.logger.error('Failed to get file stats', err);
@@ -83,18 +89,16 @@ export class LogWrapper {
fs.unlink(filePath, err => { fs.unlink(filePath, err => {
if (err) { if (err) {
if (err.code === 'ENOENT') { if (err.code === 'ENOENT') {
this.logger.warn(`File already deleted: ${file}`); this.logger.warn(`File already deleted: ${filePath}`);
} else { } else {
this.logger.error('Failed to delete old log file', err); this.logger.error('Failed to delete old log file', err);
} }
} else { } else {
this.logger.info(`Deleted old log file: ${file}`); this.logger.info(`Deleted old log file: ${filePath}`);
} }
}); });
} }
}); });
});
});
} }
setFileAndConsoleLogLevel(fileLogLevel: LogLevel, consoleLogLevel: LogLevel) { setFileAndConsoleLogLevel(fileLogLevel: LogLevel, consoleLogLevel: LogLevel) {
@@ -198,7 +202,7 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
tokens.push(`群聊 [${msg.peerName}(${msg.peerUin})]`); tokens.push(`群聊 [${msg.peerName}(${msg.peerUin})]`);
} }
if (msg.senderUin !== '0') { if (msg.senderUin !== '0') {
tokens.push(`[${msg.sendMemberName || msg.sendRemarkName || msg.sendNickName}(${msg.senderUin})]`); 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('移动设备');
@@ -206,28 +210,20 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
tokens.push(`临时消息 (${msg.peerUin})`); tokens.push(`临时消息 (${msg.peerUin})`);
} }
function msgElementToText(element: MessageElement) { for (const element of msg.elements) {
if (element.textElement) { tokens.push(msgElementToText(element, msg, recursiveLevel));
if (element.textElement.atType === AtType.notAt) {
const originalContentLines = element.textElement.content.split('\n');
return `${originalContentLines[0]}${originalContentLines.length > 1 ? ' ...' : ''}`;
} else if (element.textElement.atType === AtType.atAll) {
return `@全体成员`;
} else if (element.textElement.atType === AtType.atUser) {
return `${element.textElement.content} (${element.textElement.atUid})`;
} }
return tokens.join(' ');
}
function msgElementToText(element: MessageElement, msg: RawMessage, recursiveLevel: number): string {
if (element.textElement) {
return textElementToText(element.textElement);
} }
if (element.replyElement) { if (element.replyElement) {
const recordMsgOrNull = msg.records.find( return replyElementToText(element.replyElement, msg, recursiveLevel);
record => element.replyElement!.sourceMsgIdInRecords === record.msgId,
);
return `[回复消息 ${recordMsgOrNull &&
recordMsgOrNull.peerUin != '284840486' && recordMsgOrNull.peerUin != '1094950020'
?
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
}]`;
} }
if (element.picElement) { if (element.picElement) {
@@ -271,11 +267,28 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
} }
return `[未实现 (ElementType = ${element.elementType})]`; return `[未实现 (ElementType = ${element.elementType})]`;
} }
for (const element of msg.elements) { function textElementToText(textElement: any): string {
tokens.push(msgElementToText(element)); if (textElement.atType === AtType.notAt) {
} const originalContentLines = textElement.content.split('\n');
return `${originalContentLines[0]}${originalContentLines.length > 1 ? ' ...' : ''}`;
return tokens.join(' '); } else if (textElement.atType === AtType.atAll) {
return `@全体成员`;
} else if (textElement.atType === AtType.atUser) {
return `${textElement.content} (${textElement.atUid})`;
}
return '';
}
function replyElementToText(replyElement: any, msg: RawMessage, recursiveLevel: number): string {
const recordMsgOrNull = msg.records.find(
record => replyElement.sourceMsgIdInRecords === record.msgId,
);
return `[回复消息 ${recordMsgOrNull &&
recordMsgOrNull.peerUin != '284840486' && recordMsgOrNull.peerUin != '1094950020'
?
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
`未找到消息记录 (MsgId = ${replyElement.sourceMsgIdInRecords})`
}]`;
} }

View File

@@ -30,4 +30,13 @@ export class LRUCache<K, V> {
} }
this.cache.set(key, value); this.cache.set(key, value);
} }
public resetCapacity(newCapacity: number): void {
this.capacity = newCapacity;
while (this.cache.size > this.capacity) {
const firstKey = this.cache.keys().next().value;
if (firstKey !== undefined) {
this.cache.delete(firstKey);
}
}
}
} }

View File

@@ -2,8 +2,8 @@ import { Peer } from '@/core';
import crypto from 'crypto'; import crypto from 'crypto';
export class LimitedHashTable<K, V> { export class LimitedHashTable<K, V> {
private keyToValue: Map<K, V> = new Map(); private readonly keyToValue: Map<K, V> = new Map();
private valueToKey: Map<V, K> = new Map(); private readonly valueToKey: Map<V, K> = new Map();
private maxSize: number; private maxSize: number;
constructor(maxSize: number) { constructor(maxSize: number) {
@@ -75,8 +75,8 @@ export class LimitedHashTable<K, V> {
} }
class MessageUniqueWrapper { class MessageUniqueWrapper {
private msgDataMap: LimitedHashTable<string, number>; private readonly msgDataMap: LimitedHashTable<string, number>;
private msgIdMap: LimitedHashTable<string, number>; private readonly msgIdMap: LimitedHashTable<string, number>;
constructor(maxMap: number = 1000) { constructor(maxMap: number = 1000) {
this.msgIdMap = new LimitedHashTable<string, number>(maxMap); this.msgIdMap = new LimitedHashTable<string, number>(maxMap);

View File

@@ -8,34 +8,40 @@ export class RequestUtil {
const client = url.startsWith('https') ? https : http; const client = url.startsWith('https') ? https : http;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const req = client.get(url, (res) => { const req = client.get(url, (res) => {
let cookies: { [key: string]: string } = {}; const cookies: { [key: string]: string } = {};
const handleRedirect = (res: http.IncomingMessage) => {
//console.log(res.headers.location); res.on('data', () => { }); // Necessary to consume the stream
res.on('end', () => {
this.handleRedirect(res, url, cookies)
.then(resolve)
.catch(reject);
});
if (res.headers['set-cookie']) {
this.extractCookies(res.headers['set-cookie'], cookies);
}
});
req.on('error', (error: Error) => {
reject(error);
});
});
}
private static async handleRedirect(res: http.IncomingMessage, url: string, cookies: { [key: string]: string }): Promise<{ [key: string]: string }> {
if (res.statusCode === 301 || res.statusCode === 302) { if (res.statusCode === 301 || res.statusCode === 302) {
if (res.headers.location) { if (res.headers.location) {
const redirectUrl = new URL(res.headers.location, url); const redirectUrl = new URL(res.headers.location, url);
RequestUtil.HttpsGetCookies(redirectUrl.href).then((redirectCookies) => { const redirectCookies = await this.HttpsGetCookies(redirectUrl.href);
// 合并重定向过程中的cookies // 合并重定向过程中的cookies
cookies = { ...cookies, ...redirectCookies }; return { ...cookies, ...redirectCookies };
resolve(cookies);
}).catch((err) => {
reject(err);
});
} else {
resolve(cookies);
} }
} else {
resolve(cookies);
} }
}; return cookies;
res.on('data', () => { }
}); // Necessary to consume the stream
res.on('end', () => { private static extractCookies(setCookieHeaders: string[], cookies: { [key: string]: string }) {
handleRedirect(res); setCookieHeaders.forEach((cookie) => {
});
if (res.headers['set-cookie']) {
//console.log(res.headers['set-cookie']);
res.headers['set-cookie'].forEach((cookie) => {
const parts = cookie.split(';')[0].split('='); const parts = cookie.split(';')[0].split('=');
const key = parts[0]; const key = parts[0];
const value = parts[1]; const value = parts[1];
@@ -44,13 +50,6 @@ export class RequestUtil {
} }
}); });
} }
});
req.on('error', (error: any) => {
reject(error);
});
});
}
// 请求和回复都是JSON data传原始内容 自动编码json // 请求和回复都是JSON data传原始内容 自动编码json
static async HttpGetJson<T>(url: string, method: string = 'GET', data?: any, headers: { static async HttpGetJson<T>(url: string, method: string = 'GET', data?: any, headers: {
@@ -88,13 +87,13 @@ export class RequestUtil {
} else { } else {
reject(new Error(`Unexpected status code: ${res.statusCode}`)); reject(new Error(`Unexpected status code: ${res.statusCode}`));
} }
} catch (parseError) { } catch (parseError: unknown) {
reject(parseError); reject(new Error((parseError as Error).message));
} }
}); });
}); });
req.on('error', (error: any) => { req.on('error', (error: Error) => {
reject(error); reject(error);
}); });
if (method === 'POST' || method === 'PUT' || method === 'PATCH') { if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
@@ -133,62 +132,4 @@ export class RequestUtil {
Buffer.from(footer, 'utf8'), Buffer.from(footer, 'utf8'),
]); ]);
} }
static async uploadImageForOpenPlatform(filePath: string, cookies: string): Promise<string> {
return new Promise(async (resolve, reject) => {
type retType = { retcode: number, result?: { url: string } };
try {
const options = {
hostname: 'cgi.connect.qq.com',
port: 443,
path: '/qqconnectopen/upload_share_image',
method: 'POST',
headers: {
'Referer': 'https://cgi.connect.qq.com',
'Cookie': cookies,
'Accept': '*/*',
'Connection': 'keep-alive',
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW',
},
};
const req = https.request(options, async (res) => {
let responseBody = '';
res.on('data', (chunk: string | Buffer) => {
responseBody += chunk.toString();
});
res.on('end', () => {
try {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
const responseJson = JSON.parse(responseBody) as retType;
resolve(responseJson.result!.url!);
} else {
reject(new Error(`Unexpected status code: ${res.statusCode}`));
}
} catch (parseError) {
reject(parseError);
}
});
});
req.on('error', (error) => {
reject(error);
console.log('Error during upload:', error);
});
const body = await RequestUtil.createFormData('WebKitFormBoundary7MA4YWxkTrZu0gW', filePath);
// req.setHeader('Content-Length', Buffer.byteLength(body));
// console.log(`Prepared data size: ${Buffer.byteLength(body)} bytes`);
req.write(body);
req.end();
return;
} catch (error) {
reject(error);
}
return undefined;
});
}
} }

View File

@@ -1 +1 @@
export const napCatVersion = '3.6.17'; export const napCatVersion = '4.0.0';

View File

@@ -20,7 +20,7 @@ export async function getVideoInfo(filePath: string, logger: LogWrapper) {
ffmpeg.setFfmpegPath(ffmpegPath); ffmpeg.setFfmpegPath(ffmpegPath);
ffmpeg(filePath).ffprobe((err: any, metadata: ffmpeg.FfprobeData) => { ffmpeg(filePath).ffprobe((err: any, metadata: ffmpeg.FfprobeData) => {
if (err) { if (err) {
reject(err); reject(new Error('无法获取视频信息。'));
} else { } else {
const videoStream = metadata.streams.find((s: FfprobeStream) => s.codec_type === 'video'); const videoStream = metadata.streams.find((s: FfprobeStream) => s.codec_type === 'video');
if (videoStream) { if (videoStream) {

View File

@@ -6,8 +6,10 @@ export class NodeIDependsAdapter {
} }
onMSFSsoError(args: unknown) { onMSFSsoError(args: unknown) {
} }
getGroupCode(args: unknown) { getGroupCode(args: unknown) {
} }
} }

View File

@@ -357,16 +357,14 @@ export class NTQQFileApi {
async getImageSize(filePath: string): Promise<ISizeCalculationResult> { async getImageSize(filePath: string): Promise<ISizeCalculationResult> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
imageSize(filePath, (err, dimensions) => { imageSize(filePath, (err: Error | null, dimensions) => {
if (err) { if (err) {
reject(err); reject(new Error(err.message));
} else { } else if (!dimensions) {
if (!dimensions) {
reject(new Error('获取图片尺寸失败')); reject(new Error('获取图片尺寸失败'));
} else { } else {
resolve(dimensions); resolve(dimensions);
} }
}
}); });
}); });
} }
@@ -408,26 +406,31 @@ export class NTQQFileApi {
return fileData.filePath!; return fileData.filePath!;
} }
async getImageUrl(element: PicElement) { async getImageUrl(element: PicElement): Promise<string> {
if (!element) { if (!element) {
return ''; return '';
} }
const url: string = element.originImageUrl ?? ''; const url: string = element.originImageUrl ?? '';
const md5HexStr = element.md5HexStr; const md5HexStr = element.md5HexStr;
const fileMd5 = element.md5HexStr; const fileMd5 = element.md5HexStr;
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 rkeyData = await this.getRkeyData();
const imageAppid = parsedUrl.searchParams.get('appid'); return this.getImageUrlFromParsedUrl(parsedUrl, rkeyData);
const isNTV2 = imageAppid && ['1406', '1407'].includes(imageAppid); }
const imageFileId = parsedUrl.searchParams.get('fileid');
return this.getImageUrlFromMd5(fileMd5, md5HexStr);
}
private async getRkeyData() {
const rkeyData = { const rkeyData = {
private_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qEc3Rbib9LP4', private_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qEc3Rbib9LP4',
group_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qffcqm614gds', group_rkey: 'CAQSKAB6JWENi5LM_xp9vumLbuThJSaYf-yzMrbZsuq7Uz2qffcqm614gds',
online_rkey: false online_rkey: false
}; };
try { try {
if (this.core.apis.PacketApi.available) { if (this.core.apis.PacketApi.available) {
const rkey_expired_private = !this.packetRkey || this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000; const rkey_expired_private = !this.packetRkey || this.packetRkey[0].time + Number(this.packetRkey[0].ttl) < Date.now() / 1000;
@@ -455,23 +458,35 @@ export class NTQQFileApi {
this.context.logger.logError.bind(this.context.logger)('获取rkey失败 Fallback Old Mode', e); this.context.logger.logError.bind(this.context.logger)('获取rkey失败 Fallback Old Mode', e);
} }
} }
return rkeyData;
}
private getImageUrlFromParsedUrl(parsedUrl: URL, rkeyData: any): string {
const urlRkey = parsedUrl.searchParams.get('rkey');
const imageAppid = parsedUrl.searchParams.get('appid');
const isNTV2 = imageAppid && ['1406', '1407'].includes(imageAppid);
const imageFileId = parsedUrl.searchParams.get('fileid');
if (isNTV2 && urlRkey) { if (isNTV2 && urlRkey) {
return IMAGE_HTTP_HOST_NT + urlRkey; return IMAGE_HTTP_HOST_NT + urlRkey;
} else if (isNTV2 && rkeyData.online_rkey) { } else if (isNTV2 && rkeyData.online_rkey) {
const rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey; const rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST_NT + url + `&rkey=${rkey}`; return IMAGE_HTTP_HOST_NT + parsedUrl.pathname + `&rkey=${rkey}`;
} else if (isNTV2 && imageFileId) { } else if (isNTV2 && imageFileId) {
const rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey; const rkey = imageAppid === '1406' ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST + `/download?appid=${imageAppid}&fileid=${imageFileId}&rkey=${rkey}`; return IMAGE_HTTP_HOST + `/download?appid=${imageAppid}&fileid=${imageFileId}&rkey=${rkey}`;
} }
return '';
} }
//到这里说明可能是旧客户端
private getImageUrlFromMd5(fileMd5: string | undefined, md5HexStr: string | undefined): string {
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获取失败', { fileMd5, md5HexStr });
return ''; return '';
} }
} }

View File

@@ -15,7 +15,7 @@ export class NTQQFriendApi {
} }
async getBuddyV2SimpleInfoMap(refresh = false) { async getBuddyV2SimpleInfoMap(refresh = false) {
const buddyService = this.context.session.getBuddyService(); const buddyService = this.context.session.getBuddyService();
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL); const buddyListV2 = await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
const uids = buddyListV2.data.flatMap(item => item.buddyUids); const uids = buddyListV2.data.flatMap(item => item.buddyUids);
return await this.core.eventWrapper.callNoListenerEvent( return await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelProfileService/getCoreAndBaseInfo', 'NodeIKernelProfileService/getCoreAndBaseInfo',
@@ -41,14 +41,10 @@ export class NTQQFriendApi {
tempBothDel: tempBothDel tempBothDel: tempBothDel
}); });
} }
async getBuddyV2ExWithCate(refresh = false) { async getBuddyV2ExWithCate() {
const categoryMap: Map<string, any> = new Map();
const buddyService = this.context.session.getBuddyService(); const buddyService = this.context.session.getBuddyService();
const buddyListV2 = refresh ? (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data : (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data; const buddyListV2 = (await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL)).data;
const uids = buddyListV2.flatMap(item => { const uids = buddyListV2.flatMap(item => {
item.buddyUids.forEach(uid => {
categoryMap.set(uid, { categoryId: item.categoryId, categoryName: item.categroyName });
});
return item.buddyUids; return item.buddyUids;
}); });
const data = await this.core.eventWrapper.callNoListenerEvent( const data = await this.core.eventWrapper.callNoListenerEvent(

View File

@@ -25,9 +25,10 @@ export class NTQQGroupApi {
constructor(context: InstanceContext, core: NapCatCore) { constructor(context: InstanceContext, core: NapCatCore) {
this.context = context; this.context = context;
this.core = core; this.core = core;
this.initCache().then().catch(context.logger.logError.bind(context.logger));
} }
async initApi() {
this.initCache().then().catch(this.context.logger.logError.bind(this.context.logger));
}
async initCache() { async initCache() {
this.groups = await this.getGroups(); this.groups = await this.getGroups();
for (const group of this.groups) { for (const group of this.groups) {
@@ -54,7 +55,7 @@ export class NTQQGroupApi {
}, pskey); }, pskey);
} }
async getGroupShutUpMemberList(groupCode: string) { async getGroupShutUpMemberList(groupCode: string) {
const data = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onShutUpMemberListChanged', 1, 1000, (group_id) => group_id === groupCode); const data = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onShutUpMemberListChanged', (group_id) => group_id === groupCode, 1, 1000);
this.context.session.getGroupService().getGroupShutUpMemberList(groupCode); this.context.session.getGroupService().getGroupShutUpMemberList(groupCode);
return (await data)[1]; return (await data)[1];
} }
@@ -258,9 +259,9 @@ export class NTQQGroupApi {
async getGroupMemberV2(GroupCode: string, uid: string, forced = false) { async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
const Listener = this.core.eventWrapper.registerListen( const Listener = this.core.eventWrapper.registerListen(
'NodeIKernelGroupListener/onMemberInfoChange', 'NodeIKernelGroupListener/onMemberInfoChange',
(params, _, members) => params === GroupCode && members.size > 0,
1, 1,
forced ? 5000 : 250, forced ? 5000 : 250,
(params, _, members) => params === GroupCode && members.size > 0,
); );
const retData = await ( const retData = await (
this.core.eventWrapper this.core.eventWrapper
@@ -318,13 +319,13 @@ export class NTQQGroupApi {
return undefined; return undefined;
} }
async tryGetGroupMembersV2(modeListener = false, groupQQ: string, num = 30, timeout = 100): Promise<{ async tryGetGroupMembersV2(groupQQ: string, modeListener = false, num = 30, timeout = 100): Promise<{
infos: Map<string, GroupMember>; infos: Map<string, GroupMember>;
finish: boolean; finish: boolean;
hasNext: boolean | undefined; hasNext: boolean | undefined;
}> { }> {
const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow_1'); const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow_1');
const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', 0, timeout, (params) => params.sceneId === sceneId) const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', (params) => params.sceneId === sceneId, 0, timeout)
.catch(() => { }); .catch(() => { });
const result = await this.context.session.getGroupService().getNextMemberList(sceneId, undefined, num); const result = await this.context.session.getGroupService().getNextMemberList(sceneId, undefined, num);
if (result.errCode !== 0) { if (result.errCode !== 0) {
@@ -352,7 +353,7 @@ export class NTQQGroupApi {
listenerMode: boolean; listenerMode: boolean;
}> { }> {
const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow_1'); const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow_1');
const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', 0, timeout, (params) => params.sceneId === sceneId) const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', (params) => params.sceneId === sceneId, 0, timeout)
.catch(() => { }); .catch(() => { });
const result = await this.context.session.getGroupService().getNextMemberList(sceneId, undefined, num); const result = await this.context.session.getGroupService().getNextMemberList(sceneId, undefined, num);
if (result.errCode !== 0) { if (result.errCode !== 0) {
@@ -371,7 +372,7 @@ export class NTQQGroupApi {
infos: new Map([...(resMode2?.infos ?? []), ...result.result.infos]), infos: new Map([...(resMode2?.infos ?? []), ...result.result.infos]),
finish: result.result.finish, finish: result.result.finish,
hasNext: resMode2?.hasNext, hasNext: resMode2?.hasNext,
listenerMode: resMode2?.hasNext !== undefined ? true : false listenerMode: resMode2?.hasNext !== undefined
}; };
} }

View File

@@ -4,5 +4,4 @@ export * from './group';
export * from './msg'; export * from './msg';
export * from './user'; export * from './user';
export * from './webapi'; export * from './webapi';
export * from './sign';
export * from './system'; export * from './system';

View File

@@ -144,7 +144,7 @@ export class NTQQMsgApi {
params, params,
], ],
() => true, () => true,
() => true, // Todo: 应当通过 groupFileListResult 判断 () => true, // 应当通过 groupFileListResult 判断
1, 1,
5000, 5000,
); );
@@ -194,7 +194,7 @@ export class NTQQMsgApi {
async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) { async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
//唉?!我有个想法 //唉?!我有个想法
if (peer.chatType === ChatType.KCHATTYPETEMPC2CFROMGROUP && peer.guildId && peer.guildId !== '') { if (peer.chatType === ChatType.KCHATTYPETEMPC2CFROMGROUP && peer.guildId && peer.guildId !== '') {
const member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid!); const member = await this.core.apis.GroupApi.getGroupMember(peer.guildId, peer.peerUid);
if (member) { if (member) {
await this.PrepareTempChat(peer.peerUid, peer.guildId, member.nick); await this.PrepareTempChat(peer.peerUid, peer.guildId, member.nick);
} }

View File

@@ -26,14 +26,15 @@ export class NTQQPacketApi {
this.context = context; this.context = context;
this.core = core; this.core = core;
this.logger = core.context.logger; this.logger = core.context.logger;
this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion()) }
async initApi() {
await this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion())
.then() .then()
.catch((err) => { .catch((err) => {
this.logger.logError.bind(this.core.context.logger); this.logger.logError.bind(this.core.context.logger);
this.errStack.push(err); this.errStack.push(err);
}); });
} }
get available(): boolean { get available(): boolean {
return this.pkt?.available ?? false; return this.pkt?.available ?? false;
} }

View File

@@ -1,5 +1,3 @@
import { RequestUtil } from '@/common/request';
import { MiniAppLuaJsonType } from '@/core';
import { InstanceContext, NapCatCore } from '..'; import { InstanceContext, NapCatCore } from '..';
export class NTQQMusicSignApi { export class NTQQMusicSignApi {
@@ -10,210 +8,6 @@ export class NTQQMusicSignApi {
this.context = context; this.context = context;
this.core = core; this.core = core;
} }
async signMiniApp(CardData: MiniAppLuaJsonType) {
// {
// "app": "com.tencent.miniapp.lua",
// "bizsrc": "tianxuan.imgJumpArk",
// "view": "miniapp",
// "prompt": "hi! 这里有我的日常故事,只想讲给你听",
// "config": {
// "type": "normal",
// "forward": 1,
// "autosize": 0
// },
// "meta": {
// "miniapp": {
// "title": "hi! 这里有我的日常故事,只想讲给你听",
// "preview": "https:\/\/tianquan.gtimg.cn\/qqAIAgent\/item\/7\/square.png",
// "jumpUrl": "https:\/\/club.vip.qq.com\/transfer?open_kuikly_info=%7B%22version%22%3A%20%221%22%2C%22src_type%22%3A%20%22web%22%2C%22kr_turbo_display%22%3A%20%221%22%2C%22page_name%22%3A%20%22vas_ai_persona_moments%22%2C%22bundle_name%22%3A%20%22vas_ai_persona_moments%22%7D&page_name=vas_ai_persona_moments&enteranceId=share&robot_uin=3889008584",
// "tag": "QQ智能体",
// "tagIcon": "https:\/\/tianquan.gtimg.cn\/shoal\/qqAIAgent\/3e9d70c9-d98c-45b8-80b4-79d82971b514.png",
// "source": "QQ智能体",
// "sourcelogo": "https:\/\/tianquan.gtimg.cn\/shoal\/qqAIAgent\/3e9d70c9-d98c-45b8-80b4-79d82971b514.png"
// }
// }
// }
// token : function(url,skey){
// var str = skey || cookie('skey') || cookie('rv2') || '',
// hash = 5381;
// if(url){
// var hostname = uri(url).hostname;
// if(hostname.indexOf('qun.qq.com') > -1 || (hostname.indexOf('qzone.qq.com') > -1 && hostname.indexOf('qun.qzone.qq.com') === -1)){
// str = cookie('p_skey') || str;
// }
// }
// for(var i = 0, len = str.length; i < len; ++i){
// hash += (hash << 5) + str.charAt(i).charCodeAt();
// }
// return hash & 0x7fffffff;
// },
//
// function signToken(skey: string) {
// let hash = 5381;
// for (let i = 0, len = skey.length; i < len; ++i) {
// hash += (hash << 5) + skey.charCodeAt(i);
// }
// return hash & 0x7fffffff;
// }
const signCard = {
'app': 'com.tencent.miniapp.lua',
'bizsrc': 'tianxuan.imgJumpArk',
'view': 'miniapp',
'prompt': CardData.prompt,
'config': {
'type': 'normal',
'forward': 1,
'autosize': 0,
},
'meta': {
'miniapp': {
'title': CardData.title,
'preview': (CardData.preview as string).replace(/\\/g, '\\/\\/'),
'jumpUrl': (CardData.jumpUrl as string).replace(/\\/g, '\\/\\/'),
'tag': CardData.tag,
'tagIcon': (CardData.tagIcon as string).replace(/\\/g, '\\/\\/'),
'source': CardData.source,
'sourcelogo': (CardData.sourcelogo as string).replace(/\\/g, '\\/\\/'),
},
},
};
// let signCard = {
// "app": "com.tencent.eventshare.lua",
// "prompt": "Bot Test",
// "bizsrc": "tianxuan.business",
// "meta": {
// "eventshare": {
// "button1URL": "https://www.bilibili.com",
// "button1disable": false,
// "button1title": "点我前往",
// "button2URL": "",
// "button2disable": false,
// "button2title": "",
// "buttonNum": 1,
// "jumpURL": "https://www.bilibili.com",
// "preview": "https://tianquan.gtimg.cn/shoal/card/9930bc4e-4a92-4da3-814f-8094a2421d9c.png",
// "tag": "QQ集卡",
// "tagIcon": "https://tianquan.gtimg.cn/shoal/card/c034854b-102d-40be-a545-5ca90a7c49c9.png",
// "title": "Bot Test"
// }
// },
// "config": {
// "autosize": 0,
// "collect": 0,
// "ctime": 1716568575,
// "forward": 1,
// "height": 336,
// "reply": 0,
// "round": 1,
// "type": "normal",
// "width": 263
// },
// "view": "eventshare",
// "ver": "0.0.0.1"
// };
const data = (await this.core.apis.UserApi.getQzoneCookies());
const Bkn = this.core.apis.WebApi.getBknFromCookie(data.p_skey);
const CookieValue = 'p_skey=' + data.p_skey + '; skey=' + data.skey + '; p_uin=o' + this.core.selfInfo.uin + '; uin=o' + this.core.selfInfo.uin;
const signurl = 'https://h5.qzone.qq.com/v2/vip/tx/trpc/ark-share/GenNewSignedArk?g_tk=' + Bkn + '&ark=' + encodeURIComponent(JSON.stringify(signCard));
let signed_ark = '';
try {
const retData = await RequestUtil.HttpGetJson<{
code: number,
data: { signed_ark: string }
}>(signurl, 'GET', undefined, { Cookie: CookieValue });
//logDebug('MiniApp JSON 消息生成成功', retData);
signed_ark = retData.data.signed_ark;
} catch (error) {
this.context.logger.logDebug('MiniApp JSON 消息生成失败', error);
}
return signed_ark;
}
async signInternal(songname: string, singer: string, cover: string, songmid: string, songmusic: string) {
//curl -X POST 'https://mqq.reader.qq.com/api/mqq/share/card?accessToken&_csrfToken&source=c0003' -H 'Content-Type: application/json' -H 'Cookie: uin=o10086' -d '{"app":"com.tencent.qqreader.share","config":{"ctime":1718634110,"forward":1,"token":"9a63343c32d5a16bcde653eb97faa25d","type":"normal"},"extra":{"app_type":1,"appid":100497308,"msg_seq":14386738075403815000.0,"uin":1733139081},"meta":{"music":{"action":"","android_pkg_name":"","app_type":1,"appid":100497308,"ctime":1718634110,"desc":"周杰伦","jumpUrl":"https://i.y.qq.com/v8/playsong.html?songmid=0039MnYb0qxYhV&type=0","musicUrl":"http://ws.stream.qqmusic.qq.com/http://isure6.stream.qqmusic.qq.com/M800002202B43Cq4V4.mp3?fromtag=810033622&guid=br_xzg&trace=23fe7bcbe2336bbf&uin=553&vkey=CF0F5CE8B0FA16F3001F8A88D877A217EB5E4F00BDCEF1021EB6C48969CA33C6303987AEECE9CC840122DD2F917A59D6130D8A8CA4577C87","preview":"https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg","cover":"https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg","sourceMsgId":"0","source_icon":"https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0","source_url":"","tag":"QQ音乐","title":"晴天","uin":10086}},"prompt":"[分享]晴天","ver":"0.0.0.1","view":"music"}'
const signurl = 'https://mqq.reader.qq.com/api/mqq/share/card?accessToken&_csrfToken&source=c0003';
//let = "https://y.qq.com/music/photo_new/T002R800x800M000000MkMni19ClKG.jpg";
const signCard = {
app: 'com.tencent.qqreader.share',
config: {
ctime: 1718634110,
forward: 1,
token: '9a63343c32d5a16bcde653eb97faa25d',
type: 'normal',
},
extra: {
app_type: 1,
appid: 100497308,
msg_seq: 14386738075403815000,
uin: 1733139081,
},
meta: {
music: {
action: '',
android_pkg_name: '',
app_type: 1,
appid: 100497308,
ctime: 1718634110,
desc: singer,
jumpUrl: 'https://i.y.qq.com/v8/playsong.html?songmid=' + songmid + '&type=0',
musicUrl: songmusic,
preview: cover,
cover: cover,
sourceMsgId: '0',
source_icon: 'https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0',
source_url: '',
tag: 'QQ音乐',
title: songname,
uin: 10086,
},
},
prompt: '[分享]' + songname,
ver: '0.0.0.1',
view: 'music',
};
//console.log(JSON.stringify(signCard, null, 2));
const data = await RequestUtil.HttpGetJson<{ code: number, data: { arkResult: string } }>
(signurl, 'POST', signCard, { 'Cookie': 'uin=o10086', 'Content-Type': 'application/json' });
return data;
}
//注意处理错误
async signWay03(id: string = '', mid: string = '') {
let signedMid;
if (mid == '') {
const MusicInfo = await RequestUtil.HttpGetJson<{
songinfo?: {
data?: {
track_info: {
mid: string
}
}
}
}>(
'https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&data={"comm":{"ct":24,"cv":0},"songinfo":{"method":"get_song_detail_yqq","param":{"song_type":0,"song_mid":"","song_id":' + id + '},"module":"music.pf_song_detail_svr"}}',
'GET',
undefined,
);
signedMid = MusicInfo.songinfo?.data?.track_info.mid;
}
//第三方接口 存在速率限制 现在勉强用
const MusicReal = await RequestUtil.HttpGetJson<{
code: number,
data?: {
name: string,
singer: string,
url: string,
cover: string
}
}>('https://api.leafone.cn/api/qqmusic?id=' + signedMid + '&type=8', 'GET');
//console.log(MusicReal);
return { ...MusicReal.data, mid: signedMid };
}
//转换外域名为 https://qq.ugcimg.cn/v1/cpqcbu4b8870i61bde6k7cbmjgejq8mr3in82qir4qi7ielffv5slv8ck8g42novtmev26i233ujtuab6tvu2l2sjgtupfr389191v00s1j5oh5325j5eqi40774jv1i/khovifoh7jrqd6eahoiv7koh8o //转换外域名为 https://qq.ugcimg.cn/v1/cpqcbu4b8870i61bde6k7cbmjgejq8mr3in82qir4qi7ielffv5slv8ck8g42novtmev26i233ujtuab6tvu2l2sjgtupfr389191v00s1j5oh5325j5eqi40774jv1i/khovifoh7jrqd6eahoiv7koh8o
//https://cgi.connect.qq.com/qqconnectopen/openapi/change_image_url?url=https://th.bing.com/th?id=OSK.b8ed36f1fb1889de6dc84fd81c187773&w=46&h=46&c=11&rs=1&qlt=80&o=6&dpr=2&pid=SANGAM //https://cgi.connect.qq.com/qqconnectopen/openapi/change_image_url?url=https://th.bing.com/th?id=OSK.b8ed36f1fb1889de6dc84fd81c187773&w=46&h=46&c=11&rs=1&qlt=80&o=6&dpr=2&pid=SANGAM
@@ -227,10 +21,5 @@ export class NTQQMusicSignApi {
//https://y.gtimg.cn/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg?max_age=2592000 //https://y.gtimg.cn/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg?max_age=2592000
//还有一处公告上传可以上传高质量图片 持久为qq域名 //还有一处公告上传可以上传高质量图片 持久为qq域名
async SignMusicWrapper(id: string = '') {
const MusicInfo = await this.signWay03(id)!;
return await this.signInternal(MusicInfo.name!, MusicInfo.singer!, MusicInfo.cover!, MusicInfo.mid!, 'https://ws.stream.qqmusic.qq.com/' + MusicInfo.url!);
}
} }

View File

@@ -212,15 +212,13 @@ export class NTQQWebApi {
} }
} }
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) { private async getDataInternal(cookieObject: any, groupCode: string, type: number) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => {
let resJson; let resJson;
try { try {
const res = await RequestUtil.HttpGetText( const res = await RequestUtil.HttpGetText(
`https://qun.qq.com/interactive/honorlist?${new URLSearchParams({ `https://qun.qq.com/interactive/honorlist?${new URLSearchParams({
gc: Internal_groupCode, gc: groupCode,
type: Internal_type.toString(), type: type.toString(),
}).toString()}`, }).toString()}`,
'GET', 'GET',
'', '',
@@ -230,90 +228,49 @@ export class NTQQWebApi {
if (match) { if (match) {
resJson = JSON.parse(match[1].trim()); resJson = JSON.parse(match[1].trim());
} }
if (Internal_type === 1) { return type === 1 ? resJson?.talkativeList : resJson?.actorList;
return resJson?.talkativeList;
} else {
return resJson?.actorList;
}
} catch (e) { } catch (e) {
this.context.logger.logDebug('获取当前群荣耀失败', e); this.context.logger.logDebug('获取当前群荣耀失败', e);
}
return undefined; return undefined;
}; }
}
private async getHonorList(cookieObject: any, groupCode: string, type: number) {
const data = await this.getDataInternal(cookieObject, groupCode, type);
if (!data) {
this.context.logger.logError(`获取类型 ${type} 的荣誉信息失败`);
return [];
}
return data.map((item: any) => ({
user_id: item?.uin,
nickname: item?.name,
avatar: item?.avatar,
description: item?.desc,
}));
}
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const HonorInfo: any = { group_id: groupCode }; const HonorInfo: any = { group_id: groupCode };
if (getType === WebHonorType.TALKATIVE || getType === WebHonorType.ALL) { if (getType === WebHonorType.TALKATIVE || getType === WebHonorType.ALL) {
const RetInternal = await getDataInternal(groupCode, 1); const talkativeList = await this.getHonorList(cookieObject, groupCode, 1);
if (RetInternal) { if (talkativeList.length > 0) {
HonorInfo.current_talkative = { HonorInfo.current_talkative = talkativeList[0];
user_id: RetInternal[0]?.uin, HonorInfo.talkative_list = talkativeList;
avatar: RetInternal[0]?.avatar,
nickname: RetInternal[0]?.name,
day_count: 0,
description: RetInternal[0]?.desc,
};
HonorInfo.talkative_list = [];
for (const talkative_ele of RetInternal) {
HonorInfo.talkative_list.push({
user_id: talkative_ele?.uin,
avatar: talkative_ele?.avatar,
description: talkative_ele?.desc,
day_count: 0,
nickname: talkative_ele?.name,
});
}
} else {
this.context.logger.logError.bind(this.context.logger)('获取龙王信息失败');
} }
} }
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) { if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
const RetInternal = await getDataInternal(groupCode, 2); HonorInfo.performer_list = await this.getHonorList(cookieObject, groupCode, 2);
if (RetInternal) {
HonorInfo.performer_list = [];
for (const performer_ele of RetInternal) {
HonorInfo.performer_list.push({
user_id: performer_ele?.uin,
nickname: performer_ele?.name,
avatar: performer_ele?.avatar,
description: performer_ele?.desc,
});
}
} else {
this.context.logger.logError.bind(this.context.logger)('获取群聊之火失败');
}
} }
if (getType === WebHonorType.LEGEND || getType === WebHonorType.ALL) { if (getType === WebHonorType.LEGEND || getType === WebHonorType.ALL) {
const RetInternal = await getDataInternal(groupCode, 3); HonorInfo.legend_list = await this.getHonorList(cookieObject, groupCode, 3);
if (RetInternal) {
HonorInfo.legend_list = [];
for (const legend_ele of RetInternal) {
HonorInfo.legend_list.push({
user_id: legend_ele?.uin,
nickname: legend_ele?.name,
avatar: legend_ele?.avatar,
desc: legend_ele?.description,
});
}
} else {
this.context.logger.logError.bind(this.context.logger)('获取群聊炽焰失败');
}
} }
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) { if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
const RetInternal = await getDataInternal(groupCode, 6); HonorInfo.emotion_list = await this.getHonorList(cookieObject, groupCode, 6);
if (RetInternal) {
HonorInfo.emotion_list = [];
for (const emotion_ele of RetInternal) {
HonorInfo.emotion_list.push({
user_id: emotion_ele.uin,
nickname: emotion_ele.name,
avatar: emotion_ele.avatar,
desc: emotion_ele.description,
});
}
} else {
this.context.logger.logError.bind(this.context.logger)('获取快乐源泉失败');
}
} }
// 冒尖小春笋好像已经被tx扬了 R.I.P. // 冒尖小春笋好像已经被tx扬了 R.I.P.

View File

@@ -23,7 +23,7 @@ export interface ChatCacheList {
export interface ChatCacheListItem { export interface ChatCacheListItem {
chatType: ChatType; chatType: ChatType;
basicChatCacheInfo: ChatCacheListItemBasic; basicChatCacheInfo: ChatCacheListItemBasic;
guildChatCacheInfo: unknown[]; // TODO: 没用过频道所以不知道这里边的详细内容 guildChatCacheInfo: unknown[]; // work: 没用过频道所以不知道这里边的详细内容
} }
export interface ChatCacheListItemBasic { export interface ChatCacheListItemBasic {

View File

@@ -117,7 +117,7 @@ export enum GroupMemberRole {
} }
export interface GroupMember { export interface GroupMember {
memberRealLevel: string | undefined; memberRealLevel: number | undefined;
memberSpecialTitle?: string; memberSpecialTitle?: string;
avatarPath: string; avatarPath: string;
cardName: string; cardName: string;

View File

@@ -175,8 +175,8 @@ export interface SimpleInfo {
status: UserStatus | null; status: UserStatus | null;
vasInfo: VasInfo | null; vasInfo: VasInfo | null;
relationFlags: RelationFlags | null; relationFlags: RelationFlags | null;
otherFlags: any | null; otherFlags: any;
intimate: any | null; intimate: any;
} }
export type FriendV2 = SimpleInfo; export type FriendV2 = SimpleInfo;

View File

@@ -1,4 +1,4 @@
// TODO: further refactor in NapCat.Packet v2 // work:further refactor in NapCat.Packet v2
import { NapProtoMsg, ProtoField, ScalarType } from "@napneko/nap-proto-core"; import { NapProtoMsg, ProtoField, ScalarType } from "@napneko/nap-proto-core";
const LikeDetail = { const LikeDetail = {

View File

@@ -1,4 +1,4 @@
// TODO: further refactor in NapCat.Packet v2 // work:further refactor in NapCat.Packet v2
import { NapProtoMsg, ProtoField, ScalarType } from "@napneko/nap-proto-core"; import { NapProtoMsg, ProtoField, ScalarType } from "@napneko/nap-proto-core";
const BodyInner = { const BodyInner = {

View File

@@ -84,11 +84,10 @@ export function getMajorPath(QQVersion: string): string {
} }
export class NapCatCore { export class NapCatCore {
readonly context: InstanceContext; readonly context: InstanceContext;
readonly apis: StableNTApiWrapper;
readonly eventWrapper: NTEventWrapper; readonly eventWrapper: NTEventWrapper;
// readonly eventChannel: NTEventChannel; NapCatDataPath: string = '';
NapCatDataPath: string; NapCatTempPath: string = '';
NapCatTempPath: string; apis: StableNTApiWrapper;
// runtime info, not readonly // runtime info, not readonly
selfInfo: SelfInfo; selfInfo: SelfInfo;
util: NodeQQNTWrapperUtil; util: NodeQQNTWrapperUtil;
@@ -112,6 +111,8 @@ export class NapCatCore {
UserApi: new NTQQUserApi(this.context, this), UserApi: new NTQQUserApi(this.context, this),
GroupApi: new NTQQGroupApi(this.context, this), GroupApi: new NTQQGroupApi(this.context, this),
}; };
}
async initCore() {
this.NapCatDataPath = path.join(this.dataPath, 'NapCat'); this.NapCatDataPath = path.join(this.dataPath, 'NapCat');
fs.mkdirSync(this.NapCatDataPath, { recursive: true }); fs.mkdirSync(this.NapCatDataPath, { recursive: true });
this.NapCatTempPath = path.join(this.NapCatDataPath, 'temp'); this.NapCatTempPath = path.join(this.NapCatDataPath, 'temp');
@@ -119,7 +120,13 @@ export class NapCatCore {
if (!fs.existsSync(this.NapCatTempPath)) { if (!fs.existsSync(this.NapCatTempPath)) {
fs.mkdirSync(this.NapCatTempPath, { recursive: true }); fs.mkdirSync(this.NapCatTempPath, { recursive: true });
} }
//遍历this.apis[i].initApi 如果存在该函数进行async 调用
for (const apiKey in this.apis) {
const api = this.apis[apiKey as keyof StableNTApiWrapper];
if ('initApi' in api && typeof api.initApi === 'function') {
await api.initApi();
}
}
this.initNapCatCoreListeners().then().catch(this.context.logger.logError.bind(this.context.logger)); this.initNapCatCoreListeners().then().catch(this.context.logger.logError.bind(this.context.logger));
this.context.logger.setFileLogEnabled( this.context.logger.setFileLogEnabled(
@@ -133,7 +140,6 @@ export class NapCatCore {
this.configLoader.configData.consoleLogLevel as LogLevel, this.configLoader.configData.consoleLogLevel as LogLevel,
); );
} }
get dataPath(): string { get dataPath(): string {
let result = this.context.wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig(); let result = this.context.wrapper.NodeQQNTWrapperUtil.getNTUserDataInfoConfig();
if (!result) { if (!result) {
@@ -204,7 +210,7 @@ export class NapCatCore {
}); });
}; };
groupListener.onMemberListChange = (arg) => { groupListener.onMemberListChange = (arg) => {
// todo: 应该加一个内部自己维护的成员变动callback用于判断成员变化通知 // work:应该加一个内部自己维护的成员变动callback用于判断成员变化通知
const groupCode = arg.sceneId.split('_')[0]; const groupCode = arg.sceneId.split('_')[0];
if (this.apis.GroupApi.groupMemberCache.has(groupCode)) { if (this.apis.GroupApi.groupMemberCache.has(groupCode)) {
const existMembers = this.apis.GroupApi.groupMemberCache.get(groupCode)!; const existMembers = this.apis.GroupApi.groupMemberCache.get(groupCode)!;
@@ -214,7 +220,7 @@ export class NapCatCore {
if (existMember) { if (existMember) {
Object.assign(existMember, member); Object.assign(existMember, member);
} else { } else {
existMembers!.set(uid, member); existMembers.set(uid, member);
} }
//移除成员 //移除成员
if (member.isDelete) { if (member.isDelete) {

View File

@@ -3,57 +3,57 @@ import { BuddyCategoryType, FriendRequestNotify } from '@/core/entities';
export type OnBuddyChangeParams = BuddyCategoryType[]; export type OnBuddyChangeParams = BuddyCategoryType[];
export class NodeIKernelBuddyListener { export class NodeIKernelBuddyListener {
onBuddyListChangedV2(arg: unknown): void { onBuddyListChangedV2(arg: unknown): any {
} }
onAddBuddyNeedVerify(arg: unknown) { onAddBuddyNeedVerify(arg: unknown): any {
} }
onAddMeSettingChanged(arg: unknown) { onAddMeSettingChanged(arg: unknown): any {
} }
onAvatarUrlUpdated(arg: unknown) { onAvatarUrlUpdated(arg: unknown): any {
} }
onBlockChanged(arg: unknown) { onBlockChanged(arg: unknown): any {
} }
onBuddyDetailInfoChange(arg: unknown) { onBuddyDetailInfoChange(arg: unknown): any {
} }
onBuddyInfoChange(arg: unknown) { onBuddyInfoChange(arg: unknown): any {
} }
onBuddyListChange(arg: OnBuddyChangeParams): void { onBuddyListChange(arg: OnBuddyChangeParams): any {
} }
onBuddyRemarkUpdated(arg: unknown): void { onBuddyRemarkUpdated(arg: unknown): any {
} }
onBuddyReqChange(arg: FriendRequestNotify): void { onBuddyReqChange(arg: FriendRequestNotify): any {
} }
onBuddyReqUnreadCntChange(arg: unknown): void { onBuddyReqUnreadCntChange(arg: unknown): any {
} }
onCheckBuddySettingResult(arg: unknown): void { onCheckBuddySettingResult(arg: unknown): any {
} }
onDelBatchBuddyInfos(arg: unknown): void { onDelBatchBuddyInfos(arg: unknown): any {
} }
onDoubtBuddyReqChange(arg: unknown): void { onDoubtBuddyReqChange(arg: unknown): any {
} }
onDoubtBuddyReqUnreadNumChange(arg: unknown): void { onDoubtBuddyReqUnreadNumChange(arg: unknown): any {
} }
onNickUpdated(arg: unknown): void { onNickUpdated(arg: unknown): any {
} }
onSmartInfos(arg: unknown): void { onSmartInfos(arg: unknown): any {
} }
onSpacePermissionInfos(arg: unknown): void { onSpacePermissionInfos(arg: unknown): any {
} }
} }

View File

@@ -7,19 +7,19 @@ export class NodeIKernelFileAssistantListener {
fileSpeed: number, fileSpeed: number,
thumbPath: string | null, thumbPath: string | null,
filePath: string | null, filePath: string | null,
}) { }): any {
} }
onSessionListChanged(...args: unknown[]) { onSessionListChanged(...args: unknown[]): any {
} }
onSessionChanged(...args: unknown[]) { onSessionChanged(...args: unknown[]): any {
} }
onFileListChanged(...args: unknown[]) { onFileListChanged(...args: unknown[]): any {
} }
onFileSearch(searchResult: SearchResultWrapper) { onFileSearch(searchResult: SearchResultWrapper): any {
} }
} }

View File

@@ -1,70 +1,70 @@
import { DataSource, Group, GroupListUpdateType, GroupMember, GroupNotify, ShutUpGroupMember } from '@/core/entities'; import { DataSource, Group, GroupListUpdateType, GroupMember, GroupNotify, ShutUpGroupMember } from '@/core/entities';
export class NodeIKernelGroupListener { export class NodeIKernelGroupListener {
onGroupListInited(listEmpty: boolean): void { } onGroupListInited(listEmpty: boolean): any { }
// 发现于Win 9.9.9 23159 // 发现于Win 9.9.9 23159
onGroupMemberLevelInfoChange(...args: unknown[]): void { onGroupMemberLevelInfoChange(...args: unknown[]): any {
} }
onGetGroupBulletinListResult(...args: unknown[]) { onGetGroupBulletinListResult(...args: unknown[]): any {
} }
onGroupAllInfoChange(...args: unknown[]) { onGroupAllInfoChange(...args: unknown[]): any {
} }
onGroupBulletinChange(...args: unknown[]) { onGroupBulletinChange(...args: unknown[]): any {
} }
onGroupBulletinRemindNotify(...args: unknown[]) { onGroupBulletinRemindNotify(...args: unknown[]): any {
} }
onGroupArkInviteStateResult(...args: unknown[]) { onGroupArkInviteStateResult(...args: unknown[]): any {
} }
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]) { onGroupBulletinRichMediaDownloadComplete(...args: unknown[]): any {
} }
onGroupConfMemberChange(...args: unknown[]) { onGroupConfMemberChange(...args: unknown[]): any {
} }
onGroupDetailInfoChange(...args: unknown[]) { onGroupDetailInfoChange(...args: unknown[]): any {
} }
onGroupExtListUpdate(...args: unknown[]) { onGroupExtListUpdate(...args: unknown[]): any {
} }
onGroupFirstBulletinNotify(...args: unknown[]) { onGroupFirstBulletinNotify(...args: unknown[]): any {
} }
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]) { onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): any {
} }
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]) { onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]): any {
} }
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]) { onGroupBulletinRichMediaProgressUpdate(...args: unknown[]): any {
} }
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) { onGroupNotifiesUnreadCountUpdated(...args: unknown[]): any {
} }
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]) { onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]): any {
} }
onGroupsMsgMaskResult(...args: unknown[]) { onGroupsMsgMaskResult(...args: unknown[]): any {
} }
onGroupStatisticInfoChange(...args: unknown[]) { onGroupStatisticInfoChange(...args: unknown[]): any {
} }
onJoinGroupNotify(...args: unknown[]) { onJoinGroupNotify(...args: unknown[]): any {
} }
onJoinGroupNoVerifyFlag(...args: unknown[]) { onJoinGroupNoVerifyFlag(...args: unknown[]): any {
} }
onMemberInfoChange(groupCode: string, dateSource: DataSource, members: Map<string, GroupMember>) { onMemberInfoChange(groupCode: string, dateSource: DataSource, members: Map<string, GroupMember>): any {
} }
onMemberListChange(arg: { onMemberListChange(arg: {
@@ -74,12 +74,12 @@ export class NodeIKernelGroupListener {
hasPrev: boolean, hasPrev: boolean,
hasNext: boolean, hasNext: boolean,
hasRobot: boolean hasRobot: boolean
}) { }): any {
} }
onSearchMemberChange(...args: unknown[]) { onSearchMemberChange(...args: unknown[]): any {
} }
onShutUpMemberListChanged(groupCode: string, members: Array<ShutUpGroupMember>) { onShutUpMemberListChanged(groupCode: string, members: Array<ShutUpGroupMember>): any {
} }
} }

View File

@@ -1,57 +1,57 @@
export class NodeIKernelLoginListener { export class NodeIKernelLoginListener {
onLoginConnected(...args: any[]): void { onLoginConnected(...args: any[]): any {
} }
onLoginDisConnected(...args: any[]): void { onLoginDisConnected(...args: any[]): any {
} }
onLoginConnecting(...args: any[]): void { onLoginConnecting(...args: any[]): any {
} }
onQRCodeGetPicture(arg: { pngBase64QrcodeData: string, qrcodeUrl: string }): void { onQRCodeGetPicture(arg: { pngBase64QrcodeData: string, qrcodeUrl: string }): any {
// let base64Data: string = arg.pngBase64QrcodeData // let base64Data: string = arg.pngBase64QrcodeData
// base64Data = base64Data.split("data:image/png;base64,")[1] // base64Data = base64Data.split("data:image/png;base64,")[1]
// let buffer = Buffer.from(base64Data, 'base64') // let buffer = Buffer.from(base64Data, 'base64')
// console.log("onQRCodeGetPicture", arg); // console.log("onQRCodeGetPicture", arg);
} }
onQRCodeLoginPollingStarted(...args: any[]): void { onQRCodeLoginPollingStarted(...args: any[]): any {
} }
onQRCodeSessionUserScaned(...args: any[]): void { onQRCodeSessionUserScaned(...args: any[]): any {
} }
onQRCodeLoginSucceed(arg: QRCodeLoginSucceedResult): void { onQRCodeLoginSucceed(arg: QRCodeLoginSucceedResult): any {
} }
onQRCodeSessionFailed(...args: any[]): void { onQRCodeSessionFailed(...args: any[]): any {
} }
onLoginFailed(...args: any[]): void { onLoginFailed(...args: any[]): any {
} }
onLogoutSucceed(...args: any[]): void { onLogoutSucceed(...args: any[]): any {
} }
onLogoutFailed(...args: any[]): void { onLogoutFailed(...args: any[]): any {
} }
onUserLoggedIn(...args: any[]): void { onUserLoggedIn(...args: any[]): any {
} }
onQRCodeSessionQuickLoginFailed(...args: any[]): void { onQRCodeSessionQuickLoginFailed(...args: any[]): any {
} }
onPasswordLoginFailed(...args: any[]): void { onPasswordLoginFailed(...args: any[]): any {
} }
OnConfirmUnusualDeviceFailed(...args: any[]): void { OnConfirmUnusualDeviceFailed(...args: any[]): any {
} }
onQQLoginNumLimited(...args: any[]): void { onQQLoginNumLimited(...args: any[]): any {
} }
onLoginState(...args: any[]): void { onLoginState(...args: any[]): any {
} }
} }

View File

@@ -20,8 +20,8 @@ export interface OnRichMediaDownloadCompleteParams {
fileSrvErrCode: string, fileSrvErrCode: string,
clientMsg: string, clientMsg: string,
businessId: number, businessId: number,
userTotalSpacePerDay: unknown | null, userTotalSpacePerDay: unknown,
userUsedSpacePerDay: unknown | null userUsedSpacePerDay: unknown
} }
export interface GroupFileInfoUpdateParamType { export interface GroupFileInfoUpdateParamType {
@@ -94,108 +94,108 @@ export interface TempOnRecvParams {
} }
export class NodeIKernelMsgListener { export class NodeIKernelMsgListener {
onAddSendMsg(msgRecord: RawMessage) { onAddSendMsg(msgRecord: RawMessage): any {
} }
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown) { onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown): any {
} }
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown) { onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown): any {
} }
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown) { onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown): any {
} }
onContactUnreadCntUpdate(hashMap: unknown) { onContactUnreadCntUpdate(hashMap: unknown): any {
} }
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown) { onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown): any {
} }
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown) { onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown): any {
} }
onEmojiDownloadComplete(emojiNotifyInfo: unknown) { onEmojiDownloadComplete(emojiNotifyInfo: unknown): any {
} }
onEmojiResourceUpdate(emojiResourceInfo: unknown) { onEmojiResourceUpdate(emojiResourceInfo: unknown): any {
} }
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown) { onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
} }
onFileMsgCome(arrayList: unknown) { onFileMsgCome(arrayList: unknown): any {
} }
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown) { onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
} }
onFirstViewGroupGuildMapping(arrayList: unknown) { onFirstViewGroupGuildMapping(arrayList: unknown): any {
} }
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown) { onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown): any {
} }
onGroupFileInfoAdd(groupItem: unknown) { onGroupFileInfoAdd(groupItem: unknown): any {
} }
onGroupFileInfoUpdate(groupFileListResult: GroupFileInfoUpdateParamType) { onGroupFileInfoUpdate(groupFileListResult: GroupFileInfoUpdateParamType): any {
} }
onGroupGuildUpdate(groupGuildNotifyInfo: unknown) { onGroupGuildUpdate(groupGuildNotifyInfo: unknown): any {
} }
onGroupTransferInfoAdd(groupItem: unknown) { onGroupTransferInfoAdd(groupItem: unknown): any {
} }
onGroupTransferInfoUpdate(groupFileListResult: unknown) { onGroupTransferInfoUpdate(groupFileListResult: unknown): any {
} }
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown) { onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown): any {
} }
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown) { onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown): any {
} }
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown) { onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown): any {
} }
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown) { onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown): any {
} }
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown) { onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown): any {
} }
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown) { onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown): any {
} }
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown) { onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown): any {
} }
@@ -208,176 +208,176 @@ export class NodeIKernelMsgListener {
statusText: string; statusText: string;
timestamp: string; timestamp: string;
toUin: string; toUin: string;
}) { }): any {
} }
onKickedOffLine(kickedInfo: KickedOffLineInfo) { onKickedOffLine(kickedInfo: KickedOffLineInfo): any {
} }
onLineDev(arrayList: unknown) { onLineDev(arrayList: unknown): any {
} }
onLogLevelChanged(j2: unknown) { onLogLevelChanged(j2: unknown): any {
} }
onMsgAbstractUpdate(arrayList: unknown) { onMsgAbstractUpdate(arrayList: unknown): any {
} }
onMsgBoxChanged(arrayList: unknown) { onMsgBoxChanged(arrayList: unknown): any {
} }
onMsgDelete(contact: unknown, arrayList: unknown) { onMsgDelete(contact: unknown, arrayList: unknown): any {
} }
onMsgEventListUpdate(hashMap: unknown) { onMsgEventListUpdate(hashMap: unknown): any {
} }
onMsgInfoListAdd(arrayList: unknown) { onMsgInfoListAdd(arrayList: unknown): any {
} }
onMsgInfoListUpdate(msgList: RawMessage[]) { onMsgInfoListUpdate(msgList: RawMessage[]): any {
} }
onMsgQRCodeStatusChanged(i2: unknown) { onMsgQRCodeStatusChanged(i2: unknown): any {
} }
onMsgRecall(i2: unknown, str: unknown, j2: unknown) { onMsgRecall(i2: unknown, str: unknown, j2: unknown): any {
} }
onMsgSecurityNotify(msgRecord: unknown) { onMsgSecurityNotify(msgRecord: unknown): any {
} }
onMsgSettingUpdate(msgSetting: unknown) { onMsgSettingUpdate(msgSetting: unknown): any {
} }
onNtFirstViewMsgSyncEnd() { onNtFirstViewMsgSyncEnd(): any {
} }
onNtMsgSyncEnd() { onNtMsgSyncEnd(): any {
} }
onNtMsgSyncStart() { onNtMsgSyncStart(): any {
} }
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown) { onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): any {
} }
onRecvGroupGuildFlag(i2: unknown) { onRecvGroupGuildFlag(i2: unknown): any {
} }
onRecvMsg(arrayList: RawMessage[]) { onRecvMsg(arrayList: RawMessage[]): any {
} }
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown) { onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown): any {
} }
onRecvOnlineFileMsg(arrayList: unknown) { onRecvOnlineFileMsg(arrayList: unknown): any {
} }
onRecvS2CMsg(arrayList: unknown) { onRecvS2CMsg(arrayList: unknown): any {
} }
onRecvSysMsg(arrayList: Array<number>) { onRecvSysMsg(arrayList: Array<number>): any {
} }
onRecvUDCFlag(i2: unknown) { onRecvUDCFlag(i2: unknown): any {
} }
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) { onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): any {
} }
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown) { onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown): any {
} }
onRichMediaUploadComplete(fileTransNotifyInfo: unknown) { onRichMediaUploadComplete(fileTransNotifyInfo: unknown): any {
} }
onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown) { onSearchGroupFileInfoUpdate(searchGroupFileResult: unknown): any {
} }
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown) { onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): any {
} }
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown) { onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown): any {
} }
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams) { onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams): any {
} }
onUnreadCntAfterFirstView(hashMap: unknown) { onUnreadCntAfterFirstView(hashMap: unknown): any {
} }
onUnreadCntUpdate(hashMap: unknown) { onUnreadCntUpdate(hashMap: unknown): any {
} }
onUserChannelTabStatusChanged(z: unknown) { onUserChannelTabStatusChanged(z: unknown): any {
} }
onUserOnlineStatusChanged(z: unknown) { onUserOnlineStatusChanged(z: unknown): any {
} }
onUserTabStatusChanged(arrayList: unknown) { onUserTabStatusChanged(arrayList: unknown): any {
} }
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown) { onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown): any {
} }
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown) { onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown): any {
} }
// 第一次发现于Linux // 第一次发现于Linux
onUserSecQualityChanged(...args: unknown[]) { onUserSecQualityChanged(...args: unknown[]): any {
} }
onMsgWithRichLinkInfoUpdate(...args: unknown[]) { onMsgWithRichLinkInfoUpdate(...args: unknown[]): any {
} }
onRedTouchChanged(...args: unknown[]) { onRedTouchChanged(...args: unknown[]): any {
} }
// 第一次发现于Win 9.9.9-23159 // 第一次发现于Win 9.9.9-23159
onBroadcastHelperProgerssUpdate(...args: unknown[]) { onBroadcastHelperProgerssUpdate(...args: unknown[]): any {
} }
} }

View File

@@ -5,67 +5,67 @@ export class NodeIKernelProfileListener {
} }
onProfileSimpleChanged(...args: unknown[]) { onProfileSimpleChanged(...args: unknown[]): any {
} }
onProfileDetailInfoChanged(profile: User) { onProfileDetailInfoChanged(profile: User): any {
} }
onStatusUpdate(...args: unknown[]) { onStatusUpdate(...args: unknown[]): any {
} }
onSelfStatusChanged(...args: unknown[]) { onSelfStatusChanged(...args: unknown[]): any {
} }
onStrangerRemarkChanged(...args: unknown[]) { onStrangerRemarkChanged(...args: unknown[]): any {
} }
onMemberListChange(...args: unknown[]) { onMemberListChange(...args: unknown[]): any {
} }
onMemberInfoChange(...args: unknown[]) { onMemberInfoChange(...args: unknown[]): any {
} }
onGroupListUpdate(...args: unknown[]) { onGroupListUpdate(...args: unknown[]): any {
} }
onGroupAllInfoChange(...args: unknown[]) { onGroupAllInfoChange(...args: unknown[]): any {
} }
onGroupDetailInfoChange(...args: unknown[]) { onGroupDetailInfoChange(...args: unknown[]): any {
} }
onGroupConfMemberChange(...args: unknown[]) { onGroupConfMemberChange(...args: unknown[]): any {
} }
onGroupExtListUpdate(...args: unknown[]) { onGroupExtListUpdate(...args: unknown[]): any {
} }
onGroupNotifiesUpdated(...args: unknown[]) { onGroupNotifiesUpdated(...args: unknown[]): any {
} }
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) { onGroupNotifiesUnreadCountUpdated(...args: unknown[]): any {
} }
onGroupMemberLevelInfoChange(...args: unknown[]) { onGroupMemberLevelInfoChange(...args: unknown[]): any {
} }
onGroupBulletinChange(...args: unknown[]) { onGroupBulletinChange(...args: unknown[]): any {
} }
} }

View File

@@ -1,25 +1,25 @@
export class NodeIKernelRecentContactListener { export class NodeIKernelRecentContactListener {
onDeletedContactsNotify(...args: unknown[]) { onDeletedContactsNotify(...args: unknown[]): any {
} }
onRecentContactNotification(msgList: any, arg0: { msgListUnreadCnt: string }, arg1: number) { onRecentContactNotification(msgList: any, arg0: { msgListUnreadCnt: string }, arg1: number): any {
} }
onMsgUnreadCountUpdate(...args: unknown[]) { onMsgUnreadCountUpdate(...args: unknown[]): any {
} }
onGuildDisplayRecentContactListChanged(...args: unknown[]) { onGuildDisplayRecentContactListChanged(...args: unknown[]): any {
} }
onRecentContactListChanged(...args: unknown[]) { onRecentContactListChanged(...args: unknown[]): any {
} }
onRecentContactListChangedVer2(...args: unknown[]) { onRecentContactListChangedVer2(...args: unknown[]): any {
} }
} }

View File

@@ -1,13 +1,13 @@
export class NodeIKernelRobotListener { export class NodeIKernelRobotListener {
onRobotFriendListChanged(...args: unknown[]) { onRobotFriendListChanged(...args: unknown[]): any {
} }
onRobotListChanged(...args: unknown[]) { onRobotListChanged(...args: unknown[]): any {
} }
onRobotProfileChanged(...args: unknown[]) { onRobotProfileChanged(...args: unknown[]): any {
} }
} }

View File

@@ -57,7 +57,7 @@ export interface GroupSearchResult {
} }
export interface NodeIKernelSearchListener { export interface NodeIKernelSearchListener {
onSearchGroupResult(params: GroupSearchResult): void; onSearchGroupResult(params: GroupSearchResult): any;
onSearchFileKeywordsResult(params: { onSearchFileKeywordsResult(params: {
searchId: string, searchId: string,
@@ -93,5 +93,5 @@ export interface NodeIKernelSearchListener {
end: number end: number
}[] }[]
}[] }[]
}): void; }): any;
} }

View File

@@ -1,25 +1,25 @@
export class NodeIKernelSessionListener { export class NodeIKernelSessionListener {
onNTSessionCreate(args: unknown) { onNTSessionCreate(args: unknown): any {
} }
onGProSessionCreate(args: unknown) { onGProSessionCreate(args: unknown): any {
} }
onSessionInitComplete(args: unknown) { onSessionInitComplete(args: unknown): any {
} }
onOpentelemetryInit(args: unknown) { onOpentelemetryInit(args: unknown): any {
} }
onUserOnlineResult(args: unknown) { onUserOnlineResult(args: unknown): any {
} }
onGetSelfTinyId(args: unknown) { onGetSelfTinyId(args: unknown): any {
} }
} }

View File

@@ -1,21 +1,21 @@
export class NodeIKernelStorageCleanListener { export class NodeIKernelStorageCleanListener {
onCleanCacheProgressChanged(args: unknown) { onCleanCacheProgressChanged(args: unknown): any {
} }
onScanCacheProgressChanged(args: unknown) { onScanCacheProgressChanged(args: unknown): any {
} }
onCleanCacheStorageChanged(args: unknown) { onCleanCacheStorageChanged(args: unknown): any {
} }
onFinishScan(args: unknown) { onFinishScan(args: unknown): any {
} }
onChatCleanDone(args: unknown) { onChatCleanDone(args: unknown): any {
} }
} }

View File

@@ -1,2 +1,5 @@
export class NodeIKernelTicketListener { export class NodeIKernelTicketListener {
listener(): any {
}
} }

View File

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

View File

@@ -16,8 +16,8 @@ export interface NativePacketExportType {
export class NativePacketClient extends IPacketClient { export class NativePacketClient extends IPacketClient {
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 MoeHooExport: { exports: NativePacketExportType } = { exports: {} }; private readonly MoeHooExport: { exports: NativePacketExportType } = { exports: {} };
private sendEvent = new LRUCache<number, string>(500); // seq->trace_id private readonly sendEvent = new LRUCache<number, string>(500); // seq->trace_id
constructor(context: PacketContext, logStack: LogStack) { constructor(context: PacketContext, logStack: LogStack) {
super(context, logStack); super(context, logStack);

View File

@@ -1,9 +1,9 @@
import { Data, WebSocket } from "ws"; import { Data, WebSocket, ErrorEvent } from "ws";
import { IPacketClient, RecvPacket } from "@/core/packet/client/baseClient"; import { IPacketClient, RecvPacket } from "@/core/packet/client/baseClient";
import { PacketContext } from "@/core/packet/context/packetContext"; import { PacketContext } from "@/core/packet/context/packetContext";
import { LogStack } from "@/core/packet/context/clientContext"; import { LogStack } from "@/core/packet/context/clientContext";
export class wsPacketClient extends IPacketClient { export class WsPacketClient extends IPacketClient {
private websocket: WebSocket | null = null; private websocket: WebSocket | null = null;
private reconnectAttempts: number = 0; private reconnectAttempts: number = 0;
private readonly maxReconnectAttempts: number = 60; // 现在暂时不可配置 private readonly maxReconnectAttempts: number = 60; // 现在暂时不可配置
@@ -85,11 +85,11 @@ export class wsPacketClient extends IPacketClient {
this.websocket.onmessage = (event) => this.handleMessage(event.data).catch(err => { this.websocket.onmessage = (event) => this.handleMessage(event.data).catch(err => {
this.context.logger.error(`处理消息时出错: ${err}`); this.context.logger.error(`处理消息时出错: ${err}`);
}); });
this.websocket.onerror = (error) => { this.websocket.onerror = (event: ErrorEvent) => {
this.available = false; this.available = false;
this.context.logger.error(`WebSocket 出错: ${error.message}`); this.context.logger.error(`WebSocket 出错: ${event.message}`);
this.websocket?.close(); this.websocket?.close();
reject(error); reject(new Error(`WebSocket 出错: ${event.message}`));
}; };
}); });
} }

View File

@@ -24,7 +24,7 @@ export class PacketClientSession {
return this.context.operation; return this.context.operation;
} }
// TODO: global message element adapter (? // work: global message element adapter (?
get msgConverter() { get msgConverter() {
return this.context.msgConverter; return this.context.msgConverter;
} }

View File

@@ -1,7 +1,7 @@
import { PacketContext } from "@/core/packet/context/packetContext"; import { PacketContext } from "@/core/packet/context/packetContext";
import { IPacketClient } from "@/core/packet/client/baseClient"; import { IPacketClient } from "@/core/packet/client/baseClient";
import { NativePacketClient } from "@/core/packet/client/nativeClient"; import { NativePacketClient } from "@/core/packet/client/nativeClient";
import { wsPacketClient } from "@/core/packet/client/wsClient"; import { WsPacketClient } from "@/core/packet/client/wsClient";
import { OidbPacket } from "@/core/packet/transformer/base"; import { OidbPacket } from "@/core/packet/transformer/base";
import { PacketLogger } from "@/core/packet/context/loggerContext"; import { PacketLogger } from "@/core/packet/context/loggerContext";
@@ -11,12 +11,12 @@ type clientPriority = {
const clientPriority: clientPriority = { const clientPriority: clientPriority = {
10: (context: PacketContext, logStack: LogStack) => new NativePacketClient(context, logStack), 10: (context: PacketContext, logStack: LogStack) => new NativePacketClient(context, logStack),
1: (context: PacketContext, logStack: LogStack) => new wsPacketClient(context, logStack), 1: (context: PacketContext, logStack: LogStack) => new WsPacketClient(context, logStack),
}; };
export class LogStack { export class LogStack {
private stack: string[] = []; private stack: string[] = [];
private logger: PacketLogger; private readonly logger: PacketLogger;
constructor(logger: PacketLogger) { constructor(logger: PacketLogger) {
this.logger = logger; this.logger = logger;
@@ -88,7 +88,7 @@ export class PacketClientContext {
break; break;
case "frida": case "frida":
this.context.logger.info("[Core] [Packet] 使用指定的 FridaPacketClient 作为后端"); this.context.logger.info("[Core] [Packet] 使用指定的 FridaPacketClient 作为后端");
client = new wsPacketClient(this.context, this.logStack); client = new WsPacketClient(this.context, this.logStack);
break; break;
case "auto": case "auto":
case undefined: case undefined:
@@ -98,9 +98,12 @@ export class PacketClientContext {
this.context.logger.error(`未知的PacketBackend ${prefer},请检查配置文件!`); this.context.logger.error(`未知的PacketBackend ${prefer},请检查配置文件!`);
client = null; client = null;
} }
if (!(client && client.check())) { if (!client?.check()) {
throw new Error("[Core] [Packet] 无可用的后端NapCat.Packet将不会加载"); throw new Error("[Core] [Packet] 无可用的后端NapCat.Packet将不会加载");
} }
if (!client) {
throw new Error("[Core] [Packet] 后端异常NapCat.Packet将不会加载");
}
return client; return client;
} }

View File

@@ -1,7 +1,7 @@
import { LogLevel, LogWrapper } from "@/common/log"; import { LogLevel, LogWrapper } from "@/common/log";
import { PacketContext } from "@/core/packet/context/packetContext"; import { PacketContext } from "@/core/packet/context/packetContext";
// TODO: check bind? // work: check bind?
export class PacketLogger { export class PacketLogger {
private readonly napLogger: LogWrapper; private readonly napLogger: LogWrapper;

View File

@@ -15,7 +15,7 @@ import { NapProtoDecodeStructType, NapProtoEncodeStructType } from "@napneko/nap
import { IndexNode, MsgInfo } from "@/core/packet/transformer/proto"; import { IndexNode, MsgInfo } from "@/core/packet/transformer/proto";
export class PacketOperationContext { export class PacketOperationContext {
private context: PacketContext; private readonly context: PacketContext;
constructor(context: PacketContext) { constructor(context: PacketContext) {
this.context = context; this.context = context;
} }
@@ -65,35 +65,22 @@ export class PacketOperationContext {
} }
async UploadResources(msg: PacketMsg[], groupUin: number = 0) { async UploadResources(msg: PacketMsg[], groupUin: number = 0) {
const reqList = []; const chatType = groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C;
for (const m of msg) { const peerUid = groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid;
for (const e of m.msg) { const reqList = msg.flatMap(m =>
m.msg.map(e => {
if (e instanceof PacketMsgPicElement) { if (e instanceof PacketMsgPicElement) {
reqList.push(this.context.highway.uploadImage({ return this.context.highway.uploadImage({ chatType, peerUid }, e);
chatType: groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C, } else if (e instanceof PacketMsgVideoElement) {
peerUid: groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid return this.context.highway.uploadVideo({ chatType, peerUid }, e);
}, e)); } else if (e instanceof PacketMsgPttElement) {
} return this.context.highway.uploadPtt({ chatType, peerUid }, e);
if (e instanceof PacketMsgVideoElement) { } else if (e instanceof PacketMsgFileElement) {
reqList.push(this.context.highway.uploadVideo({ return this.context.highway.uploadFile({ chatType, peerUid }, e);
chatType: groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C,
peerUid: groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid
}, e));
}
if (e instanceof PacketMsgPttElement) {
reqList.push(this.context.highway.uploadPtt({
chatType: groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C,
peerUid: groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid
}, e));
}
if (e instanceof PacketMsgFileElement) {
reqList.push(this.context.highway.uploadFile({
chatType: groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C,
peerUid: groupUin ? String(groupUin) : this.context.napcore.basicInfo.uid
}, e));
}
}
} }
return null;
}).filter(Boolean)
);
const res = await Promise.allSettled(reqList); const res = await Promise.allSettled(reqList);
this.context.logger.info(`上传资源${res.length}个,失败${res.filter(r => r.status === 'rejected').length}`); this.context.logger.info(`上传资源${res.length}个,失败${res.filter(r => r.status === 'rejected').length}`);
res.forEach((result, index) => { res.forEach((result, index) => {

View File

@@ -33,7 +33,7 @@ export interface PacketHighwaySig {
} }
export class PacketHighwayContext { export class PacketHighwayContext {
private context: PacketContext; private readonly context: PacketContext;
protected sig: PacketHighwaySig; protected sig: PacketHighwaySig;
protected logger: PacketLogger; protected logger: PacketLogger;
protected hwClient: PacketHighwayClient; protected hwClient: PacketHighwayClient;
@@ -223,12 +223,12 @@ export class PacketHighwayContext {
msgInfoBody: preRespData.upload.msgInfo.msgInfoBody, msgInfoBody: preRespData.upload.msgInfo.msgInfoBody,
blockSize: BlockSize, blockSize: BlockSize,
hash: { hash: {
fileSha1: await calculateSha1StreamBytes(video.filePath!) fileSha1: await calculateSha1StreamBytes(video.filePath)
} }
}); });
await this.hwClient.upload( await this.hwClient.upload(
1005, 1005,
fs.createReadStream(video.filePath!, { highWaterMark: BlockSize }), fs.createReadStream(video.filePath, { highWaterMark: BlockSize }),
+video.fileSize!, +video.fileSize!,
md5, md5,
extend extend
@@ -256,7 +256,7 @@ export class PacketHighwayContext {
}); });
await this.hwClient.upload( await this.hwClient.upload(
1006, 1006,
fs.createReadStream(video.thumbPath!, { highWaterMark: BlockSize }), fs.createReadStream(video.thumbPath, { highWaterMark: BlockSize }),
+video.thumbSize!, +video.thumbSize!,
md5, md5,
extend extend
@@ -288,12 +288,12 @@ export class PacketHighwayContext {
msgInfoBody: preRespData.upload.msgInfo.msgInfoBody, msgInfoBody: preRespData.upload.msgInfo.msgInfoBody,
blockSize: BlockSize, blockSize: BlockSize,
hash: { hash: {
fileSha1: await calculateSha1StreamBytes(video.filePath!) fileSha1: await calculateSha1StreamBytes(video.filePath)
} }
}); });
await this.hwClient.upload( await this.hwClient.upload(
1001, 1001,
fs.createReadStream(video.filePath!, { highWaterMark: BlockSize }), fs.createReadStream(video.filePath, { highWaterMark: BlockSize }),
+video.fileSize!, +video.fileSize!,
md5, md5,
extend extend
@@ -321,7 +321,7 @@ export class PacketHighwayContext {
}); });
await this.hwClient.upload( await this.hwClient.upload(
1002, 1002,
fs.createReadStream(video.thumbPath!, { highWaterMark: BlockSize }), fs.createReadStream(video.thumbPath, { highWaterMark: BlockSize }),
+video.thumbSize!, +video.thumbSize!,
md5, md5,
extend extend

View File

@@ -64,11 +64,11 @@ export class HighwayHttpUploader extends IHighwayUploader {
}); });
}); });
req.write(frame); req.write(frame);
req.on('error', (error) => { req.on('error', (error: Error) => {
reject(error); reject(error);
}); });
} catch (error) { } catch (error: unknown) {
reject(error); reject(new Error((error as Error).message));
} }
}); });
} }

View File

@@ -7,12 +7,12 @@ export const int32ip2str = (ip: number) => {
return [ip & 0xff, (ip & 0xff00) >> 8, (ip & 0xff0000) >> 16, ((ip & 0xff000000) >> 24) & 0xff].join('.'); return [ip & 0xff, (ip & 0xff00) >> 8, (ip & 0xff0000) >> 16, ((ip & 0xff000000) >> 24) & 0xff].join('.');
}; };
export const oidbIpv4s2HighwayIpv4s = (ipv4s: NapProtoEncodeStructType<typeof proto.IPv4>[]): NapProtoEncodeStructType<typeof proto.NTHighwayIPv4>[] =>{ export const oidbIpv4s2HighwayIpv4s = (ipv4s: NapProtoEncodeStructType<typeof proto.IPv4>[]): NapProtoEncodeStructType<typeof proto.NTHighwayIPv4>[] => {
return ipv4s.map((ip) => { return ipv4s.map((ip) => {
return { return {
domain: { domain: {
isEnable: true, isEnable: true,
ip: int32ip2str(ip.outIP!), ip: int32ip2str(ip.outIP ?? 0),
}, },
port: ip.outPort! port: ip.outPort!
} as NapProtoEncodeStructType<typeof proto.NTHighwayIPv4>; } as NapProtoEncodeStructType<typeof proto.NTHighwayIPv4>;

View File

@@ -16,7 +16,7 @@ export class PacketMsgBuilder {
return element.map((node): NapProtoEncodeStructType<typeof PushMsgBody> => { return element.map((node): NapProtoEncodeStructType<typeof PushMsgBody> => {
const avatar = `https://q.qlogo.cn/headimg_dl?dst_uin=${node.senderUin}&spec=640&img_type=jpg`; const avatar = `https://q.qlogo.cn/headimg_dl?dst_uin=${node.senderUin}&spec=640&img_type=jpg`;
const msgContent = node.msg.reduceRight((acc: undefined | Uint8Array, msg: IPacketMsgElement<PacketSendMsgElement>) => { const msgContent = node.msg.reduceRight((acc: undefined | Uint8Array, msg: IPacketMsgElement<PacketSendMsgElement>) => {
return acc !== undefined ? acc : msg.buildContent(); return acc ?? msg.buildContent();
}, undefined); }, undefined);
const msgElement = node.msg.flatMap(msg => msg.buildElement() ?? []); const msgElement = node.msg.flatMap(msg => msg.buildElement() ?? []);
if (!msgContent && !msgElement.length) { if (!msgContent && !msgElement.length) {

View File

@@ -76,13 +76,13 @@ export type rawMsgWithSendMsg = {
msg: PacketSendMsgElement[] msg: PacketSendMsgElement[]
} }
// TODO: make it become adapter? // work:make it become adapter?
export class PacketMsgConverter { export class PacketMsgConverter {
private isValidElementType(type: ElementType): type is keyof ElementToPacketMsgConverters { private isValidElementType(type: ElementType): type is keyof ElementToPacketMsgConverters {
return SupportedElementTypes.includes(type); return SupportedElementTypes.includes(type);
} }
private rawToPacketMsgConverters: ElementToPacketMsgConverters = { private readonly rawToPacketMsgConverters: ElementToPacketMsgConverters = {
[ElementType.TEXT]: (element) => { [ElementType.TEXT]: (element) => {
if (element.textElement?.atType) { if (element.textElement?.atType) {
return new PacketMsgAtElement(element as SendTextElement); return new PacketMsgAtElement(element as SendTextElement);
@@ -116,7 +116,7 @@ export class PacketMsgConverter {
[ElementType.MARKDOWN]: (element) => { [ElementType.MARKDOWN]: (element) => {
return new PacketMsgMarkDownElement(element as SendMarkdownElement); return new PacketMsgMarkDownElement(element as SendMarkdownElement);
}, },
// TODO: check this logic, move it in arkElement? // work:check this logic, move it in arkElement?
[ElementType.STRUCTLONGMSG]: (element) => { [ElementType.STRUCTLONGMSG]: (element) => {
return new PacketMultiMsgElement(element as SendStructLongMsgElement); return new PacketMultiMsgElement(element as SendStructLongMsgElement);
} }

View File

@@ -32,7 +32,7 @@ import { ForwardMsgBuilder } from "@/common/forward-msg-builder";
import { PacketMsg, PacketSendMsgElement } from "@/core/packet/message/message"; import { PacketMsg, PacketSendMsgElement } from "@/core/packet/message/message";
// raw <-> packet // raw <-> packet
// TODO: SendStructLongMsgElement // work:SendStructLongMsgElement
export abstract class IPacketMsgElement<T extends PacketSendMsgElement> { export abstract class IPacketMsgElement<T extends PacketSendMsgElement> {
protected constructor(rawElement: T) { protected constructor(rawElement: T) {
} }
@@ -118,7 +118,7 @@ export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
this.targetUin = +(element.replyElement.senderUin ?? 0); this.targetUin = +(element.replyElement.senderUin ?? 0);
this.targetUid = element.replyElement.senderUidStr ?? ''; this.targetUid = element.replyElement.senderUidStr ?? '';
this.time = +(element.replyElement.replyMsgTime ?? 0); this.time = +(element.replyElement.replyMsgTime ?? 0);
this.elems = []; // TODO: in replyElement.sourceMsgTextElems this.elems = []; // work:in replyElement.sourceMsgTextElems
} }
get isGroupReply(): boolean { get isGroupReply(): boolean {
@@ -131,7 +131,7 @@ export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
origSeqs: [this.isGroupReply ? this.messageClientSeq : this.messageSeq], origSeqs: [this.isGroupReply ? this.messageClientSeq : this.messageSeq],
senderUin: BigInt(this.targetUin), senderUin: BigInt(this.targetUin),
time: this.time, time: this.time,
elems: [], // TODO: in replyElement.sourceMsgTextElems elems: [], // work:in replyElement.sourceMsgTextElems
pbReserve: { pbReserve: {
messageId: this.messageId, messageId: this.messageId,
}, },
@@ -346,9 +346,9 @@ export class PacketMsgPttElement extends IPacketMsgElement<SendPttElement> {
constructor(element: SendPttElement) { constructor(element: SendPttElement) {
super(element); super(element);
this.filePath = element.pttElement.filePath; this.filePath = element.pttElement.filePath;
this.fileSize = +element.pttElement.fileSize; // TODO: cc this.fileSize = +element.pttElement.fileSize; // work:cc
this.fileMd5 = element.pttElement.md5HexStr; this.fileMd5 = element.pttElement.md5HexStr;
this.fileDuration = Math.round(element.pttElement.duration); // TODO: cc this.fileDuration = Math.round(element.pttElement.duration); // work:cc
} }
get valid(): boolean { get valid(): boolean {

View File

@@ -25,7 +25,7 @@ class DownloadOfflineFile extends PacketTransformer<typeof proto.OidbSvcTrpcTcp0
return OidbBase.build(0xE37, 800, body, false, false); return OidbBase.build(0xE37, 800, body, false, false);
} }
// TODO: check // work:check
parse(data: Buffer) { parse(data: Buffer) {
const oidbBody = OidbBase.parse(data).body; const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.OidbSvcTrpcTcp0XE37Response).decode(oidbBody); return new NapProtoMsg(proto.OidbSvcTrpcTcp0XE37Response).decode(oidbBody);

View File

@@ -16,7 +16,7 @@ class FetchSessionKey extends PacketTransformer<typeof proto.HttpConn0x6ff_501Re
field4: 1, field4: 1,
field6: 3, field6: 3,
serviceTypes: [1, 5, 10, 21], serviceTypes: [1, 5, 10, 21],
// tgt: "", // TODO: do we really need tgt? seems not // tgt: "", // work:do we really need tgt? seems not
field9: 2, field9: 2,
field10: 9, field10: 9,
field11: 8, field11: 8,

View File

@@ -16,7 +16,7 @@ class UploadGroupFile extends PacketTransformer<typeof proto.OidbSvcTrpcTcp0x6D6
appId: 4, appId: 4,
busId: 102, busId: 102,
entrance: 6, entrance: 6,
targetDirectory: '/', // TODO: targetDirectory: '/', // work:
fileName: file.fileName, fileName: file.fileName,
localDirectory: `/${file.fileName}`, localDirectory: `/${file.fileName}`,
fileSize: BigInt(file.fileSize), fileSize: BigInt(file.fileSize),

View File

@@ -40,7 +40,7 @@ class UploadGroupImage extends PacketTransformer<typeof proto.NTV2RichMediaResp>
fileName: img.name, fileName: img.name,
type: { type: {
type: 1, type: 1,
picFormat: img.picType, //TODO: extend NapCat imgType /cc @MliKiowa picFormat: img.picType, //work:extend NapCat imgType /cc @MliKiowa
videoFormat: 0, videoFormat: 0,
voiceFormat: 0, voiceFormat: 0,
}, },
@@ -59,7 +59,7 @@ class UploadGroupImage extends PacketTransformer<typeof proto.NTV2RichMediaResp>
extBizInfo: { extBizInfo: {
pic: { pic: {
bytesPbReserveTroop: Buffer.from("0800180020004200500062009201009a0100a2010c080012001800200028003a00", 'hex'), bytesPbReserveTroop: Buffer.from("0800180020004200500062009201009a0100a2010c080012001800200028003a00", 'hex'),
textSummary: "Nya~", // TODO: textSummary: "Nya~", // work:
}, },
video: { video: {
bytesPbReserve: Buffer.alloc(0), bytesPbReserve: Buffer.alloc(0),

View File

@@ -40,7 +40,7 @@ class UploadPrivateImage extends PacketTransformer<typeof proto.NTV2RichMediaRes
fileName: img.name, fileName: img.name,
type: { type: {
type: 1, type: 1,
picFormat: img.picType, //TODO: extend NapCat imgType /cc @MliKiowa picFormat: img.picType, //work:extend NapCat imgType /cc @MliKiowa
videoFormat: 0, videoFormat: 0,
voiceFormat: 0, voiceFormat: 0,
}, },
@@ -59,7 +59,7 @@ class UploadPrivateImage extends PacketTransformer<typeof proto.NTV2RichMediaRes
extBizInfo: { extBizInfo: {
pic: { pic: {
bytesPbReserveTroop: Buffer.from("0800180020004200500062009201009a0100a2010c080012001800200028003a00", 'hex'), bytesPbReserveTroop: Buffer.from("0800180020004200500062009201009a0100a2010c080012001800200028003a00", 'hex'),
textSummary: "Nya~", // TODO: textSummary: "Nya~", // work:
}, },
video: { video: {
bytesPbReserve: Buffer.alloc(0), bytesPbReserve: Buffer.alloc(0),

View File

@@ -5,17 +5,17 @@ import * as fs from 'fs';
import { CalculateStreamBytesTransform } from "@/core/packet/utils/crypto/sha1StreamBytesTransform"; import { CalculateStreamBytesTransform } from "@/core/packet/utils/crypto/sha1StreamBytesTransform";
function sha1Stream(readable: stream.Readable) { function sha1Stream(readable: stream.Readable) {
return new Promise((resolve, reject) => { return new Promise<Buffer>((resolve, reject) => {
readable.on('error', reject); readable.on('error', reject);
readable.pipe(crypto.createHash('sha1').on('error', reject).on('data', resolve)); readable.pipe(crypto.createHash('sha1').on('error', reject).on('data', resolve));
}) as Promise<Buffer>; });
} }
function md5Stream(readable: stream.Readable) { function md5Stream(readable: stream.Readable) {
return new Promise((resolve, reject) => { return new Promise<Buffer>((resolve, reject) => {
readable.on('error', reject); readable.on('error', reject);
readable.pipe(crypto.createHash('md5').on('error', reject).on('data', resolve)); readable.pipe(crypto.createHash('md5').on('error', reject).on('data', resolve));
}) as Promise<Buffer>; });
} }
export function calculateSha1(filePath: string): Promise<Buffer> { export function calculateSha1(filePath: string): Promise<Buffer> {
@@ -39,7 +39,7 @@ export function calculateSha1StreamBytes(filePath: string): Promise<Buffer[]> {
calculateStreamBytes.on('end', () => { calculateStreamBytes.on('end', () => {
resolve(byteArrayList); resolve(byteArrayList);
}); });
calculateStreamBytes.on('error', (err) => { calculateStreamBytes.on('error', (err: Error) => {
reject(err); reject(err);
}); });
readable.pipe(calculateStreamBytes); readable.pipe(calculateStreamBytes);

View File

@@ -45,11 +45,17 @@ export class Sha1Stream {
let e = this._state[4]; let e = this._state[4];
for (let i = 0; i < 80; i++) { for (let i = 0; i < 80; i++) {
const [f, k] = (i < 20) ? [(b & c) | ((~b) & d), 0x5A827999] : let temp;
(i < 40) ? [b ^ c ^ d, 0x6ED9EBA1] : if (i < 20) {
(i < 60) ? [(b & c) | (b & d) | (c & d), 0x8F1BBCDC] : temp = ((b & c) | (~b & d)) + 0x5A827999;
[b ^ c ^ d, 0xCA62C1D6]; } else if (i < 40) {
const temp = (this.rotateLeft(a, 5) + f + k + e + w[i]) >>> 0; temp = (b ^ c ^ d) + 0x6ED9EBA1;
} else if (i < 60) {
temp = ((b & c) | (b & d) | (c & d)) + 0x8F1BBCDC;
} else {
temp = (b ^ c ^ d) + 0xCA62C1D6;
}
temp += ((this.rotateLeft(a, 5) + e + w[i]) >>> 0);
e = d; e = d;
d = c; d = c;
c = this.rotateLeft(b, 30) >>> 0; c = this.rotateLeft(b, 30) >>> 0;

View File

@@ -3,7 +3,7 @@ import { Sha1Stream } from "@/core/packet/utils/crypto/sha1Stream";
export class CalculateStreamBytesTransform extends stream.Transform { export class CalculateStreamBytesTransform extends stream.Transform {
private readonly blockSize = 1024 * 1024; private readonly blockSize = 1024 * 1024;
private sha1: Sha1Stream; private readonly sha1: Sha1Stream;
private buffer: Buffer; private buffer: Buffer;
private bytesRead: number; private bytesRead: number;
private readonly byteArrayList: Buffer[]; private readonly byteArrayList: Buffer[];

View File

@@ -9,10 +9,10 @@ import {
type MiniAppTemplateNameList = "bili" | "weibo"; type MiniAppTemplateNameList = "bili" | "weibo";
export abstract class MiniAppInfo { export abstract class MiniAppInfo {
static sdkId: string = "V1_PC_MINISDK_99.99.99_1_APP_A"; static readonly sdkId: string = "V1_PC_MINISDK_99.99.99_1_APP_A";
template: MiniAppReqTemplateParams; template: MiniAppReqTemplateParams;
private static appMap = new Map<MiniAppTemplateNameList, MiniAppInfo>(); private static readonly appMap = new Map<MiniAppTemplateNameList, MiniAppInfo>();
protected constructor(template: MiniAppReqTemplateParams) { protected constructor(template: MiniAppReqTemplateParams) {
this.template = template; this.template = template;
@@ -22,7 +22,7 @@ export abstract class MiniAppInfo {
return this.appMap.get(name); return this.appMap.get(name);
} }
static Bili = new class extends MiniAppInfo { static readonly Bili = new class extends MiniAppInfo {
constructor() { constructor() {
super({ super({
sdkId: MiniAppInfo.sdkId, sdkId: MiniAppInfo.sdkId,
@@ -40,7 +40,7 @@ export abstract class MiniAppInfo {
} }
}; };
static WeiBo = new class extends MiniAppInfo { static readonly WeiBo = new class extends MiniAppInfo {
constructor() { constructor() {
super({ super({
sdkId: MiniAppInfo.sdkId, sdkId: MiniAppInfo.sdkId,

View File

@@ -89,7 +89,7 @@ export interface NodeIKernelGroupService {
isEssenceMsg(req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>; isEssenceMsg(req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>;
queryCachedEssenceMsg(req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<unknown>; queryCachedEssenceMsg(req: { groupCode: string, msgRandom: number, msgSeq: number }): Promise<{ items: Array<unknown> }>;
fetchGroupEssenceList(req: { fetchGroupEssenceList(req: {
groupCode: string, groupCode: string,

View File

@@ -29,10 +29,7 @@ import { NodeIKernelECDHService } from './services/NodeIKernelECDHService';
import { NodeIO3MiscService } from './services/NodeIO3MiscService'; import { NodeIO3MiscService } from './services/NodeIO3MiscService';
export interface NodeQQNTWrapperUtil { export interface NodeQQNTWrapperUtil {
get(): unknown; get(): NodeQQNTWrapperUtil;
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeQQNTWrapperUtil;
getNTUserDataInfoConfig(): string; getNTUserDataInfoConfig(): string;

View File

@@ -55,11 +55,12 @@ export async function NCoreInitFramework(
// await sleep(2500); // await sleep(2500);
// 初始化 NapCatFramework // 初始化 NapCatFramework
const loaderObject = new NapCatFramework(wrapper, session, logger, loginService, selfInfo, basicInfoWrapper, pathWrapper); const loaderObject = new NapCatFramework(wrapper, session, logger, loginService, selfInfo, basicInfoWrapper, pathWrapper);
await loaderObject.core.initCore();
//启动WebUi //启动WebUi
InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger)); InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger));
//初始化LLNC的Onebot实现 //初始化LLNC的Onebot实现
new NapCatOneBot11Adapter(loaderObject.core, loaderObject.context, pathWrapper); await new NapCatOneBot11Adapter(loaderObject.core, loaderObject.context, pathWrapper).InitOneBot();
} }
export class NapCatFramework { export class NapCatFramework {

Binary file not shown.

View File

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

View File

@@ -29,7 +29,7 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
}); });
return { return {
valid: false, valid: false,
message: errorMessages.join('\n') as string || '未知错误', message: errorMessages.join('\n') ?? '未知错误',
}; };
} }
return { return {

View File

@@ -6,7 +6,7 @@ export class GetFriendWithCategory extends BaseAction<void, any> {
actionName = ActionName.GetFriendsWithCategory; actionName = ActionName.GetFriendsWithCategory;
async _handle(payload: void) { async _handle(payload: void) {
return (await this.core.apis.FriendApi.getBuddyV2ExWithCate(true)).map(category => ({ return (await this.core.apis.FriendApi.getBuddyV2ExWithCate()).map(category => ({
...category, ...category,
buddyList: OB11Entities.friendsV2(category.buddyList), buddyList: OB11Entities.friendsV2(category.buddyList),
})); }));

View File

@@ -60,7 +60,7 @@ export class GetMiniAppArk extends GetPacketStatusDepends<Payload, {
if (payload.type) { if (payload.type) {
reqParam = MiniAppInfoHelper.generateReq(customParams, MiniAppInfo.get(payload.type)!.template); reqParam = MiniAppInfoHelper.generateReq(customParams, MiniAppInfo.get(payload.type)!.template);
} else { } else {
const { appId, scene, iconUrl, templateType, businessType, verType, shareType, versionId, withShareTicket } = payload as Required<Payload>; const { appId, scene, iconUrl, templateType, businessType, verType, shareType, versionId, withShareTicket } = payload;
reqParam = MiniAppInfoHelper.generateReq( reqParam = MiniAppInfoHelper.generateReq(
customParams, customParams,
{ {

View File

@@ -1,5 +1,5 @@
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName, BaseCheckResult } from '../types'; import { ActionName } from '../types';
interface Payload { interface Payload {
start: number, start: number,
@@ -13,9 +13,9 @@ export class GetProfileLike extends BaseAction<Payload, any> {
const start = payload.start ? Number(payload.start) : 0; const start = payload.start ? Number(payload.start) : 0;
const count = payload.count ? Number(payload.count) : 10; const count = payload.count ? Number(payload.count) : 10;
const ret = await this.core.apis.UserApi.getProfileLike(this.core.selfInfo.uid, start, count); const ret = await this.core.apis.UserApi.getProfileLike(this.core.selfInfo.uid, start, count);
const listdata: any[] = ret.info.userLikeInfos[0].voteInfo.userInfos; const listdata = ret.info.userLikeInfos[0].voteInfo.userInfos;
for (let i = 0; i < listdata.length; i++) { for (const item of listdata) {
listdata[i].uin = parseInt((await this.core.apis.UserApi.getUinByUidV2(listdata[i].uid)) || ''); item.uin = parseInt((await this.core.apis.UserApi.getUinByUidV2(item.uid)) || '');
} }
return ret.info.userLikeInfos[0].voteInfo; return ret.info.userLikeInfos[0].voteInfo;
} }

View File

@@ -21,7 +21,7 @@ export class OCRImage extends BaseAction<Payload, any> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.image)); const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.image));
if (!success) { if (!success) {
throw `OCR ${payload.image}失败,image字段可能格式不正确`; throw new Error(`OCR ${payload.image}失败,image字段可能格式不正确`);
} }
if (path) { if (path) {
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断 await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断
@@ -29,12 +29,12 @@ export class OCRImage extends BaseAction<Payload, any> {
fs.unlink(path, () => { }); fs.unlink(path, () => { });
if (!ret) { if (!ret) {
throw `OCR ${payload.file}失败`; throw new Error(`OCR ${payload.file}失败`);
} }
return ret.result; return ret.result;
} }
fs.unlink(path, () => { }); fs.unlink(path, () => { });
throw `OCR ${payload.file}失败,文件可能不存在`; throw new Error(`OCR ${payload.file}失败,文件可能不存在`);
} }
} }

View File

@@ -1,7 +1,7 @@
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, BaseCheckResult } from '../types'; import { ActionName } from '../types';
import { ChatType, Peer } from '@/core'; import { ChatType } from '@/core';
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
@@ -9,7 +9,7 @@ const SchemaData = {
event_type: { type: 'number' }, event_type: { type: 'number' },
user_id: { type: ['number', 'string'] }, user_id: { type: ['number', 'string'] },
}, },
required: ['event_type','user_id'], required: ['event_type', 'user_id'],
} as const satisfies JSONSchema; } as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>; type Payload = FromSchema<typeof SchemaData>;

View File

@@ -26,7 +26,7 @@ export default class SetAvatar extends BaseAction<Payload, null> {
async _handle(payload: Payload): Promise<null> { async _handle(payload: Payload): Promise<null> {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
if (!success) { if (!success) {
throw `头像${payload.file}设置失败,file字段可能格式不正确`; throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
} }
if (path) { if (path) {
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断 await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断
@@ -35,18 +35,18 @@ export default class SetAvatar extends BaseAction<Payload, null> {
}); });
if (!ret) { if (!ret) {
throw `头像${payload.file}设置失败,api无返回`; throw new Error(`头像${payload.file}设置失败,api无返回`);
} }
// log(`头像设置返回:${JSON.stringify(ret)}`) // log(`头像设置返回:${JSON.stringify(ret)}`)
if (ret.result as number == 1004022) { if (ret.result as number == 1004022) {
throw `头像${payload.file}设置失败,文件可能不是图片格式`; throw new Error(`头像${payload.file}设置失败,文件可能不是图片格式`);
} else if (ret.result != 0) { } else if (ret.result != 0) {
throw `头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`; throw new Error(`头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`);
} }
} else { } else {
fs.unlink(path, () => { }); fs.unlink(path, () => { });
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`; throw new Error(`头像${payload.file}设置失败,无法获取头像,文件可能不存在`);
} }
return null; return null;
} }

View File

@@ -61,7 +61,7 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
} }
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId); const rootMsgId = MessageUnique.getShortIdByMsgId(msgId);
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId || parseInt(msgId)); const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId ?? +msgId);
if (!rootMsg) { if (!rootMsg) {
throw new Error('msg not found'); throw new Error('msg not found');
} }

View File

@@ -31,14 +31,14 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString()); const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
const MsgCount = +(payload.count ?? 20); const MsgCount = +(payload.count ?? 20);
const isReverseOrder = typeof payload.reverseOrder === 'string' ? payload.reverseOrder === 'true' : !!payload.reverseOrder; const isReverseOrder = typeof payload.reverseOrder === 'string' ? payload.reverseOrder === 'true' : !!payload.reverseOrder;
if (!uid) throw `记录${payload.user_id}不存在`; if (!uid) throw new Error(`记录${payload.user_id}不存在`);
const friend = await this.core.apis.FriendApi.isBuddy(uid); const friend = await this.core.apis.FriendApi.isBuddy(uid);
const peer = { chatType: friend ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: uid }; const peer = { chatType: friend ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: uid };
const hasMessageSeq = !payload.message_seq ? !!payload.message_seq : !(payload.message_seq?.toString() === '' || payload.message_seq?.toString() === '0'); const hasMessageSeq = !payload.message_seq ? !!payload.message_seq : !(payload.message_seq?.toString() === '' || payload.message_seq?.toString() === '0');
const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0'; const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0';
const msgList = hasMessageSeq ? const msgList = hasMessageSeq ?
(await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList : (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, MsgCount)).msgList; (await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList : (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, MsgCount)).msgList;
if (msgList.length === 0) throw `消息${payload.message_seq}不存在`; if (msgList.length === 0) throw new Error(`消息${payload.message_seq}不存在`);
//翻转消息 //翻转消息
if (isReverseOrder) msgList.reverse(); if (isReverseOrder) msgList.reverse();
//转换序号 //转换序号

View File

@@ -15,7 +15,7 @@ type Payload = FromSchema<typeof SchemaData>;
export class GetGroupFileSystemInfo extends BaseAction<Payload, { export class GetGroupFileSystemInfo extends BaseAction<Payload, {
file_count: number, file_count: number,
limit_count: number, // unimplemented limit_count: number, // unimplemented
used_space: number, // todo: unimplemented, but can be implemented later used_space: number, // work:unimplemented, but can be implemented later
total_space: number, // unimplemented, 10 GB by default total_space: number, // unimplemented, 10 GB by default
}> { }> {
actionName = ActionName.GoCQHTTP_GetGroupFileSystemInfo; actionName = ActionName.GoCQHTTP_GetGroupFileSystemInfo;

View File

@@ -36,7 +36,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0'; const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0';
const msgList = hasMessageSeq ? const msgList = hasMessageSeq ?
(await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList : (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, MsgCount)).msgList; (await this.core.apis.MsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList : (await this.core.apis.MsgApi.getAioFirstViewLatestMsgs(peer, MsgCount)).msgList;
if (msgList.length === 0) throw `消息${payload.message_seq}不存在`; if (msgList.length === 0) throw new Error(`消息${payload.message_seq}不存在`);
//翻转消息 //翻转消息
if (isReverseOrder) msgList.reverse(); if (isReverseOrder) msgList.reverse();
//转换序号 //转换序号

View File

@@ -34,15 +34,15 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
success, success,
} = (await uri2local(this.core.NapCatTempPath, payload.image)); } = (await uri2local(this.core.NapCatTempPath, payload.image));
if (!success) { if (!success) {
throw `群公告${payload.image}设置失败,image字段可能格式不正确`; throw new Error(`群公告${payload.image}设置失败,image字段可能格式不正确`);
} }
if (!path) { if (!path) {
throw `群公告${payload.image}设置失败,获取资源失败`; throw new Error(`群公告${payload.image}设置失败,获取资源失败`);
} }
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断 await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断
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 `群公告${payload.image}设置失败,图片上传失败`; throw new Error(`群公告${payload.image}设置失败,图片上传失败`);
} }
unlink(path, () => { unlink(path, () => {

View File

@@ -27,24 +27,24 @@ export default class SetGroupPortrait extends BaseAction<Payload, any> {
async _handle(payload: Payload): Promise<any> { async _handle(payload: Payload): Promise<any> {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
if (!success) { if (!success) {
throw `头像${payload.file}设置失败,file字段可能格式不正确`; throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
} }
if (path) { if (path) {
await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断 await checkFileReceived(path, 5000); // 文件不存在QQ会崩溃需要提前判断
const ret = await this.core.apis.GroupApi.setGroupAvatar(payload.group_id.toString(), path); const ret = await this.core.apis.GroupApi.setGroupAvatar(payload.group_id.toString(), path);
fs.unlink(path, () => { }); fs.unlink(path, () => { });
if (!ret) { if (!ret) {
throw `头像${payload.file}设置失败,api无返回`; throw new Error(`头像${payload.file}设置失败,api无返回`);
} }
if (ret.result as number == 1004022) { if (ret.result as number == 1004022) {
throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`; throw new Error(`头像${payload.file}设置失败,文件可能不是图片格式或权限不足`);
} else if (ret.result != 0) { } else if (ret.result != 0) {
throw `头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`; throw new Error(`头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`);
} }
return ret; return ret;
} else { } else {
fs.unlink(path, () => {}); fs.unlink(path, () => { });
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`; throw new Error(`头像${payload.file}设置失败,无法获取头像,文件可能不存在`);
} }
} }
} }

View File

@@ -14,7 +14,7 @@ const SchemaData = {
type Payload = FromSchema<typeof SchemaData>; type Payload = FromSchema<typeof SchemaData>;
export class SetQQProfile extends BaseAction<Payload, any | null> { export class SetQQProfile extends BaseAction<Payload, any> {
actionName = ActionName.SetQQProfile; actionName = ActionName.SetQQProfile;
payloadSchema = SchemaData; payloadSchema = SchemaData;

View File

@@ -27,7 +27,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
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) {
throw `私聊${payload.user_id}不存在`; throw new Error( `私聊${payload.user_id}不存在`);
} }
const isBuddy = await this.core.apis.FriendApi.isBuddy(peerUid); const isBuddy = await this.core.apis.FriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid }; return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };

View File

@@ -3,7 +3,6 @@ import { OB11Entities } from '@/onebot/entities';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { GroupMember } from '@/core';
const SchemaData = { const SchemaData = {
type: 'object', type: 'object',
@@ -44,7 +43,7 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
} else { } else {
this.core.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息`); this.core.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息`);
} }
return OB11Entities.groupMember(payload.group_id.toString(), member as GroupMember); return OB11Entities.groupMember(payload.group_id.toString(), member);
} }
} }

View File

@@ -1,4 +1,3 @@
import { OB11Group } from '@/onebot';
import BaseAction from '../BaseAction'; import BaseAction from '../BaseAction';
import { ActionName } from '../types'; import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { FromSchema, JSONSchema } from 'json-schema-to-ts';

View File

@@ -26,7 +26,7 @@ export class SendGroupAiRecord extends GetPacketStatusDepends<Payload, {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const rawRsp = await this.core.apis.PacketApi.pkt.operation.GetAiVoice(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound); const rawRsp = await this.core.apis.PacketApi.pkt.operation.GetAiVoice(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound);
const url = await this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+payload.group_id, rawRsp.msgInfoBody[0].index); const url = await this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+payload.group_id, rawRsp.msgInfoBody[0].index);
const { path, fileName, errMsg, success } = (await uri2local(this.core.NapCatTempPath, url)); const { path, errMsg, success } = (await uri2local(this.core.NapCatTempPath, url));
if (!success) { if (!success) {
throw new Error(errMsg); throw new Error(errMsg);
} }

View File

@@ -28,7 +28,7 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
throw Error('参数message_id不能为空'); throw Error('参数message_id不能为空');
} }
const MsgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString()); const MsgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString());
const msgIdWithPeer = MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString())); const msgIdWithPeer = MessageUnique.getMsgIdAndPeerByShortId(MsgShortId ?? +payload.message_id);
if (!msgIdWithPeer) { if (!msgIdWithPeer) {
throw new Error('消息不存在'); throw new Error('消息不存在');
} }

View File

@@ -30,7 +30,7 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
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) {
throw `私聊${payload.user_id}不存在`; throw new Error( `私聊${payload.user_id}不存在`);
} }
const isBuddy = await this.core.apis.FriendApi.isBuddy(peerUid); const isBuddy = await this.core.apis.FriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid }; return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };

View File

@@ -68,7 +68,7 @@ export async function createContext(core: NapCatCore, payload: OB11PostContext,
} }
return { return {
chatType: ChatType.KCHATTYPEC2C, chatType: ChatType.KCHATTYPEC2C,
peerUid: Uid!, peerUid: Uid,
guildId: '', guildId: '',
}; };
} }
@@ -133,7 +133,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
guildId: '', guildId: '',
peerUid: peer.peerUid, peerUid: peer.peerUid,
chatType: peer.chatType, chatType: peer.chatType,
}, (returnMsgAndResId.message)!.msgId); }, (returnMsgAndResId.message).msgId);
return { message_id: msgShortId!, res_id: returnMsgAndResId.res_id }; return { message_id: msgShortId!, res_id: returnMsgAndResId.res_id };
} else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) { } else if (returnMsgAndResId.res_id && !returnMsgAndResId.message) {
throw Error(`发送转发消息res_id${returnMsgAndResId.res_id} 失败`); throw Error(`发送转发消息res_id${returnMsgAndResId.res_id} 失败`);
@@ -150,7 +150,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
const { sendElements, deleteAfterSentFiles } = await this.obContext.apis.MsgApi const { sendElements, deleteAfterSentFiles } = await this.obContext.apis.MsgApi
.createSendElements(messages, peer); .createSendElements(messages, peer);
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles); const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, deleteAfterSentFiles);
return { message_id: returnMsg!.id! }; return { message_id: returnMsg.id! };
} }
private async uploadForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[], source?: string, news?: { private async uploadForwardedNodesPacket(msgPeer: Peer, messageNodes: OB11MessageNode[], source?: string, news?: {
@@ -175,7 +175,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
if (getSpecialMsgNum({ message: OB11Data }, OB11MessageDataType.node)) { if (getSpecialMsgNum({ message: OB11Data }, OB11MessageDataType.node)) {
const uploadReturnData = await this.uploadForwardedNodesPacket(msgPeer, OB11Data as OB11MessageNode[], node.data.source, node.data.news, node.data.summary, node.data.prompt, { const uploadReturnData = await this.uploadForwardedNodesPacket(msgPeer, OB11Data as OB11MessageNode[], node.data.source, node.data.news, node.data.summary, node.data.prompt, {
user_id: (node.data.user_id || node.data.uin)?.toString() ?? parentMeta?.user_id ?? this.core.selfInfo.uin, user_id: (node.data.user_id ?? node.data.uin)?.toString() ?? parentMeta?.user_id ?? this.core.selfInfo.uin,
nickname: (node.data.nickname || node.data.name) ?? parentMeta?.nickname ?? "QQ用户", nickname: (node.data.nickname || node.data.name) ?? parentMeta?.nickname ?? "QQ用户",
}, dp + 1); }, dp + 1);
sendElements = uploadReturnData?.finallySendElements ? [uploadReturnData.finallySendElements] : []; sendElements = uploadReturnData?.finallySendElements ? [uploadReturnData.finallySendElements] : [];
@@ -185,7 +185,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
} }
const packetMsgElements: rawMsgWithSendMsg = { const packetMsgElements: rawMsgWithSendMsg = {
senderUin: Number((node.data.user_id || node.data.uin) ?? parentMeta?.user_id) || +this.core.selfInfo.uin, senderUin: Number((node.data.user_id ?? node.data.uin) ?? parentMeta?.user_id) || +this.core.selfInfo.uin,
senderName: (node.data.nickname || node.data.name) ?? parentMeta?.nickname ?? "QQ用户", senderName: (node.data.nickname || node.data.name) ?? parentMeta?.nickname ?? "QQ用户",
groupId: msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : undefined, groupId: msgPeer.chatType === ChatType.KCHATTYPEGROUP ? +msgPeer.peerUid : undefined,
time: Number(node.data.time) || Date.now(), time: Number(node.data.time) || Date.now(),
@@ -194,7 +194,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
logger.logDebug(`handleForwardedNodesPacket[SendRaw] 开始转换 ${stringifyWithBigInt(packetMsgElements)}`); logger.logDebug(`handleForwardedNodesPacket[SendRaw] 开始转换 ${stringifyWithBigInt(packetMsgElements)}`);
const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgWithSendMsgToPacketMsg(packetMsgElements); const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgWithSendMsgToPacketMsg(packetMsgElements);
logger.logDebug(`handleForwardedNodesPacket[SendRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`); logger.logDebug(`handleForwardedNodesPacket[SendRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`);
packetMsg.push(transformedMsg!); packetMsg.push(transformedMsg);
} else if (node.data.id) { } else if (node.data.id) {
const id = node.data.id; const id = node.data.id;
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(+id) || MessageUnique.getPeerByMsgId(id); const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(+id) || MessageUnique.getPeerByMsgId(id);
@@ -207,7 +207,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
await this.core.apis.FileApi.downloadRawMsgMedia([msg]); await this.core.apis.FileApi.downloadRawMsgMedia([msg]);
const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgToPacketMsg(msg, msgPeer); const transformedMsg = this.core.apis.PacketApi.pkt.msgConverter.rawMsgToPacketMsg(msg, msgPeer);
logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`); logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${stringifyWithBigInt(transformedMsg)}`);
packetMsg.push(transformedMsg!); packetMsg.push(transformedMsg);
} else { } else {
logger.logDebug(`handleForwardedNodesPacket 跳过元素 ${stringifyWithBigInt(node)}`); logger.logDebug(`handleForwardedNodesPacket 跳过元素 ${stringifyWithBigInt(node)}`);
} }

View File

@@ -7,7 +7,7 @@ export abstract class GetPacketStatusDepends<PT, RT> extends BaseAction<PT, RT>
protected async check(payload: PT): Promise<BaseCheckResult>{ protected async check(payload: PT): Promise<BaseCheckResult>{
if (!this.core.apis.PacketApi.available) { if (!this.core.apis.PacketApi.available) {
// TODO: add error stack? // work:add error stack?
return { return {
valid: false, valid: false,
message: "packetBackend不可用请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置" + message: "packetBackend不可用请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置" +

View File

@@ -18,13 +18,11 @@ export default class SendLike extends BaseAction<Payload, null> {
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> { async _handle(payload: Payload): Promise<null> {
//logDebug('点赞参数', payload);
const qq = payload.user_id.toString(); const qq = payload.user_id.toString();
const uid: string = await this.core.apis.UserApi.getUidByUinV2(qq) || ''; const uid: string = await this.core.apis.UserApi.getUidByUinV2(qq) ?? '';
const result = await this.core.apis.UserApi.like(uid, parseInt(payload.times?.toString()) || 1); const result = await this.core.apis.UserApi.like(uid, parseInt(payload.times?.toString()) || 1);
//logDebug('点赞结果', result);
if (result.result !== 0) { if (result.result !== 0) {
throw `点赞失败 ${result.errMsg}`; throw new Error(`点赞失败 ${result.errMsg}`);
} }
return null; return null;
} }

View File

@@ -40,7 +40,7 @@ export class OneBotGroupApi {
if (msg.senderUin && msg.senderUin !== '0') { if (msg.senderUin && msg.senderUin !== '0') {
const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUid, msg.senderUin); const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUid, msg.senderUin);
if (member && member.cardName !== msg.sendMemberName) { if (member && member.cardName !== msg.sendMemberName) {
const newCardName = msg.sendMemberName || ''; const newCardName = msg.sendMemberName ?? '';
const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName); const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
member.cardName = newCardName; member.cardName = newCardName;
return event; return event;
@@ -48,7 +48,7 @@ export class OneBotGroupApi {
} }
for (const element of msg.elements) { for (const element of msg.elements) {
if (element.grayTipElement && element.grayTipElement.groupElement) { if (element.grayTipElement?.groupElement) {
const groupElement = element.grayTipElement.groupElement; const groupElement = element.grayTipElement.groupElement;
if (groupElement.type == TipGroupElementType.memberIncrease) { if (groupElement.type == TipGroupElementType.memberIncrease) {
const MemberIncreaseEvent = await this.obContext.apis.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement); const MemberIncreaseEvent = await this.obContext.apis.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement);
@@ -83,7 +83,7 @@ export class OneBotGroupApi {
url: pathToFileURL(element.fileElement.filePath).href, url: pathToFileURL(element.fileElement.filePath).href,
name: element.fileElement.fileName, name: element.fileElement.fileName,
size: parseInt(element.fileElement.fileSize), size: parseInt(element.fileElement.fileSize),
busid: element.fileElement.fileBizId || 0, busid: element.fileElement.fileBizId ?? 0,
}, },
); );
} }
@@ -109,8 +109,8 @@ export class OneBotGroupApi {
return new OB11GroupPokeEvent( return new OB11GroupPokeEvent(
this.core, this.core,
parseInt(msg.peerUid), parseInt(msg.peerUid),
parseInt((await this.core.apis.UserApi.getUinByUidV2(poke_uid[0].uid))!), +await this.core.apis.UserApi.getUinByUidV2(poke_uid[0].uid),
parseInt((await this.core.apis.UserApi.getUinByUidV2(poke_uid[1].uid))!), +await this.core.apis.UserApi.getUinByUidV2(poke_uid[1].uid),
pokedetail, pokedetail,
); );
} }
@@ -165,13 +165,13 @@ export class OneBotGroupApi {
async parseGroupBanEvent(GroupCode: string, grayTipElement: GrayTipElement) { async parseGroupBanEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const groupElement = grayTipElement?.groupElement; const groupElement = grayTipElement?.groupElement;
if (!groupElement?.shutUp) return undefined; if (!groupElement?.shutUp) return undefined;
const memberUid = groupElement.shutUp!.member.uid; const memberUid = groupElement.shutUp.member.uid;
const adminUid = groupElement.shutUp!.admin.uid; const adminUid = groupElement.shutUp.admin.uid;
let memberUin: string; let memberUin: string;
let duration = parseInt(groupElement.shutUp!.duration); let duration = parseInt(groupElement.shutUp.duration);
const subType: 'ban' | 'lift_ban' = duration > 0 ? 'ban' : 'lift_ban'; const subType: 'ban' | 'lift_ban' = duration > 0 ? 'ban' : 'lift_ban';
if (memberUid) { if (memberUid) {
memberUin = (await this.core.apis.GroupApi.getGroupMember(GroupCode, memberUid))?.uin || ''; memberUin = (await this.core.apis.GroupApi.getGroupMember(GroupCode, memberUid))?.uin ?? '';
} else { } else {
memberUin = '0'; // 0表示全员禁言 memberUin = '0'; // 0表示全员禁言
if (duration > 0) { if (duration > 0) {
@@ -225,7 +225,7 @@ export class OneBotGroupApi {
const memberUin = member?.uin; const memberUin = member?.uin;
const adminMember = await this.core.apis.GroupApi.getGroupMember(GroupCode, groupElement.adminUid); const adminMember = await this.core.apis.GroupApi.getGroupMember(GroupCode, groupElement.adminUid);
if (memberUin) { if (memberUin) {
const operatorUin = adminMember?.uin || memberUin; const operatorUin = adminMember?.uin ?? memberUin;
return new OB11GroupIncreaseEvent( return new OB11GroupIncreaseEvent(
this.core, this.core,
parseInt(GroupCode), parseInt(GroupCode),
@@ -240,7 +240,7 @@ export class OneBotGroupApi {
async parseGroupKickEvent(GroupCode: string, grayTipElement: GrayTipElement) { async parseGroupKickEvent(GroupCode: string, grayTipElement: GrayTipElement) {
const groupElement = grayTipElement?.groupElement; const groupElement = grayTipElement?.groupElement;
if (!groupElement) return undefined; if (!groupElement) return undefined;
const adminUin = (await this.core.apis.GroupApi.getGroupMember(GroupCode, groupElement.adminUid))?.uin || (await this.core.apis.UserApi.getUidByUinV2(groupElement.adminUid)); const adminUin = (await this.core.apis.GroupApi.getGroupMember(GroupCode, groupElement.adminUid))?.uin ?? (await this.core.apis.UserApi.getUidByUinV2(groupElement.adminUid));
if (adminUin) { if (adminUin) {
return new OB11GroupDecreaseEvent( return new OB11GroupDecreaseEvent(
this.core, this.core,

View File

@@ -187,6 +187,7 @@ export class OneBotMsgApi {
url: url, url: url,
key: _.key, key: _.key,
emoji_id: _.emojiId, emoji_id: _.emojiId,
emoji_package_id: _.emojiPackageId,
file_unique: _.key file_unique: _.key
}, },
}; };
@@ -699,16 +700,16 @@ export class OneBotMsgApi {
//跳过空消息 //跳过空消息
const resMsg: OB11Message = { const resMsg: OB11Message = {
self_id: parseInt(this.core.selfInfo.uin), self_id: parseInt(this.core.selfInfo.uin),
user_id: parseInt(msg.senderUin!), user_id: parseInt(msg.senderUin),
time: parseInt(msg.msgTime) || Date.now(), time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.id!, message_id: msg.id!,
message_seq: msg.id!, message_seq: msg.id!,
real_id: msg.id!, real_id: msg.id!,
message_type: msg.chatType == ChatType.KCHATTYPEGROUP ? 'group' : 'private', message_type: msg.chatType == ChatType.KCHATTYPEGROUP ? 'group' : 'private',
sender: { sender: {
user_id: parseInt(msg.senderUin || '0'), user_id: +(msg.senderUin ?? 0),
nickname: msg.sendNickName, nickname: msg.sendNickName,
card: msg.sendMemberName || '', card: msg.sendMemberName ?? '',
}, },
raw_message: '', raw_message: '',
font: 14, font: 14,

View File

@@ -25,7 +25,7 @@ export class OB11Entities {
user_id: parseInt(rawFriend.coreInfo.uin), user_id: parseInt(rawFriend.coreInfo.uin),
nickname: rawFriend.coreInfo.nick, nickname: rawFriend.coreInfo.nick,
remark: rawFriend.coreInfo.remark ?? rawFriend.coreInfo.nick, remark: rawFriend.coreInfo.remark ?? rawFriend.coreInfo.nick,
sex: this.sex(rawFriend.baseInfo.sex!), sex: this.sex(rawFriend.baseInfo.sex),
level: 0, level: 0,
})); }));
} }
@@ -66,7 +66,7 @@ export class OB11Entities {
sex: OB11Entities.sex(member.sex!), sex: OB11Entities.sex(member.sex!),
age: member.age ?? 0, age: member.age ?? 0,
area: '', area: '',
level: member.memberRealLevel ?? '0', level: member.memberRealLevel?.toString() ?? '0',
qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0, qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0,
join_time: +member.joinTime, join_time: +member.joinTime,
last_sent_time: +member.lastSpeakTime, last_sent_time: +member.lastSpeakTime,
@@ -76,7 +76,7 @@ export class OB11Entities {
is_robot: member.isRobot, is_robot: member.isRobot,
shut_up_timestamp: member.shutUpTime, shut_up_timestamp: member.shutUpTime,
role: OB11Entities.groupMemberRole(member.role), role: OB11Entities.groupMemberRole(member.role),
title: member.memberSpecialTitle || '', title: member.memberSpecialTitle ?? '',
}; };
} }

View File

@@ -11,11 +11,11 @@ export class OB11HeartbeatEvent extends OB11BaseMetaEvent {
status: HeartbeatStatus; status: HeartbeatStatus;
interval: number; interval: number;
public constructor(core: NapCatCore, interval: number, isOnline: boolean | undefined, isGood: boolean) { public constructor(core: NapCatCore, interval: number, isOnline: boolean, isGood: boolean) {
super(core); super(core);
this.interval = interval; this.interval = interval;
this.status = { this.status = {
online: isOnline && true, online: isOnline,
good: isGood, good: isGood,
}; };
} }

View File

@@ -5,7 +5,7 @@ export type GroupDecreaseSubType = 'leave' | 'kick' | 'kick_me';
export class OB11GroupDecreaseEvent extends OB11GroupNoticeEvent { export class OB11GroupDecreaseEvent extends OB11GroupNoticeEvent {
notice_type = 'group_decrease'; notice_type = 'group_decrease';
sub_type: GroupDecreaseSubType = 'leave'; // TODO: 实现其他几种子类型的识别 ("leave" | "kick" | "kick_me") sub_type: GroupDecreaseSubType = 'leave'; // work:实现其他几种子类型的识别 ("leave" | "kick" | "kick_me")
operator_id: number; operator_id: number;
constructor(core: NapCatCore, groupId: number, userId: number, operatorId: number, subType: GroupDecreaseSubType = 'leave') { constructor(core: NapCatCore, groupId: number, userId: number, operatorId: number, subType: GroupDecreaseSubType = 'leave') {

View File

@@ -1,7 +1,7 @@
import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent'; import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
//输入状态事件 初步完成 Mlikio wa Todo 需要做一些过滤 //work: 输入状态事件 初步完成 Mlikiowa 需要做一些过滤
export class OB11InputStatusEvent extends OB11BaseNoticeEvent { export class OB11InputStatusEvent extends OB11BaseNoticeEvent {
notice_type = 'notify'; notice_type = 'notify';
sub_type = 'input_status'; sub_type = 'input_status';

View File

@@ -25,7 +25,7 @@ export class OB11GroupPokeEvent extends OB11PokeEvent {
raw_info: any; raw_info: any;
//raw_message nb等框架标准为string //raw_message nb等框架标准为string
constructor(core: NapCatCore, group_id: number, user_id: number = 0, target_id: number = 0, raw_message: any) { constructor(core: NapCatCore, group_id: number, user_id: number, target_id: number, raw_message: any) {
super(core); super(core);
this.group_id = group_id; this.group_id = group_id;
this.target_id = target_id; this.target_id = target_id;

View File

@@ -6,7 +6,6 @@ import {
GroupNotifyMsgStatus, GroupNotifyMsgStatus,
GroupNotifyMsgType, GroupNotifyMsgType,
InstanceContext, InstanceContext,
MsgSourceType,
NapCatCore, NapCatCore,
NodeIKernelBuddyListener, NodeIKernelBuddyListener,
NodeIKernelGroupListener, NodeIKernelGroupListener,
@@ -45,8 +44,6 @@ import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRec
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent'; import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
import { LRUCache } from '@/common/lru-cache'; import { LRUCache } from '@/common/lru-cache';
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener'; import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
import { Native } from '@/native';
import { decodeMessage, decodeRecallGroup } from "@/core/helper/adaptSysMessageDecoder";
import { BotOfflineEvent } from './event/notice/BotOfflineEvent'; import { BotOfflineEvent } from './event/notice/BotOfflineEvent';
//OneBot实现类 //OneBot实现类
@@ -58,8 +55,7 @@ export class NapCatOneBot11Adapter {
apis: StableOneBotApiWrapper; apis: StableOneBotApiWrapper;
networkManager: OB11NetworkManager; networkManager: OB11NetworkManager;
actions: ActionMap; actions: ActionMap;
nativeCore: Native | undefined; private readonly bootTime = Date.now() / 1000;
private bootTime = Date.now() / 1000;
recallMsgCache = new LRUCache<string, RawMessage>(100); recallMsgCache = new LRUCache<string, RawMessage>(100);
constructor(core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) { constructor(core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) {
@@ -75,41 +71,8 @@ export class NapCatOneBot11Adapter {
}; };
this.actions = createActionMap(this, core); this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager(); this.networkManager = new OB11NetworkManager();
// this.registerNative(core, context).catch(e => this.context.logger.logWarn.bind(this.context.logger)('初始化Native失败', e)).then(); }
this.InitOneBot()
.catch(e => this.context.logger.logError.bind(this.context.logger)('初始化OneBot失败', e));
}
async registerNative(core: NapCatCore, context: InstanceContext) {
try {
this.nativeCore = new Native(context.pathWrapper.binaryPath);
if (!this.nativeCore.inited) throw new Error('Native Not Init');
// this.nativeCore.registerRecallCallback(async (hex: string) => {
// try {
// const data = decodeMessage(Buffer.from(hex, 'hex'));
// //data.MsgHead.BodyInner.MsgType SubType
// const bodyInner = data.msgHead?.bodyInner;
// //context.logger.log("[appNative] Parse MsgType:" + bodyInner.msgType + " / SubType:" + bodyInner.subType);
// if (bodyInner && bodyInner.msgType == 732 && bodyInner.subType == 17 && data?.msgHead?.noifyData?.innerData) {
// const RecallData = Buffer.from(data?.msgHead?.noifyData?.innerData);
// //跳过 4字节 群号 + 不知道的1字节 +2字节 长度
// const uid = RecallData.readUint32BE();
// const buffer = Buffer.from(RecallData.toString('hex').slice(14), 'hex');
// const seq: number = decodeRecallGroup(buffer).recallDetails.subDetail.msgSeq;
// const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: uid.toString() };
// context.logger.log("[Native] 群消息撤回 Peer: " + uid.toString() + " / MsgSeq:" + seq);
// const msgs = await core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, seq.toString());
// this.recallMsgCache.put(msgs.msgList[0].msgId, msgs.msgList[0]);
// }
// } catch (error: any) {
// context.logger.logWarn("[Native] Error:", (error as Error).message, ' HEX:', hex);
// }
// });
} catch (error) {
context.logger.logWarn("[Native] Error:", (error as Error).message);
return;
}
}
async InitOneBot() { async InitOneBot() {
const selfInfo = this.core.selfInfo; const selfInfo = this.core.selfInfo;
const ob11Config = this.configLoader.configData; const ob11Config = this.configLoader.configData;
@@ -211,8 +174,7 @@ export class NapCatOneBot11Adapter {
} else { } else {
await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11ActiveHttpAdapter); await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11ActiveHttpAdapter);
} }
} else { } else if (now.http.enablePost) {
if (now.http.enablePost) {
const { added, removed } = this.findDifference<string>(prev.http.postUrls, now.http.postUrls); const { added, removed } = this.findDifference<string>(prev.http.postUrls, now.http.postUrls);
await this.networkManager.closeAdapterByPredicate( await this.networkManager.closeAdapterByPredicate(
adapter => adapter instanceof OB11ActiveHttpAdapter && removed.includes(adapter.url), adapter => adapter instanceof OB11ActiveHttpAdapter && removed.includes(adapter.url),
@@ -223,7 +185,7 @@ export class NapCatOneBot11Adapter {
)); ));
} }
} }
}
// check difference in passive websocket (Ws) // check difference in passive websocket (Ws)
if (prev.ws.enable !== now.ws.enable) { if (prev.ws.enable !== now.ws.enable) {
@@ -251,8 +213,7 @@ export class NapCatOneBot11Adapter {
adapter => adapter instanceof OB11ActiveWebSocketAdapter, adapter => adapter instanceof OB11ActiveWebSocketAdapter,
); );
} }
} else { } else if (now.reverseWs.enable) {
if (now.reverseWs.enable) {
const { added, removed } = this.findDifference<string>(prev.reverseWs.urls, now.reverseWs.urls); const { added, removed } = this.findDifference<string>(prev.reverseWs.urls, now.reverseWs.urls);
await this.networkManager.closeAdapterByPredicate( await this.networkManager.closeAdapterByPredicate(
adapter => adapter instanceof OB11ActiveWebSocketAdapter && removed.includes(adapter.url), adapter => adapter instanceof OB11ActiveWebSocketAdapter && removed.includes(adapter.url),
@@ -263,7 +224,7 @@ export class NapCatOneBot11Adapter {
)); ));
} }
} }
}
} }
private findDifference<T>(prev: T[], now: T[]): { added: T[], removed: T[] } { private findDifference<T>(prev: T[], now: T[]): { added: T[], removed: T[] } {
@@ -305,10 +266,8 @@ export class NapCatOneBot11Adapter {
}, },
m.msgId, m.msgId,
); );
// if (m.sourceType == MsgSourceType.K_DOWN_SOURCETYPE_AIOINNER) {
await this.emitMsg(m) await this.emitMsg(m)
.catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e)); .catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e));
// }
} }
}; };
@@ -368,7 +327,7 @@ export class NapCatOneBot11Adapter {
const requesterUin = await this.core.apis.UserApi.getUinByUidV2(req.friendUid); const requesterUin = await this.core.apis.UserApi.getUinByUidV2(req.friendUid);
await this.networkManager.emitEvent(new OB11FriendRequestEvent( await this.networkManager.emitEvent(new OB11FriendRequestEvent(
this.core, this.core,
parseInt(requesterUin!), +requesterUin,
req.extWords, req.extWords,
req.friendUid + '|' + req.reqTime, req.friendUid + '|' + req.reqTime,
)); ));
@@ -432,7 +391,7 @@ export class NapCatOneBot11Adapter {
} }
} else if (notify.type == GroupNotifyMsgType.MEMBER_LEAVE_NOTIFY_ADMIN || notify.type == GroupNotifyMsgType.KICK_MEMBER_NOTIFY_ADMIN) { } else if (notify.type == GroupNotifyMsgType.MEMBER_LEAVE_NOTIFY_ADMIN || notify.type == GroupNotifyMsgType.KICK_MEMBER_NOTIFY_ADMIN) {
this.context.logger.logDebug('有成员退出通知', notify); this.context.logger.logDebug('有成员退出通知', notify);
const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!; const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid));
let operatorId = member1Uin; let operatorId = member1Uin;
let subType: GroupDecreaseSubType = 'leave'; let subType: GroupDecreaseSubType = 'leave';
if (notify.user2.uid) { if (notify.user2.uid) {
@@ -458,7 +417,7 @@ export class NapCatOneBot11Adapter {
].includes(notify.type) && notify.status == GroupNotifyMsgStatus.KUNHANDLE) { ].includes(notify.type) && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
this.context.logger.logDebug('有加群请求'); this.context.logger.logDebug('有加群请求');
try { try {
let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!; let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid));
if (isNaN(parseInt(requestUin))) { if (isNaN(parseInt(requestUin))) {
requestUin = (await this.core.apis.UserApi.getUserDetailInfo(notify.user1.uid)).uin; requestUin = (await this.core.apis.UserApi.getUserDetailInfo(notify.user1.uid)).uin;
} }
@@ -540,10 +499,9 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug('转化为 OB11Message', ob11Msg); this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
if (debug) { if (debug) {
ob11Msg.raw = message; ob11Msg.raw = message;
} else { } else if (ob11Msg.message.length === 0) {
if (ob11Msg.message.length === 0) {
return; return;
}
} }
const isSelfMsg = ob11Msg.user_id.toString() == this.core.selfInfo.uin; const isSelfMsg = ob11Msg.user_id.toString() == this.core.selfInfo.uin;
if (isSelfMsg && !reportSelfMessage) { if (isSelfMsg && !reportSelfMessage) {
@@ -603,7 +561,7 @@ export class NapCatOneBot11Adapter {
for (const message of msgList) { for (const message of msgList) {
// log("message update", message.sendStatus, message.msgId, message.msgSeq) // log("message update", message.sendStatus, message.msgId, message.msgSeq)
const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' }; const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' };
if (message.recallTime != '0' && !cache.get(message.msgId)) { //todo: 这个判断方法不太好,应该使用灰色消息元素来判断? if (message.recallTime != '0' && !cache.get(message.msgId)) { //work:这个判断方法不太好,应该使用灰色消息元素来判断?
cache.put(message.msgId, true); cache.put(message.msgId, true);
// 撤回消息上报 // 撤回消息上报
let oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId); let oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId);
@@ -613,7 +571,7 @@ export class NapCatOneBot11Adapter {
if (message.chatType == ChatType.KCHATTYPEC2C) { if (message.chatType == ChatType.KCHATTYPEC2C) {
const friendRecallEvent = new OB11FriendRecallNoticeEvent( const friendRecallEvent = new OB11FriendRecallNoticeEvent(
this.core, this.core,
parseInt(message!.senderUin), +message.senderUin,
oriMessageId, oriMessageId,
); );
this.networkManager.emitEvent(friendRecallEvent) this.networkManager.emitEvent(friendRecallEvent)
@@ -624,13 +582,13 @@ export class NapCatOneBot11Adapter {
const operatorUid = element.grayTipElement?.revokeElement.operatorUid; const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
if (!operatorUid) return; if (!operatorUid) return;
const operator = await this.core.apis.GroupApi.getGroupMember(message.peerUin, operatorUid); const operator = await this.core.apis.GroupApi.getGroupMember(message.peerUin, operatorUid);
operatorId = operator?.uin || message.senderUin; operatorId = operator?.uin ?? message.senderUin;
} }
const groupRecallEvent = new OB11GroupRecallNoticeEvent( const groupRecallEvent = new OB11GroupRecallNoticeEvent(
this.core, this.core,
parseInt(message.peerUin), +message.peerUin,
parseInt(message.senderUin), +message.senderUin,
parseInt(operatorId), +operatorId,
oriMessageId oriMessageId
); );
this.networkManager.emitEvent(groupRecallEvent) this.networkManager.emitEvent(groupRecallEvent)

View File

@@ -18,7 +18,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
public url: string, public url: string,
public reconnectIntervalInMillis: number, public reconnectIntervalInMillis: number,
public heartbeatIntervalInMillis: number, public heartbeatIntervalInMillis: number,
private token: string, private readonly token: string,
public core: NapCatCore, public core: NapCatCore,
public actions: ActionMap, public actions: ActionMap,
) { ) {
@@ -37,7 +37,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
} }
this.heartbeatRef = setInterval(() => { this.heartbeatRef = setInterval(() => {
if (this.connection && this.connection.readyState === WebSocket.OPEN) { if (this.connection && this.connection.readyState === WebSocket.OPEN) {
this.connection.send(JSON.stringify(new OB11HeartbeatEvent(this.core, this.heartbeatIntervalInMillis, this.core.selfInfo.online, true))); this.connection.send(JSON.stringify(new OB11HeartbeatEvent(this.core, this.heartbeatIntervalInMillis, this.core.selfInfo.online ?? true, true)));
} }
}, this.heartbeatIntervalInMillis); }, this.heartbeatIntervalInMillis);
await this.tryConnect(); await this.tryConnect();
@@ -148,7 +148,6 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
return; return;
} }
const retdata = await action.websocketHandle(receiveData.params, echo ?? ''); const retdata = await action.websocketHandle(receiveData.params, echo ?? '');
const packet = Object.assign({}, retdata); this.checkStateAndReply<any>({ ...retdata });
this.checkStateAndReply<any>(packet);
} }
} }

View File

@@ -145,7 +145,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
this.wsClientsMutex.runExclusive(async () => { this.wsClientsMutex.runExclusive(async () => {
this.wsClientWithEvent.forEach((wsClient) => { this.wsClientWithEvent.forEach((wsClient) => {
if (wsClient.readyState === WebSocket.OPEN) { if (wsClient.readyState === WebSocket.OPEN) {
wsClient.send(JSON.stringify(new OB11HeartbeatEvent(this.core, this.heartbeatInterval, this.core.selfInfo.online, true))); wsClient.send(JSON.stringify(new OB11HeartbeatEvent(this.core, this.heartbeatInterval, this.core.selfInfo.online ?? true, true)));
} }
}); });
}); });
@@ -189,8 +189,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
return; return;
} }
const retdata = await action.websocketHandle(receiveData.params, echo ?? ''); const retdata = await action.websocketHandle(receiveData.params, echo ?? '');
const packet = Object.assign({}, retdata); this.checkStateAndReply<any>({ ...retdata }, wsClient);
this.checkStateAndReply<any>(packet, wsClient);
} }
} }

View File

@@ -34,33 +34,16 @@ program.option('-q, --qq [number]', 'QQ号').parse(process.argv);
const cmdOptions = program.opts(); const cmdOptions = program.opts();
// NapCat Shell App ES 入口文件 // NapCat Shell App ES 入口文件
export async function NCoreInitShell() { async function handleUncaughtExceptions(logger: LogWrapper) {
console.log('NapCat Shell App Loading...');
process.on('uncaughtException', (err) => { process.on('uncaughtException', (err) => {
console.log('[NapCat] [Error] Unhandled Exception:', err.message); logger.logError('[NapCat] [Error] Unhandled Exception:', err.message);
}); });
process.on('unhandledRejection', (reason, promise) => { process.on('unhandledRejection', (reason, promise) => {
console.log('[NapCat] [Error] unhandledRejection:', reason); logger.logError('[NapCat] [Error] unhandledRejection:', reason);
}); });
const pathWrapper = new NapCatPathWrapper(); }
const logger = new LogWrapper(pathWrapper.logsPath);
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
const o3Service = wrapper.NodeIO3MiscService.get(); function getDataPaths(wrapper: WrapperNodeApi): [string, string] {
o3Service.addO3MiscListener(new NodeIO3MiscListener());
logger.log(`[NapCat] [Core] NapCat.Core Version: ` + napCatVersion);
InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger));
// from constructor
const engine = wrapper.NodeIQQNTWrapperEngine.get();
//const util = wrapper.NodeQQNTWrapperUtil.get();
const loginService = wrapper.NodeIKernelLoginService.get();
const session = wrapper.NodeIQQNTWrapperSession.create();
// from get dataPath
const [dataPath, dataPathGlobal] = (() => {
if (os.platform() === 'darwin') { if (os.platform() === 'darwin') {
const userPath = os.homedir(); const userPath = os.homedir();
const appDataPath = path.resolve(userPath, './Library/Application Support/QQ'); const appDataPath = path.resolve(userPath, './Library/Application Support/QQ');
@@ -73,15 +56,24 @@ 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];
})(); }
function getPlatformType(): PlatformType {
const platformMapping: Partial<Record<NodeJS.Platform, PlatformType>> = { const platformMapping: Partial<Record<NodeJS.Platform, PlatformType>> = {
win32: PlatformType.KWINDOWS, win32: PlatformType.KWINDOWS,
darwin: PlatformType.KMAC, darwin: PlatformType.KMAC,
linux: PlatformType.KLINUX, linux: PlatformType.KLINUX,
}; };
const systemPlatform = platformMapping[os.platform()] ?? PlatformType.KWINDOWS; return platformMapping[os.platform()] ?? PlatformType.KWINDOWS;
if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined'); }
// from initConfig
async function initializeEngine(
engine: any,
basicInfoWrapper: QQBasicInfoWrapper,
dataPathGlobal: string,
systemPlatform: PlatformType,
systemVersion: string
) {
engine.initWithDeskTopConfig( engine.initWithDeskTopConfig(
{ {
base_path_prefix: '', base_path_prefix: '',
@@ -98,33 +90,38 @@ export async function NCoreInitShell() {
}, },
new NodeIGlobalAdapter(), new NodeIGlobalAdapter(),
); );
}
async function initializeLoginService(
loginService: NodeIKernelLoginService,
basicInfoWrapper: QQBasicInfoWrapper,
dataPathGlobal: string,
systemVersion: string,
hostname: string
) {
loginService.initConfig({ loginService.initConfig({
machineId: '', machineId: '',
appid: basicInfoWrapper.QQVersionAppid, appid: basicInfoWrapper.QQVersionAppid ?? '',
platVer: systemVersion, platVer: systemVersion,
commonPath: dataPathGlobal, commonPath: dataPathGlobal,
clientVer: basicInfoWrapper.getFullQQVesion(), clientVer: basicInfoWrapper.getFullQQVesion(),
hostName: hostname, hostName: hostname,
}); });
}
let quickLoginUin = cmdOptions.qq; // undefined | 'true' | string async function handleLogin(
const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList; loginService: NodeIKernelLoginService,
if (quickLoginUin == 'true') { logger: LogWrapper,
if (historyLoginList.length > 0) { pathWrapper: NapCatPathWrapper,
quickLoginUin = historyLoginList[0].uin; quickLoginUin: string | undefined,
logger.log(`-q 指令指定使用最近的 QQ ${quickLoginUin} 进行快速登录`); historyLoginList: any[]
} else { ): Promise<SelfInfo> {
quickLoginUin = ''; return new Promise<SelfInfo>((resolve) => {
}
}
const dataTimestape = new Date().getTime().toString();
o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']);
const selfInfo = await new Promise<SelfInfo>((resolve) => {
const loginListener = new NodeIKernelLoginListener(); const loginListener = new NodeIKernelLoginListener();
let isLogined = false; let isLogined = false;
// from constructor
loginListener.onUserLoggedIn = (userid: string) => { loginListener.onUserLoggedIn = (userid: string) => {
logger.logError.bind(logger)(`当前账号(${userid})已登录,无法重复登录`); logger.logError(`当前账号(${userid})已登录,无法重复登录`);
}; };
loginListener.onQRCodeLoginSucceed = async (loginResult) => { loginListener.onQRCodeLoginSucceed = async (loginResult) => {
@@ -132,13 +129,12 @@ export async function NCoreInitShell() {
resolve({ resolve({
uid: loginResult.uid, uid: loginResult.uid,
uin: loginResult.uin, uin: loginResult.uin,
nick: '', // 获取不到 nick: '',
online: true, online: true,
}); });
}; };
loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => { loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {
//设置WebuiQrcode
WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl); WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl);
const realBase64 = pngBase64QrcodeData.replace(/^data:image\/\w+;base64,/, ''); const realBase64 = pngBase64QrcodeData.replace(/^data:image\/\w+;base64,/, '');
@@ -157,50 +153,46 @@ export async function NCoreInitShell() {
}); });
}); });
}; };
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => { loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
if (!isLogined) { if (!isLogined) {
logger.logError.bind(logger)('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg); logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg);
if (errType == 1 && errCode == 3) { if (errType == 1 && errCode == 3) {
// 二维码过期刷新 // 二维码过期刷新
} }
loginService.getQRCodePicture(); loginService.getQRCodePicture();
} }
}; };
loginListener.onLoginFailed = (args) => { loginListener.onLoginFailed = (args) => {
logger.logError.bind(logger)('[Core] [Login] Login Error , ErrInfo: ', args); logger.logError('[Core] [Login] Login Error , ErrInfo: ', args);
}; };
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger)); loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger));
const isConnect = loginService.connect(); const isConnect = loginService.connect();
if (!isConnect) { if (!isConnect) {
logger.logError.bind(logger)('核心登录服务连接失败!'); logger.logError('核心登录服务连接失败!');
return; return;
} }
logger.log('核心登录服务连接成功!'); logger.log('核心登录服务连接成功!');
// 实现WebUi快速登录
loginService.getLoginList().then((res) => { loginService.getLoginList().then((res) => {
// 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList // 遍历 res.LocalLoginInfoList[x].isQuickLogin是否可以 res.LocalLoginInfoList[x].uin 转为string 加入string[] 最后遍历完成调用WebUiDataRuntime.setQQQuickLoginList
WebUiDataRuntime.setQQQuickLoginList(res.LocalLoginInfoList.filter((item) => item.isQuickLogin).map((item) => item.uin.toString())); WebUiDataRuntime.setQQQuickLoginList(res.LocalLoginInfoList.filter((item) => item.isQuickLogin).map((item) => item.uin.toString()));
}); });
if (basicInfoWrapper.QQVersionConfig?.curVersion) {
loginService.getLoginMiscData('hotUpdateSign').then((res) => {
if (res.result === 0) {
loginService.setLoginMiscData('hotUpdateSign', res.value);
}
});
session.getNodeMiscService().writeVersionToRegistry(basicInfoWrapper.QQVersionConfig?.curVersion);
}
WebUiDataRuntime.setQuickLoginCall(async (uin: string) => { WebUiDataRuntime.setQuickLoginCall(async (uin: string) => {
return await new Promise((resolve) => { return await new Promise((resolve) => {
if (uin) { if (uin) {
logger.log.bind(logger)('正在快速登录 ', uin); logger.log('正在快速登录 ', uin);
loginService.quickLoginWithUin(uin).then(res => { loginService.quickLoginWithUin(uin).then(res => {
if (res.loginErrorInfo.errMsg) { if (res.loginErrorInfo.errMsg) {
resolve({ result: false, message: res.loginErrorInfo.errMsg }); resolve({ result: false, message: res.loginErrorInfo.errMsg });
} }
resolve({ result: true, message: '' }); resolve({ result: true, message: '' });
}).catch((e) => { }).catch((e) => {
logger.logError.bind(logger)(e); logger.logError(e);
resolve({ result: false, message: '快速登录发生错误' }); resolve({ result: false, message: '快速登录发生错误' });
}); });
} else { } else {
@@ -216,14 +208,14 @@ export async function NCoreInitShell() {
loginService.quickLoginWithUin(quickLoginUin) loginService.quickLoginWithUin(quickLoginUin)
.then(result => { .then(result => {
if (result.loginErrorInfo.errMsg) { if (result.loginErrorInfo.errMsg) {
logger.logError.bind(logger)('快速登录错误:', result.loginErrorInfo.errMsg); logger.logError('快速登录错误:', result.loginErrorInfo.errMsg);
if (!isLogined) loginService.getQRCodePicture(); if (!isLogined) loginService.getQRCodePicture();
} }
}) })
.catch(); .catch();
}, 1000); }, 1000);
} else { } else {
logger.logError.bind(logger)('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式'); logger.logError('快速登录失败,未找到该 QQ 历史登录记录,将使用二维码登录方式');
if (!isLogined) loginService.getQRCodePicture(); if (!isLogined) loginService.getQRCodePicture();
} }
} else { } else {
@@ -237,37 +229,20 @@ export async function NCoreInitShell() {
loginService.getQRCodePicture(); loginService.getQRCodePicture();
} }
}); });
// BEFORE LOGGING IN }
const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));
// AFTER LOGGING IN
//99b15bdb4c984fc69d5aa1feb9aa16xx --> 99b15bdb-4c98-4fc6-9d5a-a1feb9aa16xx
//把guid从左向右转换为guid格式 loginService.getMachineGuid()
let guid = loginService.getMachineGuid(); async function initializeSession(
guid = guid.slice(0, 8) + '-' + guid.slice(8, 12) + '-' + guid.slice(12, 16) + '-' + guid.slice(16, 20) + '-' + guid.slice(20); session: NodeIQQNTWrapperSession,
//console.log('guid:', guid); sessionConfig: any,
//NodeIO3MiscService/reportAmgomWeather login a6 [ '1726748166943', '184', '329' ] logger: LogWrapper
o3Service.reportAmgomWeather('login', 'a6', [dataTimestape, '184', '329']); ) {
// if(session.getUnitedConfigService()){ return new Promise<void>((resolve, reject) => {
// session.getUnitedConfigService().fetchUnitedCommendConfig([]);
// }
// from initSession
await new Promise<void>(async (resolve, reject) => {
const sessionConfig = await genSessionConfig(
guid,
basicInfoWrapper.QQVersionAppid!,
basicInfoWrapper.getFullQQVesion(),
selfInfo.uin,
selfInfo.uid,
dataPath,
);
const sessionListener = new NodeIKernelSessionListener(); const sessionListener = new NodeIKernelSessionListener();
sessionListener.onSessionInitComplete = (r: unknown) => { sessionListener.onSessionInitComplete = (r: unknown) => {
if (r === 0) { if (r === 0) {
resolve(); resolve();
} else { } else {
reject(r); reject(new Error('登录异常' + r?.toString()));
} }
}; };
session.init( session.init(
@@ -278,21 +253,74 @@ export async function NCoreInitShell() {
); );
try { try {
session.startNT(0); session.startNT(0);
} catch (_) { /* Empty */ } catch (_) {
try { try {
session.startNT(); session.startNT();
} catch (e) { } catch (e: unknown) {
reject('init failed ' + e); reject(new Error('init failed ' + (e as Error).message));
} }
} }
}); });
// Initialization end! }
export async function NCoreInitShell() {
console.log('NapCat Shell App Loading...');
const pathWrapper = new NapCatPathWrapper();
const logger = new LogWrapper(pathWrapper.logsPath);
handleUncaughtExceptions(logger);
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
const o3Service = wrapper.NodeIO3MiscService.get();
o3Service.addO3MiscListener(new NodeIO3MiscListener());
logger.log(`[NapCat] [Core] NapCat.Core Version: ` + napCatVersion);
InitWebUi(logger, pathWrapper).then().catch(logger.logError.bind(logger));
const engine = wrapper.NodeIQQNTWrapperEngine.get();
const loginService = wrapper.NodeIKernelLoginService.get();
const session = wrapper.NodeIQQNTWrapperSession.create();
const [dataPath, dataPathGlobal] = getDataPaths(wrapper);
const systemPlatform = getPlatformType();
if (!basicInfoWrapper.QQVersionAppid || !basicInfoWrapper.QQVersionQua) throw new Error('QQVersionAppid or QQVersionQua is not defined');
await initializeEngine(engine, basicInfoWrapper, dataPathGlobal, systemPlatform, systemVersion);
await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
const quickLoginUin = cmdOptions.qq;
const historyLoginList = (await loginService.getLoginList()).LocalLoginInfoList;
const dataTimestape = new Date().getTime().toString();
o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']);
const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList);
const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));
let guid = loginService.getMachineGuid();
guid = guid.slice(0, 8) + '-' + guid.slice(8, 12) + '-' + guid.slice(12, 16) + '-' + guid.slice(16, 20) + '-' + guid.slice(20);
o3Service.reportAmgomWeather('login', 'a6', [dataTimestape, '184', '329']);
const sessionConfig = await genSessionConfig(
guid,
basicInfoWrapper.QQVersionAppid,
basicInfoWrapper.getFullQQVesion(),
selfInfo.uin,
selfInfo.uid,
dataPath,
);
await initializeSession(session, sessionConfig, logger);
const accountDataPath = path.resolve(dataPath, './NapCat/data'); const accountDataPath = path.resolve(dataPath, './NapCat/data');
fs.mkdirSync(dataPath, { recursive: true }); fs.mkdirSync(dataPath, { recursive: true });
logger.logDebug('本账号数据/缓存目录:', accountDataPath); logger.logDebug('本账号数据/缓存目录:', accountDataPath);
new NapCatShell( await new NapCatShell(
wrapper, wrapper,
session, session,
logger, logger,
@@ -300,9 +328,10 @@ export async function NCoreInitShell() {
selfInfo, selfInfo,
basicInfoWrapper, basicInfoWrapper,
pathWrapper, pathWrapper,
); ).InitNapCat();
} }
export class NapCatShell { export class NapCatShell {
readonly core: NapCatCore; readonly core: NapCatCore;
readonly context: InstanceContext; readonly context: InstanceContext;
@@ -327,8 +356,13 @@ export class NapCatShell {
}; };
this.core = new NapCatCore(this.context, selfInfo); this.core = new NapCatCore(this.context, selfInfo);
// TODO: complete ob11 adapter initialization logic
new NapCatOneBot11Adapter(this.core, this.context, pathWrapper);
}
async InitNapCat() {
await this.core.initCore();
new NapCatOneBot11Adapter(this.core, this.context, this.context.pathWrapper).InitOneBot()
.catch(e => this.context.logger.logError.bind(this.context.logger)('初始化OneBot失败', e));
} }
} }

Some files were not shown because too many files have changed in this diff Show More