mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e3814403e4 | ||
![]() |
3d16d52dd8 | ||
![]() |
1ae47fffb4 | ||
![]() |
4e7096b9e2 | ||
![]() |
8cc9b7f6a7 | ||
![]() |
fb45c1020e | ||
![]() |
e9db4ae8f4 | ||
![]() |
c46ec32bd6 | ||
![]() |
c58a26ed99 | ||
![]() |
a66f5e4971 | ||
![]() |
574c8c6089 | ||
![]() |
67afd95910 | ||
![]() |
f7d0cb0be7 | ||
![]() |
be9b68a0b1 | ||
![]() |
4637414af2 | ||
![]() |
4bd92a72bd | ||
![]() |
a3be26f3e4 | ||
![]() |
675c906cbf | ||
![]() |
6be6023236 | ||
![]() |
42cee0d018 | ||
![]() |
041f725748 | ||
![]() |
0594d61631 | ||
![]() |
15cae6b765 | ||
![]() |
b984116c35 | ||
![]() |
13bda6e3f4 |
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "4.2.61",
|
"version": "4.2.68",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "4.2.61",
|
"version": "4.2.68",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
|
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
|
||||||
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
|
||||||
|
107
src/common/cancel-task.ts
Normal file
107
src/common/cancel-task.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
export type TaskExecutor<T> = (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void, onCancel: (callback: () => void) => void) => void;
|
||||||
|
|
||||||
|
export class CancelableTask<T> {
|
||||||
|
private promise: Promise<T>;
|
||||||
|
private cancelCallback: (() => void) | null = null;
|
||||||
|
private isCanceled = false;
|
||||||
|
private cancelListeners: Array<() => void> = [];
|
||||||
|
|
||||||
|
constructor(executor: TaskExecutor<T>) {
|
||||||
|
this.promise = new Promise<T>((resolve, reject) => {
|
||||||
|
const onCancel = (callback: () => void) => {
|
||||||
|
this.cancelCallback = callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
executor(
|
||||||
|
(value) => {
|
||||||
|
if (!this.isCanceled) {
|
||||||
|
resolve(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(reason) => {
|
||||||
|
if (!this.isCanceled) {
|
||||||
|
reject(reason);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel() {
|
||||||
|
if (this.cancelCallback) {
|
||||||
|
this.cancelCallback();
|
||||||
|
}
|
||||||
|
this.isCanceled = true;
|
||||||
|
this.cancelListeners.forEach(listener => listener());
|
||||||
|
}
|
||||||
|
|
||||||
|
public isTaskCanceled(): boolean {
|
||||||
|
return this.isCanceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onCancel(listener: () => void) {
|
||||||
|
this.cancelListeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
|
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
||||||
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
|
||||||
|
): Promise<TResult1 | TResult2> {
|
||||||
|
return this.promise.then(onfulfilled, onrejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public catch<TResult = never>(
|
||||||
|
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
|
||||||
|
): Promise<T | TResult> {
|
||||||
|
return this.promise.catch(onrejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public finally(onfinally?: (() => void) | undefined | null): Promise<T> {
|
||||||
|
return this.promise.finally(onfinally);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.asyncIterator]() {
|
||||||
|
return {
|
||||||
|
next: () => this.promise.then(value => ({ value, done: true })),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function demoAwait() {
|
||||||
|
const executor: TaskExecutor<number> = (resolve, reject, onCancel) => {
|
||||||
|
let count = 0;
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
count++;
|
||||||
|
console.log(`Task is running... Count: ${count}`);
|
||||||
|
if (count === 5) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
resolve(count);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
onCancel(() => {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
console.log('Task has been canceled.');
|
||||||
|
reject(new Error('Task was canceled'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const task = new CancelableTask(executor);
|
||||||
|
|
||||||
|
task.onCancel(() => {
|
||||||
|
console.log('Cancel listener triggered.');
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
task.cancel(); // 取消任务
|
||||||
|
}, 6000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await task;
|
||||||
|
console.log(`Task completed with result: ${result}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Task failed:', error);
|
||||||
|
}
|
||||||
|
}
|
22
src/common/decorator.ts
Normal file
22
src/common/decorator.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// decoratorAsyncMethod(this,function,wrapper)
|
||||||
|
async function decoratorMethod<T, R>(
|
||||||
|
target: T,
|
||||||
|
method: () => Promise<R>,
|
||||||
|
wrapper: (result: R) => Promise<any>,
|
||||||
|
executeImmediately: boolean = true
|
||||||
|
): Promise<any> {
|
||||||
|
const execute = async () => {
|
||||||
|
try {
|
||||||
|
const result = await method.call(target);
|
||||||
|
return wrapper(result);
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (executeImmediately) {
|
||||||
|
return execute();
|
||||||
|
} else {
|
||||||
|
return execute;
|
||||||
|
}
|
||||||
|
}
|
43
src/common/fall-back.ts
Normal file
43
src/common/fall-back.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
type Handler<T> = () => T | Promise<T>;
|
||||||
|
type Checker<T> = (result: T) => T | Promise<T>;
|
||||||
|
|
||||||
|
export class Fallback<T> {
|
||||||
|
private handlers: Handler<T>[] = [];
|
||||||
|
private checker: Checker<T>;
|
||||||
|
|
||||||
|
constructor(checker?: Checker<T>) {
|
||||||
|
this.checker = checker || (async (result: T) => result);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(handler: Handler<T>): this {
|
||||||
|
this.handlers.push(handler);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行处理程序链
|
||||||
|
async run(): Promise<T> {
|
||||||
|
const errors: Error[] = [];
|
||||||
|
for (const handler of this.handlers) {
|
||||||
|
try {
|
||||||
|
const result = await handler();
|
||||||
|
let data = await this.checker(result);
|
||||||
|
if (data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
errors.push(error instanceof Error ? error : new Error(String(error)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AggregateError(errors, 'All handlers failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class FallbackUtil {
|
||||||
|
static boolchecker<T>(value: T, condition: boolean): T {
|
||||||
|
if (condition) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
throw new Error('Condition is false, throwing error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
import https from 'node:https';
|
import https from 'node:https';
|
||||||
import { napCatVersion } from './version';
|
import { napCatVersion } from './version';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
|
|
||||||
export class UmamiTraceCore {
|
export class UmamiTraceCore {
|
||||||
napcatVersion = napCatVersion;
|
napcatVersion = napCatVersion;
|
||||||
qqversion = '1.0.0';
|
qqversion = '1.0.0';
|
||||||
@@ -13,20 +14,22 @@ export class UmamiTraceCore {
|
|||||||
workname: string = 'default';
|
workname: string = 'default';
|
||||||
bootTime = Date.now();
|
bootTime = Date.now();
|
||||||
cache: string = '';
|
cache: string = '';
|
||||||
|
platform = process.platform;
|
||||||
|
|
||||||
init(qqversion: string, guid: string, workname: string) {
|
init(qqversion: string, guid: string, workname: string) {
|
||||||
this.qqversion = qqversion;
|
this.qqversion = qqversion;
|
||||||
this.workname = workname;
|
this.workname = workname;
|
||||||
let UaList = {
|
const UaList = {
|
||||||
'linux': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/124.0.0.0 Safari/537.36 PTST/240508.140043',
|
linux: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36',
|
||||||
'win32': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.2128.93 Safari/537.36',
|
win32: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.2128.93 Safari/537.36',
|
||||||
'darwin': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36',
|
darwin: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36',
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (process.platform === 'win32') {
|
if (this.platform === 'win32') {
|
||||||
const ntVersion = os.release();
|
const ntVersion = os.release();
|
||||||
UaList.win32 = `Mozilla/5.0 (Windows NT ${ntVersion}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.2128.93 Safari/537.36`;
|
UaList.win32 = `Mozilla/5.0 (Windows NT ${ntVersion}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.2128.93 Safari/537.36`;
|
||||||
} else if (process.platform === 'darwin') {
|
} else if (this.platform === 'darwin') {
|
||||||
const macVersion = os.release();
|
const macVersion = os.release();
|
||||||
UaList.darwin = `Mozilla/5.0 (Macintosh; Intel Mac OS X ${macVersion}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36`;
|
UaList.darwin = `Mozilla/5.0 (Macintosh; Intel Mac OS X ${macVersion}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36`;
|
||||||
}
|
}
|
||||||
@@ -34,7 +37,7 @@ export class UmamiTraceCore {
|
|||||||
this.ua = UaList.win32;
|
this.ua = UaList.win32;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ua = UaList[process.platform as keyof typeof UaList] || UaList.win32;
|
this.ua = UaList[this.platform as keyof typeof UaList] || UaList.win32;
|
||||||
|
|
||||||
this.identifyUser(guid);
|
this.identifyUser(guid);
|
||||||
this.startHeartbeat();
|
this.startHeartbeat();
|
||||||
@@ -47,17 +50,17 @@ export class UmamiTraceCore {
|
|||||||
qq_version: this.qqversion,
|
qq_version: this.qqversion,
|
||||||
napcat_working: this.workname,
|
napcat_working: this.workname,
|
||||||
device_guid: this.guid,
|
device_guid: this.guid,
|
||||||
device_platform: os.platform(),
|
device_platform: this.platform,
|
||||||
device_arch: os.arch(),
|
device_arch: os.arch(),
|
||||||
boot_time: new Date(this.bootTime + 8 * 60 * 60 * 1000).toISOString().replace('T', ' ').substring(0, 19),
|
boot_time: new Date(this.bootTime + 8 * 60 * 60 * 1000).toISOString().replace('T', ' ').substring(0, 19),
|
||||||
sys_time: new Date(Date.now() - os.uptime() * 1000 + 8 * 60 * 60 * 1000).toISOString().replace('T', ' ').substring(0, 19)
|
sys_time: new Date(Date.now() - os.uptime() * 1000 + 8 * 60 * 60 * 1000).toISOString().replace('T', ' ').substring(0, 19),
|
||||||
};
|
};
|
||||||
this.sendEvent(
|
this.sendEvent(
|
||||||
{
|
{
|
||||||
website: this.website,
|
website: this.website,
|
||||||
hostname: this.hostname,
|
hostname: this.hostname,
|
||||||
referrer: this.referrer,
|
referrer: this.referrer,
|
||||||
tittle: 'NapCat ' + this.napcatVersion,
|
title: 'NapCat ' + this.napcatVersion,
|
||||||
url: `/${this.qqversion}/${this.napcatVersion}/${this.workname}/identify`,
|
url: `/${this.qqversion}/${this.napcatVersion}/${this.workname}/identify`,
|
||||||
},
|
},
|
||||||
data,
|
data,
|
||||||
@@ -76,8 +79,8 @@ export class UmamiTraceCore {
|
|||||||
language: language || 'en-US',
|
language: language || 'en-US',
|
||||||
screen: '1920x1080',
|
screen: '1920x1080',
|
||||||
data: {
|
data: {
|
||||||
...data
|
...data,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
this.sendRequest(payload, type);
|
this.sendRequest(payload, type);
|
||||||
}
|
}
|
||||||
@@ -87,7 +90,7 @@ export class UmamiTraceCore {
|
|||||||
website: this.website,
|
website: this.website,
|
||||||
hostname: this.hostname,
|
hostname: this.hostname,
|
||||||
title: 'NapCat ' + this.napcatVersion,
|
title: 'NapCat ' + this.napcatVersion,
|
||||||
url: `/${this.qqversion}/${this.napcatVersion}/${this.workname}/${eventName}` + (!!data ? `/${data}` : ''),
|
url: `/${this.qqversion}/${this.napcatVersion}/${this.workname}/${eventName}` + (data ? `/${data}` : ''),
|
||||||
referrer: this.referrer,
|
referrer: this.referrer,
|
||||||
};
|
};
|
||||||
this.sendRequest(payload);
|
this.sendRequest(payload);
|
||||||
@@ -103,23 +106,39 @@ export class UmamiTraceCore {
|
|||||||
"Host": "umami.napneko.icu",
|
"Host": "umami.napneko.icu",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"User-Agent": this.ua,
|
"User-Agent": this.ua,
|
||||||
...(this.cache ? { 'x-umami-cache': this.cache } : {})
|
...(this.cache ? { 'x-umami-cache': this.filterInvalidChars(this.cache) } : {})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
const request = https.request(options, (res) => {
|
const request = https.request(options, (res) => {
|
||||||
res.on('error', (error) => {
|
let responseData = '';
|
||||||
|
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
responseData += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
if (!this.cache) {
|
||||||
|
this.cache = responseData;
|
||||||
|
console.log('Umami cache:', this.cache);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('error', (error) => {
|
||||||
|
});
|
||||||
});
|
});
|
||||||
res.on('data', (data) => {
|
|
||||||
if (!this.cache) {
|
request.on('error', (error) => {
|
||||||
this.cache = data.toString();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}).on('error', () => { });
|
|
||||||
|
request.write(JSON.stringify({ type, payload }));
|
||||||
request.write(JSON.stringify({ type, payload }));
|
request.end();
|
||||||
request.end();
|
} catch (error) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterInvalidChars(value: string): string {
|
||||||
|
return value.replace(/[^\x00-\x7F]/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
startHeartbeat() {
|
startHeartbeat() {
|
||||||
@@ -142,4 +161,5 @@ export class UmamiTraceCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UmamiTrace = new UmamiTraceCore();
|
export const UmamiTrace = new UmamiTraceCore();
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '4.2.61';
|
export const napCatVersion = '4.2.68';
|
||||||
|
@@ -18,6 +18,7 @@ export class NTQQGroupApi {
|
|||||||
context: InstanceContext;
|
context: InstanceContext;
|
||||||
core: NapCatCore;
|
core: NapCatCore;
|
||||||
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
|
||||||
|
groupMemberCacheEvent: Map<string, boolean> = new Map<string, boolean>();
|
||||||
essenceLRU = new LimitedHashTable<number, string>(1000);
|
essenceLRU = new LimitedHashTable<number, string>(1000);
|
||||||
|
|
||||||
constructor(context: InstanceContext, core: NapCatCore) {
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
@@ -44,7 +45,7 @@ export class NTQQGroupApi {
|
|||||||
|
|
||||||
async initCache() {
|
async initCache() {
|
||||||
for (const group of await this.getGroups(true)) {
|
for (const group of await this.getGroups(true)) {
|
||||||
this.refreshGroupMemberCache(group.groupCode).then().catch();
|
this.refreshGroupMemberCache(group.groupCode, false).then().catch(e => this.context.logger.logError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,14 +127,26 @@ export class NTQQGroupApi {
|
|||||||
return this.context.session.getGroupService().getAllMemberList(groupCode, forced);
|
return this.context.session.getGroupService().getAllMemberList(groupCode, forced);
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshGroupMemberCache(groupCode: string) {
|
async refreshGroupMemberCache(groupCode: string, isWait = true) {
|
||||||
try {
|
this.groupMemberCacheEvent.set(groupCode, true);
|
||||||
const members = await this.getGroupMemberAll(groupCode, true);
|
const updateCache = async () => {
|
||||||
this.groupMemberCache.set(groupCode, members.result.infos);
|
try {
|
||||||
} catch (e) {
|
const members = await this.getGroupMemberAll(groupCode, true);
|
||||||
this.context.logger.logError(`刷新群成员缓存失败, 群号: ${groupCode}, 错误: ${e}`);
|
this.groupMemberCache.set(groupCode, members.result.infos);
|
||||||
|
} catch (e) {
|
||||||
|
this.context.logger.logError(`刷新群成员缓存失败, 群号: ${groupCode}, 错误: ${e}`);
|
||||||
|
} finally {
|
||||||
|
this.groupMemberCacheEvent.set(groupCode, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isWait) {
|
||||||
|
await updateCache();
|
||||||
|
} else {
|
||||||
|
updateCache();
|
||||||
}
|
}
|
||||||
return this.groupMemberCache;
|
|
||||||
|
return this.groupMemberCache.get(groupCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) {
|
async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) {
|
||||||
@@ -143,7 +156,7 @@ export class NTQQGroupApi {
|
|||||||
// 获取群成员缓存
|
// 获取群成员缓存
|
||||||
let members = this.groupMemberCache.get(groupCodeStr);
|
let members = this.groupMemberCache.get(groupCodeStr);
|
||||||
if (!members) {
|
if (!members) {
|
||||||
members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr);
|
members = (await this.refreshGroupMemberCache(groupCodeStr, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
const getMember = () => {
|
const getMember = () => {
|
||||||
@@ -157,7 +170,7 @@ export class NTQQGroupApi {
|
|||||||
let member = getMember();
|
let member = getMember();
|
||||||
// 如果缓存中不存在该成员,尝试刷新缓存
|
// 如果缓存中不存在该成员,尝试刷新缓存
|
||||||
if (!member) {
|
if (!member) {
|
||||||
members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr);
|
members = (await this.refreshGroupMemberCache(groupCodeStr, true));
|
||||||
member = getMember();
|
member = getMember();
|
||||||
}
|
}
|
||||||
return member;
|
return member;
|
||||||
|
@@ -2,8 +2,7 @@ import { ModifyProfileParams, User, UserDetailSource } from '@/core/types';
|
|||||||
import { RequestUtil } from '@/common/request';
|
import { RequestUtil } from '@/common/request';
|
||||||
import { InstanceContext, NapCatCore, ProfileBizType } from '..';
|
import { InstanceContext, NapCatCore, ProfileBizType } from '..';
|
||||||
import { solveAsyncProblem } from '@/common/helper';
|
import { solveAsyncProblem } from '@/common/helper';
|
||||||
import { promisify } from 'node:util';
|
import { Fallback, FallbackUtil } from '@/common/fall-back';
|
||||||
import { LRUCache } from '@/common/lru-cache';
|
|
||||||
|
|
||||||
export class NTQQUserApi {
|
export class NTQQUserApi {
|
||||||
context: InstanceContext;
|
context: InstanceContext;
|
||||||
@@ -108,6 +107,19 @@ export class NTQQUserApi {
|
|||||||
return retUser;
|
return retUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getUserDetailInfoV2(uid: string): Promise<User> {
|
||||||
|
const fallback = new Fallback<User>((user) => FallbackUtil.boolchecker(user, user !== undefined && user.uin !== '0'))
|
||||||
|
.add(() => this.fetchUserDetailInfo(uid, UserDetailSource.KDB))
|
||||||
|
.add(() => this.fetchUserDetailInfo(uid, UserDetailSource.KSERVER));
|
||||||
|
const retUser = await fallback.run().then(async (user) => {
|
||||||
|
if (user && user.uin === '0') {
|
||||||
|
user.uin = await this.core.apis.UserApi.getUidByUinV2(uid) ?? '0';
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
return retUser;
|
||||||
|
}
|
||||||
|
|
||||||
async modifySelfProfile(param: ModifyProfileParams) {
|
async modifySelfProfile(param: ModifyProfileParams) {
|
||||||
return this.context.session.getProfileService().modifyDesktopMiniProfile(param);
|
return this.context.session.getProfileService().modifyDesktopMiniProfile(param);
|
||||||
}
|
}
|
||||||
@@ -169,46 +181,34 @@ export class NTQQUserApi {
|
|||||||
return skey;
|
return skey;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUidByUinV2(Uin: string) {
|
async getUidByUinV2(uin: string) {
|
||||||
if (!Uin) {
|
if (!uin) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const services = [
|
|
||||||
() => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined),
|
const fallback =
|
||||||
() => promisify<string, string[], Map<string, string>>
|
new Fallback<string | undefined>((uid) => FallbackUtil.boolchecker(uid, uid !== undefined && uid.indexOf('*') === -1 && uid !== ''))
|
||||||
(this.context.session.getProfileService().getUidByUin)('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined),
|
.add(() => this.context.session.getUixConvertService().getUid([uin]).then((data) => data.uidInfo.get(uin)))
|
||||||
() => this.context.session.getGroupService().getUidByUins([Uin]).then((data) => data.uids.get(Uin)).catch(() => undefined),
|
.add(() => this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [uin]).get(uin))
|
||||||
() => this.getUserDetailInfoByUin(Uin).then((data) => data.detail.uid).catch(() => undefined),
|
.add(() => this.context.session.getGroupService().getUidByUins([uin]).then((data) => data.uids.get(uin)))
|
||||||
];
|
.add(() => this.getUserDetailInfoByUin(uin).then((data) => data.detail.uid));
|
||||||
let uid: string | undefined = undefined;
|
|
||||||
for (const service of services) {
|
const uid = await fallback.run().catch(() => '');
|
||||||
uid = await service();
|
|
||||||
if (uid && uid.indexOf('*') == -1 && uid !== '') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uid ?? '';
|
return uid ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUinByUidV2(Uid: string) {
|
async getUinByUidV2(uid: string) {
|
||||||
if (!Uid) {
|
if (!uid) {
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
const services = [
|
|
||||||
() => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined),
|
const fallback = new Fallback<string | undefined>((uin) => FallbackUtil.boolchecker(uin, uin !== undefined && uin !== '0' && uin !== ''))
|
||||||
() => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined),
|
.add(() => this.context.session.getUixConvertService().getUin([uid]).then((data) => data.uinInfo.get(uid)))
|
||||||
() => promisify<string, string[], Map<string, string>>
|
.add(() => this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [uid]).get(uid))
|
||||||
(this.context.session.getProfileService().getUinByUid)('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined),
|
.add(() => this.context.session.getGroupService().getUinByUids([uid]).then((data) => data.uins.get(uid)))
|
||||||
() => this.core.apis.FriendApi.getBuddyIdMap(true).then((data) => data.getKey(Uid)).catch(() => undefined),
|
.add(() => this.getUserDetailInfo(uid).then((data) => data.uin));
|
||||||
() => this.getUserDetailInfo(Uid).then((data) => data.uin).catch(() => undefined),
|
|
||||||
];
|
const uin = await fallback.run().catch(() => '0');
|
||||||
let uin: string | undefined = undefined;
|
|
||||||
for (const service of services) {
|
|
||||||
uin = await service();
|
|
||||||
if (uin && uin !== '0' && uin !== '') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uin ?? '0';
|
return uin ?? '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ import {
|
|||||||
WebHonorType,
|
WebHonorType,
|
||||||
} from '@/core';
|
} from '@/core';
|
||||||
import { NapCatCore } from '..';
|
import { NapCatCore } from '..';
|
||||||
import { createReadStream, readFileSync, statSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { createHash } from 'node:crypto';
|
import { createHash } from 'node:crypto';
|
||||||
import { basename } from 'node:path';
|
import { basename } from 'node:path';
|
||||||
|
|
||||||
|
4
src/core/external/appid.json
vendored
4
src/core/external/appid.json
vendored
@@ -158,5 +158,9 @@
|
|||||||
"6.9.63-30899": {
|
"6.9.63-30899": {
|
||||||
"appid": 537263820,
|
"appid": 537263820,
|
||||||
"qua": "V1_MAC_NQ_6.9.63_30899_GW_B"
|
"qua": "V1_MAC_NQ_6.9.63_30899_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.17-31219": {
|
||||||
|
"appid": 537266450,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.17_31219_GW_B"
|
||||||
}
|
}
|
||||||
}
|
}
|
4
src/core/external/offset.json
vendored
4
src/core/external/offset.json
vendored
@@ -202,5 +202,9 @@
|
|||||||
"6.9.63-30899-arm64": {
|
"6.9.63-30899-arm64": {
|
||||||
"send": "41DCBD8",
|
"send": "41DCBD8",
|
||||||
"recv": "41DF3F0"
|
"recv": "41DF3F0"
|
||||||
|
},
|
||||||
|
"9.9.17-31219-x64": {
|
||||||
|
"send": "39C1350",
|
||||||
|
"recv": "39C5784"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -30,7 +30,6 @@ import os from 'node:os';
|
|||||||
import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
|
import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
|
||||||
import { proxiedListenerOf } from '@/common/proxy-handler';
|
import { proxiedListenerOf } from '@/common/proxy-handler';
|
||||||
import { NTQQPacketApi } from './apis/packet';
|
import { NTQQPacketApi } from './apis/packet';
|
||||||
import { UmamiTrace } from '@/common/umami';
|
|
||||||
export * from './wrapper';
|
export * from './wrapper';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
export * from './services';
|
export * from './services';
|
||||||
@@ -155,11 +154,6 @@ export class NapCatCore {
|
|||||||
const msgListener = new NodeIKernelMsgListener();
|
const msgListener = new NodeIKernelMsgListener();
|
||||||
|
|
||||||
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
||||||
if (this.context.workingEnv === NapCatCoreWorkingEnv.Framework) {
|
|
||||||
UmamiTrace.sendTrace('event/kickoff');
|
|
||||||
} else {
|
|
||||||
UmamiTrace.sendTrace('event/kickoff');
|
|
||||||
}
|
|
||||||
// 下线通知
|
// 下线通知
|
||||||
this.context.logger.logError('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc);
|
this.context.logger.logError('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc);
|
||||||
this.selfInfo.online = false;
|
this.selfInfo.online = false;
|
||||||
|
@@ -21,7 +21,7 @@ export class PacketOperationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async GroupPoke(groupUin: number, uin: number) {
|
async GroupPoke(groupUin: number, uin: number) {
|
||||||
const req = trans.SendPoke.build(groupUin, uin);
|
const req = trans.SendPoke.build(uin, groupUin);
|
||||||
await this.context.client.sendOidbPacket(req);
|
await this.context.client.sendOidbPacket(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,6 @@ import { NodeIKernelLoginService } from '@/core/services';
|
|||||||
import { NodeIQQNTWrapperSession, WrapperNodeApi } from '@/core/wrapper';
|
import { NodeIQQNTWrapperSession, WrapperNodeApi } from '@/core/wrapper';
|
||||||
import { InitWebUi, WebUiConfig } from '@/webui';
|
import { InitWebUi, WebUiConfig } from '@/webui';
|
||||||
import { NapCatOneBot11Adapter } from '@/onebot';
|
import { NapCatOneBot11Adapter } from '@/onebot';
|
||||||
import { UmamiTrace } from '@/common/umami';
|
|
||||||
|
|
||||||
//Framework ES入口文件
|
//Framework ES入口文件
|
||||||
export async function getWebUiUrl() {
|
export async function getWebUiUrl() {
|
||||||
@@ -26,7 +25,6 @@ export async function NCoreInitFramework(
|
|||||||
console.log('NapCat Framework App Loading...');
|
console.log('NapCat Framework App Loading...');
|
||||||
|
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
UmamiTrace.sendTrace('uncaught/error', err.message);
|
|
||||||
console.log('[NapCat] [Error] Unhandled Exception:', err.message);
|
console.log('[NapCat] [Error] Unhandled Exception:', err.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,10 +36,6 @@ export async function NCoreInitFramework(
|
|||||||
const logger = new LogWrapper(pathWrapper.logsPath);
|
const logger = new LogWrapper(pathWrapper.logsPath);
|
||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
|
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
|
||||||
let guid = loginService.getMachineGuid();
|
|
||||||
UmamiTrace.init(basicInfoWrapper.getFullQQVesion(), guid,'framework');
|
|
||||||
UmamiTrace.sendTrace('boot/init');
|
|
||||||
UmamiTrace.sendTrace('login/success');
|
|
||||||
//直到登录成功后,执行下一步
|
//直到登录成功后,执行下一步
|
||||||
const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
|
const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
|
||||||
const loginListener = new NodeIKernelLoginListener();
|
const loginListener = new NodeIKernelLoginListener();
|
||||||
|
@@ -83,5 +83,5 @@ export abstract class OneBotAction<PayloadType, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract _handle(payload: PayloadType, adaptername: string): PromiseLike<ReturnDataType>;
|
abstract _handle(payload: PayloadType, adaptername: string): Promise<ReturnDataType>;
|
||||||
}
|
}
|
||||||
|
@@ -14,22 +14,23 @@ class OCRImageBase extends OneBotAction<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.image));
|
const { path, success } = await uriToLocalFile(this.core.NapCatTempPath, payload.image);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(`OCR ${payload.image}失败,image字段可能格式不正确`);
|
throw new Error(`OCR ${payload.image}失败, image字段可能格式不正确`);
|
||||||
}
|
}
|
||||||
if (path) {
|
if (path) {
|
||||||
await checkFileExist(path, 5000); // 避免崩溃
|
try {
|
||||||
const ret = await this.core.apis.SystemApi.ocrImage(path);
|
await checkFileExist(path, 5000); // 避免崩溃
|
||||||
fs.unlink(path, () => { });
|
const ret = await this.core.apis.SystemApi.ocrImage(path);
|
||||||
|
if (!ret) {
|
||||||
if (!ret) {
|
throw new Error(`OCR ${payload.image}失败`);
|
||||||
throw new Error(`OCR ${payload.image}失败`);
|
}
|
||||||
|
return ret.result;
|
||||||
|
} finally {
|
||||||
|
fs.unlink(path, () => { });
|
||||||
}
|
}
|
||||||
return ret.result;
|
|
||||||
}
|
}
|
||||||
fs.unlink(path, () => { });
|
throw new Error(`OCR ${payload.image}失败, 文件可能不存在`);
|
||||||
throw new Error(`OCR ${payload.image}失败,文件可能不存在`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,4 +40,4 @@ export class OCRImage extends OCRImageBase {
|
|||||||
|
|
||||||
export class IOCRImage extends OCRImageBase {
|
export class IOCRImage extends OCRImageBase {
|
||||||
actionName = ActionName.IOCRImage;
|
actionName = ActionName.IOCRImage;
|
||||||
}
|
}
|
@@ -13,12 +13,13 @@ type Payload = Static<typeof SchemaData>;
|
|||||||
|
|
||||||
export default class GoCQHTTPGetStrangerInfo extends OneBotAction<Payload, OB11User> {
|
export default class GoCQHTTPGetStrangerInfo extends OneBotAction<Payload, OB11User> {
|
||||||
actionName = ActionName.GoCQHTTP_GetStrangerInfo;
|
actionName = ActionName.GoCQHTTP_GetStrangerInfo;
|
||||||
|
payloadSchema = SchemaData;
|
||||||
async _handle(payload: Payload): Promise<OB11User> {
|
async _handle(payload: Payload): Promise<OB11User> {
|
||||||
const user_id = payload.user_id.toString();
|
const user_id = payload.user_id.toString();
|
||||||
const extendData = await this.core.apis.UserApi.getUserDetailInfoByUin(user_id);
|
const extendData = await this.core.apis.UserApi.getUserDetailInfoByUin(user_id);
|
||||||
let uid = (await this.core.apis.UserApi.getUidByUinV2(user_id));
|
let uid = (await this.core.apis.UserApi.getUidByUinV2(user_id));
|
||||||
if (!uid) uid = extendData.detail.uid;
|
if (!uid) uid = extendData.detail.uid;
|
||||||
|
console.log(uid);
|
||||||
const info = (await this.core.apis.UserApi.getUserDetailInfo(uid));
|
const info = (await this.core.apis.UserApi.getUserDetailInfo(uid));
|
||||||
return {
|
return {
|
||||||
...extendData.detail.simpleInfo.coreInfo,
|
...extendData.detail.simpleInfo.coreInfo,
|
||||||
|
@@ -26,20 +26,35 @@ class GetGroupMemberInfo extends OneBotAction<Payload, OB11GroupMember> {
|
|||||||
return uid;
|
return uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
private async getGroupMemberInfo(payload: Payload, uid: string, isNocache: boolean) {
|
||||||
const isNocache = this.parseBoolean(payload.no_cache ?? true);
|
const groupMemberCache = this.core.apis.GroupApi.groupMemberCache.get(payload.group_id.toString());
|
||||||
const uid = await this.getUid(payload.user_id);
|
let groupMember = groupMemberCache?.get(uid);
|
||||||
const groupMember = this.core.apis.GroupApi.groupMemberCache.get(payload.group_id.toString())?.get(uid);
|
|
||||||
let [member, info] = await Promise.all([
|
const [member, info] = await Promise.all([
|
||||||
this.core.apis.GroupApi.getGroupMemberEx(payload.group_id.toString(), uid, isNocache),
|
this.core.apis.GroupApi.getGroupMemberEx(payload.group_id.toString(), uid, isNocache),
|
||||||
this.core.apis.UserApi.getUserDetailInfo(uid),
|
this.core.apis.UserApi.getUserDetailInfo(uid),
|
||||||
]);
|
]);
|
||||||
if (!member || !groupMember) throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在`);
|
|
||||||
if (info) {
|
if (!member) throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
member = { ...groupMember, ...member, ...info };
|
|
||||||
} else {
|
if (!groupMember && this.core.apis.GroupApi.groupMemberCacheEvent.get(payload.group_id.toString())) {
|
||||||
|
groupMember = (await this.core.apis.GroupApi.refreshGroupMemberCache(payload.group_id.toString(), true))?.get(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!groupMember) throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在`);
|
||||||
|
|
||||||
|
return info ? { ...groupMember, ...member, ...info } : member;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handle(payload: Payload) {
|
||||||
|
const isNocache = this.parseBoolean(payload.no_cache ?? true);
|
||||||
|
const uid = await this.getUid(payload.user_id);
|
||||||
|
const member = await this.getGroupMemberInfo(payload, uid, isNocache);
|
||||||
|
|
||||||
|
if (!member) {
|
||||||
this.core.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息`);
|
this.core.context.logger.logDebug(`获取群成员详细信息失败, 只能返回基础信息`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OB11Construct.groupMember(payload.group_id.toString(), member);
|
return OB11Construct.groupMember(payload.group_id.toString(), member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ import { OB11Construct } from '@/onebot/helper/data';
|
|||||||
import { OneBotAction } from '@/onebot/action/OneBotAction';
|
import { OneBotAction } from '@/onebot/action/OneBotAction';
|
||||||
import { ActionName } from '@/onebot/action/router';
|
import { ActionName } from '@/onebot/action/router';
|
||||||
import { Static, Type } from '@sinclair/typebox';
|
import { Static, Type } from '@sinclair/typebox';
|
||||||
|
import { GroupMember } from '@/core';
|
||||||
|
|
||||||
const SchemaData = Type.Object({
|
const SchemaData = Type.Object({
|
||||||
group_id: Type.Union([Type.Number(), Type.String()]),
|
group_id: Type.Union([Type.Number(), Type.String()]),
|
||||||
@@ -17,25 +18,32 @@ export class GetGroupMemberList extends OneBotAction<Payload, OB11GroupMember[]>
|
|||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
const groupIdStr = payload.group_id.toString();
|
const groupIdStr = payload.group_id.toString();
|
||||||
const noCache = payload.no_cache ? this.stringToBoolean(payload.no_cache) : false;
|
const noCache = this.parseBoolean(payload.no_cache ?? false);
|
||||||
|
const groupMembers = await this.getGroupMembers(groupIdStr, noCache);
|
||||||
|
const _groupMembers = await Promise.all(
|
||||||
|
Array.from(groupMembers.values()).map(item =>
|
||||||
|
OB11Construct.groupMember(groupIdStr, item)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return Array.from(new Map(_groupMembers.map(member => [member.user_id, member])).values());
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseBoolean(value: boolean | string): boolean {
|
||||||
|
return typeof value === 'string' ? value === 'true' : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getGroupMembers(groupIdStr: string, noCache: boolean): Promise<Map<string, GroupMember>> {
|
||||||
const memberCache = this.core.apis.GroupApi.groupMemberCache;
|
const memberCache = this.core.apis.GroupApi.groupMemberCache;
|
||||||
let groupMembers = memberCache.get(groupIdStr);
|
let groupMembers = memberCache.get(groupIdStr);
|
||||||
|
|
||||||
if (noCache || !groupMembers) {
|
if (noCache || !groupMembers) {
|
||||||
this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr).then().catch();
|
const data = this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr, true).then().catch();
|
||||||
//下次刷新
|
groupMembers = memberCache.get(groupIdStr) || (await data);
|
||||||
groupMembers = memberCache.get(groupIdStr);
|
|
||||||
if (!groupMembers) {
|
if (!groupMembers) {
|
||||||
throw new Error(`Failed to get group member list for group ${groupIdStr}`);
|
throw new Error(`Failed to get group member list for group ${groupIdStr}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const memberPromises = Array.from(groupMembers.values()).map(item =>
|
|
||||||
OB11Construct.groupMember(groupIdStr, item)
|
return groupMembers;
|
||||||
);
|
|
||||||
const _groupMembers = await Promise.all(memberPromises);
|
|
||||||
const MemberMap = new Map(_groupMembers.map(member => [member.user_id, member]));
|
|
||||||
return Array.from(MemberMap.values());
|
|
||||||
}
|
}
|
||||||
stringToBoolean(str: string | boolean): boolean {
|
}
|
||||||
return typeof str === 'boolean' ? str : str.toLowerCase() === "true";
|
|
||||||
}
|
|
||||||
}
|
|
@@ -14,6 +14,6 @@ export class GroupPoke extends GetPacketStatusDepends<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
await this.core.apis.PacketApi.pkt.operation.GroupPoke(+payload.user_id, +payload.group_id);
|
await this.core.apis.PacketApi.pkt.operation.GroupPoke(+payload.group_id, +payload.user_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,15 +21,9 @@ export class SendGroupAiRecord extends GetPacketStatusDepends<Payload, {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
const rawRsp = await this.core.apis.PacketApi.pkt.operation.GetAiVoice(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound);
|
await this.core.apis.PacketApi.pkt.operation.GetAiVoice(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound);
|
||||||
const url = await this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+payload.group_id, rawRsp.msgInfoBody[0].index);
|
return {
|
||||||
const { path, errMsg, success } = (await uriToLocalFile(this.core.NapCatTempPath, url));
|
message_id: 0 // can't get message_id from GetAiVoice
|
||||||
if (!success) {
|
};
|
||||||
throw new Error(errMsg);
|
|
||||||
}
|
|
||||||
const peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() } as Peer;
|
|
||||||
const element = await this.core.apis.FileApi.createValidSendPttElement(path);
|
|
||||||
const sendRes = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [element], [path]);
|
|
||||||
return { message_id: sendRes.id ?? -1 };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,9 +15,9 @@ export class SendPoke extends GetPacketStatusDepends<Payload, any> {
|
|||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
if (payload.group_id) {
|
if (payload.group_id) {
|
||||||
this.core.apis.PacketApi.pkt.operation.GroupPoke(+payload.group_id, +payload.user_id);
|
await this.core.apis.PacketApi.pkt.operation.GroupPoke(+payload.group_id, +payload.user_id);
|
||||||
} else {
|
} else {
|
||||||
this.core.apis.PacketApi.pkt.operation.FriendPoke(+payload.user_id);
|
await this.core.apis.PacketApi.pkt.operation.FriendPoke(+payload.user_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -223,7 +223,7 @@ export class OneBotGroupApi {
|
|||||||
} else if (element.type === TipGroupElementType.KMEMBERADD) {
|
} else if (element.type === TipGroupElementType.KMEMBERADD) {
|
||||||
// 自己的通知 协议推送为type->85 在这里实现为了避免邀请出现问题
|
// 自己的通知 协议推送为type->85 在这里实现为了避免邀请出现问题
|
||||||
if (element.memberUid == this.core.selfInfo.uid) {
|
if (element.memberUid == this.core.selfInfo.uid) {
|
||||||
this.core.apis.GroupApi.refreshGroupMemberCache(msg.peerUid).then().catch();
|
await this.core.apis.GroupApi.refreshGroupMemberCache(msg.peerUid, false);
|
||||||
return new OB11GroupIncreaseEvent(
|
return new OB11GroupIncreaseEvent(
|
||||||
this.core,
|
this.core,
|
||||||
parseInt(msg.peerUid),
|
parseInt(msg.peerUid),
|
||||||
|
@@ -797,6 +797,13 @@ export class OneBotMsgApi {
|
|||||||
|
|
||||||
private async handlePrivateMessage(resMsg: OB11Message, msg: RawMessage) {
|
private async handlePrivateMessage(resMsg: OB11Message, msg: RawMessage) {
|
||||||
resMsg.sub_type = 'friend';
|
resMsg.sub_type = 'friend';
|
||||||
|
if (await this.core.apis.FriendApi.isBuddy(msg.senderUid)) {
|
||||||
|
let nickname = (await this.core.apis.UserApi.getCoreAndBaseInfo([msg.senderUid])).get(msg.senderUid)?.coreInfo.nick;
|
||||||
|
if (nickname) {
|
||||||
|
resMsg.sender.nickname = nickname;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
resMsg.sender.nickname = (await this.core.apis.UserApi.getUserDetailInfo(msg.senderUid)).nick;
|
resMsg.sender.nickname = (await this.core.apis.UserApi.getUserDetailInfo(msg.senderUid)).nick;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1015,7 +1022,7 @@ export class OneBotMsgApi {
|
|||||||
// 邀请需要解grayTipElement
|
// 邀请需要解grayTipElement
|
||||||
if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) {
|
if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) {
|
||||||
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
|
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
|
||||||
this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch();
|
await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString(), false);
|
||||||
let operatorUid = await this.waitGroupNotify(
|
let operatorUid = await this.waitGroupNotify(
|
||||||
groupChange.groupUin.toString(),
|
groupChange.groupUin.toString(),
|
||||||
groupChange.memberUid,
|
groupChange.memberUid,
|
||||||
@@ -1045,7 +1052,7 @@ export class OneBotMsgApi {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
// 自己被踢了 5S后回收
|
// 自己被踢了 5S后回收
|
||||||
} else {
|
} else {
|
||||||
this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch();
|
await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString(), false);
|
||||||
}
|
}
|
||||||
return new OB11GroupDecreaseEvent(
|
return new OB11GroupDecreaseEvent(
|
||||||
this.core,
|
this.core,
|
||||||
@@ -1056,7 +1063,7 @@ export class OneBotMsgApi {
|
|||||||
);
|
);
|
||||||
} else if (SysMessage.contentHead.type == 44 && SysMessage.body?.msgContent) {
|
} else if (SysMessage.contentHead.type == 44 && SysMessage.body?.msgContent) {
|
||||||
const groupAmin = new NapProtoMsg(GroupAdmin).decode(SysMessage.body.msgContent);
|
const groupAmin = new NapProtoMsg(GroupAdmin).decode(SysMessage.body.msgContent);
|
||||||
this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString()).then().catch();
|
await this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString(), false);
|
||||||
let enabled = false;
|
let enabled = false;
|
||||||
let uid = '';
|
let uid = '';
|
||||||
if (groupAmin.body.extraEnable != null) {
|
if (groupAmin.body.extraEnable != null) {
|
||||||
|
@@ -390,9 +390,6 @@ export class NapCatOneBot11Adapter {
|
|||||||
this.context.logger.logDebug('有加群请求');
|
this.context.logger.logDebug('有加群请求');
|
||||||
try {
|
try {
|
||||||
let requestUin = await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid);
|
let requestUin = await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid);
|
||||||
if (isNaN(parseInt(requestUin))) {
|
|
||||||
requestUin = (await this.core.apis.UserApi.getUserDetailInfo(notify.user1.uid)).uin;
|
|
||||||
}
|
|
||||||
const groupRequestEvent = new OB11GroupRequestEvent(
|
const groupRequestEvent = new OB11GroupRequestEvent(
|
||||||
this.core,
|
this.core,
|
||||||
parseInt(notify.group.groupCode),
|
parseInt(notify.group.groupCode),
|
||||||
|
@@ -29,11 +29,9 @@ import { InitWebUi } from '@/webui';
|
|||||||
import { WebUiDataRuntime } from '@/webui/src/helper/Data';
|
import { WebUiDataRuntime } from '@/webui/src/helper/Data';
|
||||||
import { napCatVersion } from '@/common/version';
|
import { napCatVersion } from '@/common/version';
|
||||||
import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener';
|
import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener';
|
||||||
import { UmamiTrace } from '@/common/umami';
|
|
||||||
// NapCat Shell App ES 入口文件
|
// NapCat Shell App ES 入口文件
|
||||||
async function handleUncaughtExceptions(logger: LogWrapper) {
|
async function handleUncaughtExceptions(logger: LogWrapper) {
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
UmamiTrace.sendTrace('uncaught/error', err.message);
|
|
||||||
logger.logError('[NapCat] [Error] Unhandled Exception:', err.message);
|
logger.logError('[NapCat] [Error] Unhandled Exception:', err.message);
|
||||||
});
|
});
|
||||||
process.on('unhandledRejection', (reason, promise) => {
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
@@ -153,7 +151,6 @@ async function handleLogin(
|
|||||||
};
|
};
|
||||||
|
|
||||||
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
|
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
|
||||||
UmamiTrace.sendTrace('qrlogin/error', [errType, errCode, errMsg].toString());
|
|
||||||
if (!isLogined) {
|
if (!isLogined) {
|
||||||
logger.logError('[Core] [Login] Login Error,ErrType: ', errType, ' ErrCode:', errCode);
|
logger.logError('[Core] [Login] Login Error,ErrType: ', errType, ' ErrCode:', errCode);
|
||||||
if (errType == 1 && errCode == 3) {
|
if (errType == 1 && errCode == 3) {
|
||||||
@@ -164,7 +161,6 @@ async function handleLogin(
|
|||||||
};
|
};
|
||||||
|
|
||||||
loginListener.onLoginFailed = (...args) => {
|
loginListener.onLoginFailed = (...args) => {
|
||||||
UmamiTrace.sendTrace('login/error', args.toString());
|
|
||||||
logger.logError('[Core] [Login] Login Error , ErrInfo: ', JSON.stringify(args));
|
logger.logError('[Core] [Login] Login Error , ErrInfo: ', JSON.stringify(args));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -298,10 +294,7 @@ export async function NCoreInitShell() {
|
|||||||
|
|
||||||
const dataTimestape = new Date().getTime().toString();
|
const dataTimestape = new Date().getTime().toString();
|
||||||
o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']);
|
o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']);
|
||||||
UmamiTrace.init(basicInfoWrapper.getFullQQVesion(), loginService.getMachineGuid(), 'shell');
|
|
||||||
UmamiTrace.sendTrace('boot/init');
|
|
||||||
const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList);
|
const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList);
|
||||||
UmamiTrace.sendTrace('login/success');
|
|
||||||
const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
|
const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
|
||||||
o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));
|
o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));
|
||||||
|
|
||||||
|
@@ -90,7 +90,7 @@ export class WebUiConfigWrapper {
|
|||||||
try {
|
try {
|
||||||
const configPath = resolve(webUiPathWrapper.configPath, './webui.json');
|
const configPath = resolve(webUiPathWrapper.configPath, './webui.json');
|
||||||
|
|
||||||
if (!await fs.access(configPath, constants.R_OK).then(() => true).catch(() => false)) {
|
if (!await fs.access(configPath, constants.F_OK).then(() => true).catch(() => false)) {
|
||||||
await fs.writeFile(configPath, JSON.stringify(defaultconfig, null, 4));
|
await fs.writeFile(configPath, JSON.stringify(defaultconfig, null, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +101,12 @@ export class WebUiConfigWrapper {
|
|||||||
if (!parsedConfig.prefix.startsWith('/')) parsedConfig.prefix = '/' + parsedConfig.prefix;
|
if (!parsedConfig.prefix.startsWith('/')) parsedConfig.prefix = '/' + parsedConfig.prefix;
|
||||||
if (parsedConfig.prefix.endsWith('/')) parsedConfig.prefix = parsedConfig.prefix.slice(0, -1);
|
if (parsedConfig.prefix.endsWith('/')) parsedConfig.prefix = parsedConfig.prefix.slice(0, -1);
|
||||||
// 配置已经被操作过了,还是回写一下吧,不然新配置不会出现在配置文件里
|
// 配置已经被操作过了,还是回写一下吧,不然新配置不会出现在配置文件里
|
||||||
await fs.writeFile(configPath, JSON.stringify(parsedConfig, null, 4));
|
if (await fs.access(configPath, constants.W_OK).then(() => true).catch(() => false)) {
|
||||||
|
await fs.writeFile(configPath, JSON.stringify(parsedConfig, null, 4));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.warn(`文件: ${configPath} 没有写入权限, 配置的更改部分可能会在重启后还原.`);
|
||||||
|
}
|
||||||
// 不希望回写的配置放后面
|
// 不希望回写的配置放后面
|
||||||
|
|
||||||
// 查询主机地址是否可用
|
// 查询主机地址是否可用
|
||||||
|
Reference in New Issue
Block a user