Compare commits

..

3 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
edc14763f0 Improve fix: only suppress card change events for forward messages
Changed the condition to specifically check for forward message elements (multiForwardMsgElement or arkElement) before suppressing empty card name changes. This ensures legitimate card name clearing (setting to empty) still works correctly while preventing false positives from forward messages.

Co-authored-by: sj817 <74231782+sj817@users.noreply.github.com>
2025-10-02 01:43:50 +00:00
copilot-swe-agent[bot]
6a6a0e0539 Fix spurious group_card events for forward messages
Prevent false group card change events when sending forward messages with comments. The issue occurred because forward messages sometimes have empty sendMemberName fields, triggering incorrect "name → empty" card change events.

Solution: Skip card change detection when the new card name is empty but the old card name is not empty, as this indicates unreliable data (e.g., from forward messages).

Co-authored-by: sj817 <74231782+sj817@users.noreply.github.com>
2025-10-02 01:26:10 +00:00
copilot-swe-agent[bot]
b930eea84f Initial plan 2025-10-02 01:16:56 +00:00
23 changed files with 32 additions and 159 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.119",
"version": "4.8.116",
"icon": "./logo.png",
"authors": [
{

View File

@@ -93,7 +93,6 @@
"@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",
@@ -8153,19 +8152,6 @@
"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",
@@ -8186,16 +8172,6 @@
"@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,7 +95,6 @@
"@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,7 +82,6 @@ export default function FileTable({
setPreviewImages([])
setPreviewIndex(0)
setShowImage(false)
setPage(1)
}, [currentPath])
const onPreviewImage = (name: string, images: PreviewImage[]) => {

View File

@@ -171,8 +171,7 @@ 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,10 +1,9 @@
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
@@ -18,7 +17,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 }
@@ -197,13 +196,4 @@ 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,22 +1,16 @@
import { Button } from '@heroui/button'
import { Input } from '@heroui/input'
import { Switch } from '@heroui/switch'
import { useRequest } from 'ahooks'
import { useEffect, useState } from 'react'
import { useEffect } 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,
@@ -75,42 +69,6 @@ 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])
@@ -173,30 +131,6 @@ 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
@@ -248,4 +182,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.119",
"version": "4.8.98",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "napcat",
"version": "4.8.119",
"version": "4.8.98",
"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.119",
"version": "4.8.116",
"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.119';
export const napCatVersion = '4.8.116';

View File

@@ -386,9 +386,5 @@
"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,12 +507,8 @@
"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

@@ -116,6 +116,17 @@ export class OneBotGroupApi {
const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUid, msg.senderUin);
if (member && member.cardName !== msg.sendMemberName) {
const newCardName = msg.sendMemberName ?? '';
// 防止误触如果消息包含转发元素且新名片为空不触发事件转发消息的sendMemberName不可靠
const hasForwardElement = msg.elements.some(e => e.multiForwardMsgElement || e.arkElement);
if (newCardName === '' && member.cardName !== '' && hasForwardElement) {
this.core.context.logger.logDebug('忽略转发消息的不可靠群名片变更事件', {
peerUid: msg.peerUid,
senderUin: msg.senderUin,
oldCard: member.cardName,
newCard: newCardName
});
return undefined;
}
const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName);
member.cardName = newCardName;
return event;

View File

@@ -578,7 +578,7 @@ export class OneBotMsgApi {
};
}
if (!context.peer || !atQQ || context.peer.chatType == ChatType.KCHATTYPEC2C) return undefined; // 过滤掉空atQQ
if (!context.peer || context.peer.chatType == ChatType.KCHATTYPEC2C) return undefined;
if (atQQ === 'all') return at(atQQ, atQQ, NTMsgAtType.ATTYPEALL, '全体成员');
const atMember = await this.core.apis.GroupApi.getGroupMember(context.peer.peerUid, atQQ);
if (atMember) {

View File

@@ -217,15 +217,6 @@ 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

@@ -24,8 +24,3 @@ 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,9 +27,6 @@ const LoginRuntime: LoginRuntimeType = {
onQuickLoginRequested: async () => {
return { result: false, message: '' };
},
onCleanCacheRequested: async () => {
return { result: false, message: '' };
},
QQLoginList: [],
NewQQLoginList: [],
},
@@ -133,14 +130,6 @@ 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 { CleanCacheHandler, GetThemeConfigHandler, PackageInfoHandler, QQVersionHandler, SetThemeConfigHandler } from '../api/BaseInfo';
import { GetThemeConfigHandler, PackageInfoHandler, QQVersionHandler, SetThemeConfigHandler } from '../api/BaseInfo';
import { StatusRealTimeHandler } from '@webapi/api/Status';
import { GetProxyHandler } from '../api/Proxy';
@@ -11,6 +11,5 @@ 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,7 +15,6 @@ 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[];
};