koishi-plugin-nmc-radar 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -1,10 +1,14 @@
1
- import type { Awaitable, Context } from 'koishi';
1
+ import type { Awaitable, Context, Dict } from 'koishi';
2
2
  import { h, Schema } from 'koishi';
3
3
  export declare const name = "nmc-radar";
4
4
  export declare const inject: {
5
5
  optional: string[];
6
6
  };
7
- type Resolver = (urls: string[], id: string) => Awaitable<h>;
7
+ interface Product {
8
+ url: string;
9
+ slug: string;
10
+ }
11
+ type Resolver = (products: Product[], options: Dict) => Awaitable<h>;
8
12
  declare const resolvers: Record<string, Resolver>;
9
13
  export interface Config {
10
14
  defaultResolver: keyof typeof resolvers;
package/lib/index.js CHANGED
@@ -36,6 +36,7 @@ __export(src_exports, {
36
36
  name: () => name
37
37
  });
38
38
  module.exports = __toCommonJS(src_exports);
39
+ var import_node_buffer = require("node:buffer");
39
40
  var import_promises = require("node:fs/promises");
40
41
  var import_node_path = __toESM(require("node:path"));
41
42
  var import_jsdom = require("jsdom");
@@ -325,39 +326,61 @@ var inject = {
325
326
  optional: ["ffmpeg"]
326
327
  };
327
328
  var resolvers = {
328
- img: /* @__PURE__ */ __name((urls) => import_koishi.h.img(urls[0]), "img"),
329
- imgs: /* @__PURE__ */ __name((urls) => (0, import_koishi.h)(import_koishi.h.Fragment, ...urls.map((url) => import_koishi.h.img(url))), "imgs"),
330
- url: /* @__PURE__ */ __name((urls) => import_koishi.h.text(urls[0]), "url"),
331
- urls: /* @__PURE__ */ __name((urls) => import_koishi.h.text(urls.join("\n")), "urls")
329
+ img: /* @__PURE__ */ __name((products) => (0, import_koishi.h)(import_koishi.h.Fragment, ...products.map((url) => import_koishi.h.img(url.url))), "img"),
330
+ url: /* @__PURE__ */ __name((products) => import_koishi.h.text(products.map((url) => url.url).join("\n")), "url")
332
331
  };
333
332
  var Config = import_koishi.Schema.object({
334
333
  defaultResolver: import_koishi.Schema.union(Object.keys(resolvers)).default("img").description("默认输出类型。")
335
334
  });
336
335
  function apply(ctx, config) {
337
- const command = ctx.command("radar <name:string>", "查看雷达图", { checkUnknown: true }).alias("雷达").option("type", "--type <type:string> 输出类型", { type: Object.keys(resolvers) }).option("type", "--img 输出单张图片", { value: "img" }).option("type", "--imgs 输出多张图片", { value: "imgs" }).option("type", "--url 输出 URL", { value: "url" }).option("type", "--urls 输出 URL 列表", { value: "urls" }).action(async ({ session, options }, name2) => {
336
+ const command = ctx.command("radar <name:string>", "查看雷达图", { checkUnknown: true }).alias("雷达").option("name", "--name <name:string> 雷达站名称").option("count", "-n <count:number> 最大输出数量").option("reverse", "-R 反转顺序").option("type", "--type <type:string> 输出类型", { type: Object.keys(resolvers) }).option("type", "--img 输出图片", { value: "img" }).option("type", "--url 输出 URL", { value: "url" }).action(async ({ session, options = {} }, name2) => {
337
+ options.name ??= name2;
338
338
  if (!(name2 in radars_default))
339
339
  return void session?.send("雷达站不存在,可使用 radar.list 查看所有雷达站。");
340
- const url = radars_default[name2];
340
+ const url = radars_default[options.name];
341
341
  const { window: { document } } = new import_jsdom.JSDOM(await ctx.http.get(url));
342
342
  const nodes = document.querySelectorAll("div[data-img]");
343
- const urls = Array.from(nodes).map((node) => node.dataset.img || "");
344
- const resolver = resolvers[options?.type || config.defaultResolver];
345
- return await resolver(urls, import_koishi.Random.id());
343
+ options.type ??= config.defaultResolver;
344
+ options.count ??= options.type === "img" ? 1 : void 0;
345
+ const products = Array.from(nodes).slice(0, options.count).map((node) => ({
346
+ url: node.dataset.img,
347
+ slug: node.dataset.time.replaceAll(/[/ :]/g, "")
348
+ }));
349
+ options.reverse || products.reverse();
350
+ return await resolvers[options.type](products, options);
346
351
  });
347
352
  ctx.inject(["ffmpeg"], async (ctx2) => {
348
- command.option("type", "--gif 输出 GIF 动画", { value: "gif" });
349
- resolvers.gif = async (urls, id) => {
350
- const baseDir = import_node_path.default.join(ctx2.baseDir, "temp", name, id);
351
- await (0, import_promises.mkdir)(baseDir, { recursive: true });
352
- await Promise.all(urls.map(async (url, i) => {
353
- const response = await ctx2.http.get(url, { responseType: "stream" });
354
- const filename = `${String(i + 1).padStart(3, "0")}.png`;
355
- const filePath = import_node_path.default.join(baseDir, filename);
356
- await (0, import_promises.writeFile)(filePath, response);
357
- }));
358
- const outputPath = import_node_path.default.join(baseDir, "..", `${id}.gif`);
359
- await ctx2.ffmpeg.builder().input(import_node_path.default.join(baseDir, "%03d.png")).run("file", outputPath);
360
- return import_koishi.h.img(`file:///${outputPath}`);
353
+ command.option("type", "--gif 输出 GIF 动画", { value: "gif" }).option("fps", "--fps <fps:number> 帧率", { fallback: 10 }).option("loop", "--loop <loop:number> 循环次数", { fallback: -1 });
354
+ resolvers.gif = async (products, options) => {
355
+ const baseDir = import_node_path.default.join(ctx2.baseDir, "cache", name, options.name);
356
+ const outputPath = import_node_path.default.join(baseDir, [
357
+ `${products[0].slug}+${products[products.length - 1].slug}`,
358
+ `-${options.fps}@${options.loop}.gif`
359
+ ].join(""));
360
+ try {
361
+ await (0, import_promises.access)(outputPath);
362
+ } catch {
363
+ await (0, import_promises.mkdir)(baseDir, { recursive: true });
364
+ const filePaths = await Promise.all(products.map(async ({ url, slug }) => {
365
+ const filePath = import_node_path.default.join(baseDir, `${slug}.png`);
366
+ try {
367
+ await (0, import_promises.access)(filePath);
368
+ } catch {
369
+ try {
370
+ const response = await ctx2.http.get(url, { responseType: "stream" });
371
+ await (0, import_promises.writeFile)(filePath, response);
372
+ } catch {
373
+ return void await (0, import_promises.unlink)(filePath);
374
+ }
375
+ }
376
+ return filePath;
377
+ }));
378
+ const buffer = [
379
+ ...filePaths.filter(Boolean).flatMap((filePath) => `file '${filePath.replaceAll("\\", "/")}'`)
380
+ ].join("\n");
381
+ await ctx2.ffmpeg.builder().input(import_node_buffer.Buffer.from(buffer)).inputOption("-f", "concat").inputOption("-safe", "0").inputOption("-protocol_whitelist", "file,fd").inputOption("-r", options.fps).outputOption("-loop", options.loop).run("file", outputPath);
382
+ }
383
+ return import_koishi.h.img(`file://${outputPath}`);
361
384
  };
362
385
  });
363
386
  command.subcommand(".list", "查看所有雷达站").action(() => Object.keys(radars_default).join(" "));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-nmc-radar",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "气象台雷达图插件",
5
5
  "typings": "lib/index.d.ts",
6
6
  "license": "MIT",