更新指令系统

main
落雨楓 2 years ago
parent c695a18587
commit f7e53aa87f

@ -19,6 +19,8 @@ export default class App {
public config: Config;
public srcPath: string = __dirname;
public debug: boolean = false;
public event!: EventManager;
public robot!: RobotManager;
public provider!: ProviderManager;
@ -30,6 +32,8 @@ export default class App {
constructor(configFile: string) {
this.config = Yaml.parse(fs.readFileSync(configFile, { encoding: 'utf-8' }));
this.debug = this.config.debug;
this.initialize();
}
@ -83,13 +87,6 @@ export default class App {
async initChannelManager() {
this.channel = new ChannelManager(this, this.config.channel_config_path);
this.channel.on('add', (channelId) => {
this.subscribe.addChannel(channelId);
});
this.channel.on('remove', (channelId) => {
this.subscribe.removeChannel(channelId);
});
await this.channel.initialize();
}
@ -111,8 +108,8 @@ export default class App {
return this.provider.create(provider, channelId, config);
}
getSubscriber(channelId: string, robotId: string): Target[] | null {
return this.subscribe.getSubscriber(channelId, robotId);
getChannelSubscriber(channelId: string, robotId: string): Target[] | null {
return this.subscribe.getSubscriber('channel:' + channelId, robotId);
}
/**

@ -6,9 +6,11 @@ export type Config = {
plugin_path: string;
subscribe_config: string;
debug: boolean;
robot: { [key: string]: RobotConfig };
service: { [key: string]: ServiceConfig };
robot: Record<string, RobotConfig>;
service: Record<string, ServiceConfig>;
http_api: RestfulApiConfig;
command_override: CommandOverrideConfig;
focused_as_command: true;
};
export type RobotConfig = {
@ -31,3 +33,11 @@ export type GeneratorConfig = {
match: RegexFilterConfig;
tpl: any;
};
export type CommandOverrideConfig = {
[command: string]: {
name?: string;
help?: string;
alias?: string[];
}
};

@ -1,19 +1,24 @@
import App from "./App";
import { CommonReceivedMessage, CommonSendMessage } from "./message/Message";
import { GroupSender, UserSender } from "./message/Sender";
import { ControllerSubscribeSource, MessageEventOptions, MessagePriority, PluginController } from "./PluginManager";
import { CommandInfo, ControllerSubscribeSource, MessageEventOptions, MessagePriority, PluginController, PluginEvent } from "./PluginManager";
import { Robot } from "./RobotManager";
export type PluginControllerListenerInfo = {
priority: number;
callback: CallableFunction;
controller: PluginController;
controllerEvent: PluginEvent;
}
export type PluginControllerCommandInfo = {
commandInfo: CommandInfo;
controllerEvent: PluginEvent;
}
export class EventManager {
private app: App;
private eventSortDebounce: Record<string, NodeJS.Timeout> = {};
private eventList: Record<string, PluginControllerListenerInfo[]> = {};
private commandList: Record<string, PluginControllerCommandInfo> = {};
constructor(app: App) {
this.app = app;
@ -23,7 +28,7 @@ export class EventManager {
}
on(event: string, controller: PluginController, callback: CallableFunction, options?: MessageEventOptions) {
public on(event: string, controllerEvent: PluginEvent, callback: CallableFunction, options?: MessageEventOptions) {
if (!(event in this.eventList)) {
this.eventList[event] = [];
}
@ -43,7 +48,7 @@ export class EventManager {
const eventInfo = {
callback: callback,
priority: options.priority!,
controller: controller
controllerEvent
};
this.eventList[event].push(eventInfo);
@ -51,42 +56,112 @@ export class EventManager {
this.sortEvent(event);
}
off(event: string, controller: PluginController, callback: CallableFunction): void
off(controller: PluginController): void
off(...args: any): void {
public off(event: string, controllerEvent: PluginEvent, callback: CallableFunction): void
public off(controllerEvent: PluginEvent): void
public off(...args: any): void {
if (typeof args[0] === 'string') {
let [event, controller, callback] = args;
if (Array.isArray(this.eventList[event])) {
this.eventList[event] = this.eventList[event].filter((eventInfo) => eventInfo.callback !== callback || eventInfo.controller !== controller);
this.eventList[event] = this.eventList[event].filter((eventInfo) => eventInfo.callback !== callback || eventInfo.controllerEvent !== controller);
}
} else if (typeof args[0] !== 'undefined') {
let controller = args[0];
for (let event in this.eventList) {
this.eventList[event] = this.eventList[event].filter((eventInfo) => eventInfo.controller !== controller);
this.eventList[event] = this.eventList[event].filter((eventInfo) => eventInfo.controllerEvent !== controller);
}
}
}
public async emit(eventName: string, senderInfo: ControllerSubscribeSource, ...args: any[]) {
public addCommand(commandInfo: CommandInfo, controllerEvent: PluginEvent) {
let data = {
commandInfo,
controllerEvent: controllerEvent
};
this.commandList[commandInfo.command] = data;
if (Array.isArray(commandInfo.alias)) {
commandInfo.alias.forEach((alias) => {
this.commandList[alias] = data;
});
}
}
public removeCommand(commandInfo: CommandInfo): void
public removeCommand(controllerEvent: PluginEvent): void
public removeCommand(...args: any): void {
if ('command' in args[0]) {
let commandInfo: CommandInfo = args[0];
delete this.commandList[commandInfo.command];
if (Array.isArray(commandInfo.alias)) {
commandInfo.alias.forEach((alias) => {
delete this.commandList[alias];
});
}
} else if (typeof args[0] !== 'undefined') {
let controllerEvent = args[0];
for (let command in this.commandList) {
if (this.commandList[command].controllerEvent.controller?.id === controllerEvent.controller?.id) {
delete this.commandList[command];
}
}
}
}
public async emit(eventName: string, senderInfo?: ControllerSubscribeSource | null, ...args: any[]) {
if (this.app.debug) {
if (args[0] instanceof CommonReceivedMessage) {
console.log(`[DEBUG] 触发事件 ${eventName} ${args[0].contentText}`);
} else {
console.log(`[DEBUG] 触发事件 ${eventName}`);
}
}
const eventList = this.eventList[eventName];
if (!eventList) return false;
const isFilter = eventName.startsWith('filter/');
let isResolved = false;
const resolved = () => {
isResolved = true;
};
let subscribeList: string[] = [];
if (senderInfo) {
// 获取订阅列表
let targetType = '';
let targetId = '';
switch (senderInfo.type) {
case 'private':
targetType = 'user';
targetId = senderInfo.userId!;
break;
case 'group':
targetType = 'group';
targetId = senderInfo.groupId!;
break;
case 'channel':
targetType = 'channel';
targetId = senderInfo.channelId!;
break;
}
subscribeList = this.app.subscribe.getSubscribedList(senderInfo.robot.robotId!, targetType, targetId, 'controller');
}
for (let eventInfo of eventList) {
if (eventInfo.controller.autoSubscribe) {
if (!eventInfo.controller.isAllowSubscribe(senderInfo)) {
continue;
} else {
// 需要添加订阅检测
if (!isFilter && senderInfo) {
if (eventInfo.controllerEvent.autoSubscribe) {
if (!eventInfo.controllerEvent.isAllowSubscribe(senderInfo)) {
continue;
} else {
// 需要添加订阅检测
}
} else if (senderInfo.type !== 'private') {
if (!eventInfo.controllerEvent.controller || !subscribeList.includes(eventInfo.controllerEvent.controller.id)) {
continue;
}
}
} else {
// 需要添加订阅检测
continue;
}
try {
@ -107,6 +182,11 @@ export class EventManager {
let isResolved = false;
if (message.origin === 'private' || (message.origin === 'group' && message.mentionedReceiver)) {
if (this.app.config.focused_as_command) {
isResolved = await this.emitCommand(message.contentText, message);
if (isResolved) return true;
}
isResolved = await this.emit(`message/focused`, this.getSenderInfo(message), message);
if (isResolved) return true;
}
@ -120,7 +200,41 @@ export class EventManager {
return false;
}
public async emitCommand(command: string, args: string, message: CommonReceivedMessage) {
public async emitCommand(contentText: string, message: CommonReceivedMessage) {
let command = '';
let args = '';
// 尝试识别空格分隔的指令
if (contentText.includes(' ')) {
command = contentText.split(' ')[0];
args = contentText.substring(command.length + 1);
if (!(command in this.commandList)) {
command = '';
}
}
// 尝试使用最长匹配查找指令
if (command.length === 0) {
for (let registeredCommand in this.commandList) {
if (contentText.startsWith(registeredCommand)) {
if (registeredCommand.length > command.length) {
command = registeredCommand;
}
}
}
if (command.length === 0) {
return false;
}
args = contentText.substring(command.length);
}
if (this.app.debug) {
console.log('[DEBUG] 指令识别结果', command, args);
}
return await this.emit(`command/${command}`, this.getSenderInfo(message), args, message);
}
@ -130,7 +244,12 @@ export class EventManager {
public async emitRawMessage(message: CommonReceivedMessage) {
let isResolved = false;
await this.emit(`filter/message`, null, message);
isResolved = await this.emit(`raw/${message.receiver.type}/message`, this.getSenderInfo(message), message);
if (isResolved) return true;
return await this.emit('raw/message', this.getSenderInfo(message), message);
}

@ -89,7 +89,7 @@ export class PluginManager extends EventEmitter {
}
async loadController(file: string) {
if (!file.match(/\.m?js$/)) return;
if (!file.match(/Controller\.m?js$/)) return;
let moduleName = path.resolve(file).replace(/\\/g, '/').replace(/\.m?js$/, '');
@ -97,7 +97,7 @@ export class PluginManager extends EventEmitter {
const controller = await import(moduleName);
if (controller) {
const controllerClass = controller.default ?? controller;
const controllerInstance: PluginController = new controllerClass(this.app, this.app.event);
const controllerInstance: PluginController = new controllerClass(this.app);
if (controllerInstance.id && controllerInstance.id !== '') {
const controllerId = controllerInstance.id;
@ -118,6 +118,8 @@ export class PluginManager extends EventEmitter {
this.emit('controllerLoaded', controllerInstance);
}
const pluginMisc = new PluginEvent(this.app.event);
controllerInstance.event = pluginMisc;
await controllerInstance.initialize();
} else {
throw new Error('PluginController ID is not defined.');
@ -134,7 +136,8 @@ export class PluginManager extends EventEmitter {
async removeController(file: string, isReload = false) {
const controller = this.fileControllers[file];
if (controller) {
await controller.destroy();
await controller.event.destroy();
await controller.destroy?.();
delete this.controllers[file];
delete this.fileControllers[file];
@ -147,14 +150,22 @@ export class PluginManager extends EventEmitter {
}
}
export class PluginController {
public id: string = '';
public name: string = '未命名功能';
public description: string = '';
export interface PluginController {
id: string;
name: string;
description?: string;
private app: App;
event: PluginEvent;
initialize: () => Promise<void>;
destroy?: () => Promise<void>;
}
export class PluginEvent {
private eventManager: EventManager;
public controller?: PluginController;
public autoSubscribe = false;
public forceSubscribe = false;
public showInSubscribeList = true;
@ -168,8 +179,7 @@ export class PluginController {
private commandList: CommandInfo[] = [];
private eventList: Record<string, EventListenerInfo[]> = {};
constructor(app: App, eventManager: EventManager) {
this.app = app;
constructor(eventManager: EventManager) {
this.eventManager = eventManager;
}
@ -204,21 +214,21 @@ export class PluginController {
* @param callback Callback function
* @param options Options
*/
protected on(event: 'message/private', callback: MessageCallback, options?: MessageEventOptions): void
public on(event: 'message/private', callback: MessageCallback, options?: MessageEventOptions): void
/**
* Add group message handler.
* @param event Event name
* @param callback Callback function
* @param options Options
*/
protected on(event: 'message/group', callback: MessageCallback, options?: MessageEventOptions): void
public on(event: 'message/group', callback: MessageCallback, options?: MessageEventOptions): void
/**
* Add channel message handler.
* @param event Event name
* @param callback Callback function
* @param options Options
*/
protected on(event: 'message/channel', callback: MessageCallback, options?: MessageEventOptions): void
public on(event: 'message/channel', callback: MessageCallback, options?: MessageEventOptions): void
/**
* Add message handle.
* will be trigger on private message or group message with mentions to robot
@ -226,7 +236,7 @@ export class PluginController {
* @param callback Callback function
* @param options Options
*/
protected on(event: 'message/focused', callback: MessageCallback, options?: MessageEventOptions): void
public on(event: 'message/focused', callback: MessageCallback, options?: MessageEventOptions): void
/**
* Add message handler.
* Will handle all messages (group, private, channel)
@ -234,7 +244,7 @@ export class PluginController {
* @param callback Callback function
* @param options Options
*/
protected on(event: 'message', callback: MessageCallback, options?: MessageEventOptions): void
public on(event: 'message', callback: MessageCallback, options?: MessageEventOptions): void
/**
* Add raw message handler.
* Will be triggered even when the message is a command.
@ -242,28 +252,28 @@ export class PluginController {
* @param callback Callback function
* @param options Options
*/
protected on(event: 'raw/message', callback: MessageCallback, options?: MessageEventOptions): void
public on(event: 'raw/message', callback: MessageCallback, options?: MessageEventOptions): void
/**
* Add robot raw event handler.
* @param event Event name
* @param callback Callback function
* @param options Options
*/
protected on(event: 'raw/event', callback: RawEventCallback, options?: MessageEventOptions): void
public on(event: 'raw/event', callback: RawEventCallback, options?: MessageEventOptions): void
/**
* Add other event handler.
* @param event Event name
* @param callback Callback function
* @param options Options
*/
protected on(event: string, callback: CallableFunction, options?: MessageEventOptions): void
public on(event: string, callback: CallableFunction, options?: MessageEventOptions): void
/**
* Add event handler.
* @param event Event name
* @param callback Callback function
* @param options Options
*/
protected on(event: string, callback: CallableFunction, options?: MessageEventOptions): void {
public on(event: string, callback: CallableFunction, options?: MessageEventOptions): void {
if (!(event in this.eventList)) {
this.eventList[event] = [];
}
@ -289,7 +299,7 @@ export class PluginController {
this.eventManager.on(event, this, callback, options);
}
protected off(event: string, callback: CallableFunction): void {
public off(event: string, callback: CallableFunction): void {
if (Array.isArray(this.eventList[event])) {
this.eventList[event] = this.eventList[event].filter((eventInfo) => {
return eventInfo.callback !== callback;
@ -306,9 +316,9 @@ export class PluginController {
* @param callback
* @param options
*/
protected registerCommand(command: string, name: string, callback: CommandCallback, options?: MessageEventOptions): void
protected registerCommand(commandInfo: CommandInfo, callback: CommandCallback, options?: MessageEventOptions): void
protected registerCommand(...args: any[]): void {
public registerCommand(command: string, name: string, callback: CommandCallback, options?: MessageEventOptions): void
public registerCommand(commandInfo: CommandInfo, callback: CommandCallback, options?: MessageEventOptions): void
public registerCommand(...args: any[]): void {
// 处理传入参数
let commandInfo: Partial<CommandInfo> = {};
let callback: MessageCallback;
@ -334,6 +344,8 @@ export class PluginController {
this.on(`command/${cmd}`, callback, options);
});
}
this.eventManager.addCommand(commandInfo as any, this);
}
/**
@ -349,6 +361,7 @@ export class PluginController {
*/
public async destroy() {
this.eventManager.off(this);
this.eventManager.removeCommand(this);
this.eventList = {};
}

@ -98,7 +98,7 @@ export class RobotManager {
continue;
}
let targets = this.app.getSubscriber(channelId, robotId);
let targets = this.app.getChannelSubscriber(channelId, robotId);
if (!targets) {
continue;
}

@ -9,27 +9,31 @@ export interface Target {
identity: string;
}
export type SubscribeConfig = {
[robotId: string]: {
[targetType: string]: {
[targetIdentity: string]: {
[sourceType: string]: string[]
}
}
}
}
/**
*
* @todo
*/
export class SubscribeManager {
private app: App;
private subscribeFile: string;
private watcher!: chokidar.FSWatcher;
private subscribeList: {
[channelId: string]: {
[sourceId: string]: {
[robotId: string]: Target[]
}
};
private subscribeConfig: {
[robotId: string]: {
[targetType: string]: {
[targetIdentity: string]: string[]
}
}
};
private subscribeConfig: SubscribeConfig;
constructor(app: App, subscribeFile: string) {
this.app = app;
@ -76,8 +80,15 @@ export class SubscribeManager {
let targetTypeConf = targetConf[targetType];
for (let targetId in targetTypeConf) {
let subscribeList = targetTypeConf[targetId];
for (let channelId of subscribeList) {
this.addSubscribe(robotId, targetType, targetId, channelId);
if (subscribeList.channel) {
for (let sourceId in subscribeList.channel) {
this.addSubscribe(robotId, targetType, targetId, 'channel:' + sourceId);
}
}
if (subscribeList.controller) {
for (let controllerId in subscribeList.controller) {
this.addSubscribe(robotId, targetType, targetId, 'controller:' + controllerId);
}
}
}
}
@ -87,15 +98,15 @@ export class SubscribeManager {
/**
*
* @param robotId
* @param channelId
* @param sourceId
*/
public prepareTree(robotId: string, channelId: string) {
if (!(channelId in this.subscribeList)) {
this.subscribeList[channelId] = {};
public prepareTree(robotId: string, sourceId: string) {
if (!(sourceId in this.subscribeList)) {
this.subscribeList[sourceId] = {};
}
if (!(robotId in this.subscribeList[channelId])) {
this.subscribeList[channelId][robotId] = [];
if (!(robotId in this.subscribeList[sourceId])) {
this.subscribeList[sourceId][robotId] = [];
}
}
@ -104,11 +115,11 @@ export class SubscribeManager {
* @param robotId ID
* @param targetType
* @param targetId ID
* @param channelId ID
* @param sourceId ID
*/
public addSubscribe(robotId: string, targetType: string, targetId: string, channelId: string) {
this.prepareTree(robotId, channelId);
this.subscribeList[channelId][robotId].push({
public addSubscribe(robotId: string, targetType: string, targetId: string, sourceId: string) {
this.prepareTree(robotId, sourceId);
this.subscribeList[sourceId][robotId].push({
type: targetType,
identity: targetId
});
@ -119,47 +130,32 @@ export class SubscribeManager {
* @param robotId ID
* @param targetType
* @param targetId ID
* @param channelId ID
* @param sourceId ID
*/
public removeSubscribe(robotId: string, targetType: string, targetId: string, channelId: string) {
if (this.subscribeList?.[channelId]?.[robotId]) {
this.subscribeList[channelId][robotId] = this.subscribeList[channelId][robotId].filter((target) => {
public removeSubscribe(robotId: string, targetType: string, targetId: string, sourceId: string) {
if (this.subscribeList?.[sourceId]?.[robotId]) {
this.subscribeList[sourceId][robotId] = this.subscribeList[sourceId][robotId].filter((target) => {
return (target.type !== targetType || targetId != targetId);
});
}
}
/**
*
* @param channelId ID
*/
public addChannel(channelId: string) {
}
/**
*
* @param channelId ID
*/
public removeChannel(channelId: string) {
}
/**
*
* @param channelId ID
*
* @param sourceId ID
* @param robotId ID
* @returns
*/
public getSubscriber(channelId: string, robotId: string): Target[] | null {
public getSubscriber(sourceId: string, robotId: string): Target[] | null {
let subscribers: Target[] = [];
// 获取频道本身的订阅
if (this.subscribeList?.[channelId]?.[robotId]) {
subscribers.push(...this.subscribeList[channelId][robotId]);
// 获取订阅
if (this.subscribeList?.[sourceId]?.[robotId]) {
subscribers.push(...this.subscribeList[sourceId][robotId]);
}
// 获取父级(频道组)的订阅
if (channelId.includes('/')) {
let channelGroupPath = channelId.substring(0, channelId.lastIndexOf('/'));
if (sourceId.startsWith('channel:') && sourceId.includes('/')) {
// 获取父级(频道组)的订阅
let channelGroupPath = sourceId.substring(0, sourceId.lastIndexOf('/'));
if (this.subscribeList?.[channelGroupPath]?.[robotId]) {
subscribers.push(...this.subscribeList[channelGroupPath][robotId]);
}
@ -171,4 +167,8 @@ export class SubscribeManager {
return null;
}
}
public getSubscribedList(robotId: string, targetType: string, targetId: string, sourceType: string): string[] {
return this.subscribeConfig?.[robotId]?.[targetType]?.[targetId]?.[sourceType] ?? [];
}
}

@ -1,15 +1,17 @@
import { CommonReceivedMessage } from "../message/Message";
import { PluginController } from "../PluginManager";
import { PluginController, PluginEvent } from "../PluginManager";
export default class EchoController implements PluginController {
public event!: PluginEvent;
export default class EchoController extends PluginController {
public id = 'echo';
public name = '复读机';
public description = '友好地复读消息';
public autoSubscribe = true;
public async initialize(): Promise<void> {
this.on("message/focused", this.handleEcho);
this.event.autoSubscribe = true;
this.event.on("message/focused", this.handleEcho);
}
private async handleEcho(message: CommonReceivedMessage, resolved: CallableFunction) {

@ -0,0 +1,45 @@
import App from "../App";
import { PluginController, PluginEvent } from "../PluginManager";
import { WikiMisc } from "./wiki/WikiMisc";
const API_ENDPOINT = 'https://www.isekai.cn/api.php';
export default class IsekaiWikiController implements PluginController {
public event!: PluginEvent;
public app: App;
public apiEndpoint = 'https://www.isekai.cn/api.php';
public id = 'isekaiwiki';
public name = '异世界百科';
public description = '异世界百科的相关功能';
constructor(app: App) {
this.app = app;
}
public async initialize(): Promise<void> {
this.event.controller = this;
const wikiMisc = new WikiMisc(this.app, 'https://www.isekai.cn/api.php');
this.event.registerCommand({
command: '百科',
name: '搜索异世界百科',
}, (args, message, resolved) => {
resolved();
wikiMisc.handleSearch(args, message);
});
this.event.registerCommand({
command: '随机',
name: '获取随机的百科页面',
alias: ['随机词条', '随机页面'],
}, (args, message, resolved) => {
resolved();
wikiMisc.handleRandomPage(args, message);
});
}
}

@ -0,0 +1,42 @@
import App from "../App";
import { PluginController, PluginEvent } from "../PluginManager";
import { WikiMisc } from "./wiki/WikiMisc";
export default class SfsettingsController implements PluginController {
public event!: PluginEvent;
public app: App;
public id = 'sfsettings';
public name = '科幻设定百科';
public description = '科幻设定百科的相关功能';
constructor(app: App) {
this.app = app;
}
public async initialize(): Promise<void> {
this.event.controller = this;
const wikiMisc = new WikiMisc(this.app, 'https://www.sfsettings.com/w/api.php');
this.event.registerCommand({
command: '百科',
name: '在百科上搜索',
alias: ['搜索', '查找', '词条'],
}, (args, message, resolved) => {
resolved();
wikiMisc.handleSearch(args, message);
});
this.event.registerCommand({
command: '随机',
name: '获取随机的百科页面',
alias: ['随机词条', '随机页面'],
}, (args, message, resolved) => {
resolved();
wikiMisc.handleRandomPage(args, message);
});
}
}

@ -0,0 +1,168 @@
import request from "request-promise";
import App from "../../App";
import { CommonReceivedMessage } from "../../message/Message";
import { PluginEvent } from "../../PluginManager";
export class WikiMisc {
public event!: PluginEvent;
public app: App;
private apiEndpoint: string;
public id = 'sfsettings';
public name = '科幻设定百科';
public description = '科幻设定百科的相关功能';
constructor(app: App, apiEndpoint: string) {
this.app = app;
this.apiEndpoint = apiEndpoint;
}
public async handleSearch(args: string, message: CommonReceivedMessage) {
try {
let res = await request({
uri: this.apiEndpoint,
method: 'GET',
qs: {
action: 'opensearch',
search: args,
limit: 10,
namespace: 0,
format: 'json',
formatversion: 2,
},
json: true,
});
if (res.error) {
message.sendReply('获取词条列表失败: ' + res.error.info, true);
}
let titles = res[1];
if (titles.length === 0) {
message.sendReply('未找到相关词条', true);
return;
}
// Get page info
res = await request({
uri: this.apiEndpoint,
method: 'GET',
qs: {
action: 'query',
prop: 'info|extracts',
inprop: 'url',
exintro: true,
explaintext: true,
exsentences: 3,
exlimit: 1,
redirects: true,
format: 'json',
formatversion: 2,
titles: titles[0],
},
json: true,
});
if (res.error) {
message.sendReply('获取词条详情失败: ' + res.error.info, true);
return;
}
let pages = res.query.pages;
let page = pages[0];
if (page.missing) {
message.sendReply('未找到相关词条', true);
return;
}
let reply = '找到的词条:' + titles.join('、') + '\n';
reply += '《' + page.title + '》\n';
reply += page.extract;
message.sendReply(reply, true);
} catch (err: any) {
message.sendReply('获取词条详情失败: ' + err.message, true);
console.error(err);
}
}
public async handleRandomPage(args: string, message: CommonReceivedMessage) {
try {
let res = await request({
uri: this.apiEndpoint,
method: 'GET',
qs: {
action: 'query',
prop: 'info|extracts',
inprop: 'url',
exintro: true,
explaintext: true,
exsentences: 3,
exlimit: 1,
list: 'random',
rnnamespace: 0,
rnlimit: 1,
format: 'json',
formatversion: 2,
},
json: true,
});
if (res.error) {
message.sendReply('获取随机页面失败: ' + res.error.info, true);
return;
}
if (this.app.debug) {
console.log(res);
}
let pageTitle = res.query.random?.[0]?.title;
if (!pageTitle) {
message.sendReply('未找到随机页面', true);
return;
}
// Get page info
res = await request({
uri: this.apiEndpoint,
method: 'GET',
qs: {
action: 'query',
prop: 'info|extracts',
inprop: 'url',
exintro: true,
explaintext: true,
exsentences: 3,
exlimit: 1,
redirects: true,
format: 'json',
formatversion: 2,
titles: pageTitle,
},
json: true,
});
if (res.error) {
message.sendReply('获取随机页面失败: ' + res.error.info, true);
return;
}
let pages = res.query.pages;
let page = pages[0];
if (!page || page.missing) {
message.sendReply('获取随机页面失败:页面丢失', true);
return;
}
let reply = '《' + page.title + '》\n';
reply += page.extract + '\n';
reply += page.canonicalurl;
message.sendReply(reply, true);
} catch (err: any) {
message.sendReply('获取随机页面失败: ' + err.message, true);
console.error(err);
}
}
}

@ -63,6 +63,9 @@ export class CommonMessage {
/** 提到的人 */
mentions?: { uid: string, text?: string }[];
/** 附加信息 */
extra: any = {};
private _contentText?: string;
public get contentText() {

@ -140,10 +140,10 @@ export default class QQRobot implements Robot {
if (isResolved) return;
// 处理指令
let commandInfo = this.getCommandInfo(message);
if (commandInfo) {
isResolved = await this.app.event.emitCommand(commandInfo.name, commandInfo.args, message);
if (isResolved) return;
let commandText = this.getCommandContentText(message);
if (commandText) {
await this.app.event.emitCommand(commandText, message);
return;
}
// 处理消息
@ -153,12 +153,10 @@ export default class QQRobot implements Robot {
}
}
getCommandInfo(message: CommonReceivedMessage) {
getCommandContentText(message: CommonReceivedMessage) {
for (let prefix of this.commandPrefix) {
if (message.contentText.startsWith(prefix)) {
let name = message.contentText.substring(prefix.length).split(' ')[0];
let args = message.contentText.substring(prefix.length + name.length + 1);
return { name, args };
return message.contentText.substring(prefix.length);
}
}
return null;
@ -211,9 +209,12 @@ export default class QQRobot implements Robot {
let msgData = await convertMessageToQQChunk(message);
if (message.origin === 'private') {
if (this.app.debug) console.log('[DEBUG] 发送私聊消息', message.targetId, msgData);
await this.sendToUser(message.targetId, msgData);
} else if (message.origin === 'group') {
console.log('发送群消息', message.targetId, msgData);
if (this.app.debug) console.log('[DEBUG] 发送群消息', message.targetId, msgData);
await this.sendToGroup(message.targetId, msgData);
}

Loading…
Cancel
Save