mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
Merge branch 'main' of https://github.com/NapNeko/NapCatQQ
This commit is contained in:
commit
b463140de7
10
package.json
10
package.json
@ -44,13 +44,13 @@
|
|||||||
"vite": "^5.2.6",
|
"vite": "^5.2.6",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-tsconfig-paths": "^5.1.0",
|
"vite-tsconfig-paths": "^5.1.0",
|
||||||
"winston": "^3.17.0"
|
"winston": "^3.17.0",
|
||||||
|
"fluent-ffmpeg": "^2.1.2"
|
||||||
},
|
},
|
||||||
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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,36 +181,36 @@ export class NTEventWrapper {
|
|||||||
callbackTimesToWait = 1,
|
callbackTimesToWait = 1,
|
||||||
timeout = 5000,
|
timeout = 5000,
|
||||||
) {
|
) {
|
||||||
|
const id = randomUUID();
|
||||||
|
let complete = 0;
|
||||||
|
let retData: Parameters<ListenerType> | undefined = undefined;
|
||||||
|
let retEvent: any = {};
|
||||||
|
|
||||||
|
function sendDataCallback(resolve: any, reject: any) {
|
||||||
|
if (complete == 0) {
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
'Timeout: NTEvent serviceAndMethod:' +
|
||||||
|
serviceAndMethod +
|
||||||
|
' ListenerName:' +
|
||||||
|
listenerAndMethod +
|
||||||
|
' EventRet:\n' +
|
||||||
|
JSON.stringify(retEvent, null, 4) +
|
||||||
|
'\n',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListenerNameList = listenerAndMethod.split('/');
|
||||||
|
const ListenerMainName = ListenerNameList[0];
|
||||||
|
const ListenerSubName = ListenerNameList[1];
|
||||||
|
|
||||||
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
|
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
|
||||||
async (resolve, reject) => {
|
(resolve, reject) => {
|
||||||
const id = randomUUID();
|
const timeoutRef = setTimeout(() => sendDataCallback(resolve, reject), timeout);
|
||||||
let complete = 0;
|
|
||||||
let retData: Parameters<ListenerType> | undefined = undefined;
|
|
||||||
let retEvent: any = {};
|
|
||||||
|
|
||||||
function sendDataCallback() {
|
|
||||||
if (complete == 0) {
|
|
||||||
reject(
|
|
||||||
new Error(
|
|
||||||
'Timeout: NTEvent serviceAndMethod:' +
|
|
||||||
serviceAndMethod +
|
|
||||||
' ListenerName:' +
|
|
||||||
listenerAndMethod +
|
|
||||||
' EventRet:\n' +
|
|
||||||
JSON.stringify(retEvent, null, 4) +
|
|
||||||
'\n',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ListenerNameList = listenerAndMethod.split('/');
|
|
||||||
const ListenerMainName = ListenerNameList[0];
|
|
||||||
const ListenerSubName = ListenerNameList[1];
|
|
||||||
|
|
||||||
const timeoutRef = setTimeout(sendDataCallback, 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,23 +233,26 @@ 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));
|
|
||||||
if (!checkerEvent(retEvent) && timeoutRef.hasRef()) {
|
|
||||||
clearTimeout(timeoutRef);
|
|
||||||
reject(
|
|
||||||
new Error(
|
|
||||||
'EventChecker Failed: NTEvent serviceAndMethod:' +
|
|
||||||
serviceAndMethod +
|
|
||||||
' ListenerName:' +
|
|
||||||
listenerAndMethod +
|
|
||||||
' EventRet:\n' +
|
|
||||||
JSON.stringify(retEvent, null, 4) +
|
|
||||||
'\n',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.createEventFunction(serviceAndMethod)!(...(args))
|
||||||
|
.then((eventResult: any) => {
|
||||||
|
retEvent = eventResult;
|
||||||
|
if (!checkerEvent(retEvent) && timeoutRef.hasRef()) {
|
||||||
|
clearTimeout(timeoutRef);
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
'EventChecker Failed: NTEvent serviceAndMethod:' +
|
||||||
|
serviceAndMethod +
|
||||||
|
' ListenerName:' +
|
||||||
|
listenerAndMethod +
|
||||||
|
' EventRet:\n' +
|
||||||
|
JSON.stringify(retEvent, null, 4) +
|
||||||
|
'\n',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -74,26 +74,30 @@ export class LogWrapper {
|
|||||||
}
|
}
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
const filePath = path.join(logDir, file);
|
const filePath = path.join(logDir, file);
|
||||||
fs.stat(filePath, (err, stats) => {
|
this.deleteOldLogFile(filePath, oneWeekAgo);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private deleteOldLogFile(filePath: string, oneWeekAgo: number) {
|
||||||
|
fs.stat(filePath, (err, stats) => {
|
||||||
|
if (err) {
|
||||||
|
this.logger.error('Failed to get file stats', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stats.mtime.getTime() < oneWeekAgo) {
|
||||||
|
fs.unlink(filePath, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.logger.error('Failed to get file stats', err);
|
if (err.code === 'ENOENT') {
|
||||||
return;
|
this.logger.warn(`File already deleted: ${filePath}`);
|
||||||
}
|
} else {
|
||||||
if (stats.mtime.getTime() < oneWeekAgo) {
|
this.logger.error('Failed to delete old log file', err);
|
||||||
fs.unlink(filePath, err => {
|
}
|
||||||
if (err) {
|
} else {
|
||||||
if (err.code === 'ENOENT') {
|
this.logger.info(`Deleted old log file: ${filePath}`);
|
||||||
this.logger.warn(`File already deleted: ${file}`);
|
|
||||||
} else {
|
|
||||||
this.logger.error('Failed to delete old log file', err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.logger.info(`Deleted old log file: ${file}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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,76 +210,85 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
|
|||||||
tokens.push(`临时消息 (${msg.peerUin})`);
|
tokens.push(`临时消息 (${msg.peerUin})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function msgElementToText(element: MessageElement) {
|
|
||||||
if (element.textElement) {
|
|
||||||
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})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.replyElement) {
|
|
||||||
const recordMsgOrNull = msg.records.find(
|
|
||||||
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) {
|
|
||||||
return '[图片]';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.fileElement) {
|
|
||||||
return `[文件 ${element.fileElement.fileName}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.videoElement) {
|
|
||||||
return '[视频]';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.pttElement) {
|
|
||||||
return `[语音 ${element.pttElement.duration}s]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.arkElement) {
|
|
||||||
return '[卡片消息]';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.faceElement) {
|
|
||||||
return `[表情 ${element.faceElement.faceText ?? ''}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.marketFaceElement) {
|
|
||||||
return element.marketFaceElement.faceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.markdownElement) {
|
|
||||||
return '[Markdown 消息]';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.multiForwardMsgElement) {
|
|
||||||
return '[转发消息]';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.elementType === ElementType.GreyTip) {
|
|
||||||
return '[灰条消息]';
|
|
||||||
}
|
|
||||||
|
|
||||||
return `[未实现 (ElementType = ${element.elementType})]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const element of msg.elements) {
|
for (const element of msg.elements) {
|
||||||
tokens.push(msgElementToText(element));
|
tokens.push(msgElementToText(element, msg, recursiveLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokens.join(' ');
|
return tokens.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function msgElementToText(element: MessageElement, msg: RawMessage, recursiveLevel: number): string {
|
||||||
|
if (element.textElement) {
|
||||||
|
return textElementToText(element.textElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.replyElement) {
|
||||||
|
return replyElementToText(element.replyElement, msg, recursiveLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.picElement) {
|
||||||
|
return '[图片]';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.fileElement) {
|
||||||
|
return `[文件 ${element.fileElement.fileName}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.videoElement) {
|
||||||
|
return '[视频]';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.pttElement) {
|
||||||
|
return `[语音 ${element.pttElement.duration}s]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.arkElement) {
|
||||||
|
return '[卡片消息]';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.faceElement) {
|
||||||
|
return `[表情 ${element.faceElement.faceText ?? ''}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.marketFaceElement) {
|
||||||
|
return element.marketFaceElement.faceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.markdownElement) {
|
||||||
|
return '[Markdown 消息]';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.multiForwardMsgElement) {
|
||||||
|
return '[转发消息]';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.elementType === ElementType.GreyTip) {
|
||||||
|
return '[灰条消息]';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `[未实现 (ElementType = ${element.elementType})]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function textElementToText(textElement: any): string {
|
||||||
|
if (textElement.atType === AtType.notAt) {
|
||||||
|
const originalContentLines = textElement.content.split('\n');
|
||||||
|
return `${originalContentLines[0]}${originalContentLines.length > 1 ? ' ...' : ''}`;
|
||||||
|
} 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})`
|
||||||
|
}]`;
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
|
@ -9,48 +9,47 @@ export class RequestUtil {
|
|||||||
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 } = {};
|
let cookies: { [key: string]: string } = {};
|
||||||
const handleRedirect = (res: http.IncomingMessage) => {
|
|
||||||
//console.log(res.headers.location);
|
res.on('data', () => { }); // Necessary to consume the stream
|
||||||
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
||||||
if (res.headers.location) {
|
|
||||||
const redirectUrl = new URL(res.headers.location, url);
|
|
||||||
RequestUtil.HttpsGetCookies(redirectUrl.href).then((redirectCookies) => {
|
|
||||||
// 合并重定向过程中的cookies
|
|
||||||
cookies = { ...cookies, ...redirectCookies };
|
|
||||||
resolve(cookies);
|
|
||||||
}).catch((err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resolve(cookies);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resolve(cookies);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
res.on('data', () => {
|
|
||||||
}); // Necessary to consume the stream
|
|
||||||
res.on('end', () => {
|
res.on('end', () => {
|
||||||
handleRedirect(res);
|
this.handleRedirect(res, url, cookies)
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.headers['set-cookie']) {
|
if (res.headers['set-cookie']) {
|
||||||
//console.log(res.headers['set-cookie']);
|
this.extractCookies(res.headers['set-cookie'], cookies);
|
||||||
res.headers['set-cookie'].forEach((cookie) => {
|
|
||||||
const parts = cookie.split(';')[0].split('=');
|
|
||||||
const key = parts[0];
|
|
||||||
const value = parts[1];
|
|
||||||
if (key && value && key.length > 0 && value.length > 0) {
|
|
||||||
cookies[key] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('error', (error: any) => {
|
req.on('error', (error: any) => {
|
||||||
reject(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.headers.location) {
|
||||||
|
const redirectUrl = new URL(res.headers.location, url);
|
||||||
|
const redirectCookies = await this.HttpsGetCookies(redirectUrl.href);
|
||||||
|
// 合并重定向过程中的cookies
|
||||||
|
return { ...cookies, ...redirectCookies };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static extractCookies(setCookieHeaders: string[], cookies: { [key: string]: string }) {
|
||||||
|
setCookieHeaders.forEach((cookie) => {
|
||||||
|
const parts = cookie.split(';')[0].split('=');
|
||||||
|
const key = parts[0];
|
||||||
|
const value = parts[1];
|
||||||
|
if (key && value && key.length > 0 && value.length > 0) {
|
||||||
|
cookies[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 请求和回复都是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: {
|
||||||
|
@ -300,18 +300,18 @@ export class NTQQFileApi {
|
|||||||
element.elementType === ElementType.FILE
|
element.elementType === ElementType.FILE
|
||||||
) {
|
) {
|
||||||
switch (element.elementType) {
|
switch (element.elementType) {
|
||||||
case ElementType.PIC:
|
case ElementType.PIC:
|
||||||
element.picElement!.sourcePath = elementResults[elementIndex];
|
element.picElement!.sourcePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.VIDEO:
|
case ElementType.VIDEO:
|
||||||
element.videoElement!.filePath = elementResults[elementIndex];
|
element.videoElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.PTT:
|
case ElementType.PTT:
|
||||||
element.pttElement!.filePath = elementResults[elementIndex];
|
element.pttElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.FILE:
|
case ElementType.FILE:
|
||||||
element.fileElement!.filePath = elementResults[elementIndex];
|
element.fileElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
elementIndex++;
|
elementIndex++;
|
||||||
}
|
}
|
||||||
@ -357,15 +357,13 @@ 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 if (!dimensions) {
|
||||||
|
reject(new Error('获取图片尺寸失败'));
|
||||||
} else {
|
} else {
|
||||||
if (!dimensions) {
|
resolve(dimensions);
|
||||||
reject(new Error('获取图片尺寸失败'));
|
|
||||||
} else {
|
|
||||||
resolve(dimensions);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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',
|
||||||
@ -44,7 +44,7 @@ export class NTQQFriendApi {
|
|||||||
async getBuddyV2ExWithCate(refresh = false) {
|
async getBuddyV2ExWithCate(refresh = false) {
|
||||||
const categoryMap: Map<string, any> = new Map();
|
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 => {
|
item.buddyUids.forEach(uid => {
|
||||||
categoryMap.set(uid, { categoryId: item.categoryId, categoryName: item.categroyName });
|
categoryMap.set(uid, { categoryId: item.categoryId, categoryName: item.categroyName });
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ export async function NCoreInitFramework(
|
|||||||
) {
|
) {
|
||||||
//在进入本层前是否登录未进行判断
|
//在进入本层前是否登录未进行判断
|
||||||
console.log('NapCat Framework App Loading...');
|
console.log('NapCat Framework App Loading...');
|
||||||
|
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
console.log('[NapCat] [Error] Unhandled Exception:', err.message);
|
console.log('[NapCat] [Error] Unhandled Exception:', err.message);
|
||||||
});
|
});
|
||||||
@ -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 {
|
||||||
|
BIN
src/native/external/MoeHoo.win32.node
vendored
BIN
src/native/external/MoeHoo.win32.node
vendored
Binary file not shown.
@ -45,8 +45,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,7 +56,6 @@ export class NapCatOneBot11Adapter {
|
|||||||
apis: StableOneBotApiWrapper;
|
apis: StableOneBotApiWrapper;
|
||||||
networkManager: OB11NetworkManager;
|
networkManager: OB11NetworkManager;
|
||||||
actions: ActionMap;
|
actions: ActionMap;
|
||||||
nativeCore: Native | undefined;
|
|
||||||
private bootTime = Date.now() / 1000;
|
private bootTime = Date.now() / 1000;
|
||||||
recallMsgCache = new LRUCache<string, RawMessage>(100);
|
recallMsgCache = new LRUCache<string, RawMessage>(100);
|
||||||
|
|
||||||
@ -75,41 +72,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;
|
||||||
|
@ -292,7 +292,7 @@ export async function NCoreInitShell() {
|
|||||||
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,7 +300,7 @@ export async function NCoreInitShell() {
|
|||||||
selfInfo,
|
selfInfo,
|
||||||
basicInfoWrapper,
|
basicInfoWrapper,
|
||||||
pathWrapper,
|
pathWrapper,
|
||||||
);
|
).InitNapCat();
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NapCatShell {
|
export class NapCatShell {
|
||||||
@ -327,8 +327,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { resolve } from 'path';
|
|||||||
import nodeResolve from '@rollup/plugin-node-resolve';
|
import nodeResolve from '@rollup/plugin-node-resolve';
|
||||||
import { builtinModules } from 'module';
|
import { builtinModules } from 'module';
|
||||||
//依赖排除
|
//依赖排除
|
||||||
const external = ['silk-wasm', 'ws', 'express', 'fluent-ffmpeg', 'qrcode-terminal'];
|
const external = ['silk-wasm', 'ws', 'express', 'qrcode-terminal'];
|
||||||
const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat();
|
const nodeModules = [...builtinModules, builtinModules.map(m => `node:${m}`)].flat();
|
||||||
function genCpModule(module: string) {
|
function genCpModule(module: string) {
|
||||||
return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false };
|
return { src: `./node_modules/${module}`, dest: `dist/node_modules/${module}`, flatten: false };
|
||||||
@ -42,7 +42,6 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
|
|||||||
const ShellBaseConfigPlugin: PluginOption[] = [
|
const ShellBaseConfigPlugin: PluginOption[] = [
|
||||||
cp({
|
cp({
|
||||||
targets: [
|
targets: [
|
||||||
{ src: './src/native/external', dest: 'dist/native', flatten: false },
|
|
||||||
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false },
|
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false },
|
||||||
{ src: './static/', dest: 'dist/static/', flatten: false },
|
{ src: './static/', dest: 'dist/static/', flatten: false },
|
||||||
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user