This commit is contained in:
手瓜一十雪 2024-07-29 09:10:52 +08:00
parent 9837ef4f36
commit 330b086b8b
18 changed files with 119 additions and 114 deletions

View File

@ -42,7 +42,7 @@ export class WebsocketServerBase {
wss.emit('connection', ws, request); wss.emit('connection', ws, request);
}); });
}); });
log(`ws服务启动成功, 绑定到HTTP服务`); log('ws服务启动成功, 绑定到HTTP服务');
} catch (e: any) { } catch (e: any) {
throw Error('ws服务启动失败, 可能是绑定的HTTP服务异常' + e.toString()); throw Error('ws服务启动失败, 可能是绑定的HTTP服务异常' + e.toString());
} }

View File

@ -14,8 +14,8 @@ fs.mkdirSync(configDir, { recursive: true });
export class ConfigBase<T> { export class ConfigBase<T> {
public name: string = 'default_config' public name: string = 'default_config';
private pathName: string | null = null // 本次读取的文件路径 private pathName: string | null = null; // 本次读取的文件路径
constructor() { constructor() {
} }
@ -30,21 +30,21 @@ export class ConfigBase<T> {
return configDir; return configDir;
} }
getConfigPath(pathName: string | null): string { getConfigPath(pathName: string | null): string {
const suffix = pathName ? `_${pathName}` : '' const suffix = pathName ? `_${pathName}` : '';
const filename = `${this.name}${suffix}.json` const filename = `${this.name}${suffix}.json`;
return path.join(this.getConfigDir(), filename); return path.join(this.getConfigDir(), filename);
} }
read() { read() {
// 尝试加载当前账号配置 // 尝试加载当前账号配置
if (this.read_from_file(selfInfo.uin, false)) return this if (this.read_from_file(selfInfo.uin, false)) return this;
// 尝试加载默认配置 // 尝试加载默认配置
return this.read_from_file('', true) return this.read_from_file('', true);
} }
read_from_file(pathName: string, createIfNotExist: boolean) { read_from_file(pathName: string, createIfNotExist: boolean) {
const configPath = this.getConfigPath(pathName); const configPath = this.getConfigPath(pathName);
if (!fs.existsSync(configPath)) { if (!fs.existsSync(configPath)) {
if (!createIfNotExist) return null if (!createIfNotExist) return null;
this.pathName = pathName // 记录有效的设置文件 this.pathName = pathName; // 记录有效的设置文件
try { try {
fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2)); fs.writeFileSync(configPath, JSON.stringify(this, this.getKeys(), 2));
log(`配置文件${configPath}已创建\n如果修改此文件后需要重启 NapCat 生效`); log(`配置文件${configPath}已创建\n如果修改此文件后需要重启 NapCat 生效`);
@ -77,7 +77,7 @@ export class ConfigBase<T> {
Object.assign(this, config); Object.assign(this, config);
if (overwrite) { if (overwrite) {
// 用户要求强制写入,则变更当前文件为目标文件 // 用户要求强制写入,则变更当前文件为目标文件
this.pathName = `${selfInfo.uin}` this.pathName = `${selfInfo.uin}`;
} }
const configPath = this.getConfigPath(this.pathName); const configPath = this.getConfigPath(this.pathName);
try { try {

View File

@ -93,8 +93,8 @@ class MessageUniqueWrapper {
if (!heads) { if (!heads) {
return []; return [];
} }
let data = heads.map((t) => MessageUnique.getMsgIdAndPeerByShortId(t.value)); const data = heads.map((t) => MessageUnique.getMsgIdAndPeerByShortId(t.value));
let ret = data.filter((t) => t?.Peer.chatType === Peer.chatType && t?.Peer.peerUid === Peer.peerUid); const ret = data.filter((t) => t?.Peer.chatType === Peer.chatType && t?.Peer.peerUid === Peer.peerUid);
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined); return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
} }
createMsg(peer: Peer, msgId: string): number | undefined { createMsg(peer: Peer, msgId: string): number | undefined {

View File

@ -14,7 +14,7 @@ export function sleep(ms: number): Promise<void> {
export function PromiseTimer<T>(promise: Promise<T>, ms: number): Promise<T> { export function PromiseTimer<T>(promise: Promise<T>, ms: number): Promise<T> {
const timeoutPromise = new Promise<T>((_, reject) => const timeoutPromise = new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error("PromiseTimer: Operation timed out")), ms) setTimeout(() => reject(new Error('PromiseTimer: Operation timed out')), ms)
); );
return Promise.race([promise, timeoutPromise]); return Promise.race([promise, timeoutPromise]);
} }

View File

@ -15,7 +15,7 @@ export class FetchCustomFace extends BaseAction<Payload, string[]> {
actionName = ActionName.FetchCustomFace; actionName = ActionName.FetchCustomFace;
PayloadSchema = SchemaData; PayloadSchema = SchemaData;
protected async _handle(payload: Payload) { protected async _handle(payload: Payload) {
let ret = await NTQQMsgApi.fetchFavEmojiList(payload.count || 48); const ret = await NTQQMsgApi.fetchFavEmojiList(payload.count || 48);
return ret.emojiInfoList.map(e => e.url); return ret.emojiInfoList.map(e => e.url);
} }
} }

View File

@ -48,7 +48,7 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
const { enableLocalFile2Url } = ob11Config; const { enableLocalFile2Url } = ob11Config;
const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems; const NTSearchNameResult = (await NTQQFileApi.searchfile([payload.file])).resultItems;
if (NTSearchNameResult.length !== 0) { if (NTSearchNameResult.length !== 0) {
let MsgId = NTSearchNameResult[0].msgId; const MsgId = NTSearchNameResult[0].msgId;
let peer: Peer | undefined = undefined; let peer: Peer | undefined = undefined;
if (NTSearchNameResult[0].chatType == ChatType.group) { if (NTSearchNameResult[0].chatType == ChatType.group) {
peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode }; peer = { chatType: ChatType.group, peerUid: NTSearchNameResult[0].groupChatInfo[0].groupCode };
@ -56,12 +56,12 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
if (!peer) { if (!peer) {
throw new Error('chattype not support'); throw new Error('chattype not support');
} }
let msgList: RawMessage[] = (await NTQQMsgApi.getMsgsByMsgId(peer, [MsgId]))?.msgList; const msgList: RawMessage[] = (await NTQQMsgApi.getMsgsByMsgId(peer, [MsgId]))?.msgList;
if (!msgList || msgList.length == 0) { if (!msgList || msgList.length == 0) {
throw new Error('msg not found'); throw new Error('msg not found');
} }
let msg = msgList[0]; const msg = msgList[0];
let file = msg.elements.filter(e => e.elementType == NTSearchNameResult[0].elemType); const file = msg.elements.filter(e => e.elementType == NTSearchNameResult[0].elemType);
if (file.length == 0) { if (file.length == 0) {
throw new Error('file not found'); throw new Error('file not found');
} }

View File

@ -35,9 +35,9 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
const peer = { const peer = {
chatType: ChatType.group, chatType: ChatType.group,
peerUid: group.groupCode peerUid: group.groupCode
} };
if (!payload.message_seq) { if (!payload.message_seq) {
let latestMsgId = (await NTQQMsgApi.getLastestMsgByUids(peer)).msgList[0].msgId; const latestMsgId = (await NTQQMsgApi.getLastestMsgByUids(peer)).msgList[0].msgId;
targetMsgShortId = await MessageUnique.createMsg(peer, latestMsgId || '0'); targetMsgShortId = await MessageUnique.createMsg(peer, latestMsgId || '0');
} }
const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(targetMsgShortId ?? (payload.message_seq ?? 0)))?.MsgId || '0'; const startMsgId = (await MessageUnique.getMsgIdAndPeerByShortId(targetMsgShortId ?? (payload.message_seq ?? 0)))?.MsgId || '0';

View File

@ -35,7 +35,7 @@ export default class GoCQHTTPUploadPrivateFile extends BaseAction<Payload, null>
throw '缺少参数 user_id'; throw '缺少参数 user_id';
} }
protected async _handle(payload: Payload): Promise<null> { protected async _handle(payload: Payload): Promise<null> {
let peer = await this.getPeer(payload); const peer = await this.getPeer(payload);
let file = payload.file; let file = payload.file;
if (fs.existsSync(file)) { if (fs.existsSync(file)) {
file = `file://${file}`; file = `file://${file}`;

View File

@ -59,9 +59,9 @@ class GetGroupMemberInfo extends BaseAction<Payload, OB11GroupMember> {
} }
} }
} else { } else {
let LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]); const LastestMsgList = await NTQQGroupApi.getLastestMsg(payload.group_id.toString(), [payload.user_id.toString()]);
if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) { if (LastestMsgList?.msgList?.length && LastestMsgList?.msgList?.length > 0) {
let last_send_time = LastestMsgList.msgList[0].msgTime; const last_send_time = LastestMsgList.msgList[0].msgTime;
if (last_send_time && last_send_time != '0' && last_send_time != '') { if (last_send_time && last_send_time != '0' && last_send_time != '') {
retMember.last_sent_time = parseInt(last_send_time); retMember.last_sent_time = parseInt(last_send_time);
retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀 retMember.join_time = Math.round(Date.now() / 1000);//兜底数据 防止群管乱杀

View File

@ -39,7 +39,7 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>(); const MemberMap: Map<number, OB11GroupMember> = new Map<number, OB11GroupMember>();
// 转为Map 方便索引 // 转为Map 方便索引
let date = Math.round(Date.now() / 1000); const date = Math.round(Date.now() / 1000);
for (let i = 0, len = _groupMembers.length; i < len; i++) { for (let i = 0, len = _groupMembers.length; i < len; i++) {
// 保证基础数据有这个 同时避免群管插件过于依赖这个杀了 // 保证基础数据有这个 同时避免群管插件过于依赖这个杀了
_groupMembers[i].join_time = date; _groupMembers[i].join_time = date;
@ -65,7 +65,7 @@ class GetGroupMemberList extends BaseAction<Payload, OB11GroupMember[]> {
} }
} else { } else {
const DateMap = await NTQQGroupApi.getGroupMemberLastestSendTimeCache(payload.group_id.toString());//开始从本地拉取 const DateMap = await NTQQGroupApi.getGroupMemberLastestSendTimeCache(payload.group_id.toString());//开始从本地拉取
for (let DateUin of DateMap.keys()) { for (const DateUin of DateMap.keys()) {
const MemberData = MemberMap.get(parseInt(DateUin)); const MemberData = MemberMap.get(parseInt(DateUin));
if (MemberData) { if (MemberData) {
MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!); MemberData.last_sent_time = parseInt(DateMap.get(DateUin)!);

View File

@ -213,7 +213,7 @@ export default async function createSendElements(
ignoreTypes: OB11MessageDataType[] = [] ignoreTypes: OB11MessageDataType[] = []
) { ) {
const deleteAfterSentFiles: string[] = []; const deleteAfterSentFiles: string[] = [];
let callResultList: Array<Promise<SendMessageElement | undefined>> = []; const callResultList: Array<Promise<SendMessageElement | undefined>> = [];
for (const sendMsg of messageData) { for (const sendMsg of messageData) {
if (ignoreTypes.includes(sendMsg.type)) { if (ignoreTypes.includes(sendMsg.type)) {
continue; continue;
@ -224,7 +224,7 @@ export default async function createSendElements(
)?.catch(undefined); )?.catch(undefined);
callResultList.push(callResult); callResultList.push(callResult);
} }
let ret = await Promise.all(callResultList); const ret = await Promise.all(callResultList);
const sendElements: SendMessageElement[] = ret.filter(ele => ele) as SendMessageElement[]; const sendElements: SendMessageElement[] = ret.filter(ele => ele) as SendMessageElement[];
return { sendElements, deleteAfterSentFiles }; return { sendElements, deleteAfterSentFiles };
} }

View File

@ -99,7 +99,7 @@ export enum ActionName {
GetRecentContact = 'get_recent_contact', GetRecentContact = 'get_recent_contact',
_MarkAllMsgAsRead = '_mark_all_as_read', _MarkAllMsgAsRead = '_mark_all_as_read',
GetProfileLike = 'get_profile_like', GetProfileLike = 'get_profile_like',
SetGroupHeader = "set_group_head", SetGroupHeader = 'set_group_head',
FetchCustomFace = "fetch_custom_face", FetchCustomFace = 'fetch_custom_face',
GOCQHTTP_UploadPrivateFile = 'upload_private_file' GOCQHTTP_UploadPrivateFile = 'upload_private_file'
} }

View File

@ -42,7 +42,7 @@ export interface OB11Config {
} }
class Config extends ConfigBase<OB11Config> implements OB11Config { class Config extends ConfigBase<OB11Config> implements OB11Config {
name: string = 'onebot11' name: string = 'onebot11';
http = { http = {
enable: false, enable: false,
host: '', host: '',

View File

@ -12,12 +12,12 @@ import {
import { import {
AtType, AtType,
ChatType, ChatType,
ElementType, FaceIndex, FaceIndex,
Friend, Friend,
GrayTipElementSubType, GrayTipElementSubType,
Group, Group,
GroupMember, GroupMember,
IMAGE_HTTP_HOST, IMAGE_HTTP_HOST_NT, mFaceCache, mFaceCache,
Peer, Peer,
RawMessage, RawMessage,
SelfInfo, SelfInfo,
@ -76,7 +76,12 @@ export class OB11Constructor {
if (msg.chatType == ChatType.group) { if (msg.chatType == ChatType.group) {
resMsg.sub_type = 'normal'; // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼 resMsg.sub_type = 'normal'; // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼
resMsg.group_id = parseInt(msg.peerUin); resMsg.group_id = parseInt(msg.peerUin);
const member = await getGroupMember(msg.peerUin, msg.senderUin!); let member = await getGroupMember(msg.peerUin, msg.senderUin!);
if (!member) {
//直接去QQNative取
const memberList = await NTQQGroupApi.getGroupMembers(msg.peerUin);
member = memberList.get(msg.senderUin!);
}
if (member) { if (member) {
resMsg.sender.role = OB11Constructor.groupMemberRole(member.role); resMsg.sender.role = OB11Constructor.groupMemberRole(member.role);
resMsg.sender.nickname = member.nick; resMsg.sender.nickname = member.nick;
@ -138,12 +143,12 @@ export class OB11Constructor {
//log("收到回复消息", element.replyElement); //log("收到回复消息", element.replyElement);
try { try {
//做这么多都是因为NC速度太快 可能nt还没有写入数据库 //做这么多都是因为NC速度太快 可能nt还没有写入数据库
let records = msg.records.find(msgRecord => msgRecord.msgId === element.replyElement.sourceMsgIdInRecords); const records = msg.records.find(msgRecord => msgRecord.msgId === element.replyElement.sourceMsgIdInRecords);
if (!records) { if (!records) {
throw new Error('Record筛选失败'); throw new Error('Record筛选失败');
} }
let peer = { const peer = {
chatType: msg.chatType, chatType: msg.chatType,
peerUid: msg.peerUid, peerUid: msg.peerUid,
guildId: '', guildId: '',
@ -165,7 +170,7 @@ export class OB11Constructor {
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0]; replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0];
} }
if (!replyMsg || replyMsg.msgRandom !== records.msgRandom) { if (!replyMsg || replyMsg.msgRandom !== records.msgRandom) {
throw new Error('回复消息消息验证失败') throw new Error('回复消息消息验证失败');
} }
if (replyMsg) { if (replyMsg) {
message_data['data']['id'] = MessageUnique.createMsg({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, replyMsg.msgId)?.toString(); message_data['data']['id'] = MessageUnique.createMsg({ peerUid: msg.peerUid, guildId: '', chatType: msg.chatType }, replyMsg.msgId)?.toString();
@ -235,7 +240,7 @@ export class OB11Constructor {
let videoDownUrl = undefined; let videoDownUrl = undefined;
if (videoUrl) { if (videoUrl) {
let videoDownUrlTemp = videoUrl.find((url) => { if (url.url) { return true; } return false; }); const videoDownUrlTemp = videoUrl.find((url) => { if (url.url) { return true; } return false; });
if (videoDownUrlTemp) { if (videoDownUrlTemp) {
videoDownUrl = videoDownUrlTemp.url; videoDownUrl = videoDownUrlTemp.url;
} }
@ -269,7 +274,7 @@ export class OB11Constructor {
message_data['type'] = OB11MessageDataType.voice; message_data['type'] = OB11MessageDataType.voice;
message_data['data']['file'] = element.pttElement.fileName; message_data['data']['file'] = element.pttElement.fileName;
message_data['data']['path'] = element.pttElement.filePath; message_data['data']['path'] = element.pttElement.filePath;
message_data["data"]["file_id"] = element.pttElement.fileUuid; message_data['data']['file_id'] = element.pttElement.fileUuid;
message_data['data']['file_size'] = element.pttElement.fileSize; message_data['data']['file_size'] = element.pttElement.fileSize;
// dbUtil.addFileCache({ // dbUtil.addFileCache({
// name: element.pttElement.fileName, // name: element.pttElement.fileName,

View File

@ -41,7 +41,7 @@ export interface LineDevice {
device_name: string; device_name: string;
device_kind: string; device_kind: string;
} }
export let DeviceList = new Array<LineDevice>(); export const DeviceList = new Array<LineDevice>();
//peer->cached(boolen) //peer->cached(boolen)
// const PokeCache = new Map<string, boolean>(); // const PokeCache = new Map<string, boolean>();
@ -419,7 +419,7 @@ export class NapCatOnebot11 {
if (check_http_ws_equal(NewOb11) || check_http_ws_equal(OldConfig)) { if (check_http_ws_equal(NewOb11) || check_http_ws_equal(OldConfig)) {
// http与ws共站 需要同步重启 // http与ws共站 需要同步重启
if (isHttpChanged || isWsChanged) { if (isHttpChanged || isWsChanged) {
log("http与ws进行热重载") log('http与ws进行热重载');
ob11WebsocketServer.stop(); ob11WebsocketServer.stop();
ob11HTTPServer.stop(); ob11HTTPServer.stop();
if (NewOb11.http.enable) { if (NewOb11.http.enable) {
@ -436,7 +436,7 @@ export class NapCatOnebot11 {
} else { } else {
// http重启逻辑 // http重启逻辑
if (isHttpChanged) { if (isHttpChanged) {
log("http进行热重载") log('http进行热重载');
ob11HTTPServer.stop(); ob11HTTPServer.stop();
if (NewOb11.http.enable) { if (NewOb11.http.enable) {
ob11HTTPServer.start(NewOb11.http.port, NewOb11.http.host); ob11HTTPServer.start(NewOb11.http.port, NewOb11.http.host);
@ -445,7 +445,7 @@ export class NapCatOnebot11 {
// ws重启逻辑 // ws重启逻辑
if (isWsChanged) { if (isWsChanged) {
log("ws进行热重载") log('ws进行热重载');
ob11WebsocketServer.stop(); ob11WebsocketServer.stop();
if (NewOb11.ws.enable) { if (NewOb11.ws.enable) {
ob11WebsocketServer.start(NewOb11.ws.port, NewOb11.ws.host); ob11WebsocketServer.start(NewOb11.ws.port, NewOb11.ws.host);
@ -455,7 +455,7 @@ export class NapCatOnebot11 {
// 反向ws重启逻辑 // 反向ws重启逻辑
if (isWsReverseChanged) { if (isWsReverseChanged) {
log("反向ws进行热重载") log('反向ws进行热重载');
ob11ReverseWebsockets.stop(); ob11ReverseWebsockets.stop();
if (NewOb11.reverseWs.enable) { if (NewOb11.reverseWs.enable) {
ob11ReverseWebsockets.start(); ob11ReverseWebsockets.start();

View File

@ -23,7 +23,7 @@ async function tryUseHost(host: string): Promise<string> {
server.on('error', (err: any) => { server.on('error', (err: any) => {
if (err.code === 'EADDRNOTAVAIL') { if (err.code === 'EADDRNOTAVAIL') {
reject("主机地址验证失败,可能为非本机地址"); reject('主机地址验证失败,可能为非本机地址');
} else { } else {
reject(`遇到错误: ${err.code}`); reject(`遇到错误: ${err.code}`);
} }
@ -87,10 +87,10 @@ class WebUiConfigWrapper {
return this.WebUiConfigData; return this.WebUiConfigData;
} }
const defaultconfig: WebUiConfigType = { const defaultconfig: WebUiConfigType = {
host: "0.0.0.0", host: '0.0.0.0',
port: 6099, port: 6099,
prefix: "", prefix: '',
token: "", // 默认先填空,空密码无法登录 token: '', // 默认先填空,空密码无法登录
loginRate: 3 loginRate: 3
}; };
try { try {
@ -109,8 +109,8 @@ class WebUiConfigWrapper {
// 更新配置字段后新增字段可能会缺失,同步一下 // 更新配置字段后新增字段可能会缺失,同步一下
const parsedConfig = this.applyDefaults(JSON.parse(fileContent) as Partial<WebUiConfigType>, defaultconfig); const parsedConfig = this.applyDefaults(JSON.parse(fileContent) as Partial<WebUiConfigType>, defaultconfig);
if (!parsedConfig.prefix.startsWith("/")) parsedConfig.prefix = "/" + parsedConfig.prefix; if (!parsedConfig.prefix.startsWith('/')) parsedConfig.prefix = '/' + parsedConfig.prefix;
if (parsedConfig.prefix.endsWith("/")) parsedConfig.prefix = parsedConfig.prefix.slice(0, -1); if (parsedConfig.prefix.endsWith('/')) parsedConfig.prefix = parsedConfig.prefix.slice(0, -1);
// 配置已经被操作过了,还是回写一下吧,不然新配置不会出现在配置文件里 // 配置已经被操作过了,还是回写一下吧,不然新配置不会出现在配置文件里
writeFileSync(configPath, JSON.stringify(parsedConfig, null, 4)); writeFileSync(configPath, JSON.stringify(parsedConfig, null, 4));
// 不希望回写的配置放后面 // 不希望回写的配置放后面
@ -118,14 +118,14 @@ class WebUiConfigWrapper {
// 查询主机地址是否可用 // 查询主机地址是否可用
const [host_err, host] = await tryUseHost(parsedConfig.host).then(data => [null, data as string]).catch(err => [err, null]); const [host_err, host] = await tryUseHost(parsedConfig.host).then(data => [null, data as string]).catch(err => [err, null]);
if (host_err) { if (host_err) {
logError("host不可用", host_err) logError('host不可用', host_err);
parsedConfig.port = 0; // 设置为0禁用WebUI parsedConfig.port = 0; // 设置为0禁用WebUI
} else { } else {
parsedConfig.host = host; parsedConfig.host = host;
// 修正端口占用情况 // 修正端口占用情况
const [port_err, port] = await tryUsePort(parsedConfig.port, parsedConfig.host).then(data => [null, data as number]).catch(err => [err, null]); const [port_err, port] = await tryUsePort(parsedConfig.port, parsedConfig.host).then(data => [null, data as number]).catch(err => [err, null]);
if (port_err) { if (port_err) {
logError("port不可用", port_err) logError('port不可用', port_err);
parsedConfig.port = 0; // 设置为0禁用WebUI parsedConfig.port = 0; // 设置为0禁用WebUI
} else { } else {
parsedConfig.port = port; parsedConfig.port = port;