mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
@@ -1,40 +1,92 @@
|
|||||||
import https from 'node:https';
|
import https from 'node:https';
|
||||||
import { napCatVersion } from './version';
|
import { napCatVersion } from './version';
|
||||||
|
import os from 'os';
|
||||||
|
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 = '';
|
||||||
|
|
||||||
export class umamiTrace {
|
init(qqversion: string, guid: string) {
|
||||||
static napcatVersion = napCatVersion;
|
|
||||||
static qqversion = '1.0.0';
|
|
||||||
|
|
||||||
static init(qqversion: string) {
|
|
||||||
this.qqversion = qqversion;
|
this.qqversion = qqversion;
|
||||||
setInterval(() => {
|
let UaList = {
|
||||||
this.trackEvent('heartbeat');
|
'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',
|
||||||
}, 5 * 60 * 1000);
|
'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',
|
||||||
|
|
||||||
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 request = https.request({
|
try {
|
||||||
hostname: '104.19.42.72',// 固定 IP
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendRequest(payload: object, type = 'event') {
|
||||||
|
const options = {
|
||||||
|
hostname: '104.19.42.72', // 固定 IP 或者从 hostUrl 获取
|
||||||
port: 443,
|
port: 443,
|
||||||
path: '/api/send',
|
path: '/api/send',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Host": "umami.napneko.icu",
|
"Host": "umami.napneko.icu",
|
||||||
"Content-Type": "application/json",
|
"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) => {
|
res.on('error', (error) => {
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -43,7 +95,31 @@ export class umamiTrace {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
request.write(JSON.stringify(StatesData));
|
request.write(JSON.stringify({ type, payload }));
|
||||||
request.end();
|
request.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopHeartbeat() {
|
||||||
|
if (this.heartbeatInterval) {
|
||||||
|
clearInterval(this.heartbeatInterval);
|
||||||
|
this.heartbeatInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
export const UmamiTrace = new UmamiTraceCore();
|
@@ -30,7 +30,7 @@ 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';
|
import { UmamiTrace } from '@/common/umami';
|
||||||
export * from './wrapper';
|
export * from './wrapper';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
export * from './services';
|
export * from './services';
|
||||||
@@ -156,9 +156,9 @@ export class NapCatCore {
|
|||||||
|
|
||||||
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => {
|
||||||
if (this.context.workingEnv === NapCatCoreWorkingEnv.Framework) {
|
if (this.context.workingEnv === NapCatCoreWorkingEnv.Framework) {
|
||||||
umamiTrace.trackEvent('framework/kickoff');
|
UmamiTrace.sendEvent('framework/kickoff');
|
||||||
} else {
|
} else {
|
||||||
umamiTrace.trackEvent('shell/kickoff');
|
UmamiTrace.sendEvent('shell/kickoff');
|
||||||
}
|
}
|
||||||
// 下线通知
|
// 下线通知
|
||||||
this.context.logger.logError('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc);
|
this.context.logger.logError('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc);
|
||||||
|
@@ -9,7 +9,7 @@ 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';
|
import { UmamiTrace } from '@/common/umami';
|
||||||
|
|
||||||
//Framework ES入口文件
|
//Framework ES入口文件
|
||||||
export async function getWebUiUrl() {
|
export async function getWebUiUrl() {
|
||||||
@@ -26,7 +26,7 @@ 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.trackEvent('framework/error', err.message);
|
UmamiTrace.sendEvent('framework/error', { name: err.name });
|
||||||
console.log('[NapCat] [Error] Unhandled Exception:', err.message);
|
console.log('[NapCat] [Error] Unhandled Exception:', err.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -34,16 +34,14 @@ export async function NCoreInitFramework(
|
|||||||
console.log('[NapCat] [Error] unhandledRejection:', reason);
|
console.log('[NapCat] [Error] unhandledRejection:', reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('exit', (code: number) => {
|
|
||||||
umamiTrace.trackEvent('framework/exit', code.toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
const pathWrapper = new NapCatPathWrapper();
|
const pathWrapper = new NapCatPathWrapper();
|
||||||
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());
|
||||||
umamiTrace.init(basicInfoWrapper.getFullQQVesion());
|
let guid = loginService.getMachineGuid();
|
||||||
umamiTrace.trackEvent('framework/login');
|
UmamiTrace.init(basicInfoWrapper.getFullQQVesion(), guid);
|
||||||
|
UmamiTrace.sendTrace('framework/boot');
|
||||||
|
UmamiTrace.sendEvent('framework/login');
|
||||||
//直到登录成功后,执行下一步
|
//直到登录成功后,执行下一步
|
||||||
const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
|
const selfInfo = await new Promise<SelfInfo>((resolveSelfInfo) => {
|
||||||
const loginListener = new NodeIKernelLoginListener();
|
const loginListener = new NodeIKernelLoginListener();
|
||||||
|
@@ -29,11 +29,11 @@ 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';
|
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.trackEvent('framework/error', err.message);
|
UmamiTrace.sendEvent('framework/error', { name: err.name, message: 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 +153,7 @@ async function handleLogin(
|
|||||||
};
|
};
|
||||||
|
|
||||||
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number, errMsg: string) => {
|
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) {
|
if (!isLogined) {
|
||||||
logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg);
|
logger.logError('[Core] [Login] Login Error,ErrCode: ', errCode, ' ErrMsg:', errMsg);
|
||||||
if (errType == 1 && errCode == 3) {
|
if (errType == 1 && errCode == 3) {
|
||||||
@@ -164,7 +164,7 @@ async function handleLogin(
|
|||||||
};
|
};
|
||||||
|
|
||||||
loginListener.onLoginFailed = (args) => {
|
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());
|
logger.logError('[Core] [Login] Login Error , ErrInfo: ', args.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -270,13 +270,9 @@ export async function NCoreInitShell() {
|
|||||||
const pathWrapper = new NapCatPathWrapper();
|
const pathWrapper = new NapCatPathWrapper();
|
||||||
const logger = new LogWrapper(pathWrapper.logsPath);
|
const logger = new LogWrapper(pathWrapper.logsPath);
|
||||||
handleUncaughtExceptions(logger);
|
handleUncaughtExceptions(logger);
|
||||||
process.on('exit', (code: number) => {
|
|
||||||
umamiTrace.trackEvent('shell/exit', code.toString());
|
|
||||||
});
|
|
||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
|
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVesion());
|
||||||
umamiTrace.init(basicInfoWrapper.getFullQQVesion());
|
|
||||||
umamiTrace.trackEvent('shell/boot');
|
|
||||||
const o3Service = wrapper.NodeIO3MiscService.get();
|
const o3Service = wrapper.NodeIO3MiscService.get();
|
||||||
o3Service.addO3MiscListener(new NodeIO3MiscListener());
|
o3Service.addO3MiscListener(new NodeIO3MiscListener());
|
||||||
|
|
||||||
@@ -302,9 +298,10 @@ 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());
|
||||||
|
UmamiTrace.sendTrace('shell/boot');
|
||||||
const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList);
|
const selfInfo = await handleLogin(loginService, logger, pathWrapper, quickLoginUin, historyLoginList);
|
||||||
umamiTrace.trackEvent('shell/login');
|
UmamiTrace.sendEvent('shell/login');
|
||||||
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')));
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user