Compare commits

..

15 Commits

Author SHA1 Message Date
手瓜一十雪
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
手瓜一十雪
f2c62db76e Update README with new features in v4.8.115+
Added a section describing new features in version v4.8.115+, including Stream API support and recommendations to use string types for message_id, user_id, and group_id. Also explained the benefits of these changes for Docker, cross-device, large file transfers, and better compatibility with languages lacking large integer support.
2025-09-21 13:29:35 +08:00
手瓜一十雪
b1b051c4ce Update DeepWiki badge formatting in README
Reformatted the DeepWiki badge section in the README to match the table style used for other community links.
2025-09-20 16:45:27 +08:00
手瓜一十雪
a754b2ecc7 Add DeepWiki badge to README
Added a DeepWiki badge with a link to the project's DeepWiki page for increased visibility and resource access.
2025-09-20 16:44:57 +08:00
Mlikiowa
e0eb625b75 release: v4.8.116 2025-09-20 08:20:05 +00:00
手瓜一十雪
937be7678e Add file_retention parameter to upload test
Introduces the 'file_retention' field with a value of 30,000 to the upload test payload in OneBotUploadTester. This may be used to specify file retention duration in milliseconds.
2025-09-20 16:19:39 +08:00
手瓜一十雪
9b88946209 Add file retention option to UploadFileStream
Introduces a 'file_retention' parameter to control how long uploaded files are retained before automatic deletion. If set, files are deleted after the specified duration; otherwise, they are not automatically removed. This helps manage temporary file storage and cleanup.
2025-09-20 16:13:25 +08:00
Mlikiowa
74de3d9100 release: v4.8.115 2025-09-20 07:57:47 +00:00
17 changed files with 59 additions and 26 deletions

View File

@@ -13,6 +13,15 @@ _Modern protocol-side framework implemented based on NTQQ._
---
## New Feature
在 v4.8.115+ 版本开始
1. NapCatQQ 支持 [Stream Api](https://napneko.github.io/develop/file)
2. NapCatQQ 推荐 message_id/user_id/group_id 均使用字符串类型
- [1] 解决 Docker/跨设备/大文件 的多媒体上下传问题
- [2] 采用字符串可以解决扩展到int64的问题同时也可以解决部分语言如JavaScript对大整数支持不佳的问题增加极少成本。
## Welcome
+ NapCatQQ is a modern implementation of the Bot protocol based on NTQQ.
- NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现
@@ -48,6 +57,9 @@ _Modern protocol-side framework implemented based on NTQQ._
| Telegram | [![Telegram](https://img.shields.io/badge/Telegram-napcatqq-blue)](https://t.me/napcatqq) |
|:-:|:-:|
| DeepWiki | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/NapNeko/NapCatQQ) |
|:-:|:-:|
> 请不要在其余社区提及本项目(包括其余协议端/相关应用端项目)引发争论如有建议到达官方交流群讨论或PR。
## Thanks

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.114",
"version": "4.8.117",
"icon": "./logo.png",
"authors": [
{

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 }

View File

@@ -182,4 +182,4 @@ const ServerConfigCard = () => {
)
}
export default ServerConfigCard
export default ServerConfigCard

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.8.114",
"version": "4.8.117",
"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

@@ -1 +1 @@
export const napCatVersion = '4.8.114';
export const napCatVersion = '4.8.117';

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": {
"send": "31C0EB8",
"recv": "31C465C"
}
}

View File

@@ -7,6 +7,7 @@ import fs from 'fs';
import { join as joinPath } from 'node:path';
import { randomUUID } from 'crypto';
import { createHash } from 'crypto';
import { unlink } from 'node:fs';
// 简化配置
const CONFIG = {
@@ -25,7 +26,8 @@ const SchemaData = Type.Object({
is_complete: Type.Optional(Type.Boolean()),
filename: Type.Optional(Type.String()),
reset: Type.Optional(Type.Boolean()),
verify_only: Type.Optional(Type.Boolean())
verify_only: Type.Optional(Type.Boolean()),
file_retention: Type.Number({ default: 5 * 60 * 1000 }) // 默认5分钟 回收 不设置或0为不回收
});
type Payload = Static<typeof SchemaData>;
@@ -47,6 +49,7 @@ interface StreamState {
memoryChunks?: Map<number, Buffer>;
tempDir?: string;
finalPath?: string;
fileRetention?: number;
// 管理
createdAt: number;
@@ -131,9 +134,9 @@ export class UploadFileStream extends OneBotAction<Payload, StreamPacket<StreamR
expectedSha256: expected_sha256,
useMemory,
createdAt: Date.now(),
timeoutId: this.setupTimeout(stream_id)
timeoutId: this.setupTimeout(stream_id),
fileRetention: payload.file_retention
};
try {
if (useMemory) {
stream.memoryChunks = new Map();
@@ -244,7 +247,13 @@ export class UploadFileStream extends OneBotAction<Payload, StreamPacket<StreamR
// 清理资源但保留文件
this.cleanupStream(stream.id, false);
if (stream.fileRetention && stream.fileRetention > 0) {
setTimeout(() => {
unlink(finalPath, err => {
if (err) this.core.context.logger.logError(`Failed to delete retained file ${finalPath}:`, err);
});
}, stream.fileRetention);
}
return {
type: StreamStatus.Response,
stream_id: stream.id,

View File

@@ -29,7 +29,7 @@ class OneBotUploadTester:
headers["Authorization"] = f"Bearer {self.access_token}"
print(f"连接到 {self.ws_url}")
self.websocket = await websockets.connect(self.ws_url, extra_headers=headers)
self.websocket = await websockets.connect(self.ws_url, additional_headers=headers)
print("WebSocket 连接成功")
async def disconnect(self):
@@ -38,7 +38,7 @@ class OneBotUploadTester:
await self.websocket.close()
print("WebSocket 连接已断开")
def calculate_file_chunks(self, file_path: str, chunk_size: int = 64 * 1024) -> tuple[List[bytes], str, int]:
def calculate_file_chunks(self, file_path: str, chunk_size: int = 64) -> tuple[List[bytes], str, int]:
"""
计算文件分片和 SHA256
@@ -97,7 +97,7 @@ class OneBotUploadTester:
print(f"收到其他消息: {data}")
continue
async def upload_file_stream_batch(self, file_path: str, chunk_size: int = 64 * 1024) -> str:
async def upload_file_stream_batch(self, file_path: str, chunk_size: int = 64 ) -> str:
"""
一次性批量上传文件流
@@ -134,7 +134,8 @@ class OneBotUploadTester:
"total_chunks": total_chunks,
"file_size": total_size,
"expected_sha256": sha256_hash,
"filename": file_path.name
"filename": file_path.name,
"file_retention": 30 * 1000
}
# 发送分片
@@ -171,7 +172,7 @@ class OneBotUploadTester:
else:
raise Exception(f"文件状态异常: {result}")
async def test_upload(self, file_path: str, chunk_size: int = 64 * 1024):
async def test_upload(self, file_path: str, chunk_size: int = 64 ):
"""测试文件上传"""
try:
await self.connect()

View File

@@ -578,7 +578,7 @@ 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, '全体成员');
const atMember = await this.core.apis.GroupApi.getGroupMember(context.peer.peerUid, atQQ);
if (atMember) {