Compare commits

..

14 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
818224578b Successfully built cache cleaning feature
Co-authored-by: sj817 <74231782+sj817@users.noreply.github.com>
2025-10-07 15:22:39 +00:00
copilot-swe-agent[bot]
94bc4f8301 Add cache cleaning feature to backend and frontend
Co-authored-by: sj817 <74231782+sj817@users.noreply.github.com>
2025-10-07 15:15:03 +00:00
copilot-swe-agent[bot]
94676998cc Initial plan 2025-10-07 15:06:59 +00:00
Mlikiowa
2afdb2a0da release: v4.8.119 2025-10-03 04:36:00 +00:00
手瓜一十雪
5bfbf92c21 Update key for 9.9.22-40362 offsets in offset.json
Changed the key from '9.9.22-40362' to '9.9.22-40362-x64' to clarify architecture specificity in the offsets mapping.
2025-10-03 12:35:28 +08:00
Mlikiowa
a775a0dde9 release: v4.8.118 2025-10-03 04:34:56 +00:00
手瓜一十雪
d7f00c0594 Fix batch variable quoting and case consistency
Updated batch scripts to use proper variable quoting and consistent casing for 'QQPath'. This improves reliability when handling paths with spaces and ensures environment variable names are used consistently.
2025-10-03 12:34:29 +08:00
Mlikiowa
77c8f874b6 release: v4.8.117 2025-10-03 04:21:22 +00:00
手瓜一十雪
fb0a20919b Add support for version 9.9.22-40362
Updated appid.json and offset.json to include entries for version 9.9.22-40362, specifying the new appid, qua, and offset values for send and recv.
2025-10-03 12:20:21 +08:00
手瓜一十雪
0300ba4648 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2025-10-03 12:20:15 +08:00
时瑾
d472eee777 fix: Reset pagination when navigating between directories in file manager
Fix: Reset pagination when navigating between directories in file manager
2025-10-02 09:40:37 +08:00
copilot-swe-agent[bot]
41bd06e50a Fix: Reset pagination to page 1 when navigating directories
Co-authored-by: sj817 <74231782+sj817@users.noreply.github.com>
2025-10-02 01:16:14 +00:00
copilot-swe-agent[bot]
97334dfbf5 Initial plan 2025-10-02 01:10:22 +00:00
手瓜一十雪
e3d8c8e940 fix: #1260 2025-09-29 16:39:37 +08:00
55 changed files with 343 additions and 216 deletions

View File

@@ -7,7 +7,7 @@ set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
:loop_read
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
set RetString=%%b
set "RetString=%%~b"
goto :napcat_boot
)
@@ -16,7 +16,7 @@ for %%a in ("%RetString%") do (
set "pathWithoutUninstall=%%~dpa"
)
SET QQPath=%pathWithoutUninstall%QQ.exe
set "QQPath=%pathWithoutUninstall%QQ.exe"
if not exist "%QQpath%" (
echo provided QQ path is invalid

View File

@@ -7,7 +7,7 @@ set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
:loop_read
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
set RetString=%%b
set "RetString=%%~b"
goto :napcat_boot
)
@@ -16,7 +16,7 @@ for %%a in ("%RetString%") do (
set "pathWithoutUninstall=%%~dpa"
)
SET QQPath=%pathWithoutUninstall%QQ.exe
set "QQPath=%pathWithoutUninstall%QQ.exe"
if not exist "%QQpath%" (
echo provided QQ path is invalid

View File

@@ -16,7 +16,7 @@ set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
:loop_read
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
set RetString=%%b
set "RetString=%%~b"
goto :napcat_boot
)
@@ -25,7 +25,7 @@ for %%a in ("%RetString%") do (
set "pathWithoutUninstall=%%~dpa"
)
SET QQPath=%pathWithoutUninstall%QQ.exe
set "QQPath=%pathWithoutUninstall%QQ.exe"
if not exist "%QQPath%" (
echo provided QQ path is invalid

View File

@@ -16,7 +16,7 @@ set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
:loop_read
for /f "tokens=2*" %%a in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\QQ" /v "UninstallString"') do (
set RetString=%%b
set "RetString=%%~b"
goto :napcat_boot
)
@@ -25,7 +25,7 @@ for %%a in ("%RetString%") do (
set "pathWithoutUninstall=%%~dpa"
)
SET QQPath=%pathWithoutUninstall%QQ.exe
set "QQPath=%pathWithoutUninstall%QQ.exe"
if not exist "%QQPath%" (
echo provided QQ path is invalid

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ",
"slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现",
"version": "4.8.116",
"version": "4.8.119",
"icon": "./logo.png",
"authors": [
{

View File

@@ -93,6 +93,7 @@
"@types/node": "^22.12.0",
"@types/path-browserify": "^1.0.3",
"@types/react": "^19.0.8",
"@types/react-color": "^3.0.13",
"@types/react-dom": "^19.0.3",
"@types/react-window": "^1.8.8",
"@typescript-eslint/eslint-plugin": "^8.22.0",
@@ -8152,6 +8153,19 @@
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-color": {
"version": "3.0.13",
"resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.13.tgz",
"integrity": "sha512-2c/9FZ4ixC5T3JzN0LP5Cke2Mf0MKOP2Eh0NPDPWmuVH3NjPyhEjqNMQpN1Phr5m74egAy+p2lYNAFrX1z9Yrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/reactcss": "*"
},
"peerDependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-dom": {
"version": "19.1.6",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
@@ -8172,6 +8186,16 @@
"@types/react": "*"
}
},
"node_modules/@types/reactcss": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.13.tgz",
"integrity": "sha512-gi3S+aUi6kpkF5vdhUsnkwbiSEIU/BEJyD7kBy2SudWBUuKmJk8AQKE0OVcQQeEy40Azh0lV6uynxlikYIJuwg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "*"
}
},
"node_modules/@types/unist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",

View File

@@ -95,6 +95,7 @@
"@types/node": "^22.12.0",
"@types/path-browserify": "^1.0.3",
"@types/react": "^19.0.8",
"@types/react-color": "^3.0.13",
"@types/react-dom": "^19.0.3",
"@types/react-window": "^1.8.8",
"@typescript-eslint/eslint-plugin": "^8.22.0",

View File

@@ -82,6 +82,7 @@ export default function FileTable({
setPreviewImages([])
setPreviewIndex(0)
setShowImage(false)
setPage(1)
}, [currentPath])
const onPreviewImage = (name: string, images: PreviewImage[]) => {

View File

@@ -171,7 +171,8 @@ const GenericForm = <T extends keyof NetworkConfigType>({
export default GenericForm
export function random_token(length: number) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&*()-_=+[]{}|;:,.<>?'
const chars =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&*()-_=+[]{}|;:,.<>?'
let result = ''
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length))

View File

@@ -1,9 +1,10 @@
import CryptoJS from 'crypto-js'
import { EventSourcePolyfill } from 'event-source-polyfill'
import { LogLevel } from '@/const/enum'
import { serverRequest } from '@/utils/request'
import CryptoJS from "crypto-js";
export interface Log {
level: LogLevel
message: string
@@ -17,7 +18,7 @@ export default class WebUIManager {
}
public static async loginWithToken(token: string) {
const sha256 = CryptoJS.SHA256(token + '.napcat').toString();
const sha256 = CryptoJS.SHA256(token + '.napcat').toString()
const { data } = await serverRequest.post<ServerResponse<AuthResponse>>(
'/auth/login',
{ hash: sha256 }
@@ -196,4 +197,13 @@ export default class WebUIManager {
)
return data.data
}
// 清理缓存
public static async cleanCache() {
const { data } =
await serverRequest.post<
ServerResponse<{ result: boolean; message: string }>
>('/base/CleanCache')
return data.data
}
}

View File

@@ -1,16 +1,22 @@
import { Button } from '@heroui/button'
import { Input } from '@heroui/input'
import { Switch } from '@heroui/switch'
import { useRequest } from 'ahooks'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { Controller, useForm } from 'react-hook-form'
import toast from 'react-hot-toast'
import { MdDeleteSweep } from 'react-icons/md'
import SaveButtons from '@/components/button/save_buttons'
import PageLoading from '@/components/page_loading'
import useDialog from '@/hooks/use-dialog'
import WebUIManager from '@/controllers/webui_manager'
const ServerConfigCard = () => {
const dialog = useDialog()
const [isCleaningCache, setIsCleaningCache] = useState(false)
const {
data: configData,
loading: configLoading,
@@ -69,6 +75,42 @@ const ServerConfigCard = () => {
}
}
const handleCleanCache = () => {
dialog.confirm({
title: '清理缓存',
content: (
<div className="space-y-2">
<p></p>
<ul className="list-disc list-inside text-sm text-default-600">
<li></li>
<li> (Pic)</li>
<li> (Ptt)</li>
<li> (Video)</li>
<li> (File)</li>
<li> (log)</li>
</ul>
<p className="text-warning text-sm"></p>
</div>
),
onConfirm: async () => {
setIsCleaningCache(true)
try {
const result = await WebUIManager.cleanCache()
if (result.result) {
toast.success(result.message || '缓存清理成功')
} else {
toast.error(result.message || '缓存清理失败')
}
} catch (error) {
const msg = (error as Error).message
toast.error(`清理缓存失败: ${msg}`)
} finally {
setIsCleaningCache(false)
}
}
})
}
useEffect(() => {
reset()
}, [configData])
@@ -131,6 +173,30 @@ const ServerConfigCard = () => {
/>
</div>
<div className="flex flex-col gap-2">
<div className="flex-shrink-0 w-full"></div>
<div className="flex flex-col gap-2 p-4 rounded-lg bg-default-50">
<div className="flex items-start justify-between gap-2">
<div className="flex-1">
<div className="font-medium"></div>
<div className="text-sm text-default-500">
</div>
</div>
<Button
color="danger"
variant="flat"
startContent={<MdDeleteSweep size={20} />}
onPress={handleCleanCache}
isLoading={isCleaningCache}
isDisabled={!!configError}
>
</Button>
</div>
</div>
</div>
<div className="flex flex-col gap-2">
<div className="flex-shrink-0 w-full"></div>
<Controller
@@ -182,4 +248,4 @@ const ServerConfigCard = () => {
)
}
export default ServerConfigCard
export default ServerConfigCard

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "napcat",
"version": "4.8.116",
"version": "4.8.119",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "napcat",
"version": "4.8.116",
"version": "4.8.119",
"dependencies": {
"express": "^5.0.0",
"silk-wasm": "^3.6.1",

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.8.116",
"version": "4.8.119",
"scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",

View File

@@ -8,16 +8,16 @@ import { pipeline } from 'stream/promises';
import { fileURLToPath } from 'url';
import { LogWrapper } from './log';
const downloadOri = 'https://github.com/NapNeko/ffmpeg-build/releases/download/v1.0.0/ffmpeg-7.1.1-win64.zip';
const downloadOri = "https://github.com/NapNeko/ffmpeg-build/releases/download/v1.0.0/ffmpeg-7.1.1-win64.zip"
const urls = [
'https://j.1win.ggff.net/' + downloadOri,
'https://git.yylx.win/' + downloadOri,
'https://ghfile.geekertao.top/' + downloadOri,
'https://gh-proxy.net/' + downloadOri,
'https://ghm.078465.xyz/' + downloadOri,
'https://gitproxy.127731.xyz/' + downloadOri,
'https://jiashu.1win.eu.org/' + downloadOri,
'https://github.tbedu.top/' + downloadOri,
"https://j.1win.ggff.net/" + downloadOri,
"https://git.yylx.win/" + downloadOri,
"https://ghfile.geekertao.top/" + downloadOri,
"https://gh-proxy.net/" + downloadOri,
"https://ghm.078465.xyz/" + downloadOri,
"https://gitproxy.127731.xyz/" + downloadOri,
"https://jiashu.1win.eu.org/" + downloadOri,
"https://github.tbedu.top/" + downloadOri,
downloadOri
];
@@ -354,11 +354,11 @@ export async function downloadFFmpegIfNotExists(log: LogWrapper) {
return {
path: path.join(currentPath, 'ffmpeg'),
reset: true
};
}
}
return {
path: path.join(currentPath, 'ffmpeg'),
reset: true
};
}
}

View File

@@ -182,28 +182,28 @@ export async function uriToLocalFile(dir: string, uri: string, filename: string
const filePath = path.join(dir, filename);
switch (UriType) {
case FileUriType.Local: {
const fileExt = path.extname(HandledUri);
const localFileName = path.basename(HandledUri, fileExt) + fileExt;
const tempFilePath = path.join(dir, filename + fileExt);
fs.copyFileSync(HandledUri, tempFilePath);
return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath };
}
case FileUriType.Local: {
const fileExt = path.extname(HandledUri);
const localFileName = path.basename(HandledUri, fileExt) + fileExt;
const tempFilePath = path.join(dir, filename + fileExt);
fs.copyFileSync(HandledUri, tempFilePath);
return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath };
}
case FileUriType.Remote: {
const buffer = await httpDownload({ url: HandledUri, headers: headers ?? {} });
fs.writeFileSync(filePath, buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
case FileUriType.Remote: {
const buffer = await httpDownload({ url: HandledUri, headers: headers ?? {} });
fs.writeFileSync(filePath, buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
case FileUriType.Base64: {
const base64 = HandledUri.replace(/^base64:\/\//, '');
const base64Buffer = Buffer.from(base64, 'base64');
fs.writeFileSync(filePath, base64Buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
case FileUriType.Base64: {
const base64 = HandledUri.replace(/^base64:\/\//, '');
const base64Buffer = Buffer.from(base64, 'base64');
fs.writeFileSync(filePath, base64Buffer);
return { success: true, errMsg: '', fileName: filename, path: filePath };
}
default:
return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' };
default:
return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' };
}
}

View File

@@ -108,13 +108,13 @@ export class PerformanceMonitor {
const totalTime = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.totalTime, 0);
let logContent = '';
logContent += '=== 性能监控详细报告 ===\n';
logContent += `=== 性能监控详细报告 ===\n`;
logContent += `生成时间: ${now.toLocaleString()}\n`;
logContent += '统计周期: 60秒\n';
logContent += `统计周期: 60秒\n`;
logContent += `总览: ${totalFunctions} 个函数, ${totalCalls} 次调用, 总耗时: ${totalTime.toFixed(2)}ms\n\n`;
// 详细函数统计
logContent += '=== 所有函数详细统计 ===\n';
logContent += `=== 所有函数详细统计 ===\n`;
const allStats = this.getStats().sort((a, b) => b.totalTime - a.totalTime);
allStats.forEach((stat, index) => {
@@ -127,26 +127,26 @@ export class PerformanceMonitor {
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 += `\n`;
});
// 排行榜统计
logContent += '=== 总耗时排行榜 (Top 20) ===\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';
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';
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';
logContent += `\n=== 性能热点分析 ===\n`;
// 找出最耗时的前10个函数
const hotSpots = this.getTopByTotalTime(10);
hotSpots.forEach((stat, index) => {
@@ -155,11 +155,11 @@ export class PerformanceMonitor {
logContent += ` 性能影响: ${((stat.totalTime / totalTime) * 100).toFixed(2)}%\n`;
logContent += ` 调用效率: ${efficiency.toFixed(4)} 调用/ms\n`;
logContent += ` 优化建议: ${stat.averageTime > 10 ? '考虑优化此函数的执行效率' :
stat.callCount > 1000 ? '考虑减少此函数的调用频率' :
'性能表现良好'}\n\n`;
stat.callCount > 1000 ? '考虑减少此函数的调用频率' :
'性能表现良好'}\n\n`;
});
logContent += '=== 报告结束 ===\n';
logContent += `=== 报告结束 ===\n`;
// 写入文件
fs.writeFileSync(logPath, logContent, 'utf8');

View File

@@ -1 +1 @@
export const napCatVersion = '4.8.116';
export const napCatVersion = '4.8.119';

View File

@@ -9,7 +9,7 @@ export async function runTask<T, R>(workerScript: string, taskData: T): Promise<
console.error('Worker Log--->:', (result as { log: string }).log);
}
if ((result as any)?.error) {
reject(new Error('Worker error: ' + (result as { error: string }).error));
reject(new Error("Worker error: " + (result as { error: string }).error));
}
resolve(result);
});

View File

@@ -45,7 +45,7 @@ export class NTQQFileApi {
'http://ss.xingzhige.com/music_card/rkey',
'https://secret-service.bietiaop.com/rkeys',
],
this.context.logger
this.context.logger
);
}
@@ -378,18 +378,18 @@ export class NTQQFileApi {
element.elementType === ElementType.FILE
) {
switch (element.elementType) {
case ElementType.PIC:
case ElementType.PIC:
element.picElement!.sourcePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.VIDEO:
break;
case ElementType.VIDEO:
element.videoElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.PTT:
break;
case ElementType.PTT:
element.pttElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
case ElementType.FILE:
break;
case ElementType.FILE:
element.fileElement!.filePath = elementResults?.[elementIndex] ?? '';
break;
break;
}
elementIndex++;
}

View File

@@ -110,7 +110,7 @@ export class NTQQFriendApi {
time: item.reqTime, // 信息字段
type: 'doubt' //保留字段
};
}));
}))
return requests;
}
}

View File

@@ -58,13 +58,13 @@ export class NTQQGroupApi {
} as Peer,
{
busiId: 2201,
jsonStr: JSON.stringify({ 'align': 'center', 'items': [{ 'txt': tip, 'type': 'nor' }] }),
jsonStr: JSON.stringify({ "align": "center", "items": [{ "txt": tip, "type": "nor" }] }),
recentAbstract: tip,
isServer: false
},
true,
true
);
)
}
async initCache() {
for (const group of await this.getGroups(true)) {
@@ -247,12 +247,6 @@ export class NTQQGroupApi {
return member;
}
async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) {
// 添加参数验证,防止 undefined/null 导致的崩溃
if (groupCode === undefined || groupCode === null || memberUinOrUid === undefined || memberUinOrUid === null) {
this.context.logger.logError('getGroupMember: 无效的参数', { groupCode, memberUinOrUid });
return undefined;
}
const groupCodeStr = groupCode.toString();
const memberUinOrUidStr = memberUinOrUid.toString();

View File

@@ -330,9 +330,9 @@ export class NTQQWebApi {
attach_info: '',
seq: 3331,
request_time_line: {
request_invoke_time: '0'
request_invoke_time: "0"
}
});
})
}
async getAlbumList(gc: string) {
const skey = await this.core.apis.UserApi.getSKey() || '';
@@ -340,7 +340,7 @@ export class NTQQWebApi {
const bkn = this.getBknFromSKey(skey);
const uin = this.core.selfInfo.uin || '10001';
const cookies = `p_uin=o${this.core.selfInfo.uin}; p_skey=${pskey}; skey=${skey}; uin=o${uin} `;
const api = 'https://h5.qzone.qq.com/proxy/domain/u.photo.qzone.qq.com/cgi-bin/upp/qun_list_album_v2?';
const api = `https://h5.qzone.qq.com/proxy/domain/u.photo.qzone.qq.com/cgi-bin/upp/qun_list_album_v2?`;
const params = new URLSearchParams({
random: '7570',
g_tk: bkn,
@@ -450,7 +450,7 @@ export class NTQQWebApi {
attach_info: attach_info,
seq: 0,
request_time_line: {
request_invoke_time: '0'
request_invoke_time: "0"
},
album_id: albumId,
lloc: '',
@@ -466,13 +466,13 @@ export class NTQQWebApi {
const random_seq = Math.floor(Math.random() * 9000) + 1000;
const uin = this.core.selfInfo.uin || '10001';
//16位number数字
const client_key = Date.now() * 1000;
const client_key = Date.now() * 1000
return await this.context.session.getAlbumService().doQunComment(
random_seq, {
map_info: [],
map_bytes_info: [],
map_user_account: []
},
map_info: [],
map_bytes_info: [],
map_user_account: []
},
qunId,
2,
createAlbumMediaFeed(uin, albumId, lloc),
@@ -503,14 +503,14 @@ export class NTQQWebApi {
const uin = this.core.selfInfo.uin || '10001';
return await this.context.session.getAlbumService().doQunLike(
random_seq, {
map_info: [],
map_bytes_info: [],
map_user_account: []
}, {
id: id,
status: 1
},
map_info: [],
map_bytes_info: [],
map_user_account: []
}, {
id: id,
status: 1
},
createAlbumFeedPublish(qunId, uin, albumId, lloc)
);
)
}
}

View File

@@ -27,14 +27,14 @@ export function createAlbumListRequest(
): AlbumListRequest {
return {
qun_id: qunId,
attach_info: '',
attach_info: "",
seq: seq,
request_time_line: {
request_invoke_time: '0'
request_invoke_time: "0"
},
album_id: albumId,
lloc: '',
batch_id: ''
lloc: "",
batch_id: ""
};
}
@@ -75,7 +75,7 @@ export function createAlbumMediaFeed(
): AlbumMediaFeed {
return {
cell_common: {
time: ''
time: ""
},
cell_user_info: {
user: {
@@ -84,7 +84,7 @@ export function createAlbumMediaFeed(
},
cell_media: {
album_id: albumId,
batch_id: '',
batch_id: "",
media_items: [{
image: {
lloc: lloc
@@ -141,9 +141,9 @@ export function createAlbumCommentRequest(
type: RichMsgType.KRICHMSGTYPEPLAINTEXT,
content: content,
who: 0,
uid: '',
name: '',
url: ''
uid: "",
name: "",
url: ""
}],
user: {
uin: uin
@@ -196,7 +196,7 @@ export function createAlbumFeedPublish(
return {
cell_common: {
time: Date.now(),
feed_id: ''
feed_id: ""
},
cell_user_info: {
user: {

View File

@@ -1,4 +1,4 @@
import { GroupDetailInfoV2Param, GroupExtInfo, GroupExtFilter } from '../types';
import { GroupDetailInfoV2Param, GroupExtInfo, GroupExtFilter } from "../types";
export function createGroupDetailInfoV2Param(group_code: string): GroupDetailInfoV2Param {
return {
@@ -51,7 +51,7 @@ export function createGroupDetailInfoV2Param(group_code: string): GroupDetailInf
}, groupSecLevel: 0,
groupSecLevelInfo: 0,
subscriptionUin: 0,
subscriptionUid: '',
subscriptionUid: "",
allowMemberInvite: 0,
groupQuestion: 0,
groupAnswer: 0,
@@ -81,34 +81,34 @@ export function createGroupDetailInfoV2Param(group_code: string): GroupDetailInf
modifyInfo: {
noCodeFingerOpenFlag: 0,
noFingerOpenFlag: 0,
groupName: '',
groupName: "",
classExt: 0,
classText: '',
fingerMemo: '',
richFingerMemo: '',
classText: "",
fingerMemo: "",
richFingerMemo: "",
tagRecord: [],
groupGeoInfo: {
ownerUid: '',
ownerUid: "",
SetTime: 0,
CityId: 0,
Longitude: '',
Latitude: '',
GeoContent: '',
poiId: ''
Longitude: "",
Latitude: "",
GeoContent: "",
poiId: ""
},
groupExtAdminNum: 0,
flag: 0,
groupMemo: '',
groupAioSkinUrl: '',
groupBoardSkinUrl: '',
groupCoverSkinUrl: '',
groupMemo: "",
groupAioSkinUrl: "",
groupBoardSkinUrl: "",
groupCoverSkinUrl: "",
groupGrade: 0,
activeMemberNum: 0,
certificationType: 0,
certificationText: '',
certificationText: "",
groupNewGuideLines: {
enabled: false,
content: ''
content: ""
}, groupFace: 0,
addOption: 0,
shutUpTime: 0,
@@ -121,15 +121,15 @@ export function createGroupDetailInfoV2Param(group_code: string): GroupDetailInf
},
groupSecLevel: 0,
groupSecLevelInfo: 0,
subscriptionUin: '',
subscriptionUid: '',
subscriptionUin: "",
subscriptionUid: "",
allowMemberInvite: 0,
groupQuestion: '',
groupAnswer: '',
groupQuestion: "",
groupAnswer: "",
groupFlagExt3: 0,
groupFlagExt3Mask: 0,
groupOpenAppid: 0,
rootId: '',
rootId: "",
msgLimitFrequency: 0,
hlGuildAppid: 0,
hlGuildSubType: 0,
@@ -137,20 +137,20 @@ export function createGroupDetailInfoV2Param(group_code: string): GroupDetailInf
groupFlagExt4: 0,
groupFlagExt4Mask: 0,
groupSchoolInfo: {
location: '',
location: "",
grade: 0,
school: ''
school: ""
},
groupCardPrefix:
{
introduction: '',
introduction: "",
rptPrefix: []
},
allianceId: '',
allianceId: "",
groupFlagPro1: 0,
groupFlagPro1Mask: 0
}
};
}
}
export function createGroupExtInfo(group_code: string): GroupExtInfo {
return {
@@ -205,7 +205,7 @@ export function createGroupExtInfo(group_code: string): GroupExtInfo {
inviteRobotMemberExamine: 0,
groupSquareSwitch: 0,
}
};
}
}
export function createGroupExtFilter(): GroupExtFilter {
return {
@@ -241,5 +241,5 @@ export function createGroupExtFilter(): GroupExtFilter {
inviteRobotMemberSwitch: 0,
inviteRobotMemberExamine: 0,
groupSquareSwitch: 0,
};
}
};

View File

@@ -1 +1 @@
export * from './group';
export * from "./group";

View File

@@ -70,9 +70,9 @@ export function qunAlbumControl({
img_name,
sAlbumName,
sAlbumID,
photo_num = '1',
video_num = '0',
batch_num = '1'
photo_num = "1",
video_num = "0",
batch_num = "1"
}: {
uin: string,
group_id: string,
@@ -100,18 +100,18 @@ export function qunAlbumControl({
data: pskey,
appid: 5
},
appid: 'qun',
appid: "qun",
checksum: pic_md5,
check_type: 0,
file_len: img_size,
env: {
refer: 'qzone',
deviceInfo: 'h5'
refer: "qzone",
deviceInfo: "h5"
},
model: 0,
biz_req: {
sPicTitle: img_name,
sPicDesc: '',
sPicDesc: "",
sAlbumName: sAlbumName,
sAlbumID: sAlbumID,
iAlbumTypeID: 0,
@@ -119,7 +119,7 @@ export function qunAlbumControl({
iUploadType: 0,
iUpPicType: 0,
iBatchID: timestamp,
sPicPath: '',
sPicPath: "",
iPicWidth: 0,
iPicHight: 0,
iWaterType: 0,
@@ -127,7 +127,7 @@ export function qunAlbumControl({
iNeedFeeds: 1,
iUploadTime: timestamp,
mapExt: {
appid: 'qun',
appid: "qun",
userid: group_id
},
stExtendInfo: {
@@ -138,11 +138,11 @@ export function qunAlbumControl({
}
}
},
session: '',
session: "",
asy_upload: 0,
cmd: 'FileUpload'
cmd: "FileUpload"
}]
};
}
}
export function createStreamUpload(
@@ -159,16 +159,16 @@ export function createStreamUpload(
) {
return {
uin: uin,
appid: 'qun',
appid: "qun",
session: session,
offset: offset,//分片起始位置
data: data,//base64编码数据
checksum: '',
checksum: "",
check_type: 0,
retry: 0,//重试次数
seq: seq,//分片序号
end: end,//分片结束位置 文件总大小
cmd: 'FileUpload',
cmd: "FileUpload",
slice_size: slice_size,//分片大小16KB 16384
biz_req: {
iUploadType: 3

View File

@@ -386,5 +386,9 @@
"9.9.21-39038": {
"appid": 537313906,
"qua": "V1_WIN_NQ_9.9.21_39038_GW_B"
},
"9.9.22-40362": {
"appid": 537314212,
"qua": "V1_WIN_NQ_9.9.22_40362_GW_B"
}
}

View File

@@ -507,8 +507,12 @@
"send": "7B025C8",
"recv": "7B05F58"
},
"9.9.21-39038-x64": {
"9.9.21-39038-x64": {
"send": "313FB58",
"recv": "31432FC"
},
"9.9.22-40362-x64": {
"send": "31C0EB8",
"recv": "31C465C"
}
}

View File

@@ -40,7 +40,7 @@ export class NativePacketClient extends IPacketClient {
async init(_pid: number, recv: string, send: string): Promise<void> {
const platform = process.platform + '.' + process.arch;
const isNewQQ = this.napcore.basicInfo.requireMinNTQQBuild('36580');
const isNewQQ = this.napcore.basicInfo.requireMinNTQQBuild("36580");
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + (isNewQQ ? '.new' : '') + '.node');
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);

View File

@@ -183,9 +183,9 @@ export class PacketOperationContext {
const ps = msg.map((m) => {
return m.msg.map(async (e) => {
if (e instanceof PacketMsgReplyElement && !e.targetElems) {
this.context.logger.debug('Cannot find reply element\'s targetElems, prepare to fetch it...');
this.context.logger.debug(`Cannot find reply element's targetElems, prepare to fetch it...`);
if (!e.targetPeer?.peerUid) {
this.context.logger.error('targetPeer is undefined!');
this.context.logger.error(`targetPeer is undefined!`);
}
let targetMsg: NapProtoEncodeStructType<typeof PushMsgBody>[] | undefined;
if (e.isGroupReply) {
@@ -198,7 +198,7 @@ export class PacketOperationContext {
}
});
}).flat();
await Promise.all(ps);
await Promise.all(ps)
await this.UploadResources(msg, groupUin);
}
@@ -206,14 +206,14 @@ export class PacketOperationContext {
const req = trans.FetchGroupMessage.build(groupUin, startSeq, endSeq);
const resp = await this.context.client.sendOidbPacket(req, true);
const res = trans.FetchGroupMessage.parse(resp);
return res.body.messages;
return res.body.messages
}
async FetchC2CMessage(targetUid: string, startSeq: number, endSeq: number): Promise<NapProtoDecodeStructType<typeof PushMsgBody>[]> {
const req = trans.FetchC2CMessage.build(targetUid, startSeq, endSeq);
const resp = await this.context.client.sendOidbPacket(req, true);
const res = trans.FetchC2CMessage.parse(resp);
return res.messages;
return res.messages
}
async UploadForwardMsg(msg: PacketMsg[], groupUin: number = 0) {

View File

@@ -1,4 +1,4 @@
import { ProtoField, ScalarType } from '@napneko/nap-proto-core';
import { ProtoField, ScalarType } from "@napneko/nap-proto-core";
export const OidbSvcTrpcTcp0XF90_1 = {
groupUin: ProtoField(1, ScalarType.UINT32),

View File

@@ -1,4 +1,4 @@
import { AlbumCommentReplyContent, AlbumFeedLikePublish, AlbumListRequest, AlbumMediaFeed } from '../data/album';
import { AlbumCommentReplyContent, AlbumFeedLikePublish, AlbumListRequest, AlbumMediaFeed } from "../data/album";
export interface NodeIKernelAlbumService {

View File

@@ -1,12 +1,12 @@
import { EventType } from '@/onebot/event/OneBotEvent';
import type { PluginModule } from '@/onebot/network/plugin-manger';
import { EventType } from "@/onebot/event/OneBotEvent";
import type { PluginModule } from "@/onebot/network/plugin-manger";
const plugin_init: PluginModule['plugin_init'] = async (_core, _obContext, _actions, _instance) => {
console.log('[Plugin: example] 插件已初始化');
};
const plugin_onmessage: PluginModule['plugin_onmessage'] = async (adapter, _core, _obCtx, event, actions, instance) => {
const plugin_init: PluginModule["plugin_init"] = async (_core, _obContext, _actions, _instance) => {
console.log(`[Plugin: example] 插件已初始化`);
}
const plugin_onmessage: PluginModule["plugin_onmessage"] = async (adapter, _core, _obCtx, event, actions, instance) => {
if (event.post_type === EventType.MESSAGE && event.raw_message.includes('ping')) {
await actions.get('send_group_msg')?.handle({ group_id: String(event.group_id), message: 'pong' }, adapter, instance.config);
}
};
}
export { plugin_init, plugin_onmessage };

View File

@@ -25,6 +25,6 @@ export class ClickInlineKeyboardButton extends OneBotAction<Payload, unknown> {
callback_data: payload.callback_data,
dmFlag: 0,
chatType: 2
});
})
}
}

View File

@@ -5,7 +5,7 @@ import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({
group_id: Type.String(),
album_id: Type.String(),
attach_info: Type.String({ default: '' }),
attach_info: Type.String({ default: "" }),
});
type Payload = Static<typeof SchemaData>;

View File

@@ -36,7 +36,7 @@ export class GetUnidirectionalFriendList extends OneBotAction<void, Friend[]> {
uint64_uin: self_id,
uint64_top: 0,
uint32_req_num: 99,
bytes_cookies: ''
bytes_cookies: ""
};
const packed_data = await this.pack_data(JSON.stringify(req_json));
const data = Buffer.from(packed_data).toString('hex');

View File

@@ -33,7 +33,7 @@ class GetMsg extends OneBotAction<Payload, OB11Message> {
// if (orimsg) {
// msg = orimsg;
// } else {
msg = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgIdWithPeer?.MsgId || payload.message_id.toString()])).msgList[0];
msg = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgIdWithPeer?.MsgId || payload.message_id.toString()])).msgList[0];
//}
if (!msg) throw Error('消息不存在');
const retMsg = await this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat);

View File

@@ -55,7 +55,7 @@ export async function createContext(core: NapCatCore, payload: OB11PostContext |
chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(),
guildId: ''
};
}
}
throw new Error('无法获取用户信息');
}

View File

@@ -8,7 +8,7 @@ export class GetRkeyEx extends GetPacketStatusDepends<void, unknown> {
let rkeys = await this.core.apis.PacketApi.pkt.operation.FetchRkey();
return rkeys.map(rkey => {
return {
type: rkey.type === 10 ? 'private' : 'group',
type: rkey.type === 10 ? "private" : "group",
rkey: rkey.rkey,
created_at: rkey.time,
ttl: rkey.ttl,

View File

@@ -30,7 +30,7 @@ export class GetRkeyServer extends GetPacketStatusDepends<void, { private_rkey?:
private_rkey: privateRkeyItem ? privateRkeyItem.rkey : undefined,
group_rkey: groupRkeyItem ? groupRkeyItem.rkey : undefined,
expired_time: this.expiryTime,
name: 'NapCat 4'
name: "NapCat 4"
};
return this.rkeyCache;

View File

@@ -14,8 +14,8 @@ export class SendPokeBase extends GetPacketStatusDepends<Payload, void> {
async _handle(payload: Payload) {
// 这里的 !! 可以传入空字符串 忽略这些数据有利用接口统一接口
const target_id = payload.target_id ? payload.target_id : payload.user_id;
const peer_id = payload.group_id ? payload.group_id : payload.user_id;
const target_id = !!payload.target_id ? payload.target_id : payload.user_id;
const peer_id = !!payload.group_id ? payload.group_id : payload.user_id;
const is_group = !!payload.group_id;
if (!target_id || !peer_id) {

View File

@@ -14,7 +14,7 @@ export const ActionName = {
CleanStreamTempFile: 'clean_stream_temp_file',
// 所有 Upload/Download Stream Api 应当 _stream 结尾
TestDownloadStream: 'test_download_stream',
TestDownloadStream: 'test_download_stream',
UploadFileStream: 'upload_file_stream',
DownloadFileStream: 'download_file_stream',

View File

@@ -104,7 +104,7 @@ export class DownloadFileStream extends OneBotAction<Payload, StreamPacket<Downl
const base64Chunk = chunk.toString('base64');
bytesRead += chunk.length;
await req.send({
await req.send({
type: StreamStatus.Stream,
data_type: 'file_chunk',
index: chunkIndex,

View File

@@ -1,5 +1,5 @@
import { OneBotAction, OneBotRequestToolkit } from '../OneBotAction';
import { NetworkAdapterConfig } from '@/onebot/config/config';
import { OneBotAction, OneBotRequestToolkit } from "../OneBotAction";
import { NetworkAdapterConfig } from "@/onebot/config/config";
export type StreamPacketBasic = {
type: StreamStatus;
data_type?: string;

View File

@@ -154,7 +154,7 @@ export class UploadFileStream extends OneBotAction<Payload, StreamPacket<StreamR
try {
fs.rmSync(stream.tempDir, { recursive: true, force: true });
} catch (cleanupError) {
console.error('Failed to cleanup temp dir during creation error:', cleanupError);
console.error(`Failed to cleanup temp dir during creation error:`, cleanupError);
}
}
throw error;

View File

@@ -180,7 +180,7 @@ export class OneBotMsgApi {
file_size: element.fileSize,
url: url,
},
};
}
}
}
return {
@@ -489,7 +489,7 @@ export class OneBotMsgApi {
url: pttUrl,
file_size: element.fileSize,
},
};
}
}
return {
type: OB11MessageDataType.voice,
@@ -578,13 +578,8 @@ export class OneBotMsgApi {
};
}
if (!context.peer || context.peer.chatType == ChatType.KCHATTYPEC2C) return undefined;
if (!context.peer || !atQQ || context.peer.chatType == ChatType.KCHATTYPEC2C) return undefined; // 过滤掉空atQQ
if (atQQ === 'all') return at(atQQ, atQQ, NTMsgAtType.ATTYPEALL, '全体成员');
// 检查 peerUid 是否有效
if (!context.peer.peerUid) {
this.core.context.logger.logWarn('AT消息处理群组 peerUid 无效', { atQQ });
return undefined;
}
const atMember = await this.core.apis.GroupApi.getGroupMember(context.peer.peerUid, atQQ);
if (atMember) {
return at(atQQ, atMember.uid, NTMsgAtType.ATTYPEONE, atMember.nick || atMember.cardName);
@@ -1155,16 +1150,16 @@ export class OneBotMsgApi {
const calculateTotalSize = async (elements: SendMessageElement[]): Promise<number> => {
const sizePromises = elements.map(async element => {
switch (element.elementType) {
case ElementType.PTT:
return (await fsPromise.stat(element.pttElement.filePath)).size;
case ElementType.FILE:
return (await fsPromise.stat(element.fileElement.filePath)).size;
case ElementType.VIDEO:
return (await fsPromise.stat(element.videoElement.filePath)).size;
case ElementType.PIC:
return (await fsPromise.stat(element.picElement.sourcePath)).size;
default:
return 0;
case ElementType.PTT:
return (await fsPromise.stat(element.pttElement.filePath)).size;
case ElementType.FILE:
return (await fsPromise.stat(element.fileElement.filePath)).size;
case ElementType.VIDEO:
return (await fsPromise.stat(element.videoElement.filePath)).size;
case ElementType.PIC:
return (await fsPromise.stat(element.picElement.sourcePath)).size;
default:
return 0;
}
});
const sizes = await Promise.all(sizePromises);
@@ -1254,16 +1249,16 @@ export class OneBotMsgApi {
groupChangDecreseType2String(type: number): GroupDecreaseSubType {
switch (type) {
case 130:
return 'leave';
case 131:
return 'kick';
case 3:
return 'kick_me';
case 129:
return 'disband';
default:
return 'kick';
case 130:
return 'leave';
case 131:
return 'kick';
case 3:
return 'kick_me';
case 129:
return 'disband';
default:
return 'kick';
}
}

View File

@@ -217,6 +217,15 @@ export class NapCatOneBot11Adapter {
//this.context.logger.log(`OneBot11 配置更改:${JSON.stringify(prev)} -> ${JSON.stringify(newConfig)}`);
await this.reloadNetwork(prev, newConfig);
});
WebUiDataRuntime.setCleanCacheCall(async () => {
try {
await this.actions.get('clean_cache')?.handle({});
return { result: true, message: '缓存清理成功' };
} catch (error) {
this.context.logger.logError('清理缓存失败:', error);
return { result: false, message: `清理缓存失败: ${(error as Error).message}` };
}
});
}

View File

@@ -129,7 +129,7 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter<HttpServerConfig>
await this.onEvent({ ...OB11Response.ok(data, real_echo, true) } as unknown as OB11EmitEventContent);
} : async (data: object) => {
let newPromise = new Promise<void>((resolve, _reject) => {
res.write(JSON.stringify({ ...OB11Response.ok(data, real_echo, true) }) + '\r\n\r\n', () => {
res.write(JSON.stringify({ ...OB11Response.ok(data, real_echo, true) }) + "\r\n\r\n", () => {
resolve();
});
});
@@ -137,7 +137,7 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter<HttpServerConfig>
}
}, real_echo);
if (useStream) {
res.write(JSON.stringify({ ...result }) + '\r\n\r\n');
res.write(JSON.stringify({ ...result }) + "\r\n\r\n");
return res.end();
};
return res.json(result);

View File

@@ -74,7 +74,7 @@ export class OB11PluginMangerAdapter extends IOB11NetworkAdapter<PluginConfig> {
this.logger.log(`[Plugin Adapter] Loaded ${this.loadedPlugins.size} plugins`);
} catch (error) {
this.logger.logError('[Plugin Adapter] Error loading plugins:', error);
this.logger.logError(`[Plugin Adapter] Error loading plugins:`, error);
}
}

View File

@@ -74,7 +74,7 @@ export class OB11PluginAdapter extends IOB11NetworkAdapter<PluginConfig> {
this.logger.log(`[Plugin Adapter] Loaded ${this.loadedPlugins.size} plugins`);
} catch (error) {
this.logger.logError('[Plugin Adapter] Error loading plugins:', error);
this.logger.logError(`[Plugin Adapter] Error loading plugins:`, error);
}
}

View File

@@ -143,7 +143,7 @@ async function handleLogin(
handleLoginInner(context, logger, loginService, quickLoginUin, historyLoginList).then().catch(e => logger.logError(e));
loginListener.onLoginConnected = () => { };
});
};
}
loginListener.onQRCodeGetPicture = ({ pngBase64QrcodeData, qrcodeUrl }) => {
WebUiDataRuntime.setQQLoginQrcodeURL(qrcodeUrl);
@@ -222,7 +222,7 @@ async function handleLoginInner(context: { isLogined: boolean }, logger: LogWrap
logger.log(`可用于快速登录的 QQ\n${historyLoginList
.map((u, index) => `${index + 1}. ${u.uin} ${u.nickName}`)
.join('\n')
}`);
}`);
}
loginService.getQRCodePicture();
try {

View File

@@ -24,3 +24,8 @@ export const SetThemeConfigHandler: RequestHandler = async (req, res) => {
await WebUiConfig.UpdateTheme(theme);
sendSuccess(res, { message: '更新成功' });
};
export const CleanCacheHandler: RequestHandler = async (_, res) => {
const result = await WebUiDataRuntime.requestCleanCache();
sendSuccess(res, result);
};

View File

@@ -27,6 +27,9 @@ const LoginRuntime: LoginRuntimeType = {
onQuickLoginRequested: async () => {
return { result: false, message: '' };
},
onCleanCacheRequested: async () => {
return { result: false, message: '' };
},
QQLoginList: [],
NewQQLoginList: [],
},
@@ -130,6 +133,14 @@ export const WebUiDataRuntime = {
return LoginRuntime.NapCatHelper.onOB11ConfigChanged(ob11);
} as LoginRuntimeType['NapCatHelper']['onOB11ConfigChanged'],
setCleanCacheCall(func: LoginRuntimeType['NapCatHelper']['onCleanCacheRequested']): void {
LoginRuntime.NapCatHelper.onCleanCacheRequested = func;
},
requestCleanCache: function () {
return LoginRuntime.NapCatHelper.onCleanCacheRequested();
} as LoginRuntimeType['NapCatHelper']['onCleanCacheRequested'],
getPackageJson() {
return LoginRuntime.packageJson;
},

View File

@@ -1,5 +1,5 @@
import { Router } from 'express';
import { GetThemeConfigHandler, PackageInfoHandler, QQVersionHandler, SetThemeConfigHandler } from '../api/BaseInfo';
import { CleanCacheHandler, GetThemeConfigHandler, PackageInfoHandler, QQVersionHandler, SetThemeConfigHandler } from '../api/BaseInfo';
import { StatusRealTimeHandler } from '@webapi/api/Status';
import { GetProxyHandler } from '../api/Proxy';
@@ -11,5 +11,6 @@ router.get('/GetSysStatusRealTime', StatusRealTimeHandler);
router.get('/proxy', GetProxyHandler);
router.get('/Theme', GetThemeConfigHandler);
router.post('/SetTheme', SetThemeConfigHandler);
router.post('/CleanCache', CleanCacheHandler);
export { router as BaseRouter };

View File

@@ -15,6 +15,7 @@ interface LoginRuntimeType {
NapCatHelper: {
onQuickLoginRequested: (uin: string) => Promise<{ result: boolean; message: string }>;
onOB11ConfigChanged: (ob11: OneBotConfig) => Promise<void>;
onCleanCacheRequested: () => Promise<{ result: boolean; message: string }>;
QQLoginList: string[];
NewQQLoginList: LoginListItem[];
};