koishi-plugin-pcr-arena
Version:
PCR jjc/pjjc plugin
426 lines (421 loc) • 16.4 kB
JavaScript
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
});