UNPKG

koishi-plugin-pcr-arena

Version:
426 lines (421 loc) 16.4 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/arena/src/index.ts var index_exports = {}; __export(index_exports, { Arena: () => Arena, Config: () => Config, apply: () => apply, inject: () => inject, name: () => name }); module.exports = __toCommonJS(index_exports); // packages/arena/src/api.ts var import_dns = require("dns"); var import_promises = require("fs/promises"); var import_path = __toESM(require("path")); var import_koishi = require("koishi"); var import_undici = require("undici"); var _Arena = class _Arena extends import_koishi.Service { alias = ["怎么拆", "怎么解", "怎么打", "如何拆", "如何解", "如何打", "jjc查询", "jjc"]; alias_bcr = []; alias_tw = []; alias_jp = []; aliases; EQUIPMENT; STAR; STAR_PINK; STAR_DISABLE; THUMB_DOWN; THUMB_UP; NUMBER_YELLOW = []; NUMBER_BLUE = []; client; API; PATH = "/x/v1/search"; constructor(ctx, config) { super(ctx, "arena", true); this.config = config; this.API = typeof this.config.API === "string" ? this.config.API : this.config.API.endpoint.trim(); this.alias.forEach((a) => { this.alias_bcr.push(`b${a}`); this.alias_tw.push(`台${a}`); this.alias_jp.push(`日${a}`); }); this.aliases = this.alias.concat(this.alias_bcr, this.alias_tw, this.alias_jp); this.alias_bcr.push("cn"); this.alias_tw.push("tw"); this.alias_jp.push("jp"); const clientOptions = { connect: { timeout: 1e4 } }; if (this.config.IPv4) { clientOptions.connect.family = 4; clientOptions.connect.lookup = (hostname, options, callback) => { try { (0, import_dns.resolve4)(hostname, (err, addresses) => { if (err || !addresses || !addresses.length) { return callback(err || new Error("No IPv4 address found"), null, 4); } callback(null, addresses[0], 4); }); } catch (error) { (0, import_dns.lookup)(hostname, { family: 4, all: false }, callback); } }; } this.client = new import_undici.Client(this.API, clientOptions); } async start() { this.EQUIPMENT = await this.ctx.canvas.loadImage(await (0, import_promises.readFile)(this.normalize(__dirname, "../public/EQUIPMENT.png"))); this.STAR = await this.ctx.canvas.loadImage(await (0, import_promises.readFile)(this.normalize(__dirname, "../public/STAR.png"))); this.STAR_PINK = await this.ctx.canvas.loadImage(await (0, import_promises.readFile)(this.normalize(__dirname, "../public/STAR_PINK.png"))); this.STAR_DISABLE = await this.ctx.canvas.loadImage(await (0, import_promises.readFile)(this.normalize(__dirname, "../public/STAR_DISABLE.png"))); this.THUMB_DOWN = await this.ctx.canvas.loadImage(await (0, import_promises.readFile)(this.normalize(__dirname, "../public/THUMB_DOWN_FILL.png"))); this.THUMB_UP = await this.ctx.canvas.loadImage(await (0, import_promises.readFile)(this.normalize(__dirname, "../public/THUMB_UP_FILL.png"))); for (let i = 0; i < 10; i++) { this.NUMBER_YELLOW.push(await this.ctx.canvas.loadImage(await (0, import_promises.readFile)(this.normalize(__dirname, `../public/YELLOW_${i}.png`)))); this.NUMBER_BLUE.push(await this.ctx.canvas.loadImage(await (0, import_promises.readFile)(this.normalize(__dirname, `../public/BLUE_${i}.png`)))); } } normalize(...file) { return import_path.default.posix.normalize(import_path.default.resolve(...file)); } async request(defenders, region = 1) { const data = { _sign: "a", def: defenders, nonce: "a", page: 1, sort: 1, ts: Math.floor(Date.now() / 1e3), region }; const headers = new import_undici.Headers({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Authorization": this.config.API_KEY }); this.ctx.logger.debug(data); const resp = await this.client.request({ path: this.PATH, method: "POST", headers, body: JSON.stringify(data) }); return await resp.body.json(); } prefix(msg) { var _a; const escaped = this.aliases.map((item) => `^${item}`); const pattern = escaped.join("|"); const regex = new RegExp(pattern, "gi"); const match = (_a = msg.match(regex)) == null ? void 0 : _a[0]; return match; } region(msg) { if (this.alias_bcr.includes(msg)) { return 2; } if (this.alias_tw.includes(msg)) { return 3; } if (this.alias_jp.includes(msg)) { return 4; } return 1; } roundRect(ctx, x, y, width, height, radius) { ctx.beginPath(); ctx.moveTo(x + radius, y); ctx.lineTo(x + width - radius, y); ctx.quadraticCurveTo(x + width, y, x + width, y + radius); ctx.lineTo(x + width, y + height - radius); ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); ctx.lineTo(x + radius, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - radius); ctx.lineTo(x, y + radius); ctx.quadraticCurveTo(x, y, x + radius, y); ctx.closePath(); } }; __name(_Arena, "Arena"); var Arena = _Arena; // packages/arena/src/config.ts var import_koishi2 = require("koishi"); var Config = import_koishi2.Schema.object({ API: import_koishi2.Schema.union([ import_koishi2.Schema.const("https://api.pcrdfans.com"), import_koishi2.Schema.object({ endpoint: import_koishi2.Schema.string().required() }).description("自定义 pcrdfans 的 API 地址") ]).default("https://api.pcrdfans.com").description("pcrdfans 的 API 地址"), API_KEY: import_koishi2.Schema.string().description("pcrdfans 的 API KEY").required(true), IPv4: import_koishi2.Schema.boolean().default(false).description("仅使用 IPv4 连接") }); // packages/arena/src/index.ts var name = "arena"; var inject = ["pcr", "canvas"]; function apply(ctx, config) { ctx.plugin(Arena, config); ctx.inject(["arena"], (ctx2) => { ctx2.middleware((session, next) => { var _a; const elements = session.elements; const selfId = session.bot.selfId; const prefix = session.app.config.prefix.valueOf(); ctx2.logger.debug(elements); ctx2.logger.debug(prefix); if (!elements || !elements.length) { return next(); } if (elements[0].type === "at" && ((_a = elements[0].attrs) == null ? void 0 : _a.id) === selfId) { elements.shift(); } if (elements[0].type !== "text") { return next(); } let msg = elements[0].attrs.content.trim(); if (typeof prefix === "string") { if (msg.startsWith(prefix)) { msg = msg.substring(prefix.length, msg.length); } } else if (Array.isArray(prefix)) { for (const pre of prefix) { if (msg.startsWith(pre)) { msg = msg.substring(pre.length, msg.length); } } } const command = ctx2.arena.prefix(msg); if (!command) { return next(); } const Region = ctx2.arena.region(command); const def = msg.replace(command, "").trim(); return session.execute({ name: command, args: [def], options: { Region } }); }); ctx2.command("怎么拆 <def:string>").option("region", "-r <region:string>", { type: /* @__PURE__ */ __name((r) => r.toLocaleLowerCase(), "type") }).option("Region", "-R <Region:number>", { hidden: true }).alias("b怎么拆", { options: { Region: 2 } }).alias("台怎么拆", { options: { Region: 3 } }).alias("日怎么拆", { options: { Region: 4 } }).action(async ({ session, options }, def) => { if ((options == null ? void 0 : options.region) && !["cn", "jp", "tw"].includes(options == null ? void 0 : options.region) || (options == null ? void 0 : options.Region) && ![1, 2, 3, 4].includes(options == null ? void 0 : options.Region)) { return `未知的区服:${options == null ? void 0 : options.region}`; } const region = (options == null ? void 0 : options.region) ? ctx2.arena.region(options == null ? void 0 : options.region) : (options == null ? void 0 : options.Region) ? options == null ? void 0 : options.Region : 1; if (!(def == null ? void 0 : def.trim())) { return `查询请发送"怎么拆+防守队伍"`; } if (ctx2.pcr.isReloading()) { return `兰德索尔花名册正在重新加载中,请稍后尝试`; } const defId = ctx2.pcr.parseTeam(def, true); if (!defId[0]) { return `无法解析「${defId[1]}」`; } if (defId.length < 5) { return `少于5名角色的检索条件请移步 pcrdfans 进行查询`; } if (defId.length > 5) { return `编队不能多于5名角色`; } if (defId.length !== new Set(defId).size) { return `编队中有重复的角色`; } if (defId.some((d) => ctx2.pcr.isUnavailableChara(Number(d)))) { return `编队中含未实装角色`; } await session.send("正在查询,请稍等..."); const res = await ctx2.arena.request(defId.map((t) => t + "01").map(Number), region); ctx2.logger.debug(res); if (res.code) { switch (res.code) { case 103: return `此 API KEY 已绑定至另一个 IP,请联系 Bot 主人解决。`; case 117: case -429: return `高峰期服务器限流!请前往 pcrdfans 手动查询。`; case 601: return `IP 被封禁,请联系 Bot 主人解决`; default: return `未知错误:${res.code},请联系 Bot 主人解决。`; } } const result = res.data.result; ctx2.logger.debug(result); if (result.length === 0) { return `没有查询到解法 作业上传请前往 pcrdfans`; } const radius = 20; const iconSize = 128; const fontSize = 32; const numberHeight = 46; const numberWidth = 35; const smallIconSize = 20; const n = result.length >= 10 ? 10 : result.length; const borderPix = 5; const width = 5 * iconSize + radius * 2 + (Math.max(...result.map((r) => Math.max(r.up, r.down))).toString().length + 1) * numberWidth; const height = n * (iconSize + borderPix) + fontSize * 2; const fontPink = "#fa5393"; const borderYellow = "#f5d68e"; await session.send("已查询到解法,正在渲染至图片,首次下载角色资源会耗时较久..."); return await session.app.canvas.render(width + radius, height + radius, async (cv) => { cv.strokeStyle = borderYellow; cv.lineWidth = borderPix; cv.fillStyle = "rgb(253, 251, 249)"; ctx2.arena.roundRect(cv, radius / 5, radius / 5, width, height, radius); cv.fill(); cv.stroke(); for (let i = 0; i < n; i++) { let x = 0; let y = 0; for (let j = 0; j < 5; j++) { ctx2.logger.debug(`i: ${i}, j: ${j}`); const atk = result[i].atk[j]; const star = result[i].atk[j].star === 0 ? 3 : result[i].atk[j].star; ctx2.logger.debug(`atk star: ${atk.star}`); ctx2.logger.debug(`star: ${star}`); x = iconSize * j + radius; y = (iconSize + borderPix) * i + radius; const image = await ctx2.pcr.getUnitIcon(atk.id.toString().slice(0, -2), star, true); if (!image.buffer && !image.type) { cv.font = `bolder ${fontSize}px sans-serif`; cv.textAlign = "left"; cv.textBaseline = "top"; cv.fillStyle = fontPink; const text = `获取角色图标失败,请重试`; cv.fillText(text, (width - 382) / 2, (height - 30) / 2); return; } cv.drawImage( image.buffer, x, y, iconSize, iconSize ); if (atk.equip) { cv.drawImage( ctx2.arena.EQUIPMENT, x + borderPix, y + borderPix, smallIconSize, smallIconSize ); } for (let k = 1; k <= 6; k++) { if (k === 6 && star === 6) { cv.drawImage( ctx2.arena.STAR_PINK, x + (smallIconSize - borderPix) * (k - 1) + 2, y + iconSize - smallIconSize - 2, smallIconSize, smallIconSize ); } else if (k <= star) { cv.drawImage( ctx2.arena.STAR, x + (smallIconSize - borderPix) * (k - 1) + 2, y + iconSize - smallIconSize - 2, smallIconSize, smallIconSize ); } else if (k > star && k < 6) { cv.drawImage( ctx2.arena.STAR_DISABLE, x + (smallIconSize - borderPix) * (k - 1) + 2, y + iconSize - smallIconSize - 2, smallIconSize, smallIconSize ); } } } x += iconSize; y += 9; cv.drawImage( ctx2.arena.THUMB_UP, x, y, numberHeight, numberHeight ); cv.drawImage( ctx2.arena.THUMB_DOWN, x, y + iconSize / 2, numberHeight, numberHeight ); x += numberHeight; const up = result[i].up.toString(); const down = result[i].down.toString(); for (let i2 = 0; i2 < Math.max(up.length, down.length); i2++) { if (i2 < up.length) { cv.drawImage( ctx2.arena.NUMBER_YELLOW[Number(up[i2])], x + numberWidth * i2, y, numberWidth, numberHeight ); } if (i2 < down.length) { cv.drawImage( ctx2.arena.NUMBER_BLUE[Number(down[i2])], x + numberWidth * i2, y + iconSize / 2, numberWidth, numberHeight ); } } } cv.font = `bolder ${fontSize}px sans-serif`; cv.textAlign = "left"; cv.textBaseline = "top"; cv.fillStyle = fontPink; const support = "Support by pcrdfans.com"; cv.fillText( support, radius, (iconSize + borderPix) * n + radius ); }); }); }); } __name(apply, "apply"); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Arena, Config, apply, inject, name });