Compare commits

...
7 Commits
Author SHA1 Message Date
手瓜一十雪 e3814403e4 fix: #682
Build Release / check-version (push) Canceled after 0s
Build Release / Build-LiteLoader (push) Canceled after 0s
Build Release / Build-Shell (push) Canceled after 0s
Build Release / release-napcat (push) Canceled after 0s
Build Action / Build-LiteLoader (push) Canceled after 0s
Build Action / Build-Shell (push) Canceled after 0s
2025-01-07 19:28:46 +08:00
手瓜一十雪 3d16d52dd8 fix: #696 fallback 2025-01-07 18:51:04 +08:00
Mlikiowa 1ae47fffb4 release: v4.2.68 2025-01-06 13:08:43 +00:00
手瓜一十雪 4e7096b9e2 feat: offset win 31219
Build Release / check-version (push) Canceled after 0s
Build Release / Build-LiteLoader (push) Canceled after 0s
Build Release / Build-Shell (push) Canceled after 0s
Build Release / release-napcat (push) Canceled after 0s
Build Action / Build-LiteLoader (push) Canceled after 0s
Build Action / Build-Shell (push) Canceled after 0s
2025-01-06 21:08:18 +08:00
Mlikiowa 8cc9b7f6a7 release: v4.2.67 2025-01-05 02:50:38 +00:00
手瓜一十雪 fb45c1020e revert: 再也不用umami了
Build Release / check-version (push) Canceled after 0s
Build Release / Build-LiteLoader (push) Canceled after 0s
Build Release / Build-Shell (push) Canceled after 0s
Build Release / release-napcat (push) Canceled after 0s
Build Action / Build-LiteLoader (push) Canceled after 0s
Build Action / Build-Shell (push) Canceled after 0s
2025-01-05 10:49:49 +08:00
Mlikiowa e9db4ae8f4 release: v4.2.66 2025-01-04 11:52:38 +00:00
17 changed files with 121 additions and 79 deletions

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@
"name": "NapCatQQ",
"slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现",
"version": "4.2.65",
"version": "4.2.68",
"icon": "./logo.png",
"authors": [
{
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.2.65",
"version": "4.2.68",
"scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",
+5 -5
View File
@@ -20,19 +20,19 @@ export class Fallback<T> {
for (const handler of this.handlers) {
try {
const result = await handler();
try {
return await this.checker(result);
} catch (checkerError) {
errors.push(checkerError instanceof Error ? checkerError : new Error(String(checkerError)));
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{
export class FallbackUtil {
static boolchecker<T>(value: T, condition: boolean): T {
if (condition) {
return value;
+29 -13
View File
@@ -106,23 +106,39 @@ export class UmamiTraceCore {
"Host": "umami.napneko.icu",
"Content-Type": "application/json",
"User-Agent": this.ua,
...(this.cache ? { 'x-umami-cache': this.cache } : {})
...(this.cache ? { 'x-umami-cache': this.filterInvalidChars(this.cache) } : {})
}
};
const request = https.request(options, (res) => {
res.on('error', (error) => {
try {
const request = https.request(options, (res) => {
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) {
this.cache = data.toString();
}
request.on('error', (error) => {
});
}).on('error', () => { });
request.write(JSON.stringify({ type, payload }));
request.end();
request.write(JSON.stringify({ type, payload }));
request.end();
} catch (error) {
}
}
filterInvalidChars(value: string): string {
return value.replace(/[^\x00-\x7F]/g, '');
}
startHeartbeat() {
+1 -1
View File
@@ -1 +1 @@
export const napCatVersion = '4.2.65';
export const napCatVersion = '4.2.68';
+23 -10
View File
@@ -18,6 +18,7 @@ export class NTQQGroupApi {
context: InstanceContext;
core: NapCatCore;
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);
constructor(context: InstanceContext, core: NapCatCore) {
@@ -44,7 +45,7 @@ export class NTQQGroupApi {
async initCache() {
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);
}
async refreshGroupMemberCache(groupCode: string) {
try {
const members = await this.getGroupMemberAll(groupCode, true);
this.groupMemberCache.set(groupCode, members.result.infos);
} catch (e) {
this.context.logger.logError(`刷新群成员缓存失败, 群号: ${groupCode}, 错误: ${e}`);
async refreshGroupMemberCache(groupCode: string, isWait = true) {
this.groupMemberCacheEvent.set(groupCode, true);
const updateCache = async () => {
try {
const members = await this.getGroupMemberAll(groupCode, true);
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) {
@@ -143,7 +156,7 @@ export class NTQQGroupApi {
// 获取群成员缓存
let members = this.groupMemberCache.get(groupCodeStr);
if (!members) {
members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr);
members = (await this.refreshGroupMemberCache(groupCodeStr, true));
}
const getMember = () => {
@@ -157,7 +170,7 @@ export class NTQQGroupApi {
let member = getMember();
// 如果缓存中不存在该成员,尝试刷新缓存
if (!member) {
members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr);
members = (await this.refreshGroupMemberCache(groupCodeStr, true));
member = getMember();
}
return member;
+1 -1
View File
@@ -193,7 +193,7 @@ export class NTQQUserApi {
.add(() => this.context.session.getGroupService().getUidByUins([uin]).then((data) => data.uids.get(uin)))
.add(() => this.getUserDetailInfoByUin(uin).then((data) => data.detail.uid));
const uid = await fallback.run().catch(() => '0');
const uid = await fallback.run().catch(() => '');
return uid ?? '';
}
+4
View File
@@ -158,5 +158,9 @@
"6.9.63-30899": {
"appid": 537263820,
"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
View File
@@ -202,5 +202,9 @@
"6.9.63-30899-arm64": {
"send": "41DCBD8",
"recv": "41DF3F0"
},
"9.9.17-31219-x64": {
"send": "39C1350",
"recv": "39C5784"
}
}
-6
View File
@@ -30,7 +30,6 @@ import os from 'node:os';
import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
import { proxiedListenerOf } from '@/common/proxy-handler';
import { NTQQPacketApi } from './apis/packet';
import { UmamiTrace } from '@/common/umami';
export * from './wrapper';
export * from './types';
export * from './services';
@@ -155,11 +154,6 @@ export class NapCatCore {
const msgListener = new NodeIKernelMsgListener();
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.selfInfo.online = false;
-6
View File
@@ -9,7 +9,6 @@ import { NodeIKernelLoginService } from '@/core/services';
import { NodeIQQNTWrapperSession, WrapperNodeApi } from '@/core/wrapper';
import { InitWebUi, WebUiConfig } from '@/webui';
import { NapCatOneBot11Adapter } from '@/onebot';
import { UmamiTrace } from '@/common/umami';
//Framework ES入口文件
export async function getWebUiUrl() {
@@ -26,7 +25,6 @@ export async function NCoreInitFramework(
console.log('NapCat Framework App Loading...');
process.on('uncaughtException', (err) => {
UmamiTrace.sendTrace('uncaught/error', 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 basicInfoWrapper = new QQBasicInfoWrapper({ logger });
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 loginListener = new NodeIKernelLoginListener();
@@ -13,12 +13,13 @@ type Payload = Static<typeof SchemaData>;
export default class GoCQHTTPGetStrangerInfo extends OneBotAction<Payload, OB11User> {
actionName = ActionName.GoCQHTTP_GetStrangerInfo;
payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<OB11User> {
const user_id = payload.user_id.toString();
const extendData = await this.core.apis.UserApi.getUserDetailInfoByUin(user_id);
let uid = (await this.core.apis.UserApi.getUidByUinV2(user_id));
if (!uid) uid = extendData.detail.uid;
console.log(uid);
const info = (await this.core.apis.UserApi.getUserDetailInfo(uid));
return {
...extendData.detail.simpleInfo.coreInfo,
+24 -9
View File
@@ -26,20 +26,35 @@ class GetGroupMemberInfo extends OneBotAction<Payload, OB11GroupMember> {
return uid;
}
async _handle(payload: Payload) {
const isNocache = this.parseBoolean(payload.no_cache ?? true);
const uid = await this.getUid(payload.user_id);
const groupMember = this.core.apis.GroupApi.groupMemberCache.get(payload.group_id.toString())?.get(uid);
let [member, info] = await Promise.all([
private async getGroupMemberInfo(payload: Payload, uid: string, isNocache: boolean) {
const groupMemberCache = this.core.apis.GroupApi.groupMemberCache.get(payload.group_id.toString());
let groupMember = groupMemberCache?.get(uid);
const [member, info] = await Promise.all([
this.core.apis.GroupApi.getGroupMemberEx(payload.group_id.toString(), uid, isNocache),
this.core.apis.UserApi.getUserDetailInfo(uid),
]);
if (!member || !groupMember) throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在`);
if (info) {
member = { ...groupMember, ...member, ...info };
} else {
if (!member) throw new Error(`群(${payload.group_id})成员${payload.user_id}不存在`);
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(`获取群成员详细信息失败, 只能返回基础信息`);
}
return OB11Construct.groupMember(payload.group_id.toString(), member);
}
}
+22 -14
View File
@@ -3,6 +3,7 @@ import { OB11Construct } from '@/onebot/helper/data';
import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router';
import { Static, Type } from '@sinclair/typebox';
import { GroupMember } from '@/core';
const SchemaData = Type.Object({
group_id: Type.Union([Type.Number(), Type.String()]),
@@ -17,25 +18,32 @@ export class GetGroupMemberList extends OneBotAction<Payload, OB11GroupMember[]>
async _handle(payload: Payload) {
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;
let groupMembers = memberCache.get(groupIdStr);
if (noCache || !groupMembers) {
this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr).then().catch();
//下次刷新
groupMembers = memberCache.get(groupIdStr);
const data = this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr, true).then().catch();
groupMembers = memberCache.get(groupIdStr) || (await data);
if (!groupMembers) {
throw new Error(`Failed to get group member list for group ${groupIdStr}`);
}
}
const memberPromises = Array.from(groupMembers.values()).map(item =>
OB11Construct.groupMember(groupIdStr, item)
);
const _groupMembers = await Promise.all(memberPromises);
const MemberMap = new Map(_groupMembers.map(member => [member.user_id, member]));
return Array.from(MemberMap.values());
return groupMembers;
}
stringToBoolean(str: string | boolean): boolean {
return typeof str === 'boolean' ? str : str.toLowerCase() === "true";
}
}
}
+1 -1
View File
@@ -223,7 +223,7 @@ export class OneBotGroupApi {
} else if (element.type === TipGroupElementType.KMEMBERADD) {
// 自己的通知 协议推送为type->85 在这里实现为了避免邀请出现问题
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(
this.core,
parseInt(msg.peerUid),
+3 -3
View File
@@ -1022,7 +1022,7 @@ export class OneBotMsgApi {
// 邀请需要解grayTipElement
if (SysMessage.contentHead.type == 33 && 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(
groupChange.groupUin.toString(),
groupChange.memberUid,
@@ -1052,7 +1052,7 @@ export class OneBotMsgApi {
}, 5000);
// 自己被踢了 5S后回收
} else {
this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch();
await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString(), false);
}
return new OB11GroupDecreaseEvent(
this.core,
@@ -1063,7 +1063,7 @@ export class OneBotMsgApi {
);
} else if (SysMessage.contentHead.type == 44 && 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 uid = '';
if (groupAmin.body.extraEnable != null) {
-7
View File
@@ -29,11 +29,9 @@ import { InitWebUi } from '@/webui';
import { WebUiDataRuntime } from '@/webui/src/helper/Data';
import { napCatVersion } from '@/common/version';
import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener';
import { UmamiTrace } from '@/common/umami';
// NapCat Shell App ES 入口文件
async function handleUncaughtExceptions(logger: LogWrapper) {
process.on('uncaughtException', (err) => {
UmamiTrace.sendTrace('uncaught/error', err.message);
logger.logError('[NapCat] [Error] Unhandled Exception:', err.message);
});
process.on('unhandledRejection', (reason, promise) => {
@@ -153,7 +151,6 @@ async function handleLogin(
};
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
UmamiTrace.sendTrace('qrlogin/error', [errType, errCode, errMsg].toString());
if (!isLogined) {
logger.logError('[Core] [Login] Login Error,ErrType: ', errType, ' ErrCode:', errCode);
if (errType == 1 && errCode == 3) {
@@ -164,7 +161,6 @@ async function handleLogin(
};
loginListener.onLoginFailed = (...args) => {
UmamiTrace.sendTrace('login/error', args.toString());
logger.logError('[Core] [Login] Login Error , ErrInfo: ', JSON.stringify(args));
};
@@ -298,10 +294,7 @@ export async function NCoreInitShell() {
const dataTimestape = new Date().getTime().toString();
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);
UmamiTrace.sendTrace('login/success');
const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129';
o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex')));