UNPKG

koishi-plugin-autochat

Version:
202 lines (200 loc) 8.81 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name2 in all) __defProp(target, name2, { get: all[name2], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { Config: () => Config, apply: () => apply, inject: () => inject, name: () => name, reusable: () => reusable }); module.exports = __toCommonJS(src_exports); var import_koishi = require("koishi"); var import_koishi_plugin_chatluna = require("koishi-plugin-chatluna"); var import_chains = require("koishi-plugin-chatluna/chains"); var name = "autochat"; var reusable = true; var inject = ["chatluna", "database"]; var Config = import_koishi.Schema.object({ startTime: import_koishi.Schema.string().role("time").description("定时任务的起始时间,格式 HH:mm。").default("08:00"), endTime: import_koishi.Schema.string().role("time").description("定时任务的结束时间,格式 HH:mm。").default("22:00"), interval: import_koishi.Schema.number().description("在指定时间段内,按固定间隔发送对话的间隔(小时)。设为 0 可禁用。").default(4), randomSends: import_koishi.Schema.number().description("在指定时间段内,每日额外随机发送的对话次数。").default(2), prompt: import_koishi.Schema.string().description("发送给 AI 的内容模板,`{time}` 将被替换为当前时间。").default("现在是北京时间 {time},请记录下来。"), channelId: import_koishi.Schema.string().description("要把回复发送到的频道 ID(QQ 号或群号)").required(), channelType: import_koishi.Schema.union(["group", "private"]).description("发送到群聊还是私聊").default("group"), botSelfId: import_koishi.Schema.string().description("(可选)指定用于发送消息的机器人 selfId,如果留空则使用第一个可用机器人。").default("") }); function buildSession(ctx, bot, config, content) { const s = bot.session(); if (config.channelType === "group") { s.guildId = config.channelId; s.channelId = config.channelId; s.userId = "autochat"; } else { s.guildId = "0"; s.channelId = config.channelId; s.userId = config.channelId; } s.content = content; return s; } __name(buildSession, "buildSession"); function apply(ctx, config) { let intervalTimeout = null; let randomTimeouts = []; let dailyResetTimeout = null; const loadedPlatforms = /* @__PURE__ */ new Set(); const events = { "llm-queue-waiting": /* @__PURE__ */ __name(async () => { }, "llm-queue-waiting"), "llm-used-token-count": /* @__PURE__ */ __name(async () => { }, "llm-used-token-count") }; const cleanup = /* @__PURE__ */ __name(() => { if (intervalTimeout) clearTimeout(intervalTimeout); if (dailyResetTimeout) clearTimeout(dailyResetTimeout); randomTimeouts.forEach(clearTimeout); intervalTimeout = null; dailyResetTimeout = null; randomTimeouts = []; }, "cleanup"); async function sendOnce(customPrompt, sessionSource) { const bot = ctx.bots.find((b) => !config.botSelfId || b.selfId === config.botSelfId); if (!bot) { ctx.logger.warn("[autochat] 没有找到可用的 bot,取消发送。"); return; } const timeStr = (/* @__PURE__ */ new Date()).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }); const message = customPrompt && customPrompt.length > 0 ? customPrompt : config.prompt.replace("{time}", timeStr); const vSession = sessionSource ?? buildSession(ctx, bot, config, message); let room; if (config.channelType === "private") { room = await (0, import_chains.queryJoinedConversationRoom)(ctx, vSession); } else { room = await (0, import_chains.queryPublicConversationRoom)(ctx, vSession); } if (!room) { ctx.logger.warn("[autochat] 找不到对应房间,已跳过。"); return; } try { const platform = room.model.split("/")[0]; if (!loadedPlatforms.has(platform)) { ctx.logger.info(`[autochat] 等待平台 ${platform} 加载...`); await ctx.chatluna.awaitLoadPlatform(platform, 6e4); loadedPlatforms.add(platform); ctx.logger.info(`[autochat] 平台 ${platform} 已就绪。`); } } catch { } try { ctx.logger.info(`[autochat] 向房间发送: ${message}`); const replyMsg = await ctx.chatluna.chat( vSession, room, { role: "user", content: message }, events, false ); const reply = replyMsg?.content ?? ""; if (!reply) return; ctx.logger.info(`[autochat] ChatLuna 回复: ${reply}`); const targetId = (config.channelType === "private" ? "private:" : "group:") + config.channelId; await bot.sendMessage(targetId, reply); } catch (err) { ctx.logger.error("[autochat] 调用 ChatLuna 失败", err); } } __name(sendOnce, "sendOnce"); const scheduleAllTasks = /* @__PURE__ */ __name(() => { cleanup(); const [startHour, startMinute] = config.startTime.split(":").map(Number); const [endHour, endMinute] = config.endTime.split(":").map(Number); if ([startHour, startMinute, endHour, endMinute].some(isNaN)) { ctx.logger.warn("[autochat] 无效的时间配置,定时任务已停止。"); return; } const now = /* @__PURE__ */ new Date(); const startTimeToday = new Date(now); startTimeToday.setHours(startHour, startMinute, 0, 0); const endTimeToday = new Date(now); endTimeToday.setHours(endHour, endMinute, 0, 0); if (startTimeToday.getTime() >= endTimeToday.getTime()) { ctx.logger.warn("[autochat] 起始时间必须早于结束时间,定时任务已停止。"); return; } if (config.interval > 0) { let nextSendTime = new Date(startTimeToday); while (nextSendTime.getTime() < now.getTime()) { nextSendTime.setHours(nextSendTime.getHours() + config.interval); } if (nextSendTime.getTime() > endTimeToday.getTime()) { nextSendTime = new Date(startTimeToday); nextSendTime.setDate(nextSendTime.getDate() + 1); } const delay = nextSendTime.getTime() - now.getTime(); ctx.logger.info(`[autochat] 下次固定间隔发送已安排在 ${nextSendTime.toLocaleString("zh-CN")}`); intervalTimeout = setTimeout(() => { sendOnce(); scheduleAllTasks(); }, delay); } if (config.randomSends > 0) { const scheduleWindowStart = Math.max(now.getTime(), startTimeToday.getTime()); const scheduleWindowEnd = endTimeToday.getTime(); if (scheduleWindowStart < scheduleWindowEnd) { let scheduledCount = 0; for (let i = 0; i < config.randomSends; i++) { const randomTimestamp = scheduleWindowStart + Math.random() * (scheduleWindowEnd - scheduleWindowStart); const delay = randomTimestamp - now.getTime(); if (delay > 0) { const handle = setTimeout(() => sendOnce(), delay); randomTimeouts.push(handle); scheduledCount++; } } if (scheduledCount > 0) { ctx.logger.info(`[autochat] 已安排 ${scheduledCount} 次随机发送。`); } } } const nextMidnight = new Date(now); nextMidnight.setDate(nextMidnight.getDate() + 1); nextMidnight.setHours(0, 1, 0, 0); const delayToMidnight = nextMidnight.getTime() - now.getTime(); dailyResetTimeout = setTimeout(scheduleAllTasks, delayToMidnight); ctx.logger.info(`[autochat] 已安排在 ${nextMidnight.toLocaleString("zh-CN")} 重新规划次日任务。`); }, "scheduleAllTasks"); ctx.on("ready", scheduleAllTasks); ctx.on("dispose", cleanup); ctx.command("autochat [prompt:text]", "立即触发一次自动聊天", { authority: 1 }).usage("示例: /autochat 现在几点?").action(async ({ session }, prompt) => { await sendOnce(prompt, session); }); } __name(apply, "apply"); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Config, apply, inject, name, reusable });