Compare commits

...

66 Commits

Author SHA1 Message Date
手瓜一十雪
f691320453 fix: handleQuickOperation 2024-08-27 22:47:30 +08:00
手瓜一十雪
be39fc3a21 chore: 移除 2024-08-27 22:17:54 +08:00
手瓜一十雪
d2fafaf33a chore: rename nekodoge 2024-08-27 20:53:07 +08:00
手瓜一十雪
27ae331352 chore: new adapter 2024-08-27 20:37:23 +08:00
Seijo Cecilia
3f2dcfbacc (partially) fix: 'throw' of exception caught locally 2024-08-27 15:05:51 +08:00
Alen
8565aee8b6 Merge pull request #310 from cnxysoft/upmain
style: 规范代码
2024-08-27 14:11:33 +08:00
Alen
f983add599 style: 规范代码 2024-08-27 14:09:28 +08:00
Seijo Cecilia
030192afeb Merge remote-tracking branch 'origin/main' 2024-08-27 14:00:05 +08:00
Seijo Cecilia
c8b6a158f1 chore: optimize imports 2024-08-27 13:59:47 +08:00
Alen
e71f7849a7 Merge pull request #309 from cnxysoft/upmain
fix: 群员获取异常
2024-08-27 13:54:33 +08:00
Alen
b64d1ff4ff revert: 入群事件
因getGroupMembers切换到V2,恢复原本逻辑
2024-08-27 13:52:23 +08:00
Alen
5a0028be26 chore: 去除无用代码 2024-08-27 13:41:30 +08:00
Alen
926d7deb43 Merge branch 'main' into upmain 2024-08-27 13:39:38 +08:00
Seijo Cecilia
6384b50bae chore: run eslint 2024-08-27 13:35:25 +08:00
手瓜一十雪
9feb0f4b53 release: v2.2.16 2024-08-27 12:18:20 +08:00
手瓜一十雪
43ec1b7cfd feat: 更高效率的识别 2024-08-27 12:16:57 +08:00
Wesley F. Young
05b7a59f8d refactor: move version info into version.ts 2024-08-27 10:30:38 +08:00
Wesley F. Young
17e680f7af refactor: move all utility files to /common 2024-08-27 10:18:31 +08:00
Wesley F. Young
035d256d4e refactor: rename event-legacy -> event (currently in use) 2024-08-27 10:15:46 +08:00
Wesley F. Young
8939adf886 refactor: rename event -> event-v2 (not ready for use) 2024-08-27 10:14:55 +08:00
Wesley F. Young
027ffbffa6 chore: remove redundant eslint-disable 2024-08-27 10:05:23 +08:00
Alen
3cca06712b fix: 群成员拉取失败(实验性) 2024-08-27 02:43:27 +08:00
手瓜一十雪
2b9359dbf4 fix: Type
NodeIKernelSessionListener/onNTSessionCreate->NodeIKernelGroupListener/onGroupListInited
2024-08-26 21:33:33 +08:00
手瓜一十雪
c0f5d3bd2e release:2.2.15 2024-08-26 19:56:16 +08:00
手瓜一十雪
2a2d5382e1 Merge pull request #308 from LingLambda/main
规范了_send_group_notice接口参数命名,适当的规范了部分变量命名
2024-08-26 19:30:55 +08:00
ling
2e4986024c 规范了_send_group_notice接口参数命名,适当的规范了部分变量命名 2024-08-26 18:53:33 +08:00
手瓜一十雪
8a9c605dae release: 2.2.15 2024-08-26 18:02:21 +08:00
手瓜一十雪
44f51a93c8 release: 2.2.13 2024-08-26 17:46:17 +08:00
手瓜一十雪
66c8537b41 feat: support->27391 2024-08-26 17:44:04 +08:00
手瓜一十雪
86ae6dd332 fix: music sign 2024-08-26 17:23:39 +08:00
Seijo Cecilia
69380c9c73 Merge remote-tracking branch 'origin/main' 2024-08-26 16:27:43 +08:00
Seijo Cecilia
3d3759137c fix: remove redundant uid in GetStrangerInfo 2024-08-26 16:27:29 +08:00
手瓜一十雪
9b9b8f6f6f fix: type 2024-08-26 16:26:10 +08:00
手瓜一十雪
8ff87a8245 feat: getGroupExtFE0Info 2024-08-26 16:12:29 +08:00
Seijo Cecilia
d1896da171 update: promise executor functions should not be async 2024-08-26 15:40:27 +08:00
Seijo Cecilia
0bc4f6fd96 refactor: enhanced type definition for other methods 2024-08-26 15:37:17 +08:00
Seijo Cecilia
b16a429686 chore: reformat code style 2024-08-26 14:58:04 +08:00
Seijo Cecilia
fa1d266696 Merge remote-tracking branch 'origin/main' 2024-08-26 14:36:41 +08:00
Seijo Cecilia
d5dd2e9551 update: optimize entity factory (formerly converter.ts) 2024-08-26 14:36:10 +08:00
手瓜一十雪
be57c312c4 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-26 14:30:29 +08:00
手瓜一十雪
f180687ba3 refactor: action param handle 2024-08-26 14:30:11 +08:00
Seijo Cecilia
3f3d9cc6f1 refactor: move calls & clean code 2024-08-26 14:22:08 +08:00
Seijo Cecilia
4f98c0d045 refactor: make quick action apis instance methods 2024-08-26 14:14:49 +08:00
Seijo Cecilia
c254441d40 Merge remote-tracking branch 'origin/main' 2024-08-26 14:01:21 +08:00
手瓜一十雪
17cbe74fa3 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-26 14:01:06 +08:00
Seijo Cecilia
7aa0bd9b79 refactor: move all callNormalEvent calls to V2 substitutes 2024-08-26 14:00:48 +08:00
手瓜一十雪
2553cf6b72 style: lint 2024-08-26 13:58:28 +08:00
linyuchen
fe9050aeda Update README.md 2024-08-26 13:52:00 +08:00
Wesley F. Young
7092894d22 refactor: normalize file names in common/utils 2024-08-26 12:48:45 +08:00
Wesley F. Young
af6ac26664 chore: lint & style 2024-08-26 12:44:44 +08:00
Wesley F. Young
a22ef67486 fix: type definition for json data 2024-08-26 12:39:32 +08:00
Wesley F. Young
7bb57cd78a fix: use allSettled instead of all when parsing raw message 2024-08-26 12:38:39 +08:00
Wesley F. Young
89b69bbdf8 Merge remote-tracking branch 'origin/main' 2024-08-26 12:02:47 +08:00
Wesley F. Young
e21c779d06 refactor: enhanced type definition for callNormalEvent
类型体操,伟大,无需多言!
2024-08-26 12:02:41 +08:00
手瓜一十雪
dfa3553b71 chore: 谁打的不规范日志 2024-08-26 11:35:18 +08:00
手瓜一十雪
19097388d0 chore: onRecvSysMsg 2024-08-26 11:33:17 +08:00
Wesley F. Young
a71eddbed2 fix: reference error 2024-08-26 11:25:37 +08:00
手瓜一十雪
65bbed0c26 Merge pull request #304 from intling-luo/main
修改群公告的参数名 feed_id 为 notice_id
2024-08-26 11:11:32 +08:00
ling
871cc61dfc 修改群公告的参数名 feed_id 为 notice_id 2024-08-26 10:37:11 +08:00
Wesley F. Young
bc62feb71b refactor: normalize naming 2024-08-26 10:04:31 +08:00
Wesley F. Young
0bba329999 refactor: make sendMsg(WithOb11UniqueId) an instance method 2024-08-26 09:34:51 +08:00
Wesley F. Young
b1a1fdbeee refactor: rename all coreContext -> core 2024-08-26 09:19:50 +08:00
Wesley F. Young
542c5beb1b chore: suppress type check 2024-08-26 09:09:33 +08:00
手瓜一十雪
7b87b0919b release: 2.2.12 2024-08-26 02:57:08 +08:00
手瓜一十雪
9c34f558d3 release: 2.2.11 2024-08-26 02:16:24 +08:00
手瓜一十雪
3e2da3b490 release: v2.2.10 2024-08-26 01:58:41 +08:00
164 changed files with 1728 additions and 1555 deletions

View File

@@ -31,5 +31,7 @@ NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现
---
## 约法三章
> [!CAUTION]\
> **请不要在 QQ 官方群聊和任何影响力较大的简中互联网平台(包括但不限于: 哔哩哔哩,微博,知乎,抖音等)发布和讨论*任何*与本项目存在相关性的信息**
任何使用本仓库代码的地方,都应当严格遵守[本仓库开源许可](./LICENSE)。**此外,禁止任何项目未经授权二次分发或基于 NapCat 代码开发。**

View File

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

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "2.2.9",
"version": "2.2.16",
"scripts": {
"build:framework": "vite build --mode framework",
"build:shell": "vite build --mode shell",

2
script/KillQQ.bat Normal file
View File

@@ -0,0 +1,2 @@
@echo off
taskkill /f /im QQ.exe

View File

@@ -4,13 +4,13 @@ import type { NapCatCore } from '@/core';
export abstract class ConfigBase<T> {
name: string;
coreContext: NapCatCore;
core: NapCatCore;
configPath: string;
configData: T = {} as T;
protected constructor(name: string, coreContext: NapCatCore, configPath: string) {
protected constructor(name: string, core: NapCatCore, configPath: string) {
this.name = name;
this.coreContext = coreContext;
this.core = core;
this.configPath = configPath;
fs.mkdirSync(this.configPath, { recursive: true });
this.read();
@@ -28,8 +28,8 @@ export abstract class ConfigBase<T> {
}
read(): T {
const logger = this.coreContext.context.logger;
const configPath = this.getConfigPath(this.coreContext.selfInfo.uin);
const logger = this.core.context.logger;
const configPath = this.getConfigPath(this.core.selfInfo.uin);
if (!fs.existsSync(configPath)) {
try {
fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8'));
@@ -54,8 +54,8 @@ export abstract class ConfigBase<T> {
save(newConfigData: T = this.configData) {
const logger = this.coreContext.context.logger;
const selfInfo = this.coreContext.selfInfo;
const logger = this.core.context.logger;
const selfInfo = this.core.selfInfo;
this.configData = newConfigData;
const configPath = this.getConfigPath(selfInfo.uin);
try {

View File

@@ -6,10 +6,11 @@ export type ListenerClassBase = Record<string, string>;
export interface ListenerIBase {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: any): ListenerClassBase;
[key: string]: any;
}
export class NTEventChannel extends EventEmitter {
export class NTEventWrapperV2 extends EventEmitter {
private wrapperApi: WrapperNodeApi;
private wrapperSession: NodeIQQNTWrapperSession;
private listenerRefStorage = new Map<string, ListenerIBase>();
@@ -122,10 +123,9 @@ export class NTEventChannel extends EventEmitter {
async callEvent<EventType extends (...args: any[]) => Promise<any> | any>(
EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
return new Promise<Awaited<ReturnType<EventType>>>((resolve) => {
const EventFunc = this.createEventFunction<EventType>(EventName);
const retData = await EventFunc!(...args);
resolve(retData);
EventFunc!(...args).then((retData: Awaited<ReturnType<EventType>> | PromiseLike<Awaited<ReturnType<EventType>>>) => resolve(retData));
});
}
}

View File

@@ -1,5 +1,6 @@
import { NodeIQQNTWrapperSession } from '@/core/wrapper/wrapper';
import { randomUUID } from 'crypto';
import { ListenerNamingMapping, ServiceNamingMapping } from '@/core';
interface InternalMapKey {
timeout: number;
@@ -10,18 +11,13 @@ interface InternalMapKey {
export type ListenerClassBase = Record<string, string>;
export interface ListenerIBase {
new(listener: any): ListenerClassBase;
[key: string]: any;
}
export class LegacyNTEventWrapper {
export class NTEventWrapper {
private WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession
private 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}
constructor(
wrapperSession: NodeIQQNTWrapperSession
wrapperSession: NodeIQQNTWrapperSession,
) {
this.WrapperSession = wrapperSession;
}
@@ -45,7 +41,13 @@ export class LegacyNTEventWrapper {
);
}
createEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined {
createEventFunction<
Service extends keyof ServiceNamingMapping,
ServiceMethod extends Exclude<keyof ServiceNamingMapping[Service], symbol>,
// eslint-disable-next-line
// @ts-ignore
T extends (...args: any) => any = ServiceNamingMapping[Service][ServiceMethod],
>(eventName: `${Service}/${ServiceMethod}`): T | undefined {
const eventNameArr = eventName.split('/');
type eventType = {
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>> };
@@ -69,9 +71,10 @@ export class LegacyNTEventWrapper {
if (!existListener) {
const Listener = this.createProxyDispatch(listenerMainName);
const ServiceSubName = /^NodeIKernel(.*?)Listener$/.exec(listenerMainName)![1];
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
addfunc!(Listener as T);
const Service = `NodeIKernel${ServiceSubName}Service/addKernel${ServiceSubName}Listener`;
// eslint-disable-next-line
// @ts-ignore
this.createEventFunction(Service)(Listener as T);
this.listenerManager.set(listenerMainName + uniqueCode, Listener);
return Listener as T;
}
@@ -93,36 +96,49 @@ export class LegacyNTEventWrapper {
});
}
async callNoListenerEvent<EventType extends (...args: any[]) => Promise<any> | any>(
EventName = '',
async callNoListenerEvent<
Service extends keyof ServiceNamingMapping,
ServiceMethod extends Exclude<keyof ServiceNamingMapping[Service], symbol>,
// eslint-disable-next-line
// @ts-ignore
EventType extends (...args: any) => any = ServiceNamingMapping[Service][ServiceMethod],
>(
serviceAndMethod: `${Service}/${ServiceMethod}`,
...args: Parameters<EventType>
): Promise<Awaited<ReturnType<EventType>>> {
const EventFunc = this.createEventFunction<EventType>(EventName);
return EventFunc!(...args);
return (this.createEventFunction(serviceAndMethod))!(...args);
}
async RegisterListen<ListenerType extends (...args: any[]) => void>(
ListenerName = '',
async registerListen<
Listener extends keyof ListenerNamingMapping,
ListenerMethod extends Exclude<keyof ListenerNamingMapping[Listener], symbol>,
// eslint-disable-next-line
// @ts-ignore
ListenerType extends (...args: any) => any = ListenerNamingMapping[Listener][ListenerMethod],
>(
listenerAndMethod: `${Listener}/${ListenerMethod}`,
waitTimes = 1,
timeout = 5000,
checker: (...args: Parameters<ListenerType>) => boolean,
) {
return new Promise<Parameters<ListenerType>>((resolve, reject) => {
const ListenerNameList = ListenerName.split('/');
const ListenerNameList = listenerAndMethod.split('/');
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
const id = randomUUID();
let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined;
const databack = () => {
function sendDataCallback() {
if (complete == 0) {
reject(new Error(' ListenerName:' + ListenerName + ' timeout'));
reject(new Error(' ListenerName:' + listenerAndMethod + ' timeout'));
} else {
resolve(retData!);
}
};
const timeoutRef = setTimeout(databack, timeout);
const eventCallbak = {
}
const timeoutRef = setTimeout(sendDataCallback, timeout);
const eventCallback = {
timeout: timeout,
createtime: Date.now(),
checker: checker,
@@ -131,7 +147,7 @@ export class LegacyNTEventWrapper {
retData = args;
if (complete >= waitTimes) {
clearTimeout(timeoutRef);
databack();
sendDataCallback();
}
},
};
@@ -141,21 +157,30 @@ export class LegacyNTEventWrapper {
if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) {
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
}
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback);
this.createListenerFunction(ListenerMainName);
});
}
async CallNormalEventV2<
EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void
async callNormalEventV2<
Service extends keyof ServiceNamingMapping,
ServiceMethod extends Exclude<keyof ServiceNamingMapping[Service], symbol>,
Listener extends keyof ListenerNamingMapping,
ListenerMethod extends Exclude<keyof ListenerNamingMapping[Listener], symbol>,
// eslint-disable-next-line
// @ts-ignore
EventType extends (...args: any) => any = ServiceNamingMapping[Service][ServiceMethod],
// eslint-disable-next-line
// @ts-ignore
ListenerType extends (...args: any) => any = ListenerNamingMapping[Listener][ListenerMethod]
>(
EventName = '',
ListenerName = '',
waitTimes = 1,
timeout: number = 3000,
serviceAndMethod: `${Service}/${ServiceMethod}`,
listenerAndMethod: `${Listener}/${ListenerMethod}`,
args: Parameters<EventType>,
checkerEvent: (ret: Awaited<ReturnType<EventType>>) => boolean = () => true,
checkerListener: (...args: Parameters<ListenerType>) => boolean = () => true,
...args: Parameters<EventType>
callbackTimesToWait = 1,
timeout = 5000,
) {
return new Promise<[EventRet: Awaited<ReturnType<EventType>>, ...Parameters<ListenerType>]>(
async (resolve, reject) => {
@@ -163,14 +188,15 @@ export class LegacyNTEventWrapper {
let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined;
let retEvent: any = {};
const databack = () => {
function sendDataCallback() {
if (complete == 0) {
reject(
new Error(
'Timeout: NTEvent EventName:' +
EventName +
'Timeout: NTEvent serviceAndMethod:' +
serviceAndMethod +
' ListenerName:' +
ListenerName +
listenerAndMethod +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
@@ -179,24 +205,24 @@ export class LegacyNTEventWrapper {
} else {
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
}
};
}
const ListenerNameList = ListenerName.split('/');
const ListenerNameList = listenerAndMethod.split('/');
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
const Timeouter = setTimeout(databack, timeout);
const timeoutRef = setTimeout(sendDataCallback, timeout);
const eventCallbak = {
const eventCallback = {
timeout: timeout,
createtime: Date.now(),
checker: checkerListener,
func: (...args: any[]) => {
complete++;
retData = args as Parameters<ListenerType>;
if (complete >= waitTimes) {
clearTimeout(Timeouter);
databack();
if (complete >= callbackTimesToWait) {
clearTimeout(timeoutRef);
sendDataCallback();
}
},
};
@@ -206,18 +232,18 @@ export class LegacyNTEventWrapper {
if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) {
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
}
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback);
this.createListenerFunction(ListenerMainName);
const EventFunc = this.createEventFunction<EventType>(EventName);
retEvent = await EventFunc!(...(args as any[]));
const eventFunction = this.createEventFunction(serviceAndMethod);
retEvent = await eventFunction!(...(args));
if (!checkerEvent(retEvent)) {
clearTimeout(Timeouter);
clearTimeout(timeoutRef);
reject(
new Error(
'EventChecker Failed: NTEvent EventName:' +
EventName +
'EventChecker Failed: NTEvent serviceAndMethod:' +
serviceAndMethod +
' ListenerName:' +
ListenerName +
listenerAndMethod +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
@@ -227,12 +253,22 @@ export class LegacyNTEventWrapper {
},
);
}
async CallNormalEvent<
EventType extends (...args: any[]) => Promise<any>,
ListenerType extends (...args: any[]) => void
/*
async callNormalEvent<
Service extends keyof ServiceNamingMapping,
ServiceMethod extends Exclude<keyof ServiceNamingMapping[Service], symbol>,
Listener extends keyof ListenerNamingMapping,
ListenerMethod extends Exclude<keyof ListenerNamingMapping[Listener], symbol>,
// eslint-disable-next-line
// @ts-ignore
EventType extends (...args: any) => any = ServiceNamingMapping[Service][ServiceMethod],
// eslint-disable-next-line
// @ts-ignore
ListenerType extends (...args: any) => any = ListenerNamingMapping[Listener][ListenerMethod]
>(
EventName = '',
ListenerName = '',
serviceAndMethod: `${Service}/${ServiceMethod}`,
listenerAndMethod: `${Listener}/${ListenerMethod}`,
waitTimes = 1,
timeout: number = 3000,
checker: (...args: Parameters<ListenerType>) => boolean,
@@ -249,9 +285,9 @@ export class LegacyNTEventWrapper {
reject(
new Error(
'Timeout: NTEvent EventName:' +
EventName +
serviceAndMethod +
' ListenerName:' +
ListenerName +
listenerAndMethod +
' EventRet:\n' +
JSON.stringify(retEvent, null, 4) +
'\n',
@@ -262,7 +298,7 @@ export class LegacyNTEventWrapper {
}
};
const ListenerNameList = ListenerName.split('/');
const ListenerNameList = listenerAndMethod.split('/');
const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1];
@@ -290,9 +326,10 @@ export class LegacyNTEventWrapper {
}
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
this.createListenerFunction(ListenerMainName);
const EventFunc = this.createEventFunction<EventType>(EventName);
const EventFunc = this.createEventFunction<EventType>(serviceAndMethod);
retEvent = await EventFunc!(...(args as any[]));
},
);
}
*/
}

View File

@@ -13,6 +13,7 @@ export function isGIF(path: string) {
fs.closeSync(fd);
return buffer.toString() === 'GIF8';
}
// 定义一个异步函数来检查文件是否存在
export function checkFileReceived(path: string, timeout: number = 3000): Promise<void> {
return new Promise((resolve, reject) => {
@@ -123,15 +124,14 @@ export interface HttpDownloadOptions {
}
export async function httpDownload(options: string | HttpDownloadOptions): Promise<Buffer> {
const chunks: Buffer[] = [];
// const chunks: Buffer[] = [];
let url: string;
let headers: Record<string, string> = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36',
};
if (typeof options === 'string') {
url = options;
const host = new URL(url).hostname;
headers['Host'] = host;
headers['Host'] = new URL(url).hostname;
} else {
url = options.url;
if (options.headers) {
@@ -269,4 +269,4 @@ export async function uri2local(dir: string, uri: string, filename: string | und
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath, isLocal: true };
}
return { success: false, errMsg: '未知文件类型', fileName: '', ext: '', path: '', isLocal: false };
}
}

View File

@@ -18,11 +18,12 @@ export async function solveAsyncProblem<T extends (...args: any[]) => Promise<an
return new Promise<Awaited<ReturnType<T>> | undefined>((resolve) => {
func(...args).then((result) => {
resolve(result);
}).catch((e) => {
}).catch(() => {
resolve(undefined);
});
});
}
//下面这个类是用于将uid+msgid合并的类
export class UUIDConverter {
static encode(highStr: string, lowStr: string): string {
@@ -31,11 +32,10 @@ export class UUIDConverter {
const highHex = high.toString(16).padStart(16, '0');
const lowHex = low.toString(16).padStart(16, '0');
const combinedHex = highHex + lowHex;
const uuid = `${combinedHex.substring(0, 8)}-${combinedHex.substring(8, 12)}-${combinedHex.substring(
return `${combinedHex.substring(0, 8)}-${combinedHex.substring(8, 12)}-${combinedHex.substring(
12,
16,
)}-${combinedHex.substring(16, 20)}-${combinedHex.substring(20)}`;
return uuid;
}
static decode(uuid: string): { high: string; low: string } {
@@ -122,11 +122,11 @@ export function getDefaultQQVersionConfigInfo(): QQVersionConfigType {
};
}
return {
baseVersion: '9.9.15-27254',
curVersion: '9.9.15-27254',
baseVersion: '9.9.15-27391',
curVersion: '9.9.15-27391',
prevVersion: '',
onErrorVersions: [],
buildId: '27254',
buildId: '27391',
};
}

View File

@@ -1,5 +1,5 @@
import log4js, { Configuration } from 'log4js';
import { truncateString } from '@/common/utils/helper';
import { truncateString } from '@/common/helper';
import path from 'node:path';
import chalk from 'chalk';
import { AtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/core';
@@ -177,12 +177,12 @@ export function rawMessageToText(msg: RawMessage, recursiveLevel = 0): string {
if (element.replyElement) {
const recordMsgOrNull = msg.records.find(
record => element.replyElement!.sourceMsgIdInRecords === record.msgId
record => element.replyElement!.sourceMsgIdInRecords === record.msgId,
);
return `[回复消息 ${
recordMsgOrNull &&
recordMsgOrNull.peerUin != '284840486' // 非转发消息; 否则定位不到
?
?
rawMessageToText(recordMsgOrNull, recursiveLevel + 1) :
`未找到消息记录 (MsgId = ${element.replyElement.sourceMsgIdInRecords})`
}]`;

View File

@@ -2,8 +2,6 @@ import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
export const napcat_version = '2.2.9';
export class NapCatPathWrapper {
binaryPath: string;
logsPath: string;

View File

@@ -1,6 +1,6 @@
import path from 'node:path';
import fs from 'node:fs';
import { systemPlatform } from '@/common/utils/system';
import { systemPlatform } from '@/common/system';
import { getDefaultQQVersionConfigInfo, getQQVersionConfigPath } from './helper';
import AppidTable from '@/core/external/appid.json';
import { LogWrapper } from './log';
@@ -60,20 +60,17 @@ export class QQBasicInfoWrapper {
getAppidV2(): { appid: string; qua: string } {
const appidTbale = AppidTable as unknown as QQAppidTableType;
try {
const fullVersion = this.getFullQQVesion();
if (!fullVersion) throw new Error('QQ版本获取失败');
const fullVersion = this.getFullQQVesion();
if (fullVersion) {
const data = appidTbale[fullVersion];
if (data) {
return data;
}
} catch (e) {
this.context.logger.log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`);
}
// 以下是兜底措施
this.context.logger.log(
`[QQ版本兼容性检测] ${this.getFullQQVesion()} 版本兼容性不佳,可能会导致一些功能无法正常使用`,
);
// else
this.context.logger.log(`[QQ版本兼容性检测] 获取Appid异常 请检测NapCat/QQNT是否正常`);
this.context.logger.log(`[QQ版本兼容性检测] ${fullVersion} 版本兼容性不佳,可能会导致一些功能无法正常使用`,);
return { appid: systemPlatform === 'linux' ? '537240795' : '537240709', qua: this.getQUAInternal() };
}
}

File diff suppressed because one or more lines are too long

1
src/common/version.ts Normal file
View File

@@ -0,0 +1 @@
export const napCatVersion = '2.2.16';

62
src/common/video.ts Normal file

File diff suppressed because one or more lines are too long

View File

@@ -17,18 +17,17 @@ import {
import path from 'path';
import fs from 'fs';
import fsPromises from 'fs/promises';
import { InstanceContext, NapCatCore, OnRichMediaDownloadCompleteParams } from '@/core';
import { InstanceContext, NapCatCore } from '@/core';
import * as fileType from 'file-type';
import imageSize from 'image-size';
import { ISizeCalculationResult } from 'image-size/dist/types/interface';
import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService';
import { RkeyManager } from '../helper/rkey';
import { calculateFileMD5, isGIF } from '@/common/utils/file';
import { calculateFileMD5, isGIF } from '@/common/file';
import pathLib from 'node:path';
import { defaultVideoThumbB64, getVideoInfo } from '@/common/utils/video';
import { defaultVideoThumbB64, getVideoInfo } from '@/common/video';
import ffmpeg from 'fluent-ffmpeg';
import fsnormal from 'node:fs';
import { encodeSilk } from '@/common/utils/audio';
import { encodeSilk } from '@/common/audio';
export class NTQQFileApi {
@@ -97,9 +96,13 @@ export class NTQQFileApi {
async createValidSendFileElement(
filePath: string,
fileName: string = '',
folderId: string = ''
folderId: string = '',
): Promise<SendFileElement> {
const { fileName: _fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.FILE);
const {
fileName: _fileName,
path,
fileSize,
} = await this.core.apis.FileApi.uploadFile(filePath, ElementType.FILE);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
@@ -118,9 +121,14 @@ export class NTQQFileApi {
async createValidSendPicElement(
picPath: string,
summary: string = '',
subType: 0 | 1 = 0
subType: 0 | 1 = 0,
): Promise<SendPicElement> {
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
const {
md5,
fileName,
path,
fileSize,
} = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
@@ -153,7 +161,12 @@ export class NTQQFileApi {
diyThumbPath: string = '',
): Promise<SendVideoElement> {
const logger = this.core.context.logger;
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
const {
fileName: _fileName,
path,
fileSize,
md5,
} = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
@@ -200,7 +213,7 @@ export class NTQQFileApi {
const thumbSize = _thumbPath ? (await fsPromises.stat(_thumbPath)).size : 0;
// log("生成缩略图", _thumbPath)
thumbPath.set(0, _thumbPath);
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : "";
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : '';
// "fileElement": {
// "fileMd5": "",
// "fileName": "1.mp4",
@@ -304,66 +317,45 @@ export class NTQQFileApi {
return sourcePath;
}
}
const data = await this.core.eventWrapper.CallNormalEvent<
(
params: {
fileModelId: string,
downloadSourceType: number,
triggerType: number,
msgId: string,
chatType: ChatType,
peerUid: string,
elementId: string,
thumbSize: number,
downloadType: number,
filePath: string
}) => Promise<unknown>,
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void
>(
'NodeIKernelMsgService/downloadRichMedia',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
1,
timeout,
(arg: OnRichMediaDownloadCompleteParams) => {
if (arg.msgId === msgId) {
return true;
}
return false;
},
{
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
},
);
const [, fileTransNotifyInfo] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelMsgService/downloadRichMedia',
'NodeIKernelMsgListener/onRichMediaDownloadComplete',
[{
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
}],
() => true,
(arg) => arg.msgId === msgId,
1,
timeout,
);
const msg = await this.core.apis.MsgApi.getMsgsByMsgId({
guildId: '',
chatType: chatType,
peerUid: peerUid,
}, [msgId]);
if (msg.msgList.length === 0) {
return data[1].filePath;
return fileTransNotifyInfo.filePath;
}
//获取原始消息
const FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId);
if (!FileElements) {
//失败则就乱来 Todo
return data[1].filePath;
return fileTransNotifyInfo.filePath;
}
//从原始消息获取文件路径
const filePath =
FileElements?.fileElement?.filePath ??
return FileElements?.fileElement?.filePath ??
FileElements?.pttElement?.filePath ??
FileElements?.videoElement?.filePath ??
FileElements?.picElement?.sourcePath;
return filePath;
}
async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {
@@ -438,56 +430,14 @@ export class NTQQFileApi {
}
async searchfile(keys: string[]) {
type EventType = NodeIKernelSearchService['searchFileWithKeywords'];
interface OnListener {
searchId: string,
hasMore: boolean,
resultItems: {
chatType: ChatType,
buddyChatInfo: any[],
discussChatInfo: any[],
groupChatInfo:
{
groupCode: string,
isConf: boolean,
hasModifyConfGroupFace: boolean,
hasModifyConfGroupName: boolean,
groupName: string,
remark: string
}[],
dataLineChatInfo: any[],
tmpChatInfo: any[],
msgId: string,
msgSeq: string,
msgTime: string,
senderUid: string,
senderNick: string,
senderRemark: string,
senderCard: string,
elemId: string,
elemType: number,
fileSize: string,
filePath: string,
fileName: string,
hits:
{
start: number,
end: number
}[]
}[]
}
const Event = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelSearchService/searchFileWithKeywords');
let id = '';
const Listener = this.core.eventWrapper.RegisterListen<(params: OnListener) => void>
(
const Event = this.core.eventWrapper.createEventFunction('NodeIKernelSearchService/searchFileWithKeywords');
const id = await Event!(keys, 12);
const Listener = this.core.eventWrapper.registerListen(
'NodeIKernelSearchListener/onSearchFileKeywordsResult',
1,
20000,
(params) => id !== '' && params.searchId == id,
);
id = await Event!(keys, 12);
);
const [ret] = (await Listener);
return ret;
}
@@ -499,7 +449,7 @@ export class NTQQFileApi {
const url: string = element.originImageUrl!; // 没有域名
const md5HexStr = element.md5HexStr;
const fileMd5 = element.md5HexStr;
const fileUuid = element.fileUuid;
// const fileUuid = element.fileUuid;
if (url) {
const UrlParse = new URL(IMAGE_HTTP_HOST + url);//临时解析拼接
@@ -571,9 +521,9 @@ export class NTQQFileCacheApi {
}
getFileCacheInfo(fileType: CacheFileType, pageSize: number = 1000, lastRecord?: CacheFileListItem) {
const _lastRecord = lastRecord ? lastRecord : { fileType: fileType };
//需要五个参数
//return napCatCore.session.getStorageCleanService().getFileCacheInfo();
// const _lastRecord = lastRecord ? lastRecord : { fileType: fileType };
// 需要五个参数
// return napCatCore.session.getStorageCleanService().getFileCacheInfo();
}
async clearChatCache(chats: ChatCacheListItemBasic[] = [], fileKeys: string[] = []) {

View File

@@ -1,11 +1,12 @@
import { Friend, FriendV2, User } from '@/core/entities';
import { BuddyListReqType, InstanceContext, NapCatCore, NodeIKernelBuddyListener, NodeIKernelBuddyService, NodeIKernelProfileService, OnBuddyChangeParams } from '@/core';
import { LimitedHashTable } from '@/common/utils/MessageUnique';
import { FriendV2 } from '@/core/entities';
import { BuddyListReqType, InstanceContext, NapCatCore } from '@/core';
import { LimitedHashTable } from '@/common/message-unique';
export class NTQQFriendApi {
context: InstanceContext;
core: NapCatCore;
//friends: Map<string, Friend> = new Map<string, FriendV2>();
// friends: Map<string, Friend> = new Map<string, FriendV2>();
constructor(context: InstanceContext, core: NapCatCore) {
this.context = context;
@@ -20,7 +21,7 @@ export class NTQQFriendApi {
const buddyService = this.context.session.getBuddyService();
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
const data = await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
);
return Array.from(data.values());
@@ -32,10 +33,10 @@ export class NTQQFriendApi {
const buddyService = this.context.session.getBuddyService();
const buddyListV2 = refresh ? await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL) : await buddyService.getBuddyListV2('0', BuddyListReqType.KNOMAL);
uids.push(...buddyListV2.data.flatMap(item => item.buddyUids));
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
const data = await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
);
data.forEach((value, key) => {
data.forEach((value) => {
retMap.set(value.uin!, value.uid!);
});
//console.log('getBuddyIdMap', retMap.getValue);
@@ -54,7 +55,7 @@ export class NTQQFriendApi {
});
return item.buddyUids;
}));
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
const data = await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
);
return buddyListV2.map(category => ({
@@ -70,18 +71,17 @@ export class NTQQFriendApi {
async isBuddy(uid: string) {
return this.context.session.getBuddyService().isBuddy(uid);
}
async clearBuddyReqUnreadCnt() {
return this.context.session.getBuddyService().clearBuddyReqUnreadCnt();
}
async getBuddyReq() {
const [, ret] = await this.core.eventWrapper.CallNormalEventV2<
NodeIKernelBuddyService['getBuddyReq'],
NodeIKernelBuddyListener['onBuddyReqChange']
>(
const [, ret] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelBuddyService/getBuddyReq',
'NodeIKernelBuddyListener/onBuddyReqChange',
1,
5000);
[],
);
return ret;
}

View File

@@ -4,16 +4,13 @@ import {
Group,
GroupMember,
GroupMemberRole,
GroupNotify,
GroupRequestOperateTypes,
InstanceContext,
KickMemberV2Req,
MemberExtSourceType,
NapCatCore,
NodeIKernelGroupListener,
NodeIKernelGroupService,
} from '@/core';
import { isNumeric, runAllWithTimeout, sleep } from '@/common/utils/helper';
import { isNumeric, runAllWithTimeout } from '@/common/helper';
export class NTQQGroupApi {
context: InstanceContext;
@@ -21,40 +18,74 @@ export class NTQQGroupApi {
groupCache: Map<string, Group> = new Map<string, Group>();
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
groups: Group[] = [];
constructor(context: InstanceContext, core: NapCatCore) {
this.context = context;
this.core = core;
sleep(1000).then(() => {
this.initCache().then().catch(context.logger.logError);
});
this.initCache().then().catch(context.logger.logError);
}
async initCache() {
this.groups = await this.getGroups();
for (const group of this.groups) {
this.groupCache.set(group.groupCode, group);
const data = await this.getGroupMembers(group.groupCode, 3000);
this.groupMemberCache.set(group.groupCode, data);
}
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
}
async setGroupAvatar(gc: string, filePath: string) {
return this.context.session.getGroupService().setHeader(gc, filePath);
}
async getGroups(forced = false) {
type ListenerType = NodeIKernelGroupListener['onGroupListUpdate'];
const [_retData, _updateType, groupList] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, ListenerType>
(
const [, , groupList] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getGroupList',
'NodeIKernelGroupListener/onGroupListUpdate',
1,
5000,
() => true,
forced,
);
[forced],
);
return groupList;
}
async getGroupExtFE0Info(GroupCode: string[], forced = true) {
return this.context.session.getGroupService().getGroupExt0xEF0Info(
GroupCode,
[],
{
bindGuildId: 1,
blacklistExpireTime: 1,
companyId: 1,
essentialMsgPrivilege: 1,
essentialMsgSwitch: 1,
fullGroupExpansionSeq: 1,
fullGroupExpansionSwitch: 1,
gangUpId: 1,
groupAioBindGuildId: 1,
groupBindGuildIds: 1,
groupBindGuildSwitch: 1,
groupExcludeGuildIds: 1,
groupExtFlameData: 1,
groupFlagPro1: 1,
groupInfoExtSeq: 1,
groupOwnerId: 1,
groupSquareSwitch: 1,
hasGroupCustomPortrait: 1,
inviteRobotMemberExamine: 1,
inviteRobotMemberSwitch: 1,
inviteRobotSwitch: 1,
isLimitGroupRtc: 1,
lightCharNum: 1,
luckyWord: 1,
luckyWordId: 1,
msgEventSeq: 1,
qqMusicMedalSwitch: 1,
reserve: 1,
showPlayTogetherSwitch: 1,
starId: 1,
todoSeq: 1,
viewedMsgDisappearTime: 1
},
forced
);
}
async getGroup(groupCode: string, forced = false) {
let group = this.groupCache.get(groupCode.toString());
if (!group) {
@@ -139,11 +170,10 @@ export class NTQQGroupApi {
let members = this.groupMemberCache.get(groupCodeStr);
if (!members) {
try {
members = await this.getGroupMembers(groupCodeStr);
members = await this.getGroupMembersV2(groupCodeStr);
// 更新群成员列表
this.groupMemberCache.set(groupCodeStr, members);
}
catch (e) {
} catch (e) {
return null;
}
}
@@ -161,7 +191,7 @@ export class NTQQGroupApi {
let member = getMember();
if (!member) {
members = await this.getGroupMembers(groupCodeStr);
members = await this.getGroupMembersV2(groupCodeStr);
member = getMember();
}
return member;
@@ -222,21 +252,25 @@ export class NTQQGroupApi {
// GetMsgByShoretID(ShoretID); -> MsgService.getMsgs(Peer,MsgId,1,false); -> 组出参数
return this.context.session.getGroupService().addGroupEssence(param);
}
async kickMemberV2Inner(param: KickMemberV2Req) {
return this.context.session.getGroupService().kickMemberV2(param);
}
async deleteGroupBulletin(GroupCode: string, feedId: string) {
async deleteGroupBulletin(GroupCode: string, noticeId: string) {
const _Pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return this.context.session.getGroupService().deleteGroupBulletin(GroupCode, _Pskey, feedId);
return this.context.session.getGroupService().deleteGroupBulletin(GroupCode, _Pskey, noticeId);
}
async quitGroupV2(GroupCode: string, needDeleteLocalMsg: boolean) {
const param = {
groupCode: GroupCode,
needDeleteLocalMsg: needDeleteLocalMsg
needDeleteLocalMsg: needDeleteLocalMsg,
};
//应该是直接返回不需要Listener的 未经测试 需测试再发布
return this.context.session.getGroupService().quitGroupV2(param);
}
async removeGroupEssence(GroupCode: string, msgId: string) {
// 代码没测过
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
@@ -255,33 +289,29 @@ export class NTQQGroupApi {
}
async getSingleScreenNotifies(num: number) {
const [_retData, _doubt, _seq, notifies] = await this.core.eventWrapper.CallNormalEvent<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void>
(
const [, , , notifies] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getSingleScreenNotifies',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
1,
5000,
() => true,
false,
'',
num,
);
[
false,
'',
num,
],
);
return notifies;
}
async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
type EventType = NodeIKernelGroupService['getMemberInfo'];
const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void>
(
const Listener = this.core.eventWrapper.registerListen(
'NodeIKernelGroupListener/onMemberInfoChange',
1,
forced ? 5000 : 250,
(params) => {
return params === GroupCode;
},
);
const EventFunc = this.core.eventWrapper.createEventFunction<EventType>('NodeIKernelGroupService/getMemberInfo');
const retData = await EventFunc!(GroupCode, [uid], forced);
(params) => params === GroupCode,
);
const retData = await (
this.core.eventWrapper
.createEventFunction('NodeIKernelGroupService/getMemberInfo')
)!(GroupCode, [uid], forced);
if (retData.result !== 0) {
throw new Error(`${retData.errMsg}`);
}
@@ -294,6 +324,38 @@ export class NTQQGroupApi {
return member;
}
async getGroupMembersV2(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
const groupService = this.context.session.getGroupService();
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
const listener = this.core.eventWrapper.registerListen(
'NodeIKernelGroupListener/onMemberListChange',
1,
500,
(params) => params.sceneId === sceneId,
);
try {
const [membersFromFunc, membersFromListener] = await Promise.allSettled([
groupService.getNextMemberList(sceneId, undefined, num),
listener,
]);
if (membersFromFunc.status === 'fulfilled' && membersFromListener.status === 'fulfilled') {
return new Map([
...membersFromFunc.value.result.infos,
...membersFromListener.value[0].infos
]);
}
if (membersFromFunc.status === 'fulfilled') {
return membersFromFunc.value.result.infos;
}
if (membersFromListener.status === 'fulfilled') {
return membersFromListener.value[0].infos;
}
throw new Error('获取群成员列表失败');
} finally {
groupService.destroyMemberListScene(sceneId);
}
}
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
const groupService = this.context.session.getGroupService();
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
@@ -327,12 +389,10 @@ export class NTQQGroupApi {
}
async getArkJsonGroupShare(GroupCode: string) {
const ret = await this.core.eventWrapper.callNoListenerEvent<(GroupId: string) => Promise<GeneralCallResult & {
arkJson: string
}>>(
const ret = await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelGroupService/getGroupRecommendContactArkJson',
GroupCode,
);
) as GeneralCallResult & { arkJson: string };
return ret.arkJson;
}

View File

@@ -1,9 +1,12 @@
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement, SendStatusType } from '@/core/entities';
import { InstanceContext, NapCatCore } from '@/core';
import { GroupFileInfoUpdateParamType } from '@/core/listeners';
import { GeneralCallResult } from '@/core/services/common';
export class NTQQMsgApi {
// nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览
// nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid
// 其实以官方文档为准是最好的https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType
context: InstanceContext;
core: NapCatCore;
@@ -11,7 +14,12 @@ export class NTQQMsgApi {
this.context = context;
this.core = core;
}
async getAioFirstViewLatestMsgs(peer: Peer, MsgCount: number) {
return this.context.session.getMsgService().getAioFirstViewLatestMsgs(peer, MsgCount);
}
async getLatestDbMsgs(peer: Peer, MsgCount: number) {
return this.context.session.getMsgService().getLatestDbMsgs(peer, MsgCount);
}
async FetchLongMsg(peer: Peer, msgId: string) {
return this.context.session.getMsgService().fetchLongMsg(peer, msgId);
}
@@ -19,10 +27,11 @@ export class NTQQMsgApi {
async sendShowInputStatusReq(peer: Peer, eventType: number) {
return this.context.session.getMsgService().sendShowInputStatusReq(peer.chatType, eventType, peer.peerUid);
}
async getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, count: number = 20) {
//console.log(peer, msgSeq, emojiId, emojiType, count);
//注意此处emojiType 可选值一般为1-2 2好像是unicode表情dec值 大部分情况 Taged M likiowa
return this.context.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, '', false, 20);
return this.context.session.getMsgService().getMsgEmojiLikesList(peer, msgSeq, emojiId, emojiType, '', false, count);
}
// napCatCore: NapCatCore | null = null;
@@ -40,6 +49,7 @@ export class NTQQMsgApi {
emojiId = emojiId.toString();
return this.context.session.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiId.length > 3 ? '2' : '1', set);
}
async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string): Promise<GeneralCallResult & {
msgList: RawMessage[]
} | undefined> {
@@ -51,7 +61,7 @@ export class NTQQMsgApi {
}
async getLastestMsgByUids(peer: Peer, count: number = 20, isReverseOrder: boolean = false) {
const ret = await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', '0', {
chatInfo: peer,
filterMsgType: [],
filterSendersUid: [],
@@ -61,7 +71,6 @@ export class NTQQMsgApi {
isIncludeCurrent: true,
pageLimit: count,
});
return ret;
}
async getMsgsByMsgId(peer: Peer | undefined, msgIds: string[] | undefined) {
@@ -80,7 +89,7 @@ export class NTQQMsgApi {
}
async queryMsgsWithFilterExWithSeq(peer: Peer, msgSeq: string) {
const ret = await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
filterMsgType: [],
filterSendersUid: [],
@@ -90,17 +99,17 @@ export class NTQQMsgApi {
isIncludeCurrent: true,
pageLimit: 1,
});
return ret;
}
async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) {
return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z);
}
async getMsgExBySeq(peer: Peer, msgSeq: string) {
const DateNow = Math.floor(Date.now() / 1000);
const filterMsgFromTime = (DateNow - 300).toString();
const filterMsgToTime = DateNow.toString();
const ret = await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, {
chatInfo: peer,//此处为Peer 为关键查询参数 没有啥也没有 by mlik iowa
filterMsgType: [],
filterSendersUid: [],
@@ -110,29 +119,29 @@ export class NTQQMsgApi {
isIncludeCurrent: true,
pageLimit: 100,
});
return ret;
}
async setMsgRead(peer: Peer) {
return this.context.session.getMsgService().setMsgRead(peer);
}
async getGroupFileList(GroupCode: string, params: GetFileListParam) {
const data = await this.core.eventWrapper.CallNormalEvent<
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
(groupFileListResult: GroupFileInfoUpdateParamType) => void
>(
'NodeIKernelRichMediaService/getGroupFileList',
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
1,
5000,
(groupFileListResult: GroupFileInfoUpdateParamType) => {
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
return true;
},
const [, groupFileListResult] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelRichMediaService/getGroupFileList',
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
[
GroupCode,
params,
);
return data[1].item;
],
() => true,
( /* groupFileListResult: GroupFileInfoUpdateParamType */) => {
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
return true;
},
1,
5000,
);
return groupFileListResult.item;
}
async getMsgHistory(peer: Peer, msgId: string, count: number, isReverseOrder: boolean = false) {
@@ -146,29 +155,32 @@ export class NTQQMsgApi {
peerUid: peer.peerUid,
}, msgIds);
}
async PrepareTempChat(toUserUid: string, GroupCode: string, nickname: string) {
//By Jadx/Ida Mlikiowa
const TempGameSession = {
nickname: "",
gameAppId: "",
selfTinyId: "",
peerRoleId: "",
peerOpenId: "",
nickname: '',
gameAppId: '',
selfTinyId: '',
peerRoleId: '',
peerOpenId: '',
};
return this.context.session.getMsgService().prepareTempChat({
chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP,
peerUid: toUserUid,
peerNickname: nickname,
fromGroupCode: GroupCode,
sig: "",
selfPhone: "",
sig: '',
selfPhone: '',
selfUid: this.core.selfInfo.uid,
gameSession: TempGameSession
gameSession: TempGameSession,
});
}
async getTempChatInfo(chatType: ChatType, peerUid: string) {
return this.context.session.getMsgService().getTempChatInfo(chatType, peerUid);
}
async sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) {
//唉? !我有个想法
if (peer.chatType === ChatType.KCHATTYPETEMPC2CFROMGROUP && peer.guildId && peer.guildId !== '') {
@@ -179,33 +191,32 @@ export class NTQQMsgApi {
}
const msgId = await this.generateMsgUniqueId(peer.chatType, await this.getServerTime());
peer.guildId = msgId;
const data = await this.core.eventWrapper.CallNormalEvent<
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void
>(
'NodeIKernelMsgService/sendMsg',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
timeout,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
return true;
}
}
return false;
},
const [, msgList] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelMsgService/sendMsg',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
[
'0',
peer,
msgElements,
new Map(),
);
const retMsg = data[1].find(msgRecord => {
],
() => true,
msgRecords => {
for (const msgRecord of msgRecords) {
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
return true;
}
}
return false;
},
1,
timeout,
);
return msgList.find(msgRecord => {
if (msgRecord.guildId === msgId) {
return true;
}
});
return retMsg;
}
async generateMsgUniqueId(chatType: number, time: string) {
@@ -224,34 +235,32 @@ export class NTQQMsgApi {
const msgInfos = msgIds.map(id => {
return { msgId: id, senderShowName: this.core.selfInfo.nick };
});
const data = await this.core.eventWrapper.CallNormalEvent<
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>,
(msgList: RawMessage[]) => void
>(
'NodeIKernelMsgService/multiForwardMsgWithComment',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
1,
5000,
(msgRecords: RawMessage[]) => {
for (const msgRecord of msgRecords) {
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
return true;
}
}
return false;
},
const [, msgList] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelMsgService/multiForwardMsgWithComment',
'NodeIKernelMsgListener/onMsgInfoListUpdate',
[
msgInfos,
srcPeer,
destPeer,
[],
new Map(),
);
for (const msg of data[1]) {
],
() => true,
(msgRecords) => {
for (const msgRecord of msgRecords) {
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
return true;
}
}
return false;
},
);
for (const msg of msgList) {
const arkElement = msg.elements.find(ele => ele.arkElement);
if (!arkElement) {
continue;
}
const forwardData: any = JSON.parse(arkElement.arkElement?.bytesData ?? "");
const forwardData: any = JSON.parse(arkElement.arkElement?.bytesData ?? '');
if (forwardData.app != 'com.tencent.multimsg') {
continue;
}

View File

@@ -1,4 +1,4 @@
import { RequestUtil } from '@/common/utils/request';
import { RequestUtil } from '@/common/request';
import { MiniAppLuaJsonType } from '@/core';
import { InstanceContext, NapCatCore } from '..';

View File

@@ -1,4 +1,4 @@
import { GeneralCallResult, InstanceContext, NapCatCore } from '@/core';
import { InstanceContext, NapCatCore } from '@/core';
export class NTQQSystemApi {
context: InstanceContext;
@@ -28,18 +28,12 @@ export class NTQQSystemApi {
//1-2-162b9b42-65b9-4405-a8ed-2e256ec8aa50
async getArkJsonCollection(cid: string) {
const ret = await this.core.eventWrapper.callNoListenerEvent<(cid: string) => Promise<GeneralCallResult & {
arkJson: string
}>>(
'NodeIKernelCollectionService/collectionArkShare',
'1717662698058',
);
return ret;
return await this.core.eventWrapper.callNoListenerEvent('NodeIKernelCollectionService/collectionArkShare', '1717662698058');
}
async BootMiniApp(appfile: string, params: string) {
await this.context.session.getNodeMiscService().setMiniAppVersion('2.16.4');
const c = await this.context.session.getNodeMiscService().getMiniAppPath();
// const c = await this.context.session.getNodeMiscService().getMiniAppPath();
return this.context.session.getNodeMiscService().startNewMiniApp(appfile, params);
}

View File

@@ -1,9 +1,8 @@
import type { ModifyProfileParams, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '@/core/entities';
import { NodeIKernelProfileListener } from '@/core/listeners';
import { RequestUtil } from '@/common/utils/request';
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
import type { ModifyProfileParams, User } from '@/core/entities';
import { RequestUtil } from '@/common/request';
import { ProfileBizType, UserDetailSource } from '@/core/services';
import { InstanceContext, NapCatCore } from '..';
import { solveAsyncProblem } from '@/common/utils/helper';
import { solveAsyncProblem } from '@/common/helper';
export class NTQQUserApi {
context: InstanceContext;
@@ -65,17 +64,18 @@ export class NTQQUserApi {
}
async fetchUserDetailInfos(uids: string[]) {
//26702 以上使用新接口 .Dev Mlikiowa
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
// TODO: 26702 以上使用新接口 .Dev MliKiowa
const retData: User[] = [];
const [_retData, _retListener] = await this.core.eventWrapper.CallNormalEvent<
EventService, EventListener
>(
const [_retData, _retListener] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelProfileService/fetchUserDetailInfo',
'NodeIKernelProfileListener/onUserDetailInfoChanged',
uids.length,
5000,
[
'BuddyProfileStore',
uids,
UserDetailSource.KSERVER,
[ProfileBizType.KALL],
],
() => true,
(profile) => {
if (uids.includes(profile.uid)) {
const RetUser: User = {
@@ -92,28 +92,24 @@ export class NTQQUserApi {
}
return false;
},
'BuddyProfileStore',
uids,
UserDetailSource.KSERVER,
[ProfileBizType.KALL],
uids.length,
);
return retData;
}
async fetchUserDetailInfo(uid: string, mode: UserDetailSource = UserDetailSource.KDB) {
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
const [_retData, profile] = await this.core.eventWrapper.CallNormalEvent<EventService, EventListener>(
const [_retData, profile] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelProfileService/fetchUserDetailInfo',
'NodeIKernelProfileListener/onUserDetailInfoChanged',
1,
5000,
[
'BuddyProfileStore',
[uid],
mode,
[ProfileBizType.KALL],
],
() => true,
(profile) => profile.uid === uid,
'BuddyProfileStore',
[uid],
mode,
[ProfileBizType.KALL],
);
const RetUser: User = {
...profile.simpleInfo.coreInfo,
@@ -146,8 +142,7 @@ export class NTQQUserApi {
const ClientKeyData = await this.forceFetchClientKey();
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin +
'&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2F' + domain + '%2F' + this.core.selfInfo.uin + '%2Finfocenter&keyindex=19%27';
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
return cookies;
return await RequestUtil.HttpsGetCookies(requestUrl);
}
async getPSkey(domainList: string[]) {
@@ -170,8 +165,7 @@ export class NTQQUserApi {
async getQzoneCookies() {
const ClientKeyData = await this.forceFetchClientKey();
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin + '&clientkey=' + ClientKeyData.clientKey + '&u1=https%3A%2F%2Fuser.qzone.qq.com%2F' + this.core.selfInfo.uin + '%2Finfocenter&keyindex=19%27';
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
return cookies;
return await RequestUtil.HttpsGetCookies(requestUrl);
}
//需要异常处理
@@ -182,7 +176,7 @@ export class NTQQUserApi {
throw new Error('getClientKey Error');
}
const clientKey = ClientKeyData.clientKey;
const keyIndex = ClientKeyData.keyIndex;
// const keyIndex = ClientKeyData.keyIndex;
const requestUrl = 'https://ssl.ptlogin2.qq.com/jump?ptlang=1033&clientuin=' + this.core.selfInfo.uin + '&clientkey=' + clientKey + '&u1=https%3A%2F%2Fh5.qzone.qq.com%2Fqqnt%2Fqzoneinpcqq%2Ffriend%3Frefresh%3D0%26clientuin%3D0%26darkMode%3D0&keyindex=19%27';
const cookies: { [key: string]: string; } = await RequestUtil.HttpsGetCookies(requestUrl);
const skey = cookies['skey'];
@@ -237,8 +231,7 @@ export class NTQQUserApi {
}
async getUserDetailInfoByUinV2(Uin: string) {
return await this.core.eventWrapper.callNoListenerEvent<(Uin: string) => Promise<UserDetailInfoByUinV2>>
('NodeIKernelProfileService/getUserDetailInfoByUin', Uin);
return await this.core.eventWrapper.callNoListenerEvent('NodeIKernelProfileService/getUserDetailInfoByUin', Uin);
}
async forceFetchClientKey() {

View File

@@ -1,4 +1,4 @@
import { RequestUtil } from '@/common/utils/request';
import { RequestUtil } from '@/common/request';
import {
GroupEssenceMsgRet,
InstanceContext,
@@ -184,11 +184,8 @@ export class NTQQWebApi {
const HonorInfo: any = { group_id: groupCode };
if (getType === WebHonorType.TALKATIVE || getType === WebHonorType.ALL) {
try {
const RetInternal = await getDataInternal(groupCode, 1);
if (!RetInternal) {
throw new Error('获取龙王信息失败');
}
const RetInternal = await getDataInternal(groupCode, 1);
if (RetInternal) {
HonorInfo.current_talkative = {
user_id: RetInternal[0]?.uin,
avatar: RetInternal[0]?.avatar,
@@ -206,16 +203,13 @@ export class NTQQWebApi {
nickname: talkative_ele?.name,
});
}
} catch (e) {
this.context.logger.logDebug(e);
} else {
this.context.logger.logError('获取龙王信息失败');
}
}
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
try {
const RetInternal = await getDataInternal(groupCode, 2);
if (!RetInternal) {
throw new Error('获取群聊之火失败');
}
const RetInternal = await getDataInternal(groupCode, 2);
if (RetInternal) {
HonorInfo.performer_list = [];
for (const performer_ele of RetInternal) {
HonorInfo.performer_list.push({
@@ -225,16 +219,13 @@ export class NTQQWebApi {
description: performer_ele?.desc,
});
}
} catch (e) {
this.context.logger.logDebug(e);
} else {
this.context.logger.logError('获取群聊之火失败');
}
}
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
try {
const RetInternal = await getDataInternal(groupCode, 3);
if (!RetInternal) {
throw new Error('获取群聊炽焰失败');
}
const RetInternal = await getDataInternal(groupCode, 3);
if (RetInternal) {
HonorInfo.legend_list = [];
for (const legend_ele of RetInternal) {
HonorInfo.legend_list.push({
@@ -244,16 +235,13 @@ export class NTQQWebApi {
desc: legend_ele?.description,
});
}
} catch (e) {
this.context.logger.logDebug('获取群聊炽焰失败', e);
} else {
this.context.logger.logError('获取群聊炽焰失败');
}
}
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
try {
const RetInternal = await getDataInternal(groupCode, 6);
if (!RetInternal) {
throw new Error('获取快乐源泉失败');
}
const RetInternal = await getDataInternal(groupCode, 6);
if (RetInternal) {
HonorInfo.emotion_list = [];
for (const emotion_ele of RetInternal) {
HonorInfo.emotion_list.push({
@@ -263,11 +251,11 @@ export class NTQQWebApi {
desc: emotion_ele.description,
});
}
} catch (e) {
this.context.logger.logDebug('获取快乐源泉失败', e);
} else {
this.context.logger.logError('获取快乐源泉失败');
}
}
//冒尖小春笋好像已经被tx扬了
// 冒尖小春笋好像已经被tx扬了 R.I.P.
if (getType === WebHonorType.EMOTION || getType === WebHonorType.ALL) {
HonorInfo.strong_newbie_list = [];
}

View File

@@ -1,16 +1,16 @@
import { NodeQQNTWrapperUtil, NTApiContext, WrapperNodeApi } from '@/core/wrapper';
import { NodeQQNTWrapperUtil, StableNTApiWrapper, WrapperNodeApi } from '@/core/wrapper';
import path from 'node:path';
import fs from 'node:fs';
import { InstanceContext } from './wrapper';
import { proxiedListenerOf } from '@/common/utils/proxy-handler';
import { NodeIKernelMsgListener, NodeIKernelGroupListener, NodeIKernelProfileListener } from './listeners';
import { proxiedListenerOf } from '@/common/proxy-handler';
import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from './listeners';
import { DataSource, GroupMember, SelfInfo } from './entities';
import { LegacyNTEventWrapper } from '@/common/framework/event-legacy';
import { NTEventWrapper } from '@/common/event';
import { NTQQFileApi, NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQSystemApi, NTQQUserApi, NTQQWebApi } from './apis';
import os from 'node:os';
import { NTQQCollectionApi } from './apis/collection';
import { NapCatConfigLoader } from './helper/config';
import { LogLevel } from '@/common/utils/log';
import { LogLevel } from '@/common/log';
export enum NapCatCoreWorkingEnv {
Unknown = 0,
@@ -30,8 +30,8 @@ export function loadQQWrapper(QQVersion: string): WrapperNodeApi {
export class NapCatCore {
readonly context: InstanceContext;
readonly apis: NTApiContext;
readonly eventWrapper: LegacyNTEventWrapper;
readonly apis: StableNTApiWrapper;
readonly eventWrapper: NTEventWrapper;
// readonly eventChannel: NTEventChannel;
NapCatDataPath: string;
NapCatTempPath: string;
@@ -45,7 +45,7 @@ export class NapCatCore {
this.selfInfo = selfInfo;
this.context = context;
this.util = this.context.wrapper.NodeQQNTWrapperUtil;
this.eventWrapper = new LegacyNTEventWrapper(context.session);
this.eventWrapper = new NTEventWrapper(context.session);
this.apis = {
FileApi: new NTQQFileApi(this.context, this),
SystemApi: new NTQQSystemApi(this.context, this),
@@ -98,7 +98,7 @@ export class NapCatCore {
};
//await sleep(2500);
this.context.session.getMsgService().addKernelMsgListener(
proxiedListenerOf(msgListener, this.context.logger) as any
proxiedListenerOf(msgListener, this.context.logger) as any,
);
const profileListener = new NodeIKernelProfileListener();
@@ -125,18 +125,18 @@ export class NapCatCore {
//群成员数量变化 应该刷新缓存
if (existGroup && g.memberCount === existGroup.memberCount) {
Object.assign(existGroup, g);
}
else {
} else {
this.apis.GroupApi.groupCache.set(g.groupCode, g);
// 获取群成员
}
const sceneId = this.context.session.getGroupService().createMemberListScene(g.groupCode, 'groupMemberList_MainWindow');
this.context.session.getGroupService().getNextMemberList(sceneId!, undefined, 3000).then( /* r => {
this.context.session.getGroupService().getNextMemberList(sceneId, undefined, 3000).then( /* r => {
// console.log(`get group ${g.groupCode} members`, r);
// r.result.infos.forEach(member => {
// });
// groupMembers.set(g.groupCode, r.result.infos);
} */);
this.context.session.getGroupService().destroyMemberListScene(sceneId);
});
};
groupListener.onMemberListChange = (arg) => {
@@ -149,8 +149,7 @@ export class NapCatCore {
const existMember = existMembers.get(uid);
if (existMember) {
Object.assign(existMember, member);
}
else {
} else {
existMembers!.set(uid, member);
}
//移除成员
@@ -158,14 +157,11 @@ export class NapCatCore {
existMembers.delete(uid);
}
});
}
else {
} else {
this.apis.GroupApi.groupMemberCache.set(groupCode, arg.infos);
}
// console.log('onMemberListChange', groupCode, arg);
};
groupListener.onMemberInfoChange = (groupCode, dataSource, members) => {
//console.log('onMemberInfoChange', groupCode, changeType, members);
if (dataSource === DataSource.LOCAL && members.get(this.selfInfo.uid)?.isDelete) {
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
setTimeout(() => {
@@ -182,8 +178,7 @@ export class NapCatCore {
member.isChangeRole = this.checkAdminEvent(groupCode, member, existMember);
// 更新成员信息
Object.assign(existMember, member);
}
else {
} else {
existMembers.set(uid, member);
}
//移除成员
@@ -191,15 +186,15 @@ export class NapCatCore {
existMembers.delete(uid);
}
});
}
else {
} else {
this.apis.GroupApi.groupMemberCache.set(groupCode, members);
}
};
this.context.session.getGroupService().addKernelGroupListener(
proxiedListenerOf(groupListener, this.context.logger) as any
proxiedListenerOf(groupListener, this.context.logger) as any,
);
}
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
if (memberNew.role !== memberOld?.role) {
this.context.logger.logDebug(`${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);

View File

@@ -1,34 +1,84 @@
import { QQLevel, Sex, User } from './user';
import { QQLevel, Sex } from './user';
export interface KickMemberInfo {
optFlag: number,
optOperate: number,
optMemberUid: string,
optBytesMsg: string,
}
export interface KickMemberV2Req{
//getGroupDetailInfo GroupCode,GroupInfoSource
export enum GroupInfoSource {
KUNSPECIFIED,
KBIGDATACARD,
KDATACARD,
KNOTICE,
KAIO,
KRECENTCONTACT,
KMOREPANEL
}
export interface GroupExt0xEF0InfoFilter {
bindGuildId: number;
blacklistExpireTime: number;
companyId: number;
essentialMsgPrivilege: number;
essentialMsgSwitch: number;
fullGroupExpansionSeq: number;
fullGroupExpansionSwitch: number;
gangUpId: number;
groupAioBindGuildId: number;
groupBindGuildIds: number;
groupBindGuildSwitch: number;
groupExcludeGuildIds: number;
groupExtFlameData: number;
groupFlagPro1: number;
groupInfoExtSeq: number;
groupOwnerId: number;
groupSquareSwitch: number;
hasGroupCustomPortrait: number;
inviteRobotMemberExamine: number;
inviteRobotMemberSwitch: number;
inviteRobotSwitch: number;
isLimitGroupRtc: number;
lightCharNum: number;
luckyWord: number;
luckyWordId: number;
msgEventSeq: number;
qqMusicMedalSwitch: number;
reserve: number;
showPlayTogetherSwitch: number;
starId: number;
todoSeq: number;
viewedMsgDisappearTime: number;
}
export interface KickMemberV2Req {
groupCode: string,
kickFlag: number,
kickList: Array<KickMemberInfo>,
kickListUids: Array<string>,
kickMsg: string
}
export enum DataSource {
LOCAL,
REMOTE
}
export enum GroupListUpdateType {
REFRESHALL,
GETALL,
MODIFIED,
REMOVE
}
export interface GroupMemberCache {
group: {
data: GroupMember[];
isExpired: boolean;
}
};
isExpired: boolean;
}
export interface Group {
groupCode: string,
createTime?: string,//高版本才有

View File

@@ -1,4 +1,4 @@
import { GroupMemberRole } from './group';
import { GroupMemberRole } from '@/core';
export interface Peer {
chatType: ChatType;
@@ -145,6 +145,7 @@ export interface TaskTopMsgElement {
iconUrl: string;
topMsgType: number;
}
export enum NTMsgType {
KMSGTYPEARKSTRUCT = 11,
KMSGTYPEFACEBUBBLE = 24,
@@ -168,6 +169,7 @@ export enum NTMsgType {
KMSGTYPEVIDEO = 7,
KMSGTYPEWALLET = 10
}
export interface SendTaskTopMsgElement {
elementType: ElementType.TASKTOPMSG;
elementId: string;
@@ -355,6 +357,7 @@ export enum NTMsgAtType {
ATTYPESUMMONROLE = 256,
ATTYPEUNKNOWN = 0
}
export interface SendPicElement {
elementType: ElementType.PIC;
elementId: string;
@@ -511,6 +514,7 @@ export enum AtType {
atAll = 1,
atUser = 2
}
// 来自Android分析
export enum ChatType {
KCHATTYPEADELIE = 42,
@@ -626,6 +630,7 @@ export enum NTGrayTipElementSubTypeV2 {
GRAYTIP_ELEMENT_SUBTYPE_WALLET = 16,
GRAYTIP_ELEMENT_SUBTYPE_XMLMSG = 12,
}
export interface GrayTipElement {
subElementType: NTGrayTipElementSubTypeV2;
revokeElement: {
@@ -848,12 +853,14 @@ export interface MultiForwardMsgElement {
resId: string;
fileName: string;
}
export enum SendStatusType {
KSEND_STATUS_FAILED = 0,
KSEND_STATUS_SENDING = 1,
KSEND_STATUS_SUCCESS = 2,
KSEND_STATUS_SUCCESS_NOSEQ = 3
}
export interface RawMessage {
parentMsgPeer: Peer;
@@ -923,4 +930,4 @@ export interface RawMessage {
records: RawMessage[];
elements: MessageElement[];
}
}

View File

@@ -16,6 +16,7 @@ export enum GroupNotifyMsgType {
TRANSFER_GROUP_NOTIFY_OLDOWNER,
TRANSFER_GROUP_NOTIFY_ADMIN
}
export interface GroupNotifies {
doubt: boolean;
nextStartSeq: string;
@@ -29,17 +30,20 @@ export enum GroupNotifyMsgStatus {
KREFUSED,//拒绝
KIGNORED//忽略
}
export enum GroupInviteStatus {
INIT,
WAIT_TO_APPROVE,
JOINED,
REFUSED_BY_ADMINI_STRATOR
}
export enum GroupInviteType {
BYBUDDY,
BYGROUPMEMBER,
BYDISCUSSMEMBER
}
export interface GroupNotify {
seq: string; // 通知序列号
type: GroupNotifyMsgType;

View File

@@ -57,7 +57,7 @@ export interface WebApiGroupMemberRet {
export interface WebApiGroupNoticeFeed {
u: number;//发送者
fid: string;//fid
fid: string;//fid,notice_id
pubt: number;//时间
msg: {
text: string

View File

@@ -7,24 +7,28 @@
"appid": 537240645,
"qua": "V1_LNX_NQ_3.2.12_27206_GW_B"
},
"3.2.12-27254":{
"3.2.12-27254": {
"appid": 537240795,
"qua": "V1_LNX_NQ_3.2.12_27254_GW_B"
},
"9.9.15-27187":{
"9.9.15-27187": {
"appid": 537240610,
"qua": "V1_WIN_NQ_9.9.15_27187_GW_B"
},
"9.9.15-27206":{
"9.9.15-27206": {
"appid": 537240610,
"qua": "V1_WIN_NQ_9.9.15_27206_GW_B"
},
"9.9.15-27254":{
"9.9.15-27254": {
"appid": 537240709,
"qua": "V1_WIN_NQ_9.9.15_27254_GW_B"
},
"9.9.15-27333": {
"appid": 537240709,
"qua": "V1_WIN_NQ_9.9.15_27333_GW_B"
},
"9.9.15-27391": {
"appid": 537240709,
"qua": "V1_WIN_NQ_9.9.15_27333_GW_B"
}
}
}

View File

@@ -1,13 +1,11 @@
import { ConfigBase } from "@/common/utils/ConfigBase";
import { ConfigBase } from '@/common/config-base';
import napCatDefaultConfig from '@/core/external/napcat.json';
import { NapCatCore } from '@/core';
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
export type NapCatConfig = typeof napCatDefaultConfig;
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
export class NapCatConfigLoader extends ConfigBase<NapCatConfig> {
constructor(coreContext: NapCatCore, configPath: string) {
super('napcat', coreContext, configPath);
constructor(core: NapCatCore, configPath: string) {
super('napcat', core, configPath);
}
}

View File

@@ -1,5 +1,5 @@
import { LogWrapper } from '@/common/utils/log';
import { RequestUtil } from '@/common/utils/request';
import { LogWrapper } from '@/common/log';
import { RequestUtil } from '@/common/request';
interface ServerRkeyData {
group_rkey: string;

View File

@@ -1,4 +1,3 @@
export class NodeIKernelFileAssistantListener {
onFileStatusChanged(...args: unknown[]) {
}

View File

@@ -1,6 +1,7 @@
import { DataSource, Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
export class NodeIKernelGroupListener {
onGroupListInited(listEmpty: boolean): void { }
// 发现于Win 9.9.9 23159
onGroupMemberLevelInfoChange(...args: unknown[]): void {

View File

@@ -1,4 +1,3 @@
export class NodeIKernelLoginListener {
onLoginConnected(...args: any[]): void {
}

View File

@@ -259,7 +259,7 @@ export class NodeIKernelMsgListener {
}
onRecvSysMsg(arrayList: unknown) {
onRecvSysMsg(arrayList: Array<number>) {
}

View File

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

View File

@@ -1,4 +1,4 @@
export class NodeIKernelRobotListener{
export class NodeIKernelRobotListener {
onRobotFriendListChanged(...args: unknown[]) {
}

View File

@@ -0,0 +1,39 @@
import { ChatType } from '@/core';
export interface NodeIKernelSearchListener_Polyfill {
onSearchFileKeywordsResult(params: {
searchId: string,
hasMore: boolean,
resultItems: {
chatType: ChatType,
buddyChatInfo: any[],
discussChatInfo: any[],
groupChatInfo: {
groupCode: string,
isConf: boolean,
hasModifyConfGroupFace: boolean,
hasModifyConfGroupName: boolean,
groupName: string,
remark: string
}[],
dataLineChatInfo: any[],
tmpChatInfo: any[],
msgId: string,
msgSeq: string,
msgTime: string,
senderUid: string,
senderNick: string,
senderRemark: string,
senderCard: string,
elemId: string,
elemType: number,
fileSize: string,
filePath: string,
fileName: string,
hits: {
start: number,
end: number
}[]
}[]
}): void;
}

View File

@@ -9,4 +9,32 @@ export * from './NodeIKernelProfileListener';
export * from './NodeIKernelTicketListener';
export * from './NodeIKernelStorageCleanListener';
export * from './NodeIKernelFileAssistantListener';
export * from './NodeIKernelSearchListener_Polyfill';
import type {
NodeIKernelBuddyListener,
NodeIKernelFileAssistantListener,
NodeIKernelGroupListener,
NodeIKernelLoginListener,
NodeIKernelMsgListener,
NodeIKernelProfileListener,
NodeIKernelRobotListener,
NodeIKernelSearchListener_Polyfill,
NodeIKernelSessionListener,
NodeIKernelStorageCleanListener,
NodeIKernelTicketListener,
} from '.';
export type ListenerNamingMapping = {
NodeIKernelSessionListener: NodeIKernelSessionListener;
NodeIKernelLoginListener: NodeIKernelLoginListener;
NodeIKernelMsgListener: NodeIKernelMsgListener;
NodeIKernelGroupListener: NodeIKernelGroupListener;
NodeIKernelBuddyListener: NodeIKernelBuddyListener;
NodeIKernelProfileListener: NodeIKernelProfileListener;
NodeIKernelRobotListener: NodeIKernelRobotListener;
NodeIKernelTicketListener: NodeIKernelTicketListener;
NodeIKernelStorageCleanListener: NodeIKernelStorageCleanListener;
NodeIKernelFileAssistantListener: NodeIKernelFileAssistantListener;
NodeIKernelSearchListener: NodeIKernelSearchListener_Polyfill;
};

View File

@@ -1,18 +1,23 @@
import { IGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import { NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import {
GroupExt0xEF0InfoFilter,
GroupExtParam,
GroupMember,
GroupMemberRole,
GroupNotifyMsgType,
GroupRequestOperateTypes,
KickMemberV2Req
KickMemberV2Req,
} from '@/core/entities';
import { GeneralCallResult } from '@/core/services/common';
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
export interface NodeIKernelGroupService {
//getGroupExt0xEF0Info(this.$enableGroupCodes, this.$bannedGroupCodes, this.$filter, this.$forceFetch
getGroupExt0xEF0Info(enableGroupCodes: string[], bannedGroupCodes: string[], filter: GroupExt0xEF0InfoFilter, forceFetch: boolean):
Promise<GeneralCallResult & { result: { groupExtInfos: Map<string, any> }}>;
kickMemberV2(param: KickMemberV2Req): Promise<GeneralCallResult>;
quitGroupV2(param: { groupCode: string; needDeleteLocalMsg: boolean; }): Promise<GeneralCallResult>;
getMemberCommonInfo(Req: {
@@ -108,7 +113,7 @@ export interface NodeIKernelGroupService {
setHeader(uid: string, path: string): unknown;
addKernelGroupListener(listener: IGroupListener): number;
addKernelGroupListener(listener: NodeIKernelGroupListener): number;
removeKernelGroupListener(listenerId: unknown): void;
@@ -209,7 +214,7 @@ export interface NodeIKernelGroupService {
getGroupBulletin(groupCode: string): unknown;
deleteGroupBulletin(groupCode: string, seq: string, feedId: string): void;
deleteGroupBulletin(groupCode: string, seq: string, noticeId: string): void;
publishGroupBulletin(groupCode: string, pskey: string, data: any): Promise<GeneralCallResult>;

View File

@@ -12,6 +12,7 @@ export interface QueryMsgsParams {
isReverseOrder: boolean,
isIncludeCurrent: boolean
}
export interface TmpChatInfoApi {
errMsg: string;
result: number;
@@ -166,11 +167,17 @@ export interface NodeIKernelMsgService {
getAllOnlineFileMsgs(...args: unknown[]): unknown;
getLatestDbMsgs(peer: Peer, cnt: number): Promise<unknown>;
getLatestDbMsgs(peer: Peer, cnt: number): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>;
getLastMessageList(peer: Peer[]): Promise<unknown>;
getLastMessageList(peer: Peer[]): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>;
getAioFirstViewLatestMsgs(peer: Peer, num: number): unknown;
getAioFirstViewLatestMsgs(peer: Peer, num: number): Promise<GeneralCallResult & {
msgList: RawMessage[]
}>;
getMsgs(peer: Peer, msgId: string, count: unknown, queryOrder: boolean): Promise<unknown>;

View File

@@ -1,6 +1,5 @@
import { AnyCnameRecord } from 'node:dns';
import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
import { NodeIKernelProfileListener } from '../listeners';
import { BizKey, ModifyProfileParams, NodeIKernelProfileListener, SimpleInfo, UserDetailInfoByUinV2 } from '@/core';
import { GeneralCallResult } from '@/core/services/common';
export enum UserDetailSource {
@@ -86,7 +85,7 @@ export interface NodeIKernelProfileService {
getUserDetailInfoWithBizInfo(uid: string, Biz: BizKey[]): Promise<GeneralCallResult>;
getUserDetailInfoByUin(uin: string): Promise<UserDetailInfoByUin>;
getUserDetailInfoByUin(uin: string): Promise<UserDetailInfoByUinV2>;
getZplanAvatarInfos(args: string[]): Promise<unknown>;

View File

@@ -14,3 +14,45 @@ export * from './NodeIKernelRobotService';
export * from './NodeIKernelRichMediaService';
export * from './NodeIKernelDbToolsService';
export * from './NodeIKernelTipOffService';
export * from './NodeIKernelSearchService';
export * from './NodeIKernelCollectionService';
import type {
NodeIKernelAvatarService,
NodeIKernelBuddyService,
NodeIKernelCollectionService,
NodeIKernelDbToolsService,
NodeIKernelFileAssistantService,
NodeIKernelGroupService,
NodeIKernelLoginService,
NodeIKernelMsgService,
NodeIKernelOnlineStatusService,
NodeIKernelProfileLikeService,
NodeIKernelProfileService,
NodeIKernelRichMediaService,
NodeIKernelRobotService,
NodeIKernelSearchService,
NodeIKernelStorageCleanService,
NodeIKernelTicketService,
NodeIKernelTipOffService,
} from '.';
export type ServiceNamingMapping = {
NodeIKernelAvatarService: NodeIKernelAvatarService;
NodeIKernelBuddyService: NodeIKernelBuddyService;
NodeIKernelFileAssistantService: NodeIKernelFileAssistantService;
NodeIKernelGroupService: NodeIKernelGroupService;
NodeIKernelLoginService: NodeIKernelLoginService;
NodeIKernelMsgService: NodeIKernelMsgService;
NodeIKernelOnlineStatusService: NodeIKernelOnlineStatusService;
NodeIKernelProfileLikeService: NodeIKernelProfileLikeService;
NodeIKernelProfileService: NodeIKernelProfileService;
NodeIKernelTicketService: NodeIKernelTicketService;
NodeIKernelStorageCleanService: NodeIKernelStorageCleanService;
NodeIKernelRobotService: NodeIKernelRobotService;
NodeIKernelRichMediaService: NodeIKernelRichMediaService;
NodeIKernelDbToolsService: NodeIKernelDbToolsService;
NodeIKernelTipOffService: NodeIKernelTipOffService;
NodeIKernelSearchService: NodeIKernelSearchService,
NodeIKernelCollectionService: NodeIKernelCollectionService;
};

View File

@@ -1,9 +1,9 @@
import { LogWrapper } from '@/common/utils/log';
import { QQBasicInfoWrapper } from '@/common/utils/QQBasicInfo';
import { LogWrapper } from '@/common/log';
import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
import { NapCatCoreWorkingEnv, NodeIKernelLoginService, NodeIQQNTWrapperSession, WrapperNodeApi } from '@/core';
import { NTQQFileApi, NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQSystemApi, NTQQUserApi, NTQQWebApi } from '../apis';
import { NTQQCollectionApi } from '../apis/collection';
import { NapCatPathWrapper } from '@/common/framework/napcat';
import { NapCatPathWrapper } from '@/common/path';
export interface InstanceContext {
readonly workingEnv: NapCatCoreWorkingEnv;
@@ -15,7 +15,7 @@ export interface InstanceContext {
readonly pathWrapper: NapCatPathWrapper;
}
export interface NTApiContext {
export interface StableNTApiWrapper {
FileApi: NTQQFileApi,
SystemApi: NTQQSystemApi,
CollectionApi: NTQQCollectionApi,

View File

@@ -1,7 +1,7 @@
import path from 'node:path';
import fs from 'node:fs';
import { PlatformType, VendorType, WrapperSessionInitConfig } from './wrapper';
import { getMachineId, hostname, systemName, systemVersion } from '@/common/utils/system';
import { getMachineId, hostname, systemName, systemVersion } from '@/common/system';
export async function genSessionConfig(QQVersionAppid: string, QQVersion: string, selfUin: string, selfUid: string, account_path: string): Promise<WrapperSessionInitConfig> {
const downloadPath = path.join(account_path, 'NapCat', 'temp');

View File

@@ -1,7 +1,4 @@
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '../adapters';
import {
NodeIKernelSessionListener
} from '../listeners';
import {
NodeIKernelAvatarService,
NodeIKernelBuddyService,
@@ -11,11 +8,12 @@ import {
NodeIKernelProfileLikeService,
NodeIKernelProfileService,
NodeIKernelRichMediaService,
NodeIKernelRobotService,
NodeIKernelSessionListener,
NodeIKernelStorageCleanService,
NodeIKernelTicketService,
NodeIKernelTipOffService,
} from '../services';
import { NodeIKernelStorageCleanService } from '../services/NodeIKernelStorageCleanService';
import { NodeIKernelRobotService } from '../services/NodeIKernelRobotService';
} from '@/core';
import { NodeIKernelNodeMiscService } from '../services/NodeIKernelNodeMiscService';
import { NodeIKernelUixConvertService } from '../services/NodeIKernelUixConvertService';
import { NodeIKernelMsgBackupService } from '../services/NodeIKernelMsgBackupService';
@@ -31,6 +29,7 @@ import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService';
export interface NodeQQNTWrapperUtil {
get(): unknown;
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(): NodeQQNTWrapperUtil;

View File

@@ -1,6 +1,6 @@
//LiteLoader需要提供部分IPC接口以便于其他插件调用
const { ipcMain } = require('electron');
const napcat= require('./napcat.cjs');
ipcMain.handle("napcat_get_webtoken", async (event, arg) => {
const napcat = require('./napcat.cjs');
ipcMain.handle('napcat_get_webtoken', async (event, arg) => {
return napcat.NCgetWebUiUrl();
});

View File

@@ -1,7 +1,7 @@
import { NapCatPathWrapper } from '@/common/framework/napcat';
import { LogWrapper } from '@/common/utils/log';
import { proxiedListenerOf } from '@/common/utils/proxy-handler';
import { QQBasicInfoWrapper } from '@/common/utils/QQBasicInfo';
import { NapCatPathWrapper } from '@/common/path';
import { LogWrapper } from '@/common/log';
import { proxiedListenerOf } from '@/common/proxy-handler';
import { QQBasicInfoWrapper } from '@/common/qq-basic-info';
import { loadQQWrapper, NapCatCore, NapCatCoreWorkingEnv } from '@/core/core';
import { InstanceContext } from '@/core';
import { SelfInfo } from '@/core/entities';
@@ -14,8 +14,9 @@ import { NapCatOneBot11Adapter } from '@/onebot';
//Framework ES入口文件
export async function getWebUiUrl() {
const WebUiConfigData = (await WebUiConfig.GetWebUIConfig());
return "http://127.0.0.1:" + WebUiConfigData.port + '/webui/?token=' + WebUiConfigData.token;
return 'http://127.0.0.1:' + WebUiConfigData.port + '/webui/?token=' + WebUiConfigData.token;
}
export async function NCoreInitFramework(
session: NodeIQQNTWrapperSession,
loginService: NodeIKernelLoginService,

View File

@@ -1,11 +1,10 @@
const { contextBridge } = require('electron')
const { ipcRenderer } = require('electron')
const { contextBridge } = require('electron');
const { ipcRenderer } = require('electron');
const napcat = {
getWebUiUrl: async () => {
return ipcRenderer.invoke("napcat_get_webtoken")
}
}
return ipcRenderer.invoke('napcat_get_webtoken');
},
};
// 在window对象下导出只读对象
contextBridge.exposeInMainWorld('napcat', napcat)
contextBridge.exposeInMainWorld('napcat', napcat);

View File

@@ -1,6 +1,5 @@
export const onSettingWindowCreated = async (view) => {
// view.style.width = "100%";
// view.style.height = "100%";
// //添加iframe
@@ -14,7 +13,7 @@ export const onSettingWindowCreated = async (view) => {
// //有滚动条何尝不是一种美
// view.appendChild(iframe);
let webui = await window.napcat.getWebUiUrl();
let panel = `
view.innerHTML = `
<setting-section data-title="">
<setting-panel>
<setting-list data-direction="column">
@@ -30,9 +29,8 @@ export const onSettingWindowCreated = async (view) => {
</setting-panel>
</setting-section>
`;
view.innerHTML = panel;
view.querySelector(".nc_openwebui").addEventListener("click", () => {
window.open(webui, "_blank");
view.querySelector('.nc_openwebui').addEventListener('click', () => {
window.open(webui, '_blank');
});
view.querySelector(".nc_webui").innerText = webui;
};
view.querySelector('.nc_webui').innerText = webui;
};

4
src/nekodoge/Readme.md Normal file
View File

@@ -0,0 +1,4 @@
# nekodoge
此协议为替代QQ平台 OnebotV11长期不可靠问题
# 规划路线

View File

View File

View File

0
src/nekodoge/index.ts Normal file
View File

View File

@@ -0,0 +1,18 @@
import { createServer } from 'node:net';
export class NewAdapterNetwork {
constructor(public host: number, public port: number) { }
async open() {
const server = createServer((socket) => {
socket.on('data', (data) => {
});
socket.on('end', () => {
});
socket.on('connect', () => {
})
});
server.listen(this.port, this.host);
}
}

View File

@@ -1,4 +0,0 @@
# NewWebui
基于Vue3实现的现代化轻量化NapCat管理面板
## 进度
画饼

View File

@@ -8,19 +8,19 @@ import { NapCatOneBot11Adapter } from '@/onebot';
abstract class BaseAction<PayloadType, ReturnDataType> {
actionName: ActionName = ActionName.Unknown;
CoreContext: NapCatCore;
core: NapCatCore;
private validate: undefined | ValidateFunction<any> = undefined;
PayloadSchema: any = undefined;
OneBotContext: NapCatOneBot11Adapter;
payloadSchema: any = undefined;
obContext: NapCatOneBot11Adapter;
constructor(onebotContext: NapCatOneBot11Adapter, coreContext: NapCatCore) {
this.OneBotContext = onebotContext;
this.CoreContext = coreContext;
constructor(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
this.obContext = obContext;
this.core = core;
}
protected async check(payload: PayloadType): Promise<BaseCheckResult> {
if (this.PayloadSchema) {
this.validate = new Ajv({ allowUnionTypes: true }).compile(this.PayloadSchema);
if (this.payloadSchema) {
this.validate = new Ajv({ allowUnionTypes: true }).compile(this.payloadSchema);
}
if (this.validate && !this.validate(payload)) {
const errors = this.validate.errors as ErrorObject[];
@@ -46,7 +46,7 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
const resData = await this._handle(payload);
return OB11Response.ok(resData);
} catch (e: any) {
this.CoreContext.context.logger.logError('发生错误', e);
this.core.context.logger.logError('发生错误', e);
return OB11Response.error(e?.toString() || e?.stack?.toString() || '未知错误,可能操作超时', 200);
}
}
@@ -60,7 +60,7 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
const resData = await this._handle(payload);
return OB11Response.ok(resData, echo);
} catch (e: any) {
this.CoreContext.context.logger.logError('发生错误', e);
this.core.context.logger.logError('发生错误', e);
return OB11Response.error(e.stack?.toString() || e.toString(), 1200, echo);
}
}

View File

@@ -1,6 +1,6 @@
import { OB11Return } from '../types';
import { isNull } from '../../common/utils/helper';
import { isNull } from '../../common/helper';
export class OB11Response {
static res<T>(data: T, status: string, retcode: number, message: string = ''): OB11Return<T> {

View File

@@ -15,13 +15,13 @@ type Payload = FromSchema<typeof SchemaData>;
export class CreateCollection extends BaseAction<Payload, any> {
actionName = ActionName.CreateCollection;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
return await this.CoreContext.apis.CollectionApi.createCollection(
this.CoreContext.selfInfo.uin,
this.CoreContext.selfInfo.uid,
this.CoreContext.selfInfo.nick,
return await this.core.apis.CollectionApi.createCollection(
this.core.selfInfo.uin,
this.core.selfInfo.uid,
this.core.selfInfo.nick,
payload.brief, payload.rawData,
);
}

View File

@@ -13,11 +13,11 @@ type Payload = FromSchema<typeof SchemaData>;
export class FetchCustomFace extends BaseAction<Payload, string[]> {
actionName = ActionName.FetchCustomFace;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
//48 可能正好是QQ需要的一个页面的数量 Tagged Mlikiowa
const ret = await this.CoreContext.apis.MsgApi.fetchFavEmojiList(+(payload.count ?? 48));
const ret = await this.core.apis.MsgApi.fetchFavEmojiList(+(payload.count ?? 48));
return ret.emojiInfoList.map(e => e.url);
}
}

View File

@@ -2,7 +2,7 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { MessageUnique } from '@/common/message-unique';
const SchemaData = {
type: 'object',
@@ -21,9 +21,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class FetchEmojiLike extends BaseAction<Payload, any> {
actionName = ActionName.FetchEmojiLike;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const NTQQMsgApi = this.core.apis.MsgApi;
const msgIdPeer = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString()));
if (!msgIdPeer) throw new Error('消息不存在');
const msg = (await NTQQMsgApi.getMsgsByMsgId(msgIdPeer.Peer, [msgIdPeer.MsgId])).msgList[0];

View File

@@ -15,9 +15,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class GetCollectionList extends BaseAction<Payload, any> {
actionName = ActionName.GetCollectionList;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQCollectionApi = this.CoreContext.apis.CollectionApi;
const NTQQCollectionApi = this.core.apis.CollectionApi;
return await NTQQCollectionApi.getAllCollection(parseInt(payload.category.toString()), +(payload.count ?? 1));
}
}

View File

@@ -1,13 +1,14 @@
import { OB11Constructor } from '@/onebot/helper/converter';
import { OB11Entities } from '@/onebot/helper/entities';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
export class GetFriendWithCategory extends BaseAction<void, any> {
actionName = ActionName.GetFriendsWithCategory;
async _handle(payload: void) {
return (await this.CoreContext.apis.FriendApi.getBuddyV2ExWithCate(true)).map(category => ({
return (await this.core.apis.FriendApi.getBuddyV2ExWithCate(true)).map(category => ({
...category,
buddyList: OB11Constructor.friendsV2(category.buddyList),
buddyList: OB11Entities.friendsV2(category.buddyList),
}));
}
}

View File

@@ -11,7 +11,7 @@ export default class GetGroupAddRequest extends BaseAction<null, OB11GroupReques
actionName = ActionName.GetGroupIgnoreAddRequest;
async _handle(payload: null): Promise<OB11GroupRequestNotify[] | null> {
const data = await this.CoreContext.apis.GroupApi.getGroupIgnoreNotifies();
const data = await this.core.apis.GroupApi.getGroupIgnoreNotifies();
// log(data);
// const notifies: GroupNotify[] = data.notifies.filter(notify => notify.status === GroupNotifyStatus.WAIT_HANDLE);
// const returnData: OB11GroupRequestNotify[] = [];

View File

@@ -5,8 +5,8 @@ export class GetProfileLike extends BaseAction<void, any> {
actionName = ActionName.GetProfileLike;
async _handle(payload: void) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const ret = await NTQQUserApi.getProfileLike(this.CoreContext.selfInfo.uid);
const NTQQUserApi = this.core.apis.UserApi;
const ret = await NTQQUserApi.getProfileLike(this.core.selfInfo.uid);
const listdata: any[] = ret.info.userLikeInfos[0].favoriteInfo.userInfos;
for (let i = 0; i < listdata.length; i++) {
listdata[i].uin = parseInt((await NTQQUserApi.getUinByUidV2(listdata[i].uid)) || '');

View File

@@ -5,7 +5,7 @@ export class GetRobotUinRange extends BaseAction<void, Array<any>> {
actionName = ActionName.GetRobotUinRange;
async _handle(payload: void) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQUserApi = this.core.apis.UserApi;
return await NTQQUserApi.getRobotUinRange();
}
}

View File

@@ -1,8 +1,9 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { checkFileReceived, uri2local } from '@/common/utils/file';
import { checkFileReceived, uri2local } from '@/common/file';
import fs from 'fs';
const SchemaData = {
type: 'object',
properties: {
@@ -15,11 +16,11 @@ type Payload = FromSchema<typeof SchemaData>;
export class OCRImage extends BaseAction<Payload, any> {
actionName = ActionName.OCRImage;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQSystemApi = this.CoreContext.apis.SystemApi;
const { path, isLocal, errMsg, success } = (await uri2local(this.CoreContext.NapCatTempPath, payload.image));
const NTQQSystemApi = this.core.apis.SystemApi;
const { path, isLocal, success } = (await uri2local(this.core.NapCatTempPath, payload.image));
if (!success) {
throw `OCR ${payload.image}失败,image字段可能格式不正确`;
}

View File

@@ -8,7 +8,7 @@ const SchemaData = {
properties: {
eventType: { type: 'string' },
group_id: { type: 'string' },
user_id: { type: 'string' }
user_id: { type: 'string' },
},
required: ['eventType'],
} as const satisfies JSONSchema;
@@ -19,26 +19,25 @@ export class SetInputStatus extends BaseAction<Payload, any> {
actionName = ActionName.SetInputStatus;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const NTQQUserApi = this.core.apis.UserApi;
const NTQQMsgApi = this.core.apis.MsgApi;
let peer: Peer;
if (payload.group_id) {
peer = {
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id
peerUid: payload.group_id,
};
} else if (payload.user_id) {
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id);
if (!uid) throw new Error('uid is empty');
peer = {
chatType: ChatType.KCHATTYPEC2C,
peerUid: uid
peerUid: uid,
};
} else {
throw new Error('请指定 group_id 或 user_id');
}
const ret = await NTQQMsgApi.sendShowInputStatusReq(peer, parseInt(payload.eventType));
return ret;
return await NTQQMsgApi.sendShowInputStatusReq(peer, parseInt(payload.eventType));
}
}

View File

@@ -14,10 +14,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class SetLongNick extends BaseAction<Payload, any> {
actionName = ActionName.SetLongNick;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQUserApi = this.core.apis.UserApi;
const ret = await NTQQUserApi.setLongNick(payload.longNick);
return ret;
}

View File

@@ -17,14 +17,14 @@ type Payload = FromSchema<typeof SchemaData>;
export class SetOnlineStatus extends BaseAction<Payload, null> {
actionName = ActionName.SetOnlineStatus;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQUserApi = this.core.apis.UserApi;
const ret = await NTQQUserApi.setSelfOnlineStatus(
parseInt(payload.status.toString()),
parseInt(payload.extStatus.toString()),
parseInt(payload.batteryStatus.toString())
parseInt(payload.batteryStatus.toString()),
);
if (ret.result !== 0) {
throw new Error('设置在线状态失败');

View File

@@ -1,7 +1,7 @@
import BaseAction from '../BaseAction';
import { ActionName, BaseCheckResult } from '../types';
import * as fs from 'node:fs';
import { checkFileReceived, uri2local } from '@/common/utils/file';
import { checkFileReceived, uri2local } from '@/common/file';
interface Payload {
file: string;
@@ -24,8 +24,8 @@ export default class SetAvatar extends BaseAction<Payload, null> {
}
async _handle(payload: Payload): Promise<null> {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const { path, isLocal, errMsg, success } = (await uri2local(this.CoreContext.NapCatTempPath, payload.file));
const NTQQUserApi = this.core.apis.UserApi;
const { path, isLocal, errMsg, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
if (!success) {
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
}

View File

@@ -16,11 +16,11 @@ type Payload = FromSchema<typeof SchemaData>;
export class SharePeer extends BaseAction<Payload, any> {
actionName = ActionName.SharePeer;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQUserApi = this.core.apis.UserApi;
const NTQQGroupApi = this.core.apis.GroupApi;
if (payload.group_id) {
return await NTQQGroupApi.getGroupRecommendContactArkJson(payload.group_id);
} else if (payload.user_id) {
@@ -41,10 +41,10 @@ type PayloadGroupEx = FromSchema<typeof SchemaDataGroupEx>;
export class ShareGroupEx extends BaseAction<PayloadGroupEx, any> {
actionName = ActionName.ShareGroupEx;
PayloadSchema = SchemaDataGroupEx;
payloadSchema = SchemaDataGroupEx;
async _handle(payload: PayloadGroupEx) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQGroupApi = this.core.apis.GroupApi;
return await NTQQGroupApi.getArkJsonGroupShare(payload.group_id);
}
}

View File

@@ -17,10 +17,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class TranslateEnWordToZn extends BaseAction<Payload, Array<any> | null> {
actionName = ActionName.TranslateEnWordToZn;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQSystemApi = this.CoreContext.apis.SystemApi;
const NTQQSystemApi = this.core.apis.SystemApi;
const ret = await NTQQSystemApi.translateEnWordToZn(payload.words);
if (ret.result !== 0) {
throw new Error('翻译失败');

View File

@@ -15,10 +15,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class DelGroupFile extends BaseAction<Payload, any> {
actionName = ActionName.DelGroupFile;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQGroupApi = this.core.apis.GroupApi;
return await NTQQGroupApi.DelGroupFile(payload.group_id.toString(), [payload.file_id]);
}
}

View File

@@ -15,10 +15,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class DelGroupFileFolder extends BaseAction<Payload, any> {
actionName = ActionName.DelGroupFileFolder;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQGroupApi = this.core.apis.GroupApi;
return (await NTQQGroupApi.DelGroupFileFolder(payload.group_id.toString(), payload.folder_id)).groupFileCommonResult;
}
}

View File

@@ -1,8 +1,8 @@
import BaseAction from '../BaseAction';
import fs from 'fs/promises';
import { UUIDConverter } from '@/common/utils/helper';
import { UUIDConverter } from '@/common/helper';
import { ActionName } from '../types';
import { ChatType, ElementType, FileElement, Peer, RawMessage, VideoElement } from '@/core/entities';
import { ChatType, ElementType, Peer, RawMessage } from '@/core/entities';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
export interface GetFilePayload {
@@ -26,71 +26,65 @@ const GetFileBase_PayloadSchema = {
} as const satisfies JSONSchema;
export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
PayloadSchema: any = GetFileBase_PayloadSchema;
payloadSchema: any = GetFileBase_PayloadSchema;
async _handle(payload: GetFilePayload): Promise<GetFileResponse> {
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQFileApi = this.CoreContext.apis.FileApi;
let UuidData: {
high: string;
low: string;
} | undefined;
const NTQQFriendApi = this.core.apis.FriendApi;
const NTQQUserApi = this.core.apis.UserApi;
const NTQQMsgApi = this.core.apis.MsgApi;
const NTQQGroupApi = this.core.apis.GroupApi;
const NTQQFileApi = this.core.apis.FileApi;
try {
UuidData = UUIDConverter.decode(payload.file);
if (UuidData) {
const peerUin = UuidData.high;
const msgId = UuidData.low;
const isGroup: boolean = !!(await NTQQGroupApi.getGroups(false)).find(e => e.groupCode == peerUin);
let peer: Peer | undefined;
//识别Peer
if (isGroup) {
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: peerUin };
}
const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin);
if (PeerUid) {
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
if (isBuddy) {
peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: PeerUid };
} else {
peer = { chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: PeerUid };
}
}
if (!peer) {
throw new Error('chattype not support');
}
const msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]);
if (msgList.msgList.length == 0) {
throw new Error('msg not found');
}
const msg = msgList.msgList[0];
const findEle = msg.elements.find(e => e.elementType == ElementType.VIDEO || e.elementType == ElementType.FILE || e.elementType == ElementType.PTT);
if (!findEle) {
throw new Error('element not found');
}
const downloadPath = await NTQQFileApi.downloadMedia(msgId, msg.chatType, msg.peerUid, findEle.elementId, '', '');
const fileSize = findEle?.videoElement?.fileSize || findEle?.fileElement?.fileSize || findEle?.pttElement?.fileSize || '0';
const fileName = findEle?.videoElement?.fileName || findEle?.fileElement?.fileName || findEle?.pttElement?.fileName || '';
const res: GetFileResponse = {
file: downloadPath,
url: downloadPath,
file_size: fileSize,
file_name: fileName,
};
if (true/*enableLocalFile2Url*/ && downloadPath) {
try {
res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) {
throw new Error('文件下载失败. ' + e);
}
}
//不手动删除?文件持久化了
return res;
const uuidData = UUIDConverter.decode(payload.file);
const peerUin = uuidData.high;
const msgId = uuidData.low;
const isGroup: boolean = !!(await NTQQGroupApi.getGroups(false)).find(e => e.groupCode == peerUin);
let peer: Peer | undefined;
//识别Peer
if (isGroup) {
peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: peerUin };
}
const PeerUid = await NTQQUserApi.getUidByUinV2(peerUin);
if (PeerUid) {
const isBuddy = await NTQQFriendApi.isBuddy(PeerUid);
if (isBuddy) {
peer = { chatType: ChatType.KCHATTYPEC2C, peerUid: PeerUid };
} else {
peer = { chatType: ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid: PeerUid };
}
}
if (!peer) {
throw new Error('chattype not support');
}
const msgList = await NTQQMsgApi.getMsgsByMsgId(peer, [msgId]);
if (msgList.msgList.length == 0) {
throw new Error('msg not found');
}
const msg = msgList.msgList[0];
const findEle = msg.elements.find(e => e.elementType == ElementType.VIDEO || e.elementType == ElementType.FILE || e.elementType == ElementType.PTT);
if (!findEle) {
throw new Error('element not found');
}
const downloadPath = await NTQQFileApi.downloadMedia(msgId, msg.chatType, msg.peerUid, findEle.elementId, '', '');
const fileSize = findEle?.videoElement?.fileSize || findEle?.fileElement?.fileSize || findEle?.pttElement?.fileSize || '0';
const fileName = findEle?.videoElement?.fileName || findEle?.fileElement?.fileName || findEle?.pttElement?.fileName || '';
const res: GetFileResponse = {
file: downloadPath,
url: downloadPath,
file_size: fileSize,
file_name: fileName,
};
if (/* enableLocalFile2Url && */ downloadPath) {
try {
res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) {
throw new Error('文件下载失败. ' + e);
}
}
//不手动删除?文件持久化了
return res;
} catch {
this.CoreContext.context.logger.logDebug('GetFileBase Mode - 1 Error');
this.core.context.logger.logDebug('GetFileBase Mode - 1 Error');
}
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems;
@@ -119,7 +113,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
file_size: NTSearchNameResult[0].fileSize.toString(),
file_name: NTSearchNameResult[0].fileName,
};
if (true/*enableLocalFile2Url*/ && downloadPath) {
if (/* enableLocalFile2Url && */ downloadPath) {
try {
res.base64 = await fs.readFile(downloadPath, 'base64');
} catch (e) {
@@ -130,66 +124,6 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
return res;
}
throw new Error('file not found');
// let cache = await dbUtil.getFileCacheByName(payload.file);
// if (!cache) {
// cache = await dbUtil.getFileCacheByUuid(payload.file);
// }
// if (!cache) {
// throw new Error('file not found');
// }
// const { enableLocalFile2Url } = ob11Config;
// try {
// await fs.access(cache.path, fs.constants.F_OK);
// } catch (e) {
// logDebug('local file not found, start download...');
// // if (cache.url) {
// // const downloadResult = await uri2local(cache.url);
// // if (downloadResult.success) {
// // cache.path = downloadResult.path;
// // dbUtil.updateFileCache(cache).then();
// // } else {
// // throw new Error('file download failed. ' + downloadResult.errMsg);
// // }
// // } else {
// // // 没有url的可能是私聊文件或者群文件需要自己下载
// // log('需要调用 NTQQ 下载文件api');
// let peer = MessageUnique.getPeerByMsgId(cache.msgId);
// let msg = await NTQQMsgApi.getMsgsByMsgId(peer?.Peer!,cache.msgId);
// // log('文件 msg', msg);
// if (msg) {
// // 构建下载函数
// const downloadPath = await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid,
// cache.elementId, '', '');
// // await sleep(1000);
// // log('download result', downloadPath);
// let peer = MessageUnique.getPeerByMsgId(cache.msgId);
// msg = await NTQQMsgApi.getMsgsByMsgId(peer?.Peer!,cache.msgId);
// // log('下载完成后的msg', msg);
// cache.path = downloadPath!;
// dbUtil.updateFileCache(cache).then();
// // log('下载完成后的msg', msg);
// // }
// }
// }
// // log('file found', cache);
// const res: GetFileResponse = {
// file: cache.path,
// url: cache.url,
// file_size: cache.size.toString(),
// file_name: cache.name
// };
// if (enableLocalFile2Url) {
// if (!cache.url) {
// try {
// res.base64 = await fs.readFile(cache.path, 'base64');
// } catch (e) {
// throw new Error('文件下载失败. ' + e);
// }
// }
// }
//return res;
}
}
@@ -210,7 +144,7 @@ interface GetFile_Payload extends GetFile_Payload_Internal {
export default class GetFile extends GetFileBase {
actionName = ActionName.GetFile;
PayloadSchema = GetFile_PayloadSchema;
payloadSchema = GetFile_PayloadSchema;
async _handle(payload: GetFile_Payload): Promise<GetFileResponse> {
payload.file = payload.file_id;

View File

@@ -14,10 +14,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class GetGroupFileCount extends BaseAction<Payload, { count: number }> {
actionName = ActionName.GetGroupFileCount;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQGroupApi = this.core.apis.GroupApi;
const ret = await NTQQGroupApi.GetGroupFileCount([payload.group_id?.toString()]);
return { count: ret.groupFileCounts[0] };
}

View File

@@ -16,10 +16,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class GetGroupFileList extends BaseAction<Payload, { FileList: Array<any> }> {
actionName = ActionName.GetGroupFileList;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const NTQQMsgApi = this.core.apis.MsgApi;
const ret = await NTQQMsgApi.getGroupFileList(payload.group_id.toString(), {
sortType: 1,
fileCount: payload.file_count,

View File

@@ -15,10 +15,10 @@ type Payload = FromSchema<typeof SchemaData>;
export class SetGroupFileFolder extends BaseAction<Payload, any> {
actionName = ActionName.SetGroupFileFolder;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQGroupApi = this.core.apis.GroupApi;
return (await NTQQGroupApi.CreatGroupFileFolder(payload.group_id.toString(), payload.folder_name)).resultWithGroupItem;
}
}

View File

@@ -2,7 +2,7 @@ import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import fs from 'fs';
import { join as joinPath } from 'node:path';
import { calculateFileMD5, httpDownload } from '@/common/utils/file';
import { calculateFileMD5, httpDownload } from '@/common/file';
import { randomUUID } from 'crypto';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -30,12 +30,12 @@ type Payload = FromSchema<typeof SchemaData>;
export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileResponse> {
actionName = ActionName.GoCQHTTP_DownloadFile;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<FileResponse> {
const isRandomName = !payload.name;
const name = payload.name || randomUUID();
const filePath = joinPath(this.CoreContext.NapCatTempPath, name);
const filePath = joinPath(this.core.NapCatTempPath, name);
if (payload.base64) {
fs.writeFileSync(filePath, payload.base64, 'base64');
@@ -51,7 +51,7 @@ export default class GoCQHTTPDownloadFile extends BaseAction<Payload, FileRespon
if (isRandomName) {
// 默认实现要名称未填写时文件名为文件 md5
const md5 = await calculateFileMD5(filePath);
const newPath = joinPath(this.CoreContext.NapCatTempPath, md5);
const newPath = joinPath(this.core.NapCatTempPath, md5);
fs.renameSync(filePath, newPath);
return { file: newPath };
}

View File

@@ -1,8 +1,8 @@
import BaseAction from '../BaseAction';
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '@/onebot';
import { OB11ForwardMessage } from '@/onebot';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { MessageUnique } from '@/common/message-unique';
const SchemaData = {
type: 'object',
@@ -14,16 +14,12 @@ const SchemaData = {
type Payload = FromSchema<typeof SchemaData>;
interface Response {
messages: (OB11Message & { content: OB11MessageData })[];
}
export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
actionName = ActionName.GoCQHTTP_GetForwardMsg;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<any> {
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const NTQQMsgApi = this.core.apis.MsgApi;
const msgId = payload.message_id || payload.id;
if (!msgId) {
throw Error('message_id is required');
@@ -39,7 +35,7 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
}
const msgList = data.msgList;
const messages = (await Promise.all(msgList.map(async msg => {
const resMsg = await this.OneBotContext.apiContext.MsgApi
const resMsg = await this.obContext.apis.MsgApi
.parseMessage(msg);
if (!resMsg) return;
resMsg.message_id = MessageUnique.createMsg({

View File

@@ -1,9 +1,9 @@
import BaseAction from '../BaseAction';
import { OB11Message } from '@/onebot';
import { ActionName } from '../types';
import { ChatType, RawMessage } from '@/core/entities';
import { ChatType } from '@/core/entities';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { MessageUnique } from '@/common/message-unique';
interface Response {
messages: OB11Message[];
@@ -13,9 +13,9 @@ const SchemaData = {
type: 'object',
properties: {
user_id: { type: ['number', 'string'] },
message_seq: { type: 'number' },
message_seq: { type: ['number', 'string'] },
count: { type: ['number', 'string'] },
reverseOrder: { type: 'boolean' },
reverseOrder: { type: ['boolean', 'string'] },
},
required: ['user_id'],
} as const satisfies JSONSchema;
@@ -24,36 +24,34 @@ type Payload = FromSchema<typeof SchemaData>;
export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
actionName = ActionName.GetFriendMsgHistory;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<Response> {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
const NTQQUserApi = this.core.apis.UserApi;
const NTQQMsgApi = this.core.apis.MsgApi;
const NTQQFriendApi = this.core.apis.FriendApi;
//处理参数
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const MsgCount = +(payload.count ?? 20);
const isReverseOrder = payload.reverseOrder || true;
const isReverseOrder = typeof payload.reverseOrder === 'string' ? payload.reverseOrder === 'true' : !!payload.reverseOrder;
if (!uid) throw `记录${payload.user_id}不存在`;
const friend = await NTQQFriendApi.isBuddy(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');
//拉取消息
let msgList: RawMessage[];
if (!payload.message_seq || payload.message_seq == 0) {
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, MsgCount)).msgList;
} else {
const startMsgId = MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq)?.MsgId;
if (!startMsgId) throw `消息${payload.message_seq}不存在`;
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList;
}
const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0';
const msgList = hasMessageSeq ?
(await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList : (await NTQQMsgApi.getAioFirstViewLatestMsgs(peer, MsgCount)).msgList;
if (msgList.length === 0) throw `消息${payload.message_seq}不存在`;
//翻转消息
if (isReverseOrder) msgList.reverse();
//转换序号
await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
}));
//转换消息
//烘焙消息
const ob11MsgList = (await Promise.all(
msgList.map(msg => this.OneBotContext.apiContext.MsgApi.parseMessage(msg)))
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList };
}

View File

@@ -16,13 +16,13 @@ type Payload = FromSchema<typeof SchemaData>;
export class GetGroupHonorInfo extends BaseAction<Payload, Array<any>> {
actionName = ActionName.GetGroupHonorInfo;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
if (!payload.type) {
payload.type = WebHonorType.ALL;
}
const NTQQWebApi = this.CoreContext.apis.WebApi;
const NTQQWebApi = this.core.apis.WebApi;
return await NTQQWebApi.getGroupHonorInfo(payload.group_id.toString(), payload.type);
}
}

View File

@@ -1,9 +1,9 @@
import BaseAction from '../BaseAction';
import { OB11Message } from '@/onebot';
import { ActionName } from '../types';
import { ChatType, Peer, RawMessage } from '@/core/entities';
import { ChatType, Peer } from '@/core/entities';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { MessageUnique } from '@/common/message-unique';
interface Response {
messages: OB11Message[];
@@ -13,9 +13,9 @@ const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['number', 'string'] },
message_seq: { type: 'number' },
message_seq: { type: ['number', 'string'] },
count: { type: ['number', 'string'] },
reverseOrder: { type: 'boolean' },
reverseOrder: { type: ['boolean', 'string'] },
},
required: ['group_id'],
} as const satisfies JSONSchema;
@@ -24,31 +24,29 @@ type Payload = FromSchema<typeof SchemaData>;
export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Response> {
actionName = ActionName.GoCQHTTP_GetGroupMsgHistory;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<Response> {
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const NTQQMsgApi = this.core.apis.MsgApi;
//处理参数
const isReverseOrder = payload.reverseOrder || true;
const isReverseOrder = typeof payload.reverseOrder === 'string' ? payload.reverseOrder === 'true' : !!payload.reverseOrder;
const MsgCount = +(payload.count ?? 20);
const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
const hasMessageSeq = !payload.message_seq ? !!payload.message_seq : !(payload.message_seq?.toString() === '' || payload.message_seq?.toString() === '0');
//拉取消息
let msgList: RawMessage[];
if (!payload.message_seq || payload.message_seq == 0) {
msgList = (await NTQQMsgApi.getLastestMsgByUids(peer, MsgCount)).msgList;
} else {
const startMsgId = MessageUnique.getMsgIdAndPeerByShortId(payload.message_seq)?.MsgId;
if (!startMsgId) throw `消息${payload.message_seq}不存在`;
msgList = (await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList;
}
const startMsgId = hasMessageSeq ? (MessageUnique.getMsgIdAndPeerByShortId(+payload.message_seq!)?.MsgId ?? payload.message_seq!.toString()) : '0';
const msgList = hasMessageSeq ?
(await NTQQMsgApi.getMsgHistory(peer, startMsgId, MsgCount)).msgList : (await NTQQMsgApi.getAioFirstViewLatestMsgs(peer, MsgCount)).msgList;
if (msgList.length === 0) throw `消息${payload.message_seq}不存在`;
//翻转消息
if (isReverseOrder) msgList.reverse();
//转换序号
await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
}));
//转换消息
//烘焙消息
const ob11MsgList = (await Promise.all(
msgList.map(msg => this.OneBotContext.apiContext.MsgApi.parseMessage(msg)))
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList };
}

View File

@@ -1,7 +1,7 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { JSONSchema } from 'json-schema-to-ts';
import { sleep } from '@/common/utils/helper';
import { sleep } from '@/common/helper';
const SchemaData = {
type: 'object',
@@ -15,7 +15,7 @@ export class GetOnlineClient extends BaseAction<void, Array<any>> {
async _handle(payload: void) {
//注册监听
const NTQQSystemApi = this.CoreContext.apis.SystemApi;
const NTQQSystemApi = this.core.apis.SystemApi;
NTQQSystemApi.getOnlineDev();
await sleep(500);

View File

@@ -1,9 +1,9 @@
import BaseAction from '../BaseAction';
import { OB11User, OB11UserSex } from '../../types';
import { OB11Constructor } from '@/onebot/helper/converter';
import { OB11User, OB11UserSex } from '@/onebot';
import { OB11Entities } from '@/onebot/helper/entities';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { calcQQLevel } from '@/common/utils/helper';
import { calcQQLevel } from '@/common/helper';
const SchemaData = {
type: 'object',
@@ -19,12 +19,12 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
actionName = ActionName.GoCQHTTP_GetStrangerInfo;
async _handle(payload: Payload): Promise<OB11User> {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQUserApi = this.core.apis.UserApi;
const user_id = payload.user_id.toString();
const extendData = await NTQQUserApi.getUserDetailInfoByUinV2(user_id);
const uid = (await NTQQUserApi.getUidByUinV2(user_id))!;
if (!uid || uid.indexOf('*') != -1) {
const ret = {
return {
...extendData.detail.simpleInfo.coreInfo,
...extendData.detail.commonExt,
...extendData.detail.simpleInfo.baseInfo,
@@ -36,11 +36,9 @@ export default class GoCQHTTPGetStrangerInfo extends BaseAction<Payload, OB11Use
qid: extendData.detail.simpleInfo.baseInfo.qid,
level: calcQQLevel(extendData.detail.commonExt?.qqLevel ?? 0) || 0,
login_days: 0,
uid: ''
};
return ret;
}
const data = { ...extendData, ...(await NTQQUserApi.getUserDetailInfo(uid)) };
return OB11Constructor.stranger(data);
return OB11Entities.stranger(data);
}
}

View File

@@ -1,4 +1,3 @@
import { handleQuickOperation } from '@/onebot/helper/quick';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { QuickAction, QuickActionEvent } from '@/onebot/types';
@@ -12,7 +11,9 @@ export class GoCQHTTPHandleQuickAction extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_HandleQuickAction;
async _handle(payload: Payload): Promise<null> {
handleQuickOperation(this.CoreContext, this.OneBotContext, payload.context, payload.operation).then().catch(this.CoreContext.context.logger.logError);
this.obContext.apis.QuickActionApi
.handleQuickOperation(payload.context, payload.operation)
.catch(this.core.context.logger.logError.bind(this.core.context.logger));
return null;
}
}

View File

@@ -1,4 +1,4 @@
import { checkFileReceived, uri2local } from '@/common/utils/file';
import { checkFileReceived, uri2local } from '@/common/file';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { unlink } from 'node:fs';
@@ -10,8 +10,8 @@ const SchemaData = {
group_id: { type: ['number', 'string'] },
content: { type: 'string' },
image: { type: 'string' },
pinned: { type: 'number' },
confirmRequired: { type: 'number' },
pinned: { type: ['number', 'string'] },
confirm_required: { type: ['number', 'string'] },
},
required: ['group_id', 'content'],
} as const satisfies JSONSchema;
@@ -22,16 +22,15 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_SendGroupNotice;
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQGroupApi = this.core.apis.GroupApi;
let UploadImage: { id: string, width: number, height: number } | undefined = undefined;
if (payload.image) {
//公告图逻辑
const {
errMsg,
path,
isLocal,
success,
} = (await uri2local(this.CoreContext.NapCatTempPath, payload.image));
} = (await uri2local(this.core.NapCatTempPath, payload.image));
if (!success) {
throw `群公告${payload.image}设置失败,image字段可能格式不正确`;
}
@@ -49,18 +48,12 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
}
UploadImage = ImageUploadResult.picInfo;
}
let Notice_Pinned = 0;
let Notice_confirmRequired = 0;
if (!payload.pinned) {
Notice_Pinned = 0;
}
if (!payload.confirmRequired) {
Notice_confirmRequired = 0;
}
const PublishGroupBulletinResult = await NTQQGroupApi.publishGroupBulletin(payload.group_id.toString(), payload.content, UploadImage, Notice_Pinned, Notice_confirmRequired);
const noticePinned = +(payload.pinned ?? 0);
const noticeConfirmRequired = +(payload.confirm_required ?? 0);
const publishGroupBulletinResult = await NTQQGroupApi.publishGroupBulletin(payload.group_id.toString(), payload.content, UploadImage, noticePinned, noticeConfirmRequired);
if (PublishGroupBulletinResult.result != 0) {
throw `设置群公告失败,错误信息:${PublishGroupBulletinResult.errMsg}`;
if (publishGroupBulletinResult.result != 0) {
throw `设置群公告失败,错误信息:${publishGroupBulletinResult.errMsg}`;
}
return null;
}

View File

@@ -1,9 +1,7 @@
import BaseAction from '../BaseAction';
import { ActionName, BaseCheckResult } from '../types';
import * as fs from 'node:fs';
import { checkFileReceived, uri2local } from '@/common/utils/file';
// import { log } from "../../../common/utils";
import { checkFileReceived, uri2local } from '@/common/file';
interface Payload {
file: string,
@@ -27,8 +25,8 @@ export default class SetGroupPortrait extends BaseAction<Payload, any> {
}
async _handle(payload: Payload): Promise<any> {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const { path, isLocal, errMsg, success } = (await uri2local(this.CoreContext.NapCatTempPath, payload.file));
const NTQQGroupApi = this.core.apis.GroupApi;
const { path, isLocal, errMsg, success } = (await uri2local(this.core.NapCatTempPath, payload.file));
if (!success) {
throw `头像${payload.file}设置失败,file字段可能格式不正确`;
}
@@ -42,7 +40,6 @@ export default class SetGroupPortrait extends BaseAction<Payload, any> {
if (!ret) {
throw `头像${payload.file}设置失败,api无返回`;
}
// log(`头像设置返回:${JSON.stringify(ret)}`)
if (ret['result'] == 1004022) {
throw `头像${payload.file}设置失败,文件可能不是图片格式或权限不足`;
} else if (ret['result'] != 0) {
@@ -56,6 +53,5 @@ export default class SetGroupPortrait extends BaseAction<Payload, any> {
}
throw `头像${payload.file}设置失败,无法获取头像,文件可能不存在`;
}
return null;
}
}

View File

@@ -16,19 +16,22 @@ type Payload = FromSchema<typeof SchemaData>;
export class SetQQProfile extends BaseAction<Payload, any | null> {
actionName = ActionName.SetQQProfile;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const self = this.CoreContext.selfInfo;
const NTQQUserApi = this.core.apis.UserApi;
const self = this.core.selfInfo;
const OldProfile = await NTQQUserApi.getUserDetailInfo(self.uid);
const ret = await NTQQUserApi.modifySelfProfile({
return await NTQQUserApi.modifySelfProfile({
nick: payload.nickname,
longNick: (payload?.personal_note ?? OldProfile?.longNick) || '',
sex: parseInt(payload?.sex ? payload?.sex.toString() : OldProfile?.sex!.toString()),
birthday: { birthday_year: OldProfile?.birthday_year!.toString(), birthday_month: OldProfile?.birthday_month!.toString(), birthday_day: OldProfile?.birthday_day!.toString() },
birthday: {
birthday_year: OldProfile?.birthday_year!.toString(),
birthday_month: OldProfile?.birthday_month!.toString(),
birthday_day: OldProfile?.birthday_day!.toString(),
},
location: undefined,
});
return ret;
}
}

View File

@@ -2,8 +2,7 @@ import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { ChatType } from '@/core/entities';
import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file';
import { uri2local } from '@/common/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
@@ -22,19 +21,19 @@ type Payload = FromSchema<typeof SchemaData>;
export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
actionName = ActionName.GoCQHTTP_UploadGroupFile;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> {
let file = payload.file;
if (fs.existsSync(file)) {
file = `file://${file}`;
}
const downloadResult = await uri2local(this.CoreContext.NapCatTempPath, file);
const downloadResult = await uri2local(this.core.NapCatTempPath, file);
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}
const sendFileEle = await this.CoreContext.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name, payload.folder_id);
await sendMsg(this.CoreContext, {
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name, payload.folder_id);
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId({
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),
}, [sendFileEle], [], true);

View File

@@ -2,8 +2,7 @@ import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { ChatType, Peer, SendFileElement } from '@/core/entities';
import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file';
import { uri2local } from '@/common/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
@@ -20,11 +19,11 @@ type Payload = FromSchema<typeof SchemaData>;
export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null> {
actionName = ActionName.GOCQHTTP_UploadPrivateFile;
PayloadSchema = SchemaData;
payloadSchema = SchemaData;
async getPeer(payload: Payload): Promise<Peer> {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
const NTQQUserApi = this.core.apis.UserApi;
const NTQQFriendApi = this.core.apis.FriendApi;
if (payload.user_id) {
const peerUid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
if (!peerUid) {
@@ -33,21 +32,20 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
}
throw new Error( '缺少参数 user_id');
throw new Error('缺少参数 user_id');
}
async _handle(payload: Payload): Promise<null> {
const peer = await this.getPeer(payload);
let file = payload.file;
if (fs.existsSync(file)) {
file = `file://${file}`;
}
const downloadResult = await uri2local(this.CoreContext.NapCatTempPath, file);
const downloadResult = await uri2local(this.core.NapCatTempPath, file);
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}
const sendFileEle: SendFileElement = await this.CoreContext.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name);
await sendMsg(this.CoreContext, peer, [sendFileEle], [], true);
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name);
await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], [], true);
return null;
}
}

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