feat: msg push

This commit is contained in:
手瓜一十雪 2024-11-14 20:18:19 +08:00
parent a0a50755d3
commit 4487db4e0a
15 changed files with 345 additions and 245 deletions

View File

@ -8,12 +8,12 @@ export abstract class ConfigBase<T> {
configPath: string; configPath: string;
configData: T = {} as T; configData: T = {} as T;
protected constructor(name: string, core: NapCatCore, configPath: string) { protected constructor(name: string, core: NapCatCore, configPath: string, copy_default: boolean = true) {
this.name = name; this.name = name;
this.core = core; this.core = core;
this.configPath = configPath; this.configPath = configPath;
fs.mkdirSync(this.configPath, { recursive: true }); fs.mkdirSync(this.configPath, { recursive: true });
this.read(); this.read(copy_default);
} }
protected getKeys(): string[] | null { protected getKeys(): string[] | null {
@ -32,16 +32,18 @@ export abstract class ConfigBase<T> {
} }
} }
read(): T { read(copy_default: boolean = true): T {
const logger = this.core.context.logger; const logger = this.core.context.logger;
const configPath = this.getConfigPath(this.core.selfInfo.uin); const configPath = this.getConfigPath(this.core.selfInfo.uin);
if (!fs.existsSync(configPath)) { if (!fs.existsSync(configPath) && copy_default) {
try { try {
fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8')); fs.writeFileSync(configPath, fs.readFileSync(this.getConfigPath(undefined), 'utf-8'));
logger.log(`[Core] [Config] 配置文件创建成功!\n`); logger.log(`[Core] [Config] 配置文件创建成功!\n`);
} catch (e: any) { } catch (e: any) {
logger.logError.bind(logger)(`[Core] [Config] 创建配置文件时发生错误:`, e.message); logger.logError.bind(logger)(`[Core] [Config] 创建配置文件时发生错误:`, e.message);
} }
} else if (!fs.existsSync(configPath) && !copy_default) {
fs.writeFileSync(configPath, '{}');
} }
try { try {
this.configData = JSON.parse(fs.readFileSync(configPath, 'utf-8')); this.configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));

View File

@ -37,13 +37,13 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
}; };
} }
public async handle(payload: PayloadType): Promise<OB11Return<ReturnDataType | null>> { public async handle(payload: PayloadType, adaptername: string): Promise<OB11Return<ReturnDataType | null>> {
const result = await this.check(payload); const result = await this.check(payload);
if (!result.valid) { if (!result.valid) {
return OB11Response.error(result.message, 400); return OB11Response.error(result.message, 400);
} }
try { try {
const resData = await this._handle(payload); const resData = await this._handle(payload, adaptername);
return OB11Response.ok(resData); return OB11Response.ok(resData);
} catch (e: any) { } catch (e: any) {
this.core.context.logger.logError.bind(this.core.context.logger)('发生错误', e); this.core.context.logger.logError.bind(this.core.context.logger)('发生错误', e);
@ -51,13 +51,13 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
} }
} }
public async websocketHandle(payload: PayloadType, echo: any): Promise<OB11Return<ReturnDataType | null>> { public async websocketHandle(payload: PayloadType, echo: any, adaptername: string): Promise<OB11Return<ReturnDataType | null>> {
const result = await this.check(payload); const result = await this.check(payload);
if (!result.valid) { if (!result.valid) {
return OB11Response.error(result.message, 1400, echo); return OB11Response.error(result.message, 1400, echo);
} }
try { try {
const resData = await this._handle(payload); const resData = await this._handle(payload, adaptername);
return OB11Response.ok(resData, echo); return OB11Response.ok(resData, echo);
} catch (e: any) { } catch (e: any) {
this.core.context.logger.logError.bind(this.core.context.logger)('发生错误', e); this.core.context.logger.logError.bind(this.core.context.logger)('发生错误', e);
@ -65,7 +65,7 @@ abstract class BaseAction<PayloadType, ReturnDataType> {
} }
} }
abstract _handle(payload: PayloadType): PromiseLike<ReturnDataType>; abstract _handle(payload: PayloadType, adaptername: string): PromiseLike<ReturnDataType>;
} }
export default BaseAction; export default BaseAction;

View File

@ -72,7 +72,7 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
} }
const singleMsg = data.msgList[0]; const singleMsg = data.msgList[0];
const resMsg = await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array');//强制array 以便处理 const resMsg = (await this.obContext.apis.MsgApi.parseMessageV2(singleMsg))?.arrayMsg;//强制array 以便处理
if (!(resMsg?.message?.[0] as OB11MessageForward)?.data?.content) { if (!(resMsg?.message?.[0] as OB11MessageForward)?.data?.content) {
throw new Error('找不到相关的聊天记录'); throw new Error('找不到相关的聊天记录');
} }

View File

@ -26,7 +26,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
actionName = ActionName.GetFriendMsgHistory; actionName = ActionName.GetFriendMsgHistory;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<Response> { async _handle(payload: Payload, adapter: string): Promise<Response> {
//处理参数 //处理参数
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString()); const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
const MsgCount = +(payload.count ?? 20); const MsgCount = +(payload.count ?? 20);
@ -45,9 +45,10 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
await Promise.all(msgList.map(async msg => { await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
})); }));
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
//烘焙消息 //烘焙消息
const ob11MsgList = (await Promise.all( const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg))) msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array')))
).filter(msg => msg !== undefined); ).filter(msg => msg !== undefined);
return { 'messages': ob11MsgList }; return { 'messages': ob11MsgList };
} }

View File

@ -368,7 +368,7 @@ export class OneBotMsgApi {
multiMsgItem.parentMsgPeer = parentMsgPeer; multiMsgItem.parentMsgPeer = parentMsgPeer;
multiMsgItem.parentMsgIdList = msg.parentMsgIdList; multiMsgItem.parentMsgIdList = msg.parentMsgIdList;
multiMsgItem.id = MessageUnique.createUniqueMsgId(parentMsgPeer, multiMsgItem.msgId); //该ID仅用查看 无法调用 multiMsgItem.id = MessageUnique.createUniqueMsgId(parentMsgPeer, multiMsgItem.msgId); //该ID仅用查看 无法调用
return await this.parseMessage(multiMsgItem); return await this.parseMessage(multiMsgItem, 'array');
}, },
))).filter(item => item !== undefined), ))).filter(item => item !== undefined),
}, },
@ -693,7 +693,7 @@ export class OneBotMsgApi {
async parseMessage( async parseMessage(
msg: RawMessage, msg: RawMessage,
messagePostFormat: string = this.obContext.configLoader.configData.messagePostFormat, messagePostFormat: string,
) { ) {
if (msg.senderUin == '0' || msg.senderUin == '') return; if (msg.senderUin == '0' || msg.senderUin == '') return;
if (msg.peerUin == '0' || msg.peerUin == '') return; if (msg.peerUin == '0' || msg.peerUin == '') return;
@ -796,6 +796,110 @@ export class OneBotMsgApi {
return resMsg; return resMsg;
} }
async parseMessageV2(
msg: RawMessage,
) {
if (msg.senderUin == '0' || msg.senderUin == '') return;
if (msg.peerUin == '0' || msg.peerUin == '') return;
//跳过空消息
const resMsg: OB11Message = {
self_id: parseInt(this.core.selfInfo.uin),
user_id: parseInt(msg.senderUin),
time: parseInt(msg.msgTime) || Date.now(),
message_id: msg.id!,
message_seq: msg.id!,
real_id: msg.id!,
message_type: msg.chatType == ChatType.KCHATTYPEGROUP ? 'group' : 'private',
sender: {
user_id: +(msg.senderUin ?? 0),
nickname: msg.sendNickName,
card: msg.sendMemberName ?? '',
},
raw_message: '',
font: 14,
sub_type: 'friend',
message: [],
message_format: 'array',
post_type: this.core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
};
if (this.core.selfInfo.uin == msg.senderUin) {
resMsg.message_sent_type = 'self';
}
if (msg.chatType == ChatType.KCHATTYPEGROUP) {
resMsg.sub_type = 'normal'; // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼
resMsg.group_id = parseInt(msg.peerUin);
let member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (!member) member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin);
if (member) {
resMsg.sender.role = OB11Entities.groupMemberRole(member.role);
resMsg.sender.nickname = member.nick;
}
} else if (msg.chatType == ChatType.KCHATTYPEC2C) {
resMsg.sub_type = 'friend';
resMsg.sender.nickname = (await this.core.apis.UserApi.getUserDetailInfo(msg.senderUid)).nick;
} else if (msg.chatType == ChatType.KCHATTYPETEMPC2CFROMGROUP) {
resMsg.sub_type = 'group';
const ret = await this.core.apis.MsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid);
if (ret.result === 0) {
const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin);
resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode);
resMsg.sender.nickname = member?.nick ?? member?.cardName ?? '临时会话';
resMsg.temp_source = resMsg.group_id;
} else {
resMsg.group_id = 284840486; //兜底数据
resMsg.temp_source = resMsg.group_id;
resMsg.sender.nickname = '临时会话';
}
}
// 处理消息段
const msgSegments = await Promise.allSettled(msg.elements.map(
async (element) => {
for (const key in element) {
if (keyCanBeParsed(key, this.rawToOb11Converters) && element[key]) {
const converters = this.rawToOb11Converters[key] as (
element: Exclude<MessageElement[keyof RawToOb11Converters], null | undefined>,
msg: RawMessage,
elementWrapper: MessageElement,
) => PromiseLike<OB11MessageData | null>;
const parsedElement = await converters?.(
element[key],
msg,
element,
);
// 对于 face 类型的消息,检查是否存在
if (key === 'faceElement' && !parsedElement) {
return null; // 如果没有找到对应的表情,返回 null
}
return parsedElement;
}
}
},
));
// 过滤掉无效的消息段
const validSegments = msgSegments.filter(entry => {
if (entry.status === 'fulfilled') {
return !!entry.value;
} else {
this.core.context.logger.logError.bind(this.core.context.logger)('消息段解析失败', entry.reason);
return false;
}
}).map((entry) => (<PromiseFulfilledResult<OB11MessageData>>entry).value).filter(value => value != null);
const msgAsCQCode = validSegments.map(msg => encodeCQCode(msg)).join('').trim();
resMsg.message = validSegments;
resMsg.raw_message = msgAsCQCode;
let stringMsg = structuredClone(resMsg);
stringMsg = await this.importArrayTostringMsg(stringMsg);
return { stringMsg: stringMsg, arrayMsg: resMsg };
}
async importArrayTostringMsg(msg: OB11Message) {
msg.message_format = 'string';
msg.message = msg.raw_message;
return msg;
}
async createSendElements( async createSendElements(
messageData: OB11MessageData[], messageData: OB11MessageData[],
peer: Peer, peer: Peer,

View File

@ -1,6 +1,6 @@
export interface AdapterConfig { export interface AdapterConfig {
name: string; name: string;
enabled: boolean; enable: boolean;
[key: string]: any; [key: string]: any;
} }
@ -8,8 +8,8 @@ const createDefaultAdapterConfig = <T extends AdapterConfig>(config: T): T => co
const httpServerDefaultConfigs = createDefaultAdapterConfig({ const httpServerDefaultConfigs = createDefaultAdapterConfig({
name: 'http-server', name: 'http-server',
enabled: false, enable: false,
port: '3000', port: 3000,
host: '0.0.0.0', host: '0.0.0.0',
enableCors: true, enableCors: true,
enableWebsocket: true, enableWebsocket: true,
@ -22,7 +22,7 @@ export type HttpServerConfig = typeof httpServerDefaultConfigs;
const httpClientDefaultConfigs = createDefaultAdapterConfig({ const httpClientDefaultConfigs = createDefaultAdapterConfig({
name: 'http-client', name: 'http-client',
enabled: false, enable: false,
url: 'http://localhost:8080', url: 'http://localhost:8080',
messagePostFormat: 'array', messagePostFormat: 'array',
reportSelfMessage: false, reportSelfMessage: false,
@ -33,9 +33,9 @@ export type HttpClientConfig = typeof httpClientDefaultConfigs;
const websocketServerDefaultConfigs = createDefaultAdapterConfig({ const websocketServerDefaultConfigs = createDefaultAdapterConfig({
name: 'websocket-server', name: 'websocket-server',
enabled: false, enable: false,
host: '0.0.0.0', host: '0.0.0.0',
port: '3002', port: 3002,
messagePostFormat: 'array', messagePostFormat: 'array',
reportSelfMessage: false, reportSelfMessage: false,
token: '', token: '',
@ -47,7 +47,7 @@ export type WebsocketServerConfig = typeof websocketServerDefaultConfigs;
const websocketClientDefaultConfigs = createDefaultAdapterConfig({ const websocketClientDefaultConfigs = createDefaultAdapterConfig({
name: 'websocket-client', name: 'websocket-client',
enabled: false, enable: false,
url: 'ws://localhost:8082', url: 'ws://localhost:8082',
messagePostFormat: 'array', messagePostFormat: 'array',
reportSelfMessage: false, reportSelfMessage: false,
@ -71,6 +71,7 @@ export function mergeConfigs<T extends AdapterConfig>(defaultConfig: T, userConf
export interface OnebotConfig { export interface OnebotConfig {
network: NetworkConfig;//网络配置 network: NetworkConfig;//网络配置
musicSignUrl: string;//音乐签名地址 musicSignUrl: string;//音乐签名地址
enableLocalFile2Url: boolean
} }
const createDefaultConfig = <T>(config: T): T => config; const createDefaultConfig = <T>(config: T): T => config;
@ -82,7 +83,8 @@ export const defaultOnebotConfig = createDefaultConfig<OnebotConfig>({
websocketServers: [], websocketServers: [],
websocketClients: [], websocketClients: [],
}, },
musicSignUrl: "" musicSignUrl: "",
enableLocalFile2Url: false
}) })
export const mergeNetworkDefaultConfig = { export const mergeNetworkDefaultConfig = {
httpServers: httpServerDefaultConfigs, httpServers: httpServerDefaultConfigs,
@ -110,4 +112,4 @@ export function mergeOnebotConfigs(defaultConfig: OnebotConfig, userConfig: Part
mergedConfig.musicSignUrl = userConfig.musicSignUrl; mergedConfig.musicSignUrl = userConfig.musicSignUrl;
} }
return mergedConfig; return mergedConfig;
} }

View File

@ -1,11 +1,9 @@
import { ConfigBase } from '@/common/config-base'; import { ConfigBase } from '@/common/config-base';
import ob11DefaultConfig from './onebot11.json';
import { NapCatCore } from '@/core'; import { NapCatCore } from '@/core';
import { OnebotConfig } from './config';
export type OB11Config = typeof ob11DefaultConfig; export class OB11ConfigLoader extends ConfigBase<OnebotConfig> {
export class OB11ConfigLoader extends ConfigBase<OB11Config> {
constructor(core: NapCatCore, configPath: string) { constructor(core: NapCatCore, configPath: string) {
super('onebot11', core, configPath); super('onebot11', core, configPath, false);
} }
} }

View File

@ -1,31 +0,0 @@
{
"http": {
"enable": false,
"host": "",
"port": 3000,
"secret": "",
"enableHeart": false,
"enablePost": false,
"postUrls": []
},
"ws": {
"enable": false,
"host": "",
"port": 3001
},
"reverseWs": {
"enable": false,
"urls": []
},
"GroupLocalTime": {
"Record": false,
"RecordList": []
},
"debug": false,
"heartInterval": 30000,
"messagePostFormat": "array",
"enableLocalFile2Url": true,
"musicSignUrl": "",
"reportSelfMessage": false,
"token": ""
}

View File

@ -14,7 +14,7 @@ import {
RawMessage, RawMessage,
SendStatusType, SendStatusType,
} from '@/core'; } from '@/core';
import { OB11Config, OB11ConfigLoader } from '@/onebot/config'; import { OB11ConfigLoader } from '@/onebot/config';
import { import {
OB11ActiveHttpAdapter, OB11ActiveHttpAdapter,
OB11ActiveWebSocketAdapter, OB11ActiveWebSocketAdapter,
@ -45,6 +45,8 @@ import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecal
import { LRUCache } from '@/common/lru-cache'; import { LRUCache } from '@/common/lru-cache';
import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener'; import { NodeIKernelRecentContactListener } from '@/core/listeners/NodeIKernelRecentContactListener';
import { BotOfflineEvent } from './event/notice/BotOfflineEvent'; import { BotOfflineEvent } from './event/notice/BotOfflineEvent';
import { defaultOnebotConfig, mergeOnebotConfigs, OnebotConfig } from './config/config';
import { OB11Message } from './types';
//OneBot实现类 //OneBot实现类
export class NapCatOneBot11Adapter { export class NapCatOneBot11Adapter {
@ -61,7 +63,8 @@ export class NapCatOneBot11Adapter {
constructor(core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) { constructor(core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) {
this.core = core; this.core = core;
this.context = context; this.context = context;
this.configLoader = new OB11ConfigLoader(core, pathWrapper.configPath); this.configLoader = new OB11ConfigLoader(core, pathWrapper.configPath,);
this.configLoader.save(mergeOnebotConfigs(defaultOnebotConfig, this.configLoader.configData));
this.apis = { this.apis = {
GroupApi: new OneBotGroupApi(this, core), GroupApi: new OneBotGroupApi(this, core),
UserApi: new OneBotUserApi(this, core), UserApi: new OneBotUserApi(this, core),
@ -72,65 +75,77 @@ export class NapCatOneBot11Adapter {
this.actions = createActionMap(this, core); this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager(); this.networkManager = new OB11NetworkManager();
} }
async creatOneBotLog(ob11Config: OnebotConfig) {
let log = `[network] 配置加载\n`;
for (const key of ob11Config.network.httpServers) {
log += `HTTP服务: ${key.host}:${key.port}, : ${key.enable ? '已启动' : '未启动'}\n`;
}
for (const key of ob11Config.network.httpClients) {
log += `HTTP上报服务: ${key.url}, : ${key.enable ? '已启动' : '未启动'}\n`;
}
for (const key of ob11Config.network.websocketServers) {
log += `WebSocket服务: ${key.host}:${key.port}, : ${key.enable ? '已启动' : '未启动'}\n`;
}
for (const key of ob11Config.network.websocketClients) {
log += `WebSocket反向服务: ${key.url}, : ${key.enable ? '已启动' : '未启动'}\n`;
}
return log;
}
async InitOneBot() { async InitOneBot() {
const selfInfo = this.core.selfInfo; const selfInfo = this.core.selfInfo;
const ob11Config = this.configLoader.configData; const ob11Config = this.configLoader.configData;
const serviceInfo = `
HTTP服务 ${ob11Config.http.enable ? '已启动' : '未启动'}, ${ob11Config.http.host}:${ob11Config.http.port}
HTTP上报服务 ${ob11Config.http.enablePost ? '已启动' : '未启动'}, 上报地址: ${ob11Config.http.postUrls}
WebSocket服务 ${ob11Config.ws.enable ? '已启动' : '未启动'}, ${ob11Config.ws.host}:${ob11Config.ws.port}
WebSocket反向服务 ${ob11Config.reverseWs.enable ? '已启动' : '未启动'}, 反向地址: ${ob11Config.reverseWs.urls}`;
this.core.apis.UserApi.getUserDetailInfo(selfInfo.uid).then(user => { this.core.apis.UserApi.getUserDetailInfo(selfInfo.uid).then(user => {
selfInfo.nick = user.nick; selfInfo.nick = user.nick;
this.context.logger.setLogSelfInfo(selfInfo); this.context.logger.setLogSelfInfo(selfInfo);
}).catch(this.context.logger.logError.bind(this.context.logger)); }).catch(this.context.logger.logError.bind(this.context.logger));
let serviceInfo = await this.creatOneBotLog(ob11Config);
this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`); this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`);
//创建NetWork服务 // //创建NetWork服务
if (ob11Config.http.enable) { for (const key of ob11Config.network.httpServers) {
this.networkManager.registerAdapter(new OB11PassiveHttpAdapter( if (key.enable) {
ob11Config.http.port, ob11Config.token, this.core, this.actions, this.networkManager.registerAdapter(new OB11PassiveHttpAdapter(
)); key.name, key.port, key.token, this.core, this.actions,
));
}
} }
if (ob11Config.http.enablePost) { for (const key of ob11Config.network.httpClients) {
ob11Config.http.postUrls.forEach(url => { if (key.enable) {
this.networkManager.registerAdapter(new OB11ActiveHttpAdapter( this.networkManager.registerAdapter(new OB11ActiveHttpAdapter(
url, ob11Config.http.secret, this.core, this, key.name, key.url, key.token, this.core, this,
)); ));
}); }
} }
if (ob11Config.ws.enable) { for (const key of ob11Config.network.websocketServers) {
const OBPassiveWebSocketAdapter = new OB11PassiveWebSocketAdapter( if (key.enable) {
ob11Config.ws.host, ob11Config.ws.port, ob11Config.heartInterval, ob11Config.token, this.core, this.actions, this.networkManager.registerAdapter(new OB11PassiveWebSocketAdapter(
); key.name, key.host, key.port, key.heartInterval, key.token, this.core, this.actions,
this.networkManager.registerAdapter(OBPassiveWebSocketAdapter); ));
}
} }
if (ob11Config.reverseWs.enable) { for (const key of ob11Config.network.websocketClients) {
ob11Config.reverseWs.urls.forEach(url => { if (key.enable) {
this.networkManager.registerAdapter(new OB11ActiveWebSocketAdapter( this.networkManager.registerAdapter(new OB11ActiveWebSocketAdapter(
url, 5000, ob11Config.heartInterval, ob11Config.token, this.core, this.actions, key.name, key.url, 5000, key.heartInterval, key.token, this.core, this.actions,
)); ));
}); }
} }
await this.networkManager.openAllAdapters(); await this.networkManager.openAllAdapters();
this.initMsgListener(); this.initMsgListener();
this.initBuddyListener(); this.initBuddyListener();
this.initGroupListener(); this.initGroupListener();
//this.initRecentContactListener();
await WebUiDataRuntime.setQQLoginUin(selfInfo.uin.toString()); await WebUiDataRuntime.setQQLoginUin(selfInfo.uin.toString());
await WebUiDataRuntime.setQQLoginStatus(true); await WebUiDataRuntime.setQQLoginStatus(true);
await WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig: OB11Config) => { // await WebUiDataRuntime.setOnOB11ConfigChanged(async (newConfig: OB11Config) => {
const prev = this.configLoader.configData; // const prev = this.configLoader.configData;
this.configLoader.save(newConfig); // this.configLoader.save(newConfig);
this.context.logger.log(`OneBot11 配置更改:${JSON.stringify(prev)} -> ${JSON.stringify(newConfig)}`); // this.context.logger.log(`OneBot11 配置更改:${JSON.stringify(prev)} -> ${JSON.stringify(newConfig)}`);
await this.reloadNetwork(prev, newConfig); // await this.reloadNetwork(prev, newConfig);
}); // });
} }
initRecentContactListener() { initRecentContactListener() {
@ -144,88 +159,88 @@ export class NapCatOneBot11Adapter {
}; };
} }
private async reloadNetwork(prev: OB11Config, now: OB11Config) { // private async reloadNetwork(prev: OB11Config, now: OB11Config) {
const serviceInfo = ` // const serviceInfo = `
HTTP服务 ${now.http.enable ? '已启动' : '未启动'}, ${now.http.host}:${now.http.port} // HTTP服务 ${now.http.enable ? '已启动' : '未启动'}, ${now.http.host}:${now.http.port}
HTTP上报服务 ${now.http.enablePost ? '已启动' : '未启动'}, 上报地址: ${now.http.postUrls} // HTTP上报服务 ${now.http.enablePost ? '已启动' : '未启动'}, 上报地址: ${now.http.postUrls}
WebSocket服务 ${now.ws.enable ? '已启动' : '未启动'}, ${now.ws.host}:${now.ws.port} // WebSocket服务 ${now.ws.enable ? '已启动' : '未启动'}, ${now.ws.host}:${now.ws.port}
WebSocket反向服务 ${now.reverseWs.enable ? '已启动' : '未启动'}, 反向地址: ${now.reverseWs.urls}`; // WebSocket反向服务 ${now.reverseWs.enable ? '已启动' : '未启动'}, 反向地址: ${now.reverseWs.urls}`;
this.context.logger.log(`[Notice] [OneBot11] 热重载 ${serviceInfo}`); // this.context.logger.log(`[Notice] [OneBot11] 热重载 ${serviceInfo}`);
// check difference in passive http (Http) // // check difference in passive http (Http)
if (prev.http.enable !== now.http.enable) { // if (prev.http.enable !== now.http.enable) {
if (now.http.enable) { // if (now.http.enable) {
await this.networkManager.registerAdapterAndOpen(new OB11PassiveHttpAdapter( // await this.networkManager.registerAdapterAndOpen(new OB11PassiveHttpAdapter(
now.http.port, now.token, this.core, this.actions, // now.http.port, now.token, this.core, this.actions,
)); // ));
} else { // } else {
await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11PassiveHttpAdapter); // await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11PassiveHttpAdapter);
} // }
} // }
// check difference in active http (HttpPost) // // check difference in active http (HttpPost)
if (prev.http.enablePost !== now.http.enablePost) { // if (prev.http.enablePost !== now.http.enablePost) {
if (now.http.enablePost) { // if (now.http.enablePost) {
now.http.postUrls.forEach(url => { // now.http.postUrls.forEach(url => {
this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter( // this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter(
url, now.http.secret, this.core, this, // url, now.http.secret, this.core, this,
)); // ));
}); // });
} else { // } else {
await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11ActiveHttpAdapter); // await this.networkManager.closeAdapterByPredicate(adapter => adapter instanceof OB11ActiveHttpAdapter);
} // }
} else if (now.http.enablePost) { // } else if (now.http.enablePost) {
const { added, removed } = this.findDifference<string>(prev.http.postUrls, now.http.postUrls); // const { added, removed } = this.findDifference<string>(prev.http.postUrls, now.http.postUrls);
await this.networkManager.closeAdapterByPredicate( // await this.networkManager.closeAdapterByPredicate(
adapter => adapter instanceof OB11ActiveHttpAdapter && removed.includes(adapter.url), // adapter => adapter instanceof OB11ActiveHttpAdapter && removed.includes(adapter.url),
); // );
for (const url of added) { // for (const url of added) {
await this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter( // await this.networkManager.registerAdapterAndOpen(new OB11ActiveHttpAdapter(
url, now.http.secret, this.core, this, // url, now.http.secret, this.core, this,
)); // ));
} // }
} // }
// check difference in passive websocket (Ws) // // check difference in passive websocket (Ws)
if (prev.ws.enable !== now.ws.enable) { // if (prev.ws.enable !== now.ws.enable) {
if (now.ws.enable) { // if (now.ws.enable) {
await this.networkManager.registerAdapterAndOpen(new OB11PassiveWebSocketAdapter( // await this.networkManager.registerAdapterAndOpen(new OB11PassiveWebSocketAdapter(
now.ws.host, now.ws.port, now.heartInterval, now.token, this.core, this.actions, // now.ws.host, now.ws.port, now.heartInterval, now.token, this.core, this.actions,
)); // ));
} else { // } else {
await this.networkManager.closeAdapterByPredicate( // await this.networkManager.closeAdapterByPredicate(
adapter => adapter instanceof OB11PassiveWebSocketAdapter, // adapter => adapter instanceof OB11PassiveWebSocketAdapter,
); // );
} // }
} // }
// check difference in active websocket (ReverseWs) // // check difference in active websocket (ReverseWs)
if (prev.reverseWs.enable !== now.reverseWs.enable) { // if (prev.reverseWs.enable !== now.reverseWs.enable) {
if (now.reverseWs.enable) { // if (now.reverseWs.enable) {
now.reverseWs.urls.forEach(url => { // now.reverseWs.urls.forEach(url => {
this.networkManager.registerAdapterAndOpen(new OB11ActiveWebSocketAdapter( // this.networkManager.registerAdapterAndOpen(new OB11ActiveWebSocketAdapter(
url, 5000, now.heartInterval, now.token, this.core, this.actions, // url, 5000, now.heartInterval, now.token, this.core, this.actions,
)); // ));
}); // });
} else { // } else {
await this.networkManager.closeAdapterByPredicate( // await this.networkManager.closeAdapterByPredicate(
adapter => adapter instanceof OB11ActiveWebSocketAdapter, // adapter => adapter instanceof OB11ActiveWebSocketAdapter,
); // );
} // }
} else if (now.reverseWs.enable) { // } else if (now.reverseWs.enable) {
const { added, removed } = this.findDifference<string>(prev.reverseWs.urls, now.reverseWs.urls); // const { added, removed } = this.findDifference<string>(prev.reverseWs.urls, now.reverseWs.urls);
await this.networkManager.closeAdapterByPredicate( // await this.networkManager.closeAdapterByPredicate(
adapter => adapter instanceof OB11ActiveWebSocketAdapter && removed.includes(adapter.url), // adapter => adapter instanceof OB11ActiveWebSocketAdapter && removed.includes(adapter.url),
); // );
for (const url of added) { // for (const url of added) {
await this.networkManager.registerAdapterAndOpen(new OB11ActiveWebSocketAdapter( // await this.networkManager.registerAdapterAndOpen(new OB11ActiveWebSocketAdapter(
url, 5000, now.heartInterval, now.token, this.core, this.actions, // url, 5000, now.heartInterval, now.token, this.core, this.actions,
)); // ));
} // }
} // }
} // }
private findDifference<T>(prev: T[], now: T[]): { added: T[], removed: T[] } { private findDifference<T>(prev: T[], now: T[]): { added: T[], removed: T[] } {
const added = now.filter(item => !prev.includes(item)); const added = now.filter(item => !prev.includes(item));
@ -285,21 +300,12 @@ export class NapCatOneBot11Adapter {
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && msgIdSend.get(msg.msgId) == 0) { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && msgIdSend.get(msg.msgId) == 0) {
msgIdSend.put(msg.msgId, 1); msgIdSend.put(msg.msgId, 1);
// 完成后再post // 完成后再post
this.apis.MsgApi.parseMessage(msg) msg.id = MessageUnique.createUniqueMsgId({
.then((ob11Msg) => { chatType: msg.chatType,
if (!ob11Msg) return; peerUid: msg.peerUid,
ob11Msg.target_id = parseInt(msg.peerUin); guildId: '',
if (this.configLoader.configData.reportSelfMessage) { }, msg.msgId);
msg.id = MessageUnique.createUniqueMsgId({ this.emitMsg(msg, true);
chatType: msg.chatType,
peerUid: msg.peerUid,
guildId: '',
}, msg.msgId);
this.emitMsg(msg);
} else {
// logOB11Message(this.core, ob11Msg);
}
});
} }
} }
}; };
@ -491,56 +497,52 @@ export class NapCatOneBot11Adapter {
); );
} }
private async emitMsg(message: RawMessage, parseEvent: boolean = true) { private async emitMsg(message: RawMessage, selfMsg: boolean = true) {
const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData; let network = Object.values(this.configLoader.configData.network) as Array<typeof this.configLoader.configData.network[keyof typeof this.configLoader.configData.network]>;
this.context.logger.logDebug('收到新消息 RawMessage', message); this.context.logger.logDebug('收到新消息 RawMessage', message);
this.apis.MsgApi.parseMessage(message, messagePostFormat).then((ob11Msg) => { this.apis.MsgApi.parseMessageV2(message).then((ob11Msg) => {
if (!ob11Msg) return; if (!ob11Msg) return;
const isSelfMsg = ob11Msg.stringMsg.user_id.toString() == this.core.selfInfo.uin || ob11Msg.arrayMsg.user_id.toString() == this.core.selfInfo.uin;
this.context.logger.logDebug('转化为 OB11Message', ob11Msg); this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
if (debug) { let msgMap: Map<string, OB11Message> = new Map();
ob11Msg.raw = message; let enable_client: string[] = [];
} else if (ob11Msg.message.length === 0) { network.flat().filter(e => e.enable).map(e => {
enable_client.push(e.name);
if (e.messagePostFormat == 'string') {
msgMap.set(e.name, structuredClone(ob11Msg.stringMsg));
} else {
msgMap.set(e.name, structuredClone(ob11Msg.arrayMsg));
}
if (isSelfMsg) {
ob11Msg.stringMsg.target_id = parseInt(message.peerUin);
ob11Msg.arrayMsg.target_id = parseInt(message.peerUin);
}
});
let debug_network = network.flat().filter(e => e.enable && e.debug);
if (debug_network.length > 0) {
for (const adapter of debug_network) {
if (adapter.name) {
const msg = msgMap.get(adapter.name);
if (msg) {
msg.raw = message;
}
}
}
} else if (ob11Msg.stringMsg.message.length === 0 || ob11Msg.arrayMsg.message.length == 0) {
return; return;
} }
const isSelfMsg = ob11Msg.user_id.toString() == this.core.selfInfo.uin; let notreportSelf_network = network.flat().filter(e => e.enable && !e.reportSelfMessage);
if (isSelfMsg && !reportSelfMessage) {
return;
}
if (isSelfMsg) { if (isSelfMsg) {
ob11Msg.target_id = parseInt(message.peerUin); for (const adapter of notreportSelf_network) {
msgMap.delete(adapter.name);
}
} }
// if (ob11Msg.raw_message.startsWith('!set')) {
// this.core.apis.UserApi.getUidByUinV2(ob11Msg.user_id.toString()).then(uid => {
// if(uid){
// this.core.apis.PacketApi.sendSetSpecialTittlePacket(message.peerUin, uid, '测试');
// console.log('set', message.peerUin, uid);
// }
// }); this.networkManager.emitEventByNames(msgMap);
// }
// if (ob11Msg.raw_message.startsWith('!status')) {
// console.log('status', message.peerUin, message.senderUin);
// let delMsg: string[] = [];
// let peer = {
// peerUid: message.peerUin,
// chatType: 2,
// };
// this.core.apis.PacketApi.sendStatusPacket(+message.senderUin).then(async e => {
// if (e) {
// const { sendElements } = await this.apis.MsgApi.createSendElements([{
// type: OB11MessageDataType.text,
// data: {
// text: 'status ' + JSON.stringify(e, null, 2),
// }
// }], peer)
// this.apis.MsgApi.sendMsgWithOb11UniqueId(peer, sendElements, delMsg)
// }
// })
// }
this.networkManager.emitEvent(ob11Msg);
}).catch(e => this.context.logger.logError.bind(this.context.logger)('constructMessage error: ', e)); }).catch(e => this.context.logger.logError.bind(this.context.logger)('constructMessage error: ', e));
this.apis.GroupApi.parseGroupEvent(message).then(groupEvent => { this.apis.GroupApi.parseGroupEvent(message).then(groupEvent => {

View File

@ -10,6 +10,7 @@ export class OB11ActiveHttpAdapter implements IOB11NetworkAdapter {
isOpen: boolean = false; isOpen: boolean = false;
constructor( constructor(
public name: string,
public url: string, public url: string,
public secret: string | undefined, public secret: string | undefined,
public core: NapCatCore, public core: NapCatCore,

View File

@ -15,6 +15,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
private heartbeatRef: NodeJS.Timeout | null = null; private heartbeatRef: NodeJS.Timeout | null = null;
constructor( constructor(
public name: string,
public url: string, public url: string,
public reconnectIntervalInMillis: number, public reconnectIntervalInMillis: number,
public heartbeatIntervalInMillis: number, public heartbeatIntervalInMillis: number,
@ -147,7 +148,7 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter {
this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo)); this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo));
return; return;
} }
const retdata = await action.websocketHandle(receiveData.params, echo ?? ''); const retdata = await action.websocketHandle(receiveData.params, echo ?? '', this.name);
this.checkStateAndReply<any>({ ...retdata }); this.checkStateAndReply<any>({ ...retdata });
} }
} }

View File

@ -6,6 +6,7 @@ export type OB11EmitEventContent = OB11BaseEvent | OB11Message;
export interface IOB11NetworkAdapter { export interface IOB11NetworkAdapter {
actions?: ActionMap; actions?: ActionMap;
name: string;
onEvent<T extends OB11EmitEventContent>(event: T): void; onEvent<T extends OB11EmitEventContent>(event: T): void;
@ -15,19 +16,34 @@ export interface IOB11NetworkAdapter {
} }
export class OB11NetworkManager { export class OB11NetworkManager {
adapters: IOB11NetworkAdapter[] = []; adapters: Map<string, IOB11NetworkAdapter> = new Map();
async openAllAdapters() { async openAllAdapters() {
return Promise.all(this.adapters.map(adapter => adapter.open())); return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.open()));
} }
async emitEvent(event: OB11EmitEventContent) { async emitEvent(event: OB11EmitEventContent) {
//console.log('adapters', this.adapters.length); return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.onEvent(event)));
return Promise.all(this.adapters.map(adapter => adapter.onEvent(event)));
} }
async emitEventByName(names: string[], event: OB11EmitEventContent) {
return Promise.all(names.map(name => {
const adapter = this.adapters.get(name);
if (adapter) {
return adapter.onEvent(event);
}
}));
}
async emitEventByNames(map:Map<string,OB11EmitEventContent>){
return Promise.all(Array.from(map.entries()).map(([name, event]) => {
const adapter = this.adapters.get(name);
if (adapter) {
return adapter.onEvent(event);
}
}));
}
registerAdapter(adapter: IOB11NetworkAdapter) { registerAdapter(adapter: IOB11NetworkAdapter) {
this.adapters.push(adapter); this.adapters.set(adapter.name, adapter);
} }
async registerAdapterAndOpen(adapter: IOB11NetworkAdapter) { async registerAdapterAndOpen(adapter: IOB11NetworkAdapter) {
@ -36,24 +52,28 @@ export class OB11NetworkManager {
} }
async closeSomeAdapters(adaptersToClose: IOB11NetworkAdapter[]) { async closeSomeAdapters(adaptersToClose: IOB11NetworkAdapter[]) {
this.adapters = this.adapters.filter(adapter => !adaptersToClose.includes(adapter)); for (const adapter of adaptersToClose) {
await Promise.all(adaptersToClose.map(adapter => adapter.close())); this.adapters.delete(adapter.name);
await adapter.close();
}
}
findSomeAdapter(name: string) {
return this.adapters.get(name);
} }
/**
* Close all adapters that satisfy the predicate.
*/
async closeAdapterByPredicate(closeFilter: (adapter: IOB11NetworkAdapter) => boolean) { async closeAdapterByPredicate(closeFilter: (adapter: IOB11NetworkAdapter) => boolean) {
await this.closeSomeAdapters(this.adapters.filter(closeFilter)); const adaptersToClose = Array.from(this.adapters.values()).filter(closeFilter);
await this.closeSomeAdapters(adaptersToClose);
} }
async closeAllAdapters() { async closeAllAdapters() {
await Promise.all(this.adapters.map(adapter => adapter.close())); await Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.close()));
this.adapters = []; this.adapters.clear();
} }
} }
export * from './active-http'; export * from './active-http';
export * from './active-websocket'; export * from './active-websocket';
export * from './passive-http'; export * from './passive-http';
export * from './passive-websocket'; export * from './passive-websocket';

View File

@ -12,6 +12,7 @@ export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter {
private isOpen: boolean = false; private isOpen: boolean = false;
constructor( constructor(
public name: string,
public port: number, public port: number,
public token: string, public token: string,
public core: NapCatCore, public core: NapCatCore,
@ -101,7 +102,7 @@ export class OB11PassiveHttpAdapter implements IOB11NetworkAdapter {
const action = this.actions.get(actionName); const action = this.actions.get(actionName);
if (action) { if (action) {
try { try {
const result = await action.handle(payload); const result = await action.handle(payload,this.name);
return res.json(result); return res.json(result);
} catch (error: any) { } catch (error: any) {
return res.json(OB11Response.error(error?.stack?.toString() || error?.message || 'Error Handle', 200)); return res.json(OB11Response.error(error?.stack?.toString() || error?.message || 'Error Handle', 200));

View File

@ -24,6 +24,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
wsClientWithEvent: WebSocket[] = []; wsClientWithEvent: WebSocket[] = [];
constructor( constructor(
public name: string,
ip: string, ip: string,
port: number, port: number,
heartbeatInterval: number, heartbeatInterval: number,
@ -188,7 +189,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo), wsClient); this.checkStateAndReply<any>(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo), wsClient);
return; return;
} }
const retdata = await action.websocketHandle(receiveData.params, echo ?? ''); const retdata = await action.websocketHandle(receiveData.params, echo ?? '', this.name);
this.checkStateAndReply<any>({ ...retdata }, wsClient); this.checkStateAndReply<any>({ ...retdata }, wsClient);
} }
} }

View File

@ -24,7 +24,6 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
{ src: './src/core/external/napcat.json', dest: 'dist/config/' }, { src: './src/core/external/napcat.json', dest: 'dist/config/' },
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false }, { src: './src/native/packet', dest: 'dist/moehoo', flatten: false },
{ src: './static/', dest: 'dist/static/', flatten: false }, { src: './static/', dest: 'dist/static/', flatten: false },
{ src: './src/onebot/config/onebot11.json', dest: 'dist/config/' },
{ src: './src/framework/liteloader.cjs', dest: 'dist' }, { src: './src/framework/liteloader.cjs', dest: 'dist' },
{ src: './src/framework/napcat.cjs', dest: 'dist' }, { src: './src/framework/napcat.cjs', dest: 'dist' },
{ src: './src/framework/preload.cjs', dest: 'dist' }, { src: './src/framework/preload.cjs', dest: 'dist' },
@ -41,7 +40,6 @@ const ShellBaseConfigPlugin: PluginOption[] = [
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false }, { src: './src/native/packet', dest: 'dist/moehoo', flatten: false },
{ src: './static/', dest: 'dist/static/', flatten: false }, { src: './static/', dest: 'dist/static/', flatten: false },
{ src: './src/core/external/napcat.json', dest: 'dist/config/' }, { src: './src/core/external/napcat.json', dest: 'dist/config/' },
{ src: './src/onebot/config/onebot11.json', dest: 'dist/config/' },
{ src: './package.json', dest: 'dist' }, { src: './package.json', dest: 'dist' },
{ src: './launcher/', dest: 'dist', flatten: true }, { src: './launcher/', dest: 'dist', flatten: true },
...(startScripts.map((startScript) => { ...(startScripts.map((startScript) => {