UNPKG

koishi

Version:

A QQ bot framework based on CQHTTP

307 lines (305 loc) 11.7 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {enumerable: true, configurable: true, writable: true, value}) : obj[key] = value; var __objSpread = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __markAsModule = (target) => __defProp(target, "__esModule", {value: true}); var __reExport = (target, module2, desc) => { if (module2 && typeof module2 === "object" || typeof module2 === "function") { for (let key of __getOwnPropNames(module2)) if (!__hasOwnProp.call(target, key) && key !== "default") __defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable}); } return target; }; var __toModule = (module2) => { return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2); }; // packages/koishi/src/worker.ts var import_koishi_core = __toModule(require("koishi-core")); var import_path = __toModule(require("path")); var import_koishi_utils = __toModule(require("koishi-utils")); var import_fs = __toModule(require("fs")); var import_perf_hooks = __toModule(require("perf_hooks")); var import_kleur = __toModule(require("kleur")); var logger = new import_koishi_utils.Logger("app"); var configDir = process.cwd(); function handleException(error) { logger.error(error); process.exit(1); } process.on("uncaughtException", handleException); var configFile; var configExt; var basename = "koishi.config"; if (process.env.KOISHI_CONFIG_FILE) { configFile = (0, import_path.resolve)(configDir, process.env.KOISHI_CONFIG_FILE); configExt = (0, import_path.extname)(configFile); configDir = (0, import_path.dirname)(configFile); } else { const files = (0, import_fs.readdirSync)(configDir); const ext = [".js", ".json", ".ts", ".yaml", ".yml"].find((ext2) => files.includes(basename + ext2)); if (!ext) { throw new Error(`config file not found. use ${(0, import_kleur.yellow)("koishi init")} command to initialize a config file.`); } configFile = configDir + "/" + basename + ext; } function loadConfig() { if ([".yaml", ".yml"].includes(configExt)) { const {load} = require("js-yaml"); return load((0, import_fs.readFileSync)(configFile, "utf8")); } else { const exports2 = require(configFile); return exports2.__esModule ? exports2.default : exports2; } } function isErrorModule(error) { return error.code !== "MODULE_NOT_FOUND" || error.requireStack && error.requireStack[0] !== __filename; } function loadEcosystem(type, name) { const prefix = `koishi-${type}-`; const modules = []; if ("./".includes(name[0])) { modules.push((0, import_path.resolve)(configDir, name)); } else if (name.includes(prefix)) { modules.push(name); } else if (name[0] === "@") { const index = name.lastIndexOf("/"); modules.push(name.slice(0, index + 1) + prefix + name.slice(index + 1), name); } else { modules.push(prefix + name, name); } for (const path of modules) { logger.debug("resolving %c", path); try { const result = require(path); logger.info("apply %s %c", type, result.name || name); return [path, result]; } catch (error) { if (isErrorModule(error)) { throw error; } } } throw new Error(`cannot resolve ${type} ${name}`); } function ensureBaseLevel(config2, base) { var _a; (_a = config2.base) != null ? _a : config2.base = base; Object.values(config2).forEach((value) => { if (typeof value !== "object") return; ensureBaseLevel(value, config2.base); }); } var config = loadConfig(); if (typeof config.logLevel === "object") { import_koishi_utils.Logger.levels = config.logLevel; } else if (typeof config.logLevel === "number") { import_koishi_utils.Logger.levels.base = config.logLevel; } if (config.logTime === true) config.logTime = "yyyy/MM/dd hh:mm:ss"; if (config.logTime) import_koishi_utils.Logger.showTime = config.logTime; if (process.env.KOISHI_LOG_LEVEL) { import_koishi_utils.Logger.levels.base = +process.env.KOISHI_LOG_LEVEL; } ensureBaseLevel(import_koishi_utils.Logger.levels, 2); if (process.env.KOISHI_DEBUG) { for (const name of process.env.KOISHI_DEBUG.split(",")) { new import_koishi_utils.Logger(name).level = import_koishi_utils.Logger.DEBUG; } } process.on("message", (data) => { if (data.type === "send") { const {channelId, sid, message} = data.payload; const bot = app.bots[sid]; bot.sendMessage(channelId, message); } }); function loadAdapter(bot) { const [name] = bot.type.split(":", 1); loadEcosystem("adapter", name); } if (config.type) { loadAdapter(config); } else { config.bots.forEach(loadAdapter); } var app = new import_koishi_core.App(config); app.command("exit", "停止机器人运行", {authority: 4}).option("restart", "-r 重新启动").shortcut("关机", {prefix: true}).shortcut("重启", {prefix: true, options: {restart: true}}).action(async ({options, session}) => { const {channelId, sid} = session; if (!options.restart) { await session.send("正在关机……").catch(import_koishi_utils.noop); process.exit(); } process.send({type: "exit", payload: {channelId, sid, message: "已成功重启。"}}); await session.send(`正在重启……`).catch(import_koishi_utils.noop); process.exit(114); }); var selectors = ["user", "group", "channel", "self", "private", "platform"]; function createContext(options) { let ctx = app; for (const type of selectors) { const value = options[`$${type}`]; if (value === true) { ctx = ctx[type](); } else if (value === false) { ctx = ctx[type].except(); } else if (value !== void 0) { ctx = ctx[type](...(0, import_koishi_utils.makeArray)(value).map((item) => "" + item)); } } if (options.$union) { let ctx2 = app; for (const selection of options.$union) { ctx2 = ctx2.union(createContext(selection)); } ctx = ctx.intersect(ctx2); } if (options.$except) { ctx = ctx.except(createContext(options.$except)); } return ctx; } var plugins = new Set(); var pluginEntries = Array.isArray(config.plugins) ? config.plugins.map((item) => Array.isArray(item) ? item : [item]) : Object.entries(config.plugins || {}); for (const [name, options] of pluginEntries) { const [path, plugin] = loadEcosystem("plugin", name); plugins.add(require.resolve(path)); createContext(options).plugin(plugin, options); } process.on("unhandledRejection", (error) => { logger.warn(error); }); app.start().then(() => { var _a; logger.info("%C", `Koishi/${import_koishi_core.version}`); app.bots.forEach((bot) => { logger.info("logged in to %s as %c (%s)", bot.platform, bot.username, bot.selfId); }); const time = Math.max(0, import_perf_hooks.performance.now() - +process.env.KOISHI_START_TIME).toFixed(); logger.success(`bot started successfully in ${time} ms`); import_koishi_utils.Logger.timestamp = Date.now(); import_koishi_utils.Logger.showDiff = (_a = config.logDiff) != null ? _a : !import_koishi_utils.Logger.showTime; process.send({type: "start"}); createWatcher(); }, handleException); function loadDependencies(filename, ignored) { const dependencies = new Set(); function traverse({filename: filename2, children}) { if (ignored.has(filename2) || dependencies.has(filename2) || filename2.includes("/node_modules/")) return; dependencies.add(filename2); children.forEach(traverse); } traverse(require.cache[filename]); return dependencies; } function createWatcher() { var _a; if (process.env.KOISHI_WATCH_ROOT === void 0 && !config.watch) return; const {watch} = require("chokidar"); const {root = "", ignored = [], fullReload} = config.watch || {}; const watchRoot = (0, import_path.resolve)(configDir, (_a = process.env.KOISHI_WATCH_ROOT) != null ? _a : root); const watcher = watch(watchRoot, __objSpread(__objSpread({}, config.watch), { ignored: ["**/node_modules/**", "**/.git/**", ...ignored] })); const externals = loadDependencies(__filename, plugins); const logger2 = new import_koishi_utils.Logger("app:watcher"); function triggerFullReload() { if (!fullReload) return; logger2.info("trigger full reload"); process.exit(114); } let stashed = new Set(); let currentUpdate; function flushChanges() { const tasks = []; const reloads = []; const accepted = new Set(); const declined = new Set(externals); function traverse(filename) { if (externals.has(filename) || filename.includes("/node_modules/")) return; const {children} = require.cache[filename]; let isActive = stashed.has(filename); for (const module2 of children) { if (traverse(module2.filename)) { stashed.add(filename); isActive = true; } } if (isActive) return isActive; declined.add(filename); } Array.from(stashed).forEach(traverse); for (const filename in require.cache) { const module2 = require.cache[filename]; const state = app.registry.get(module2.exports); if (!state) continue; const dependencies = [...loadDependencies(filename, declined)]; if (!dependencies.some((dep) => stashed.has(dep))) continue; dependencies.forEach((dep) => accepted.add(dep)); const plugin = require(filename); if (state == null ? void 0 : state.sideEffect) { triggerFullReload(); continue; } const displayName = plugin.name || (0, import_path.relative)(watchRoot, filename); reloads.push([filename, state]); tasks.push(app.dispose(plugin).catch((err) => { logger2.warn("failed to dispose plugin %c\n" + (0, import_koishi_utils.coerce)(err), displayName); })); } stashed = new Set(); currentUpdate = Promise.all(tasks).then(() => { accepted.forEach((path) => { logger2.debug("cache deleted:", path); delete require.cache[path]; }); for (const [filename, state] of reloads) { try { const plugin = require(filename); state.context.plugin(plugin, state.config); const displayName = plugin.name || (0, import_path.relative)(watchRoot, filename); logger2.info("reload plugin %c", displayName); } catch (err) { logger2.warn("failed to reload plugin at %c\n" + (0, import_koishi_utils.coerce)(err), (0, import_path.relative)(watchRoot, filename)); } } }); } watcher.on("change", (path) => { if (!require.cache[path]) return; logger2.debug("change detected:", path); if (path === configFile || externals.has(path)) { return triggerFullReload(); } stashed.add(path); Promise.resolve(currentUpdate).then(flushChanges); }); } //# sourceMappingURL=worker.js.map