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 +115 -28
- package/lib/utils.d.ts +10 -2
- package/package.json +1 -1
- package/readme.md +3 -0
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
|
|
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
|
-
|
|
411
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
446
|
-
|
|
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
|
|
700
|
-
|
|
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
|
-
|
|
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: "
|
|
858
|
+
borderColor: "red",
|
|
806
859
|
backgroundColor: "rgba(0,0,255,0.1)",
|
|
807
|
-
tension: 0
|
|
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.
|
|
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("查汇率
|
|
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
|
|
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
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` | 查汇率适配更多语法 |
|