Compare commits

..

35 Commits

Author SHA1 Message Date
手瓜一十雪
c123b34d5f Update ffmpeg addon binary for Darwin ARM64
Replaces the ffmpegAddon.darwin.arm64.node binary with a new version, likely to include bug fixes or performance improvements for ARM64 macOS systems.
2025-11-18 20:16:03 +08:00
手瓜一十雪
d25b43ebf2 Update ffmpeg native binaries for Linux
Replaces ffmpegAddon.linux.arm64.node and ffmpegAddon.linux.x64.node with new versions. This may include bug fixes, performance improvements, or compatibility updates for native ffmpeg functionality.
2025-11-18 17:36:09 +08:00
时瑾
8fe4a9e6ac 更新 Bug 反馈模板,增强对不当内容的说明和合规性要求 2025-11-16 15:55:25 +08:00
手瓜一十雪
09da80aad5 Remove unused dependencies from package.json
Deleted the 'dependencies' section from napcat-qrcode/package.json, removing references to napcat-core, napcat-common, napcat-onebot, and napcat-webui-backend. This streamlines the package configuration and may indicate these dependencies are no longer required.
2025-11-16 11:02:21 +08:00
手瓜一十雪
3d3f718fd5 Refactor interfaces and decouple backend dependencies
Introduced new interface definitions in napcat-common for logging, status, and subscription. Refactored napcat-webui-backend to use these interfaces, decoupling it from napcat-core and napcat-onebot. Moved OneBot config schema to backend and updated imports. Updated framework and shell to pass subscriptions to InitWebUi. Improved type safety and modularity across backend and shared packages.
2025-11-16 10:58:30 +08:00
手瓜一十雪
6068abdec0 Update VSCode settings for formatting and file nesting
Enhanced .vscode/settings.json with file nesting patterns, formatting preferences, auto-save behavior, and import specifier options for JavaScript and TypeScript. Removed old debug source map overrides to streamline workspace configuration.
2025-11-15 17:17:24 +08:00
手瓜一十雪
3957d7af5a Use environment variables for secret keys in dev and backend
Set fixed secret keys for JWT and WebUI in development environment via environment variables. Updated backend to use NAPCAT_WEBUI_SECRET_KEY and NAPCAT_WEBUI_JWT_SECRET_KEY from environment if available, improving configurability and security.
2025-11-15 17:00:52 +08:00
手瓜一十雪
a2837974fe Add VSCode launch and TailwindCSS config files
Added .vscode/launch.json for Node.js debugging and .vscode/tailwindcss.json for TailwindCSS directive support in VSCode. These files improve development workflow and editor integration.
2025-11-15 16:51:46 +08:00
手瓜一十雪
6f8edfe570 清理重复的 Vitest configuration file
Deleted vitest.config.ts, which contained test environment and path alias settings. This may indicate a change in testing strategy or migration away from Vitest.
2025-11-15 16:42:50 +08:00
手瓜一十雪
0b655db4dd Add test step to build workflow
Inserts 'pnpm test' into both build jobs in the GitHub Actions workflow to ensure tests are run during CI before building artifacts.
2025-11-15 16:25:06 +08:00
手瓜一十雪
d800466a30 Move inversify and reflect-metadata to napcat-core
Transferred 'inversify' and 'reflect-metadata' dependencies from the root package.json to packages/napcat-core/package.json to better scope dependencies and improve project organization.
2025-11-15 16:23:03 +08:00
手瓜一十雪
fa80441e36 Add ESLint config and update code style
Introduced a new eslint.config.js using neostandard and added related devDependencies. Updated codebase for consistent formatting, spacing, and function declarations. Minor refactoring and cleanup across multiple files to improve readability and maintain code style compliance.
2025-11-15 16:21:59 +08:00
手瓜一十雪
1990761ad6 Update main entry and tsconfig for JS support
Changed package.json main and exports to use index.js instead of index.cjs. Updated tsconfig.json to allow JavaScript files and expanded include patterns to support both JS and TS files.
2025-11-15 16:09:26 +08:00
手瓜一十雪
ef63812391 Add napcat-test package and Vitest setup
Introduces the napcat-test package with initial SHA-1 stream tests, configuration files, and scripts for running tests. Updates root package.json to include test commands and Vitest dependencies, and adds Vitest configuration at the root and package level for test environment setup.
2025-11-15 16:05:09 +08:00
手瓜一十雪
0f033b0ac8 Remove performance monitor module
Deleted performance-monitor.ts from napcat-common, removing the function statistics and reporting utility. This may indicate a refactor or replacement of performance monitoring functionality.
2025-11-15 15:13:56 +08:00
手瓜一十雪
9fdef3cde9 Revert "Update default model in release workflow"
This reverts commit d32ccc6eb5.
2025-11-15 14:56:34 +08:00
手瓜一十雪
20e8643193 Fix import paths for require_dlopen module
Updated import statements in prebuild-loader.ts and windowsPtyAgent.ts to use relative path './index' for require_dlopen. This resolves incorrect module resolution issues.
2025-11-15 14:43:08 +08:00
手瓜一十雪
8645ed4d9d Update tsconfig to include typeRoots and format paths
Added 'typeRoots' to specify custom type definitions directory and reformatted the 'paths' property for better readability. This improves TypeScript type resolution and project maintainability.
2025-11-15 14:40:06 +08:00
手瓜一十雪
c0b9817ff5 Add type checking to build workflow
Incorporates 'pnpm run typecheck' into the build steps for both frontend and shell jobs to ensure type safety during CI builds.
2025-11-15 14:01:11 +08:00
手瓜一十雪
b147e57c1c Refactor TypeScript configs to use shared base
Introduced tsconfig.base.json for shared TypeScript configuration and updated all package tsconfig.json files to extend from it, reducing duplication and improving maintainability. Also updated typecheck script in package.json and fixed import in prebuild-loader.ts.
2025-11-15 14:00:27 +08:00
手瓜一十雪
ad4a108781 feat: 大规模去耦合
Moved various helper, event, and utility files from napcat-common to napcat-core/helper for better modularity and separation of concerns. Updated imports across packages to reflect new file locations. Removed unused dependencies from napcat-common and added them to napcat-core where needed. Also consolidated type definitions and cleaned up tsconfig settings for improved compatibility.
2025-11-15 13:36:33 +08:00
手瓜一十雪
df824d77ae feat: 所有的类型检查 2025-11-15 12:57:19 +08:00
手瓜一十雪
19888d52dc Remove unused test utility files
Deleted test.ts and test2.ts from utils as they are no longer needed for the project.
2025-11-15 12:02:07 +08:00
手瓜一十雪
4dc8b3ed3b Refactor FileApi usage to obContext in OneBot
Replaced references to `core.apis.FileApi` with `obContext.apis.FileApi` across actions and message API to ensure consistent context usage. Added FileApi to the ApiListType and initialized it in NapCatOneBot11Adapter. This improves maintainability and context handling for file-related operations.
2025-11-15 11:20:01 +08:00
手瓜一十雪
8df54d5cd3 feat: 去core耦合onebot
Moved file element creation methods (for file, picture, video, and ptt) from napcat-core/apis/file.ts to a new OneBotFileApi class in napcat-onebot/api/file.ts. Updated package.json dependencies to remove unused packages and fix workspace references. This improves separation of concerns and modularity between core and onebot-specific logic.
2025-11-15 11:17:57 +08:00
手瓜一十雪
aa982b3071 Improve version folder selection in loadNapCat.cjs
Enhanced logic to handle multiple or missing version folders by selecting the most recently modified folder if more than one exists, and providing clearer error messages. Also updated .vscode/settings.json to add source map path overrides for additional packages.
2025-11-15 10:58:33 +08:00
手瓜一十雪
8e71dec63a Bind TypedEventEmitter to DI container and update usage
Added TypedEventEmitter to the dependency injection container and exposed it in ServiceBase. Updated OlPushService to use the injected event emitter instead of directly importing appEvent. Also performed minor code style improvements for consistency.
2025-11-15 10:48:59 +08:00
手瓜一十雪
31bb1e5dee Add emoji like event handling to core and onebot
Introduces a typed event emitter for app events in napcat-core, specifically for emoji like events in groups. OlPushService now emits 'event:emoji_like' when a group reaction is detected. napcat-onebot listens for this event and emits corresponding OneBot events. Refactors and adds missing type definitions and improves method formatting for consistency.
2025-11-15 10:45:02 +08:00
手瓜一十雪
75e1e8dd79 Improve error handling in release workflow
Enhances the OpenRouter API call step by adding error handling for both curl and jq failures. If the API call or response parsing fails, the workflow now falls back to using a default release note template.
2025-11-14 23:00:20 +08:00
手瓜一十雪
d32ccc6eb5 Update default model in release workflow
Changed the OPENROUTER_MODEL environment variable from 'kimi-k2-0905-turbo' to 'glm-4.6-turbo' in the release workflow configuration.
2025-11-14 22:45:17 +08:00
手瓜一十雪
7b3e94d568 Add raw response output to release workflow
Added echo statements to display the raw API response in the release workflow for improved debugging and visibility.
2025-11-14 22:35:58 +08:00
手瓜一十雪
5cfe479044 Refactor createListenerFunction type and usage
Simplifies the generic type signature of createListenerFunction and updates the way createEventFunction is called, removing unnecessary type parameters and using a direct function call with TypeScript ignore for type checking.
2025-11-14 22:24:50 +08:00
手瓜一十雪
f04ffa5dc6 Add service handler registration and DI support
Introduces dependency injection via Inversify and reflect-metadata, adds a service handler registry for packet handling, and updates core initialization to auto-register and bind service handlers. Also updates Vite configs and auto-include logic to support protocol service files.
2025-11-14 22:20:33 +08:00
手瓜一十雪
a2a73ce2dd Add dev build script and improve Vite config
Introduces a 'build:shell:dev' script for development builds and updates napcat-shell's Vite config to conditionally enable source maps in development mode. This enhances build flexibility for development and production environments.
2025-11-14 21:25:29 +08:00
手瓜一十雪
66d02eeb6a Enable source maps and improve debugging support
This commit enables source maps in napcat-shell's Vite config for better debugging, adds source map path overrides to VSCode settings, and updates nodeTest.ps1 to launch with the Node.js inspector. The autoIncludeTSPlugin transform now returns a source map for improved breakpoint support in VSCode. Also adds a sources.txt file listing project and dependency sources.
2025-11-14 21:21:49 +08:00
142 changed files with 1463 additions and 1579 deletions

View File

@@ -1,6 +1,6 @@
name: Bug 反馈
description: 报告可能的 NapCat 异常行为
title: '[BUG] '
title: "[BUG] "
labels: bug
body:
- type: markdown
@@ -10,6 +10,10 @@ body:
在提交新的 Bug 反馈前,请确保您:
* 已经搜索了现有的 issues并且没有找到可以解决您问题的方法
* 不与现有的某一 issue 重复
* **不接受因发送不当内容而导致的问题报告**
- 包括但不限于:多媒体发送失败、转发消息失败、消息被拦截等因 18+ 内容、违规内容或触发风控的问题
- 提交 issue 前,请确认您发送的多媒体内容、链接、文本等均为正常合规内容,不会触发平台风控机制
- 因违规内容导致的问题,一律不予受理
- type: input
id: system-version
attributes:

View File

@@ -21,6 +21,8 @@ jobs:
run: |
npm i -g pnpm
pnpm i
pnpm run typecheck || exit 1
pnpm test || exit 1
pnpm --filter napcat-webui-frontend run build || exit 1
pnpm run build:framework
mv packages/napcat-framework/dist framework-dist
@@ -45,6 +47,8 @@ jobs:
run: |
npm i -g pnpm
pnpm i
pnpm run typecheck || exit 1
pnpm test || exit 1
pnpm --filter napcat-webui-frontend run build || exit 1
pnpm run build:shell
mv packages/napcat-shell/dist shell-dist

View File

@@ -232,24 +232,32 @@ jobs:
echo "$BODY" | jq .
# 调用 OpenRouter
RESPONSE=$(curl -s -X POST "$OPENROUTER_API_URL" \
if RESPONSE=$(curl -s -X POST "$OPENROUTER_API_URL" \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d "$BODY")
-d "$BODY"); then
echo "=== raw response ==="
echo "$RESPONSE"
echo "=== OpenRouter raw response ==="
if echo "$RESPONSE" | jq . >/dev/null 2>&1; then
echo "$RESPONSE" | jq .
else
echo "jq failed to parse response"
fi
echo "=== OpenRouter raw response ==="
echo "$RESPONSE" | jq .
# 提取生成内容
RELEASE_BODY=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // .choices[0].text // ""' 2>/dev/null || echo "")
# 提取生成内容
RELEASE_BODY=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // .choices[0].text // ""')
if [ -z "$RELEASE_BODY" ]; then
echo "❌ OpenRouter failed to generate release note, terminating workflow."
exit 1
if [ -z "$RELEASE_BODY" ]; then
echo "❌ OpenRouter failed to generate release note, using default.md"
cp .github/prompt/default.md CHANGELOG.md
else
echo -e "$RELEASE_BODY" > CHANGELOG.md
fi
else
echo "❌ Curl failed, using default.md"
cp .github/prompt/default.md CHANGELOG.md
fi
# 输出到 CHANGELOG.md
echo -e "$RELEASE_BODY" > CHANGELOG.md
echo "=== generated release note ==="
cat CHANGELOG.md

12
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node-terminal",
"request": "launch",
"name": "调试程序",
"command": "pnpm run dev:shell",
"cwd": "${workspaceFolder}"
}
]
}

35
.vscode/settings.json vendored
View File

@@ -1,2 +1,37 @@
{
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
".env.universal": ".env.*",
"vite.config.ts": "vite*.ts",
"README.md": "CODE_OF_CONDUCT.md, RELEASES.md, CONTRIBUTING.md, CHANGELOG.md, SECURITY.md",
"tsconfig.json": "tsconfig.*.json, env.d.ts",
"package.json": "package-lock.json, eslint*, .prettier*, .editorconfig, manifest.json, logo.png, .gitignore, LICENSE"
},
"css.customData": [
".vscode/tailwindcss.json"
],
"editor.detectIndentation": false,
"editor.tabSize": 2,
"editor.formatOnSave": true,
"editor.formatOnType": false,
"editor.formatOnPaste": true,
"editor.formatOnSaveMode": "file",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
},
"files.autoSave": "onFocusChange",
"javascript.preferences.quoteStyle": "single",
"typescript.preferences.quoteStyle": "single",
"javascript.format.semicolons": "insert",
"typescript.format.semicolons": "insert",
"javascript.format.insertSpaceBeforeFunctionParenthesis": true,
"typescript.format.insertSpaceBeforeFunctionParenthesis": true,
"typescript.format.insertSpaceAfterConstructor": true,
"javascript.format.insertSpaceAfterConstructor": true,
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "minimal",
"javascript.preferences.importModuleSpecifier": "non-relative",
"javascript.preferences.importModuleSpecifierEnding": "minimal",
"typescript.disableAutomaticTypeAcquisition": true
}

55
.vscode/tailwindcss.json vendored Normal file
View File

@@ -0,0 +1,55 @@
{
"version": 1.1,
"atDirectives": [
{
"name": "@tailwind",
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
}
]
},
{
"name": "@apply",
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that youd like to extract to a new component.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
}
]
},
{
"name": "@responsive",
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
}
]
},
{
"name": "@screen",
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
}
]
},
{
"name": "@variants",
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
}
]
}
]
}

52
eslint.config.js Normal file
View File

@@ -0,0 +1,52 @@
import neostandard from 'neostandard';
/** 尾随逗号 */
const commaDangle = val => {
if (val?.rules?.['@stylistic/comma-dangle']?.[0] === 'warn') {
const rule = val?.rules?.['@stylistic/comma-dangle']?.[1];
Object.keys(rule).forEach(key => {
rule[key] = 'always-multiline';
});
val.rules['@stylistic/comma-dangle'][1] = rule;
}
/** 三元表达式 */
if (val?.rules?.['@stylistic/indent']) {
val.rules['@stylistic/indent'][2] = {
...val.rules?.['@stylistic/indent']?.[2],
flatTernaryExpressions: true,
offsetTernaryExpressions: false,
};
}
/** 支持下划线 - 禁用 camelcase 规则 */
if (val?.rules?.camelcase) {
val.rules.camelcase = 'off';
}
/** 未使用的变量强制报错 */
if (val?.rules?.['@typescript-eslint/no-unused-vars']) {
val.rules['@typescript-eslint/no-unused-vars'] = ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
}];
}
return val;
};
/** 忽略的文件 */
const ignores = [
'node_modules',
'**/dist/**',
'launcher',
];
const options = neostandard({
ts: true,
ignores,
semi: true, // 强制使用分号
}).map(commaDangle);
export default options;

View File

@@ -5,17 +5,26 @@
"version": "0.0.1",
"scripts": {
"build:shell": "pnpm --filter napcat-shell run build || exit 1",
"build:shell:dev": "pnpm --filter napcat-shell run build:dev || exit 1",
"build:framework": "pnpm --filter napcat-framework run build || exit 1",
"build:webui": "pnpm --filter napcat-webui-frontend run build || exit 1",
"dev:shell": "pnpm --filter napcat-develop run dev || exit 1",
"typecheck": "pnpm -w -r --filter napcat-shell --filter napcat-framework run typecheck"
"typecheck": "pnpm -r --if-present run typecheck",
"test": "pnpm --filter napcat-test run test",
"test:ui": "pnpm --filter napcat-test run test:ui",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.3",
"@vitejs/plugin-react-swc": "^4.2.2",
"@vitest/ui": "^4.0.9",
"eslint": "^9.39.1",
"neostandard": "^0.12.2",
"typescript": "^5.3.0",
"vite": "^6.4.1",
"vite-plugin-cp": "^6.0.3"
"vite-plugin-cp": "^6.0.3",
"vitest": "^4.0.9"
},
"dependencies": {
"express": "^5.0.0",

View File

@@ -4,6 +4,9 @@
"private": true,
"type": "module",
"main": "src/index.ts",
"scripts": {
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"exports": {
".": {
"import": "./src/index.ts"
@@ -13,14 +16,9 @@
}
},
"dependencies": {
"compressing": "^1.10.1",
"json5": "^2.2.3",
"ajv": "^8.13.0",
"file-type": "^21.0.0",
"napcat-image-size": "workspace:*",
"napcat-core": "workspace:*",
"silk-wasm": "^3.6.1",
"winston": "^3.17.0"
"silk-wasm": "^3.6.1"
},
"devDependencies": {
"@types/node": "^22.0.1"

View File

@@ -1,4 +1,4 @@
import { Peer } from 'napcat-core/index';
import { Peer } from './types';
import { randomUUID } from 'crypto';
class TimeBasedCache<K, V> {

View File

@@ -2,7 +2,7 @@ import fs from 'fs';
import { stat } from 'fs/promises';
import crypto, { randomUUID } from 'crypto';
import path from 'node:path';
import { solveProblem } from '@/napcat-common/helper';
import { solveProblem } from '@/napcat-common/src/helper';
export interface HttpDownloadOptions {
url: string;

View File

@@ -1,8 +1,7 @@
import path from 'node:path';
import fs from 'fs';
import os from 'node:os';
import { QQLevel } from 'napcat-core/index';
import { QQVersionConfigType } from './types';
import { QQVersionConfigType, QQLevel } from './types';
export async function solveProblem<T extends (...arg: any[]) => any> (func: T, ...args: Parameters<T>): Promise<ReturnType<T> | undefined> {
return new Promise<ReturnType<T> | undefined>((resolve) => {

View File

@@ -0,0 +1,24 @@
export enum LogLevel {
DEBUG = 'debug',
INFO = 'info',
WARN = 'warn',
ERROR = 'error',
FATAL = 'fatal',
}
export interface ILogWrapper {
fileLogEnabled: boolean;
consoleLogEnabled: boolean;
cleanOldLogs (logDir: string): void;
setFileAndConsoleLogLevel (fileLogLevel: LogLevel, consoleLogLevel: LogLevel): void;
setLogSelfInfo (selfInfo: { nick: string; uid: string; }): void;
setFileLogEnabled (isEnabled: boolean): void;
setConsoleLogEnabled (isEnabled: boolean): void;
formatMsg (msg: any[]): string;
_log (level: LogLevel, ...args: any[]): void;
log (...args: any[]): void;
logDebug (...args: any[]): void;
logError (...args: any[]): void;
logWarn (...args: any[]): void;
logFatal (...args: any[]): void;
logMessage (msg: unknown, selfInfo: unknown): void;
}

View File

@@ -1,6 +1,5 @@
import { Peer } from 'napcat-core/index';
import crypto from 'crypto';
import { Peer } from './types';
export class LimitedHashTable<K, V> {
private readonly keyToValue: Map<K, V> = new Map();
private readonly valueToKey: Map<V, K> = new Map();

View File

@@ -1,317 +0,0 @@
/**
* 性能监控器 - 用于统计函数调用次数、耗时等信息
*/
import * as fs from 'fs';
import * as path from 'path';
export interface FunctionStats {
name: string;
callCount: number;
totalTime: number;
averageTime: number;
minTime: number;
maxTime: number;
fileName?: string;
lineNumber?: number;
}
export class PerformanceMonitor {
private static instance: PerformanceMonitor;
private stats = new Map<string, FunctionStats>();
private startTimes = new Map<string, number>();
private reportInterval: NodeJS.Timeout | null = null;
static getInstance (): PerformanceMonitor {
if (!PerformanceMonitor.instance) {
PerformanceMonitor.instance = new PerformanceMonitor();
// 启动定时统计报告
PerformanceMonitor.instance.startPeriodicReport();
}
return PerformanceMonitor.instance;
}
/**
* 开始定时统计报告 (每60秒)
*/
private startPeriodicReport (): void {
if (this.reportInterval) {
clearInterval(this.reportInterval);
}
this.reportInterval = setInterval(() => {
if (this.stats.size > 0) {
this.printPeriodicReport();
this.writeDetailedLogToFile();
}
}, 60000); // 60秒
}
/**
* 停止定时统计报告
*/
stopPeriodicReport (): void {
if (this.reportInterval) {
clearInterval(this.reportInterval);
this.reportInterval = null;
}
}
/**
* 打印定时统计报告 (简化版本)
*/
private printPeriodicReport (): void {
const now = new Date().toLocaleString();
console.log(`\n=== 性能监控定时报告 [${now}] ===`);
const totalFunctions = this.stats.size;
const totalCalls = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.callCount, 0);
const totalTime = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.totalTime, 0);
console.log(`📊 总览: ${totalFunctions} 个函数, ${totalCalls} 次调用, 总耗时: ${totalTime.toFixed(2)}ms`);
// 显示Top 5最活跃的函数
console.log('\n🔥 最活跃函数 (Top 5):');
this.getTopByCallCount(5).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms`);
});
// 显示Top 5最耗时的函数
console.log('\n⏱ 最耗时函数 (Top 5):');
this.getTopByTotalTime(5).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('===============================\n');
}
/**
* 将详细统计数据写入日志文件
*/
private writeDetailedLogToFile (): void {
try {
const now = new Date();
const dateStr = now.toISOString().replace(/[:.]/g, '-').split('T')[0];
const timeStr = now.toTimeString().split(' ')[0]?.replace(/:/g, '-') || 'unknown-time';
const timestamp = `${dateStr}_${timeStr}`;
const fileName = `${timestamp}.log.txt`;
const logPath = path.join(process.cwd(), 'logs', fileName);
// 确保logs目录存在
const logsDir = path.dirname(logPath);
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
const totalFunctions = this.stats.size;
const totalCalls = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.callCount, 0);
const totalTime = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.totalTime, 0);
let logContent = '';
logContent += '=== 性能监控详细报告 ===\n';
logContent += `生成时间: ${now.toLocaleString()}\n`;
logContent += '统计周期: 60秒\n';
logContent += `总览: ${totalFunctions} 个函数, ${totalCalls} 次调用, 总耗时: ${totalTime.toFixed(2)}ms\n\n`;
// 详细函数统计
logContent += '=== 所有函数详细统计 ===\n';
const allStats = this.getStats().sort((a, b) => b.totalTime - a.totalTime);
allStats.forEach((stat, index) => {
logContent += `${index + 1}. 函数: ${stat.name}\n`;
logContent += ` 文件: ${stat.fileName || 'N/A'}\n`;
logContent += ` 行号: ${stat.lineNumber || 'N/A'}\n`;
logContent += ` 调用次数: ${stat.callCount}\n`;
logContent += ` 总耗时: ${stat.totalTime.toFixed(4)}ms\n`;
logContent += ` 平均耗时: ${stat.averageTime.toFixed(4)}ms\n`;
logContent += ` 最小耗时: ${stat.minTime === Infinity ? 'N/A' : stat.minTime.toFixed(4)}ms\n`;
logContent += ` 最大耗时: ${stat.maxTime.toFixed(4)}ms\n`;
logContent += ` 性能占比: ${((stat.totalTime / totalTime) * 100).toFixed(2)}%\n`;
logContent += '\n';
});
// 排行榜统计
logContent += '=== 总耗时排行榜 (Top 20) ===\n';
this.getTopByTotalTime(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 调用: ${stat.callCount}次, 平均: ${stat.averageTime.toFixed(2)}ms\n`;
});
logContent += '\n=== 调用次数排行榜 (Top 20) ===\n';
this.getTopByCallCount(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均: ${stat.averageTime.toFixed(2)}ms\n`;
});
logContent += '\n=== 平均耗时排行榜 (Top 20) ===\n';
this.getTopByAverageTime(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 平均: ${stat.averageTime.toFixed(2)}ms, 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms\n`;
});
logContent += '\n=== 性能热点分析 ===\n';
// 找出最耗时的前10个函数
const hotSpots = this.getTopByTotalTime(10);
hotSpots.forEach((stat, index) => {
const efficiency = stat.callCount / stat.totalTime; // 每毫秒的调用次数
logContent += `${index + 1}. ${stat.name}\n`;
logContent += ` 性能影响: ${((stat.totalTime / totalTime) * 100).toFixed(2)}%\n`;
logContent += ` 调用效率: ${efficiency.toFixed(4)} 调用/ms\n`;
logContent += ` 优化建议: ${stat.averageTime > 10
? '考虑优化此函数的执行效率'
: stat.callCount > 1000
? '考虑减少此函数的调用频率'
: '性能表现良好'}\n\n`;
});
logContent += '=== 报告结束 ===\n';
// 写入文件
fs.writeFileSync(logPath, logContent, 'utf8');
console.log(`📄 详细性能报告已保存到: ${logPath}`);
} catch (error) {
console.error('写入性能日志文件时出错:', error);
}
}
/**
* 开始记录函数调用
*/
startFunction (functionName: string, fileName?: string, lineNumber?: number): string {
const callId = `${functionName}_${Date.now()}_${Math.random()}`;
this.startTimes.set(callId, performance.now());
// 初始化或更新统计信息
if (!this.stats.has(functionName)) {
this.stats.set(functionName, {
name: functionName,
callCount: 0,
totalTime: 0,
averageTime: 0,
minTime: Infinity,
maxTime: 0,
fileName,
lineNumber,
});
}
const stat = this.stats.get(functionName)!;
stat.callCount++;
return callId;
}
/**
* 结束记录函数调用
*/
endFunction (callId: string, functionName: string): void {
const startTime = this.startTimes.get(callId);
if (!startTime) return;
const endTime = performance.now();
const duration = endTime - startTime;
this.startTimes.delete(callId);
const stat = this.stats.get(functionName);
if (!stat) return;
stat.totalTime += duration;
stat.averageTime = stat.totalTime / stat.callCount;
stat.minTime = Math.min(stat.minTime, duration);
stat.maxTime = Math.max(stat.maxTime, duration);
}
/**
* 获取所有统计信息
*/
getStats (): FunctionStats[] {
return Array.from(this.stats.values());
}
/**
* 获取排行榜 - 按总耗时排序
*/
getTopByTotalTime (limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.totalTime - a.totalTime)
.slice(0, limit);
}
/**
* 获取排行榜 - 按调用次数排序
*/
getTopByCallCount (limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.callCount - a.callCount)
.slice(0, limit);
}
/**
* 获取排行榜 - 按平均耗时排序
*/
getTopByAverageTime (limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.averageTime - a.averageTime)
.slice(0, limit);
}
/**
* 清空统计数据
*/
clear (): void {
this.stats.clear();
this.startTimes.clear();
}
/**
* 打印统计报告
*/
printReport (): void {
console.log('\n=== 函数性能监控报告 ===');
console.log('\n🔥 总耗时排行榜 (Top 10):');
this.getTopByTotalTime(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 调用次数: ${stat.callCount}, 平均耗时: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('\n📈 调用次数排行榜 (Top 10):');
this.getTopByCallCount(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 调用次数: ${stat.callCount}, 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均耗时: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('\n⏱ 平均耗时排行榜 (Top 10):');
this.getTopByAverageTime(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 平均耗时: ${stat.averageTime.toFixed(2)}ms, 调用次数: ${stat.callCount}, 总耗时: ${stat.totalTime.toFixed(2)}ms`);
});
console.log('\n========================\n');
}
/**
* 获取JSON格式的统计数据
*/
toJSON (): FunctionStats[] {
return this.getStats();
}
}
// 全局性能监控器实例
export const performanceMonitor = PerformanceMonitor.getInstance();
// 在进程退出时打印报告并停止定时器
if (typeof process !== 'undefined') {
process.on('exit', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
});
process.on('SIGINT', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
process.exit(0);
});
process.on('SIGTERM', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
process.exit(0);
});
}

View File

@@ -0,0 +1,24 @@
export interface SystemStatus {
cpu: {
model: string,
speed: string;
usage: {
system: string;
qq: string;
},
core: number;
},
memory: {
total: string;
usage: {
system: string;
qq: string;
};
},
arch: string;
}
export interface IStatusHelperSubscription {
on (event: 'statusUpdate', listener: (status: SystemStatus) => void): this;
off (event: 'statusUpdate', listener: (status: SystemStatus) => void): this;
emit (event: 'statusUpdate', status: SystemStatus): boolean;
}

View File

@@ -19,4 +19,4 @@ class Store {
const store = new Store();
export default store;
export default store;

View File

@@ -0,0 +1,6 @@
export type LogListener = (msg: string) => void;
export interface ISubscription {
subscribe (listener: LogListener): void;
unsubscribe (listener: LogListener): void;
notify (msg: string): void;
}

View File

@@ -15,3 +15,14 @@ export type QQVersionConfigType = {
export type QQAppidTableType = {
[key: string]: { appid: string, qua: string };
};
export interface Peer {
chatType: number; // 聊天类型
peerUid: string; // 对等方的唯一标识符
guildId?: string; // 可选的频道ID
}
export interface QQLevel {
crownNum: number;
sunNum: number;
moonNum: number;
starNum: number;
}

View File

@@ -1,3 +1,2 @@
// @ts-ignore
export const napCatVersion = (typeof import.meta?.env !== 'undefined' && import.meta.env.VITE_NAPCAT_VERSION) || 'alpha';

View File

@@ -11,11 +11,11 @@
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": "src",
"rootDir": ".",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
@@ -36,8 +36,8 @@
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/napcat-common/*": [
"src/*"
"@/*": [
"../*"
]
},
"skipLibCheck": true,

View File

@@ -24,4 +24,4 @@ export class NodeIDependsAdapter {
// console.log('[NodeIDependsAdapter] onSendMsfReply', _seq, _cmd, _uk1, _uk2, Buffer.from(_rsp.pbBuffer).toString('hex'));
// }
}
}

View File

@@ -5,12 +5,7 @@ import {
IMAGE_HTTP_HOST_NT,
Peer,
PicElement,
PicSubType,
RawMessage,
SendFileElement,
SendPicElement,
SendPttElement,
SendVideoElement,
} from '@/napcat-core/types';
import path from 'path';
import fs from 'fs';
@@ -19,16 +14,9 @@ import { InstanceContext, NapCatCore, SearchResultItem } from '@/napcat-core/ind
import { fileTypeFromFile } from 'file-type';
import { RkeyManager } from '@/napcat-core/helper/rkey';
import { calculateFileMD5 } from 'napcat-common/src/file';
import pathLib from 'node:path';
import { defaultVideoThumbB64 } from 'napcat-common/src/video';
import { encodeSilk } from 'napcat-common/src/audio';
import { SendMessageContext } from 'napcat-onebot/api/msg';
import { getFileTypeForSendType } from '../helper/msg';
import { FFmpegService } from 'napcat-common/src/ffmpeg';
import { rkeyDataType } from '../types/file';
import { NapProtoMsg } from 'napcat-protobuf';
import { FileId } from '../packet/transformer/proto/misc/fileid';
import { imageSizeFallBack } from 'napcat-image-size';
export class NTQQFileApi {
context: InstanceContext;
@@ -45,7 +33,7 @@ export class NTQQFileApi {
'http://ss.xingzhige.com/music_card/rkey',
'https://secret-service.bietiaop.com/rkeys',
],
this.context.logger
this.context.logger
);
}
@@ -181,165 +169,6 @@ export class NTQQFileApi {
};
}
async createValidSendFileElement (context: SendMessageContext, filePath: string, fileName: string = '', folderId: string = ''): Promise<SendFileElement> {
const {
fileName: _fileName,
path,
fileSize,
} = await this.core.apis.FileApi.uploadFile(filePath, ElementType.FILE);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
context.deleteAfterSentFiles.push(path);
return {
elementType: ElementType.FILE,
elementId: '',
fileElement: {
fileName: fileName || _fileName,
folderId,
filePath: path,
fileSize: fileSize.toString(),
},
};
}
async createValidSendPicElement (context: SendMessageContext, picPath: string, summary: string = '', subType: PicSubType = 0): Promise<SendPicElement> {
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
const imageSize = await imageSizeFallBack(picPath);
context.deleteAfterSentFiles.push(path);
return {
elementType: ElementType.PIC,
elementId: '',
picElement: {
md5HexStr: md5,
fileSize: fileSize.toString(),
picWidth: imageSize.width,
picHeight: imageSize.height,
fileName,
sourcePath: path,
original: true,
picType: await getFileTypeForSendType(picPath),
picSubType: subType,
fileUuid: '',
fileSubId: '',
thumbFileSize: 0,
summary,
} as PicElement,
};
}
async createValidSendVideoElement (context: SendMessageContext, filePath: string, fileName: string = '', _diyThumbPath: string = ''): Promise<SendVideoElement> {
let videoInfo = {
width: 1920,
height: 1080,
time: 15,
format: 'mp4',
size: 0,
filePath,
};
let fileExt = 'mp4';
try {
const tempExt = (await fileTypeFromFile(filePath))?.ext;
if (tempExt) fileExt = tempExt;
} catch (e) {
this.context.logger.logError('获取文件类型失败', e);
}
const newFilePath = `${filePath}.${fileExt}`;
fs.copyFileSync(filePath, newFilePath);
context.deleteAfterSentFiles.push(newFilePath);
filePath = newFilePath;
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
context.deleteAfterSentFiles.push(path);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
const thumbDir = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
fs.mkdirSync(pathLib.dirname(thumbDir), { recursive: true });
const thumbPath = pathLib.join(pathLib.dirname(thumbDir), `${md5}_0.png`);
try {
videoInfo = await FFmpegService.getVideoInfo(filePath, thumbPath);
if (!fs.existsSync(thumbPath)) {
this.context.logger.logError('获取视频缩略图失败', new Error('缩略图不存在'));
throw new Error('获取视频缩略图失败');
}
} catch (e) {
this.context.logger.logError('获取视频信息失败', e);
fs.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
}
if (_diyThumbPath) {
try {
await this.copyFile(_diyThumbPath, thumbPath);
} catch (e) {
this.context.logger.logError('复制自定义缩略图失败', e);
}
}
context.deleteAfterSentFiles.push(thumbPath);
const thumbSize = (await fsPromises.stat(thumbPath)).size;
const thumbMd5 = await calculateFileMD5(thumbPath);
context.deleteAfterSentFiles.push(thumbPath);
const uploadName = (fileName || _fileName).toLocaleLowerCase().endsWith(`.${fileExt.toLocaleLowerCase()}`) ? (fileName || _fileName) : `${fileName || _fileName}.${fileExt}`;
return {
elementType: ElementType.VIDEO,
elementId: '',
videoElement: {
fileName: uploadName,
filePath: path,
videoMd5: md5,
thumbMd5,
fileTime: videoInfo.time,
thumbPath: new Map([[0, thumbPath]]),
thumbSize,
thumbWidth: videoInfo.width,
thumbHeight: videoInfo.height,
fileSize: fileSize.toString(),
},
};
}
async createValidSendPttElement (_context: SendMessageContext, pttPath: string): Promise<SendPttElement> {
const { converted, path: silkPath, duration } = await encodeSilk(pttPath, this.core.NapCatTempPath, this.core.context.logger);
if (!silkPath) {
throw new Error('语音转换失败, 请检查语音文件是否正常');
}
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(silkPath, ElementType.PTT);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
if (converted) {
fsPromises.unlink(silkPath).then().catch((e) => this.context.logger.logError('删除临时文件失败', e));
}
return {
elementType: ElementType.PTT,
elementId: '',
pttElement: {
fileName,
filePath: path,
md5HexStr: md5,
fileSize: fileSize.toString(),
duration: duration ?? 1,
formatType: 1,
voiceType: 1,
voiceChangeType: 0,
canConvert2Text: true,
waveAmplitudes: [
0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17,
],
fileSubId: '',
playState: 1,
autoConvertText: 0,
storeID: 0,
otherBusinessInfo: {
aiVoiceType: 0,
},
},
};
}
async downloadFileForModelId (peer: Peer, modelId: string, unknown: string, timeout = 1000 * 60 * 2) {
const [, fileTransNotifyInfo] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelRichMediaService/downloadFileForModelId',

View File

@@ -1,5 +1,5 @@
import { FriendRequest, FriendV2 } from 'napcat-core/types';
import { BuddyListReqType, InstanceContext, NapCatCore } from 'napcat-core/index';
import { FriendRequest, FriendV2 } from '@/napcat-core/types';
import { BuddyListReqType, InstanceContext, NapCatCore } from '@/napcat-core/index';
import { LimitedHashTable } from 'napcat-common/src/message-unique';
export class NTQQFriendApi {

View File

@@ -15,9 +15,9 @@ import {
} from '@/napcat-core/index';
import { isNumeric, solveAsyncProblem } from 'napcat-common/src/helper';
import { LimitedHashTable } from 'napcat-common/src/message-unique';
import { NTEventWrapper } from 'napcat-common/src/event';
import { CancelableTask, TaskExecutor } from 'napcat-common/src/cancel-task';
import { createGroupDetailInfoV2Param, createGroupExtFilter, createGroupExtInfo } from '../data';
import { NTEventWrapper } from '../helper/event';
export class NTQQGroupApi {
context: InstanceContext;
@@ -395,7 +395,7 @@ export class NTQQGroupApi {
'NodeIKernelGroupListener/onMemberInfoChange',
[groupCode, [uid], forced],
(ret) => ret.result === 0,
(params, _, members) => params === GroupCode && members.size > 0 && members.has(uid),
(params: string, _: any, members: Map<string, GroupMember>) => params === GroupCode && members.size > 0 && members.has(uid),
1,
forced ? 2500 : 250
);

View File

@@ -1,9 +1,9 @@
import * as os from 'os';
import offset from '@/napcat-core/external/napi2native.json';
import { InstanceContext, NapCatCore } from '@/napcat-core/index';
import { LogWrapper } from 'napcat-common/src/log';
import { PacketClientSession } from '@/napcat-core/packet/clientSession';
import { napCatVersion } from 'napcat-common/src/version';
import { LogWrapper } from '../helper/log';
interface OffsetType {
[key: string]: {

View File

@@ -2,10 +2,10 @@ import fsPromise from 'fs/promises';
import path from 'node:path';
import { randomUUID } from 'crypto';
import { EncodeResult, getDuration, getWavFileInfo, isSilk, isWav } from 'silk-wasm';
import { LogWrapper } from '@/napcat-common/log';
import { EncodeArgs } from '@/napcat-common/audio-worker';
import { FFmpegService } from '@/napcat-common/ffmpeg';
import { runTask } from './worker';
import { LogWrapper } from '@/napcat-core/helper/log';
import { EncodeArgs } from 'napcat-common/src/audio-worker';
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
import { runTask } from 'napcat-common/src/worker';
import { fileURLToPath } from 'node:url';
const ALLOW_SAMPLE_RATE = [8000, 12000, 16000, 24000, 32000, 44100, 48000];

View File

@@ -1,6 +1,6 @@
import path from 'node:path';
import fs from 'node:fs';
import type { NapCatCore } from 'napcat-core';
import type { NapCatCore } from '@/napcat-core';
import json5 from 'json5';
import Ajv, { AnySchema, ValidateFunction } from 'ajv';

View File

@@ -1,4 +1,4 @@
import { ConfigBase } from 'napcat-common/src/config-base';
import { ConfigBase } from '@/napcat-core/helper/config-base';
import { NapCatCore } from '@/napcat-core/index';
import { Type, Static } from '@sinclair/typebox';
import { AnySchema } from 'ajv';

View File

@@ -1,6 +1,6 @@
import { NodeIQQNTWrapperSession } from 'napcat-core/wrapper';
import { NodeIQQNTWrapperSession } from '@/napcat-core/wrapper';
import { randomUUID } from 'crypto';
import { ListenerNamingMapping, ServiceNamingMapping } from 'napcat-core/index';
import { ListenerNamingMapping, ServiceNamingMapping } from '@/napcat-core/index';
interface InternalMapKey {
timeout: number;
@@ -54,7 +54,7 @@ export class NTEventWrapper {
Service extends keyof ServiceNamingMapping,
ServiceMethod extends FuncKeys<ServiceNamingMapping[Service]>,
T extends (...args: any) => any = EnsureFunc<ServiceNamingMapping[Service][ServiceMethod]>
> (eventName: `${Service}/${ServiceMethod}`): T | undefined {
>(eventName: `${Service}/${ServiceMethod}`): T | undefined {
const eventNameArr = eventName.split('/');
type eventType = {
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>>; };
@@ -78,18 +78,15 @@ export class NTEventWrapper {
return undefined;
}
createListenerFunction<T, Listener extends keyof ListenerNamingMapping,
ListenerMethod extends FuncKeys<ListenerNamingMapping[Listener]>> (listenerMainName: string, uniqueCode: string = ''): T {
createListenerFunction<T> (listenerMainName: string, uniqueCode: string = ''): T {
const existListener = this.listenerManager.get(listenerMainName + uniqueCode);
if (!existListener) {
const Listener = this.createProxyDispatch(listenerMainName);
const ServiceSubName = /^NodeIKernel(.*?)Listener$/.exec(listenerMainName)![1];
const Service = `NodeIKernel${ServiceSubName}Service/addKernel${ServiceSubName}Listener` as `${Listener}/${ListenerMethod}`;
this.createEventFunction<
keyof ServiceNamingMapping,
FuncKeys<ServiceNamingMapping[keyof ServiceNamingMapping]>,
(...args: any[]) => any
>(Service as `${keyof ServiceNamingMapping}/${FuncKeys<ServiceNamingMapping[keyof ServiceNamingMapping]>}`);
const Service = `NodeIKernel${ServiceSubName}Service/addKernel${ServiceSubName}Listener`;
// @ts-ignore
this.createEventFunction(Service)(Listener as T);
this.listenerManager.set(listenerMainName + uniqueCode, Listener);
return Listener as T;
}
@@ -115,7 +112,7 @@ export class NTEventWrapper {
Service extends keyof ServiceNamingMapping,
ServiceMethod extends FuncKeys<ServiceNamingMapping[Service]>,
EventType extends (...args: any) => any = EnsureFunc<ServiceNamingMapping[Service][ServiceMethod]>
> (
>(
serviceAndMethod: `${Service}/${ServiceMethod}`,
...args: Parameters<EventType>
): Promise<Awaited<ReturnType<EventType>>> {
@@ -126,7 +123,7 @@ export class NTEventWrapper {
Listener extends keyof ListenerNamingMapping,
ListenerMethod extends FuncKeys<ListenerNamingMapping[Listener]>,
ListenerType extends (...args: any) => any = EnsureFunc<ListenerNamingMapping[Listener][ListenerMethod]>
> (
>(
listenerAndMethod: `${Listener}/${ListenerMethod}`,
checker: (...args: Parameters<ListenerType>) => boolean,
waitTimes = 1,
@@ -180,7 +177,7 @@ export class NTEventWrapper {
ListenerMethod extends FuncKeys<ListenerNamingMapping[Listener]>,
EventType extends (...args: any) => any = EnsureFunc<ServiceNamingMapping[Service][ServiceMethod]>,
ListenerType extends (...args: any) => any = EnsureFunc<ListenerNamingMapping[Listener][ListenerMethod]>
> (
>(
serviceAndMethod: `${Service}/${ServiceMethod}`,
listenerAndMethod: `${Listener}/${ListenerMethod}`,
args: Parameters<EventType>,

View File

@@ -6,7 +6,7 @@ import * as os from 'os';
import * as compressing from 'compressing'; // 修正导入方式
import { pipeline } from 'stream/promises';
import { fileURLToPath } from 'url';
import { LogWrapper } from './log';
import { LogWrapper } from '@/napcat-core/helper/log';
const downloadOri = 'https://github.com/NapNeko/ffmpeg-build/releases/download/v1.0.0/ffmpeg-7.1.1-win64.zip';
const urls = [

View File

@@ -3,7 +3,7 @@
* FFmpeg
*/
import { LogWrapper } from './log';
import { LogWrapper } from '@/napcat-core/helper/log';
import { FFmpegAddonAdapter } from './ffmpeg-addon-adapter';
import { FFmpegExecAdapter } from './ffmpeg-exec-adapter';
import type { IFFmpegAdapter } from './ffmpeg-adapter-interface';

View File

@@ -68,13 +68,13 @@ export class FFmpegAddonAdapter implements IFFmpegAdapter {
const addon = this.ensureAddon();
const info = await addon.getVideoInfo(videoPath);
let format = info.format.includes(',') ? info.format.split(',')[0] ?? info.format : info.format;
const format = info.format.includes(',') ? info.format.split(',')[0] ?? info.format : info.format;
console.log('[FFmpegAddonAdapter] Detected format:', format);
return {
width: info.width,
height: info.height,
duration: info.duration,
format: format,
format,
thumbnail: info.image,
};
}

View File

@@ -10,7 +10,7 @@ import { promisify } from 'util';
import { fileTypeFromFile } from 'file-type';
import { imageSizeFallBack } from 'napcat-image-size/src/index';
import { downloadFFmpegIfNotExists } from './download-ffmpeg';
import { LogWrapper } from './log';
import { LogWrapper } from '@/napcat-core/helper/log';
import type { IFFmpegAdapter, VideoInfoResult } from './ffmpeg-adapter-interface';
const execFileAsync = promisify(execFile);

View File

@@ -3,7 +3,7 @@ import path from 'path';
import type { VideoInfo } from './video';
import { fileTypeFromFile } from 'file-type';
import { platform } from 'node:os';
import { LogWrapper } from './log';
import { LogWrapper } from '@/napcat-core/helper/log';
import { FFmpegAdapterFactory } from './ffmpeg-adapter-factory';
import type { IFFmpegAdapter } from './ffmpeg-adapter-interface';
@@ -53,7 +53,6 @@ export class FFmpegService {
throw new Error('FFmpeg service not initialized. Please call FFmpegService.init() first.');
}
return this.adapter.name;
}
/**

View File

@@ -1,5 +1,5 @@
import * as crypto from 'node:crypto';
import { PacketMsg } from 'napcat-core/packet/message/message';
import { PacketMsg } from '@/napcat-core/packet/message/message';
interface ForwardMsgJson {
app: string

View File

@@ -1,8 +1,9 @@
import winston, { format, transports } from 'winston';
import { truncateString } from './helper';
import { truncateString } from 'napcat-common/src/helper';
import path from 'node:path';
import fs from 'node:fs/promises';
import { NTMsgAtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from 'napcat-core/index';
import { NTMsgAtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/napcat-core/index';
import { ILogWrapper } from 'napcat-common/src/log-interface';
import EventEmitter from 'node:events';
export enum LogLevel {
DEBUG = 'debug',
@@ -56,7 +57,7 @@ class Subscription {
export const logSubscription = new Subscription();
export class LogWrapper {
export class LogWrapper implements ILogWrapper {
fileLogEnabled = true;
consoleLogEnabled = true;
logger: winston.Logger;

View File

@@ -1,4 +1,4 @@
import { LogWrapper } from '@/napcat-common/log';
import { LogWrapper } from '@/napcat-core/helper/log';
export function proxyHandlerOf (logger: LogWrapper) {
return {

View File

@@ -1,10 +1,10 @@
import fs from 'node:fs';
import { systemPlatform } from '@/napcat-common/system';
import { getDefaultQQVersionConfigInfo, getQQPackageInfoPath, getQQVersionConfigPath, parseAppidFromMajor } from './helper';
import AppidTable from 'napcat-core/external/appid.json';
import { LogWrapper } from '@/napcat-common/log';
import { getMajorPath } from 'napcat-core';
import { QQAppidTableType, QQPackageInfoType, QQVersionConfigType } from './types';
import { systemPlatform } from 'napcat-common/src/system';
import { getDefaultQQVersionConfigInfo, getQQPackageInfoPath, getQQVersionConfigPath, parseAppidFromMajor } from 'napcat-common/src/helper';
import AppidTable from '@/napcat-core/external/appid.json';
import { LogWrapper } from './log';
import { getMajorPath } from '@/napcat-core/index';
import { QQAppidTableType, QQPackageInfoType, QQVersionConfigType } from 'napcat-common/src/types';
export class QQBasicInfoWrapper {
QQMainPath: string | undefined;

View File

@@ -1,5 +1,5 @@
import { LogWrapper } from 'napcat-common/src/log';
import { RequestUtil } from 'napcat-common/src/request';
import { LogWrapper } from './log';
interface ServerRkeyData {
group_rkey: string;

View File

@@ -1,24 +1,24 @@
import os from 'node:os';
import EventEmitter from 'node:events';
import { IStatusHelperSubscription } from 'napcat-common/src/status-interface';
export interface SystemStatus {
cpu: {
model: string,
speed: string
speed: string;
usage: {
system: string
qq: string
system: string;
qq: string;
},
core: number
core: number;
},
memory: {
total: string
total: string;
usage: {
system: string
qq: string
}
system: string;
qq: string;
};
},
arch: string
arch: string;
}
export class StatusHelper {
@@ -101,7 +101,7 @@ export class StatusHelper {
}
}
class StatusHelperSubscription extends EventEmitter {
class StatusHelperSubscription extends EventEmitter implements IStatusHelperSubscription {
private statusHelper: StatusHelper;
private interval: NodeJS.Timeout | null = null;

View File

@@ -6,7 +6,7 @@ import {
NTQQSystemApi,
NTQQUserApi,
NTQQWebApi,
} from 'napcat-core/apis';
} from '@/napcat-core/apis';
import { NTQQCollectionApi } from '@/napcat-core/apis/collection';
import {
NodeIQQNTWrapperSession,
@@ -16,21 +16,24 @@ import {
WrapperNodeApi,
WrapperSessionInitConfig,
} from '@/napcat-core/wrapper';
import { LogLevel, LogWrapper } from 'napcat-common/src/log';
import { LogLevel, LogWrapper } from '@/napcat-core/helper/log';
import { NodeIKernelLoginService } from '@/napcat-core/services';
import { QQBasicInfoWrapper } from 'napcat-common/src/qq-basic-info';
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
import { NapCatPathWrapper } from 'napcat-common/src/path';
import path from 'node:path';
import fs from 'node:fs';
import { hostname, systemName, systemVersion } from 'napcat-common/src/system';
import { NTEventWrapper } from 'napcat-common/src/event';
import { NTEventWrapper } from '@/napcat-core/helper/event';
import { KickedOffLineInfo, SelfInfo, SelfStatusInfo } from '@/napcat-core/types';
import { NapCatConfigLoader, NapcatConfigSchema } from '@/napcat-core/helper/config';
import os from 'node:os';
import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/napcat-core/listeners';
import { proxiedListenerOf } from 'napcat-common/src/proxy-handler';
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
import { NTQQPacketApi } from './apis/packet';
import { NativePacketHandler } from './packet/handler/client';
import { container, ReceiverServiceRegistry } from './packet/handler/serviceRegister';
import { appEvent } from './packet/handler/eventList';
import { TypedEventEmitter } from './packet/handler/typeEvent';
export * from './wrapper';
export * from './types/index';
export * from './services/index';
@@ -92,6 +95,7 @@ export function getMajorPath (QQVersion: string): string {
export class NapCatCore {
readonly context: InstanceContext;
readonly eventWrapper: NTEventWrapper;
event = appEvent;
NapCatDataPath: string = '';
NapCatTempPath: string = '';
apis: StableNTApiWrapper;
@@ -118,6 +122,16 @@ export class NapCatCore {
UserApi: new NTQQUserApi(this.context, this),
GroupApi: new NTQQGroupApi(this.context, this),
};
container.bind(NapCatCore).toConstantValue(this);
container.bind(TypedEventEmitter).toConstantValue(this.event);
ReceiverServiceRegistry.forEach((ServiceClass, serviceName) => {
container.bind(ServiceClass).toSelf();
console.log(`Registering service handler for: ${serviceName}`);
this.context.packetHandler.onCmd(serviceName, ({ seq, hex_data }) => {
const serviceInstance = container.get(ServiceClass);
return serviceInstance.handler(seq, hex_data);
});
});
}
async initCore () {

View File

@@ -1,5 +1,5 @@
import { ChatType, KickedOffLineInfo, RawMessage } from '@/napcat-core/types';
import { CommonFileInfo } from 'napcat-core/index';
import { CommonFileInfo } from '@/napcat-core/index';
export interface OnRichMediaDownloadCompleteParams {
fileModelId: string,

View File

@@ -4,6 +4,9 @@
"private": true,
"type": "module",
"main": "index.ts",
"scripts": {
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"exports": {
".": {
"import": "./index.ts"
@@ -13,15 +16,18 @@
}
},
"dependencies": {
"winston": "^3.17.0",
"json5": "^2.2.3",
"inversify": "^7.10.4",
"reflect-metadata": "^0.2.2",
"@protobuf-ts/runtime": "^2.11.1",
"napcat-protobuf": "workspace:*",
"ajv": "^8.13.0",
"@sinclair/typebox": "^0.34.38",
"file-type": "^21.0.0",
"compressing": "^1.10.1",
"napcat-image-size": "workspace:*",
"napcat-core": "workspace:*",
"napcat-common": "workspace:*",
"napcat-onebot": "workspace:*"
"napcat-protobuf": "workspace:*"
},
"devDependencies": {
"@types/node": "^22.0.1"

View File

@@ -1,5 +1,5 @@
import { LogLevel, LogWrapper } from 'napcat-common/src/log';
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
import { LogWrapper, LogLevel } from '@/napcat-core/helper/log';
// TODO: check bind?
export class PacketLogger {

View File

@@ -9,7 +9,7 @@ import {
PacketMsgReplyElement,
PacketMsgVideoElement,
} from '@/napcat-core/packet/message/element';
import { ChatType, MsgSourceType, NTMsgType, RawMessage } from 'napcat-core/index';
import { ChatType, MsgSourceType, NTMsgType, RawMessage } from '@/napcat-core/index';
import { MiniAppRawData, MiniAppReqParams } from '@/napcat-core/packet/entities/miniApp';
import { AIVoiceChatType } from '@/napcat-core/packet/entities/aiChat';
import { NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg } from 'napcat-protobuf';

View File

@@ -1,5 +1,5 @@
import { PacketHighwayContext } from '@/napcat-core/packet/highway/highwayContext';
import { NapCatCore } from 'napcat-core/index';
import { NapCatCore } from '@/napcat-core/index';
import { PacketLogger } from '@/napcat-core/packet/context/loggerContext';
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
import { PacketClientContext } from '@/napcat-core/packet/context/clientContext';

View File

@@ -2,8 +2,8 @@ import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { constants } from 'node:os';
import { LogWrapper } from 'napcat-common/src/log';
import offset from '@/napcat-core/external/packet.json';
import { LogWrapper } from '../../helper/log';
interface OffsetType {
[key: string]: {
recv: string;
@@ -50,7 +50,6 @@ export class NativePacketHandler {
this.logger.logError('NativePacketClient 加载出错:', error);
this.loaded = false;
}
}
/**

View File

@@ -0,0 +1,6 @@
import { TypedEventEmitter } from './typeEvent';
export interface AppEvents {
'event:emoji_like': { groupId: string; senderUin: string; emojiId: string, msgSeq: string, isAdd: boolean, count: number };
}
export const appEvent = new TypedEventEmitter<AppEvents>();

View File

@@ -0,0 +1,28 @@
import 'reflect-metadata';
import { Container, injectable } from 'inversify';
import { NapCatCore } from '../..';
import { TypedEventEmitter } from './typeEvent';
export const container = new Container();
export const ReceiverServiceRegistry = new Map<string, new (...args: any[]) => ServiceBase>();
export abstract class ServiceBase {
get core (): NapCatCore {
return container.get(NapCatCore);
}
get event () {
return container.get(TypedEventEmitter);
}
abstract handler (seq: number, hex_data: string): Promise<void> | void;
}
export function ReceiveService (serviceName: string) {
return function <T extends new (...args: any[]) => ServiceBase>(constructor: T) {
injectable()(constructor);
ReceiverServiceRegistry.set(serviceName, constructor);
return constructor;
};
}

View File

@@ -0,0 +1,22 @@
import { EventEmitter } from 'node:events';
export class TypedEventEmitter<E extends Record<string, any>> {
private emitter = new EventEmitter();
on<K extends keyof E>(event: K, listener: (payload: E[K]) => void) {
this.emitter.on(event as string, listener);
return () => this.off(event, listener);
}
once<K extends keyof E>(event: K, listener: (payload: E[K]) => void) {
this.emitter.once(event as string, listener);
}
off<K extends keyof E>(event: K, listener: (payload: E[K]) => void) {
this.emitter.off(event as string, listener);
}
emit<K extends keyof E>(event: K, payload: E[K]) {
this.emitter.emit(event as string, payload);
}
}

View File

@@ -32,8 +32,8 @@ import {
SendTextElement,
SendVideoElement,
Peer,
} from 'napcat-core/index';
import { ForwardMsgBuilder } from 'napcat-common/src/forward-msg-builder';
} from '@/napcat-core/index';
import { ForwardMsgBuilder } from '@/napcat-core/helper/forward-msg-builder';
import { PacketMsg, PacketSendMsgElement } from '@/napcat-core/packet/message/message';
export type ParseElementFnR = [MessageElement, NapProtoDecodeStructType<typeof Elem> | null] | undefined;

View File

@@ -1,5 +1,5 @@
import { IPacketMsgElement } from '@/napcat-core/packet/message/element';
import { SendMessageElement, SendMultiForwardMsgElement } from 'napcat-core/index';
import { SendMessageElement, SendMultiForwardMsgElement } from '@/napcat-core/index';
export type PacketSendMsgElement = SendMessageElement | SendMultiForwardMsgElement;

View File

@@ -0,0 +1,36 @@
import { NapProtoMsg } from 'napcat-protobuf';
import { ReceiveService, ServiceBase } from '../packet/handler/serviceRegister';
import { GroupReactNotify, PushMsg } from '../packet/transformer/proto';
@ReceiveService('trpc.msg.olpush.OlPushService.MsgPush')
export class OlPushService extends ServiceBase {
async handler (_seq: number, hex_data: string) {
const data = new NapProtoMsg(PushMsg).decode(Buffer.from(hex_data, 'hex'));
if (data.message.contentHead.type === 732 && data.message.contentHead.subType === 16) {
const pbNotify = data.message.body?.msgContent?.slice(7);
if (!pbNotify) {
return;
}
// 开始解析Notify
const notify = new NapProtoMsg(GroupReactNotify).decode(pbNotify);
if ((notify.field13 ?? 0) === 35) {
// Group React Notify
const groupCode = notify.groupUin?.toString() ?? '';
const operatorUid = notify.groupReactionData?.data?.data?.groupReactionDataContent?.operatorUid ?? '';
const type = notify.groupReactionData?.data?.data?.groupReactionDataContent?.type ?? 0;
const seq = notify.groupReactionData?.data?.data?.groupReactionTarget?.seq?.toString() ?? '';
const code = notify.groupReactionData?.data?.data?.groupReactionDataContent?.code ?? '';
const count = notify.groupReactionData?.data?.data?.groupReactionDataContent?.count ?? 0;
const senderUin = await this.core.apis.UserApi.getUinByUidV2(operatorUid);
this.event.emit('event:emoji_like', {
groupId: groupCode,
senderUin,
emojiId: code,
msgSeq: seq,
isAdd: type === 1,
count,
});
}
}
}
}

View File

@@ -24,7 +24,6 @@ export interface NodeIKernelBuddyService {
}>;
}>;
getBuddyListFromCache (reqType: BuddyListReqType): Promise<Array<
{
categoryId: number, // 9999为特别关心

View File

@@ -1,5 +1,5 @@
import { AnyCnameRecord } from 'node:dns';
import { BizKey, ModifyProfileParams, NodeIKernelProfileListener, ProfileBizType, SimpleInfo, UserDetailInfoByUin, UserDetailInfoListenerArg, UserDetailSource } from 'napcat-core/index';
import { BizKey, ModifyProfileParams, NodeIKernelProfileListener, ProfileBizType, SimpleInfo, UserDetailInfoByUin, UserDetailInfoListenerArg, UserDetailSource } from '@/napcat-core/index';
import { GeneralCallResult } from '@/napcat-core/services/common';
export interface NodeIKernelProfileService {

View File

@@ -1,48 +1,5 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": ".",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/napcat-core/*": [
"*"
]
},
"skipLibCheck": true,
"skipDefaultLibCheck": true
},
"extends": "../../tsconfig.base.json",
"include": [
"*.ts",
"**/*.ts"

View File

@@ -23,13 +23,13 @@ type ElementBase<
K extends keyof ElementFullBase,
S extends Partial<{ [P in K]: keyof NonNullable<ElementFullBase[P]> | Array<keyof NonNullable<ElementFullBase[P]>> }> = object
> = {
[P in K]:
S[P] extends Array<infer U>
[P in K]:
S[P] extends Array<infer U>
? Pick<NonNullable<ElementFullBase[P]>, U & keyof NonNullable<ElementFullBase[P]>>
: S[P] extends keyof NonNullable<ElementFullBase[P]>
? Pick<NonNullable<ElementFullBase[P]>, S[P]>
: NonNullable<ElementFullBase[P]>;
};
? Pick<NonNullable<ElementFullBase[P]>, S[P]>
: NonNullable<ElementFullBase[P]>;
};
export interface TextElement {
content: string;

View File

@@ -10,5 +10,5 @@ const NAPCAT_MJS_PATH = path.join(BASE_DIR, 'napcat', 'napcat.mjs');
process.env.NAPCAT_WRAPPER_PATH = WRAPPER_NODE_PATH;
process.env.NAPCAT_QQ_PACKAGE_INFO_PATH = PACKAGE_JSON_PATH;
process.env.NAPCAT_QQ_VERSION_CONFIG_PATH = CONFIG_JSON_PATH;
process.env.NAPCAT_DISABLE_PIPE = '1';
import(pathToFileURL(NAPCAT_MJS_PATH).href);
process.env.NAPCAT_DISABLE_PIPE = '1';
import(pathToFileURL(NAPCAT_MJS_PATH).href);

View File

@@ -12,15 +12,28 @@ if (!mainPath) {
const versionsDir = path.join(mainPath, 'versions');
console.log(`Looking for version folders in: ${versionsDir}`);
const versionFolders = fs.readdirSync(versionsDir).filter(f => fs.statSync(path.join(versionsDir, f)).isDirectory());
if (versionFolders.length !== 1) {
console.error('versions 文件夹下必须且只能有一个版本目录');
let selectedFolder;
if (versionFolders.length === 0) {
console.error('versions 文件夹下没有找到版本目录');
process.exit(1);
} else if (versionFolders.length === 1) {
selectedFolder = versionFolders[0];
} else {
// 获取时间最新的文件夹
const stats = versionFolders.map(folder => {
const folderPath = path.join(versionsDir, folder);
return { folder, mtime: fs.statSync(folderPath).mtime };
});
stats.sort((a, b) => b.mtime - a.mtime);
selectedFolder = stats[0].folder;
console.log(`多个版本文件夹存在,选择最新的: ${selectedFolder}`);
}
const BASE_DIR = path.join(versionsDir, versionFolders[0], 'resources', 'app');
const BASE_DIR = path.join(versionsDir, selectedFolder, 'resources', 'app');
console.log(`BASE_DIR: ${BASE_DIR}`);
const TARGET_DIR = path.join(__dirname, 'dist');
const QQNT_FILE = path.join(__dirname, 'QQNT.dll');
const NAPCAT_MJS_PATH = path.join(__dirname, '..', 'napcat-shell','dist', 'napcat.mjs');
const NAPCAT_MJS_PATH = path.join(__dirname, '..', 'napcat-shell', 'dist', 'napcat.mjs');
const itemsToCopy = [
'avif_convert.dll',
@@ -32,15 +45,15 @@ const itemsToCopy = [
'opencv.dll',
'package.json',
'QBar.dll',
'wrapper.node'
'wrapper.node',
];
async function copyAll () {
const qqntDllPath = path.join(TARGET_DIR, 'QQNT.dll');
const configPath = path.join(TARGET_DIR, 'config.json');
const allItemsExist = await fs.pathExists(qqntDllPath)
&& await fs.pathExists(configPath)
&& (await Promise.all(itemsToCopy.map(item => fs.pathExists(path.join(TARGET_DIR, item))))).every(exists => exists);
const allItemsExist = await fs.pathExists(qqntDllPath) &&
await fs.pathExists(configPath) &&
(await Promise.all(itemsToCopy.map(item => fs.pathExists(path.join(TARGET_DIR, item))))).every(exists => exists);
if (!allItemsExist) {
console.log('Copying required files...');
@@ -61,9 +74,11 @@ async function copyAll () {
process.env.NAPCAT_QQ_VERSION_CONFIG_PATH = path.join(TARGET_DIR, 'config.json');
process.env.NAPCAT_DISABLE_PIPE = '1';
process.env.NAPCAT_WORKDIR = TARGET_DIR;
// 开发环境使用固定密钥
process.env.NAPCAT_WEBUI_JWT_SECRET_KEY = 'napcat_dev_secret_key';
process.env.NAPCAT_WEBUI_SECRET_KEY = 'napcat';
console.log('Loading NapCat module...');
await import(pathToFileURL(NAPCAT_MJS_PATH).href);
}
copyAll().catch(console.error);
copyAll().catch(console.error);

View File

@@ -5,4 +5,4 @@ $uninstall = $uninstall.Trim('"')
$qqPath = Split-Path $uninstall -Parent
Write-Host "QQPath: $qqPath"
node.exe "./loadNapCat.cjs" "$qqPath"
node.exe --inspect "./loadNapCat.cjs" "$qqPath"

View File

@@ -3,13 +3,13 @@
"version": "0.0.1",
"private": true,
"type": "module",
"main": "index.cjs",
"main": "index.js",
"scripts": {
"dev": "powershell ./nodeTest.ps1"
},
"exports": {
".": {
"require": "./index.cjs"
"require": "./index.js"
},
"./*": {
"require": "./*"

View File

@@ -1,46 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": "./",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": "./",
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"allowJs": true
},
"include": [
"**/*.cjs"
"*.cjs",
"**/*.cjs",
"**/*.ts",
"*.js",
"**/*.js"
],
"exclude": [
"node_modules",

View File

@@ -1,16 +1,13 @@
import { NapCatPathWrapper } from 'napcat-common/src/path';
import { LogWrapper } from 'napcat-common/src/log';
import { proxiedListenerOf } from 'napcat-common/src/proxy-handler';
import { QQBasicInfoWrapper } from 'napcat-common/src/qq-basic-info';
import { InstanceContext, loadQQWrapper, NapCatCore, NapCatCoreWorkingEnv } from 'napcat-core/index';
import { SelfInfo } from 'napcat-core/types';
import { NodeIKernelLoginListener } from 'napcat-core/listeners';
import { NodeIKernelLoginService } from 'napcat-core/services';
import { NodeIQQNTWrapperSession, WrapperNodeApi } from 'napcat-core/wrapper';
import { InitWebUi, WebUiConfig, webUiRuntimePort } from 'napcat-webui-backend/src/index';
import { InitWebUi, WebUiConfig, webUiRuntimePort } from 'napcat-webui-backend/index';
import { NapCatOneBot11Adapter } from 'napcat-onebot/index';
import { FFmpegService } from 'napcat-common/src/ffmpeg';
import { NativePacketHandler } from 'napcat-core/packet/handler/client';
import { FFmpegService } from 'napcat-core/helper/ffmpeg/ffmpeg';
import { logSubscription, LogWrapper } from 'napcat-core/helper/log';
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
import { InstanceContext, loadQQWrapper, NapCatCore, NapCatCoreWorkingEnv, NodeIKernelLoginListener, NodeIKernelLoginService, NodeIQQNTWrapperSession, SelfInfo, WrapperNodeApi } from '@/napcat-core';
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
import { statusHelperSubscription } from '@/napcat-core/helper/status';
// Framework ES入口文件
export async function getWebUiUrl () {
@@ -76,7 +73,7 @@ export async function NCoreInitFramework (
await loaderObject.core.initCore();
// 启动WebUi
InitWebUi(logger, pathWrapper).then().catch(e => logger.logError(e));
InitWebUi(logger, pathWrapper, logSubscription, statusHelperSubscription).then().catch(e => logger.logError(e));
// 初始化LLNC的Onebot实现
await new NapCatOneBot11Adapter(loaderObject.core, loaderObject.context, pathWrapper).InitOneBot();
}

View File

@@ -1,49 +1,5 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": ".",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/napcat-onebot/*": ["../napcat-onebot/*"],
"@/napcat-core/*": ["../napcat-core/*"],
"@/napcat-common/*": ["../napcat-common/src/*"],
"@/napcat-webui-backend/*": ["../napcat-webui-backend/src/*"]
},
"skipLibCheck": true,
"skipDefaultLibCheck": true
},
"extends": "../../tsconfig.base.json",
"include": [
"*.ts",
"**/*.ts"

View File

@@ -2,22 +2,23 @@ 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 "napcat-vite/vite-auto-include.js";
import { autoIncludeTSPlugin } from 'napcat-vite/vite-auto-include.js';
import { builtinModules } from 'module';
import react from '@vitejs/plugin-react-swc';
import napcatVersion from "napcat-vite/vite-plugin-version.js";
//依赖排除
import napcatVersion from 'napcat-vite/vite-plugin-version.js';
// 依赖排除
const external = [
'silk-wasm',
'ws',
'express'
'express',
];
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
const FrameworkBaseConfigPlugin: PluginOption[] = [
autoIncludeTSPlugin({
entries: [
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-onebot/action/test') }
]
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-core/protocol') },
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-onebot/action/test') },
],
}),
react({ tsDecorators: true }),
cp({
@@ -45,10 +46,10 @@ const FrameworkBaseConfig = () =>
conditions: ['node', 'default'],
alias: {
'@/napcat-core': resolve(__dirname, '../napcat-core'),
'@/napcat-common': resolve(__dirname, '../napcat-common/src'),
'@/napcat-common': resolve(__dirname, '../napcat-common'),
'@/napcat-onebot': resolve(__dirname, '../napcat-onebot'),
'@/napcat-pty': resolve(__dirname, '../napcat-pty'),
'@/napcat-webui-backend': resolve(__dirname, '../napcat-webui-backend/src'),
'@/napcat-webui-backend': resolve(__dirname, '../napcat-webui-backend'),
'@/image-size': resolve(__dirname, '../image-size'),
},
},

View File

@@ -1,48 +1,5 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": "src",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/napcat-webui-backend/*": [
"src/*"
]
},
"skipLibCheck": true,
"skipDefaultLibCheck": true
},
"extends": "../../tsconfig.base.json",
"include": [
"src/**/*.ts"
],

View File

@@ -1,6 +1,6 @@
import { OneBotAction } from './OneBotAction';
export const AutoRegisterRouter: Array<new (...args: any[]) => OneBotAction<unknown, unknown>> = [];
export function ActionHandler(target: new (...args: any[]) => OneBotAction<unknown, unknown>) {
AutoRegisterRouter.push(target);
export function ActionHandler (target: new (...args: any[]) => OneBotAction<unknown, unknown>) {
AutoRegisterRouter.push(target);
}

View File

@@ -2,7 +2,7 @@ import { GetFileBase, GetFilePayload, GetFileResponse } from './GetFile';
import { ActionName } from '@/napcat-onebot/action/router';
import { promises as fs } from 'fs';
import { decode } from 'silk-wasm';
import { FFmpegService } from 'napcat-common/src/ffmpeg';
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
const out_format = ['mp3', 'amr', 'wma', 'm4a', 'spx', 'ogg', 'wav', 'flac'];

View File

@@ -41,7 +41,7 @@ export default class GoCQHTTPUploadGroupFile extends OneBotAction<Payload, Uploa
peer,
deleteAfterSentFiles: [],
};
const sendFileEle = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id);
const sendFileEle = await this.obContext.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name, payload.folder ?? payload.folder_id);
msgContext.deleteAfterSentFiles.push(downloadResult.path);
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [sendFileEle], msgContext.deleteAfterSentFiles);

View File

@@ -51,7 +51,7 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction<Payload, Upl
}, ContextMode.Private),
deleteAfterSentFiles: [],
};
const sendFileEle: SendFileElement = await this.core.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
const sendFileEle: SendFileElement = await this.obContext.apis.FileApi.createValidSendFileElement(msgContext, downloadResult.path, payload.name);
msgContext.deleteAfterSentFiles.push(downloadResult.path);
const returnMsg = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(await this.getPeer(payload), [sendFileEle], msgContext.deleteAfterSentFiles);

View File

@@ -138,7 +138,7 @@ import { TestDownloadStream } from './stream/TestStreamDownload';
import { UploadFileStream } from './stream/UploadFileStream';
import { AutoRegisterRouter } from './auto-register';
export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCore) {
export function createActionMap (obContext: NapCatOneBot11Adapter, core: NapCatCore) {
const actionHandlers = [
new CleanStreamTempFile(obContext, core),
new DownloadFileStream(obContext, core),
@@ -315,7 +315,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
// function get<K extends keyof MapType>(key: K): MapType[K];
// function get<K extends keyof MapType>(key: K): null;
// function get<K extends keyof MapType>(key: K): HandlerUnion | null | MapType[K]
function get<K extends keyof MapType>(key: K): MapType[K] | undefined {
function get<K extends keyof MapType> (key: K): MapType[K] | undefined {
return _map.get(key as keyof MapType) as MapType[K] | undefined;
}
return { get };

View File

@@ -11,7 +11,7 @@ import { decodeCQCode } from '@/napcat-onebot/helper/cqcode';
import { MessageUnique } from 'napcat-common/src/message-unique';
import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendArkElement, SendMessageElement } from 'napcat-core';
import { OneBotAction } from '@/napcat-onebot/action/OneBotAction';
import { ForwardMsgBuilder } from 'napcat-common/src/forward-msg-builder';
import { ForwardMsgBuilder } from '@/napcat-core/helper/forward-msg-builder';
import { stringifyWithBigInt } from 'napcat-common/src/helper';
import { PacketMsg } from 'napcat-core/packet/message/message';
import { rawMsgWithSendMsg } from 'napcat-core/packet/message/converter';

View File

@@ -5,7 +5,7 @@ import { NetworkAdapterConfig } from '@/napcat-onebot/config/config';
import { StreamPacket, StreamStatus } from './StreamBasic';
import fs from 'fs';
import { decode } from 'silk-wasm';
import { FFmpegService } from 'napcat-common/src/ffmpeg';
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
import { BaseDownloadStream, DownloadResult } from './BaseDownloadStream';
const out_format = ['mp3', 'amr', 'wma', 'm4a', 'spx', 'ogg', 'wav', 'flac'];

View File

@@ -0,0 +1,180 @@
import { NapCatOneBot11Adapter } from '@/napcat-onebot/index';
import { encodeSilk } from '@/napcat-core/helper/audio';
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
import { calculateFileMD5 } from 'napcat-common/src/file';
import { ElementType, NapCatCore, PicElement, PicSubType, SendFileElement, SendPicElement, SendPttElement, SendVideoElement } from 'napcat-core';
import { getFileTypeForSendType } from 'napcat-core/helper/msg';
import { imageSizeFallBack } from 'napcat-image-size';
import { SendMessageContext } from './msg';
import { fileTypeFromFile } from 'file-type';
import pathLib from 'node:path';
import fsPromises from 'fs/promises';
import fs from 'fs';
import { defaultVideoThumbB64 } from '@/napcat-core/helper/ffmpeg/video';
export class OneBotFileApi {
obContext: NapCatOneBot11Adapter;
core: NapCatCore;
constructor (obContext: NapCatOneBot11Adapter, core: NapCatCore) {
this.obContext = obContext;
this.core = core;
}
async createValidSendFileElement (context: SendMessageContext, filePath: string, fileName: string = '', folderId: string = ''): Promise<SendFileElement> {
const {
fileName: _fileName,
path,
fileSize,
} = await this.core.apis.FileApi.uploadFile(filePath, ElementType.FILE);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
context.deleteAfterSentFiles.push(path);
return {
elementType: ElementType.FILE,
elementId: '',
fileElement: {
fileName: fileName || _fileName,
folderId,
filePath: path,
fileSize: fileSize.toString(),
},
};
}
async createValidSendPicElement (context: SendMessageContext, picPath: string, summary: string = '', subType: PicSubType = 0): Promise<SendPicElement> {
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(picPath, ElementType.PIC, subType);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
const imageSize = await imageSizeFallBack(picPath);
context.deleteAfterSentFiles.push(path);
return {
elementType: ElementType.PIC,
elementId: '',
picElement: {
md5HexStr: md5,
fileSize: fileSize.toString(),
picWidth: imageSize.width,
picHeight: imageSize.height,
fileName,
sourcePath: path,
original: true,
picType: await getFileTypeForSendType(picPath),
picSubType: subType,
fileUuid: '',
fileSubId: '',
thumbFileSize: 0,
summary,
} as PicElement,
};
}
async createValidSendVideoElement (context: SendMessageContext, filePath: string, fileName: string = '', _diyThumbPath: string = ''): Promise<SendVideoElement> {
let videoInfo = {
width: 1920,
height: 1080,
time: 15,
format: 'mp4',
size: 0,
filePath,
};
let fileExt = 'mp4';
try {
const tempExt = (await fileTypeFromFile(filePath))?.ext;
if (tempExt) fileExt = tempExt;
} catch (e) {
this.core.context.logger.logError('获取文件类型失败', e);
}
const newFilePath = `${filePath}.${fileExt}`;
fs.copyFileSync(filePath, newFilePath);
context.deleteAfterSentFiles.push(newFilePath);
filePath = newFilePath;
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
context.deleteAfterSentFiles.push(path);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
const thumbDir = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`);
fs.mkdirSync(pathLib.dirname(thumbDir), { recursive: true });
const thumbPath = pathLib.join(pathLib.dirname(thumbDir), `${md5}_0.png`);
try {
videoInfo = await FFmpegService.getVideoInfo(filePath, thumbPath);
if (!fs.existsSync(thumbPath)) {
this.core.context.logger.logError('获取视频缩略图失败', new Error('缩略图不存在'));
throw new Error('获取视频缩略图失败');
}
} catch (e) {
this.core.context.logger.logError('获取视频信息失败', e);
fs.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
}
if (_diyThumbPath) {
try {
await this.core.apis.FileApi.copyFile(_diyThumbPath, thumbPath);
} catch (e) {
this.core.context.logger.logError('复制自定义缩略图失败', e);
}
}
context.deleteAfterSentFiles.push(thumbPath);
const thumbSize = (await fsPromises.stat(thumbPath)).size;
const thumbMd5 = await calculateFileMD5(thumbPath);
context.deleteAfterSentFiles.push(thumbPath);
const uploadName = (fileName || _fileName).toLocaleLowerCase().endsWith(`.${fileExt.toLocaleLowerCase()}`) ? (fileName || _fileName) : `${fileName || _fileName}.${fileExt}`;
return {
elementType: ElementType.VIDEO,
elementId: '',
videoElement: {
fileName: uploadName,
filePath: path,
videoMd5: md5,
thumbMd5,
fileTime: videoInfo.time,
thumbPath: new Map([[0, thumbPath]]),
thumbSize,
thumbWidth: videoInfo.width,
thumbHeight: videoInfo.height,
fileSize: fileSize.toString(),
},
};
}
async createValidSendPttElement (_context: SendMessageContext, pttPath: string): Promise<SendPttElement> {
const { converted, path: silkPath, duration } = await encodeSilk(pttPath, this.core.NapCatTempPath, this.core.context.logger);
if (!silkPath) {
throw new Error('语音转换失败, 请检查语音文件是否正常');
}
const { md5, fileName, path, fileSize } = await this.core.apis.FileApi.uploadFile(silkPath, ElementType.PTT);
if (fileSize === 0) {
throw new Error('文件异常大小为0');
}
if (converted) {
fsPromises.unlink(silkPath).then().catch((e) => this.core.context.logger.logError('删除临时文件失败', e));
}
return {
elementType: ElementType.PTT,
elementId: '',
pttElement: {
fileName,
filePath: path,
md5HexStr: md5,
fileSize: fileSize.toString(),
duration: duration ?? 1,
formatType: 1,
voiceType: 1,
voiceChangeType: 0,
canConvert2Text: true,
waveAmplitudes: [
0, 18, 9, 23, 16, 17, 16, 15, 44, 17, 24, 20, 14, 15, 17,
],
fileSubId: '',
playState: 1,
autoConvertText: 0,
storeID: 0,
otherBusinessInfo: {
aiVoiceType: 0,
},
},
};
}
}

View File

@@ -11,7 +11,6 @@ import {
TipGroupElement,
TipGroupElementType,
} from 'napcat-core';
import { NapCatOneBot11Adapter } from '@/napcat-onebot/index';
import { OB11GroupBanEvent } from '@/napcat-onebot/event/notice/OB11GroupBanEvent';
import fastXmlParser from 'fast-xml-parser';
import { OB11GroupMsgEmojiLikeEvent } from '@/napcat-onebot/event/notice/OB11MsgEmojiLikeEvent';
@@ -26,6 +25,7 @@ import { FileNapCatOneBotUUID } from 'napcat-common/src/file-uuid';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { NapProtoMsg } from 'napcat-protobuf';
import { GroupReactNotify, PushMsg } from 'napcat-core/packet/transformer/proto';
import { NapCatOneBot11Adapter } from '..';
export class OneBotGroupApi {
obContext: NapCatOneBot11Adapter;
@@ -172,6 +172,23 @@ export class OneBotGroupApi {
});
}
async registerParseGroupReactEventByCore () {
this.core.event.on('event:emoji_like', async (data) => {
console.log('Received emoji_like event from core:', data);
const event = await this.createGroupEmojiLikeEvent(
data.groupId,
data.senderUin,
data.msgSeq,
data.emojiId,
data.isAdd,
data.count
);
if (event) {
this.obContext.networkManager.emitEvent(event);
}
});
}
async parsePaiYiPai (msg: RawMessage, jsonStr: string) {
const json = JSON.parse(jsonStr);
// 判断业务类型

View File

@@ -36,7 +36,7 @@ import { uriToLocalFile } from 'napcat-common/src/file';
import { RequestUtil } from 'napcat-common/src/request';
import fsPromise from 'node:fs/promises';
import { OB11FriendAddNoticeEvent } from '@/napcat-onebot/event/notice/OB11FriendAddNoticeEvent';
import { ForwardMsgBuilder } from 'napcat-common/src/forward-msg-builder';
import { ForwardMsgBuilder } from '@/napcat-core/helper/forward-msg-builder';
import { NapProtoMsg } from 'napcat-protobuf';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { GroupDecreaseSubType, OB11GroupDecreaseEvent } from '../event/notice/OB11GroupDecreaseEvent';
@@ -666,7 +666,7 @@ export class OneBotMsgApi {
// File service
[OB11MessageDataType.image]: async (sendMsg, context) => {
return await this.core.apis.FileApi.createValidSendPicElement(
return await this.obContext.apis.FileApi.createValidSendPicElement(
context,
(await this.handleOb11FileLikeMessage(sendMsg, context)).path,
sendMsg.data.summary,
@@ -676,7 +676,7 @@ export class OneBotMsgApi {
[OB11MessageDataType.file]: async (sendMsg, context) => {
const { path, fileName } = await this.handleOb11FileLikeMessage(sendMsg, context);
return await this.core.apis.FileApi.createValidSendFileElement(context, path, fileName);
return await this.obContext.apis.FileApi.createValidSendFileElement(context, path, fileName);
},
[OB11MessageDataType.video]: async (sendMsg, context) => {
@@ -691,11 +691,11 @@ export class OneBotMsgApi {
}
}
return await this.core.apis.FileApi.createValidSendVideoElement(context, path, fileName, thumb);
return await this.obContext.apis.FileApi.createValidSendVideoElement(context, path, fileName, thumb);
},
[OB11MessageDataType.voice]: async (sendMsg, context) =>
this.core.apis.FileApi.createValidSendPttElement(context,
this.obContext.apis.FileApi.createValidSendPttElement(context,
(await this.handleOb11FileLikeMessage(sendMsg, context)).path),
[OB11MessageDataType.json]: async ({ data: { data } }) => ({

View File

@@ -1,4 +1,4 @@
import { ConfigBase } from 'napcat-common/src/config-base';
import { ConfigBase } from 'napcat-core/helper/config-base';
import type { NapCatCore } from 'napcat-core';
import { OneBotConfig } from './config';
import { AnySchema } from 'ajv';

View File

@@ -17,7 +17,7 @@ import {
NTMsgAtType,
} from 'napcat-core';
import { OB11ConfigLoader } from '@/napcat-onebot/config';
import { pendingTokenToSend } from 'napcat-webui-backend/src/index';
import { pendingTokenToSend } from 'napcat-webui-backend/index';
import {
OB11HttpClientAdapter,
OB11WebSocketClientAdapter,
@@ -38,7 +38,6 @@ import { ActionMap, createActionMap } from '@/napcat-onebot/action';
import { WebUiDataRuntime } from 'napcat-webui-backend/src/helper/Data';
import { OB11InputStatusEvent } from '@/napcat-onebot/event/notice/OB11InputStatusEvent';
import { MessageUnique } from 'napcat-common/src/message-unique';
import { proxiedListenerOf } from 'napcat-common/src/proxy-handler';
import { OB11FriendRequestEvent } from '@/napcat-onebot/event/request/OB11FriendRequest';
import { OB11GroupRequestEvent } from '@/napcat-onebot/event/request/OB11GroupRequest';
import { OB11FriendRecallNoticeEvent } from '@/napcat-onebot/event/notice/OB11FriendRecallNoticeEvent';
@@ -54,14 +53,24 @@ import { IOB11NetworkAdapter } from '@/napcat-onebot/network/adapter';
import { OB11HttpSSEServerAdapter } from './network/http-server-sse';
import { OB11PluginMangerAdapter } from './network/plugin-manger';
import { existsSync } from 'node:fs';
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
import { OneBotFileApi } from './api/file';
interface ApiListType {
GroupApi: OneBotGroupApi;
UserApi: OneBotUserApi;
FriendApi: OneBotFriendApi;
MsgApi: OneBotMsgApi;
QuickActionApi: OneBotQuickActionApi;
FileApi: OneBotFileApi;
}
// OneBot实现类
export class NapCatOneBot11Adapter {
readonly core: NapCatCore;
readonly context: InstanceContext;
configLoader: OB11ConfigLoader;
public readonly apis;
public apis: ApiListType;
networkManager: OB11NetworkManager;
actions: ActionMap;
private readonly bootTime = Date.now() / 1000;
@@ -76,6 +85,7 @@ export class NapCatOneBot11Adapter {
FriendApi: new OneBotFriendApi(this, core),
MsgApi: new OneBotMsgApi(this, core),
QuickActionApi: new OneBotQuickActionApi(this, core),
FileApi: new OneBotFileApi(this, core),
} as const;
this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager();
@@ -218,7 +228,7 @@ export class NapCatOneBot11Adapter {
// this.context.logger.log(`OneBot11 配置更改:${JSON.stringify(prev)} -> ${JSON.stringify(newConfig)}`);
await this.reloadNetwork(prev, newConfig);
});
this.apis.GroupApi.registerParseGroupReactEvent().catch(e =>
this.apis.GroupApi.registerParseGroupReactEventByCore().catch(e =>
this.context.logger.logError('注册群消息反应表情失败', e)
);
}
@@ -236,7 +246,7 @@ export class NapCatOneBot11Adapter {
await this.handleConfigChange(prev.network.websocketClients, now.network.websocketClients, OB11WebSocketClientAdapter);
}
private async handleConfigChange<CT extends NetworkAdapterConfig> (
private async handleConfigChange<CT extends NetworkAdapterConfig>(
prevConfig: NetworkAdapterConfig[],
nowConfig: NetworkAdapterConfig[],
adapterClass: new (

View File

@@ -1,5 +1,5 @@
import { NetworkAdapterConfig } from '@/napcat-onebot/config/config';
import { LogWrapper } from 'napcat-common/src/log';
import { LogWrapper } from 'napcat-core/helper/log';
import { NapCatCore } from 'napcat-core';
import { NapCatOneBot11Adapter } from '@/napcat-onebot/index';
import { ActionMap } from '@/napcat-onebot/action';

View File

@@ -4,6 +4,9 @@
"private": true,
"type": "module",
"main": "index.ts",
"scripts": {
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"exports": {
".": {
"import": "./index.ts"

View File

@@ -1,48 +1,5 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": ".",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/napcat-onebot/*": [
"*"
]
},
"skipLibCheck": true,
"skipDefaultLibCheck": true
},
"extends": "../../tsconfig.base.json",
"include": [
"*.ts",
"**/*.ts"

View File

@@ -1,43 +1,5 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": ".",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"skipLibCheck": true,
"skipDefaultLibCheck": true
},
"extends": "../../tsconfig.base.json",
"include": [
"*.ts",
"**/*.ts"

View File

@@ -7,18 +7,18 @@ type LowerCamelCase<S extends string> = CamelCaseHelper<S, false, true>;
type CamelCaseHelper<
S extends string,
CapNext extends boolean,
IsFirstChar extends boolean,
IsFirstChar extends boolean
> = S extends `${infer F}${infer R}`
? F extends '_'
? CamelCaseHelper<R, true, false>
: F extends `${number}`
? `${F}${CamelCaseHelper<R, true, false>}`
: CapNext extends true
? `${Uppercase<F>}${CamelCaseHelper<R, false, false>}`
: IsFirstChar extends true
? `${Lowercase<F>}${CamelCaseHelper<R, false, false>}`
: `${F}${CamelCaseHelper<R, false, false>}`
: '';
? F extends '_'
? CamelCaseHelper<R, true, false>
: F extends `${number}`
? `${F}${CamelCaseHelper<R, true, false>}`
: CapNext extends true
? `${Uppercase<F>}${CamelCaseHelper<R, false, false>}`
: IsFirstChar extends true
? `${Lowercase<F>}${CamelCaseHelper<R, false, false>}`
: `${F}${CamelCaseHelper<R, false, false>}`
: '';
type ScalarTypeToTsType<T extends ScalarType> = T extends
| ScalarType.DOUBLE
@@ -28,36 +28,36 @@ type ScalarTypeToTsType<T extends ScalarType> = T extends
| ScalarType.UINT32
| ScalarType.SFIXED32
| ScalarType.SINT32
? number
: T extends ScalarType.INT64 | ScalarType.UINT64 | ScalarType.FIXED64 | ScalarType.SFIXED64 | ScalarType.SINT64
? bigint
: T extends ScalarType.BOOL
? boolean
: T extends ScalarType.STRING
? string
: T extends ScalarType.BYTES
? Uint8Array
: never;
? number
: T extends ScalarType.INT64 | ScalarType.UINT64 | ScalarType.FIXED64 | ScalarType.SFIXED64 | ScalarType.SINT64
? bigint
: T extends ScalarType.BOOL
? boolean
: T extends ScalarType.STRING
? string
: T extends ScalarType.BYTES
? Uint8Array
: never;
interface BaseProtoFieldType<T, O extends boolean, R extends O extends true ? false : boolean> {
kind: 'scalar' | 'message';
no: number;
type: T;
optional: O;
repeat: R;
kind: 'scalar' | 'message';
no: number;
type: T;
optional: O;
repeat: R;
}
export interface ScalarProtoFieldType<T extends ScalarType, O extends boolean, R extends O extends true ? false : boolean>
extends BaseProtoFieldType<T, O, R> {
kind: 'scalar';
extends BaseProtoFieldType<T, O, R> {
kind: 'scalar';
}
export interface MessageProtoFieldType<
T extends () => ProtoMessageType,
O extends boolean,
R extends O extends true ? false : boolean,
R extends O extends true ? false : boolean
> extends BaseProtoFieldType<T, O, R> {
kind: 'message';
kind: 'message';
}
type ProtoFieldType =
@@ -65,62 +65,62 @@ type ProtoFieldType =
| MessageProtoFieldType<() => ProtoMessageType, boolean, boolean>;
type ProtoMessageType = {
[key: string]: ProtoFieldType;
[key: string]: ProtoFieldType;
};
export function ProtoField<
T extends ScalarType,
O extends boolean = false,
R extends O extends true ? false : boolean = false,
>(no: number, type: T, optional?: O, repeat?: R): ScalarProtoFieldType<T, O, R>;
R extends O extends true ? false : boolean = false
> (no: number, type: T, optional?: O, repeat?: R): ScalarProtoFieldType<T, O, R>;
export function ProtoField<
T extends () => ProtoMessageType,
O extends boolean = false,
R extends O extends true ? false : boolean = false,
>(no: number, type: T, optional?: O, repeat?: R): MessageProtoFieldType<T, O, R>;
export function ProtoField(
no: number,
type: ScalarType | (() => ProtoMessageType),
optional?: boolean,
repeat?: boolean
R extends O extends true ? false : boolean = false
> (no: number, type: T, optional?: O, repeat?: R): MessageProtoFieldType<T, O, R>;
export function ProtoField (
no: number,
type: ScalarType | (() => ProtoMessageType),
optional?: boolean,
repeat?: boolean
): ProtoFieldType {
if (typeof type === 'function') {
return { kind: 'message', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false };
} else {
return { kind: 'scalar', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false };
}
if (typeof type === 'function') {
return { kind: 'message', no, type, optional: optional ?? false, repeat: repeat ?? false };
} else {
return { kind: 'scalar', no, type, optional: optional ?? false, repeat: repeat ?? false };
}
}
type ProtoFieldReturnType<T, E extends boolean> =
NonNullable<T> extends ScalarProtoFieldType<infer S, infer O, infer R>
? ScalarTypeToTsType<S>
: T extends NonNullable<MessageProtoFieldType<infer S, infer O, infer R>>
? NonNullable<NapProtoStructType<ReturnType<S>, E>>
: never;
NonNullable<T> extends ScalarProtoFieldType<infer S, infer _O, infer _R>
? ScalarTypeToTsType<S>
: T extends NonNullable<MessageProtoFieldType<infer S, infer _O, infer _R>>
? NonNullable<NapProtoStructType<ReturnType<S>, E>>
: never;
type RequiredFieldsBaseType<T, E extends boolean> = {
[K in keyof T as T[K] extends { optional: true } ? never : LowerCamelCase<K & string>]: T[K] extends {
repeat: true;
}
? ProtoFieldReturnType<T[K], E>[]
: ProtoFieldReturnType<T[K], E>;
[K in keyof T as T[K] extends { optional: true } ? never : LowerCamelCase<K & string>]: T[K] extends {
repeat: true;
}
? ProtoFieldReturnType<T[K], E>[]
: ProtoFieldReturnType<T[K], E>;
};
type OptionalFieldsBaseType<T, E extends boolean> = {
[K in keyof T as T[K] extends { optional: true } ? LowerCamelCase<K & string> : never]?: T[K] extends {
repeat: true;
}
? ProtoFieldReturnType<T[K], E>[]
: ProtoFieldReturnType<T[K], E>;
[K in keyof T as T[K] extends { optional: true } ? LowerCamelCase<K & string> : never]?: T[K] extends {
repeat: true;
}
? ProtoFieldReturnType<T[K], E>[]
: ProtoFieldReturnType<T[K], E>;
};
type RequiredFieldsType<T, E extends boolean> = E extends true
? Partial<RequiredFieldsBaseType<T, E>>
: RequiredFieldsBaseType<T, E>;
? Partial<RequiredFieldsBaseType<T, E>>
: RequiredFieldsBaseType<T, E>;
type OptionalFieldsType<T, E extends boolean> = E extends true
? Partial<OptionalFieldsBaseType<T, E>>
: OptionalFieldsBaseType<T, E>;
? Partial<OptionalFieldsBaseType<T, E>>
: OptionalFieldsBaseType<T, E>;
type NapProtoStructType<T, E extends boolean> = RequiredFieldsType<T, E> & OptionalFieldsType<T, E>;
@@ -129,72 +129,74 @@ export type NapProtoEncodeStructType<T> = NapProtoStructType<T, true>;
export type NapProtoDecodeStructType<T> = NapProtoStructType<T, false>;
class NapProtoRealMsg<T extends ProtoMessageType> {
private readonly _field: PartialFieldInfo[];
private readonly _proto_msg: MessageType<NapProtoStructType<T, boolean>>;
private static cache = new WeakMap<ProtoMessageType, NapProtoRealMsg<any>>();
private readonly _field: PartialFieldInfo[];
private readonly _proto_msg: MessageType<NapProtoStructType<T, boolean>>;
private static cache = new WeakMap<ProtoMessageType, NapProtoRealMsg<any>>();
private constructor(fields: T) {
this._field = Object.keys(fields).map((key) => {
const field = fields[key];
if (field.kind === 'scalar') {
const repeatType = field.repeat
? [ScalarType.STRING, ScalarType.BYTES].includes(field.type)
? RepeatType.UNPACKED
: RepeatType.PACKED
: RepeatType.NO;
return {
no: field.no,
name: key,
kind: 'scalar',
T: field.type,
opt: field.optional,
repeat: repeatType,
};
} else if (field.kind === 'message') {
return {
no: field.no,
name: key,
kind: 'message',
repeat: field.repeat ? RepeatType.PACKED : RepeatType.NO,
T: () => NapProtoRealMsg.getInstance(field.type())._proto_msg,
};
}
}) as PartialFieldInfo[];
this._proto_msg = new MessageType<NapProtoStructType<T, boolean>>('nya', this._field);
}
private constructor (fields: T) {
this._field = Object.keys(fields).map((key) => {
const field = fields[key];
if (field.kind === 'scalar') {
const repeatType = field.repeat
? [ScalarType.STRING, ScalarType.BYTES].includes(field.type)
? RepeatType.UNPACKED
: RepeatType.PACKED
: RepeatType.NO;
return {
no: field.no,
name: key,
kind: 'scalar',
T: field.type,
opt: field.optional,
repeat: repeatType,
};
} else if (field.kind === 'message') {
return {
no: field.no,
name: key,
kind: 'message',
repeat: field.repeat ? RepeatType.PACKED : RepeatType.NO,
T: () => NapProtoRealMsg.getInstance(field.type())._proto_msg,
};
} else {
throw new Error(`Unknown field kind: ${field.kind}`);
}
}) as PartialFieldInfo[];
this._proto_msg = new MessageType<NapProtoStructType<T, boolean>>('nya', this._field);
}
static getInstance<T extends ProtoMessageType>(fields: T): NapProtoRealMsg<T> {
let instance = this.cache.get(fields);
if (!instance) {
instance = new NapProtoRealMsg(fields);
this.cache.set(fields, instance);
}
return instance;
static getInstance<T extends ProtoMessageType>(fields: T): NapProtoRealMsg<T> {
let instance = this.cache.get(fields);
if (!instance) {
instance = new NapProtoRealMsg(fields);
this.cache.set(fields, instance);
}
return instance;
}
encode(data: NapProtoEncodeStructType<T>): Uint8Array {
return this._proto_msg.toBinary(this._proto_msg.create(data as PartialMessage<NapProtoEncodeStructType<T>>));
}
encode (data: NapProtoEncodeStructType<T>): Uint8Array {
return this._proto_msg.toBinary(this._proto_msg.create(data as PartialMessage<NapProtoEncodeStructType<T>>));
}
decode(data: Uint8Array): NapProtoDecodeStructType<T> {
return this._proto_msg.fromBinary(data) as NapProtoDecodeStructType<T>;
}
decode (data: Uint8Array): NapProtoDecodeStructType<T> {
return this._proto_msg.fromBinary(data) as NapProtoDecodeStructType<T>;
}
}
export class NapProtoMsg<T extends ProtoMessageType> {
private realMsg: NapProtoRealMsg<T>;
private realMsg: NapProtoRealMsg<T>;
constructor(fields: T) {
this.realMsg = NapProtoRealMsg.getInstance(fields);
}
constructor (fields: T) {
this.realMsg = NapProtoRealMsg.getInstance(fields);
}
encode(data: NapProtoEncodeStructType<T>): Uint8Array {
return this.realMsg.encode(data);
}
encode (data: NapProtoEncodeStructType<T>): Uint8Array {
return this.realMsg.encode(data);
}
decode(data: Uint8Array): NapProtoDecodeStructType<T> {
return this.realMsg.decode(data);
}
decode (data: Uint8Array): NapProtoDecodeStructType<T> {
return this.realMsg.decode(data);
}
}
export { ScalarType } from '@protobuf-ts/runtime';

View File

@@ -14,12 +14,7 @@
}
},
"dependencies": {
"@homebridge/node-pty-prebuilt-multiarch":"^0.12.0",
"napcat-core": "workspace:*",
"napcat-common": "workspace:*",
"napcat-onebot": "workspace:*",
"napcat-webui-backend": "workspace:*",
"napcat-qrcode": "workspace:*"
"@homebridge/node-pty-prebuilt-multiarch":"^0.12.0"
},
"devDependencies": {

View File

@@ -1,4 +1,4 @@
import { require_dlopen } from '.';
import { require_dlopen } from './index';
export function pty_loader () {
let pty: any;
try {

View File

@@ -11,7 +11,7 @@ import { Socket } from 'net';
import { ArgvOrCommandLine } from '@homebridge/node-pty-prebuilt-multiarch/src/types';
import { fork } from 'child_process';
import { ConoutConnection } from './windowsConoutConnection';
import { require_dlopen } from '.';
import { require_dlopen } from './index';
import { fileURLToPath } from 'url';
import { dirname } from 'path';

View File

@@ -1,28 +1,21 @@
{
"name": "napcat-qrcode",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "index.ts",
"exports": {
".": {
"import": "./index.ts"
},
"./*": {
"import": "./*"
}
"name": "napcat-qrcode",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "index.ts",
"exports": {
".": {
"import": "./index.ts"
},
"dependencies": {
"napcat-core": "workspace:*",
"napcat-common": "workspace:*",
"napcat-onebot": "workspace:*",
"napcat-webui-backend": "workspace:*"
},
"devDependencies": {
"@types/node": "^22.0.1"
},
"engines": {
"node": ">=18.0.0"
"./*": {
"import": "./*"
}
},
"devDependencies": {
"@types/node": "^22.0.1"
},
"engines": {
"node": ">=18.0.0"
}
}

View File

@@ -1,43 +1,5 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": ".",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"skipLibCheck": true,
"skipDefaultLibCheck": true
},
"extends": "../../tsconfig.base.json",
"include": [
"*.ts",
"**/*.ts"

View File

@@ -1,5 +1,5 @@
const path = require('path');
const CurrentPath = path.dirname(__filename);
(async () => {
await import("file://" + path.join(CurrentPath, './napcat/napcat.mjs'));
})();
await import('file://' + path.join(CurrentPath, './napcat/napcat.mjs'));
})();

View File

@@ -1,6 +1,5 @@
import type { SelfInfo } from 'napcat-core/types';
import type { SelfInfo } from 'napcat-core/index';
import { LogWrapper } from 'napcat-common/src/log';
import { NodeIKernelLoginListener, NodeIKernelSessionListener } from 'napcat-core/listeners';
import { NodeIDependsAdapter, NodeIDispatcherAdapter, NodeIGlobalAdapter } from 'napcat-core/adapters';
import { NapCatPathWrapper } from 'napcat-common/src/path';
@@ -17,23 +16,25 @@ import {
WrapperNodeApi,
WrapperSessionInitConfig,
} from 'napcat-core';
import { QQBasicInfoWrapper } from 'napcat-common/src/qq-basic-info';
import { hostname, systemVersion } from 'napcat-common/src/system';
import { proxiedListenerOf } from 'napcat-common/src/proxy-handler';
import path from 'path';
import fs from 'fs';
import os from 'os';
import { LoginListItem, NodeIKernelLoginService } from 'napcat-core/services';
import qrcode from 'napcat-qrcode/lib/main';
import { NapCatOneBot11Adapter } from 'napcat-onebot/index';
import { InitWebUi } from 'napcat-webui-backend/src/index';
import { InitWebUi } from 'napcat-webui-backend/index';
import { WebUiDataRuntime } from 'napcat-webui-backend/src/helper/Data';
import { napCatVersion } from 'napcat-common/src/version';
import { NodeIO3MiscListener } from 'napcat-core/listeners/NodeIO3MiscListener';
import { sleep } from 'napcat-common/src/helper';
import { FFmpegService } from 'napcat-common/src/ffmpeg';
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
import { connectToNamedPipe } from './pipe';
import { NativePacketHandler } from 'napcat-core/packet/handler/client';
import { logSubscription, LogWrapper } from '@/napcat-core/helper/log';
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
import { statusHelperSubscription } from '@/napcat-core/helper/status';
// NapCat Shell App ES 入口文件
async function handleUncaughtExceptions (logger: LogWrapper) {
process.on('uncaughtException', (err) => {
@@ -337,7 +338,7 @@ export async function NCoreInitShell () {
o3Service.addO3MiscListener(new NodeIO3MiscListener());
logger.log('[NapCat] [Core] NapCat.Core Version: ' + napCatVersion);
InitWebUi(logger, pathWrapper).then().catch(e => logger.logError(e));
InitWebUi(logger, pathWrapper, logSubscription, statusHelperSubscription).then().catch(e => logger.logError(e));
const engine = wrapper.NodeIQQNTWrapperEngine.get();
const loginService = wrapper.NodeIKernelLoginService.get();
@@ -364,7 +365,7 @@ export async function NCoreInitShell () {
await initializeLoginService(loginService, basicInfoWrapper, dataPathGlobal, systemVersion, hostname);
handleProxy(session, logger);
let quickLoginUin: string | undefined = undefined;
let quickLoginUin: string | undefined;
try {
const args = process.argv;
const qIndex = args.findIndex(arg => arg === '-q' || arg === '--qq');

View File

@@ -6,6 +6,7 @@
"main": "index.ts",
"scripts": {
"build": "vite build",
"build:dev": "vite build --mode development",
"typecheck": "tsc --noEmit --skipLibCheck -p tsconfig.json"
},
"exports": {

View File

@@ -1,4 +1,4 @@
import { LogWrapper } from 'napcat-common/src/log';
import { LogWrapper } from 'napcat-core/helper/log';
import * as net from 'net';
import * as process from 'process';
import { Writable } from 'stream';

View File

@@ -1,49 +1,5 @@
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "Node",
"lib": [
"ES2021"
],
"typeRoots": [
"./node_modules/@types"
],
"esModuleInterop": true,
"outDir": "dist",
"rootDir": ".",
"noEmit": false,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"alwaysStrict": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": false,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"@/napcat-onebot/*": ["../napcat-onebot/*"],
"@/napcat-core/*": ["../napcat-core/*"],
"@/napcat-common/*": ["../napcat-common/src/*"],
"@/napcat-webui-backend/*": ["../napcat-webui-backend/src/*"]
},
"skipLibCheck": true,
"skipDefaultLibCheck": true
},
"extends": "../../tsconfig.base.json",
"include": [
"*.ts",
"**/*.ts"

Some files were not shown because too many files have changed in this diff Show More