diff --git a/src/common/utils/system.ts b/src/common/utils/system.ts new file mode 100644 index 00000000..18b476c8 --- /dev/null +++ b/src/common/utils/system.ts @@ -0,0 +1,74 @@ +import os from 'node:os'; +import path from 'node:path'; +import { networkInterfaces } from 'os'; +import { randomUUID } from 'crypto'; + +// 缓解Win7设备兼容性问题 +let osName: string; +// 设备ID +let machineId: Promise; + +try { + osName = os.hostname(); +} catch (e) { + osName = 'NapCat'; // + crypto.randomUUID().substring(0, 4); +} + +const invalidMacAddresses = new Set([ + '00:00:00:00:00:00', + 'ff:ff:ff:ff:ff:ff', + 'ac:de:48:00:11:22' +]); + +function validateMacAddress(candidate: string): boolean { + // eslint-disable-next-line no-useless-escape + const tempCandidate = candidate.replace(/\-/g, ':').toLowerCase(); + return !invalidMacAddresses.has(tempCandidate); +} + +export async function getMachineId(): Promise { + if (!machineId) { + machineId = (async () => { + const id = await getMacMachineId(); + return id || randomUUID(); // fallback, generate a UUID + })(); + } + + return machineId; +} + +export function getMac(): string { + const ifaces = networkInterfaces(); + for (const name in ifaces) { + const networkInterface = ifaces[name]; + if (networkInterface) { + for (const { mac } of networkInterface) { + if (validateMacAddress(mac)) { + return mac; + } + } + } + } + + throw new Error('Unable to retrieve mac address (unexpected format)'); +} + +async function getMacMachineId(): Promise { + try { + const crypto = await import('crypto'); + const macAddress = getMac(); + return crypto.createHash('sha256').update(macAddress, 'utf8').digest('hex'); + } catch (err) { + return undefined; + } +} + +const homeDir = os.homedir(); + + +export const systemPlatform = os.platform(); +export const cpuArch = os.arch(); +export const systemVersion = os.release(); +export const hostname = osName; +export const downloadsPath = path.join(homeDir, 'Downloads'); +export const systemName = os.type(); \ No newline at end of file diff --git a/src/core/adapters/NodeIDependsAdapter.ts b/src/core/adapters/NodeIDependsAdapter.ts new file mode 100644 index 00000000..7f77a83c --- /dev/null +++ b/src/core/adapters/NodeIDependsAdapter.ts @@ -0,0 +1,29 @@ +import { log } from "@/common/utils/log"; + +interface IDependsAdapter { + onMSFStatusChange(arg1: number, arg2: number): void; + + onMSFSsoError(args: unknown): void; + + getGroupCode(args: unknown): void; +} + +export interface NodeIDependsAdapter extends IDependsAdapter { + // eslint-disable-next-line @typescript-eslint/no-misused-new + new(adapter: IDependsAdapter): NodeIDependsAdapter; +} + +export class DependsAdapter implements IDependsAdapter { + onMSFStatusChange(arg1: number, arg2: number) { + // console.log(arg1, arg2); + // if (arg1 == 2 && arg2 == 2) { + // log("NapCat丢失网络连接,请检查网络") + // } + } + + onMSFSsoError(args: unknown) { + } + + getGroupCode(args: unknown) { + } +} diff --git a/src/core/adapters/NodeIDispatcherAdapter.ts b/src/core/adapters/NodeIDispatcherAdapter.ts new file mode 100644 index 00000000..20557687 --- /dev/null +++ b/src/core/adapters/NodeIDispatcherAdapter.ts @@ -0,0 +1,23 @@ +interface IDispatcherAdapter { + dispatchRequest(arg: unknown): void; + + dispatchCall(arg: unknown): void; + + dispatchCallWithJson(arg: unknown): void; +} + +export interface NodeIDispatcherAdapter extends IDispatcherAdapter { + // eslint-disable-next-line @typescript-eslint/no-misused-new + new(adapter: IDispatcherAdapter): NodeIDispatcherAdapter; +} + +export class DispatcherAdapter implements IDispatcherAdapter { + dispatchRequest(arg: unknown) { + } + + dispatchCall(arg: unknown) { + } + + dispatchCallWithJson(arg: unknown) { + } +} diff --git a/src/core/adapters/NodeIGlobalAdapter.ts b/src/core/adapters/NodeIGlobalAdapter.ts new file mode 100644 index 00000000..29cb906a --- /dev/null +++ b/src/core/adapters/NodeIGlobalAdapter.ts @@ -0,0 +1,48 @@ +interface IGlobalAdapter { + onLog(...args: unknown[]): void; + + onGetSrvCalTime(...args: unknown[]): void; + + onShowErrUITips(...args: unknown[]): void; + + fixPicImgType(...args: unknown[]): void; + + getAppSetting(...args: unknown[]): void; + + onInstallFinished(...args: unknown[]): void; + + onUpdateGeneralFlag(...args: unknown[]): void; + + onGetOfflineMsg(...args: unknown[]): void; +} + +export interface NodeIGlobalAdapter extends IGlobalAdapter { + // eslint-disable-next-line @typescript-eslint/no-misused-new + new(adapter: IGlobalAdapter): NodeIGlobalAdapter; +} + +export class GlobalAdapter implements IGlobalAdapter { + onLog(...args: unknown[]) { + } + + onGetSrvCalTime(...args: unknown[]) { + } + + onShowErrUITips(...args: unknown[]) { + } + + fixPicImgType(...args: unknown[]) { + } + + getAppSetting(...args: unknown[]) { + } + + onInstallFinished(...args: unknown[]) { + } + + onUpdateGeneralFlag(...args: unknown[]) { + } + + onGetOfflineMsg(...args: unknown[]) { + } +} diff --git a/src/core/adapters/index.ts b/src/core/adapters/index.ts new file mode 100644 index 00000000..2db6d288 --- /dev/null +++ b/src/core/adapters/index.ts @@ -0,0 +1,3 @@ +export * from './NodeIDependsAdapter'; +export * from './NodeIDispatcherAdapter'; +export * from './NodeIGlobalAdapter'; diff --git a/src/core/core.ts b/src/core/core.ts index aee5ba08..ba488a2f 100644 --- a/src/core/core.ts +++ b/src/core/core.ts @@ -1,3 +1,5 @@ +import { NodeIQQNTWrapperSession } from "./wrapper/wrapper"; + export enum NCoreWorkMode { Unknown = 0, Shell = 1, @@ -6,6 +8,10 @@ export enum NCoreWorkMode { export class NapCatCore { public WorkMode: NCoreWorkMode = NCoreWorkMode.Unknown; public isInit: boolean = false; + public session: NodeIQQNTWrapperSession | undefined; + get IsInit(): boolean { + return this.isInit; + } } export class NapCatShell extends NapCatCore { public WorkMode: NCoreWorkMode = NCoreWorkMode.Shell; diff --git a/src/core/external/appid.json b/src/core/external/appid.json new file mode 100644 index 00000000..44fd31c7 --- /dev/null +++ b/src/core/external/appid.json @@ -0,0 +1,50 @@ +{ + "3.1.2-13107": { + "appid": 537146866, + "qua": "V1_LNX_NQ_3.1.2-13107_RDM_B" + }, + "3.2.10-25765": { + "appid": 537234773, + "qua": "V1_LNX_NQ_3.2.10_25765_GW_B" + }, + "3.2.12-26702": { + "appid": 537237950, + "qua": "V1_LNX_NQ_3.2.12_26702_GW_B" + }, + "3.2.12-26740": { + "appid": 537237950, + "qua": "V1_WIN_NQ_9.9.15_26740_GW_B" + }, + "9.9.11-24815": { + "appid": 537226656, + "qua": "V1_WIN_NQ_9.9.11_24815_GW_B" + }, + "9.9.12-25493": { + "appid": 537231759, + "qua": "V1_WIN_NQ_9.9.12_25493_GW_B" + }, + "9.9.12-25765": { + "appid": 537234702, + "qua": "V1_WIN_NQ_9.9.12_25765_GW_B" + }, + "9.9.12-26299": { + "appid": 537234826, + "qua": "V1_WIN_NQ_9.9.12_26299_GW_B" + }, + "9.9.12-26339": { + "appid": 537234826, + "qua": "V1_WIN_NQ_9.9.12_26339_GW_B" + }, + "9.9.12-26466": { + "appid": 537234826, + "qua": "V1_WIN_NQ_9.9.12_26466_GW_B" + }, + "9.9.15-26702": { + "appid": 537237765, + "qua": "V1_WIN_NQ_9.9.15_26702_GW_B" + }, + "9.9.15-26740": { + "appid": 537237765, + "qua": "V1_WIN_NQ_9.9.15_26702_GW_B" + } +} \ No newline at end of file diff --git a/src/core/wrapper/wrapper.ts b/src/core/wrapper/wrapper.ts new file mode 100644 index 00000000..863bc302 --- /dev/null +++ b/src/core/wrapper/wrapper.ts @@ -0,0 +1,347 @@ +import path from 'node:path'; +import fs from 'node:fs'; +import { + NodeIDependsAdapter, + NodeIDispatcherAdapter, + NodeIGlobalAdapter, +} from '../adapters'; +import { + NodeIKernelSessionListener, + NodeIKernelMsgListener, + NodeIKernelLoginListener, + NodeIKernelBuddyListener, + NodeIKernelGroupListener, + NodeIKernelProfileListener, +} from '../listeners'; +import { + NodeIKernelLoginService, + NodeIKernelMsgService, + NodeIKernelBuddyService, + NodeIKernelGroupService, + NodeIKernelProfileService, + NodeIKernelProfileLikeService, + NodeIKernelTicketService, + NodeIKernelTipOffService, + NodeIKernelRichMediaService, + NodeIKernelAvatarService, +} from '../services'; +import { NodeIKernelStorageCleanService } from '../services/NodeIKernelStorageCleanService'; +import { NodeIKernelRobotService } from '../services/NodeIKernelRobotService'; +import { NodeIKernelNodeMiscService } from '../services/NodeIKernelNodeMiscService'; +import { NodeIKernelUixConvertService } from '../services/NodeIKernelUixConvertService'; +import { NodeIKernelMsgBackupService } from '../services/NodeIKernelMsgBackupService'; +import { NodeIKernelAlbumService } from '../services/NodeIKernelAlbumService'; +import { NodeIKernelTianShuService } from '../services/NodeIKernelTianShuService'; +import { NodeIKernelUnitedConfigService } from '../services/NodeIKernelUnitedConfigService'; +import { NodeIKernelSearchService } from '../services/NodeIKernelSearchService'; +import { NodeIKernelCollectionService } from '../services/NodeIKernelCollectionService'; +import { NodeIKernelRecentContactService } from '../services/NodeIKernelRecentContactService'; +import { NodeIKernelMSFService } from '../services/NodeIKernelMSFService'; +import { NodeIkernelTestPerformanceService } from '../services/NodeIkernelTestPerformanceService'; +import { NodeIKernelECDHService } from '../services/NodeIKernelECDHService'; +export interface NodeQQNTWrapperUtil { + // eslint-disable-next-line @typescript-eslint/no-misused-new + new(): NodeQQNTWrapperUtil + + getNTUserDataInfoConfig(): string + + emptyWorkingSet(n: number): void + + getSsoCmdOfOidbReq(arg1: number, arg2: number): unknown, + + getSsoBufferOfOidbReq(...args: unknown[]): unknown,//有点看不懂参数定义 待补充 好像是三个参数 + + getOidbRspInfo(arg: string): unknown,//可能是错的 + + getFileSize(path: string): Promise,//直接的猜测 + + genFileMd5Buf(arg: string): unknown,//可能是错的 + + genFileMd5Hex(path: string): unknown,//直接的猜测 + + genFileShaBuf(path: string): unknown,//直接的猜测 + + genFileCumulateSha1(path: string): unknown,//直接的猜测 + + genFileShaHex(path: string): unknown,//直接的猜测 + + fileIsExist(path: string): unknown, + + startTrace(path: string): unknown,//可能是错的 + + copyFile(src: string, dst: string): unknown, + + genFileShaAndMd5Hex(path: string, unknown: number): unknown,//可能是错的 + + setTraceInfo(unknown: Object): unknown, + + encodeOffLine(unknown: Object): unknown, + + decodeOffLine(arg: string): unknown,//可能是错的 传递hex + + DecoderRecentInfo(arg: string): unknown,//可能是错的 传递hex + + getPinyin(arg0: string, arg1: boolean): unknown, + + matchInPinyin(arg0: any[], arg1: string): unknown,//参数特复杂 arg0是个复杂数据类型 + + makeDirByPath(arg0: string): unknown, + + emptyWorkingSet(arg0: number): unknown,//参数是UINT32 + + runProcess(arg0: string, arg1: boolean): unknown, + + runProcessArgs(arg0: string, arg1: { [key: string]: string; }, arg2: boolean): unknown, + + calcThumbSize(arg0: number, arg1: number, arg2: Object): unknown, + + fullWordToHalfWord(arg0: string): unknown, + + getNTUserDataInfoConfig(): unknown, + + pathIsReadableAndWriteable(path: string): unknown,//直接的猜测 + + resetUserDataSavePathToDocument(): unknown, + + getSoBuildInfo(): any,//例如 0[0]_d491dc01e0a_0 + + registerCountInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown, + + registerValueInstruments(arg0: string, arg1: string[], arg2: number, arg3: number): unknown, + + registerValueInstrumentsWithBoundary(arg0: string, arg1: unknown, arg2: unknown, arg3: number, arg4: number): unknown, + + reportCountIndicators(arg0: string, arg1: Map, arg2: string, arg3: number, arg4: boolean): unknown, + + reportValueIndicators(arg0: string, arg1: Map, arg2: string, arg3: boolean, arg4: number): unknown, + + checkNewUserDataSaveDirAvailable(arg0: string): unknown, + + copyUserData(arg0: string, arg1: string): Promise, + + setUserDataSaveDirectory(arg0: string): Promise, + + hasOtherRunningQQProcess(): boolean, + + quitAllRunningQQProcess(arg: boolean): unknown, + + checkNvidiaConfig(): unknown, + + repairNvidiaConfig(): unknown, + + getNvidiaDriverVersion(): unknown, + + isNull(): unknown +} + +export interface NodeIQQNTWrapperSession { + // eslint-disable-next-line @typescript-eslint/no-misused-new + new(): NodeIQQNTWrapperSession; + + init( + wrapperSessionInitConfig: WrapperSessionInitConfig, + nodeIDependsAdapter: NodeIDependsAdapter, + nodeIDispatcherAdapter: NodeIDispatcherAdapter, + nodeIKernelSessionListener: NodeIKernelSessionListener + ): void; + + startNT(n: 0): void; + + startNT(): void; + + getBdhUploadService(): unknown; + + getECDHService(): NodeIKernelECDHService; + + getMsgService(): NodeIKernelMsgService; + + getProfileService(): NodeIKernelProfileService; + + getProfileLikeService(): NodeIKernelProfileLikeService; + + getGroupService(): NodeIKernelGroupService; + + getStorageCleanService(): NodeIKernelStorageCleanService; + + getBuddyService(): NodeIKernelBuddyService; + + getRobotService(): NodeIKernelRobotService; + + getTicketService(): NodeIKernelTicketService; + + getTipOffService(): NodeIKernelTipOffService; + + getNodeMiscService(): NodeIKernelNodeMiscService; + + getRichMediaService(): NodeIKernelRichMediaService; + + getMsgBackupService(): NodeIKernelMsgBackupService; + + getAlbumService(): NodeIKernelAlbumService; + + getTianShuService(): NodeIKernelTianShuService; + + getUnitedConfigService(): NodeIKernelUnitedConfigService; + + getSearchService(): NodeIKernelSearchService; + + getDirectSessionService(): unknown; + + getRDeliveryService(): unknown; + + getAvatarService(): NodeIKernelAvatarService; + + getFeedChannelService(): unknown; + + getYellowFaceService(): unknown; + + getCollectionService(): NodeIKernelCollectionService; + + getSettingService(): unknown; + + getQiDianService(): unknown; + + getFileAssistantService(): unknown; + + getGuildService(): unknown; + + getSkinService(): unknown; + + getTestPerformanceService(): NodeIkernelTestPerformanceService; + + getQQPlayService(): unknown; + + getDbToolsService(): unknown; + + getUixConvertService(): NodeIKernelUixConvertService; + + getOnlineStatusService(): unknown; + + getRemotingService(): unknown; + + getGroupTabService(): unknown; + + getGroupSchoolService(): unknown; + + getLiteBusinessService(): unknown; + + getGuildMsgService(): unknown; + + getLockService(): unknown; + + getMSFService(): NodeIKernelMSFService; + + getGuildHotUpdateService(): unknown; + + getAVSDKService(): unknown; + + getRecentContactService(): NodeIKernelRecentContactService; + + getConfigMgrService(): unknown; +} + +export interface EnginInitDesktopConfig { + base_path_prefix: string, + platform_type: 3, + app_type: 4, + app_version: string, + os_version: string, + use_xlog: true, + qua: string, + global_path_config: { + desktopGlobalPath: string, + }, + thumb_config: { maxSide: 324, minSide: 48, longLimit: 6, density: 2 } +} + +export interface NodeIQQNTWrapperEngine { + // eslint-disable-next-line @typescript-eslint/no-misused-new + new(): NodeIQQNTWrapperEngine; + initWithDeskTopConfig(config: EnginInitDesktopConfig, nodeIGlobalAdapter: NodeIGlobalAdapter): void; +} + +export interface WrapperNodeApi { + [key: string]: any; + + NodeIKernelBuddyListener: NodeIKernelBuddyListener; + NodeIKernelGroupListener: NodeIKernelGroupListener; + NodeQQNTWrapperUtil: NodeQQNTWrapperUtil; + NodeIQQNTWrapperSession: NodeIQQNTWrapperSession; + NodeIKernelMsgListener: NodeIKernelMsgListener; + NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine; + NodeIGlobalAdapter: NodeIGlobalAdapter; + NodeIDependsAdapter: NodeIDependsAdapter; + NodeIDispatcherAdapter: NodeIDispatcherAdapter; + NodeIKernelSessionListener: NodeIKernelSessionListener; + NodeIKernelLoginService: NodeIKernelLoginService; + NodeIKernelLoginListener: NodeIKernelLoginListener; + NodeIKernelProfileService: NodeIKernelProfileService; + NodeIKernelProfileListener: NodeIKernelProfileListener; +} +export enum PlatformType { + KUNKNOWN, + KANDROID, + KIOS, + KWINDOWS, + KMAC +} +export enum DeviceType { + KUNKNOWN, + KPHONE, + KPAD, + KCOMPUTER +} +//推送类型 +export enum VendorType { + KNOSETONIOS = 0, + KSUPPORTGOOGLEPUSH = 99, + KSUPPORTHMS = 3, + KSUPPORTOPPOPUSH = 4, + KSUPPORTTPNS = 2, + KSUPPORTVIVOPUSH = 5, + KUNSUPPORTANDROIDPUSH = 1 +} +export interface WrapperSessionInitConfig { + selfUin: string + selfUid: string + desktopPathConfig: { + account_path: string // 可以通过NodeQQNTWrapperUtil().getNTUserDataInfoConfig()获取 + } + clientVer: string // 9.9.8-22355 + a2: string, + d2: string, + d2Key: string, + machineId: string, + platform: PlatformType, // 3是Windows? + platVer: string, // 系统版本号, 应该可以固定 + appid: string, + rdeliveryConfig: { + appKey: string, + systemId: number, + appId: string, + logicEnvironment: string, + platform: PlatformType, + language: string, + sdkVersion: string, + userId: string, + appVersion: string, + osVersion: string, + bundleId: string, + serverUrl: string, + fixedAfterHitKeys: string[] + } + defaultFileDownloadPath: string, // 这个可以通过环境变量获取? + deviceInfo: { + guid: string, + buildVer: string, + localId: number, + devName: string, + devType: string, + vendorName: string, + osVer: string, + vendorOsName: string, + setMute: boolean, + vendorType: VendorType + }, + deviceConfig: '{"appearance":{"isSplitViewMode":true},"msg":{}}' +} \ No newline at end of file