koishi
Version:
A QQ bot framework based on CQHTTP
307 lines (305 loc) • 11.7 kB
JavaScript
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