koishi-plugin-starfx-bot 0.19.0 → 0.19.1

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.js CHANGED
@@ -405,12 +405,54 @@ __name(drawBanGDream, "drawBanGDream");
405
405
  async function intervalGetExchangeRate(ctx, cfg, session, searchString, exchangeRatePath) {
406
406
  }
407
407
  __name(intervalGetExchangeRate, "intervalGetExchangeRate");
408
- async function getExchangeRate(ctx, cfg, session, searchString) {
408
+ async function parseNaturalCurrency(searchString) {
409
+ const index = await getCurrencyCodesMap();
410
+ const s = searchString.replace(/\s+/g, "");
411
+ const match = s.match(/(.+?)(兑|对)(.+)/);
412
+ if (match) {
413
+ const from = index[match[1]];
414
+ const to = index[match[3]];
415
+ if (from && to) {
416
+ return from + to;
417
+ }
418
+ }
419
+ if (index[s]) {
420
+ return index[s];
421
+ }
422
+ return null;
423
+ }
424
+ __name(parseNaturalCurrency, "parseNaturalCurrency");
425
+ async function getExchangeRate(ctx, cfg, session, searchString, raw, retryTimes = 1) {
426
+ const apiKey = retryTimes ? await getMvpAPIKey() : await updateMvpAPIKey();
409
427
  try {
410
- const apiKey = await getMvpAPIKey();
411
- const guids = await getExchangeGuids(searchString, apiKey);
428
+ let guids = [];
429
+ if (searchString) {
430
+ const parsed = await parseNaturalCurrency(searchString);
431
+ if (parsed) {
432
+ searchString = parsed;
433
+ }
434
+ }
435
+ if (raw && /^([a-z0-9]{6})([,\s]([a-z0-9]{6}))*$/.test(raw)) {
436
+ guids = raw.split(",");
437
+ } else if (/^([a-zA-Z]{3}|[a-zA-Z]{6})([,\s]([a-zA-Z]{3}|[a-zA-Z]{6}))*$/.test(searchString)) {
438
+ const currencies = await getCurrencyCodes();
439
+ const codes = searchString.split(/[,\s]+/);
440
+ const invalidCodes = [];
441
+ let search6Strings = [];
442
+ codes.forEach((code) => {
443
+ const first = code.slice(0, 3).toUpperCase();
444
+ const second = code.length === 3 ? "CNY" : code.slice(3, 6).toUpperCase();
445
+ search6Strings.push(code.length === 3 ? first + second : code);
446
+ if (!currencies.some((c) => c.code === first)) invalidCodes.push(first);
447
+ if (code.length === 6 && !currencies.some((c) => c.code === second)) invalidCodes.push(second);
448
+ });
449
+ if (invalidCodes.length > 0) {
450
+ throw new Error(`输入的货币代码不合法: ${invalidCodes.join(", ")}`);
451
+ }
452
+ guids = await getExchangeGuids(search6Strings.join(","), apiKey);
453
+ }
412
454
  if (!guids.length || guids.every((g) => !g)) {
413
- throw new Error("Invalid symbols");
455
+ throw new Error(`输入无效: ${searchString}`);
414
456
  }
415
457
  const result = [];
416
458
  for (const guid of guids) {
@@ -422,28 +464,35 @@ async function getExchangeRate(ctx, cfg, session, searchString) {
422
464
  if (!nowPrice || !oneMonthPrice) {
423
465
  throw new Error("Failed to fetch price");
424
466
  }
425
- const chartBuffer = await drawOneMonthChartSkia(oneMonthPrice.prices, oneMonthPrice.timeStamps);
467
+ const chartBuffer = await drawOneMonthChartSkia(oneMonthPrice.prices, oneMonthPrice.timeStamps, `1 ${nowPrice.fromCurrency} to 1 ${nowPrice.currency}`);
426
468
  const imgSrc = "data:image/png;base64," + chartBuffer.toString("base64");
427
469
  result.push({
428
470
  name: nowPrice.symbolName,
471
+ fromCurrency: nowPrice.fromCurrency,
472
+ currency: nowPrice.currency,
429
473
  nowPrice: nowPrice.price,
430
474
  oneMonthChart: import_koishi.h.image(imgSrc)
431
475
  });
432
476
  }
433
477
  for (const item of result) {
434
478
  await session.send([
435
- import_koishi.h.text((/* @__PURE__ */ new Date()).toLocaleString()),
436
479
  import_koishi.h.text(`
480
+ ${(/* @__PURE__ */ new Date()).toLocaleString()}
437
481
  ${item.name}
438
- 当前价格: `),
439
- import_koishi.h.text(`${item.nowPrice}
440
- 近30天价格走势: `),
482
+ (${item.fromCurrency}/${item.currency})
483
+ 当前价格: ${item.nowPrice}
484
+ 近30天价格走势:
485
+ `),
441
486
  item.oneMonthChart
442
487
  ]);
443
488
  }
444
489
  } catch (err) {
445
- const message = err instanceof Error ? err.message : typeof err === "string" ? err : JSON.stringify(err);
446
- await session.send(`查询失败:${message}`);
490
+ if (retryTimes === 0) {
491
+ const message = err instanceof Error ? err.message : typeof err === "string" ? err : JSON.stringify(err);
492
+ await session.send(`查询失败:${message}`);
493
+ } else {
494
+ await getExchangeRate(ctx, cfg, session, searchString, raw, retryTimes - 1);
495
+ }
447
496
  }
448
497
  }
449
498
  __name(getExchangeRate, "getExchangeRate");
@@ -696,8 +745,9 @@ async function updateMvpAPIKey() {
696
745
  const apiResp = await fetch(apiUrl);
697
746
  if (!apiResp.ok) throw new Error(`Failed to fetch config API: ${apiResp.status}`);
698
747
  const json = await apiResp.json();
699
- const mvpAPIkey = json?.configs?.["shared/msn-ns/CommonAutoSuggest/default"]?.properties?.["mvpAPIkey"] ?? null;
700
- return mvpAPIkey;
748
+ const newMvpAPIKey = json?.configs?.["shared/msn-ns/CommonAutoSuggest/default"]?.properties?.["mvpAPIkey"] ?? null;
749
+ mvpAPIKey = newMvpAPIKey;
750
+ return newMvpAPIKey;
701
751
  } catch (err) {
702
752
  console.error(err);
703
753
  return null;
@@ -714,7 +764,8 @@ async function getExchangeGuids(symbols, apikey) {
714
764
  const url = `https://assets.msn.cn/service/Finance/IdMap?apikey=${apikey}&MStarIds=${encodeURIComponent(symbols)}`;
715
765
  const resp = await fetch(url);
716
766
  if (!resp.ok) throw new Error(`请求失败: ${resp.status}`);
717
- const data = await resp.json();
767
+ let data = await resp.json();
768
+ data = data.filter((d) => d.status === 200);
718
769
  const inputSymbols = symbols.split(",");
719
770
  const guidMap = new Map(data.map((item) => [item.mStarId, item.guid]));
720
771
  return inputSymbols.map((sym) => guidMap.get(sym) || "");
@@ -744,11 +795,13 @@ async function getQuotes(ids, apiKey) {
744
795
  q.instrumentId,
745
796
  {
746
797
  price: q.price,
747
- symbolName: q.localizedAttributes?.["zh-cn"]?.symbolName || q.symbol
798
+ symbolName: q.localizedAttributes?.["zh-cn"]?.symbolName || q.localizedAttributes?.["zh-cn"]?.displayName || q.symbol,
799
+ fromCurrency: q.fromCurrency || q.displayName,
800
+ currency: q.currency
748
801
  }
749
802
  ])
750
803
  );
751
- return inputIds.map((id) => quoteMap.get(id) ?? { price: 0, symbolName: "" });
804
+ return inputIds.map((id) => quoteMap.get(id) ?? { price: 0, symbolName: "", fromCurrency: "", currency: "" });
752
805
  } catch (err) {
753
806
  console.error(err);
754
807
  return [];
@@ -775,7 +828,7 @@ async function getMonthMSNClosePrices(ids, apiKey) {
775
828
  });
776
829
  }
777
830
  __name(getMonthMSNClosePrices, "getMonthMSNClosePrices");
778
- async function drawOneMonthChartSkia(prices, timeStamps, width = 1200, height = 600) {
831
+ async function drawOneMonthChartSkia(prices, timeStamps, title = "兑换汇率", width = 1200, height = 600) {
779
832
  if (!prices.length || !timeStamps.length || prices.length !== timeStamps.length) return;
780
833
  const dataPoints = timeStamps.map((t, i) => ({ x: new Date(t), y: prices[i] }));
781
834
  const canvas = new import_skia_canvas.Canvas(width, height);
@@ -793,7 +846,7 @@ async function drawOneMonthChartSkia(prices, timeStamps, width = 1200, height =
793
846
  type: "line",
794
847
  data: {
795
848
  datasets: [{
796
- label: "兑换汇率",
849
+ label: title,
797
850
  data: dataPoints,
798
851
  showLine: true,
799
852
  // ⭐ 不连线,只画点
@@ -802,9 +855,17 @@ async function drawOneMonthChartSkia(prices, timeStamps, width = 1200, height =
802
855
  pointBorderWidth: 0,
803
856
  // ⭐ 去掉外边框 → 变成实心点
804
857
  borderWidth: 2,
805
- borderColor: "blue",
858
+ borderColor: "red",
806
859
  backgroundColor: "rgba(0,0,255,0.1)",
807
- tension: 0.2
860
+ tension: 0,
861
+ segment: {
862
+ borderDash: /* @__PURE__ */ __name((ctx) => {
863
+ const p0 = ctx.p0.parsed.x;
864
+ const p1 = ctx.p1.parsed.x;
865
+ const diffHours = (p1 - p0) / (1e3 * 60 * 60);
866
+ return diffHours > 2 ? [6, 4] : [];
867
+ }, "borderDash")
868
+ }
808
869
  }]
809
870
  },
810
871
  options: {
@@ -836,6 +897,32 @@ async function drawOneMonthChartSkia(prices, timeStamps, width = 1200, height =
836
897
  return await canvas.toBuffer("png");
837
898
  }
838
899
  __name(drawOneMonthChartSkia, "drawOneMonthChartSkia");
900
+ var currencyCodes = [];
901
+ async function getCurrencyCodes() {
902
+ if (currencyCodes?.length) return currencyCodes;
903
+ currencyCodes = require(import_node_path.default.resolve(__dirname, "./currency.json"));
904
+ return currencyCodes;
905
+ }
906
+ __name(getCurrencyCodes, "getCurrencyCodes");
907
+ var currencyCodesMap = {};
908
+ async function getCurrencyCodesMap() {
909
+ if (Object.keys(currencyCodesMap)?.length) return currencyCodesMap;
910
+ currencyCodesMap = await buildCurrencyIndex();
911
+ return currencyCodesMap;
912
+ }
913
+ __name(getCurrencyCodesMap, "getCurrencyCodesMap");
914
+ async function buildCurrencyIndex() {
915
+ const map = {};
916
+ (await getCurrencyCodes()).forEach((c) => {
917
+ map[c.country] = c.code;
918
+ map[c.currencyCn] = c.code;
919
+ });
920
+ map["日元"] = "JPY";
921
+ map["人民币"] = "CNY";
922
+ map["港币"] = "HKD";
923
+ return map;
924
+ }
925
+ __name(buildCurrencyIndex, "buildCurrencyIndex");
839
926
 
840
927
  // src/index.ts
841
928
  var import_node_path2 = __toESM(require("node:path"));
@@ -848,7 +935,7 @@ var package_default = {
848
935
  contributors: [
849
936
  "StarFreedomX <starfreedomx@outlook.com>"
850
937
  ],
851
- version: "0.19.0",
938
+ version: "0.19.1",
852
939
  main: "lib/index.js",
853
940
  typings: "lib/index.d.ts",
854
941
  files: [
@@ -1074,7 +1161,7 @@ function apply(ctx, cfg) {
1074
1161
  }
1075
1162
  if (cfg.roomNumber) {
1076
1163
  const roomNumMap = /* @__PURE__ */ new Map();
1077
- ctx.command("room-number [param: string]").action(async ({ session }, param) => {
1164
+ ctx.command("room-number [param: string]").usage("记录房间号").action(async ({ session }, param) => {
1078
1165
  const nowRoomNumMap = cfg.saveRoomAsFile ? readMap(cfg.saveRoomAsFile) : roomNumMap;
1079
1166
  const room = nowRoomNumMap.get(session.gid);
1080
1167
  if (!param) {
@@ -1098,7 +1185,7 @@ function apply(ctx, cfg) {
1098
1185
  }
1099
1186
  if (cfg.ready) {
1100
1187
  const readyMap = /* @__PURE__ */ new Map();
1101
- ctx.command("waiting-play [param:text]", { strictOptions: true }).action(async ({ session }, param) => {
1188
+ ctx.command("waiting-play [param:text]", { strictOptions: true }).usage("待机").action(async ({ session }, param) => {
1102
1189
  return ready(session, cfg, param, readyMap);
1103
1190
  });
1104
1191
  }
@@ -1108,13 +1195,13 @@ function apply(ctx, cfg) {
1108
1195
  });
1109
1196
  }
1110
1197
  if (cfg.undo) {
1111
- ctx.command("undo").alias("撤回").action(async ({ session }) => {
1198
+ ctx.command("undo").alias("撤回").usage("撤回消息").action(async ({ session }) => {
1112
1199
  if (detectControl(controlJson, session.guildId, "undo"))
1113
1200
  await undo(cfg, session);
1114
1201
  });
1115
1202
  }
1116
1203
  if (cfg.forward) {
1117
- ctx.command("forward").option("group", "-g <group:string>").option("platform", "-p <platform:string>").action(async ({ session, options }) => {
1204
+ ctx.command("forward").option("group", "-g <group:string>").option("platform", "-p <platform:string>").usage("转发消息").action(async ({ session, options }) => {
1118
1205
  if (detectControl(controlJson, session.guildId, "forward")) {
1119
1206
  const mapPath = import_node_path2.default.join(assetsDir, "forward.json");
1120
1207
  const groupMap = readMap(mapPath);
@@ -1140,7 +1227,7 @@ function apply(ctx, cfg) {
1140
1227
  });
1141
1228
  }
1142
1229
  if (cfg.originImg) {
1143
- ctx.command("获取X原图 <urls>").alias("推特原图").action(async ({ session }, urls) => {
1230
+ ctx.command("获取X原图 <urls>").alias("推特原图").usage("获取推特原图").action(async ({ session }, urls) => {
1144
1231
  if (detectControl(controlJson, session.guildId, "originImg")) {
1145
1232
  let [xUrls, xIndex] = await Promise.all([
1146
1233
  getXUrl(session?.quote?.content),
@@ -1154,9 +1241,9 @@ function apply(ctx, cfg) {
1154
1241
  });
1155
1242
  }
1156
1243
  if (cfg.searchExchangeRate) {
1157
- ctx.command("查汇率 [exchangeParam:string]").action(async ({ session }, exchangeParam) => {
1244
+ ctx.command("查汇率 <exchangeParam:text>").usage("查询当前汇率").example("查汇率 JPY : 查询日元兑换人民币的汇率(3位字母)").example("查汇率 JPYCNY : 查询日元兑换人民币的汇率(6位字母)").example("查汇率 -r avdzk2 : 查询日元兑换人民币的汇率(msn代码avdzk2)").example("查汇率 -r auvwoc : 查询黄金的价格(msn代码auvwoc, 很怪吧我也不知道为什么是这个)").option("raw", "-r <raw:string>").action(async ({ session, options }, exchangeParam) => {
1158
1245
  if (detectControl(controlJson, session.guildId, "exchangeRate")) {
1159
- return await getExchangeRate(ctx, cfg, session, exchangeParam);
1246
+ return await getExchangeRate(ctx, cfg, session, exchangeParam, options?.raw);
1160
1247
  }
1161
1248
  });
1162
1249
  }
package/lib/utils.d.ts CHANGED
@@ -130,7 +130,7 @@ export declare function drawBanGDream(avatar: string, inputOptions?: {
130
130
  border: string;
131
131
  }): Promise<string>;
132
132
  export declare function intervalGetExchangeRate(ctx: Context, cfg: Config, session: Session, searchString: string, exchangeRatePath: string): Promise<void>;
133
- export declare function getExchangeRate(ctx: Context, cfg: Config, session: Session, searchString: string): Promise<void>;
133
+ export declare function getExchangeRate(ctx: Context, cfg: Config, session: Session, searchString?: string, raw?: string, retryTimes?: number): Promise<void>;
134
134
  export declare function parseJsonControl(text: string): FeatureControl | null;
135
135
  export declare function detectControl(controlJson: FeatureControl, guildId: string, funName: string): boolean;
136
136
  export declare function handleRoll(session: Session): string;
@@ -154,9 +154,17 @@ export declare function ready(session: Session, cfg: Config, param: string, read
154
154
  * 绘制近一个月收盘价走势图
155
155
  * @param prices 收盘价数组
156
156
  * @param timeStamps ISO 时间戳数组
157
+ * @param title 图表标题
157
158
  * @param width 图表宽度
158
159
  * @param height 图表高度
159
160
  * @returns buffer
160
161
  */
161
- export declare function drawOneMonthChartSkia(prices: number[], timeStamps: string[], width?: number, height?: number): Promise<Buffer>;
162
+ export declare function drawOneMonthChartSkia(prices: number[], timeStamps: string[], title?: string, width?: number, height?: number): Promise<Buffer>;
163
+ interface CurrencyCode {
164
+ "country": string;
165
+ "currencyCn": string;
166
+ "code": string;
167
+ }
168
+ export declare function getCurrencyCodes(): Promise<CurrencyCode[]>;
169
+ export declare function getCurrencyCodesMap(): Promise<Record<string, string>>;
162
170
  export {};
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "contributors": [
5
5
  "StarFreedomX <starfreedomx@outlook.com>"
6
6
  ],
7
- "version": "0.19.0",
7
+ "version": "0.19.1",
8
8
  "main": "lib/index.js",
9
9
  "typings": "lib/index.d.ts",
10
10
  "files": [
package/readme.md CHANGED
@@ -55,6 +55,8 @@ StarFreedomX机器人的小功能,自用
55
55
  | `sold` | 闲鱼“卖掉了”功能(对应`openSold`) |
56
56
  | `repeat` | 群复读功能(对应`openRepeat`) |
57
57
  | `record` | 群语录功能(对应`投稿、语录`) |
58
+ | `record-push` | 群语录功能(对应`投稿`) |
59
+ | `record-get` | 群语录功能(对应`语录`) |
58
60
  | `atNotSay` | “艾特我/他又不说话”系列功能 |
59
61
  | `replyBot` | “我才不是机器人!”系列功能 |
60
62
  | `iLoveYou` | “我也喜欢你”系列功能 |
@@ -116,3 +118,4 @@ StarFreedomX机器人的小功能,自用
116
118
  | `0.18.1` | 修复艾特不说话功能 |
117
119
  | `0.18.2` | 不直接操作插件目录,防止更新问题 |
118
120
  | `0.19.0` | 新增查汇率功能 |
121
+ | `0.19.0` | 查汇率适配更多语法 |