UNPKG

el-bot

Version:

A quick qq bot framework for mirai.

199 lines (177 loc) 5.15 kB
import path from 'node:path' import process from 'node:process' import consola from 'consola' import colors from 'picocolors' import { type Bot, BotPlugin, logger } from '..' import { isFunction } from '../../shared' import { merge } from '../../utils/config' import { handleError } from '../../utils/error' import { pluginLogger } from '../logger' import { getAllPlugins } from './utils' export type PluginInstallFunction = (ctx: Bot, ...options: any[]) => any export interface PluginInfo { name?: string version?: string description?: string pkg?: object } export type Plugin = ({ install: PluginInstallFunction } & PluginInfo) export type PluginType = 'default' | 'official' | 'community' | 'custom' export const PluginTypeMap: Record<PluginType, string> = { default: '默认插件', official: '官方插件', community: '社区插件', custom: '自定义插件', } export class Plugins { default = new Set<PluginInfo>() official = new Set<PluginInfo>() community = new Set<PluginInfo>() custom = new Set<PluginInfo>() constructor(public ctx: Bot) {} /** * 根据名称判断是否为官方插件 * @param name */ isOfficial(name: string) { return name.startsWith('@el-bot/plugin-') } /** * 根据名称判断是否为社区插件 * @param name */ isCommunity(name: string) { return name.startsWith('el-bot-plugin-') } /** * 根据插件类型,获得插件标准全名或路径 * @param name */ getPluginFullName(name: string, type: PluginType) { let pkgName = name switch (type) { case 'default': pkgName = `${name}` break case 'official': pkgName = `@el-bot/plugin-${name}` break case 'community': pkgName = `el-bot-plugin-${name}` break case 'custom': pkgName = path.resolve(process.cwd(), name) break default: break } return pkgName } /** * 加载配置中的插件 * plugins: [] */ async loadConfig() { const botConfig = this.ctx.el.bot! if (botConfig.plugins) { consola.start(`加载配置插件 - 共 ${colors.green(botConfig.plugins.length)} 个...`) consola.log('') for (const plugin of botConfig.plugins) { const pkgName = plugin.pkg?.name || '未知' try { if (plugin) { await plugin.setup(this.ctx) pluginLogger .child({ plugin: pkgName }) .success(`加载成功`) } } catch (err: any) { handleError(err as Error) pluginLogger .child({ plugin: pkgName }) .error(`加载失败`) } } } consola.log('') } /** * 加载自定义插件 */ async loadCustom(pluginDir: string) { if (!pluginDir) { pluginLogger.warning('未配置自定义插件目录') } else { const absolutePluginDir = path.resolve(process.cwd(), pluginDir) consola.info(`自定义插件目录: ${colors.cyan(absolutePluginDir)}`) const customPlugins = await getAllPlugins(absolutePluginDir) consola.start(`加载自定义插件 - 共 ${colors.green(customPlugins.length)} 个...`) consola.log('') for (const customPluginName of customPlugins) { const pluginPath = path.resolve(absolutePluginDir, `${customPluginName}.ts`) const importedCustomPlugin = (await import(pluginPath)).default let customPlugin: BotPlugin if (typeof importedCustomPlugin === 'function') { // TODO: 传入配置 options customPlugin = importedCustomPlugin({}) } else { customPlugin = importedCustomPlugin } await customPlugin.setup(this.ctx) } } } /** * 是否依赖于数据库 * @param pkg */ isBasedOnDb(pkg: any): boolean { return pkg['el-bot'] && pkg['el-bot'].db && !this.ctx.db } /** * 添加插件 * @param name 插件名 * @param plugin 插件函数 * @param options 默认配置 */ add(name: string, plugin: Plugin, options?: any) { const ctx = this.ctx // 插件基于数据库,但是未启用数据库时 if (plugin.pkg && this.isBasedOnDb(plugin.pkg)) { logger.warning( `[${name}] 如想要使用该插件,您须先启用数据库。`, ) return } // 加载配置项 let pluginOptions = options if (this.ctx.el.bot![name]) { if (pluginOptions) pluginOptions = merge(pluginOptions, this.ctx.el.bot![name]) else pluginOptions = this.ctx.el.bot![name] } if (plugin && isFunction(plugin.install)) plugin.install(ctx, pluginOptions) } /** * 插件列表 * @param type 插件类型 */ list(type: PluginType) { const pluginTypeName = PluginTypeMap[type] let content = `无${pluginTypeName}\n` if (this[type].size > 0) { content = `${pluginTypeName}:\n` this[type].forEach((plugin: PluginInfo) => { content += `- ${plugin.name}@${plugin.version}: ${plugin.description}\n` }) } return content } }