SnowCat 雪猫底层全源码:NapCat 4.18.28 底座 + SnowLuma 能力移植 + V8 通道适配 9.9.37

26 包 monorepo(TS 全源码):
- snowcat-core: NT 内核 API 封装 + listener(含 9.9.37 六回调适配)+ Packet 发包
- snowcat-onebot: OneBot v11 全动作 + MCP Streamable HTTP 适配器 + 稳定线
- snowcat-webui-frontend/backend: 雪猫控制台(React+HeroUI)
- snowcat-shell: 单文件 bundle 出口(vite inlineDynamicImports)
- snowcat-native: napi2native / MoeHoo / ffmpeg / pty 运行时
- 56 项 SnowLuma 独门动作移植(qzone 全家桶/群相册/装扮/闪照/收藏)
- 工具件: 解包器/启动器/便携制作器/doctor

GitHub 关联已全量指向 DandanLLab/SnowCatQQ
This commit is contained in:
天天的鸟蛋蛋 committed 2026-11-12 06:27:36 +08:00
commit b9655a5ab1
1048 files changed
+130851

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
const process = require('process');
const os = require('os');
const path = require('path');
// 保存原始dlopen
const dlopenOrig = process.dlopen;
let wrapperSession;
let wrapperNodeApi;
let wrapperLoginService;
let getWebUiUrlFunc;
let ncCallback = () => { };
let napCatInitialized = false; // 添加一个标志
function createServiceProxy (ServiceName) {
return new Proxy(() => { }, {
get: (target, FunctionName) => {
if (ServiceName === 'NodeIQQNTWrapperSession' && FunctionName === 'create') {
return () => new Proxy({}, {
get: function (target, ClassFunName, receiver) {
return function () {
if (ClassFunName === 'init') {
const origin = arguments[3].onOpentelemetryInit;
arguments[3].onOpentelemetryInit = function (result) {
origin(...arguments);
if (result.is_init) {
ncCallback();
}
};
}
const ret = wrapperSession[ClassFunName](...arguments);
return ret;
};
},
});
}
if (ServiceName === 'NodeIKernelLoginService' && FunctionName === 'get') {
return () => wrapperLoginService;
}
return wrapperNodeApi[ServiceName][FunctionName];
},
});
}
function clearHook () {
process.dlopen = dlopenOrig;
}
async function initializeNapCat () {
console.log('[NapCat] [Info] 开始初始化NapCat');
try {
const currentPath = path.dirname(__filename);
const { NCoreInitFramework, getWebUiUrl } = await import('file://' + path.join(currentPath, './napcat.mjs'));
getWebUiUrlFunc = getWebUiUrl;
await NCoreInitFramework(wrapperSession, wrapperLoginService, (callback) => { ncCallback = callback; });
} catch (error) {
console.log('[NapCat] [Error] 初始化NapCat', error);
}
}
process.dlopen = function (module, filename, flags = os.constants.dlopen.RTLD_LAZY) {
const dlopenRet = dlopenOrig(module, filename, flags);
if (!filename.includes('wrapper.node') || napCatInitialized) return dlopenRet;
napCatInitialized = true; // 初始化完成后设置标志
clearHook();
wrapperNodeApi = module.exports;
wrapperLoginService = wrapperNodeApi.NodeIKernelLoginService.get();
wrapperSession = wrapperNodeApi.NodeIQQNTWrapperSession.create();
initializeNapCat().then().catch();
module.exports = new Proxy({}, {
get: (target, ServiceName) => {
if (ServiceName === 'NodeIKernelLoginService' || ServiceName === 'NodeIQQNTWrapperSession') {
return createServiceProxy(ServiceName);
}
return wrapperNodeApi[ServiceName];
},
});
return dlopenRet;
};
module.exports = {
NCgetWebUiUrl: async () => {
if (!getWebUiUrlFunc) {
console.log('[NapCat] [Error] 未初始化完成');
return '';
}
return await getWebUiUrlFunc();
},
};
@@ -0,0 +1,25 @@
// const fs = require('fs');
const path = require('path');
async function initializeNapCat (session, loginService, registerCallback) {
// const logFile = path.join(currentPath, 'napcat.log');
console.log('[NapCat] [Info] 开始初始化NapCat');
// fs.writeFileSync(logFile, '', { flag: 'w' });
// fs.writeFileSync(logFile, '[NapCat] [Info] NapCat 初始化成功\n', { flag: 'a' });
try {
const currentPath = path.dirname(__filename);
const { NCoreInitFramework } = await import('file://' + path.join(currentPath, './napcat.mjs'));
await NCoreInitFramework(session, loginService, (callback) => { registerCallback(callback); });
} catch (error) {
console.log('[NapCat] [Error] 初始化NapCat', error);
// fs.writeFileSync(logFile, `[NapCat] [Error] 初始化NapCat失败: ${error.message}\n`, { flag: 'a' });
}
}
module.exports = {
initializeNapCat,
};
+36
View File
@@ -0,0 +1,36 @@
{
"name": "snowcat-framework",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "index.ts",
"scripts": {
"build": "vite build",
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"exports": {
".": {
"import": "./index.ts"
},
"./*": {
"import": "./*"
}
},
"dependencies": {
"snowcat-core": "workspace:*",
"snowcat-common": "workspace:*",
"snowcat-database": "workspace:*",
"snowcat-protobuf": "workspace:*",
"snowcat-adapter": "workspace:*",
"snowcat-webui-backend": "workspace:*",
"snowcat-vite": "workspace:*",
"snowcat-qrcode": "workspace:*",
"json5": "^2.2.3"
},
"devDependencies": {
"@types/node": "^22.0.1"
},
"engines": {
"node": ">=18.0.0"
}
}
+441
View File
@@ -0,0 +1,441 @@
import { SnowCatPathWrapper } from 'snowcat-common/src/path';
import { InitWebUi, WebUiConfig, webUiRuntimePort } from 'snowcat-webui-backend/index';
import { SnowCatAdapterManager } from 'snowcat-adapter';
import { NativePacketHandler } from 'snowcat-core/packet/handler/client';
import { Napi2NativeLoader } from 'snowcat-core/packet/handler/napi2nativeLoader';
import { loadSnowCatConfig } from '@/snowcat-core/helper/config';
import { FFmpegService } from 'snowcat-core/helper/ffmpeg/ffmpeg';
import { logSubscription, LogWrapper } from 'snowcat-core/helper/log';
import { QQBasicInfoWrapper } from '@/snowcat-core/helper/qq-basic-info';
import { InstanceContext, loadQQWrapper, SnowCatCore, SnowCatCoreWorkingEnv, NodeIKernelLoginListener, NodeIKernelLoginService, NodeIQQNTWrapperSession, SelfInfo, WrapperNodeApi } from '@/snowcat-core';
import { proxiedListenerOf } from '@/snowcat-core/helper/proxy-handler';
import { statusHelperSubscription } from '@/snowcat-core/helper/status';
import { applyPendingUpdates } from '@/snowcat-webui-backend/src/api/UpdateNapCat';
import { WebUiDataRuntime } from '@/snowcat-webui-backend/src/helper/Data';
import { NapProtoMsg } from 'snowcat-protobuf';
import { OidbSvcTrpcTcpBase, OidbSvcTrpcTcp0XCDE_2RespBody } from '@/snowcat-core/packet/transformer/proto';
// Framework ES入口文件
export async function getWebUiUrl () {
const WebUiConfigData = (await WebUiConfig.GetWebUIConfig());
return 'http://127.0.0.1:' + webUiRuntimePort + '/webui/?token=' + encodeURIComponent(WebUiConfigData.token);
}
export async function NCoreInitFramework (
session: NodeIQQNTWrapperSession,
loginService: NodeIKernelLoginService,
registerInitCallback: (callback: () => void) => void
) {
// 在进入本层前是否登录未进行判断
console.log('SnowCat Framework App Loading...');
process.on('uncaughtException', (err) => {
console.log('[SnowCat] [Error] Unhandled Exception:', err.message);
});
process.on('unhandledRejection', (reason) => {
console.log('[SnowCat] [Error] unhandledRejection:', reason);
});
const pathWrapper = new SnowCatPathWrapper();
const logger = new LogWrapper(pathWrapper.logsPath);
await applyPendingUpdates(pathWrapper, logger);
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.QQMainPath, basicInfoWrapper.getFullQQVersion());
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
const napi2nativeLoader = new Napi2NativeLoader({ logger }); // 初始化 Napi2NativeLoader 用于后续使用
const snowcatConfig = loadSnowCatConfig(pathWrapper.configPath);
// console.log('[SnowCat] [Napi2NativeLoader]', napi2nativeLoader.nativeExports.enableAllBypasses?.());
if (process.env['NAPCAT_DISABLE_BYPASS'] !== '1') {
const bypassOptions = snowcatConfig.bypass ?? {};
const bypassEnabled = napi2nativeLoader.nativeExports.enableAllBypasses?.(bypassOptions);
if (bypassEnabled) {
logger.log('[SnowCat] Napi2NativeLoader: 已启用Bypass');
}
logger.log('[SnowCat] Napi2NativeLoader: Framework模式Bypass配置:', bypassOptions);
} else {
logger.log('[SnowCat] Napi2NativeLoader: Bypass已通过环境变量禁用');
}
// nativePacketHandler.onAll((packet) => {
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
// });
await nativePacketHandler.init(basicInfoWrapper.getFullQQVersion(), snowcatConfig.o3HookMode === 1);
// 在 init 之后注册监听器
// 登录前监听 OidbSvcTrpcTcp.0xcde_2 数据包,获取数据库 passphrase
let dbPassphrase: string | undefined;
nativePacketHandler.onCmd('OidbSvcTrpcTcp.0xcde_2', ({ type, hex_data }) => {
if (type !== 1) return; // 仅处理接收方向
try {
const raw = Buffer.from(hex_data, 'hex');
const base = new NapProtoMsg(OidbSvcTrpcTcpBase).decode(raw);
if (base.body && base.body.length > 0) {
const body = new NapProtoMsg(OidbSvcTrpcTcp0XCDE_2RespBody).decode(base.body);
if (body.inner?.value) {
dbPassphrase = body.inner.value;
logger.log('[SnowCat] 已启用数据库辅助支持能力');
}
}
} catch (e) {
logger.logError('[SnowCat] [0xCDE_2] 解析失败:', e);
}
});
// 初始化 FFmpeg 服务
await FFmpegService.init(pathWrapper.binaryPath, logger);
// 提前启动 WebUI,使其在登录前可用,支持通过 WebUI 操控登录过程
WebUiDataRuntime.setWorkingEnv(SnowCatCoreWorkingEnv.Framework);
InitWebUi(logger, pathWrapper, logSubscription, statusHelperSubscription).then().catch(e => logger.logError(e));
// 直到登录成功后,执行下一步
const selfInfo = await new Promise<SelfInfo>((resolve) => {
const loginContext = { isLogined: false };
const loginListener = new NodeIKernelLoginListener();
loginListener.onUserLoggedIn = (userid: string) => {
const tips = `当前账号(${userid})已登录,无法重复登录`;
logger.logError(tips);
WebUiDataRuntime.setQQLoginError(tips);
};
loginListener.onQRCodeLoginSucceed = async (loginResult) => {
loginContext.isLogined = true;
WebUiDataRuntime.setQQLoginStatus(true);
WebUiDataRuntime.setQQLoginError('');
const oneBotContext = WebUiDataRuntime.getOneBotContext();
if (oneBotContext?.core?.selfInfo) {
oneBotContext.core.selfInfo.online = true;
WebUiDataRuntime.setQQLoginPhase('ready');
} else {
WebUiDataRuntime.setQQLoginPhase('initializing');
}
await new Promise<void>(resolve => {
registerInitCallback(() => resolve());
});
resolve({
uid: loginResult.uid,
uin: loginResult.uin,
nick: '', // 获取不到
online: true,
});
};
loginListener.onQRCodeGetPicture = ({ qrcodeUrl }) => {
WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl);
WebUiDataRuntime.setQQLoginError('');
WebUiDataRuntime.setQQLoginPhase('waiting_qrcode');
logger.log('[SnowCat] [Framework] 二维码已更新, URL:', qrcodeUrl);
};
loginListener.onQRCodeSessionFailed = (errType: number, errCode: number) => {
if (!loginContext.isLogined) {
logger.logError('[SnowCat] [Framework] [Login] QRCode Session Failed, ErrType:', errType, 'ErrCode:', errCode);
if (errType === 1 && errCode === 3) {
WebUiDataRuntime.setQQLoginError('二维码已过期,请刷新');
}
}
};
loginListener.onLoginFailed = (...args) => {
const errInfo = JSON.stringify(args);
logger.logError('[SnowCat] [Framework] [Login] Login Failed, ErrInfo:', errInfo);
WebUiDataRuntime.setQQLoginError(`登录失败: ${errInfo}`);
};
loginListener.onLoginConnected = () => {
logger.log('[SnowCat] [Framework] 登录服务已连接');
// 注册 WebUI 登录操作回调
registerWebUiLoginCallbacks(loginService, loginContext, logger);
// 获取历史登录列表供 WebUI 使用
loginService.getLoginList().then((res) => {
const list = res.LocalLoginInfoList.filter((item) => item.isQuickLogin);
WebUiDataRuntime.setQQQuickLoginList(list.map((item) => item.uin.toString()));
WebUiDataRuntime.setQQNewLoginList(list);
}).catch(e => logger.logError('[SnowCat] [Framework] 获取登录列表失败:', e));
if (!loginContext.isLogined) {
const requested = loginService.getQRCodePicture();
logger.logWarn('[SnowCat] [Framework] 登录服务连接完成,正在获取二维码', { requested, msfStatus: loginService.getMsfStatus() });
}
};
loginListener.onQRCodeSessionUserScaned = () => {
WebUiDataRuntime.setQQLoginPhase('qrcode_scanned');
logger.log('[SnowCat] [Framework] 二维码已被扫描,等待确认...');
};
loginListener.onPasswordLoginFailed = (...args) => {
logger.logError('[SnowCat] [Framework] 密码登录失败:', ...args);
};
loginService.addKernelLoginListener(proxiedListenerOf(loginListener, logger));
});
// 过早进入会导致addKernelMsgListener等Listener添加失败
// await sleep(2500);
// 初始化 SnowCatFramework
const loaderObject = new SnowCatFramework(wrapper, session, logger, selfInfo, basicInfoWrapper, pathWrapper, nativePacketHandler, napi2nativeLoader);
// 将捕获的 passphrase 设置到 core 上,供 DatabaseApi 使用
if (dbPassphrase) {
loaderObject.core.dbPassphrase = dbPassphrase;
}
await loaderObject.core.initCore();
// 登录成功后设置数据路径
WebUiDataRuntime.setQQDataPath(loaderObject.core.dataPath);
// // 测试 DatabaseApi
// if (dbPassphrase) {
// const dbApi = loaderObject.core.apis.DatabaseApi;
// const sqliteOk = await dbApi.isSqliteAvailable();
// if (sqliteOk) {
// try {
// const result = dbApi.readDatabase('nt_msg.db');
// if (result) {
// logger.log('[SnowCat] [Database] nt_msg.db:\n' + dbApi.formatResults([result]));
// }
// } catch (e) {
// logger.logError('[SnowCat] [Database] 读取失败:', e);
// }
// }
// }
// 使用 SnowCatAdapterManager 统一管理协议适配器
const adapterManager = new SnowCatAdapterManager(loaderObject.core, loaderObject.context, pathWrapper);
await adapterManager.initAdapters();
// 注册 OneBot 适配器到 WebUiDataRuntime,供调试功能使用
const oneBotAdapter = adapterManager.getOneBotAdapter();
if (oneBotAdapter) {
WebUiDataRuntime.setOneBotContext(oneBotAdapter);
if (WebUiDataRuntime.getQQLoginStatus()) {
WebUiDataRuntime.setQQLoginPhase('ready');
}
}
// 监听下线通知并同步到 WebUI(兜底保障,防止 WebUI 状态不同步)
let restartRequested = false;
loaderObject.core.event.on('KickedOffLine', (tips: string) => {
WebUiDataRuntime.setQQLoginStatus(false);
WebUiDataRuntime.setQQLoginQrcodeURL('');
WebUiDataRuntime.setQQLoginError(tips);
WebUiDataRuntime.setQQLoginPhase('reconnecting');
if (restartRequested) return;
restartRequested = true;
setTimeout(() => {
WebUiDataRuntime.requestRestartProcess().then((restartResult) => {
logger.logWarn('[SnowCat] [Framework] 账号被踢下线,正在重启 Worker 以重新创建 QQ 登录服务', restartResult);
if (!restartResult.result) {
restartRequested = false;
WebUiDataRuntime.setQQLoginPhase('offline');
WebUiDataRuntime.setQQLoginError(`${tips}\n登录服务重启失败:${restartResult.message}`);
}
}).catch((error) => {
restartRequested = false;
WebUiDataRuntime.setQQLoginPhase('offline');
WebUiDataRuntime.setQQLoginError(`${tips}\n登录服务重启失败:${(error as Error).message}`);
});
}, 3_500);
});
}
function registerWebUiLoginCallbacks (
loginService: NodeIKernelLoginService,
loginContext: { isLogined: boolean; },
logger: LogWrapper
) {
// 刷新二维码
WebUiDataRuntime.setRefreshQRCodeCallback(async () => {
loginContext.isLogined = false;
const msfStatus = loginService.getMsfStatus();
if (msfStatus === 2) {
WebUiDataRuntime.setQQLoginPhase('reconnecting');
const connected = loginService.connect();
logger.logWarn('[SnowCat] [Framework] 登录服务离线,正在重连以获取二维码', { connected, msfStatus });
if (!connected) {
throw new Error('QQ 登录服务无法重新连接');
}
return;
}
const requested = loginService.getQRCodePicture();
logger.logWarn('[SnowCat] [Framework] 请求刷新二维码', { requested, msfStatus });
if (!requested) {
throw new Error('QQ 未接受二维码刷新请求');
}
});
// 快速登录
WebUiDataRuntime.setQuickLoginCall(async (uin: string) => {
return await new Promise((resolve) => {
if (!uin) {
return resolve({ result: false, message: '快速登录失败' });
}
logger.log('[SnowCat] [Framework] 正在快速登录', uin);
loginService.quickLoginWithUin(uin).then(res => {
const success = res.result === '0' && !res.loginErrorInfo?.errMsg;
if (!success) {
const errMsg = res.loginErrorInfo?.errMsg || `快速登录失败,错误码: ${res.result}`;
WebUiDataRuntime.setQQLoginError(errMsg);
resolve({ result: false, message: errMsg });
} else {
loginContext.isLogined = true;
WebUiDataRuntime.setQQLoginStatus(true);
WebUiDataRuntime.setQQLoginError('');
resolve({ result: true, message: '' });
}
}).catch((e) => {
logger.logError('[SnowCat] [Framework] 快速登录异常:', e);
WebUiDataRuntime.setQQLoginError('快速登录发生错误');
resolve({ result: false, message: '快速登录发生错误' });
});
});
});
// 密码登录
WebUiDataRuntime.setPasswordLoginCall(async (uin: string, passwordMd5: string) => {
return await new Promise((resolve) => {
if (!uin || !passwordMd5) {
return resolve({ result: false, message: '密码登录失败:参数不完整' });
}
logger.log('[SnowCat] [Framework] 正在密码登录', uin);
loginService.passwordLogin({
uin,
passwordMd5,
step: 0,
newDeviceLoginSig: new Uint8Array(),
proofWaterSig: new Uint8Array(),
proofWaterRand: new Uint8Array(),
proofWaterSid: new Uint8Array(),
unusualDeviceCheckSig: new Uint8Array(),
}).then(res => {
if (res.result === '140022008') {
const proofWaterUrl = res.loginErrorInfo?.proofWaterUrl || '';
resolve({ result: false, message: '需要验证码', needCaptcha: true, proofWaterUrl });
} else if (res.result === '140022010' || res.result === '140022011') {
const jumpUrl = res.loginErrorInfo?.jumpUrl || '';
const newDevicePullQrCodeSig = res.loginErrorInfo?.newDevicePullQrCodeSig || '';
resolve({ result: false, message: '新设备需要扫码验证', needNewDevice: true, jumpUrl, newDevicePullQrCodeSig });
} else if (res.result !== '0') {
const errMsg = res.loginErrorInfo?.errMsg || '密码登录失败';
WebUiDataRuntime.setQQLoginError(errMsg);
resolve({ result: false, message: errMsg });
} else {
loginContext.isLogined = true;
WebUiDataRuntime.setQQLoginStatus(true);
WebUiDataRuntime.setQQLoginError('');
resolve({ result: true, message: '' });
}
}).catch((e) => {
logger.logError('[SnowCat] [Framework] 密码登录异常:', e);
WebUiDataRuntime.setQQLoginError('密码登录发生错误');
resolve({ result: false, message: '密码登录发生错误' });
});
});
});
// 验证码登录(密码登录需要验证码时的第二步)
WebUiDataRuntime.setCaptchaLoginCall(async (uin: string, passwordMd5: string, ticket: string, randstr: string, sid: string) => {
return await new Promise((resolve) => {
if (!uin || !passwordMd5 || !ticket) {
return resolve({ result: false, message: '验证码登录失败:参数不完整' });
}
logger.log('[SnowCat] [Framework] 正在验证码登录', uin);
loginService.passwordLogin({
uin,
passwordMd5,
step: 1,
newDeviceLoginSig: new Uint8Array(),
proofWaterSig: new TextEncoder().encode(ticket),
proofWaterRand: new TextEncoder().encode(randstr),
proofWaterSid: new TextEncoder().encode(sid),
unusualDeviceCheckSig: new Uint8Array(),
}).then(res => {
if (res.result === '140022010' || res.result === '140022011') {
const jumpUrl = res.loginErrorInfo?.jumpUrl || '';
const newDevicePullQrCodeSig = res.loginErrorInfo?.newDevicePullQrCodeSig || '';
resolve({ result: false, message: '新设备需要扫码验证', needNewDevice: true, jumpUrl, newDevicePullQrCodeSig });
} else if (res.result !== '0') {
const errMsg = res.loginErrorInfo?.errMsg || '验证码登录失败';
WebUiDataRuntime.setQQLoginError(errMsg);
resolve({ result: false, message: errMsg });
} else {
loginContext.isLogined = true;
WebUiDataRuntime.setQQLoginStatus(true);
WebUiDataRuntime.setQQLoginError('');
resolve({ result: true, message: '' });
}
}).catch((e) => {
logger.logError('[SnowCat] [Framework] 验证码登录异常:', e);
WebUiDataRuntime.setQQLoginError('验证码登录发生错误');
resolve({ result: false, message: '验证码登录发生错误' });
});
});
});
// 新设备验证登录(密码登录需要新设备验证时的第二步)
WebUiDataRuntime.setNewDeviceLoginCall(async (uin: string, passwordMd5: string, newDevicePullQrCodeSig: string) => {
return await new Promise((resolve) => {
if (!uin || !passwordMd5 || !newDevicePullQrCodeSig) {
return resolve({ result: false, message: '新设备验证登录失败:参数不完整' });
}
logger.log('[SnowCat] [Framework] 正在新设备验证登录', uin);
loginService.passwordLogin({
uin,
passwordMd5,
step: 2,
newDeviceLoginSig: new TextEncoder().encode(newDevicePullQrCodeSig),
proofWaterSig: new Uint8Array(),
proofWaterRand: new Uint8Array(),
proofWaterSid: new Uint8Array(),
unusualDeviceCheckSig: new Uint8Array(),
}).then(res => {
if (res.result === '140022011') {
const jumpUrl = res.loginErrorInfo?.jumpUrl || '';
const sig = res.loginErrorInfo?.newDevicePullQrCodeSig || '';
resolve({ result: false, message: '异常设备需要验证', needNewDevice: true, jumpUrl, newDevicePullQrCodeSig: sig });
} else if (res.result !== '0') {
const errMsg = res.loginErrorInfo?.errMsg || '新设备验证登录失败';
WebUiDataRuntime.setQQLoginError(errMsg);
resolve({ result: false, message: errMsg });
} else {
loginContext.isLogined = true;
WebUiDataRuntime.setQQLoginStatus(true);
WebUiDataRuntime.setQQLoginError('');
resolve({ result: true, message: '' });
}
}).catch((e) => {
logger.logError('[SnowCat] [Framework] 新设备验证登录异常:', e);
WebUiDataRuntime.setQQLoginError('新设备验证登录发生错误');
resolve({ result: false, message: '新设备验证登录发生错误' });
});
});
});
}
export class SnowCatFramework {
public core: SnowCatCore;
context: InstanceContext;
constructor (
wrapper: WrapperNodeApi,
session: NodeIQQNTWrapperSession,
logger: LogWrapper,
selfInfo: SelfInfo,
basicInfoWrapper: QQBasicInfoWrapper,
pathWrapper: SnowCatPathWrapper,
packetHandler: NativePacketHandler,
napi2nativeLoader: Napi2NativeLoader
) {
this.context = {
packetHandler,
napi2nativeLoader,
workingEnv: SnowCatCoreWorkingEnv.Framework,
wrapper,
session,
logger,
basicInfoWrapper,
pathWrapper,
};
this.core = new SnowCatCore(this.context, selfInfo);
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".."
},
"include": [
"*.ts",
"**/*.ts"
],
"exclude": [
"node_modules",
"dist"
]
}
+77
View File
@@ -0,0 +1,77 @@
import cp from 'vite-plugin-cp';
import { defineConfig, PluginOption, UserConfig } from 'vite';
import path, { resolve } from 'path';
import nodeResolve from '@rollup/plugin-node-resolve';
import { autoIncludeTSPlugin } from 'snowcat-vite/vite-auto-include.js';
import { builtinModules } from 'module';
import react from '@vitejs/plugin-react-swc';
import napcatVersion from 'snowcat-vite/vite-plugin-version.js';
// 依赖排除
const external = [
'ws',
'express',
];
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`), 'node:sqlite'].flat();
const FrameworkBaseConfigPlugin: PluginOption[] = [
autoIncludeTSPlugin({
entries: [
{ entry: 'snowcat.ts', dir: path.resolve(__dirname, '../snowcat-core/protocol') },
{ entry: 'snowcat.ts', dir: path.resolve(__dirname, '../snowcat-onebot/action/test') },
],
}),
react({ tsDecorators: true }),
cp({
targets: [
{ src: '../snowcat-napi-loader/', dest: 'dist', flatten: true },
{ src: '../snowcat-native/', dest: 'dist/native', flatten: false },
{ src: '../snowcat-core/external/napcat.json', dest: 'dist/config/' },
{ src: '../snowcat-webui-frontend/dist/', dest: 'dist/static/', flatten: false },
{ src: './napcat.cjs', dest: 'dist' },
{ src: './nativeLoader.cjs', dest: 'dist' },
{ src: './preload.cjs', dest: 'dist' },
{ src: './renderer.js', dest: 'dist' },
{ src: '../../package.json', dest: 'dist' },
{ src: '../../logo.png', dest: 'dist' },
],
}),
nodeResolve(),
napcatVersion(),
];
const FrameworkBaseConfig = () =>
defineConfig({
resolve: {
conditions: ['node', 'default'],
alias: {
'@/snowcat-core': resolve(__dirname, '../snowcat-core'),
'@/snowcat-common': resolve(__dirname, '../snowcat-common'),
'@/snowcat-onebot': resolve(__dirname, '../snowcat-onebot'),
'@/snowcat-pty': resolve(__dirname, '../snowcat-pty'),
'@/snowcat-webui-backend': resolve(__dirname, '../snowcat-webui-backend'),
'@/snowcat-image-size': resolve(__dirname, '../snowcat-image-size'),
'@/snowcat-protocol': resolve(__dirname, '../snowcat-protocol'),
'@/snowcat-adapter': resolve(__dirname, '../snowcat-adapter'),
},
},
build: {
sourcemap: false,
target: 'esnext',
minify: false,
lib: {
entry: {
napcat: path.resolve(__dirname, 'snowcat.ts'),
'worker/conoutSocketWorker': path.resolve(__dirname, '../snowcat-pty/worker/conoutSocketWorker.ts'),
},
formats: ['es'],
fileName: (_, entryName) => `${entryName}.mjs`,
},
rollupOptions: {
external: [...nodeModules, ...external],
},
},
});
export default defineConfig((): UserConfig => {
return {
...FrameworkBaseConfig(),
plugins: [...FrameworkBaseConfigPlugin],
};
});