miraipie
Version:
the most powerful nodejs development kit for mirai-api-http
338 lines (301 loc) • 12.2 kB
text/typescript
import EventEmitter from 'events';
import {getLogger, Logger} from 'log4js';
import {Chat} from './chat';
import {ConfigMeta, makeConfigs, UserConfigs} from './config';
import {MessageChain} from './message';
import {At, SingleMessageType} from './mirai';
import {MiraiPieApplication} from './miraipie';
/** pie消息过滤器类型 */
export type PieFilterBuilder = (...args: any[]) => PieFilter;
/** pie消息过滤器 */
export class PieFilter {
/** 消息包含@我 */
static atMe: PieFilter = {
sign: 'AtMe',
handler: (chat, chain) => {
return chain.selected('At').some((at) => (at as At).target === MiraiPieApplication.instance.qq)
}
};
/** 消息包含@全体成员 */
static atAll: PieFilter = {
sign: 'AtAll',
handler: (chat, chain) => chain.selected('AtAll').length > 0
};
/** 消息来自好友 */
static fromFriend: PieFilter = {
sign: 'FromFriend',
handler: (chat) => chat.isFriendChat()
}
/** 消息来自群聊 */
static fromGroup: PieFilter = {
sign: 'FromGroup',
handler: (chat) => chat.isGroupChat()
}
/** 消息来自群成员 */
static fromMember: PieFilter = {
sign: 'FromMember',
handler: (chat) => chat.isTempChat()
}
/** 过滤器签名, 通常要能表达过滤器的意图 */
sign: string;
/**
* 过滤器的验证器
* @param chat 当前聊天窗口
* @param chain 当前消息链
* @param pie 当前pie对象
*/
handler: (chat: Chat, chain: MessageChain, pie: Pie) => boolean;
/**
* 消息包含@指定QQ号群成员
* @param id 群成员QQ号
*/
static at: PieFilterBuilder = function (id: number): PieFilter {
return {
sign: `At(${id})`,
handler: (chat, chain) => {
return chain.selected('At').some((at) => (at as At).target === id)
}
};
}
/**
* 消息来自指定账号
* @param id QQ号或群号
*/
static from: PieFilterBuilder = function (id: number): PieFilter {
return {
sign: `From(${id})`,
handler: (chat) => chat.contact.id === id
};
}
/**
* 消息不来自指定账号
* @param id QQ号或群号
*/
static notFrom: PieFilterBuilder = function (id: number): PieFilter {
return {
sign: `NotFrom(${id})`,
handler: (chat) => chat.contact.id !== id
};
}
/**
* 消息包含单一消息类型
* @param type 单一消息类型
*/
static containsType: PieFilterBuilder = function (type: SingleMessageType): PieFilter {
return {
sign: `ContainsType(${type})`,
handler: (chat, chain) => chain.selected(type).length > 0
};
}
/**
* 消息显示串匹配正则表达式
* @param regexp 正则表达式
*/
static displayStringMatch: PieFilterBuilder = function (regexp: RegExp | string): PieFilter {
return {
sign: `DisplayStringMatch(${regexp})`,
handler: (chat, chain) => chain.toDisplayString().match(regexp) !== null
};
}
/**
* 消息显示串和指定串全等
* @param displayString 指定字符串
*/
static displayStringEquals: PieFilterBuilder = function (displayString: string): PieFilter {
return {
sign: `DisplayStringEquals(${displayString})`,
handler: (chat, chain) => chain.toDisplayString() === displayString
}
}
/**
* 多个消息过滤器求<strong>或</strong>
* @param filters 消息过滤器
*/
static or: PieFilterBuilder = function (...filters: PieFilter[]): PieFilter {
return {
sign: `Or(${filters.map((filter) => filter.sign).join(', ')})`,
handler: (chat, chain, pie) => filters.some((filter) => filter.handler(chat, chain, pie))
};
}
}
type PieMethodOptions<PieInstance> = {
[key: string]: (this: PieInstance, ...args: any) => any;
};
type MessageReceivedListener<PieInstance> = (this: PieInstance, chat: Chat, chain: MessageChain) => any;
type LifecycleHookListener<PieInstance> = (this: PieInstance) => any;
type PieHookOptions<PieInstance> = {
/**
* pie消息监听器, 用以监听消息并处理
* @param chat 当前聊天窗
* @param chain 消息链
* @example
* // 复读机
* async received(chat, chain) => {
* await chat.send(chain);
* }
*/
received?: MessageReceivedListener<PieInstance>;
/** pie安装完成hook */
installed?: LifecycleHookListener<PieInstance>;
/** pie卸载完成hook */
uninstalled?: LifecycleHookListener<PieInstance>;
/** pie启用hook */
enabled?: LifecycleHookListener<PieInstance>;
/** pie禁用hook */
disabled?: LifecycleHookListener<PieInstance>;
}
type PieOptions<C extends ConfigMeta, D extends object, M extends PieMethodOptions<Pie<C, D, M>>> = {
/** id(必须为合法标识符) */
id: string;
/** 名称 */
name: string;
/** 作者 */
author: string;
/** 版本号 */
version: string;
/** 描述 */
description?: string;
/** 作者链接 */
authorUrl?: string;
/** 项目链接 */
projectUrl?: string;
/**
* pie用户配置元声明, 用以标识用户配置项
* @example
* userConfigMeta: {
* base: {
* type: Number,
* description: '累加器初始值',
* default: 0
* },
* step: {
* type: Number,
* description: '累加器步长'
* }
* }
*/
configMeta?: C;
/** pie中数据 */
data?: D;
/**
* pie中方法, 生成pie实例时会自动挂载到实例上
* @example
* methods: {
* greet() {
* return 'Hello ' + this.author;
* }
* }
*
* // "Hello Nepsyn"
* pie.greet();
*/
methods?: M;
/**
* pie消息过滤器, 用以在执行pie处理器前过滤掉不需要的消息, 多个过滤器求<strong>与</strong>, 为true则执行messageHandler
* @example
* filters: [PieFilter.atMe] // 只响应@我的消息
* filters: [PieFilter.atMe, PieFilter.atAll] // 同时响应@我和@全体成员的消息
* filters: [PieFilter.or(PieFilter.atMe, PieFilter.atAll)] // 只响应@我或@全体成员的消息
*
* // 自行构造过滤器
* const myFilter = {
* sign: 'LuckyTest',
* handler: () => Math.random() > 0.1
* }
*/
filters?: PieFilter[];
}
& PieHookOptions<Pie<C, D, M>>
& ThisType<Pie<C, D, M>>;
export type Pie<C extends ConfigMeta = {}, D extends object = object, M extends PieMethodOptions<Pie<C, D, M>> = {}> =
{
/** id */
readonly id: string;
/** 名称 */
readonly name: string;
/** 作者 */
readonly author: string;
/** 版本号 */
readonly version: string;
/** 描述 */
description: string;
/** 作者链接 */
authorUrl: string;
/** 项目链接 */
projectUrl: string;
/** 配置元定义 */
readonly configMeta: C;
/** 用户配置 */
configs: UserConfigs<C>;
/** pie 过滤器 */
filters: PieFilter[];
/** logger */
logger: Logger;
/** 是否为 pie 标识, 恒为 true */
readonly __isPie: true;
addListener(e: 'received', listener: MessageReceivedListener<Pie<C, D, M>>);
addListener(e: 'installed', listener: LifecycleHookListener<Pie<C, D, M>>);
addListener(e: 'uninstalled', listener: LifecycleHookListener<Pie<C, D, M>>);
addListener(e: 'enabled', listener: LifecycleHookListener<Pie<C, D, M>>);
addListener(e: 'disabled', listener: LifecycleHookListener<Pie<C, D, M>>);
once(e: 'received', listener: MessageReceivedListener<Pie<C, D, M>>);
once(e: 'installed', listener: LifecycleHookListener<Pie<C, D, M>>);
once(e: 'uninstalled', listener: LifecycleHookListener<Pie<C, D, M>>);
once(e: 'enabled', listener: LifecycleHookListener<Pie<C, D, M>>);
once(e: 'disabled', listener: LifecycleHookListener<Pie<C, D, M>>);
on(e: 'received', listener: MessageReceivedListener<Pie<C, D, M>>);
on(e: 'installed', listener: LifecycleHookListener<Pie<C, D, M>>);
on(e: 'uninstalled', listener: LifecycleHookListener<Pie<C, D, M>>);
on(e: 'enabled', listener: LifecycleHookListener<Pie<C, D, M>>);
on(e: 'disabled', listener: LifecycleHookListener<Pie<C, D, M>>);
prependListener(e: 'received', listener: MessageReceivedListener<Pie<C, D, M>>);
prependListener(e: 'installed', listener: LifecycleHookListener<Pie<C, D, M>>);
prependListener(e: 'uninstalled', listener: LifecycleHookListener<Pie<C, D, M>>);
prependListener(e: 'enabled', listener: LifecycleHookListener<Pie<C, D, M>>);
prependListener(e: 'disabled', listener: LifecycleHookListener<Pie<C, D, M>>);
prependOnceListener(e: 'received', listener: MessageReceivedListener<Pie<C, D, M>>);
prependOnceListener(e: 'installed', listener: LifecycleHookListener<Pie<C, D, M>>);
prependOnceListener(e: 'uninstalled', listener: LifecycleHookListener<Pie<C, D, M>>);
prependOnceListener(e: 'enabled', listener: LifecycleHookListener<Pie<C, D, M>>);
prependOnceListener(e: 'disabled', listener: LifecycleHookListener<Pie<C, D, M>>);
emit(e: 'received', chat: Chat, chain: MessageChain);
emit(e: 'installed');
emit(e: 'uninstalled');
emit(e: 'enabled');
emit(e: 'disabled');
listeners(e: 'received'): MessageReceivedListener<Pie<C, D, M>>[];
listeners(e: 'installed'): LifecycleHookListener<Pie<C, D, M>>[];
listeners(e: 'uninstalled'): LifecycleHookListener<Pie<C, D, M>>[];
listeners(e: 'enabled'): LifecycleHookListener<Pie<C, D, M>>[];
listeners(e: 'disabled'): LifecycleHookListener<Pie<C, D, M>>[];
}
& D & M
& EventEmitter;
/**
* 创建 pie
* @param options pie 选项
*/
export function makePie<C extends ConfigMeta, D extends object, M extends PieMethodOptions<Pie<C, D, M>>>(options: PieOptions<C, D, M>): Pie<C, D, M> {
const pie = Object.assign(new EventEmitter(), {
...(options.data || {}),
...(options.methods || {}),
id: options.id,
name: options.name,
author: options.author,
version: options.version,
description: options.description || '',
authorUrl: options.authorUrl || '',
projectUrl: options.projectUrl || '',
configMeta: options.configMeta || {},
configs: makeConfigs(options.configMeta),
filters: options.filters || [],
logger: getLogger(`pie:${options.id}`),
__isPie: true
});
if (typeof options.received === 'function') pie.on('received', (...args) => options.received.apply(pie, args));
if (typeof options.installed === 'function') pie.on('installed', () => options.installed.apply(pie));
if (typeof options.uninstalled === 'function') pie.on('uninstalled', () => options.uninstalled.apply(pie));
if (typeof options.enabled === 'function') pie.on('enabled', () => options.enabled.apply(pie));
if (typeof options.disabled === 'function') pie.on('disabled', () => options.disabled.apply(pie));
return pie as Pie<C, D, M>;
}