UNPKG

koishi-plugin-anime-tracemoe

Version:

一款 Koishi 插件,可以调用 trace.moe api 识别图片搜索番剧

125 lines (123 loc) 6.8 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, name: () => name, using: () => using }); module.exports = __toCommonJS(src_exports); var import_koishi = require("koishi"); var name = "trace-moe"; var using = ["http"]; var Config = import_koishi.Schema.object({ apiKey: import_koishi.Schema.string().description("(可选,留空则使用默认的 trace.moe API)你的 trace.moe API 密钥,用于提高搜索速率限制。"), minSimilarity: import_koishi.Schema.number().min(0).max(100).default(87).description("最低相似度 (0-100)。"), cutBorders: import_koishi.Schema.boolean().default(true).description("是否自动裁剪视频黑边。"), showRomanjiTitle: import_koishi.Schema.boolean().default(true).description("是否在原生标题旁显示罗马音标题。"), sendCoverImage: import_koishi.Schema.boolean().default(true).description("是否发送番剧的封面(海报)图。"), sendScenePreview: import_koishi.Schema.boolean().default(true).description("是否发送识别出的场景预览(视频或截图)。"), useForward: import_koishi.Schema.boolean().default(false).description("是否使用合并转发的形式发送结果(仅在 QQ 平台下效果最佳)。"), logDetails: import_koishi.Schema.boolean().default(false).description("是否在控制台输出详细的调试日志。") }); function apply(ctx, config) { const logger = ctx.logger("trace-moe"); ctx.command("以图识番 <message:text>", "根据图片识别动漫番剧").action(async ({ session }, message) => { const imgUrl = import_koishi.h.select(message, "img").map((img) => img.attrs.src)[0]; if (!imgUrl) return "请在指令后附上需要识别的图片。"; if (config.logDetails) logger.info(`[1/6] 收到搜番指令,提取到图片URL: ${imgUrl}`); let placeholderMessageIds; try { const quote = (0, import_koishi.h)("quote", { id: session.messageId }); placeholderMessageIds = await session.send((0, import_koishi.h)("message", quote, "正在识别中,请稍候...")); const apiUrl = new URL("https://api.trace.moe/search"); apiUrl.searchParams.append("url", imgUrl); apiUrl.searchParams.append("anilistInfo", ""); if (config.cutBorders) apiUrl.searchParams.append("cutBorders", ""); if (config.logDetails) logger.info(`[2/6] 构造的API请求URL: ${apiUrl.href}`); const headers = { "Content-Type": "application/json" }; if (config.apiKey) headers["x-trace-key"] = config.apiKey; const data = await ctx.http.get(apiUrl.href, { headers }); if (config.logDetails) logger.info(`[3/6] 收到 trace.moe API 的原始响应。`); if (!data.result || data.result.length === 0) return "未能识别出任何结果。"; const topResult = data.result[0]; const similarity = parseFloat((topResult.similarity * 100).toFixed(2)); if (config.logDetails) logger.info(`[4/6] 解析最高匹配结果。相似度: ${similarity}%, 设定阈值: ${config.minSimilarity}%`); if (similarity < config.minSimilarity) return `相似度最高的匹配结果 (${similarity}%) 也低于设定的阈值 (${config.minSimilarity}%)。`; const { anilist, episode, from } = topResult; const finalElements = []; finalElements.push((0, import_koishi.h)("p", "【识别结果】")); if (config.sendCoverImage && anilist?.coverImage?.large) { finalElements.push(import_koishi.h.image(anilist.coverImage.large)); } const textLines = []; let titleLine = anilist.title.native; if (config.showRomanjiTitle && anilist.title.romaji && anilist.title.native !== anilist.title.romaji) { titleLine += ` ${anilist.title.romaji}`; } textLines.push(`🎬 番剧名称: ${titleLine}`); if (anilist.genres && anilist.genres.length > 0) { textLines.push(`🎭 番剧类型: ${anilist.genres.join(" / ")}`); } if (anilist.startDate?.year) { const { year, month, day } = anilist.startDate; textLines.push(`🗓️ 放送时间: ${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`); } const formatTime = /* @__PURE__ */ __name((t) => `${Math.floor(t / 60).toString().padStart(2, "0")}:${Math.floor(t % 60).toString().padStart(2, "0")}`, "formatTime"); textLines.push(`🎞️ 集数: ${episode || "未知"}`); textLines.push(`⏱️ 场景时间: ${formatTime(from)}`); textLines.push(`📈 相似度: ${similarity}%`); finalElements.push(textLines.join("\n")); if (config.sendScenePreview) { const videoUrl = typeof topResult.video === "string" && topResult.video.length > 0 ? topResult.video : null; if (videoUrl) { finalElements.push(import_koishi.h.video(videoUrl)); } else { finalElements.push(import_koishi.h.image(topResult.image)); } } if (config.logDetails) logger.info(`[5/6] 发送方式决策: useForward=${config.useForward && session.platform === "onebot"}.`); if (config.useForward && session.platform === "onebot") { return (0, import_koishi.h)("figure", {}, finalElements); } else { return (0, import_koishi.h)("message", {}, finalElements); } } catch (error) { logger.error("调用 trace.moe API 时发生错误:", error); return "识别失败,可能是网络问题或 API 暂时不可用。"; } finally { if (placeholderMessageIds && placeholderMessageIds.length > 0) { if (config.logDetails) logger.info(`[6/6] 任务完成,准备删除占位消息。`); await session.bot.deleteMessage(session.channelId, placeholderMessageIds[0]); } } }); } __name(apply, "apply"); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Config, apply, name, using });