Refactor imports and add generic protocol config API

Replaced all '@/napcat-satori/...' imports with relative paths for consistency and compatibility. Added generic protocol config get/set handlers and routes in the web UI backend to support extensible protocol configuration management. Improved error handling and default value logic for Satori protocol configuration.
This commit is contained in:
手瓜一十雪 2026-01-14 16:01:29 +08:00
parent 506358e01a
commit 26d38bebe7
23 changed files with 139 additions and 43 deletions

View File

@ -1,5 +1,5 @@
import { NapCatCore } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { NapCatSatoriAdapter } from '../index';
export abstract class SatoriAction<PayloadType, ReturnType> {
abstract actionName: string;

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriChannel, SatoriChannelType } from '@/napcat-satori/types';
import { SatoriChannel, SatoriChannelType } from '../../types';
interface ChannelGetPayload {
channel_id: string;

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriChannel, SatoriChannelType, SatoriPageResult } from '@/napcat-satori/types';
import { SatoriChannel, SatoriChannelType, SatoriPageResult } from '../../types';
interface ChannelListPayload {
guild_id: string;

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriGuild } from '@/napcat-satori/types';
import { SatoriGuild } from '../../types';
interface GuildGetPayload {
guild_id: string;

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriGuild, SatoriPageResult } from '@/napcat-satori/types';
import { SatoriGuild, SatoriPageResult } from '../../types';
interface GuildListPayload {
next?: string;

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriGuildMember } from '@/napcat-satori/types';
import { SatoriGuildMember } from '../../types';
interface GuildMemberGetPayload {
guild_id: string;
@ -25,7 +25,7 @@ export class GuildMemberGetAction extends SatoriAction<GuildMemberGetPayload, Sa
avatar: `https://q1.qlogo.cn/g?b=qq&nk=${memberInfo.uin}&s=640`,
},
nick: memberInfo.cardName || memberInfo.nick,
joined_at: memberInfo.joinTime ? memberInfo.joinTime * 1000 : undefined,
joined_at: memberInfo.joinTime ? Number(memberInfo.joinTime) * 1000 : undefined,
};
}
}

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriGuildMember, SatoriPageResult } from '@/napcat-satori/types';
import { SatoriGuildMember, SatoriPageResult } from '../../types';
import { GroupMember } from 'napcat-core';
interface GuildMemberListPayload {

View File

@ -1,5 +1,5 @@
import { NapCatCore } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { NapCatSatoriAdapter } from '../index';
import { SatoriAction } from './SatoriAction';
// 导入所有 Action

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriMessage, SatoriChannelType } from '@/napcat-satori/types';
import { SatoriMessage, SatoriChannelType } from '../../types';
import { ChatType, SendMessageElement } from 'napcat-core';
interface MessageCreatePayload {

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriMessage, SatoriChannelType } from '@/napcat-satori/types';
import { SatoriMessage, SatoriChannelType } from '../../types';
import { ChatType } from 'napcat-core';
interface MessageGetPayload {
@ -13,7 +13,13 @@ export class MessageGetAction extends SatoriAction<MessageGetPayload, SatoriMess
async handle (payload: MessageGetPayload): Promise<SatoriMessage> {
const { channel_id, message_id } = payload;
const [type, id] = channel_id.split(':');
const parts = channel_id.split(':');
const type = parts[0];
const id = parts[1];
if (!type || !id) {
throw new Error(`无效的频道ID: ${channel_id}`);
}
let chatType: ChatType;
let peerUid: string;
@ -36,6 +42,10 @@ export class MessageGetAction extends SatoriAction<MessageGetPayload, SatoriMess
}
const msg = msgs.msgList[0];
if (!msg) {
throw new Error('消息不存在');
}
const content = await this.satoriAdapter.apis.MsgApi.parseElements(msg.elements);
const message: SatoriMessage = {

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriUser, SatoriPageResult } from '@/napcat-satori/types';
import { SatoriUser, SatoriPageResult } from '../../types';
interface FriendListPayload {
next?: string;

View File

@ -1,5 +1,5 @@
import { SatoriAction } from '../SatoriAction';
import { SatoriUser } from '@/napcat-satori/types';
import { SatoriUser } from '../../types';
interface UserGetPayload {
user_id: string;

View File

@ -1,10 +1,10 @@
import { NapCatCore, RawMessage, ChatType } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { NapCatSatoriAdapter } from '../index';
import {
SatoriEvent,
SatoriChannelType,
SatoriLoginStatus,
} from '@/napcat-satori/types';
} from '../types';
export class SatoriEventApi {
private satoriAdapter: NapCatSatoriAdapter;

View File

@ -1,5 +1,5 @@
import { NapCatCore } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { NapCatSatoriAdapter } from '../index';
import { SatoriMsgApi } from './msg';
import { SatoriEventApi } from './event';

View File

@ -1,5 +1,5 @@
import { NapCatCore, MessageElement, ElementType, NTMsgAtType } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { NapCatSatoriAdapter } from '../index';
export class SatoriMsgApi {
private core: NapCatCore;

View File

@ -12,10 +12,10 @@ import {
GroupNotifyMsgStatus,
GroupNotifyMsgType,
} from 'napcat-core';
import { SatoriConfigLoader, SatoriConfig, SatoriConfigSchema, SatoriNetworkAdapterConfig } from '@/napcat-satori/config';
import { SatoriConfigLoader, SatoriConfig, SatoriConfigSchema, SatoriNetworkAdapterConfig } from './config';
import { NapCatPathWrapper } from 'napcat-common/src/path';
import { createSatoriApis, SatoriApiList } from '@/napcat-satori/api';
import { createSatoriActionMap, SatoriActionMap } from '@/napcat-satori/action';
import { createSatoriApis, SatoriApiList } from './api';
import { createSatoriActionMap, SatoriActionMap } from './action';
import {
SatoriNetworkManager,
SatoriWebSocketServerAdapter,
@ -23,10 +23,11 @@ import {
SatoriWebHookClientAdapter,
SatoriNetworkReloadType,
ISatoriNetworkAdapter,
} from '@/napcat-satori/network';
import { SatoriLoginStatus } from '@/napcat-satori/types';
} from './network';
import { SatoriLoginStatus } from './types';
import { MessageUnique } from 'napcat-common/src/message-unique';
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
import { WebUiDataRuntime } from 'napcat-webui-backend/src/helper/Data';
export class NapCatSatoriAdapter {
readonly core: NapCatCore;
@ -101,6 +102,13 @@ export class NapCatSatoriAdapter {
// 发送登录成功事件
const loginEvent = this.apis.EventApi.createLoginUpdatedEvent(SatoriLoginStatus.ONLINE);
await this.networkManager.emitEvent(loginEvent);
// 注册 Satori 配置热重载回调
WebUiDataRuntime.setOnSatoriConfigChanged(async (newConfig) => {
const prev = this.configLoader.configData;
this.configLoader.save(newConfig);
await this.reloadNetwork(prev, newConfig);
});
}
async reloadNetwork (prev: SatoriConfig, now: SatoriConfig): Promise<void> {

View File

@ -1,9 +1,9 @@
import { SatoriNetworkAdapterConfig } from '@/napcat-satori/config/config';
import { SatoriNetworkAdapterConfig } from '../config/config';
import { LogWrapper } from 'napcat-core/helper/log';
import { NapCatCore } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { SatoriActionMap } from '@/napcat-satori/action';
import { SatoriEvent } from '@/napcat-satori/types';
import { NapCatSatoriAdapter } from '../index';
import { SatoriActionMap } from '../action';
import { SatoriEvent } from '../types';
export enum SatoriNetworkReloadType {
Normal = 0,

View File

@ -1,15 +1,15 @@
import express, { Express, Request, Response, NextFunction } from 'express';
import { createServer, Server } from 'http';
import { NapCatCore } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { SatoriActionMap } from '@/napcat-satori/action';
import { SatoriHttpServerConfig } from '@/napcat-satori/config/config';
import { NapCatSatoriAdapter } from '../index';
import { SatoriActionMap } from '../action';
import { SatoriHttpServerConfig } from '../config/config';
import {
ISatoriNetworkAdapter,
SatoriEmitEventContent,
SatoriNetworkReloadType,
} from './adapter';
import { SatoriApiResponse, SatoriLoginStatus } from '@/napcat-satori/types';
import { SatoriApiResponse, SatoriLoginStatus } from '../types';
export class SatoriHttpServerAdapter extends ISatoriNetworkAdapter<SatoriHttpServerConfig> {
private app: Express | null = null;

View File

@ -1,5 +1,5 @@
import { ISatoriNetworkAdapter, SatoriEmitEventContent, SatoriNetworkReloadType } from './adapter';
import { SatoriNetworkAdapterConfig } from '@/napcat-satori/config/config';
import { ISatoriNetworkAdapter, SatoriEmitEventContent } from './adapter';
import { SatoriNetworkAdapterConfig } from '../config/config';
export class SatoriNetworkManager {
adapters: Map<string, ISatoriNetworkAdapter<SatoriNetworkAdapterConfig>> = new Map();
@ -23,7 +23,7 @@ export class SatoriNetworkManager {
async openAllAdapters (): Promise<void> {
const openPromises = Array.from(this.adapters.values()).map((adapter) =>
adapter.open().catch((e) => {
Promise.resolve(adapter.open()).catch((e) => {
adapter.logger.logError(`[Satori] 适配器 ${adapter.name} 启动失败: ${e}`);
})
);
@ -32,7 +32,7 @@ export class SatoriNetworkManager {
async closeAllAdapters (): Promise<void> {
const closePromises = Array.from(this.adapters.values()).map((adapter) =>
adapter.close().catch((e) => {
Promise.resolve(adapter.close()).catch((e) => {
adapter.logger.logError(`[Satori] 适配器 ${adapter.name} 关闭失败: ${e}`);
})
);

View File

@ -1,7 +1,7 @@
import { NapCatCore } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { SatoriActionMap } from '@/napcat-satori/action';
import { SatoriWebHookClientConfig } from '@/napcat-satori/config/config';
import { NapCatSatoriAdapter } from '../index';
import { SatoriActionMap } from '../action';
import { SatoriWebHookClientConfig } from '../config/config';
import {
ISatoriNetworkAdapter,
SatoriEmitEventContent,

View File

@ -1,9 +1,9 @@
import { WebSocketServer, WebSocket } from 'ws';
import { createServer, Server, IncomingMessage } from 'http';
import { NapCatCore } from 'napcat-core';
import { NapCatSatoriAdapter } from '@/napcat-satori/index';
import { SatoriActionMap } from '@/napcat-satori/action';
import { SatoriWebSocketServerConfig } from '@/napcat-satori/config/config';
import { NapCatSatoriAdapter } from '../index';
import { SatoriActionMap } from '../action';
import { SatoriWebSocketServerConfig } from '../config/config';
import {
ISatoriNetworkAdapter,
SatoriEmitEventContent,
@ -15,7 +15,7 @@ import {
SatoriIdentifyBody,
SatoriReadyBody,
SatoriLoginStatus,
} from '@/napcat-satori/types';
} from '../types';
interface ClientInfo {
ws: WebSocket;

View File

@ -104,6 +104,78 @@ export const SatoriSetConfigHandler: RequestHandler = async (req, res) => {
}
};
// 获取指定协议配置
export const GetProtocolConfigHandler: RequestHandler = (req, res) => {
const isLogin = WebUiDataRuntime.getQQLoginStatus();
if (!isLogin) {
return sendError(res, 'Not Login');
}
const { name } = req.params;
const uin = WebUiDataRuntime.getQQLoginUin();
// 映射 protocol name 到文件名
const protocolId = name === 'onebot11' ? 'onebot11' : name;
const configFilePath = resolve(webUiPathWrapper.configPath, `./${protocolId}_${uin}.json`);
try {
let configData: any = {};
// Satori 特殊处理默认值, 其他协议也可以在这里添加默认值
if (name === 'satori') {
configData = {
network: {
websocketServers: [],
httpServers: [],
webhookClients: [],
},
platform: 'qq',
selfId: uin,
};
}
if (existsSync(configFilePath)) {
const content = readFileSync(configFilePath, 'utf-8');
configData = json5.parse(content);
}
return sendSuccess(res, configData);
} catch (e) {
return sendError(res, 'Config Get Error: ' + e);
}
};
// 设置指定协议配置
export const SetProtocolConfigHandler: RequestHandler = async (req, res) => {
const isLogin = WebUiDataRuntime.getQQLoginStatus();
if (!isLogin) {
return sendError(res, 'Not Login');
}
const { name } = req.params;
if (isEmpty(req.body.config)) {
return sendError(res, 'config is empty');
}
try {
const config = json5.parse(req.body.config);
if (name === 'satori') {
await WebUiDataRuntime.setSatoriConfig(config);
} else {
// 对于未特殊处理的协议,走通用的写文件逻辑
const uin = WebUiDataRuntime.getQQLoginUin();
// TODO: 这里目前 napcat-core 及其 helper 可能没有通用的 setConfig
// 但 WebUiDataRuntime.setSatoriConfig 本质也是写文件。
// 暂时只支持 Satori 的通用调用 via this handler,
// OneBot11 还是走原来的 /api/config
if (name !== 'satori') {
return sendError(res, 'Protocol not supported for generic set yet');
}
}
return sendSuccess(res, null);
} catch (e) {
return sendError(res, 'Error: ' + e);
}
};
// 获取所有协议配置
export const GetAllProtocolConfigsHandler: RequestHandler = (_req, res) => {
const isLogin = WebUiDataRuntime.getQQLoginStatus();

View File

@ -5,6 +5,8 @@ import {
SatoriGetConfigHandler,
SatoriSetConfigHandler,
GetAllProtocolConfigsHandler,
GetProtocolConfigHandler,
SetProtocolConfigHandler,
} from '@/napcat-webui-backend/src/api/ProtocolConfig';
const router = Router();
@ -18,8 +20,12 @@ router.get('/status', GetProtocolStatusHandler);
// 获取所有协议配置
router.get('/all', GetAllProtocolConfigsHandler);
// Satori 配置
// Satori 配置 (Reserved for backward compatibility or specific usage)
router.get('/satori', SatoriGetConfigHandler);
router.post('/satori', SatoriSetConfigHandler);
// 通用协议配置路由
router.get('/:name/config', GetProtocolConfigHandler);
router.post('/:name/config', SetProtocolConfigHandler);
export { router as ProtocolConfigRouter };