Compare commits

...

77 Commits

Author SHA1 Message Date
手瓜一十雪
fb4a4f50be release: v2.2.9 2024-08-26 01:52:09 +08:00
手瓜一十雪
6596e9cab6 style: 27333 类型跟进 2024-08-26 01:24:22 +08:00
手瓜一十雪
f1b137f2e1 style: 移除老旧代码 2024-08-26 01:07:01 +08:00
手瓜一十雪
535720d0fe style: lint 2024-08-26 01:00:07 +08:00
手瓜一十雪
f063cf4a16 style: 移除无用代码 2024-08-26 00:50:17 +08:00
手瓜一十雪
90bbdbf2fe style: fix 2024-08-26 00:46:58 +08:00
手瓜一十雪
5f1d8fb99d style: RegExec match->exec 2024-08-26 00:45:12 +08:00
手瓜一十雪
5486ffcdcc chore: 移除无用代码 2024-08-26 00:38:19 +08:00
手瓜一十雪
adfd123970 style: lint 2024-08-26 00:29:52 +08:00
手瓜一十雪
f1a364bfa2 style: lint 2024-08-26 00:21:49 +08:00
手瓜一十雪
9da714bf15 style: fix 2024-08-26 00:17:42 +08:00
手瓜一十雪
fc73295520 build: 2.2.9 2024-08-26 00:09:07 +08:00
手瓜一十雪
ab955e41fb style: 代码质量提高 2024-08-26 00:05:33 +08:00
手瓜一十雪
c64367335c Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-26 00:01:47 +08:00
手瓜一十雪
edc787eb3e style: lint 2024-08-26 00:01:44 +08:00
手瓜一十雪
f2c69fc68b style: remove 2024-08-25 23:59:34 +08:00
手瓜一十雪
d947fe743b Merge pull request #303 from NapNeko/dev/RefactoredMsgParsers
style: Error Object
2024-08-25 23:57:58 +08:00
手瓜一十雪
5b37ae9026 fix 2024-08-25 23:57:16 +08:00
手瓜一十雪
ec9e042b29 Merge pull request #301 from NapNeko/dev/RefactoredMsgParsers
refactor: make static functions dynamic
2024-08-25 23:50:44 +08:00
手瓜一十雪
337ac0eab9 fix: 过滤无效null类型 2024-08-25 23:50:34 +08:00
手瓜一十雪
f6a1b784c4 fix: type 2024-08-25 23:42:48 +08:00
手瓜一十雪
332fcecb78 Merge branch 'main' into dev/RefactoredMsgParsers 2024-08-25 23:16:49 +08:00
手瓜一十雪
18590be1e7 fix: 修正正确类型 2024-08-25 23:04:34 +08:00
手瓜一十雪
b76edcaf1d chore: merge main 2024-08-25 22:43:29 +08:00
手瓜一十雪
6024cabb69 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-08-25 22:36:53 +08:00
手瓜一十雪
08446e648e release: 2.2.8 2024-08-25 22:36:42 +08:00
Alen
14af7a3572 Merge pull request #302 from cnxysoft/upmain
fix: 多个问题
2024-08-25 22:23:58 +08:00
Alen
cdc4275f81 fix: 多个问题
修复group_increase事件上报
修复启动时加载群员信息失败
修复文件发送失败
2024-08-25 22:23:06 +08:00
手瓜一十雪
a9ade98315 style: remove unless 2024-08-25 22:12:38 +08:00
手瓜一十雪
f3ae6fa70f style: fix 2024-08-25 22:09:30 +08:00
手瓜一十雪
96457bbec3 Merge branch 'main' into dev/RefactoredMsgParsers 2024-08-25 22:00:20 +08:00
手瓜一十雪
8f465e376e style: type 2024-08-25 21:59:07 +08:00
手瓜一十雪
adc366a959 style: 清理不规范代码 2024-08-25 21:54:20 +08:00
手瓜一十雪
a20a6bc8bb Merge branch 'main' into dev/RefactoredMsgParsers 2024-08-25 21:49:28 +08:00
手瓜一十雪
b176fa66d4 style: 样式处理 2024-08-25 21:47:55 +08:00
手瓜一十雪
f81b1926fb Merge branch 'main' into dev/RefactoredMsgParsers 2024-08-25 21:37:56 +08:00
手瓜一十雪
7b7609a068 style: 标准化样式 2024-08-25 21:37:36 +08:00
Seijo Cecilia
670d4108e6 fix: typo 2024-08-25 20:46:20 +08:00
Seijo Cecilia
a5c7b88a40 fix: createSendElements reference 2024-08-25 20:46:03 +08:00
Seijo Cecilia
e52b2e6d69 fix: createSendElements reference 2024-08-25 20:00:14 +08:00
Seijo Cecilia
e4066fb8df refactor: completely remove genMessage.ts 2024-08-25 19:53:20 +08:00
Seijo Cecilia
f7a0fb22b4 refactor: make SendMsg a single file again 2024-08-25 19:45:14 +08:00
Seijo Cecilia
cad2ae723c refactor: normalize method name 2024-08-25 19:43:41 +08:00
Seijo Cecilia
889a8c6093 fix: sync fixes in handling forwarded nodes 2024-08-25 19:43:01 +08:00
Seijo Cecilia
573418914f Merge branch 'main' into dev/RefactoredMsgParsers 2024-08-25 19:40:40 +08:00
Seijo Cecilia
d7fb6f9c05 refactor: ob11 to raw message constructors 2024-08-25 19:38:35 +08:00
手瓜一十雪
136e27d655 fix: 消息组合 2024-08-25 19:38:19 +08:00
Seijo Cecilia
d5ff2d7099 fix: from || to ?? 2024-08-25 18:20:58 +08:00
Seijo Cecilia
2a7f8d0c99 refactor: raw message parsers 2024-08-25 17:54:50 +08:00
手瓜一十雪
e3ca5df713 Delete .github/workflows/codacy.yml 2024-08-25 12:52:31 +08:00
手瓜一十雪
bda32f3e8f chore:update 2024-08-25 12:08:36 +08:00
手瓜一十雪
a7c6e45a92 chore: codacy 2024-08-25 12:02:03 +08:00
手瓜一十雪
7c20ca9b64 style:lint 2024-08-25 11:45:50 +08:00
手瓜一十雪
a201461eff chore: lint 2024-08-25 11:18:11 +08:00
Seijo Cecilia
e5d9df37c5 Merge remote-tracking branch 'origin/main' 2024-08-25 10:14:42 +08:00
Seijo Cecilia
106fbaf086 refactor: make parseMessage an instance method 2024-08-25 10:14:11 +08:00
Alen
a0024c98d5 release: 2.2.7 2024-08-25 09:59:07 +08:00
Alen
684a702638 Merge pull request #300 from cnxysoft/upmain
fix: 多处修复
2024-08-25 09:47:07 +08:00
Alen
aec4a009d1 fix: 撤回重复上报 2024-08-25 09:44:40 +08:00
Alen
822af575c9 fix: 群相关
group_admin事件上报
群成员信息/群列表缓存
ProfileService新增事件
2024-08-25 02:08:14 +08:00
Alen
485efa7d44 Merge branch 'main' into upmain 2024-08-25 01:25:12 +08:00
手瓜一十雪
3d09d45423 build: 2.2.7
DelGroupNotice
2024-08-24 23:50:05 +08:00
手瓜一十雪
4c69c6d9fd fix: v2.2.6 2024-08-24 23:24:24 +08:00
手瓜一十雪
920a41acef build: 2.2.6-test 2024-08-24 23:22:32 +08:00
Alen
0cf13a284c Merge branch 'main' into upmain 2024-08-24 22:26:56 +08:00
手瓜一十雪
a89cdef436 chore: kickMemberV2Inner 2024-08-24 22:23:44 +08:00
手瓜一十雪
881d88f4ad feat: quitGroupV2 2024-08-24 22:12:14 +08:00
Alen
a72c96f56d Merge branch 'main' into upmain 2024-08-24 21:54:43 +08:00
手瓜一十雪
bc8235b209 fix: 移除无用代码 2024-08-24 21:54:11 +08:00
手瓜一十雪
0087495749 fix: 类型推断 2024-08-24 21:50:29 +08:00
手瓜一十雪
9560afd4a7 fix: 一处错误推断 2024-08-24 21:47:39 +08:00
手瓜一十雪
56ec8559a0 fix: type 2024-08-24 21:42:49 +08:00
手瓜一十雪
dcf0a06217 chore: 扩大范围 2024-08-23 11:20:29 +08:00
手瓜一十雪
3b5e6553cd chore 2024-08-23 11:04:47 +08:00
手瓜一十雪
509390af20 release: 2.2.2 2024-08-23 11:01:24 +08:00
手瓜一十雪
9ad511a9c0 release: 2.2.1 2024-08-23 10:57:13 +08:00
手瓜一十雪
89c102513d release: 2.2.0 2024-08-23 10:55:19 +08:00
84 changed files with 1539 additions and 2420 deletions

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
import { NodeIQQNTWrapperSession } from '@/core/wrapper/wrapper';
import { randomUUID } from 'crypto';
interface Internal_MapKey {
interface InternalMapKey {
timeout: number;
createtime: number;
func: (...arg: any[]) => any;
@@ -11,14 +11,14 @@ interface Internal_MapKey {
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 LegacyNTEventWrapper {
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, Internal_MapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
private EventTask = new Map<string, Map<string, Map<string, InternalMapKey>>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
constructor(
wrapperSession: NodeIQQNTWrapperSession
@@ -27,17 +27,15 @@ export class LegacyNTEventWrapper {
}
createProxyDispatch(ListenerMainName: string) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const current = this;
const dispatcherListenerFunc = this.dispatcherListener.bind(this);
return new Proxy(
{},
{
get(target: any, prop: any, receiver: any) {
// console.log('get', prop, typeof target[prop]);
if (typeof target[prop] === 'undefined') {
// 如果方法不存在返回一个函数这个函数调用existentMethod
return (...args: any[]) => {
current.dispatcherListener.apply(current, [ListenerMainName, prop, ...args]).then();
dispatcherListenerFunc(ListenerMainName, prop, ...args).then();
};
}
// 如果方法存在,正常返回
@@ -55,8 +53,6 @@ export class LegacyNTEventWrapper {
if (eventNameArr.length > 1) {
const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', '');
const eventName = eventNameArr[1];
//getNodeIKernelGroupListener,GroupService
//console.log('2', eventName);
const services = (this.WrapperSession as unknown as eventType)[serviceName]();
let event = services[eventName];
//重新绑定this
@@ -72,7 +68,7 @@ export class LegacyNTEventWrapper {
const existListener = this.listenerManager.get(listenerMainName + uniqueCode);
if (!existListener) {
const Listener = this.createProxyDispatch(listenerMainName);
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
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);
@@ -84,16 +80,14 @@ export class LegacyNTEventWrapper {
//统一回调清理事件
async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
//console.log("[EventDispatcher]",ListenerMainName, ListenerSubName, ...args);
this.EventTask.get(ListenerMainName)
?.get(ListenerSubName)
?.forEach((task, uuid) => {
//console.log(task.func, uuid, task.createtime, task.timeout);
if (task.createtime + task.timeout < Date.now()) {
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid);
return;
}
if (task.checker && task.checker(...args)) {
if (task?.checker?.(...args)) {
task.func(...args);
}
});
@@ -101,21 +95,10 @@ export class LegacyNTEventWrapper {
async callNoListenerEvent<EventType extends (...args: any[]) => Promise<any> | any>(
EventName = '',
timeout: number = 3000,
...args: Parameters<EventType>
) {
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
const EventFunc = this.createEventFunction<EventType>(EventName);
let complete = false;
setTimeout(() => {
if (!complete) {
reject(new Error('NTEvent EventName:' + EventName + ' timeout'));
}
}, timeout);
const retData = await EventFunc!(...args);
complete = true;
resolve(retData);
});
): Promise<Awaited<ReturnType<EventType>>> {
const EventFunc = this.createEventFunction<EventType>(EventName);
return EventFunc!(...args);
}
async RegisterListen<ListenerType extends (...args: any[]) => void>(
@@ -210,7 +193,6 @@ export class LegacyNTEventWrapper {
checker: checkerListener,
func: (...args: any[]) => {
complete++;
//console.log('func', ...args);
retData = args as Parameters<ListenerType>;
if (complete >= waitTimes) {
clearTimeout(Timeouter);
@@ -228,7 +210,7 @@ export class LegacyNTEventWrapper {
this.createListenerFunction(ListenerMainName);
const EventFunc = this.createEventFunction<EventType>(EventName);
retEvent = await EventFunc!(...(args as any[]));
if(!checkerEvent(retEvent)){
if (!checkerEvent(retEvent)) {
clearTimeout(Timeouter);
reject(
new Error(
@@ -314,28 +296,3 @@ export class LegacyNTEventWrapper {
);
}
}
// 示例代码 快速创建事件
// let NTEvent = new NTEventWrapper();
// let TestEvent = NTEvent.CreatEventFunction<(force: boolean) => Promise<Number>>('NodeIKernelProfileLikeService/GetTest');
// if (TestEvent) {
// TestEvent(true);
// }
// 示例代码 快速创建监听Listener类
// let NTEvent = new NTEventWrapper();
// NTEvent.CreatListenerFunction<NodeIKernelMsgListener>('NodeIKernelMsgListener', 'core')
// 调用接口
//let NTEvent = new NTEventWrapper();
//let ret = await NTEvent.CallNormalEvent<(force: boolean) => Promise<Number>, (data1: string, data2: number) => void>('NodeIKernelProfileLikeService/GetTest', 'NodeIKernelMsgListener/onAddSendMsg', 1, 3000, true);
// 注册监听 解除监听
// NTEventDispatch.RigisterListener('NodeIKernelMsgListener/onAddSendMsg','core',cb);
// NTEventDispatch.UnRigisterListener('NodeIKernelMsgListener/onAddSendMsg','core');
// let GetTest = NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode);
// GetTest('test');
// always模式
// NTEventDispatch.CreatEvent('NodeIKernelProfileLikeService/GetTest','NodeIKernelMsgListener/onAddSendMsg',Mode,(...args:any[])=>{ console.log(args) });

View File

@@ -6,6 +6,7 @@ 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 {
@@ -26,12 +27,11 @@ export class NTEventChannel extends EventEmitter {
}
createProxyDispatch(ListenerMainName: string) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const current = this;
const dispatcherListener = this.dispatcherListener.bind(this);
return new Proxy({}, {
get(_target: any, prop: any, _receiver: any) {
return (...args: any[]) => {
current.dispatcherListener.apply(current, [ListenerMainName + '/' + prop, ...args]);
dispatcherListener(ListenerMainName + '/' + prop, ...args);
};
},
});
@@ -47,7 +47,7 @@ export class NTEventChannel extends EventEmitter {
Listener = new ListenerType(this.createProxyDispatch(listenerMainName));
if (!Listener) throw new Error('Init Listener failed');
//实例化NTQQ Listener外包装
const ServiceSubName = listenerMainName.match(/^NodeIKernel(.*?)Listener$/)![1];
const ServiceSubName = /^NodeIKernel(.*?)Listener$/.exec(listenerMainName)![1];
const Service = 'NodeIKernel' + ServiceSubName + 'Service/addKernel' + ServiceSubName + 'Listener';
const addfunc = this.createEventFunction<(listener: T) => number>(Service);
//添加Listener到NTQQ
@@ -124,14 +124,7 @@ export class NTEventChannel extends EventEmitter {
EventName = '', timeout: number = 3000, ...args: Parameters<EventType>) {
return new Promise<Awaited<ReturnType<EventType>>>(async (resolve, reject) => {
const EventFunc = this.createEventFunction<EventType>(EventName);
let complete = false;
const Timeouter = setTimeout(() => {
if (!complete) {
reject(new Error('NTEvent EventName:' + EventName + ' timeout'));
}
}, timeout);
const retData = await EventFunc!(...args);
complete = true;
resolve(retData);
});
}

View File

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

View File

@@ -53,7 +53,7 @@ export abstract class ConfigBase<T> {
}
save(newConfigData: T = this.configData as T) {
save(newConfigData: T = this.configData) {
const logger = this.coreContext.context.logger;
const selfInfo = this.coreContext.selfInfo;
this.configData = newConfigData;

View File

@@ -15,23 +15,13 @@ export class LimitedHashTable<K, V> {
}
set(key: K, value: V): void {
// const isExist = this.keyToValue.get(key);
// if (isExist && isExist === value) {
// return;
// }
this.keyToValue.set(key, value);
this.valueToKey.set(value, key);
while (this.keyToValue.size !== this.valueToKey.size) {
//console.log('keyToValue.size !== valueToKey.size Error Atom');
this.keyToValue.clear();
this.valueToKey.clear();
}
// console.log('---------------');
// console.log(this.keyToValue);
// console.log(this.valueToKey);
// console.log('---------------');
while (this.keyToValue.size > this.maxSize || this.valueToKey.size > this.maxSize) {
//console.log(this.keyToValue.size > this.maxSize, this.valueToKey.size > this.maxSize);
const oldestKey = this.keyToValue.keys().next().value;
this.valueToKey.delete(this.keyToValue.get(oldestKey)!);
this.keyToValue.delete(oldestKey);
@@ -101,17 +91,13 @@ class MessageUniqueWrapper {
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
}
createMsg(peer: Peer, msgId: string): number | undefined {
createMsg(peer: Peer, msgId: string) {
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
const hash = crypto.createHash('md5').update(key).digest();
//设置第一个bit为0 保证shortId为正数
hash[0] &= 0x7f;
const shortId = hash.readInt32BE(0);
//减少性能损耗
// const isExist = this.msgIdMap.getKey(shortId);
// if (isExist && isExist === msgId) {
// return shortId;
// }
this.msgIdMap.set(msgId, shortId);
this.msgDataMap.set(key, shortId);
return shortId;

View File

@@ -46,7 +46,7 @@ export class QQBasicInfoWrapper {
}
requireMinNTQQBuild(buildStr: string) {
const currentBuild = parseInt(this.getQQBuildStr() || '0');
const currentBuild = +(this.getQQBuildStr() ?? '0');
if (currentBuild == 0) throw new Error('QQBuildStr获取失败');
return currentBuild >= parseInt(buildStr);
}
@@ -77,5 +77,3 @@ export class QQBasicInfoWrapper {
return { appid: systemPlatform === 'linux' ? '537240795' : '537240709', qua: this.getQUAInternal() };
}
}
export let QQBasicInfo: QQBasicInfoWrapper | undefined;

View File

@@ -4,6 +4,7 @@ import crypto, { randomUUID } from 'crypto';
import util from 'util';
import path from 'node:path';
import * as fileType from 'file-type';
import { solveProblem } from './helper';
export function isGIF(path: string) {
const buffer = Buffer.alloc(4);
@@ -185,25 +186,28 @@ export enum FileUriType {
}
export async function checkUriType(Uri: string) {
//先判断是否是本地文件
try {
if (fs.existsSync(Uri)) return { Uri: Uri, Type: FileUriType.Local };
} catch (error) {
}
try {
const LocalFileRet = await solveProblem((uri: string) => {
if (fs.existsSync(uri)) {
return { Uri: uri, Type: FileUriType.Local };
}
return undefined;
}, Uri);
if (LocalFileRet) return LocalFileRet;
const OtherFileRet = await solveProblem((uri: string) => {
//再判断是否是Http
if (Uri.startsWith('http://') || Uri.startsWith('https://')) {
return { Uri: Uri, Type: FileUriType.Remote };
if (uri.startsWith('http://') || uri.startsWith('https://')) {
return { Uri: uri, Type: FileUriType.Remote };
}
//再判断是否是Base64
if (Uri.startsWith('base64://')) {
return { Uri: Uri, Type: FileUriType.Base64 };
if (uri.startsWith('base64://')) {
return { Uri: uri, Type: FileUriType.Base64 };
}
if (Uri.startsWith('file://')) {
let pathname: string;
if (uri.startsWith('file://')) {
let filePath: string;
// await fs.copyFile(url.pathname, filePath);
pathname = decodeURIComponent(new URL(Uri).pathname);
const pathname = decodeURIComponent(new URL(uri).pathname);
if (process.platform === 'win32') {
filePath = pathname.slice(1);
} else {
@@ -211,8 +215,9 @@ export async function checkUriType(Uri: string) {
}
return { Uri: filePath, Type: FileUriType.Local };
}
} catch (error) {
}
}, Uri);
if (OtherFileRet) return OtherFileRet;
return { Uri: Uri, Type: FileUriType.Unknown };
}
@@ -232,7 +237,7 @@ export async function uri2local(dir: string, uri: string, filename: string | und
//接下来都要有文件名
if (!filename) filename = randomUUID();
//解析Http和Https协议
if (UriType == FileUriType.Remote) {
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
if (pathInfo.name) {

View File

@@ -3,8 +3,27 @@ import fs from 'fs';
import os from 'node:os';
import { QQLevel } from '@/core';
//下面这个类是用于将uid+msgid合并的类
export async function solveProblem<T extends (...arg: any[]) => any>(func: T, ...args: Parameters<T>): Promise<ReturnType<T> | undefined> {
return new Promise<ReturnType<T> | undefined>((resolve) => {
try {
const result = func(...args);
resolve(result);
} catch (e) {
resolve(undefined);
}
});
}
export async function solveAsyncProblem<T extends (...args: any[]) => Promise<any>>(func: T, ...args: Parameters<T>): Promise<Awaited<ReturnType<T>> | undefined> {
return new Promise<Awaited<ReturnType<T>> | undefined>((resolve) => {
func(...args).then((result) => {
resolve(result);
}).catch((e) => {
resolve(undefined);
});
});
}
//下面这个类是用于将uid+msgid合并的类
export class UUIDConverter {
static encode(highStr: string, lowStr: string): string {
const high = BigInt(highStr);

View File

@@ -3,7 +3,6 @@ import { LogWrapper } from './log';
export function proxyHandlerOf(logger: LogWrapper) {
return {
get(target: any, prop: any, receiver: any) {
// console.log('get', prop, typeof target[prop]);
if (typeof target[prop] === 'undefined') {
// 如果方法不存在返回一个函数这个函数调用existentMethod
return (..._args: unknown[]) => {

View File

@@ -30,7 +30,7 @@ export async function getMachineId(): Promise<string> {
if (!machineId) {
machineId = (async () => {
const id = await getMacMachineId();
return id || randomUUID(); // fallback, generate a UUID
return id ?? randomUUID(); // fallback, generate a UUID
})();
}

View File

@@ -24,11 +24,11 @@ export async function getVideoInfo(filePath: string, logger: LogWrapper) {
if (videoStream) {
logger.log(`视频尺寸: ${videoStream.width}x${videoStream.height}`);
} else {
return reject('未找到视频流信息。');
return reject(new Error('未找到视频流信息。'));
}
resolve({
width: videoStream.width!, height: videoStream.height!,
time: parseInt(videoStream.duration!),
time: +(videoStream.duration ?? 10),
format: metadata.format.format_name!,
size,
filePath,
@@ -38,7 +38,7 @@ export async function getVideoInfo(filePath: string, logger: LogWrapper) {
});
}
export function checkFfmpeg(newPath: string | null = null, logger: LogWrapper): Promise<boolean> {
export function checkFfmpeg(logger: LogWrapper,newPath: string | null = null): Promise<boolean> {
return new Promise((resolve, reject) => {
logger.log('开始检查ffmpeg', newPath);
if (newPath) {

View File

@@ -1,17 +1,4 @@
interface IDependsAdapter {
onMSFStatusChange(arg1: number, arg2: number): void;
onMSFSsoError(args: unknown): void;
getGroupCode(args: unknown): void;
}
export interface NodeIDependsAdapter extends IDependsAdapter {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IDependsAdapter): NodeIDependsAdapter;
}
export class DependsAdapter implements IDependsAdapter {
export class NodeIDependsAdapter {
onMSFStatusChange(arg1: number, arg2: number) {
// console.log(arg1, arg2);
// if (arg1 == 2 && arg2 == 2) {

View File

@@ -1,17 +1,4 @@
interface IDispatcherAdapter {
dispatchRequest(arg: unknown): void;
dispatchCall(arg: unknown): void;
dispatchCallWithJson(arg: unknown): void;
}
export interface NodeIDispatcherAdapter extends IDispatcherAdapter {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IDispatcherAdapter): NodeIDispatcherAdapter;
}
export class DispatcherAdapter implements IDispatcherAdapter {
export class NodeIDispatcherAdapter {
dispatchRequest(arg: unknown) {
}

View File

@@ -1,27 +1,4 @@
interface IGlobalAdapter {
onLog(...args: unknown[]): void;
onGetSrvCalTime(...args: unknown[]): void;
onShowErrUITips(...args: unknown[]): void;
fixPicImgType(...args: unknown[]): void;
getAppSetting(...args: unknown[]): void;
onInstallFinished(...args: unknown[]): void;
onUpdateGeneralFlag(...args: unknown[]): void;
onGetOfflineMsg(...args: unknown[]): void;
}
export interface NodeIGlobalAdapter extends IGlobalAdapter {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IGlobalAdapter): NodeIGlobalAdapter;
}
export class GlobalAdapter implements IGlobalAdapter {
export class NodeIGlobalAdapter {
onLog(...args: unknown[]) {
}

View File

@@ -8,6 +8,11 @@ import {
IMAGE_HTTP_HOST_NT,
Peer,
PicElement,
PicType,
SendFileElement,
SendPicElement,
SendPttElement,
SendVideoElement,
} from '@/core/entities';
import path from 'path';
import fs from 'fs';
@@ -18,7 +23,12 @@ 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 } from '@/common/utils/file';
import { calculateFileMD5, isGIF } from '@/common/utils/file';
import pathLib from 'node:path';
import { defaultVideoThumbB64, getVideoInfo } from '@/common/utils/video';
import ffmpeg from 'fluent-ffmpeg';
import fsnormal from 'node:fs';
import { encodeSilk } from '@/common/utils/audio';
export class NTQQFileApi {
@@ -84,6 +94,198 @@ export class NTQQFileApi {
};
}
async createValidSendFileElement(
filePath: string,
fileName: string = '',
folderId: string = ''
): Promise<SendFileElement> {
const { fileName: _fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.FILE);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
return {
elementType: ElementType.FILE,
elementId: '',
fileElement: {
fileName: fileName || _fileName,
folderId: folderId,
filePath: path,
fileSize: (fileSize).toString(),
},
};
}
async createValidSendPicElement(
picPath: string,
summary: string = '',
subType: 0 | 1 = 0
): Promise<SendPicElement> {
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
const imageSize = await this.core.apis.FileApi.getImageSize(picPath);
const picElement: any = {
md5HexStr: md5,
fileSize: fileSize.toString(),
picWidth: imageSize?.width,
picHeight: imageSize?.height,
fileName: fileName,
sourcePath: path,
original: true,
picType: isGIF(picPath) ? PicType.gif : PicType.jpg,
picSubType: subType,
fileUuid: '',
fileSubId: '',
thumbFileSize: 0,
summary,
};
return {
elementType: ElementType.PIC,
elementId: '',
picElement,
};
}
async createValidSendVideoElement(
filePath: string,
fileName: string = '',
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);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
thumb = pathLib.dirname(thumb);
let videoInfo = {
width: 1920, height: 1080,
time: 15,
format: 'mp4',
size: fileSize,
filePath,
};
try {
videoInfo = await getVideoInfo(path, logger);
} catch (e) {
logger.logError('获取视频信息失败', e);
}
const createThumb = new Promise<string | undefined>((resolve, reject) => {
const thumbFileName = `${md5}_0.png`;
const thumbPath = pathLib.join(thumb, thumbFileName);
ffmpeg(filePath)
.on('error', (err) => {
logger.logDebug('获取视频封面失败,使用默认封面', err);
if (diyThumbPath) {
fsPromises.copyFile(diyThumbPath, thumbPath).then(() => {
resolve(thumbPath);
}).catch(reject);
} else {
fsnormal.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
resolve(thumbPath);
}
})
.screenshots({
timestamps: [0],
filename: thumbFileName,
folder: thumb,
size: videoInfo.width + 'x' + videoInfo.height,
}).on('end', () => {
resolve(thumbPath);
});
});
const thumbPath = new Map();
const _thumbPath = await createThumb;
const thumbSize = _thumbPath ? (await fsPromises.stat(_thumbPath)).size : 0;
// log("生成缩略图", _thumbPath)
thumbPath.set(0, _thumbPath);
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : "";
// "fileElement": {
// "fileMd5": "",
// "fileName": "1.mp4",
// "filePath": "C:\\Users\\nanae\\OneDrive\\Desktop\\1.mp4",
// "fileSize": "1847007",
// "picHeight": 1280,
// "picWidth": 720,
// "picThumbPath": {},
// "file10MMd5": "",
// "fileSha": "",
// "fileSha3": "",
// "fileUuid": "",
// "fileSubId": "",
// "thumbFileSize": 750
// }
return {
elementType: ElementType.VIDEO,
elementId: '',
videoElement: {
fileName: fileName || _fileName,
filePath: path,
videoMd5: md5,
thumbMd5,
fileTime: videoInfo.time,
thumbPath: thumbPath,
thumbSize,
thumbWidth: videoInfo.width,
thumbHeight: videoInfo.height,
fileSize: '' + fileSize,
// fileFormat: videotype
// fileUuid: "",
// transferStatus: 0,
// progress: 0,
// invalidState: 0,
// fileSubId: "",
// fileBizId: null,
// originVideoMd5: "",
// fileFormat: 2,
// import_rich_media_context: null,
// sourceVideoCodecFormat: 2
},
};
}
async createValidSendPttElement(pttPath: string): Promise<SendPttElement> {
const {
converted,
path: silkPath,
duration,
} = await encodeSilk(pttPath, this.core.NapCatTempPath, this.core.context.logger);
// 生成语音 Path: silkPath Time: duration
if (!silkPath) {
throw new Error('语音转换失败, 请检查语音文件是否正常');
}
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(silkPath, ElementType.PTT);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
if (converted) {
fsPromises.unlink(silkPath);
}
return {
elementType: ElementType.PTT,
elementId: '',
pttElement: {
fileName: fileName,
filePath: path,
md5HexStr: md5,
fileSize: fileSize,
// duration: Math.max(1, Math.round(fileSize / 1024 / 3)), // 一秒钟大概是3kb大小, 小于1秒的按1秒算
duration: duration ?? 1,
formatType: 1,
voiceType: 1,
voiceChangeType: 0,
canConvert2Text: true,
waveAmplitudes: [
0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17,
],
fileSubId: '',
playState: 1,
autoConvertText: 0,
},
};
}
async downloadMediaByUuid() {
//napCatCore.session.getRichMediaService().downloadFileForFileUuid();
}
@@ -157,9 +359,9 @@ export class NTQQFileApi {
}
//从原始消息获取文件路径
const filePath =
FileElements?.fileElement?.filePath ||
FileElements?.pttElement?.filePath ||
FileElements?.videoElement?.filePath ||
FileElements?.fileElement?.filePath ??
FileElements?.pttElement?.filePath ??
FileElements?.videoElement?.filePath ??
FileElements?.picElement?.sourcePath;
return filePath;
}
@@ -317,7 +519,7 @@ export class NTQQFileApi {
}
} else if (fileMd5 || md5HexStr) {
// 没有url需要自己拼接
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 || md5HexStr)!.toUpperCase()}/0`;
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${(fileMd5 ?? md5HexStr)!.toUpperCase()}/0`;
}
this.context.logger.logDebug('图片url获取失败', element);
return '';

View File

@@ -21,7 +21,7 @@ export class NTQQFriendApi {
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']>(
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
);
return Array.from(data.values());
}
@@ -33,7 +33,7 @@ export class NTQQFriendApi {
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']>(
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
);
data.forEach((value, key) => {
retMap.set(value.uin!, value.uid!);
@@ -55,7 +55,7 @@ export class NTQQFriendApi {
return item.buddyUids;
}));
const data = await this.core.eventWrapper.callNoListenerEvent<NodeIKernelProfileService['getCoreAndBaseInfo']>(
'NodeIKernelProfileService/getCoreAndBaseInfo', 5000, 'nodeStore', uids,
'NodeIKernelProfileService/getCoreAndBaseInfo', 'nodeStore', uids,
);
return buddyListV2.map(category => ({
categoryId: category.categoryId,
@@ -74,9 +74,14 @@ export class NTQQFriendApi {
return this.context.session.getBuddyService().clearBuddyReqUnreadCnt();
}
async getBuddyReq() {
const [, ret] = await this.core.eventWrapper.CallNormalEventV2
<NodeIKernelBuddyService['getBuddyReq'], NodeIKernelBuddyListener['onBuddyReqChange']>
('NodeIKernelBuddyService/getBuddyReq', 'NodeIKernelBuddyListener/onBuddyReqChange', 1, 5000);
const [, ret] = await this.core.eventWrapper.CallNormalEventV2<
NodeIKernelBuddyService['getBuddyReq'],
NodeIKernelBuddyListener['onBuddyReqChange']
>(
'NodeIKernelBuddyService/getBuddyReq',
'NodeIKernelBuddyListener/onBuddyReqChange',
1,
5000);
return ret;
}

View File

@@ -7,12 +7,13 @@ import {
GroupNotify,
GroupRequestOperateTypes,
InstanceContext,
KickMemberV2Req,
MemberExtSourceType,
NapCatCore,
NodeIKernelGroupListener,
NodeIKernelGroupService,
} from '@/core';
import { isNumeric, runAllWithTimeout } from '@/common/utils/helper';
import { isNumeric, runAllWithTimeout, sleep } from '@/common/utils/helper';
export class NTQQGroupApi {
context: InstanceContext;
@@ -23,7 +24,9 @@ export class NTQQGroupApi {
constructor(context: InstanceContext, core: NapCatCore) {
this.context = context;
this.core = core;
this.initCache().then().catch(context.logger.logError);
sleep(1000).then(() => {
this.initCache().then().catch(context.logger.logError);
});
}
async initCache() {
this.groups = await this.getGroups();
@@ -219,7 +222,21 @@ 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) {
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);
}
async quitGroupV2(GroupCode: string, needDeleteLocalMsg: boolean) {
const param = {
groupCode: GroupCode,
needDeleteLocalMsg: needDeleteLocalMsg
};
//应该是直接返回不需要Listener的 未经测试 需测试再发布
return this.context.session.getGroupService().quitGroupV2(param);
}
async removeGroupEssence(GroupCode: string, msgId: string) {
// 代码没测过
// 需要 ob11msgid->msgId + (peer) -> msgSeq + msgRandom
@@ -253,10 +270,7 @@ export class NTQQGroupApi {
}
async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
//type ListenerType = NodeIKernelGroupListener['onMemberInfoChange'];
type EventType = NodeIKernelGroupService['getMemberInfo'];
// NTEventDispatch.CreatListenerFunction('NodeIKernelGroupListener/onGroupMemberInfoUpdate',
//return napCatCore.session.getGroupService().getMemberInfo(GroupCode, [uid], forced);
const Listener = this.core.eventWrapper.RegisterListen<(params: any) => void>
(
'NodeIKernelGroupListener/onMemberInfoChange',
@@ -278,20 +292,6 @@ export class NTQQGroupApi {
member = members.get(uid);
}
return member;
// 原本的方法: (no_cache 下效率很高, cache 下效率一致)
// const [, , , _members] = await this.core.eventWrapper.CallNormalEvent<EventType, ListenerType>
// (
// 'NodeIKernelGroupService/getMemberInfo',
// 'NodeIKernelGroupListener/onMemberInfoChange',
// 1,
// 5000,
// (groupCode: string, changeType: number, members: Map<string, GroupMember>) => {
// return groupCode == GroupCode && members.has(uid);
// },
// GroupCode, [uid], forced,
// );
// return _members.get(uid);
}
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
@@ -299,10 +299,10 @@ export class NTQQGroupApi {
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
const result = await groupService.getNextMemberList(sceneId!, undefined, num);
if (result.errCode !== 0) {
throw ('获取群成员列表出错,' + result.errMsg);
throw new Error('获取群成员列表出错,' + result.errMsg);
}
//logDebug(`获取群(${groupQQ})成员列表结果:`, `finish: ${result.result.finish}`); //, Array.from(result.result.infos.values()));
this.context.logger.logDebug(`获取群(${groupQQ})成员列表结果:`, `members: ${result.result.infos.size}`); //, Array.from(result.result.infos.values()));
return result.result.infos;
/*
console.log(sceneId);
@@ -331,7 +331,6 @@ export class NTQQGroupApi {
arkJson: string
}>>(
'NodeIKernelGroupService/getGroupRecommendContactArkJson',
5000,
GroupCode,
);
return ret.arkJson;
@@ -357,7 +356,7 @@ export class NTQQGroupApi {
'seq': seq, // 通知序列号
'type': type,
'groupCode': groupCode,
'postscript': reason || ' ', // 仅传空值可能导致处理失败,故默认给个空格
'postscript': reason ?? ' ', // 仅传空值可能导致处理失败,故默认给个空格
},
});
}

View File

@@ -1,6 +1,6 @@
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement, SendStatusType } from '@/core/entities';
import { InstanceContext, NapCatCore } from '@/core';
import { onGroupFileInfoUpdateParamType } from '@/core/listeners';
import { GroupFileInfoUpdateParamType } from '@/core/listeners';
import { GeneralCallResult } from '@/core/services/common';
export class NTQQMsgApi {
@@ -119,13 +119,13 @@ export class NTQQMsgApi {
async getGroupFileList(GroupCode: string, params: GetFileListParam) {
const data = await this.core.eventWrapper.CallNormalEvent<
(GroupCode: string, params: GetFileListParam) => Promise<unknown>,
(groupFileListResult: onGroupFileInfoUpdateParamType) => void
(groupFileListResult: GroupFileInfoUpdateParamType) => void
>(
'NodeIKernelRichMediaService/getGroupFileList',
'NodeIKernelMsgListener/onGroupFileInfoUpdate',
1,
5000,
(groupFileListResult: onGroupFileInfoUpdateParamType) => {
(groupFileListResult: GroupFileInfoUpdateParamType) => {
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
return true;
},
@@ -251,7 +251,7 @@ export class NTQQMsgApi {
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

@@ -2,77 +2,6 @@ import { RequestUtil } from '@/common/utils/request';
import { MiniAppLuaJsonType } from '@/core';
import { InstanceContext, NapCatCore } from '..';
// let t = await napCatCore.session.getGroupService().shareDigest({
// appId: "100497308",
// appType: 1,
// msgStyle: 0,
// recvUin: "726067488",
// sendType: 1,
// clientInfo: {
// platform: 1
// },
// richMsg: {
// usingArk: true,
// title: "Bot测试title",
// summary: "Bot测试summary",
// url: "https://www.bilibili.com",
// pictureUrl: "https://y.qq.com/music/photo_new/T002R300x300M0000035DC6W4ZpSqf_1.jpg?max_age=2592000",
// brief: "Bot测试brief",
// }
// });
// {
// errCode: 0,
// errMsg: '',
// rsp: {
// sendType: 1,
// recvUin: '726067488',
// recvOpenId: '',
// errCode: 901501,
// errMsg: 'imagent service_error:150_OIDB_NO_PRIV',
// extInfo: {
// wording: '消息下发失败(错误码901501)',
// jumpResult: 0,
// jumpUrl: '',
// level: 0,
// subLevel: 0,
// developMsg: 'imagent error'
// }
// }
// }
// export class MusicSign {
// private readonly url: string;
// constructor(url: string) {
// this.url = url;
// }
// sign(postData: CustomMusicSignPostData | IdMusicSignPostData): Promise<any> {
// return new Promise((resolve, reject) => {
// fetch(this.url, {
// method: 'POST', // 指定请求方法为 POST
// headers: {
// 'Content-Type': 'application/json' // 设置请求头,指明发送的数据类型为 JSON
// },
// body: JSON.stringify(postData) // 将 JavaScript 对象转换为 JSON 字符串作为请求体
// })
// .then(response => {
// if (!response.ok) {
// reject(response.statusText); // 请求失败,返回错误信息
// }
// return response.json(); // 解析 JSON 格式的响应体
// })
// .then(data => {
// logDebug('音乐消息生成成功', data);
// resolve(data);
// })
// .catch(error => {
// reject(error);
// });
// });
// }
// }
export class NTQQMusicSignApi {
context: InstanceContext;
core: NapCatCore;

View File

@@ -32,7 +32,6 @@ export class NTQQSystemApi {
arkJson: string
}>>(
'NodeIKernelCollectionService/collectionArkShare',
5000,
'1717662698058',
);
return ret;

View File

@@ -3,6 +3,7 @@ import { NodeIKernelProfileListener } from '@/core/listeners';
import { RequestUtil } from '@/common/utils/request';
import { NodeIKernelProfileService, ProfileBizType, UserDetailSource } from '@/core/services';
import { InstanceContext, NapCatCore } from '..';
import { solveAsyncProblem } from '@/common/utils/helper';
export class NTQQUserApi {
context: InstanceContext;
@@ -128,12 +129,9 @@ export class NTQQUserApi {
}
async getUserDetailInfo(uid: string): Promise<User> {
try {
const retUser = await this.fetchUserDetailInfo(uid, UserDetailSource.KDB);
if (retUser.uin !== '0') {
return retUser;
}
} catch (e) {
const retUser = await solveAsyncProblem(async (uid) => this.fetchUserDetailInfo(uid, UserDetailSource.KDB), uid);
if (retUser && retUser.uin !== '0') {
return retUser;
}
this.context.logger.logDebug('[NapCat] [Mark] getUserDetailInfo Mode1 Failed.');
return this.fetchUserDetailInfo(uid, UserDetailSource.KSERVER);
@@ -240,7 +238,7 @@ export class NTQQUserApi {
async getUserDetailInfoByUinV2(Uin: string) {
return await this.core.eventWrapper.callNoListenerEvent<(Uin: string) => Promise<UserDetailInfoByUinV2>>
('NodeIKernelProfileService/getUserDetailInfoByUin', 5000, Uin);
('NodeIKernelProfileService/getUserDetailInfoByUin', Uin);
}
async forceFetchClientKey() {

View File

@@ -20,14 +20,13 @@ export class NTQQWebApi {
async shareDigest(groupCode: string, msgSeq: string, msgRandom: string, targetGroupCode: string) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${
new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject),
group_code: groupCode,
msg_seq: msgSeq,
msg_random: msgRandom,
target_group_code: targetGroupCode,
}).toString()
const url = `https://qun.qq.com/cgi-bin/group_digest/share_digest?${new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject),
group_code: groupCode,
msg_seq: msgSeq,
msg_random: msgRandom,
target_group_code: targetGroupCode,
}).toString()
}`;
try {
return RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
@@ -38,13 +37,12 @@ export class NTQQWebApi {
async getGroupEssenceMsg(GroupCode: string, page_start: string) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${
new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject),
group_code: GroupCode,
page_start,
page_limit: '20',
}).toString()
const url = `https://qun.qq.com/cgi-bin/group_digest/digest_list?${new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject),
group_code: GroupCode,
page_start,
page_limit: '20',
}).toString()
}`;
let ret;
try {
@@ -65,14 +63,13 @@ export class NTQQWebApi {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const retList: Promise<WebApiGroupMemberRet>[] = [];
const fastRet = await RequestUtil.HttpGetJson<WebApiGroupMemberRet>
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${
new URLSearchParams({
st: '0',
end: '40',
sort: '1',
gc: GroupCode,
bkn: this.getBknFromCookie(cookieObject),
}).toString()
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
st: '0',
end: '40',
sort: '1',
gc: GroupCode,
bkn: this.getBknFromCookie(cookieObject),
}).toString()
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
if (!fastRet?.count || fastRet?.errcode !== 0 || !fastRet?.mems) {
return [];
@@ -86,14 +83,13 @@ export class NTQQWebApi {
//遍历批量请求
for (let i = 2; i <= PageNum; i++) {
const ret = RequestUtil.HttpGetJson<WebApiGroupMemberRet>
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${
new URLSearchParams({
st: ((i - 1) * 40).toString(),
end: (i * 40).toString(),
sort: '1',
gc: GroupCode,
bkn: this.getBknFromCookie(cookieObject),
}).toString()
(`https://qun.qq.com/cgi-bin/qun_mgr/search_group_members?${new URLSearchParams({
st: ((i - 1) * 40).toString(),
end: (i * 40).toString(),
sort: '1',
gc: GroupCode,
bkn: this.getBknFromCookie(cookieObject),
}).toString()
}`, 'POST', '', { 'Cookie': this.cookieToString(cookieObject) });
retList.push(ret);
}
@@ -127,15 +123,14 @@ export class NTQQWebApi {
let ret: any = undefined;
try {
ret = await RequestUtil.HttpGetJson<any>
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${
new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject),
qid: GroupCode,
text: Content,
pinned: '0',
type: '1',
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
}).toString()
(`https://web.qun.qq.com/cgi-bin/announce/add_qun_notice${new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject),
qid: GroupCode,
text: Content,
pinned: '0',
type: '1',
settings: '{"is_show_edit_card":1,"tip_window_type":1,"confirm_required":1}',
}).toString()
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
return ret;
} catch (e) {
@@ -147,15 +142,10 @@ export class NTQQWebApi {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
let ret: WebApiGroupNoticeRet | undefined = undefined;
try {
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(`https://web.qun.qq.com/cgi-bin/announce/get_t_list?${
new URLSearchParams({
bkn: this.getBknFromCookie(cookieObject),
qid: GroupCode,
type: '1',
start: '0',
num: '1',
}).toString()
}`, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
const url = 'https://web.qun.qq.com/cgi-bin/announce/get_t_list?bkn=' +
this.getBknFromCookie(cookieObject) + '&qid=' + GroupCode + '&ft=23&ni=1&n=1&i=1&log_read=1&platform=1&s=-1&n=20';
ret = await RequestUtil.HttpGetJson<WebApiGroupNoticeRet>(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
if (ret?.ec !== 0) {
return undefined;
}
@@ -168,16 +158,15 @@ export class NTQQWebApi {
async getGroupHonorInfo(groupCode: string, getType: WebHonorType) {
const cookieObject = await this.core.apis.UserApi.getCookies('qun.qq.com');
const getDataInternal = async (Internal_groupCode: string, Internal_type: number) => {
const url = `https://qun.qq.com/interactive/honorlist?${
new URLSearchParams({
gc: Internal_groupCode,
type: Internal_type.toString(),
}).toString()
const url = `https://qun.qq.com/interactive/honorlist?${new URLSearchParams({
gc: Internal_groupCode,
type: Internal_type.toString(),
}).toString()
}`;
let resJson;
try {
const res = await RequestUtil.HttpGetText(url, 'GET', '', { 'Cookie': this.cookieToString(cookieObject) });
const match = res.match(/window\.__INITIAL_STATE__=(.*?);/);
const match = /window\.__INITIAL_STATE__=(.*?);/.exec(res);
if (match) {
resJson = JSON.parse(match[1].trim());
}

View File

@@ -3,8 +3,8 @@ import path from 'node:path';
import fs from 'node:fs';
import { InstanceContext } from './wrapper';
import { proxiedListenerOf } from '@/common/utils/proxy-handler';
import { GroupListener, MsgListener, ProfileListener } from './listeners';
import { GroupMember, SelfInfo } from './entities';
import { NodeIKernelMsgListener, NodeIKernelGroupListener, NodeIKernelProfileListener } from './listeners';
import { DataSource, GroupMember, SelfInfo } from './entities';
import { LegacyNTEventWrapper } from '@/common/framework/event-legacy';
import { NTQQFileApi, NTQQFriendApi, NTQQGroupApi, NTQQMsgApi, NTQQSystemApi, NTQQUserApi, NTQQWebApi } from './apis';
import os from 'node:os';
@@ -89,7 +89,7 @@ export class NapCatCore {
// Renamed from 'InitDataListener'
async initNapCatCoreListeners() {
const msgListener = new MsgListener();
const msgListener = new NodeIKernelMsgListener();
msgListener.onRecvMsg = (msgs) => {
msgs.forEach(msg => this.context.logger.logMessage(msg, this.selfInfo));
};
@@ -101,7 +101,7 @@ export class NapCatCore {
proxiedListenerOf(msgListener, this.context.logger) as any
);
const profileListener = new ProfileListener();
const profileListener = new NodeIKernelProfileListener();
profileListener.onProfileDetailInfoChanged = (profile) => {
if (profile.uid === this.selfInfo.uid) {
Object.assign(this.selfInfo, profile);
@@ -117,7 +117,7 @@ export class NapCatCore {
);
// 群相关
const groupListener = new GroupListener();
const groupListener = new NodeIKernelGroupListener();
groupListener.onGroupListUpdate = (updateType, groupList) => {
// console.log("onGroupListUpdate", updateType, groupList)
groupList.map(g => {
@@ -164,9 +164,9 @@ export class NapCatCore {
}
// console.log('onMemberListChange', groupCode, arg);
};
groupListener.onMemberInfoChange = (groupCode, changeType, members) => {
groupListener.onMemberInfoChange = (groupCode, dataSource, members) => {
//console.log('onMemberInfoChange', groupCode, changeType, members);
if (changeType === 0 && members.get(this.selfInfo.uid)?.isDelete) {
if (dataSource === DataSource.LOCAL && members.get(this.selfInfo.uid)?.isDelete) {
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
setTimeout(() => {
this.apis.GroupApi.groupCache.delete(groupCode);
@@ -197,12 +197,12 @@ export class NapCatCore {
}
};
this.context.session.getGroupService().addKernelGroupListener(
proxiedListenerOf(profileListener, 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.log(`${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
this.context.logger.logDebug(`${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
return true;
}
return false;

View File

@@ -1,5 +1,21 @@
import { QQLevel, Sex, User } from './user';
export interface KickMemberInfo {
optFlag: number,
optOperate: number,
optMemberUid: string,
optBytesMsg: string,
}
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,

View File

@@ -411,7 +411,7 @@ export interface ShareLocationElement {
ext: string;
}
export interface sendShareLocationElement {
export interface SendShareLocationElement {
elementType: ElementType.SHARELOCATION;
elementId: string;
shareLocationElement?: ShareLocationElement;
@@ -461,7 +461,7 @@ export interface SendMarkdownElement {
export type SendMessageElement = SendTextElement | SendPttElement |
SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement |
SendVideoElement | SendArkElement | SendMarkdownElement | sendShareLocationElement;
SendVideoElement | SendArkElement | SendMarkdownElement | SendShareLocationElement;
export interface TextElement {
content: string;
@@ -848,41 +848,6 @@ export interface MultiForwardMsgElement {
resId: string;
fileName: string;
}
export enum NTSubMsgType {
KMSGSUBTYPEARKGROUPANNOUNCE = 3,
KMSGSUBTYPEARKGROUPANNOUNCECONFIRMREQUIRED = 4,
KMSGSUBTYPEARKGROUPGIFTATME = 5,
KMSGSUBTYPEARKGROUPTASKATALL = 6,
KMSGSUBTYPEARKMULTIMSG = 7,
KMSGSUBTYPEARKNORMAL = 0,
KMSGSUBTYPEARKTENCENTDOCFROMMINIAPP = 1,
KMSGSUBTYPEARKTENCENTDOCFROMPLUSPANEL = 2,
KMSGSUBTYPEEMOTICON = 15,
KMSGSUBTYPEFILEAPP = 11,
KMSGSUBTYPEFILEAUDIO = 3,
KMSGSUBTYPEFILEDOC = 4,
KMSGSUBTYPEFILEEXCEL = 6,
KMSGSUBTYPEFILEFOLDER = 13,
KMSGSUBTYPEFILEHTML = 10,
KMSGSUBTYPEFILEIPA = 14,
KMSGSUBTYPEFILENORMAL = 0,
KMSGSUBTYPEFILEPDF = 7,
KMSGSUBTYPEFILEPIC = 1,
KMSGSUBTYPEFILEPPT = 5,
KMSGSUBTYPEFILEPSD = 12,
KMSGSUBTYPEFILETXT = 8,
KMSGSUBTYPEFILEVIDEO = 2,
KMSGSUBTYPEFILEZIP = 9,
KMSGSUBTYPELINK = 5,
KMSGSUBTYPEMARKETFACE = 1,
KMSGSUBTYPEMIXEMOTICON = 7,
KMSGSUBTYPEMIXFACE = 3,
KMSGSUBTYPEMIXMARKETFACE = 2,
KMSGSUBTYPEMIXPIC = 1,
KMSGSUBTYPEMIXREPLY = 4,
KMSGSUBTYPEMIXTEXT = 0,
KMSGSUBTYPETENCENTDOC = 6
}
export enum SendStatusType {
KSEND_STATUS_FAILED = 0,
KSEND_STATUS_SENDING = 1,
@@ -914,7 +879,7 @@ export interface RawMessage {
msgType: NTMsgType;
subMsgType: NTSubMsgType;
subMsgType: number;
senderUid: string;

View File

@@ -1,40 +1,58 @@
export enum GroupNotifyTypes {
INVITE_ME = 1,
INVITED_JOIN = 4, // 有人接受了邀请入群
JOIN_REQUEST = 7,
ADMIN_SET = 8,
KICK_MEMBER = 9,
MEMBER_EXIT = 11, // 主动退出
ADMIN_UNSET = 12,
ADMIN_UNSET_OTHER = 13, // 其他人取消管理员
export enum GroupNotifyMsgType {
UN_SPECIFIED,
INVITED_BY_MEMBER,
REFUSE_INVITED,
REFUSED_BY_ADMINI_STRATOR,
AGREED_TOJOIN_DIRECT,// 有人接受了邀请入群
INVITED_NEED_ADMINI_STRATOR_PASS,
AGREED_TO_JOIN_BY_ADMINI_STRATOR,
REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS,
SET_ADMIN,
KICK_MEMBER_NOTIFY_ADMIN,
KICK_MEMBER_NOTIFY_KICKED,
MEMBER_LEAVE_NOTIFY_ADMIN,// 主动退出
CANCEL_ADMIN_NOTIFY_CANCELED,
CANCEL_ADMIN_NOTIFY_ADMIN,// 其他人取消管理员
TRANSFER_GROUP_NOTIFY_OLDOWNER,
TRANSFER_GROUP_NOTIFY_ADMIN
}
export interface GroupNotifies {
doubt: boolean;
nextStartSeq: string;
notifies: GroupNotify[];
}
export enum GroupNotifyStatus {
IGNORE = 0,
WAIT_HANDLE = 1,
APPROVE = 2,
REJECT = 3
export enum GroupNotifyMsgStatus {
KINIT,//初始化
KUNHANDLE,//未处理
KAGREED,//同意
KREFUSED,//拒绝
KIGNORED//忽略
}
export enum GroupInviteStatus {
INIT,
WAIT_TO_APPROVE,
JOINED,
REFUSED_BY_ADMINI_STRATOR
}
export enum GroupInviteType {
BYBUDDY,
BYGROUPMEMBER,
BYDISCUSSMEMBER
}
export interface GroupNotify {
time: number; // 自己添加的字段,时间戳,毫秒, 用于判断收到短时间内收到重复的notify
seq: string; // 唯一标识符转成数字再除以1000应该就是时间戳
type: GroupNotifyTypes;
status: GroupNotifyStatus; // 0是已忽略1是未处理2是已同意
seq: string; // 通知序列号
type: GroupNotifyMsgType;
status: GroupNotifyMsgStatus;
group: { groupCode: string; groupName: string };
user1: { uid: string; nickName: string }; // 被设置管理员的人
user2: { uid: string; nickName: string }; // 操作者
actionUser: { uid: string; nickName: string }; //未知
actionTime: string;
invitationExt: {
srcType: number; // 0?未知
groupCode: string; waitStatus: number
srcType: GroupInviteType; // 邀请来源
groupCode: string;
waitStatus: GroupInviteStatus
};
postscript: string; // 加群用户填写的验证信息
repeatSeqs: [];

View File

@@ -1,53 +1,9 @@
import { BuddyCategoryType, FriendRequestNotify } from '@/core/entities';
export type OnBuddyChangeParams = BuddyCategoryType[]
export type OnBuddyChangeParams = BuddyCategoryType[];
interface IBuddyListener {
onBuddyListChangedV2(arg: unknown): void,//V2版本 还没兼容
onBuddyListChange(arg: OnBuddyChangeParams): void,
onBuddyInfoChange(arg: unknown): void,
onBuddyDetailInfoChange(arg: unknown): void,
onNickUpdated(arg: unknown): void,
onBuddyRemarkUpdated(arg: unknown): void,
onAvatarUrlUpdated(arg: unknown): void,
onBuddyReqChange(arg: FriendRequestNotify): void,
onBuddyReqUnreadCntChange(arg: unknown): void,
onCheckBuddySettingResult(arg: unknown): void,
onAddBuddyNeedVerify(arg: unknown): void,
onSmartInfos(arg: unknown): void,
onSpacePermissionInfos(arg: unknown): void,
onDoubtBuddyReqChange(arg: unknown): void,
onDoubtBuddyReqUnreadNumChange(arg: unknown): void,
onBlockChanged(arg: unknown): void,
onAddMeSettingChanged(arg: unknown): void,
onDelBatchBuddyInfos(arg: unknown): void
}
export interface NodeIKernelBuddyListener extends IBuddyListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IBuddyListener): NodeIKernelBuddyListener;
}
export class BuddyListener implements IBuddyListener {
export class NodeIKernelBuddyListener {
onBuddyListChangedV2(arg: unknown): void {
//throw new Error('Method not implemented.');
}
onAddBuddyNeedVerify(arg: unknown) {

View File

@@ -1,21 +1,5 @@
export interface IKernelFileAssistantListener {
onFileStatusChanged(...args: unknown[]): unknown;
onSessionListChanged(...args: unknown[]): unknown;
onSessionChanged(...args: unknown[]): unknown;
onFileListChanged(...args: unknown[]): unknown;
onFileSearch(...args: unknown[]): unknown;
}
export interface NodeIKernelFileAssistantListener extends IKernelFileAssistantListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IKernelFileAssistantListener): NodeIKernelFileAssistantListener;
}
export class KernelFileAssistantListener implements IKernelFileAssistantListener {
export class NodeIKernelFileAssistantListener {
onFileStatusChanged(...args: unknown[]) {
}

View File

@@ -1,68 +1,6 @@
import { Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
import { DataSource, Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
export interface IGroupListener {
onGroupListUpdate(updateType: GroupListUpdateType, groupList: Group[]): void;
onGroupExtListUpdate(...args: unknown[]): void;
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]): void;
onGroupNotifiesUpdated(dboubt: boolean, notifies: GroupNotify[]): void;
onGroupNotifiesUnreadCountUpdated(...args: unknown[]): void;
onGroupDetailInfoChange(...args: unknown[]): void;
onGroupAllInfoChange(...args: unknown[]): void;
onGroupsMsgMaskResult(...args: unknown[]): void;
onGroupConfMemberChange(...args: unknown[]): void;
onGroupBulletinChange(...args: unknown[]): void;
onGetGroupBulletinListResult(...args: unknown[]): void;
onMemberListChange(arg: {
sceneId: string,
ids: string[],
infos: Map<string, GroupMember>,
finish: boolean,
hasRobot: boolean
}): void;
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>): void;
onSearchMemberChange(...args: unknown[]): void;
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]): void;
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]): void;
onGroupStatisticInfoChange(...args: unknown[]): void;
onJoinGroupNotify(...args: unknown[]): void;
onShutUpMemberListChanged(...args: unknown[]): void;
onGroupBulletinRemindNotify(...args: unknown[]): void;
onGroupFirstBulletinNotify(...args: unknown[]): void;
onJoinGroupNoVerifyFlag(...args: unknown[]): void;
onGroupArkInviteStateResult(...args: unknown[]): void;
// 发现于Win 9.9.9 23159
onGroupMemberLevelInfoChange(...args: unknown[]): void;
}
export interface NodeIKernelGroupListener extends IGroupListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IGroupListener): NodeIKernelGroupListener;
}
export class GroupListener implements IGroupListener {
export class NodeIKernelGroupListener {
// 发现于Win 9.9.9 23159
onGroupMemberLevelInfoChange(...args: unknown[]): void {
@@ -125,7 +63,7 @@ export class GroupListener implements IGroupListener {
onJoinGroupNoVerifyFlag(...args: unknown[]) {
}
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>) {
onMemberInfoChange(groupCode: string, dateSource: DataSource, members: Map<string, GroupMember>) {
}
onMemberListChange(arg: {
@@ -142,102 +80,4 @@ export class GroupListener implements IGroupListener {
onShutUpMemberListChanged(...args: unknown[]) {
}
}
export class DebugGroupListener implements IGroupListener {
onGroupMemberLevelInfoChange(...args: unknown[]): void {
console.log('onGroupMemberLevelInfoChange:', ...args);
}
onGetGroupBulletinListResult(...args: unknown[]) {
console.log('onGetGroupBulletinListResult:', ...args);
}
onGroupAllInfoChange(...args: unknown[]) {
console.log('onGroupAllInfoChange:', ...args);
}
onGroupBulletinChange(...args: unknown[]) {
console.log('onGroupBulletinChange:', ...args);
}
onGroupBulletinRemindNotify(...args: unknown[]) {
console.log('onGroupBulletinRemindNotify:', ...args);
}
onGroupArkInviteStateResult(...args: unknown[]) {
console.log('onGroupArkInviteStateResult:', ...args);
}
onGroupBulletinRichMediaDownloadComplete(...args: unknown[]) {
console.log('onGroupBulletinRichMediaDownloadComplete:', ...args);
}
onGroupConfMemberChange(...args: unknown[]) {
console.log('onGroupConfMemberChange:', ...args);
}
onGroupDetailInfoChange(...args: unknown[]) {
console.log('onGroupDetailInfoChange:', ...args);
}
onGroupExtListUpdate(...args: unknown[]) {
console.log('onGroupExtListUpdate:', ...args);
}
onGroupFirstBulletinNotify(...args: unknown[]) {
console.log('onGroupFirstBulletinNotify:', ...args);
}
onGroupListUpdate(...args: unknown[]) {
console.log('onGroupListUpdate:', ...args);
}
onGroupNotifiesUpdated(...args: unknown[]) {
console.log('onGroupNotifiesUpdated:', ...args);
}
onGroupBulletinRichMediaProgressUpdate(...args: unknown[]) {
console.log('onGroupBulletinRichMediaProgressUpdate:', ...args);
}
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) {
console.log('onGroupNotifiesUnreadCountUpdated:', ...args);
}
onGroupSingleScreenNotifies(doubt: boolean, seq: string, notifies: GroupNotify[]) {
console.log('onGroupSingleScreenNotifies:');
}
onGroupsMsgMaskResult(...args: unknown[]) {
console.log('onGroupsMsgMaskResult:', ...args);
}
onGroupStatisticInfoChange(...args: unknown[]) {
console.log('onGroupStatisticInfoChange:', ...args);
}
onJoinGroupNotify(...args: unknown[]) {
console.log('onJoinGroupNotify:', ...args);
}
onJoinGroupNoVerifyFlag(...args: unknown[]) {
console.log('onJoinGroupNoVerifyFlag:', ...args);
}
onMemberInfoChange(groupCode: string, changeType: number, members: Map<string, GroupMember>) {
console.log('onMemberInfoChange:', groupCode, changeType, members);
}
onMemberListChange(...args: unknown[]) {
console.log('onMemberListChange:', ...args);
}
onSearchMemberChange(...args: unknown[]) {
console.log('onSearchMemberChange:', ...args);
}
onShutUpMemberListChanged(...args: unknown[]) {
console.log('onShutUpMemberListChanged:', ...args);
}
}
}

View File

@@ -1,45 +1,5 @@
export interface IKernelLoginListener {
onLoginConnected(...args: any[]): void;
onLoginDisConnected(...args: any[]): void;
onLoginConnecting(...args: any[]): void;
onQRCodeGetPicture(...args: any[]): void;
onQRCodeLoginPollingStarted(...args: any[]): void;
onQRCodeSessionUserScaned(...args: any[]): void;
onQRCodeLoginSucceed(...args: any[]): void;
onQRCodeSessionFailed(...args: any[]): void;
onLoginFailed(...args: any[]): void;
onLogoutSucceed(...args: any[]): void;
onLogoutFailed(...args: any[]): void;
onUserLoggedIn(...args: any[]): void;
onQRCodeSessionQuickLoginFailed(...args: any[]): void;
onPasswordLoginFailed(...args: any[]): void;
OnConfirmUnusualDeviceFailed(...args: any[]): void;
onQQLoginNumLimited(...args: any[]): void;
onLoginState(...args: any[]): void;
}
export interface NodeIKernelLoginListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IKernelLoginListener): NodeIKernelLoginListener;
}
export class LoginListener implements IKernelLoginListener {
export class NodeIKernelLoginListener {
onLoginConnected(...args: any[]): void {
}

View File

@@ -23,7 +23,7 @@ export interface OnRichMediaDownloadCompleteParams {
userUsedSpacePerDay: unknown | null
}
export interface onGroupFileInfoUpdateParamType {
export interface GroupFileInfoUpdateParamType {
retCode: number;
retMsg: string;
clientWording: string;
@@ -52,199 +52,7 @@ export interface TempOnRecvParams {
}
export interface IKernelMsgListener {
onAddSendMsg(msgRecord: RawMessage): void;
onBroadcastHelperDownloadComplete(broadcastHelperTransNotifyInfo: unknown): void;
onBroadcastHelperProgressUpdate(broadcastHelperTransNotifyInfo: unknown): void;
onChannelFreqLimitInfoUpdate(contact: unknown, z: unknown, freqLimitInfo: unknown): void;
onContactUnreadCntUpdate(hashMap: unknown): void;
onCustomWithdrawConfigUpdate(customWithdrawConfig: unknown): void;
onDraftUpdate(contact: unknown, arrayList: unknown, j2: unknown): void;
onEmojiDownloadComplete(emojiNotifyInfo: unknown): void;
onEmojiResourceUpdate(emojiResourceInfo: unknown): void;
onFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
onFileMsgCome(arrayList: unknown): void;
onFirstViewDirectMsgUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
onFirstViewGroupGuildMapping(arrayList: unknown): void;
onGrabPasswordRedBag(i2: unknown, str: unknown, i3: unknown, recvdOrder: unknown, msgRecord: unknown): void;
onGroupFileInfoAdd(groupItem: unknown): void;
onGroupFileInfoUpdate(groupFileListResult: onGroupFileInfoUpdateParamType): void;
onGroupGuildUpdate(groupGuildNotifyInfo: unknown): void;
onGroupTransferInfoAdd(groupItem: unknown): void;
onGroupTransferInfoUpdate(groupFileListResult: unknown): void;
onGuildInteractiveUpdate(guildInteractiveNotificationItem: unknown): void;
onGuildMsgAbFlagChanged(guildMsgAbFlag: unknown): void;
onGuildNotificationAbstractUpdate(guildNotificationAbstractInfo: unknown): void;
onHitCsRelatedEmojiResult(downloadRelateEmojiResultInfo: unknown): void;
onHitEmojiKeywordResult(hitRelatedEmojiWordsResult: unknown): void;
onHitRelatedEmojiResult(relatedWordEmojiInfo: unknown): void;
onImportOldDbProgressUpdate(importOldDbMsgNotifyInfo: unknown): void;
onInputStatusPush(inputStatusInfo: {
chatType: number;
eventType: number;
fromUin: string;
interval: string;
showTime: string;
statusText: string;
timestamp: string;
toUin: string;
}): void;
onKickedOffLine(kickedInfo: unknown): void;
onLineDev(arrayList: unknown): void;
onLogLevelChanged(j2: unknown): void;
onMsgAbstractUpdate(arrayList: unknown): void;
onMsgBoxChanged(arrayList: unknown): void;
onMsgDelete(contact: unknown, arrayList: unknown): void;
onMsgEventListUpdate(hashMap: unknown): void;
onMsgInfoListAdd(arrayList: unknown): void;
onMsgInfoListUpdate(msgList: RawMessage[]): void;
onMsgQRCodeStatusChanged(i2: unknown): void;
onMsgRecall(i2: unknown, str: unknown, j2: unknown): void;
onMsgSecurityNotify(msgRecord: unknown): void;
onMsgSettingUpdate(msgSetting: unknown): void;
onNtFirstViewMsgSyncEnd(): void;
onNtMsgSyncEnd(): void;
onNtMsgSyncStart(): void;
onReadFeedEventUpdate(firstViewDirectMsgNotifyInfo: unknown): void;
onRecvGroupGuildFlag(i2: unknown): void;
onRecvMsg(...arrayList: unknown[]): void;
onRecvMsgSvrRspTransInfo(j2: unknown, contact: unknown, i2: unknown, i3: unknown, str: unknown, bArr: unknown): void;
onRecvOnlineFileMsg(arrayList: unknown): void;
onRecvS2CMsg(arrayList: unknown): void;
onRecvSysMsg(arrayList: unknown): void;
onRecvUDCFlag(i2: unknown): void;
onRichMediaDownloadComplete(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams): void;
onRichMediaProgerssUpdate(fileTransNotifyInfo: unknown): void;
onRichMediaUploadComplete(fileTransNotifyInfo: unknown): void;
onSearchGroupFileInfoUpdate(searchGroupFileResult:
{
result: {
retCode: number,
retMsg: string,
clientWording: string
},
syncCookie: string,
totalMatchCount: number,
ownerMatchCount: number,
isEnd: boolean,
reqId: number,
item: Array<{
groupCode: string,
groupName: string,
uploaderUin: string,
uploaderName: string,
matchUin: string,
matchWords: Array<unknown>,
fileNameHits: Array<{
start: number,
end: number
}>,
fileModelId: string,
fileId: string,
fileName: string,
fileSize: string,
busId: number,
uploadTime: number,
modifyTime: number,
deadTime: number,
downloadTimes: number,
localPath: string
}>
}): void;
onSendMsgError(j2: unknown, contact: unknown, i2: unknown, str: unknown): void;
onSysMsgNotification(i2: unknown, j2: unknown, j3: unknown, arrayList: unknown): void;
onTempChatInfoUpdate(tempChatInfo: TempOnRecvParams): void;
onUnreadCntAfterFirstView(hashMap: unknown): void;
onUnreadCntUpdate(hashMap: unknown): void;
onUserChannelTabStatusChanged(z: unknown): void;
onUserOnlineStatusChanged(z: unknown): void;
onUserTabStatusChanged(arrayList: unknown): void;
onlineStatusBigIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void;
onlineStatusSmallIconDownloadPush(i2: unknown, j2: unknown, str: unknown): void;
// 第一次发现于Linux
onUserSecQualityChanged(...args: unknown[]): void;
onMsgWithRichLinkInfoUpdate(...args: unknown[]): void;
onRedTouchChanged(...args: unknown[]): void;
// 第一次发现于Win 9.9.9 23159
onBroadcastHelperProgerssUpdate(...args: unknown[]): void;
}
export interface NodeIKernelMsgListener extends IKernelMsgListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IKernelMsgListener): NodeIKernelMsgListener;
}
export class MsgListener implements IKernelMsgListener {
export class NodeIKernelMsgListener {
onAddSendMsg(msgRecord: RawMessage) {
}
@@ -305,7 +113,7 @@ export class MsgListener implements IKernelMsgListener {
}
onGroupFileInfoUpdate(groupFileListResult: onGroupFileInfoUpdateParamType) {
onGroupFileInfoUpdate(groupFileListResult: GroupFileInfoUpdateParamType) {
}

View File

@@ -1,25 +1,6 @@
import { User, UserDetailInfoListenerArg } from '@/core/entities';
interface IProfileListener {
onProfileSimpleChanged(...args: unknown[]): void;
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void;
onProfileDetailInfoChanged(profile: User): void;
onStatusUpdate(...args: unknown[]): void;
onSelfStatusChanged(...args: unknown[]): void;
onStrangerRemarkChanged(...args: unknown[]): void;
}
export interface NodeIKernelProfileListener extends IProfileListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IProfileListener): NodeIKernelProfileListener;
}
export class ProfileListener implements IProfileListener {
export class NodeIKernelProfileListener {
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void {
}
@@ -43,4 +24,48 @@ export class ProfileListener implements IProfileListener {
onStrangerRemarkChanged(...args: unknown[]) {
}
onMemberListChange(...args: unknown[]) {
}
onMemberInfoChange(...args: unknown[]) {
}
onGroupListUpdate(...args: unknown[]) {
}
onGroupAllInfoChange(...args: unknown[]) {
}
onGroupDetailInfoChange(...args: unknown[]) {
}
onGroupConfMemberChange(...args: unknown[]) {
}
onGroupExtListUpdate(...args: unknown[]) {
}
onGroupNotifiesUpdated(...args: unknown[]) {
}
onGroupNotifiesUnreadCountUpdated(...args: unknown[]) {
}
onGroupMemberLevelInfoChange(...args: unknown[]) {
}
onGroupBulletinChange(...args: unknown[]) {
}
}

View File

@@ -1,23 +1,4 @@
interface IKernelRecentContactListener {
onDeletedContactsNotify(...args: unknown[]): unknown;
onRecentContactNotification(...args: unknown[]): unknown;
onMsgUnreadCountUpdate(...args: unknown[]): unknown;
onGuildDisplayRecentContactListChanged(...args: unknown[]): unknown;
onRecentContactListChanged(...args: unknown[]): unknown;
onRecentContactListChangedVer2(...args: unknown[]): unknown;
}
export interface NodeIKernelRecentContactListener extends IKernelRecentContactListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(listener: IKernelRecentContactListener): NodeIKernelRecentContactListener;
}
export class KernelRecentContactListener implements IKernelRecentContactListener {
export class NodeIKernelRecentContactListener {
onDeletedContactsNotify(...args: unknown[]) {
}

View File

@@ -1,17 +1,4 @@
export interface IKernelRobotListener {
onRobotFriendListChanged(...args: unknown[]): void;
onRobotListChanged(...args: unknown[]): void;
onRobotProfileChanged(...args: unknown[]): void;
}
export interface NodeIKernelRobotListener extends IKernelRobotListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IKernelRobotListener): NodeIKernelRobotListener;
}
export class KernelRobotListener implements IKernelRobotListener {
export class NodeIKernelRobotListener{
onRobotFriendListChanged(...args: unknown[]) {
}

View File

@@ -1,23 +1,4 @@
export interface ISessionListener {
onNTSessionCreate(args: unknown): void;
onGProSessionCreate(args: unknown): void;
onSessionInitComplete(args: unknown): void;
onOpentelemetryInit(args: unknown): void;
onUserOnlineResult(args: unknown): void;
onGetSelfTinyId(args: unknown): void;
}
export interface NodeIKernelSessionListener extends ISessionListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: ISessionListener): NodeIKernelSessionListener;
}
export class SessionListener implements ISessionListener {
export class NodeIKernelSessionListener {
onNTSessionCreate(args: unknown) {
}

View File

@@ -1,22 +1,4 @@
export interface IStorageCleanListener {
onCleanCacheProgressChanged(args: unknown): void;
onScanCacheProgressChanged(args: unknown): void;
onCleanCacheStorageChanged(args: unknown): void;
onFinishScan(args: unknown): void;
onChatCleanDone(args: unknown): void;
}
export interface NodeIKernelStorageCleanListener extends IStorageCleanListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IStorageCleanListener): NodeIKernelStorageCleanListener;
}
export class StorageCleanListener implements IStorageCleanListener {
export class NodeIKernelStorageCleanListener {
onCleanCacheProgressChanged(args: unknown) {
}

View File

@@ -1,10 +1,2 @@
export interface IKernelTicketListener {
}
export interface NodeIKernelTicketListener extends IKernelTicketListener {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(adapter: IKernelTicketListener): NodeIKernelTicketListener;
}
export class KernelTicketListener implements IKernelTicketListener {
export class NodeIKernelTicketListener {
}

View File

@@ -1,16 +1,20 @@
import { IGroupListener, NodeIKernelGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import { IGroupListener } from '@/core/listeners/NodeIKernelGroupListener';
import {
GroupExtParam,
GroupMember,
GroupMemberRole,
GroupNotifyTypes,
GroupNotifyMsgType,
GroupRequestOperateTypes,
KickMemberV2Req
} from '@/core/entities';
import { GeneralCallResult } from '@/core/services/common';
//高版本的接口不应该随意使用 使用应该严格进行pr审核 同时部分ipc中未出现的接口不要过于依赖 应该做好数据兜底
export interface NodeIKernelGroupService {
kickMemberV2(param: KickMemberV2Req): Promise<GeneralCallResult>;
quitGroupV2(param: { groupCode: string; needDeleteLocalMsg: boolean; }): Promise<GeneralCallResult>;
getMemberCommonInfo(Req: {
groupCode: string,
startUin: string,
@@ -96,7 +100,7 @@ export interface NodeIKernelGroupService {
uid: string,
index: number//0
}>,
infos: {},
infos: unknown,
finish: true,
hasRobot: false
}
@@ -195,7 +199,7 @@ export interface NodeIKernelGroupService {
operateType: GroupRequestOperateTypes, // 2 拒绝
targetMsg: {
seq: string, // 通知序列号
type: GroupNotifyTypes,
type: GroupNotifyMsgType,
groupCode: string,
postscript: string
}
@@ -205,7 +209,7 @@ export interface NodeIKernelGroupService {
getGroupBulletin(groupCode: string): unknown;
deleteGroupBulletin(groupCode: string, seq: string): void;
deleteGroupBulletin(groupCode: string, seq: string, feedId: string): void;
publishGroupBulletin(groupCode: string, pskey: string, data: any): Promise<GeneralCallResult>;

View File

@@ -9,7 +9,7 @@ export interface LoginInitConfig {
hostName: string;
}
export interface passwordLoginRetType {
export interface PasswordLoginRetType {
result: string,
loginErrorInfo: {
step: number;
@@ -23,7 +23,7 @@ export interface passwordLoginRetType {
}
}
export interface passwordLoginArgType {
export interface PasswordLoginArgType {
uin: string;
passwordMd5: string;//passwMD5
step: number;//猜测是需要二次认证 参数 一次为0
@@ -77,7 +77,7 @@ export interface NodeIKernelLoginService {
quickLoginWithUin(uin: string): Promise<QuickLoginResult>;
passwordLogin(param: passwordLoginArgType): Promise<any>;
passwordLogin(param: PasswordLoginArgType): Promise<any>;
getQRCodePicture(): boolean;
}

View File

@@ -37,7 +37,7 @@ export interface NodeIKernelMsgService {
recallMsg(peer: Peer, msgIds: string[]): Promise<GeneralCallResult>;
addKernelMsgImportToolListener(arg: Object): unknown;
addKernelMsgImportToolListener(arg: unknown): unknown;
removeKernelMsgListener(args: unknown): unknown;
@@ -51,7 +51,7 @@ export interface NodeIKernelMsgService {
getOnLineDev(): void;
kickOffLine(DevInfo: Object): unknown;
kickOffLine(DevInfo: unknown): unknown;
setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise<GeneralCallResult>;
@@ -80,11 +80,11 @@ export interface NodeIKernelMsgService {
// this.voipToken = bArr2;
// this.profileId = str;
setToken(arg: Object): unknown;
setToken(arg: unknown): unknown;
switchForeGround(): unknown;
switchBackGround(arg: Object): unknown;
switchBackGround(arg: unknown): unknown;
//hex
setTokenForMqq(token: string): unknown;
@@ -384,7 +384,7 @@ export interface NodeIKernelMsgService {
getFileThumbSavePath(...args: unknown[]): unknown;
//猜测居多
translatePtt2Text(MsgId: string, Peer: {}, MsgElement: {}): unknown;
translatePtt2Text(MsgId: string, Peer: Peer, MsgElement: unknown): unknown;
setPttPlayedState(...args: unknown[]): unknown;
@@ -668,7 +668,7 @@ export interface NodeIKernelMsgService {
recordEmoji(...args: unknown[]): unknown;
fetchGetHitEmotionsByWord(args: Object): Promise<unknown>;//表情推荐?
fetchGetHitEmotionsByWord(args: unknown): Promise<unknown>;//表情推荐?
deleteAllRoamMsgs(...args: unknown[]): unknown;//漫游消息?

View File

@@ -1,6 +1,6 @@
import { AnyCnameRecord } from 'node:dns';
import { BizKey, ModifyProfileParams, SimpleInfo, UserDetailInfoByUin } from '../entities';
import { NodeIKernelProfileListener, ProfileListener } from '../listeners';
import { NodeIKernelProfileListener } from '../listeners';
import { GeneralCallResult } from '@/core/services/common';
export enum UserDetailSource {
@@ -35,7 +35,7 @@ export interface NodeIKernelProfileService {
fetchUserDetailInfo(trace: string, uids: string[], arg2: number, arg3: number[]): Promise<unknown>;
addKernelProfileListener(listener: ProfileListener): number;
addKernelProfileListener(listener: NodeIKernelProfileListener): number;
removeKernelProfileListener(listenerId: number): void;

View File

@@ -1,4 +1,4 @@
import { forceFetchClientKeyRetType } from './common';
import { ForceFetchClientKeyRetType } from './common';
export interface NodeIKernelTicketService {
@@ -6,7 +6,7 @@ export interface NodeIKernelTicketService {
removeKernelTicketListener(listenerId: unknown): void;
forceFetchClientKey(arg: string): Promise<forceFetchClientKeyRetType>;
forceFetchClientKey(arg: string): Promise<ForceFetchClientKeyRetType>;
isNull(): boolean;
}

View File

@@ -8,7 +8,7 @@ export interface GeneralCallResult {
errMsg: string
}
export interface forceFetchClientKeyRetType extends GeneralCallResult {
export interface ForceFetchClientKeyRetType extends GeneralCallResult {
url: string;
keyIndex: string;
clientKey: string;

View File

@@ -1,11 +1,6 @@
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from '../adapters';
import {
NodeIKernelBuddyListener,
NodeIKernelGroupListener,
NodeIKernelLoginListener,
NodeIKernelMsgListener,
NodeIKernelProfileListener,
NodeIKernelSessionListener,
NodeIKernelSessionListener
} from '../listeners';
import {
NodeIKernelAvatarService,
@@ -69,9 +64,9 @@ export interface NodeQQNTWrapperUtil {
genFileShaAndMd5Hex(path: string, unknown: number): unknown; //可能是错的
setTraceInfo(unknown: Object): unknown;
setTraceInfo(unknown: unknown): unknown;
encodeOffLine(unknown: Object): unknown;
encodeOffLine(unknown: unknown): unknown;
decodeOffLine(arg: string): unknown; //可能是错的 传递hex
@@ -89,7 +84,7 @@ export interface NodeQQNTWrapperUtil {
runProcessArgs(arg0: string, arg1: { [key: string]: string }, arg2: boolean): unknown;
calcThumbSize(arg0: number, arg1: number, arg2: Object): unknown;
calcThumbSize(arg0: number, arg1: number, arg2: unknown): unknown;
fullWordToHalfWord(arg0: string): unknown;
@@ -278,20 +273,11 @@ export interface NodeIQQNTWrapperEngine {
export interface WrapperNodeApi {
[key: string]: any;
NodeIKernelBuddyListener: NodeIKernelBuddyListener;
NodeIKernelGroupListener: NodeIKernelGroupListener;
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
NodeIKernelMsgListener: NodeIKernelMsgListener;
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
NodeIGlobalAdapter: NodeIGlobalAdapter;
NodeIDependsAdapter: NodeIDependsAdapter;
NodeIDispatcherAdapter: NodeIDispatcherAdapter;
NodeIKernelSessionListener: NodeIKernelSessionListener;
NodeIKernelLoginService: NodeIKernelLoginService;
NodeIKernelLoginListener: NodeIKernelLoginListener;
NodeIKernelProfileService: NodeIKernelProfileService;
NodeIKernelProfileListener: NodeIKernelProfileListener;
}
export enum PlatformType {

View File

@@ -5,7 +5,7 @@ import { QQBasicInfoWrapper } from '@/common/utils/QQBasicInfo';
import { loadQQWrapper, NapCatCore, NapCatCoreWorkingEnv } from '@/core/core';
import { InstanceContext } from '@/core';
import { SelfInfo } from '@/core/entities';
import { LoginListener } from '@/core/listeners';
import { NodeIKernelLoginListener } from '@/core/listeners';
import { NodeIKernelLoginService } from '@/core/services';
import { NodeIQQNTWrapperSession, WrapperNodeApi } from '@/core/wrapper/wrapper';
import { InitWebUi, WebUiConfig } from '@/webui';
@@ -29,7 +29,7 @@ export async function NCoreInitFramework(
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
//直到登录成功后,执行下一步
const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
const loginListener = new LoginListener();
const loginListener = new NodeIKernelLoginListener();
loginListener.onQRCodeLoginSucceed = async (loginResult) => {
await new Promise<void>(resolvePendingInit => {
registerInitCallback(() => resolvePendingInit());

View File

@@ -17,7 +17,7 @@ export class FetchCustomFace extends BaseAction<Payload, string[]> {
async _handle(payload: Payload) {
//48 可能正好是QQ需要的一个页面的数量 Tagged Mlikiowa
const ret = await this.CoreContext.apis.MsgApi.fetchFavEmojiList(parseInt((payload.count || '0').toString()) || 48);
const ret = await this.CoreContext.apis.MsgApi.fetchFavEmojiList(+(payload.count ?? 48));
return ret.emojiInfoList.map(e => e.url);
}
}

View File

@@ -27,6 +27,6 @@ export class FetchEmojiLike extends BaseAction<Payload, any> {
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];
return await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer, msg.msgSeq, payload.emojiId, payload.emojiType, parseInt((payload.count || '0').toString()) || 20);
return await NTQQMsgApi.getMsgEmojiLikesList(msgIdPeer.Peer, msg.msgSeq, payload.emojiId, payload.emojiType, +(payload.count ?? 20));
}
}

View File

@@ -18,6 +18,6 @@ export class GetCollectionList extends BaseAction<Payload, any> {
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQCollectionApi = this.CoreContext.apis.CollectionApi;
return await NTQQCollectionApi.getAllCollection(parseInt(payload.category.toString()), parseInt(payload.count.toString()));
return await NTQQCollectionApi.getAllCollection(parseInt(payload.category.toString()), +(payload.count ?? 1));
}
}

View File

@@ -90,7 +90,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
return res;
}
} catch {
this.CoreContext.context.logger.logDebug('GetFileBase Mode - 1 Error');
}
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems;

View File

@@ -1,10 +1,8 @@
import BaseAction from '../BaseAction';
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types';
import { OB11Constructor } from '@/onebot/helper/converter';
import { OB11ForwardMessage, OB11Message, OB11MessageData } from '@/onebot';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { RawNTMsg2Onebot } from '@/onebot/helper';
const SchemaData = {
type: 'object',
@@ -41,7 +39,8 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
}
const msgList = data.msgList;
const messages = (await Promise.all(msgList.map(async msg => {
const resMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg);
const resMsg = await this.OneBotContext.apiContext.MsgApi
.parseMessage(msg);
if (!resMsg) return;
resMsg.message_id = MessageUnique.createMsg({
guildId: '',

View File

@@ -1,11 +1,9 @@
import BaseAction from '../BaseAction';
import { OB11Message } from '../../types';
import { OB11Message } from '@/onebot';
import { ActionName } from '../types';
import { ChatType, RawMessage } from '@/core/entities';
import { OB11Constructor } from '@/onebot/helper/converter';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { RawNTMsg2Onebot } from '@/onebot/helper';
interface Response {
messages: OB11Message[];
@@ -16,7 +14,7 @@ const SchemaData = {
properties: {
user_id: { type: ['number', 'string'] },
message_seq: { type: 'number' },
count: { type: 'number' },
count: { type: ['number', 'string'] },
reverseOrder: { type: 'boolean' },
},
required: ['user_id'],
@@ -34,7 +32,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
//处理参数
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const MsgCount = payload.count || 20;
const MsgCount = +(payload.count ?? 20);
const isReverseOrder = payload.reverseOrder || true;
if (!uid) throw `记录${payload.user_id}不存在`;
const friend = await NTQQFriendApi.isBuddy(uid);
@@ -54,7 +52,9 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
}));
//转换消息
const ob11MsgList = (await Promise.all(msgList.map(msg => RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg);
const ob11MsgList = (await Promise.all(
msgList.map(msg => this.OneBotContext.apiContext.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList };
}
}

View File

@@ -1,11 +1,9 @@
import BaseAction from '../BaseAction';
import { OB11Message } from '../../types';
import { OB11Message } from '@/onebot';
import { ActionName } from '../types';
import { ChatType, Peer, RawMessage } from '@/core/entities';
import { OB11Constructor } from '@/onebot/helper/converter';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { RawNTMsg2Onebot } from '@/onebot/helper';
interface Response {
messages: OB11Message[];
@@ -16,7 +14,7 @@ const SchemaData = {
properties: {
group_id: { type: ['number', 'string'] },
message_seq: { type: 'number' },
count: { type: 'number' },
count: { type: ['number', 'string'] },
reverseOrder: { type: 'boolean' },
},
required: ['group_id'],
@@ -32,7 +30,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
//处理参数
const isReverseOrder = payload.reverseOrder || true;
const MsgCount = payload.count || 20;
const MsgCount = +(payload.count ?? 20);
const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
//拉取消息
let msgList: RawMessage[];
@@ -49,7 +47,9 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
}));
//转换消息
const ob11MsgList = (await Promise.all(msgList.map(msg => RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg);
const ob11MsgList = (await Promise.all(
msgList.map(msg => this.OneBotContext.apiContext.MsgApi.parseMessage(msg)))
).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList };
}
}

View File

@@ -62,8 +62,6 @@ export class SendGroupNotice extends BaseAction<Payload, null> {
if (PublishGroupBulletinResult.result != 0) {
throw `设置群公告失败,错误信息:${PublishGroupBulletinResult.errMsg}`;
}
// 下面实现扬了
//await WebApi.setGroupNotice(payload.group_id, payload.content) ;
return null;
}
}

View File

@@ -24,7 +24,7 @@ export class SetQQProfile extends BaseAction<Payload, any | null> {
const OldProfile = await NTQQUserApi.getUserDetailInfo(self.uid);
const ret = await NTQQUserApi.modifySelfProfile({
nick: payload.nickname,
longNick: payload?.personal_note ?? OldProfile?.longNick!,
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() },
location: undefined,

View File

@@ -1,11 +1,10 @@
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { ChatType, SendFileElement } from '@/core/entities';
import { ChatType } from '@/core/entities';
import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
const SchemaData = {
type: 'object',
@@ -34,7 +33,7 @@ export default class GoCQHTTPUploadGroupFile extends BaseAction<Payload, null> {
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name, payload.folder_id);
const sendFileEle = await this.CoreContext.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name, payload.folder_id);
await sendMsg(this.CoreContext, {
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),

View File

@@ -5,7 +5,6 @@ import fs from 'fs';
import { sendMsg } from '@/onebot/action/msg/SendMsg';
import { uri2local } from '@/common/utils/file';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
const SchemaData = {
type: 'object',
@@ -34,7 +33,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
const isBuddy = await NTQQFriendApi.isBuddy(peerUid);
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
}
throw '缺少参数 user_id';
throw new Error( '缺少参数 user_id');
}
async _handle(payload: Payload): Promise<null> {
@@ -47,7 +46,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
if (!downloadResult.success) {
throw new Error(downloadResult.errMsg);
}
const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(this.CoreContext, downloadResult.path, payload.name);
const sendFileEle: SendFileElement = await this.CoreContext.apis.FileApi.createValidSendFileElement(downloadResult.path, payload.name);
await sendMsg(this.CoreContext, peer, [sendFileEle], [], true);
return null;
}

View File

@@ -0,0 +1,28 @@
import { WebApiGroupNoticeFeed } from '@/core';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
const SchemaData = {
type: 'object',
properties: {
group_id: { type: ['number', 'string'] },
feed_id: { type: 'string' },
},
required: ['group_id','feed_id'],
} as const satisfies JSONSchema;
type Payload = FromSchema<typeof SchemaData>;
export class DelGroupNotice extends BaseAction<Payload, any> {
actionName = ActionName.DelGroupNotice;
PayloadSchema = SchemaData;
async _handle(payload: Payload) {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const group = payload.group_id.toString();
const feedId = payload.feed_id;
return await NTQQGroupApi.deleteGroupBulletin(group, feedId);
}
}

View File

@@ -26,12 +26,12 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const isNocache = typeof payload.no_cache === 'string' ? payload.no_cache === 'true' : !!payload.no_cache;
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
if (!uid) throw (`Uin2Uid Error ${payload.user_id}不存在`);
if (!uid) throw new Error (`Uin2Uid Error ${payload.user_id}不存在`);
const [member, info] = await Promise.allSettled([
NTQQGroupApi.getGroupMemberV2(payload.group_id.toString(), uid, isNocache),
NTQQUserApi.getUserDetailInfo(uid),
]);
if (member.status !== 'fulfilled') throw (`群(${payload.group_id})成员${payload.user_id}不存在 ${member.reason}`);
if (member.status !== 'fulfilled') throw new Error (`群(${payload.group_id})成员${payload.user_id}不存在 ${member.reason}`);
if (info.status === 'fulfilled') {
this.CoreContext.context.logger.logDebug("群成员详细信息结果", info.value);
Object.assign(member, info.value);

View File

@@ -6,6 +6,7 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
interface GroupNotice {
sender_id: number;
publish_time: number;
feed_id: string;
message: {
text: string
image: Array<{
@@ -44,7 +45,8 @@ export class GetGroupNotice extends BaseAction<Payload, GroupNotice[]> {
for (const key in ret.feeds) {
const retApiNotice: WebApiGroupNoticeFeed = ret.feeds[key];
const retNotice: GroupNotice = {
// ...ret.feeds[key],
//...ret.feeds[key],
feed_id: retApiNotice.fid,
sender_id: retApiNotice.u,
publish_time: retApiNotice.pubt,
message: {

View File

@@ -1,3 +1,4 @@
import { GroupNotifyMsgStatus } from '@/core';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
@@ -28,7 +29,7 @@ export class GetGroupSystemMsg extends BaseAction<void, any> {
invitor_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName,
checked: SSNotify.status === 1 ? false : true,
checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
});
} else if (SSNotify.type == 7) {
@@ -38,7 +39,7 @@ export class GetGroupSystemMsg extends BaseAction<void, any> {
requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName,
checked: SSNotify.status === 1 ? false : true,
checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0,
});
}

View File

@@ -25,7 +25,7 @@ export default class SetGroupAddRequest extends BaseAction<Payload, null> {
const approve = payload.approve?.toString() !== 'false';
await NTQQGroupApi.handleGroupRequest(flag,
approve ? GroupRequestOperateTypes.approve : GroupRequestOperateTypes.reject,
payload.reason || ' ',
payload.reason ?? ' ',
);
return null;
}

View File

@@ -71,7 +71,7 @@ import GetRecentContact from './user/GetRecentContact';
import { GetProfileLike } from './extends/GetProfileLike';
import SetGroupPortrait from './go-cqhttp/SetGroupPortrait';
import { FetchCustomFace } from './extends/FetchCustomFace';
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivareFile';
import GoCQHTTPUploadPrivateFile from './go-cqhttp/UploadPrivateFile';
import { FetchEmojiLike } from './extends/FetchEmojiLike';
import { NapCatCore } from '@/core';
@@ -80,6 +80,7 @@ import GetGuildProfile from './guild/GetGuildProfile';
import SetModelShow from './go-cqhttp/SetModelShow';
import { SetInputStatus } from './extends/SetInputStatus';
import { GetCSRF } from './system/GetCSRF';
import { DelGroupNotice } from './group/DelGroupNotice';
export type ActionMap = Map<string, BaseAction<any, any>>;
@@ -169,6 +170,7 @@ export function createActionMap(onebotContext: NapCatOneBot11Adapter, coreContex
new SetModelShow(onebotContext, coreContext),
new SetInputStatus(onebotContext, coreContext),
new GetCSRF(onebotContext, coreContext),
new DelGroupNotice(onebotContext, coreContext),
];
const actionMap = new Map();
for (const action of actionHandlers) {

View File

@@ -1,10 +1,8 @@
import { OB11Message } from '../../types';
import { OB11Constructor } from '@/onebot/helper/converter';
import { OB11Message } from '@/onebot';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { RawNTMsg2Onebot } from '@/onebot/helper';
export type ReturnDataType = OB11Message
@@ -32,19 +30,20 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
const MsgShortId = MessageUnique.getShortIdByMsgId(payload.message_id.toString());
const msgIdWithPeer = MessageUnique.getMsgIdAndPeerByShortId(MsgShortId || parseInt(payload.message_id.toString()));
if (!msgIdWithPeer) {
throw ('消息不存在');
throw new Error('消息不存在');
}
const peer = { guildId: '', peerUid: msgIdWithPeer?.Peer.peerUid, chatType: msgIdWithPeer.Peer.chatType };
const msg = await NTQQMsgApi.getMsgsByMsgId(
peer,
[msgIdWithPeer?.MsgId || payload.message_id.toString()]);
const retMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg.msgList[0], 'array');
const retMsg = await this.OneBotContext.apiContext.MsgApi.parseMessage(msg.msgList[0], 'array');
if (!retMsg) throw Error('消息为空');
try {
retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!;
retMsg.message_seq = retMsg.message_id;
retMsg.real_id = retMsg.message_id;
} catch (e) {
// ignored
}
return retMsg;
}

View File

@@ -26,7 +26,7 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
return { chatType: isBuddy ? ChatType.KCHATTYPEC2C : ChatType.KCHATTYPETEMPC2CFROMGROUP, peerUid };
}
if (!payload.group_id) {
throw '缺少参数 group_id 或 user_id';
throw new Error( '缺少参数 group_id 或 user_id');
}
return { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() };
}
@@ -36,7 +36,7 @@ class MarkMsgAsRead extends BaseAction<PlayloadType, null> {
// 调用API
const ret = await NTQQMsgApi.setMsgRead(await this.getPeer(payload));
if (ret.result != 0) {
throw ('设置已读失败,' + ret.errMsg);
throw new Error('设置已读失败,' + ret.errMsg);
}
return null;
}

View File

@@ -9,11 +9,9 @@ import { ActionName, BaseCheckResult } from '@/onebot/action/types';
import fs from 'node:fs';
import fsPromise from 'node:fs/promises';
import { decodeCQCode } from '@/onebot/helper/cqcode';
import createSendElements from './create-send-elements';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { ChatType, ElementType, NapCatCore, Peer, SendMessageElement } from '@/core';
import BaseAction from '../../BaseAction';
import { handleForwardNode } from './handle-forward-node';
import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendMessageElement } from '@/core';
import BaseAction from '../BaseAction';
export interface ReturnDataType {
message_id: number;
@@ -34,13 +32,11 @@ export function normalize(message: OB11MessageMixType, autoEscape = false): OB11
) : Array.isArray(message) ? message : [message];
}
export { createSendElements };
export async function sendMsg(coreContext: NapCatCore, peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[], waitComplete = true) {
const NTQQMsgApi = coreContext.apis.MsgApi;
const logger = coreContext.context.logger;
if (!sendElements.length) {
throw ('消息体无法解析, 请检查是否发送了不支持的消息类型');
throw new Error('消息体无法解析, 请检查是否发送了不支持的消息类型');
}
let totalSize = 0;
let timeout = 10000;
@@ -99,7 +95,7 @@ async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg,
}
if ((contextMode === ContextMode.Private || contextMode === ContextMode.Normal) && payload.user_id) {
const Uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
if (!Uid) throw '无法获取用户信息';
if (!Uid) throw new Error('无法获取用户信息');
const isBuddy = await NTQQFriendApi.isBuddy(Uid);
if (!isBuddy) {
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, Uid);
@@ -129,7 +125,7 @@ async function createContext(coreContext: NapCatCore, payload: OB11PostSendMsg,
guildId: '',
};
}
throw '请指定 group_id 或 user_id';
throw new Error('请指定 group_id 或 user_id');
}
function getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType): number {
@@ -144,9 +140,6 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
contextMode = ContextMode.Normal;
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
const NTQQGroupApi = this.CoreContext.apis.GroupApi;
const NTQQFriendApi = this.CoreContext.apis.FriendApi;
const NTQQUserApi = this.CoreContext.apis.UserApi;
const messages = normalize(payload.message);
const nodeElementLength = getSpecialMsgNum(payload, OB11MessageDataType.node);
if (nodeElementLength > 0 && nodeElementLength != messages.length) {
@@ -156,9 +149,9 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
};
}
if (payload.user_id && payload.message_type !== 'group') {
const uid = await NTQQUserApi.getUidByUinV2(payload.user_id.toString());
const isBuddy = await NTQQFriendApi.isBuddy(uid!);
if (!isBuddy) { }
// const uid = await this.CoreContext.apis.UserApi.getUidByUinV2(payload.user_id.toString());
// const isBuddy = await NTQQFriendApi.isBuddy(uid!);
// if (!isBuddy) { }
}
return { valid: true };
}
@@ -174,7 +167,7 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
);
if (getSpecialMsgNum(payload, OB11MessageDataType.node)) {
const returnMsg = await handleForwardNode(this.CoreContext, this.OneBotContext, peer, messages as OB11MessageNode[]);
const returnMsg = await this.handleForwardedNodes(peer, messages as OB11MessageNode[]);
if (returnMsg) {
const msgShortId = MessageUnique.createMsg({
guildId: '',
@@ -194,11 +187,135 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
}
// log("send msg:", peer, sendElements)
const { sendElements, deleteAfterSentFiles } = await createSendElements(this.CoreContext, this.OneBotContext, messages, peer);
//console.log(peer, JSON.stringify(sendElements,null,2));
const { sendElements, deleteAfterSentFiles } = await this.OneBotContext.apiContext.MsgApi
.createSendElements(messages, peer);
const returnMsg = await sendMsg(this.CoreContext, peer, sendElements, deleteAfterSentFiles);
return { message_id: returnMsg!.id! };
}
private async handleForwardedNodes(destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> {
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const selfPeer = {
chatType: ChatType.KCHATTYPEC2C,
peerUid: this.CoreContext.selfInfo.uid,
};
let nodeMsgIds: string[] = [];
const logger = this.CoreContext.context.logger;
for (const messageNode of messageNodes) {
const nodeId = messageNode.data.id;
if (nodeId) {
//对Mgsid和OB11ID混用情况兜底
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
if (!nodeMsg) {
logger.logError('转发消息失败,未找到消息', nodeId);
continue;
}
nodeMsgIds.push(nodeMsg.MsgId);
} else {
// 自定义的消息
try {
const OB11Data = normalize(messageNode.data.content);
//筛选node消息
const isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息
if (isNodeMsg !== 0) {
if (isNodeMsg !== OB11Data.length) {
logger.logError('子消息中包含非node消息 跳过不合法部分');
continue;
}
const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
if (nodeMsg) {
nodeMsgIds.push(nodeMsg.msgId);
MessageUnique.createMsg(selfPeer, nodeMsg.msgId);
}
//完成子卡片生成跳过后续
continue;
}
const { sendElements } = await this.OneBotContext.apiContext.MsgApi
.createSendElements(OB11Data, destPeer);
//拆分消息
const MixElement = sendElements.filter(element => element.elementType !== ElementType.FILE && element.elementType !== ElementType.VIDEO);
const SingleElement = sendElements.filter(element => element.elementType === ElementType.FILE || element.elementType === ElementType.VIDEO).map(e => [e]);
const AllElement: SendMessageElement[][] = [MixElement, ...SingleElement].filter(e => e !== undefined && e.length !== 0);
const MsgNodeList: Promise<RawMessage | undefined>[] = [];
for (const sendElementsSplitElement of AllElement) {
MsgNodeList.push(sendMsg(this.CoreContext, selfPeer, sendElementsSplitElement, [], true).catch(_ => undefined));
}
(await Promise.allSettled(MsgNodeList)).map((result) => {
if (result.status === 'fulfilled' && result.value) {
nodeMsgIds.push(result.value.msgId);
MessageUnique.createMsg(selfPeer, result.value.msgId);
}
});
} catch (e) {
logger.logDebug('生成转发消息节点失败', e);
}
}
}
const nodeMsgArray: Array<RawMessage> = [];
let srcPeer: Peer | undefined = undefined;
let needSendSelf = false;
//检测是否处于同一个Peer 不在同一个peer则全部消息由自身发送
for (const msgId of nodeMsgIds) {
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId);
if (!nodeMsgPeer) {
logger.logError('转发消息失败,未找到消息', msgId);
continue;
}
const nodeMsg = (await NTQQMsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
if (srcPeer.peerUid !== nodeMsg.peerUid) {
needSendSelf = true;
}
nodeMsgArray.push(nodeMsg);
}
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
let retMsgIds: string[] = [];
if (needSendSelf) {
for (const [, msg] of nodeMsgArray.entries()) {
if (msg.peerUid === this.CoreContext.selfInfo.uid) {
retMsgIds.push(msg.msgId);
continue;
}
const ClonedMsg = await this.cloneMsg(msg);
if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId);
}
} else {
retMsgIds = nodeMsgIds;
}
if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
try {
logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds);
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds);
} catch (e) {
logger.logError('forward failed', e);
return null;
}
}
async cloneMsg(msg: RawMessage): Promise<RawMessage | undefined> {
const selfPeer = {
chatType: ChatType.KCHATTYPEC2C,
peerUid: this.CoreContext.selfInfo.uid,
};
const logger = this.CoreContext.context.logger;
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
//msg 为待克隆消息
const sendElements: SendMessageElement[] = [];
for (const element of msg.elements) {
sendElements.push(element as SendMessageElement);
}
if (sendElements.length === 0) {
logger.logDebug('需要clone的消息无法解析将会忽略掉', msg);
}
try {
return await NTQQMsgApi.sendMsg(selfPeer, sendElements, true);
} catch (e) {
logger.logError(e, '克隆转发消息失败,将忽略本条消息', msg);
}
}
}
export default SendMsg;

View File

@@ -1,36 +0,0 @@
import { OB11MessageData } from '@/onebot/types';
function checkSendMessage(sendMsgList: OB11MessageData[]) {
function checkUri(uri: string): boolean {
const pattern = /^(file:\/\/|http:\/\/|https:\/\/|base64:\/\/)/;
return pattern.test(uri);
}
for (const msg of sendMsgList) {
if (msg['type'] && msg['data']) {
const type = msg['type'];
const data = msg['data'];
if (type === 'text' && !data['text']) {
return 400;
} else if (['image', 'voice', 'record'].includes(type)) {
if (!data['file']) {
return 400;
} else {
if (checkUri(data['file'])) {
return 200;
} else {
return 400;
}
}
} else if (type === 'at' && !data['qq']) {
return 400;
} else if (type === 'reply' && !data['id']) {
return 400;
}
} else {
return 400;
}
}
return 200;
}

View File

@@ -1,262 +0,0 @@
import { OB11MessageData, OB11MessageDataType, OB11MessageFileBase } from '@/onebot/types';
import { uri2local } from '@/common/utils/file';
import { RequestUtil } from '@/common/utils/request';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { AtType, ChatType, CustomMusicSignPostData, IdMusicSignPostData, NapCatCore, Peer, SendMessageElement } from '@/core';
import { SendMsgElementConstructor } from '@/onebot/helper/genMessage';
import { NapCatOneBot11Adapter } from '@/onebot';
export type MessageContext = {
deleteAfterSentFiles: string[],
peer: Peer
}
async function handleOb11FileLikeMessage(
coreContext: NapCatCore,
obContext: NapCatOneBot11Adapter,
{ data: inputdata }: OB11MessageFileBase,
{ deleteAfterSentFiles }: MessageContext,
) {
//inputdata?.url || inputdata.file
const isBlankUrl = !inputdata.url || inputdata.url === '';
const isBlankFile = !inputdata.file || inputdata.file === '';
if (isBlankUrl && isBlankFile) {
coreContext.context.logger.logError('文件消息缺少参数', inputdata);
throw Error('文件消息缺少参数');
}
const fileOrUrl = (isBlankUrl ? inputdata.file : inputdata.url) || "";
const {
path,
isLocal,
fileName,
errMsg,
success,
} = (await uri2local(coreContext.NapCatTempPath, fileOrUrl));
if (!success) {
coreContext.context.logger.logError('文件下载失败', errMsg);
throw Error('文件下载失败' + errMsg);
}
if (!isLocal) { // 只删除http和base64转过来的文件
deleteAfterSentFiles.push(path);
}
return { path, fileName: inputdata.name || fileName };
}
const _handlers: {
[Key in OB11MessageDataType]: (
CoreContext: NapCatCore,
obContext: NapCatOneBot11Adapter,
sendMsg: Extract<OB11MessageData, { type: Key }>,
// This picks the correct message type out
// How great the type system of TypeScript is!
context: MessageContext,
) => Promise<SendMessageElement | undefined>
} = {
[OB11MessageDataType.text]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { text } }) => SendMsgElementConstructor.text(coreContext, text),
[OB11MessageDataType.at]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { qq: atQQ } }, context) => {
if (!context.peer || context.peer.chatType == ChatType.KCHATTYPEC2C) return undefined;
if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员');
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQUserApi = coreContext.apis.UserApi;
const atMember = await NTQQGroupApi.getGroupMember(context.peer.peerUid, atQQ);
if (atMember) {
return SendMsgElementConstructor.at(coreContext, atQQ, atMember.uid, AtType.atUser, atMember.nick || atMember.cardName);
}
const uid = await NTQQUserApi.getUidByUinV2(`${atQQ}`);
if (!uid) throw new Error('Get Uid Error');
let info = await NTQQUserApi.getUserDetailInfo(uid);
return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, info.nick || '');
},
[OB11MessageDataType.reply]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { id } }) => {
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
if (!replyMsgM) {
coreContext.context.logger.logWarn('回复消息不存在', id);
return undefined;
}
const NTQQMsgApi = coreContext.apis.MsgApi;
const replyMsg = (await NTQQMsgApi.getMsgsByMsgId(
replyMsgM.Peer, [replyMsgM.MsgId!])).msgList[0];
return replyMsg ?
SendMsgElementConstructor.reply(coreContext, replyMsg.msgSeq, replyMsg.msgId, replyMsg.senderUin!, replyMsg.senderUin!) :
undefined;
},
[OB11MessageDataType.face]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { id } }) => SendMsgElementConstructor.face(coreContext, parseInt(id)),
[OB11MessageDataType.mface]: async (coreContext, obContext: NapCatOneBot11Adapter, {
data: {
emoji_package_id, emoji_id, key, summary,
},
}) => SendMsgElementConstructor.mface(coreContext, emoji_package_id, emoji_id, key, summary),
// File service
[OB11MessageDataType.image]: async (coreContext, obContext: NapCatOneBot11Adapter, sendMsg, context) => {
const PicEle = await SendMsgElementConstructor.pic(
coreContext,
(await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context)).path,
sendMsg.data.summary || '',
sendMsg.data.subType || 0,
);
context.deleteAfterSentFiles.push(PicEle.picElement.sourcePath);
return PicEle;
}, // currently not supported
[OB11MessageDataType.file]: async (coreContext, obContext: NapCatOneBot11Adapter, sendMsg, context) => {
const { path, fileName } = await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context);
//logDebug('发送文件', path, fileName);
const FileEle = await SendMsgElementConstructor.file(coreContext, path, fileName);
// 清除Upload的应该
// context.deleteAfterSentFiles.push(fileName || FileEle.fileElement.filePath);
return FileEle;
},
[OB11MessageDataType.video]: async (coreContext, obContext, sendMsg, context) => {
const { path, fileName } = await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context);
//logDebug('发送视频', path, fileName);
let thumb = sendMsg.data.thumb;
if (thumb) {
const uri2LocalRes = await uri2local(coreContext.NapCatTempPath, thumb);
if (uri2LocalRes.success) thumb = uri2LocalRes.path;
}
const videoEle = await SendMsgElementConstructor.video(coreContext, path, fileName, thumb);
//未测试
context.deleteAfterSentFiles.push(videoEle.videoElement.filePath);
return videoEle;
},
[OB11MessageDataType.voice]: async (coreContext, obContext: NapCatOneBot11Adapter, sendMsg, context) => SendMsgElementConstructor.ptt(coreContext, (await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context)).path),
[OB11MessageDataType.json]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { data } }) => SendMsgElementConstructor.ark(coreContext, data),
[OB11MessageDataType.dice]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { result } }) => SendMsgElementConstructor.dice(coreContext, result),
[OB11MessageDataType.RPS]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { result } }) => SendMsgElementConstructor.rps(coreContext, result),
[OB11MessageDataType.markdown]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { content } }) => SendMsgElementConstructor.markdown(coreContext, content),
[OB11MessageDataType.music]: async (coreContext, obContext: NapCatOneBot11Adapter, { data }) => {
// 保留, 直到...找到更好的解决方案
if (data.type === 'custom') {
if (!data.url) {
coreContext.context.logger.logError('自定义音卡缺少参数url');
return undefined;
}
if (!data.audio) {
coreContext.context.logger.logError('自定义音卡缺少参数audio');
return undefined;
}
if (!data.title) {
coreContext.context.logger.logError('自定义音卡缺少参数title');
return undefined;
}
} else {
if (!['qq', '163'].includes(data.type)) {
coreContext.context.logger.logError('音乐卡片type错误, 只支持qq、163、custom当前type:', data.type);
return undefined;
}
if (!data.id) {
coreContext.context.logger.logError('音乐卡片缺少参数id');
return undefined;
}
}
let postData: IdMusicSignPostData | CustomMusicSignPostData;
if (data.type === 'custom' && data.content) {
const { content, ...others } = data;
postData = { singer: content, ...others };
} else {
postData = data;
}
// Mlikiowa V2.2.5 Refactor Todo
const signUrl = obContext.configLoader.configData.musicSignUrl;
if (!signUrl) {
if (data.type === 'qq') {
//const musicJson = (await SignMusicWrapper(data.id.toString())).data.arkResult.slice(0, -1);
//return SendMsgElementConstructor.ark(musicJson);
}
throw Error('音乐消息签名地址未配置');
}
try {
const musicJson = await RequestUtil.HttpGetJson<any>(signUrl, 'POST', postData);
return SendMsgElementConstructor.ark(coreContext, musicJson);
} catch (e) {
coreContext.context.logger.logError('生成音乐消息失败', e);
}
},
[OB11MessageDataType.node]: async (coreContext, obContext: NapCatOneBot11Adapter) => undefined,
[OB11MessageDataType.forward]: async (coreContext, obContext: NapCatOneBot11Adapter) => undefined,
[OB11MessageDataType.xml]: async (coreContext, obContext: NapCatOneBot11Adapter) => undefined,
[OB11MessageDataType.poke]: async (coreContext, obContext: NapCatOneBot11Adapter) => undefined,
[OB11MessageDataType.Location]: async (coreContext, obContext: NapCatOneBot11Adapter) => {
return SendMsgElementConstructor.location(coreContext);
},
[OB11MessageDataType.miniapp]: function (CoreContext: NapCatCore, obContext: NapCatOneBot11Adapter, sendMsg: never, context: MessageContext): Promise<SendMessageElement | undefined> {
throw new Error('Function not implemented.');
},
};
const handlers = <{
[Key in OB11MessageDataType]: (
coreContext: NapCatCore,
obContext: NapCatOneBot11Adapter,
sendMsg: OB11MessageData,
context: MessageContext,
) => Promise<SendMessageElement | undefined>
}>_handlers;
export default async function createSendElements(
CoreContext: NapCatCore,
obContext: NapCatOneBot11Adapter,
messageData: OB11MessageData[],
peer: Peer,
ignoreTypes: OB11MessageDataType[] = [],
) {
const deleteAfterSentFiles: string[] = [];
const callResultList: Array<Promise<SendMessageElement | undefined>> = [];
for (const sendMsg of messageData) {
if (ignoreTypes.includes(sendMsg.type)) {
continue;
}
const callResult = handlers[sendMsg.type](
CoreContext,
obContext,
sendMsg,
{ peer, deleteAfterSentFiles },
)?.catch(undefined);
callResultList.push(callResult);
}
const ret = await Promise.all(callResultList);
const sendElements: SendMessageElement[] = ret.filter(ele => !!ele);
return { sendElements, deleteAfterSentFiles };
}
export async function createSendElementsParallel(
CoreContext: NapCatCore,
obContext: NapCatOneBot11Adapter,
messageData: OB11MessageData[],
peer: Peer,
ignoreTypes: OB11MessageDataType[] = [],
) {
const deleteAfterSentFiles: string[] = [];
const sendElements = <SendMessageElement[]>(
await Promise.all(
messageData.map(async sendMsg => ignoreTypes.includes(sendMsg.type) ?
undefined :
handlers[sendMsg.type](CoreContext, obContext, sendMsg, { peer, deleteAfterSentFiles })),
).then(
results => results.filter(
element => element !== undefined,
),
)
);
return { sendElements, deleteAfterSentFiles };
}

View File

@@ -1,129 +0,0 @@
import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendMessageElement } from '@/core';
import { MessageUnique } from '@/common/utils/MessageUnique';
import { OB11MessageDataType, OB11MessageNode } from '@/onebot/types';
import createSendElements from './create-send-elements';
import { normalize, sendMsg } from '../SendMsg/index';
import { NapCatOneBot11Adapter } from '@/onebot';
async function cloneMsg(coreContext: NapCatCore, msg: RawMessage): Promise<RawMessage | undefined> {
const selfPeer = {
chatType: ChatType.KCHATTYPEC2C,
peerUid: coreContext.selfInfo.uid,
};
const logger = coreContext.context.logger;
const NTQQMsgApi = coreContext.apis.MsgApi;
//logDebug('克隆的目标消息', msg);
const sendElements: SendMessageElement[] = [];
for (const element of msg.elements) {
sendElements.push(element as SendMessageElement);
}
if (sendElements.length === 0) {
logger.logDebug('需要clone的消息无法解析将会忽略掉', msg);
}
try {
const nodeMsg = await NTQQMsgApi.sendMsg(selfPeer, sendElements, true);
return nodeMsg;
} catch (e) {
logger.logError(e, '克隆转发消息失败,将忽略本条消息', msg);
}
}
export async function handleForwardNode(coreContext: NapCatCore, obContext: NapCatOneBot11Adapter, destPeer: Peer, messageNodes: OB11MessageNode[]): Promise<RawMessage | null> {
const NTQQMsgApi = coreContext.apis.MsgApi;
const selfPeer = {
chatType: ChatType.KCHATTYPEC2C,
peerUid: coreContext.selfInfo.uid,
};
let nodeMsgIds: string[] = [];
const logger = coreContext.context.logger;
for (const messageNode of messageNodes) {
const nodeId = messageNode.data.id;
if (nodeId) {
//对Mgsid和OB11ID混用情况兜底
const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId);
if (!nodeMsg) {
logger.logError('转发消息失败,未找到消息', nodeId);
continue;
}
nodeMsgIds.push(nodeMsg.MsgId);
} else {
// 自定义的消息
try {
const OB11Data = normalize(messageNode.data.content);
//筛选node消息
const isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息
if (isNodeMsg !== 0) {
if (isNodeMsg !== OB11Data.length) {
logger.logError('子消息中包含非node消息 跳过不合法部分');
continue;
}
const nodeMsg = await handleForwardNode(coreContext, obContext, selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node));
if (nodeMsg) {
nodeMsgIds.push(nodeMsg.msgId);
MessageUnique.createMsg(selfPeer, nodeMsg.msgId);
}
//完成子卡片生成跳过后续
continue;
}
const { sendElements } = await createSendElements(coreContext, obContext, OB11Data, destPeer);
//拆分消息
const MixElement = sendElements.filter(element => element.elementType !== ElementType.FILE && element.elementType !== ElementType.VIDEO);
const SingleElement = sendElements.filter(element => element.elementType === ElementType.FILE || element.elementType === ElementType.VIDEO).map(e => [e]);
const AllElement: SendMessageElement[][] = [MixElement, ...SingleElement].filter(e => e !== undefined && e.length !== 0);
const MsgNodeList: Promise<RawMessage | undefined>[] = [];
for (const sendElementsSplitElement of AllElement) {
MsgNodeList.push(sendMsg(coreContext, selfPeer, sendElementsSplitElement, [], true).catch(e => new Promise((resolve, reject) => {
resolve(undefined);
})));
}
(await Promise.allSettled(MsgNodeList)).map((result) => {
if (result.status === 'fulfilled' && result.value) {
nodeMsgIds.push(result.value.msgId);
MessageUnique.createMsg(selfPeer, result.value.msgId);
}
});
} catch (e) {
logger.logDebug('生成转发消息节点失败', e);
}
}
}
const nodeMsgArray: Array<RawMessage> = [];
let srcPeer: Peer | undefined = undefined;
let needSendSelf = false;
//检测是否处于同一个Peer 不在同一个peer则全部消息由自身发送
for (const msgId of nodeMsgIds) {
const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId);
if (!nodeMsgPeer) {
logger.logError('转发消息失败,未找到消息', msgId);
continue;
}
const nodeMsg = (await NTQQMsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0];
srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid };
if (srcPeer.peerUid !== nodeMsg.peerUid) {
needSendSelf = true;
}
nodeMsgArray.push(nodeMsg);
}
nodeMsgIds = nodeMsgArray.map(msg => msg.msgId);
let retMsgIds: string[] = [];
if (needSendSelf) {
for (const [index, msg] of nodeMsgArray.entries()) {
if (msg.peerUid === coreContext.selfInfo.uid) continue;
const ClonedMsg = await cloneMsg(coreContext, msg);
if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId);
}
} else {
retMsgIds = nodeMsgIds;
}
if (nodeMsgIds.length === 0) throw Error('转发消息失败,生成节点为空');
try {
logger.logDebug('开发转发', srcPeer, destPeer, nodeMsgIds);
return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, nodeMsgIds);
} catch (e) {
logger.logError('forward failed', e);
return null;
}
}

View File

@@ -109,4 +109,5 @@ export enum ActionName {
SetModelShow = "_set_model_show",
SetInputStatus = "set_input_status",
GetCSRF = "get_csrf_token",
DelGroupNotice = "_del_group_notice",
}

View File

@@ -1,8 +1,6 @@
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
import BaseAction from '../BaseAction';
import { ActionName } from '../types';
import { OB11Constructor } from '@/onebot/helper/converter';
import { RawNTMsg2Onebot } from '@/onebot/helper';
const SchemaData = {
type: 'object',
@@ -20,12 +18,12 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
async _handle(payload: Payload) {
const NTQQUserApi = this.CoreContext.apis.UserApi;
const NTQQMsgApi = this.CoreContext.apis.MsgApi;
const ret = await NTQQUserApi.getRecentContactListSnapShot(parseInt((payload.count || 10).toString()));
const data = await Promise.all(ret.info.changedList.map(async (t) => {
const ret = await NTQQUserApi.getRecentContactListSnapShot(+(payload.count || 10));
return await Promise.all(ret.info.changedList.map(async (t) => {
const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
if (FastMsg.msgList.length > 0) {
//扩展ret.info.changedList
const lastestMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext,FastMsg.msgList[0], 'array');
const lastestMsg = await this.OneBotContext.apiContext.MsgApi.parseMessage(FastMsg.msgList[0], 'array');
return {
lastestMsg: lastestMsg,
peerUin: t.peerUin,
@@ -49,6 +47,5 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
peerName: t.peerName,
};
}));
return data;
}
}

View File

@@ -75,7 +75,7 @@ export class OneBotGroupApi {
const NTQQGroupApi = this.coreContext.apis.GroupApi;
const groupElement = grayTipElement?.groupElement;
if (!groupElement) return undefined;
const member = await NTQQGroupApi.getGroupMember(GroupCode, groupElement.memberUid);
const member = await NTQQGroupApi.getGroupMemberV2(GroupCode, groupElement.memberUid);
const memberUin = member?.uin;
const adminMember = await NTQQGroupApi.getGroupMember(GroupCode, groupElement.adminUid);
if (memberUin) {

File diff suppressed because it is too large Load Diff

View File

@@ -94,7 +94,7 @@ export async function NT2GroupEvent(core: NapCatCore, obContext: NapCatOneBot11A
if (emojiLikeEvent) return emojiLikeEvent;
}
if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) {
const GroupIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement);
const GroupIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupIncreaseEvent(msg.peerUid, element.grayTipElement);
if (GroupIncreaseEvent) return GroupIncreaseEvent;
}

View File

@@ -1,422 +0,0 @@
import {
AtType,
ElementType,
FaceIndex,
FaceType,
NapCatCore,
PicType,
SendArkElement,
SendFaceElement,
SendFileElement,
SendMarkdownElement,
SendMarketFaceElement,
SendPicElement,
SendPttElement,
SendReplyElement,
sendShareLocationElement,
SendTextElement,
SendVideoElement,
viedo_type,
} from '@/core';
import * as fsnormal from 'node:fs';
import { promises as fs } from 'node:fs';
import ffmpeg from 'fluent-ffmpeg';
import { calculateFileMD5, isGIF } from '@/common/utils/file';
import { defaultVideoThumbB64, getVideoInfo } from '@/common/utils/video';
import { encodeSilk } from '@/common/utils/audio';
import faceConfig from '@/core/external/face_config.json';
import * as pathLib from 'node:path';
export class SendMsgElementConstructor {
static location(CoreContext: NapCatCore): sendShareLocationElement {
return {
elementType: ElementType.SHARELOCATION,
elementId: '',
shareLocationElement: {
text: '测试',
ext: '',
},
};
}
static text(CoreContext: NapCatCore, content: string): SendTextElement {
return {
elementType: ElementType.TEXT,
elementId: '',
textElement: {
content,
atType: AtType.notAt,
atUid: '',
atTinyId: '',
atNtUid: '',
},
};
}
static at(CoreContext: NapCatCore, atUid: string, atNtUid: string, atType: AtType, atName: string): SendTextElement {
return {
elementType: ElementType.TEXT,
elementId: '',
textElement: {
content: `@${atName}`,
atType,
atUid,
atTinyId: '',
atNtUid,
},
};
}
static reply(CoreContext: NapCatCore, msgSeq: string, msgId: string, senderUin: string, senderUinStr: string): SendReplyElement {
return {
elementType: ElementType.REPLY,
elementId: '',
replyElement: {
replayMsgSeq: msgSeq, // raw.msgSeq
replayMsgId: msgId, // raw.msgId
senderUin: senderUin,
senderUinStr: senderUinStr,
},
};
}
static async pic(coreContext: NapCatCore, picPath: string, summary: string = '', subType: 0 | 1 = 0): Promise<SendPicElement> {
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQUserApi = coreContext.apis.UserApi;
const NTQQFileApi = coreContext.apis.FileApi;
const NTQQMsgApi = coreContext.apis.MsgApi;
const NTQQFriendApi = coreContext.apis.FriendApi;
const logger = coreContext.context.logger;
const { md5, fileName, path, fileSize } = await NTQQFileApi.uploadFile(picPath, ElementType.PIC, subType);
if (fileSize === 0) {
throw '文件异常大小为0';
}
const imageSize = await NTQQFileApi.getImageSize(picPath);
const picElement: any = {
md5HexStr: md5,
fileSize: fileSize.toString(),
picWidth: imageSize?.width,
picHeight: imageSize?.height,
fileName: fileName,
sourcePath: path,
original: true,
picType: isGIF(picPath) ? PicType.gif : PicType.jpg,
picSubType: subType,
fileUuid: '',
fileSubId: '',
thumbFileSize: 0,
summary,
};
//logDebug('图片信息', picElement);
return {
elementType: ElementType.PIC,
elementId: '',
picElement,
};
}
static async file(coreContext: NapCatCore, filePath: string, fileName: string = '', folderId: string = ''): Promise<SendFileElement> {
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQUserApi = coreContext.apis.UserApi;
const NTQQFileApi = coreContext.apis.FileApi;
const NTQQMsgApi = coreContext.apis.MsgApi;
const NTQQFriendApi = coreContext.apis.FriendApi;
const logger = coreContext.context.logger;
const { md5, fileName: _fileName, path, fileSize } = await NTQQFileApi.uploadFile(filePath, ElementType.FILE);
if (fileSize === 0) {
throw '文件异常大小为0';
}
const element: SendFileElement = {
elementType: ElementType.FILE,
elementId: '',
fileElement: {
fileName: fileName || _fileName,
folderId: folderId,
'filePath': path!,
'fileSize': (fileSize).toString(),
},
};
return element;
}
static async video(coreContext: NapCatCore, filePath: string, fileName: string = '', diyThumbPath: string = '', videotype: viedo_type = viedo_type.VIDEO_FORMAT_MP4): Promise<SendVideoElement> {
const NTQQFileApi = coreContext.apis.FileApi;
const logger = coreContext.context.logger;
const { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO);
if (fileSize === 0) {
throw '文件异常大小为0';
}
let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
thumb = pathLib.dirname(thumb);
// log("thumb 目录", thumb)
let videoInfo = {
width: 1920, height: 1080,
time: 15,
format: 'mp4',
size: fileSize,
filePath,
};
try {
videoInfo = await getVideoInfo(path, logger);
//logDebug('视频信息', videoInfo);
} catch (e) {
logger.logError('获取视频信息失败', e);
}
const createThumb = new Promise<string | undefined>((resolve, reject) => {
const thumbFileName = `${md5}_0.png`;
const thumbPath = pathLib.join(thumb, thumbFileName);
ffmpeg(filePath)
.on('end', () => {
})
.on('error', (err) => {
logger.logDebug('获取视频封面失败,使用默认封面', err);
if (diyThumbPath) {
fs.copyFile(diyThumbPath, thumbPath).then(() => {
resolve(thumbPath);
}).catch(reject);
} else {
fsnormal.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
resolve(thumbPath);
}
})
.screenshots({
timestamps: [0],
filename: thumbFileName,
folder: thumb,
size: videoInfo.width + 'x' + videoInfo.height,
}).on('end', () => {
resolve(thumbPath);
});
});
const thumbPath = new Map();
const _thumbPath = await createThumb;
const thumbSize = _thumbPath ? (await fs.stat(_thumbPath)).size : 0;
// log("生成缩略图", _thumbPath)
thumbPath.set(0, _thumbPath);
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : "";
const element: SendVideoElement = {
elementType: ElementType.VIDEO,
elementId: '',
videoElement: {
fileName: fileName || _fileName,
filePath: path,
videoMd5: md5,
thumbMd5,
fileTime: videoInfo.time,
thumbPath: thumbPath,
thumbSize,
thumbWidth: videoInfo.width,
thumbHeight: videoInfo.height,
fileSize: '' + fileSize,
//fileFormat: videotype
// fileUuid: "",
// transferStatus: 0,
// progress: 0,
// invalidState: 0,
// fileSubId: "",
// fileBizId: null,
// originVideoMd5: "",
// fileFormat: 2,
// import_rich_media_context: null,
// sourceVideoCodecFormat: 2
},
};
// "fileElement": {
// "fileMd5": "",
// "fileName": "1.mp4",
// "filePath": "C:\\Users\\nanae\\OneDrive\\Desktop\\1.mp4",
// "fileSize": "1847007",
// "picHeight": 1280,
// "picWidth": 720,
// "picThumbPath": {},
// "file10MMd5": "",
// "fileSha": "",
// "fileSha3": "",
// "fileUuid": "",
// "fileSubId": "",
// "thumbFileSize": 750
// }
return element;
}
static async ptt(coreContext: NapCatCore, pttPath: string): Promise<SendPttElement> {
const NTQQGroupApi = coreContext.apis.GroupApi;
const NTQQUserApi = coreContext.apis.UserApi;
const NTQQFileApi = coreContext.apis.FileApi;
const NTQQMsgApi = coreContext.apis.MsgApi;
const NTQQFriendApi = coreContext.apis.FriendApi;
const logger = coreContext.context.logger;
const {
converted,
path: silkPath,
duration,
} = await encodeSilk(pttPath, coreContext.NapCatTempPath, coreContext.context.logger);
// log("生成语音", silkPath, duration);
if (!silkPath) {
throw '语音转换失败, 请检查语音文件是否正常';
}
const { md5, fileName, path, fileSize } = await NTQQFileApi.uploadFile(silkPath!, ElementType.PTT);
if (fileSize === 0) {
throw '文件异常大小为0';
}
if (converted) {
fs.unlink(silkPath).then();
}
return {
elementType: ElementType.PTT,
elementId: '',
pttElement: {
fileName: fileName,
filePath: path,
md5HexStr: md5,
fileSize: fileSize,
// duration: Math.max(1, Math.round(fileSize / 1024 / 3)), // 一秒钟大概是3kb大小, 小于1秒的按1秒算
duration: duration || 1,
formatType: 1,
voiceType: 1,
voiceChangeType: 0,
canConvert2Text: true,
waveAmplitudes: [
0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17,
],
fileSubId: '',
playState: 1,
autoConvertText: 0,
},
};
}
// NodeIQQNTWrapperSession sendMsg [
// "0",
// {
// "peerUid": "u_e_RIxgTs2NaJ68h0PwOPSg",
// "chatType": 1,
// "guildId": ""
// },
// [
// {
// "elementId": "0",
// "elementType": 6,
// "faceElement": {
// "faceIndex": 0,
// "faceType": 5,
// "msgType": 0,
// "pokeType": 1,
// "pokeStrength": 0
// }
// }
// ],
// {}
// ]
static face(CoreContext: NapCatCore, faceId: number): SendFaceElement {
// 从face_config.json中获取表情名称
const sysFaces = faceConfig.sysface;
const emojiFaces = faceConfig.emoji;
const face: any = sysFaces.find((face) => face.QSid === faceId.toString());
faceId = parseInt(faceId.toString());
// let faceType = parseInt(faceId.toString().substring(0, 1));
let faceType = 1;
if (faceId >= 222) {
faceType = 2;
}
if (face.AniStickerType) {
faceType = 3;
}
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
faceIndex: faceId,
faceType,
faceText: face.QDes,
stickerId: face.AniStickerId,
stickerType: face.AniStickerType,
packId: face.AniStickerPackId,
sourceType: 1,
},
};
}
static mface(CoreContext: NapCatCore, emojiPackageId: number, emojiId: string, key: string, faceName: string): SendMarketFaceElement {
return {
elementType: ElementType.MFACE,
marketFaceElement: {
emojiPackageId,
emojiId,
key,
faceName: faceName || '[商城表情]',
},
};
}
static dice(CoreContext: NapCatCore, resultId: number | null): SendFaceElement {
// 实际测试并不能控制结果
// 随机1到6
// if (isNull(resultId)) resultId = Math.floor(Math.random() * 6) + 1;
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
faceIndex: FaceIndex.dice,
faceType: FaceType.dice,
'faceText': '[骰子]',
'packId': '1',
'stickerId': '33',
'sourceType': 1,
'stickerType': 2,
// resultId: resultId.toString(),
'surpriseId': '',
// "randomType": 1,
},
};
}
// 猜拳(石头剪刀布)表情
static rps(CoreContext: NapCatCore, resultId: number | null): SendFaceElement {
// 实际测试并不能控制结果
// if (isNull(resultId)) resultId = Math.floor(Math.random() * 3) + 1;
return {
elementType: ElementType.FACE,
elementId: '',
faceElement: {
'faceIndex': FaceIndex.RPS,
'faceText': '[包剪锤]',
'faceType': 3,
'packId': '1',
'stickerId': '34',
'sourceType': 1,
'stickerType': 2,
// 'resultId': resultId.toString(),
'surpriseId': '',
// "randomType": 1,
},
};
}
static ark(CoreContext: NapCatCore, data: any): SendArkElement {
if (typeof data !== 'string') {
data = JSON.stringify(data);
}
return {
elementType: ElementType.ARK,
elementId: '',
arkElement: {
bytesData: data,
linkInfo: null,
subElementType: null,
},
};
}
static markdown(CoreContext: NapCatCore, content: string): SendMarkdownElement {
return {
elementType: ElementType.MARKDOWN,
elementId: '',
markdownElement: {
content,
},
};
}
}

View File

@@ -1,6 +1,4 @@
export * from './config';
export * from './parseMessage';
export * from './converter';
export * from './quick';
export * from './genMessage';
export * from './event';
export * from './event';

View File

@@ -1,117 +0,0 @@
import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataType } from '..';
import { AtType, ChatType, NapCatCore, RawMessage } from '@/core';
import { EventType } from '../event/OB11BaseEvent';
import { OB11Constructor } from './converter';
import { encodeCQCode } from './cqcode';
export async function RawNTMsg2Onebot(
core: NapCatCore,
obcore: NapCatOneBot11Adapter,
msg: RawMessage,
messagePostFormat: string = obcore.configLoader.configData.messagePostFormat
): Promise<OB11Message | undefined> {
if (msg.senderUin == "0" || msg.senderUin == "") return;
if (msg.peerUin == "0" || msg.peerUin == "") return;
//跳过空消息
const NTQQGroupApi = core.apis.GroupApi;
const NTQQUserApi = core.apis.UserApi;
const NTQQMsgApi = core.apis.MsgApi;
const resMsg: OB11Message = {
self_id: parseInt(core.selfInfo.uin),
user_id: parseInt(msg.senderUin!),
time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.id!,
message_seq: msg.id!,
real_id: msg.id!,
message_type: msg.chatType == ChatType.KCHATTYPEGROUP ? 'group' : 'private',
sender: {
user_id: parseInt(msg.senderUin || '0'),
nickname: msg.sendNickName,
card: msg.sendMemberName || '',
},
raw_message: '',
font: 14,
sub_type: 'friend',
message: messagePostFormat === 'string' ? '' : [],
message_format: messagePostFormat === 'string' ? 'string' : 'array',
post_type: core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
};
if (msg.chatType == ChatType.KCHATTYPEGROUP) {
resMsg.sub_type = 'normal'; // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼
resMsg.group_id = parseInt(msg.peerUin);
let member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (!member) member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (member) {
resMsg.sender.role = OB11Constructor.groupMemberRole(member.role);
resMsg.sender.nickname = member.nick;
}
} else if (msg.chatType == ChatType.KCHATTYPEC2C) {
resMsg.sub_type = 'friend';
resMsg.sender.nickname = (await NTQQUserApi.getUserDetailInfo(msg.senderUid)).nick;
} else if (msg.chatType == ChatType.KCHATTYPETEMPC2CFROMGROUP) {
resMsg.sub_type = 'group';
const ret = await NTQQMsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid);
if (ret.result === 0) {
resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode);
resMsg.sender.nickname = ret.tmpChatInfo!.fromNick;
} else {
resMsg.group_id = 284840486; //兜底数据
resMsg.sender.nickname = "临时会话";
}
}
for (const element of msg.elements) {
let message_data: OB11MessageData = {
data: {} as any,
type: 'unknown' as any,
};
if (element.textElement && element.textElement?.atType !== AtType.notAt) {
let textAtMsgData = await obcore.apiContext.MsgApi.parseTextElemntWithAt(msg, element);
if (textAtMsgData) message_data = textAtMsgData
} else if (element.textElement) {
let textMsgData = await obcore.apiContext.MsgApi.parseTextElement(msg, element);
if (textMsgData) message_data = textMsgData;
} else if (element.replyElement) {
let replyMsgData = await obcore.apiContext.MsgApi.parseReplyElement(msg, element);
if (replyMsgData) message_data = replyMsgData;
} else if (element.picElement) {
let PicMsgData = await obcore.apiContext.MsgApi.parsePicElement(msg, element);
if (PicMsgData) message_data = PicMsgData;
} else if (element.fileElement) {
let FileMsgData = await obcore.apiContext.MsgApi.parseFileElement(msg, element);
if (FileMsgData) message_data = FileMsgData;
} else if (element.videoElement) {
let videoMsgData = await obcore.apiContext.MsgApi.parseVideoElement(msg, element);
if (videoMsgData) message_data = videoMsgData;
} else if (element.pttElement) {
let pttMsgData = await obcore.apiContext.MsgApi.parsePTTElement(msg, element);
if (pttMsgData) message_data = pttMsgData;
} else if (element.arkElement) {
let arkMsgData = await obcore.apiContext.MsgApi.parseArkElement(msg, element);
if (arkMsgData) message_data = arkMsgData;
} else if (element.faceElement) {
let faceMsgData = await obcore.apiContext.MsgApi.parseFaceElement(msg, element);
if (faceMsgData) message_data = faceMsgData;
} else if (element.marketFaceElement) {
let marketFaceMsgData = await obcore.apiContext.MsgApi.parseMarketFaceElement(msg, element);
if (marketFaceMsgData) message_data = marketFaceMsgData;
} else if (element.markdownElement) {
message_data['type'] = OB11MessageDataType.markdown;
message_data['data']['data'] = element.markdownElement.content;
} else if (element.multiForwardMsgElement) {
let multiForwardMsgData = await obcore.apiContext.MsgApi.parseMultForwardElement(msg, element, messagePostFormat);
if (multiForwardMsgData) message_data = multiForwardMsgData;
}
if ((message_data.type as string) !== 'unknown' && message_data.data) {
const cqCode = encodeCQCode(message_data);
if (messagePostFormat === 'string') {
(resMsg.message as string) += cqCode;
} else (resMsg.message as OB11MessageData[]).push(message_data);
resMsg.raw_message += cqCode;
}
}
resMsg.raw_message = resMsg.raw_message.trim();
return resMsg;
}

View File

@@ -13,7 +13,7 @@ import {
QuickActionGroupRequest,
} from '../types';
import { isNull } from '@/common/utils/helper';
import { createSendElements, normalize, sendMsg } from '../action/msg/SendMsg';
import { normalize, sendMsg } from '../action/msg/SendMsg';
import { NapCatOneBot11Adapter } from '..';
async function handleMsg(coreContext: NapCatCore, obContext: NapCatOneBot11Adapter, msg: OB11Message, quickAction: QuickAction) {
@@ -53,7 +53,7 @@ async function handleMsg(coreContext: NapCatCore, obContext: NapCatOneBot11Adapt
}
}
replyMessage = replyMessage.concat(normalize(reply, quickAction.auto_escape));
const { sendElements, deleteAfterSentFiles } = await createSendElements(coreContext, obContext, replyMessage, peer);
const { sendElements, deleteAfterSentFiles } = await obContext.apiContext.MsgApi.createSendElements(replyMessage, peer);
sendMsg(coreContext, peer, sendElements, deleteAfterSentFiles, false).then().catch(coreContext.context.logger.logError);
}
}

View File

@@ -1,15 +1,17 @@
import {
BuddyListener,
NodeIKernelBuddyListener,
BuddyReqType,
ChatType,
GroupListener,
GroupNotifyTypes,
InstanceContext,
MsgListener,
NodeIKernelMsgListener,
NapCatCore,
RawMessage,
SendStatusType,
GroupMemberRole,
GroupNotifyMsgType,
GroupNotifyMsgStatus,
DataSource,
NodeIKernelGroupListener,
} from '@/core';
import { OB11Config, OB11ConfigLoader } from '@/onebot/helper/config';
import { OneBotApiContextType } from '@/onebot/types';
@@ -34,7 +36,7 @@ import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent';
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
import { LRUCache } from '@/common/utils/LRU';
import { NT2GroupEvent, NT2PrivateEvent, RawNTMsg2Onebot } from './helper';
import { NT2GroupEvent, NT2PrivateEvent } from './helper';
//OneBot实现类
export class NapCatOneBot11Adapter {
@@ -217,7 +219,7 @@ export class NapCatOneBot11Adapter {
}
private initMsgListener() {
const msgListener = new MsgListener();
const msgListener = new NodeIKernelMsgListener();
msgListener.onInputStatusPush = async data => {
const uin = await this.core.apis.UserApi.getUinByUidV2(data.fromUin);
this.context.logger.log(`[Notice] [输入状态] ${uin} ${data.statusText}`);
@@ -248,15 +250,16 @@ export class NapCatOneBot11Adapter {
}
};
const msgIdSend = new LRUCache<string, boolean>(100);
const recallMsgs = new LRUCache<string, boolean>(100);
msgListener.onMsgInfoListUpdate = async msgList => {
this.emitRecallMsg(msgList)
this.emitRecallMsg(msgList, recallMsgs)
.catch(e => this.context.logger.logError('处理消息失败', e));
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) {
msgIdSend.put(msg.msgId, true);
// 完成后再post
RawNTMsg2Onebot(this.core, this, msg)
this.apiContext.MsgApi.parseMessage(msg)
.then((ob11Msg) => {
if (!ob11Msg) return;
ob11Msg.target_id = parseInt(msg.peerUin);
@@ -281,7 +284,7 @@ export class NapCatOneBot11Adapter {
}
private initBuddyListener() {
const buddyListener = new BuddyListener();
const buddyListener = new NodeIKernelBuddyListener();
buddyListener.onBuddyReqChange = async reqs => {
this.core.apis.FriendApi.clearBuddyReqUnreadCnt();
@@ -316,17 +319,16 @@ export class NapCatOneBot11Adapter {
}
private initGroupListener() {
const groupListener = new GroupListener();
const groupListener = new NodeIKernelGroupListener();
groupListener.onGroupNotifiesUpdated = async (_, notifies) => {
//console.log('ob11 onGroupNotifiesUpdated', notifies[0]);
if (![
GroupNotifyTypes.ADMIN_SET,
GroupNotifyTypes.ADMIN_UNSET,
GroupNotifyTypes.ADMIN_UNSET_OTHER,
GroupNotifyMsgType.SET_ADMIN,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN,
].includes(notifies[0]?.type)) {
for (const notify of notifies) {
notify.time = Date.now();
const notifyTime = parseInt(notify.seq) / 1000 / 1000;
// log(`群通知时间${notifyTime}`, `启动时间${this.bootTime}`);
if (notifyTime < this.bootTime) {
@@ -337,9 +339,9 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug('收到群通知', notify);
if ([
GroupNotifyTypes.ADMIN_SET,
GroupNotifyTypes.ADMIN_UNSET,
GroupNotifyTypes.ADMIN_UNSET_OTHER,
GroupNotifyMsgType.SET_ADMIN,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN,
].includes(notify.type)) {
const member1 = await this.core.apis.GroupApi.getGroupMember(notify.group.groupCode, notify.user1.uid);
this.context.logger.logDebug('有管理员变动通知');
@@ -348,20 +350,21 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug('开始获取变动的管理员');
if (member1) {
this.context.logger.logDebug('变动管理员获取成功');
// member1.role = notify.type == GroupNotifyTypes.ADMIN_SET ? GroupMemberRole.admin : GroupMemberRole.normal;
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
this.core,
parseInt(notify.group.groupCode),
parseInt(member1.uin),
[GroupNotifyTypes.ADMIN_UNSET, GroupNotifyTypes.ADMIN_UNSET_OTHER].includes(notify.type) ? 'unset' : 'set',
[
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN
].includes(notify.type) ? 'unset' : 'set',
);
this.networkManager.emitEvent(groupAdminNoticeEvent)
.catch(e => this.context.logger.logError('处理群管理员变动失败', e));
} else {
this.context.logger.logDebug('获取群通知的成员信息失败', notify, this.core.apis.GroupApi.getGroup(notify.group.groupCode));
}
} else if (notify.type == GroupNotifyTypes.MEMBER_EXIT || notify.type == GroupNotifyTypes.KICK_MEMBER) {
} else if (notify.type == GroupNotifyMsgType.MEMBER_LEAVE_NOTIFY_ADMIN || notify.type == GroupNotifyMsgType.KICK_MEMBER_NOTIFY_ADMIN) {
this.context.logger.logDebug('有成员退出通知', notify);
const member1Uin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!;
let operatorId = member1Uin;
@@ -385,8 +388,8 @@ export class NapCatOneBot11Adapter {
.catch(e => this.context.logger.logError('处理群成员退出失败', e));
// notify.status == 1 表示未处理 2表示处理完成
} else if ([
GroupNotifyTypes.JOIN_REQUEST,
].includes(notify.type) && notify.status == 1) {
GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS,
].includes(notify.type) && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
this.context.logger.logDebug('有加群请求');
try {
let requestUin = (await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid))!;
@@ -406,7 +409,7 @@ export class NapCatOneBot11Adapter {
} catch (e) {
this.context.logger.logError('获取加群人QQ号失败 Uid:', notify.user1.uid, e);
}
} else if (notify.type == GroupNotifyTypes.INVITE_ME && notify.status == 1) {
} else if (notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && notify.status == GroupNotifyMsgStatus.KUNHANDLE) {
this.context.logger.logDebug(`收到邀请我加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent(
this.core,
@@ -423,9 +426,9 @@ export class NapCatOneBot11Adapter {
}
};
groupListener.onMemberInfoChange = async (groupCode, changeType, members) => {
groupListener.onMemberInfoChange = async (groupCode, dataSource, members) => {
//this.context.logger.logDebug('收到群成员信息变动通知', groupCode, changeType);
if (changeType === 0) {
if (dataSource === DataSource.LOCAL) {
const existMembers = this.core.apis.GroupApi.groupMemberCache.get(groupCode);
if (!existMembers) return;
members.forEach((member) => {
@@ -454,7 +457,7 @@ export class NapCatOneBot11Adapter {
private async emitMsg(message: RawMessage) {
const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData;
this.context.logger.logDebug('收到新消息 RawMessage', message);
RawNTMsg2Onebot(this.core, this, message, messagePostFormat).then((ob11Msg) => {
this.apiContext.MsgApi.parseMessage(message, messagePostFormat).then((ob11Msg) => {
if (!ob11Msg) return;
this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
if (debug) {
@@ -491,10 +494,11 @@ export class NapCatOneBot11Adapter {
}).catch(e => this.context.logger.logError('constructPrivateEvent error: ', e));
}
private async emitRecallMsg(msgList: RawMessage[]) {
private async emitRecallMsg(msgList: RawMessage[], cache: LRUCache<string, boolean>) {
for (const message of msgList) {
// log("message update", message.sendStatus, message.msgId, message.msgSeq)
if (message.recallTime != '0') { //todo: 这个判断方法不太好,应该使用灰色消息元素来判断?
if (message.recallTime != '0' && !cache.get(message.msgId)) { //todo: 这个判断方法不太好,应该使用灰色消息元素来判断?
cache.put(message.msgId, true);
// 撤回消息上报
const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId);
if (!oriMessageId) {

View File

@@ -94,7 +94,7 @@ export interface OB11MessageImage extends OB11MessageFileBase {
type: OB11MessageDataType.image
data: OB11MessageFileBase['data'] & {
summary?: string; // 图片摘要
subType?: PicSubType
sub_type?: PicSubType
},
}
@@ -113,7 +113,7 @@ export interface OB11MessageVideo extends OB11MessageFileBase {
export interface OB11MessageAt {
type: OB11MessageDataType.at;
data: {
qq: `${number}` | 'all'
qq: string, // `${number}` | 'all'
name?: string
};
}
@@ -156,20 +156,20 @@ export interface OB11MessageCustomMusic {
export interface OB11MessageJson {
type: OB11MessageDataType.json;
data: { config: { token: string } } & any;
data: { config?: { token: string }, data: string };
}
export interface OB11MessageDice {
type: OB11MessageDataType.dice,
data: {
result: number
result: number /* intended */ | string /* in fact */
}
}
export interface OB11MessageRPS {
type: OB11MessageDataType.RPS,
data: {
result: number
result: number | string
}
}

View File

@@ -1,8 +1,8 @@
import type { SelfInfo } from '@/core/entities';
import { LogWrapper } from '@/common/utils/log';
import { LoginListener, SessionListener } from '@/core/listeners';
import { DependsAdapter, DispatcherAdapter, GlobalAdapter } from '@/core/adapters';
import { NodeIKernelLoginListener, NodeIKernelSessionListener } from '@/core/listeners';
import { NodeIDispatcherAdapter, NodeIDependsAdapter, NodeIGlobalAdapter } from '@/core/adapters';
import { NapCatPathWrapper } from '@/common/framework/napcat';
import {
InstanceContext,
@@ -70,7 +70,7 @@ export async function NCoreInitShell() {
},
thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 },
},
new GlobalAdapter() as any,
new NodeIGlobalAdapter(),
);
loginService.initConfig({
machineId: '',
@@ -93,7 +93,7 @@ export async function NCoreInitShell() {
}
const selfInfo = await new Promise<SelfInfo>((resolve, reject) => {
const loginListener = new LoginListener();
const loginListener = new NodeIKernelLoginListener();
// from constructor
loginListener.onUserLoggedIn = (userid: string) => {
@@ -208,7 +208,7 @@ export async function NCoreInitShell() {
selfInfo.uid,
dataPath,
);
const sessionListener = new SessionListener();
const sessionListener = new NodeIKernelSessionListener();
sessionListener.onSessionInitComplete = (r: unknown) => {
if (r === 0) {
resolve();
@@ -218,8 +218,8 @@ export async function NCoreInitShell() {
};
session.init(
sessionConfig,
new DependsAdapter() as any,
new DispatcherAdapter() as any,
new NodeIDependsAdapter(),
new NodeIDispatcherAdapter(),
sessionListener as any,
);
try {

View File

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

View File

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