mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-01-18 22:40:28 +00:00
feat: 新增看门狗汪汪汪
This commit is contained in:
parent
0918b17257
commit
b97a224a14
@ -86,6 +86,7 @@ import { GetGroupMemberList } from './group/GetGroupMemberList';
|
|||||||
import { GetGroupFileUrl } from '@/napcat-onebot/action/file/GetGroupFileUrl';
|
import { GetGroupFileUrl } from '@/napcat-onebot/action/file/GetGroupFileUrl';
|
||||||
import { GetPacketStatus } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
import { GetPacketStatus } from '@/napcat-onebot/action/packet/GetPacketStatus';
|
||||||
import { GetCredentials } from './system/GetCredentials';
|
import { GetCredentials } from './system/GetCredentials';
|
||||||
|
import { SetRestart } from './system/SetRestart';
|
||||||
import { SendGroupSign, SetGroupSign } from './extends/SetGroupSign';
|
import { SendGroupSign, SetGroupSign } from './extends/SetGroupSign';
|
||||||
import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain';
|
import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain';
|
||||||
import { GoCQHTTPCheckUrlSafely } from './go-cqhttp/GoCQHTTPCheckUrlSafely';
|
import { GoCQHTTPCheckUrlSafely } from './go-cqhttp/GoCQHTTPCheckUrlSafely';
|
||||||
@ -266,6 +267,7 @@ export function createActionMap (obContext: NapCatOneBot11Adapter, core: NapCatC
|
|||||||
new GetGroupFileSystemInfo(obContext, core),
|
new GetGroupFileSystemInfo(obContext, core),
|
||||||
new GetGroupFilesByFolder(obContext, core),
|
new GetGroupFilesByFolder(obContext, core),
|
||||||
new GetPacketStatus(obContext, core),
|
new GetPacketStatus(obContext, core),
|
||||||
|
new SetRestart(obContext, core),
|
||||||
new GroupPoke(obContext, core),
|
new GroupPoke(obContext, core),
|
||||||
new FriendPoke(obContext, core),
|
new FriendPoke(obContext, core),
|
||||||
new GetUserStatus(obContext, core),
|
new GetUserStatus(obContext, core),
|
||||||
|
|||||||
@ -81,7 +81,7 @@ export const ActionName = {
|
|||||||
CanSendRecord: 'can_send_record',
|
CanSendRecord: 'can_send_record',
|
||||||
GetStatus: 'get_status',
|
GetStatus: 'get_status',
|
||||||
GetVersionInfo: 'get_version_info',
|
GetVersionInfo: 'get_version_info',
|
||||||
// Reboot : 'set_restart',
|
Reboot: 'set_restart',
|
||||||
CleanCache: 'clean_cache',
|
CleanCache: 'clean_cache',
|
||||||
Exit: 'bot_exit',
|
Exit: 'bot_exit',
|
||||||
// go-cqhttp
|
// go-cqhttp
|
||||||
|
|||||||
15
packages/napcat-onebot/action/system/SetRestart.ts
Normal file
15
packages/napcat-onebot/action/system/SetRestart.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { ActionName } from '@/napcat-onebot/action/router';
|
||||||
|
import { OneBotAction } from '../OneBotAction';
|
||||||
|
import { writeFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
export class SetRestart extends OneBotAction<void, void> {
|
||||||
|
override actionName = ActionName.Reboot;
|
||||||
|
|
||||||
|
async _handle () {
|
||||||
|
setTimeout(() => {
|
||||||
|
writeFileSync(join(this.obContext.context.pathWrapper.binaryPath, 'napcat.restart'), Date.now().toString());
|
||||||
|
process.exit(51);
|
||||||
|
}, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
54
packages/napcat-shell/launcher-win.bat
Normal file
54
packages/napcat-shell/launcher-win.bat
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
net session >nul 2>&1
|
||||||
|
if %ERRORLEVEL% == 0 (
|
||||||
|
echo Administrator mode detected.
|
||||||
|
) else (
|
||||||
|
echo Please run this script in administrator mode.
|
||||||
|
powershell -Command "Start-Process 'wt.exe' -ArgumentList 'cmd /k cd /d \"%cd%\" && \"%~f0\" %*' -Verb runAs"
|
||||||
|
exit
|
||||||
|
)
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
set NAPCAT_PATCH_PACKAGE=%cd%\qqnt.json
|
||||||
|
set NAPCAT_LOAD_PATH=%cd%\loadNapCat.js
|
||||||
|
set NAPCAT_INJECT_PATH=%cd%\NapCatWinBootHook.dll
|
||||||
|
set NAPCAT_LAUNCHER_PATH=%cd%\NapCatWinBootMain.exe
|
||||||
|
set NAPCAT_MAIN_PATH=%cd%\napcat.mjs
|
||||||
|
set NAPCAT_RESTART_SIGNAL=%cd%\napcat.restart
|
||||||
|
|
||||||
|
: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"
|
||||||
|
goto :napcat_boot
|
||||||
|
)
|
||||||
|
|
||||||
|
:napcat_boot
|
||||||
|
for %%a in ("%RetString%") do (
|
||||||
|
set "pathWithoutUninstall=%%~dpa"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "QQPath=%pathWithoutUninstall%QQ.exe"
|
||||||
|
|
||||||
|
if not exist "%QQPath%" (
|
||||||
|
echo provided QQ path is invalid
|
||||||
|
pause
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
set "ST_MAIN_PATH=%NAPCAT_MAIN_PATH:\=/%"
|
||||||
|
echo (async () =^> {await import("file:///%ST_MAIN_PATH%")})() > "%NAPCAT_LOAD_PATH%"
|
||||||
|
|
||||||
|
if exist "%NAPCAT_RESTART_SIGNAL%" del "%NAPCAT_RESTART_SIGNAL%"
|
||||||
|
|
||||||
|
echo [%date% %time%] [Watchdog] Starting NapCat...
|
||||||
|
"%NAPCAT_LAUNCHER_PATH%" "%QQPath%" "%NAPCAT_INJECT_PATH%" %*
|
||||||
|
|
||||||
|
:watchdog
|
||||||
|
timeout /t 3 /nobreak >nul
|
||||||
|
if exist "%NAPCAT_RESTART_SIGNAL%" (
|
||||||
|
echo [%date% %time%] [Watchdog] Restart signal received. Restarting...
|
||||||
|
del "%NAPCAT_RESTART_SIGNAL%"
|
||||||
|
goto napcat_boot
|
||||||
|
)
|
||||||
|
goto watchdog
|
||||||
@ -1,9 +1,11 @@
|
|||||||
import { RequestHandler } from 'express';
|
import { RequestHandler } from 'express';
|
||||||
|
import { writeFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import { WebUiDataRuntime } from '@/napcat-webui-backend/src/helper/Data';
|
import { WebUiDataRuntime } from '@/napcat-webui-backend/src/helper/Data';
|
||||||
|
import { webUiPathWrapper, WebUiConfig } from '@/napcat-webui-backend/index';
|
||||||
import { isEmpty } from '@/napcat-webui-backend/src/utils/check';
|
import { isEmpty } from '@/napcat-webui-backend/src/utils/check';
|
||||||
import { sendError, sendSuccess } from '@/napcat-webui-backend/src/utils/response';
|
import { sendError, sendSuccess } from '@/napcat-webui-backend/src/utils/response';
|
||||||
import { WebUiConfig } from '@/napcat-webui-backend/index';
|
|
||||||
|
|
||||||
// 获取QQ登录二维码
|
// 获取QQ登录二维码
|
||||||
export const QQGetQRcodeHandler: RequestHandler = async (_, res) => {
|
export const QQGetQRcodeHandler: RequestHandler = async (_, res) => {
|
||||||
@ -108,3 +110,12 @@ export const QQRefreshQRcodeHandler: RequestHandler = async (_, res) => {
|
|||||||
await WebUiDataRuntime.refreshQRCode();
|
await WebUiDataRuntime.refreshQRCode();
|
||||||
return sendSuccess(res, null);
|
return sendSuccess(res, null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 退出以重启重新登录
|
||||||
|
export const QQRestartHandler: RequestHandler = async (_, res) => {
|
||||||
|
sendSuccess(res, null);
|
||||||
|
setTimeout(() => {
|
||||||
|
writeFileSync(join(webUiPathWrapper.binaryPath, 'napcat.restart'), Date.now().toString());
|
||||||
|
process.exit(51);
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
getAutoLoginAccountHandler,
|
getAutoLoginAccountHandler,
|
||||||
setAutoLoginAccountHandler,
|
setAutoLoginAccountHandler,
|
||||||
QQRefreshQRcodeHandler,
|
QQRefreshQRcodeHandler,
|
||||||
|
QQRestartHandler,
|
||||||
} from '@/napcat-webui-backend/src/api/QQLogin';
|
} from '@/napcat-webui-backend/src/api/QQLogin';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
@ -31,5 +32,7 @@ router.post('/GetQuickLoginQQ', getAutoLoginAccountHandler);
|
|||||||
router.post('/SetQuickLoginQQ', setAutoLoginAccountHandler);
|
router.post('/SetQuickLoginQQ', setAutoLoginAccountHandler);
|
||||||
// router:刷新QQ登录二维码
|
// router:刷新QQ登录二维码
|
||||||
router.post('/RefreshQRcode', QQRefreshQRcodeHandler);
|
router.post('/RefreshQRcode', QQRefreshQRcodeHandler);
|
||||||
|
// router:重启QQ
|
||||||
|
router.post('/Restart', QQRestartHandler);
|
||||||
|
|
||||||
export { router as QQLoginRouter };
|
export { router as QQLoginRouter };
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { AxiosRequestConfig } from 'axios';
|
||||||
import { serverRequest } from '@/utils/request';
|
import { serverRequest } from '@/utils/request';
|
||||||
|
|
||||||
import { SelfInfo } from '@/types/user';
|
import { SelfInfo } from '@/types/user';
|
||||||
@ -71,9 +72,11 @@ export default class QQManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async getQQLoginInfo () {
|
public static async getQQLoginInfo (config?: AxiosRequestConfig) {
|
||||||
const data = await serverRequest.post<ServerResponse<SelfInfo>>(
|
const data = await serverRequest.post<ServerResponse<SelfInfo>>(
|
||||||
'/QQLogin/GetQQLoginInfo'
|
'/QQLogin/GetQQLoginInfo',
|
||||||
|
{},
|
||||||
|
config
|
||||||
);
|
);
|
||||||
return data.data.data;
|
return data.data.data;
|
||||||
}
|
}
|
||||||
@ -90,4 +93,8 @@ export default class QQManager {
|
|||||||
uin,
|
uin,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async reboot () {
|
||||||
|
await serverRequest.post<ServerResponse<null>>('/QQLogin/Restart');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Button } from '@heroui/button';
|
|||||||
import { useLocalStorage } from '@uidotdev/usehooks';
|
import { useLocalStorage } from '@uidotdev/usehooks';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { AnimatePresence, motion } from 'motion/react';
|
import { AnimatePresence, motion } from 'motion/react';
|
||||||
import { useEffect, useMemo, useRef } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { ErrorBoundary } from 'react-error-boundary';
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { MdMenu, MdMenuOpen } from 'react-icons/md';
|
import { MdMenu, MdMenuOpen } from 'react-icons/md';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
@ -11,7 +11,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
|
|||||||
import key from '@/const/key';
|
import key from '@/const/key';
|
||||||
|
|
||||||
import errorFallbackRender from '@/components/error_fallback';
|
import errorFallbackRender from '@/components/error_fallback';
|
||||||
// import PageLoading from "@/components/Loading/PageLoading";
|
import PageLoading from '@/components/page_loading';
|
||||||
import SideBar from '@/components/sidebar';
|
import SideBar from '@/components/sidebar';
|
||||||
|
|
||||||
import useAuth from '@/hooks/auth';
|
import useAuth from '@/hooks/auth';
|
||||||
@ -52,6 +52,7 @@ const Layout: React.FC<{ children: React.ReactNode; }> = ({ children }) => {
|
|||||||
const { isAuth, revokeAuth } = useAuth();
|
const { isAuth, revokeAuth } = useAuth();
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
const isOnlineRef = useRef(true);
|
const isOnlineRef = useRef(true);
|
||||||
|
const [isRestarting, setIsRestarting] = useState(false);
|
||||||
|
|
||||||
// 定期检查 QQ 在线状态,掉线时弹窗提示
|
// 定期检查 QQ 在线状态,掉线时弹窗提示
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -68,7 +69,39 @@ const Layout: React.FC<{ children: React.ReactNode; }> = ({ children }) => {
|
|||||||
content: '您的 QQ 账号已下线,请重新登录。',
|
content: '您的 QQ 账号已下线,请重新登录。',
|
||||||
confirmText: '重新登陆',
|
confirmText: '重新登陆',
|
||||||
cancelText: '退出账户',
|
cancelText: '退出账户',
|
||||||
onConfirm: () => navigate('/qq_login'),
|
onConfirm: async () => {
|
||||||
|
setIsRestarting(true);
|
||||||
|
try {
|
||||||
|
await QQManager.reboot();
|
||||||
|
} catch (_e) {
|
||||||
|
// 忽略错误,因为后端正在重启关闭连接
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询探测后端是否启动
|
||||||
|
const startTime = Date.now();
|
||||||
|
const maxWaitTime = 15000; // 15秒总超时
|
||||||
|
|
||||||
|
const timer = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
// 尝试请求后端,设置一个较短的请求超时避免挂起
|
||||||
|
await QQManager.getQQLoginInfo({ timeout: 500 });
|
||||||
|
// 如果能走到这一步说明请求成功了
|
||||||
|
clearInterval(timer);
|
||||||
|
setIsRestarting(false);
|
||||||
|
window.location.reload();
|
||||||
|
} catch (_e) {
|
||||||
|
// 如果请求失败(后端没起来),检查是否超时
|
||||||
|
if (Date.now() - startTime > maxWaitTime) {
|
||||||
|
clearInterval(timer);
|
||||||
|
setIsRestarting(false);
|
||||||
|
dialog.alert({
|
||||||
|
title: '启动超时',
|
||||||
|
content: '后端在 15 秒内未响应,请检查 NapCat 运行日志或手动重启。',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500); // 每 500ms 探测一次
|
||||||
|
},
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
revokeAuth();
|
revokeAuth();
|
||||||
navigate('/web_login');
|
navigate('/web_login');
|
||||||
@ -123,6 +156,7 @@ const Layout: React.FC<{ children: React.ReactNode; }> = ({ children }) => {
|
|||||||
backgroundPosition: 'center',
|
backgroundPosition: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<PageLoading loading={isRestarting} />
|
||||||
<SideBar
|
<SideBar
|
||||||
items={menus}
|
items={menus}
|
||||||
open={openSideBar}
|
open={openSideBar}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user