From 1f90364ba6cf1a43b82aac47a257fb69029457ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Mon, 30 Dec 2024 16:10:38 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E9=87=8D=E5=86=99=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E9=81=A5=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/umami.ts | 123 ++++++++++++++++++++++++++++++++-------- src/core/index.ts | 6 +- src/framework/napcat.ts | 16 +++--- src/shell/base.ts | 19 +++---- 4 files changed, 117 insertions(+), 47 deletions(-) diff --git a/src/common/umami.ts b/src/common/umami.ts index 47f001f0..c1ff1230 100644 --- a/src/common/umami.ts +++ b/src/common/umami.ts @@ -1,40 +1,92 @@ import https from 'node:https'; import { napCatVersion } from './version'; - -export class umamiTrace { +import os from 'os'; +export class UmamiTrace { static napcatVersion = napCatVersion; static qqversion = '1.0.0'; + static guid = 'default-user'; + static heartbeatInterval: NodeJS.Timeout | null = null; + static website: string = '1fabb2b1-c3a3-4416-b1be-31e2cbdce978'; + static referrer: string = 'https://trace.napneko.icu/'; + static hostname: string = 'trace.napneko.icu'; + static ua: string = ''; - static init(qqversion: string) { + static init(qqversion: string, guid: string) { this.qqversion = qqversion; - setInterval(() => { - this.trackEvent('heartbeat'); - }, 5 * 60 * 1000); - } - - static trackEvent(eventName: string, info?: string) { - const StatesData = { - type: 'event', - payload: { - 'website': '596cbbb2-1740-4373-a807-cf3d0637bfa7', - 'hostname': 'trace.napneko.icu', - 'title': 'NapCat ' + umamiTrace.napcatVersion, - 'url': '/' + umamiTrace.qqversion + '/' + umamiTrace.napcatVersion + '/' + eventName + (!info ? '' : '/' + info), - 'referrer': 'https://napcat.onebot.napneko.icu/', - } + let 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', + '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', }; - let request = https.request({ - hostname: '104.19.42.72',// 固定 IP + try { + if (process.platform === 'win32') { + 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`; + } else if (process.platform === 'darwin') { + 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`; + } + } catch (error) { + this.ua = UaList.win32; + } + + this.ua = UaList[process.platform as keyof typeof UaList] || UaList.win32; + + this.identifyUser(guid); + this.startHeartbeat(); + } + + static identifyUser(guid: string) { + this.guid = guid; + const data = { + napcat_version: this.napcatVersion, + qq_version: this.qqversion, + guid: guid + }; + this.sendRequest({ website: this.website, ...data }, 'identify'); + } + + static sendEvent(event: string, data?: object) { + const env = process.env; + const language = env.LANG || env.LANGUAGE || env.LC_ALL || env.LC_MESSAGES; + const payload = { + name: event, + hostname: this.hostname, + referrer: this.referrer, + website: this.website, + language: language || 'es-US', + napcat_version: this.napcatVersion, + qq_version: this.qqversion, + }; + this.sendRequest(payload); + } + + static sendTrace(eventName: string) { + const payload = { + website: this.website, + hostname: this.hostname, + title: 'NapCat ' + this.napcatVersion, + url: `/${this.qqversion}/${this.napcatVersion}/${eventName}`, + referrer: this.referrer, + }; + this.sendRequest(payload); + } + + static sendRequest(payload: object, type = 'event') { + const options = { + hostname: '104.19.42.72', // 固定 IP 或者从 hostUrl 获取 port: 443, path: '/api/send', method: 'POST', headers: { "Host": "umami.napneko.icu", "Content-Type": "application/json", - "User-Agent": `Mozilla/5.0 Umami/${process.version}` + "User-Agent": this.ua } - }, (res) => { + }; + + const request = https.request(options, (res) => { res.on('error', (error) => { }); @@ -42,8 +94,31 @@ export class umamiTrace { }); }); - - request.write(JSON.stringify(StatesData)); + + request.write(JSON.stringify({ type, payload })); request.end(); } + + static startHeartbeat() { + if (this.heartbeatInterval) { + clearInterval(this.heartbeatInterval); + } + this.heartbeatInterval = setInterval(() => { + this.sendEvent('heartbeat', { + title: 'NapCat ' + this.napcatVersion, + language: process.env.LANG || 'en-US', + url: `/${this.qqversion}/${this.napcatVersion}/heartbeat`, + version: this.napcatVersion, + qq_version: this.qqversion, + user_id: this.guid + }); + }, 5 * 60 * 1000); + } + + static stopHeartbeat() { + if (this.heartbeatInterval) { + clearInterval(this.heartbeatInterval); + this.heartbeatInterval = null; + } + } } \ No newline at end of file diff --git a/src/core/index.ts b/src/core/index.ts index cebc8550..8bbb0e8c 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -30,7 +30,7 @@ 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'; +import { UmamiTrace } from '@/common/umami'; export * from './wrapper'; export * from './types'; export * from './services'; @@ -156,9 +156,9 @@ export class NapCatCore { msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => { if (this.context.workingEnv === NapCatCoreWorkingEnv.Framework) { - umamiTrace.trackEvent('framework/kickoff'); + UmamiTrace.sendEvent('framework/kickoff'); } else { - umamiTrace.trackEvent('shell/kickoff'); + UmamiTrace.sendEvent('shell/kickoff'); } // 下线通知 this.context.logger.logError('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc); diff --git a/src/framework/napcat.ts b/src/framework/napcat.ts index e6d749bb..9d1dc7ef 100644 --- a/src/framework/napcat.ts +++ b/src/framework/napcat.ts @@ -9,7 +9,7 @@ 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'; +import { UmamiTrace } from '@/common/umami'; //Framework ES入口文件 export async function getWebUiUrl() { @@ -26,24 +26,22 @@ export async function NCoreInitFramework( console.log('NapCat Framework App Loading...'); process.on('uncaughtException', (err) => { - umamiTrace.trackEvent('framework/error', err.message); + UmamiTrace.sendEvent('framework/error', { name: err.name }); console.log('[NapCat] [Error] Unhandled Exception:', err.message); }); - + process.on('unhandledRejection', (reason, promise) => { console.log('[NapCat] [Error] unhandledRejection:', reason); }); - process.on('exit', (code: number) => { - umamiTrace.trackEvent('framework/exit', code.toString()); - }); - const pathWrapper = new NapCatPathWrapper(); const logger = new LogWrapper(pathWrapper.logsPath); const basicInfoWrapper = new QQBasicInfoWrapper({ logger }); const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion()); - umamiTrace.init(basicInfoWrapper.getFullQQVesion()); - umamiTrace.trackEvent('framework/login'); + let guid = loginService.getMachineGuid(); + UmamiTrace.init(basicInfoWrapper.getFullQQVesion(), guid); + UmamiTrace.sendTrace('framework/boot'); + UmamiTrace.sendEvent('framework/login'); //直到登录成功后,执行下一步 const selfInfo = await new Promise((resolveSelfInfo) => { const loginListener = new NodeIKernelLoginListener(); diff --git a/src/shell/base.ts b/src/shell/base.ts index 95772614..1c00d063 100644 --- a/src/shell/base.ts +++ b/src/shell/base.ts @@ -29,11 +29,11 @@ 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'; +import { UmamiTrace } from '@/common/umami'; // NapCat Shell App ES 入口文件 async function handleUncaughtExceptions(logger: LogWrapper) { process.on('uncaughtException', (err) => { - umamiTrace.trackEvent('framework/error', err.message); + UmamiTrace.sendEvent('framework/error', { name: err.name, message: err.message }); logger.logError('[NapCat] [Error] Unhandled Exception:', err.message); }); process.on('unhandledRejection', (reason, promise) => { @@ -153,7 +153,7 @@ async function handleLogin( }; loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => { - umamiTrace.trackEvent('shell/login/error/' + errType.toString() + '-' + errCode.toString(), errMsg); + UmamiTrace.sendEvent('shell/qrlogin/error', { errType, errCode, errMsg }); if (!isLogined) { logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg); if (errType == 1 && errCode == 3) { @@ -164,7 +164,7 @@ async function handleLogin( }; loginListener.onLoginFailed = (args) => { - umamiTrace.trackEvent('shell/login/error/' + args.toString(), args.toString()); + UmamiTrace.sendEvent('shell/login/error', { args }); logger.logError('[Core] [Login] Login Error , ErrInfo: ', args.toString()); }; @@ -270,13 +270,9 @@ export async function NCoreInitShell() { const pathWrapper = new NapCatPathWrapper(); const logger = new LogWrapper(pathWrapper.logsPath); handleUncaughtExceptions(logger); - process.on('exit', (code: number) => { - umamiTrace.trackEvent('shell/exit', code.toString()); - }); const basicInfoWrapper = new QQBasicInfoWrapper({ logger }); const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion()); - umamiTrace.init(basicInfoWrapper.getFullQQVesion()); - umamiTrace.trackEvent('shell/boot'); + const o3Service = wrapper.NodeIO3MiscService.get(); o3Service.addO3MiscListener(new NodeIO3MiscListener()); @@ -302,9 +298,10 @@ export async function NCoreInitShell() { const dataTimestape = new Date().getTime().toString(); o3Service.reportAmgomWeather('login', 'a1', [dataTimestape, '0', '0']); - + UmamiTrace.init(basicInfoWrapper.getFullQQVesion(), loginService.getMachineGuid()); + UmamiTrace.sendTrace('shell/boot'); const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList); - umamiTrace.trackEvent('shell/login'); + UmamiTrace.sendEvent('shell/login'); const amgomDataPiece = 'eb1fd6ac257461580dc7438eb099f23aae04ca679f4d88f53072dc56e3bb1129'; o3Service.setAmgomDataPiece(basicInfoWrapper.QQVersionAppid, new Uint8Array(Buffer.from(amgomDataPiece, 'hex'))); From 20c0c00fa06bf1d0ff7666c8c99105e820dce74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Mon, 30 Dec 2024 16:14:32 +0800 Subject: [PATCH 2/2] fixfix --- src/common/umami.ts | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/common/umami.ts b/src/common/umami.ts index c1ff1230..c901fe0a 100644 --- a/src/common/umami.ts +++ b/src/common/umami.ts @@ -1,17 +1,17 @@ import https from 'node:https'; import { napCatVersion } from './version'; import os from 'os'; -export class UmamiTrace { - static napcatVersion = napCatVersion; - static qqversion = '1.0.0'; - static guid = 'default-user'; - static heartbeatInterval: NodeJS.Timeout | null = null; - static website: string = '1fabb2b1-c3a3-4416-b1be-31e2cbdce978'; - static referrer: string = 'https://trace.napneko.icu/'; - static hostname: string = 'trace.napneko.icu'; - static ua: string = ''; +export class UmamiTraceCore { + napcatVersion = napCatVersion; + qqversion = '1.0.0'; + guid = 'default-user'; + heartbeatInterval: NodeJS.Timeout | null = null; + website: string = '1fabb2b1-c3a3-4416-b1be-31e2cbdce978'; + referrer: string = 'https://trace.napneko.icu/'; + hostname: string = 'trace.napneko.icu'; + ua: string = ''; - static init(qqversion: string, guid: string) { + init(qqversion: string, guid: string) { this.qqversion = qqversion; let 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', @@ -37,7 +37,7 @@ export class UmamiTrace { this.startHeartbeat(); } - static identifyUser(guid: string) { + identifyUser(guid: string) { this.guid = guid; const data = { napcat_version: this.napcatVersion, @@ -47,7 +47,7 @@ export class UmamiTrace { this.sendRequest({ website: this.website, ...data }, 'identify'); } - static sendEvent(event: string, data?: object) { + sendEvent(event: string, data?: object) { const env = process.env; const language = env.LANG || env.LANGUAGE || env.LC_ALL || env.LC_MESSAGES; const payload = { @@ -62,7 +62,7 @@ export class UmamiTrace { this.sendRequest(payload); } - static sendTrace(eventName: string) { + sendTrace(eventName: string) { const payload = { website: this.website, hostname: this.hostname, @@ -73,7 +73,7 @@ export class UmamiTrace { this.sendRequest(payload); } - static sendRequest(payload: object, type = 'event') { + sendRequest(payload: object, type = 'event') { const options = { hostname: '104.19.42.72', // 固定 IP 或者从 hostUrl 获取 port: 443, @@ -94,12 +94,12 @@ export class UmamiTrace { }); }); - + request.write(JSON.stringify({ type, payload })); request.end(); } - static startHeartbeat() { + startHeartbeat() { if (this.heartbeatInterval) { clearInterval(this.heartbeatInterval); } @@ -115,10 +115,11 @@ export class UmamiTrace { }, 5 * 60 * 1000); } - static stopHeartbeat() { + stopHeartbeat() { if (this.heartbeatInterval) { clearInterval(this.heartbeatInterval); this.heartbeatInterval = null; } } -} \ No newline at end of file +} +export const UmamiTrace = new UmamiTraceCore(); \ No newline at end of file