koishi-plugin-starfx-bot 0.18.1 → 0.19.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 +2 -1
- package/lib/index.js +316 -15
- package/lib/utils.d.ts +12 -0
- package/package.json +8 -3
- package/readme.md +9 -1
package/lib/index.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ export declare const name = "starfx-bot";
|
|
|
3
3
|
export declare let baseDir: string;
|
|
4
4
|
export declare let assetsDir: string;
|
|
5
5
|
export declare const starfxLogger: Logger;
|
|
6
|
-
export declare const inject: string[];
|
|
7
6
|
export declare const repeatContextMap: Map<string, [string, number]>;
|
|
8
7
|
interface sendLocalImageConfigItem {
|
|
9
8
|
hiddenInHelp: boolean;
|
|
@@ -35,6 +34,8 @@ export interface Config {
|
|
|
35
34
|
roomNumber: boolean;
|
|
36
35
|
saveRoomAsFile: string;
|
|
37
36
|
forward: boolean;
|
|
37
|
+
searchExchangeRate: boolean;
|
|
38
|
+
intervalGetExchangeRate: boolean;
|
|
38
39
|
atNotSay: boolean;
|
|
39
40
|
atNotSayProperty: number;
|
|
40
41
|
atNotSayOther: boolean;
|
package/lib/index.js
CHANGED
|
@@ -44,7 +44,6 @@ __export(src_exports, {
|
|
|
44
44
|
apply: () => apply,
|
|
45
45
|
assetsDir: () => assetsDir,
|
|
46
46
|
baseDir: () => baseDir,
|
|
47
|
-
inject: () => inject,
|
|
48
47
|
name: () => name,
|
|
49
48
|
repeatContextMap: () => repeatContextMap,
|
|
50
49
|
starfxLogger: () => starfxLogger,
|
|
@@ -61,10 +60,16 @@ var import_node_path = __toESM(require("node:path"));
|
|
|
61
60
|
var import_sharp = __toESM(require("sharp"));
|
|
62
61
|
var import_jimp = require("jimp");
|
|
63
62
|
var import_rss_parser = __toESM(require("rss-parser"));
|
|
63
|
+
var import_chartjs_adapter_dayjs_3 = require("chartjs-adapter-dayjs-3");
|
|
64
64
|
var cheerio = __toESM(require("cheerio"));
|
|
65
65
|
var import_http_proxy_agent = require("http-proxy-agent");
|
|
66
66
|
var import_https_proxy_agent = require("https-proxy-agent");
|
|
67
67
|
var import_axios = __toESM(require("axios"));
|
|
68
|
+
var import_chart = require("chart.js");
|
|
69
|
+
var import_skia_canvas = require("skia-canvas");
|
|
70
|
+
import_chart.Chart.register(
|
|
71
|
+
...import_chart.registerables
|
|
72
|
+
);
|
|
68
73
|
async function addRecord(ctx, gid, avatarUrl) {
|
|
69
74
|
const recordDir = `${assetsDir}/record/${gid}`;
|
|
70
75
|
const avatarBuffer = await ctx.http.get(avatarUrl, { responseType: "arraybuffer" });
|
|
@@ -397,6 +402,51 @@ async function drawBanGDream(avatar, inputOptions) {
|
|
|
397
402
|
return `data:image/png;base64,${(await image.getBuffer("image/jpeg")).toString("base64")}`;
|
|
398
403
|
}
|
|
399
404
|
__name(drawBanGDream, "drawBanGDream");
|
|
405
|
+
async function intervalGetExchangeRate(ctx, cfg, session, searchString, exchangeRatePath) {
|
|
406
|
+
}
|
|
407
|
+
__name(intervalGetExchangeRate, "intervalGetExchangeRate");
|
|
408
|
+
async function getExchangeRate(ctx, cfg, session, searchString) {
|
|
409
|
+
try {
|
|
410
|
+
const apiKey = await getMvpAPIKey();
|
|
411
|
+
const guids = await getExchangeGuids(searchString, apiKey);
|
|
412
|
+
if (!guids.length || guids.every((g) => !g)) {
|
|
413
|
+
throw new Error("Invalid symbols");
|
|
414
|
+
}
|
|
415
|
+
const result = [];
|
|
416
|
+
for (const guid of guids) {
|
|
417
|
+
if (!guid) throw new Error("GUID not found");
|
|
418
|
+
const nowPriceArr = await getQuotes(guid, apiKey);
|
|
419
|
+
const monthPriceArr = await getMonthMSNClosePrices(guid, apiKey);
|
|
420
|
+
const nowPrice = nowPriceArr?.[0];
|
|
421
|
+
const oneMonthPrice = monthPriceArr?.[0];
|
|
422
|
+
if (!nowPrice || !oneMonthPrice) {
|
|
423
|
+
throw new Error("Failed to fetch price");
|
|
424
|
+
}
|
|
425
|
+
const chartBuffer = await drawOneMonthChartSkia(oneMonthPrice.prices, oneMonthPrice.timeStamps);
|
|
426
|
+
const imgSrc = "data:image/png;base64," + chartBuffer.toString("base64");
|
|
427
|
+
result.push({
|
|
428
|
+
name: nowPrice.symbolName,
|
|
429
|
+
nowPrice: nowPrice.price,
|
|
430
|
+
oneMonthChart: import_koishi.h.image(imgSrc)
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
for (const item of result) {
|
|
434
|
+
await session.send([
|
|
435
|
+
import_koishi.h.text((/* @__PURE__ */ new Date()).toLocaleString()),
|
|
436
|
+
import_koishi.h.text(`
|
|
437
|
+
${item.name}
|
|
438
|
+
当前价格: `),
|
|
439
|
+
import_koishi.h.text(`${item.nowPrice}
|
|
440
|
+
近30天价格走势: `),
|
|
441
|
+
item.oneMonthChart
|
|
442
|
+
]);
|
|
443
|
+
}
|
|
444
|
+
} catch (err) {
|
|
445
|
+
const message = err instanceof Error ? err.message : typeof err === "string" ? err : JSON.stringify(err);
|
|
446
|
+
await session.send(`查询失败:${message}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
__name(getExchangeRate, "getExchangeRate");
|
|
400
450
|
function parseJsonControl(text) {
|
|
401
451
|
try {
|
|
402
452
|
return JSON.parse(text);
|
|
@@ -618,15 +668,239 @@ function ready(session, cfg, param, readyMap) {
|
|
|
618
668
|
return returnMessage;
|
|
619
669
|
}
|
|
620
670
|
__name(ready, "ready");
|
|
671
|
+
async function updateMvpAPIKey() {
|
|
672
|
+
try {
|
|
673
|
+
const htmlResp = await fetch("https://www.msn.com/zh-cn/money/tools/currencyconverter");
|
|
674
|
+
if (!htmlResp.ok) throw new Error(`Failed to fetch page: ${htmlResp.status}`);
|
|
675
|
+
const html = await htmlResp.text();
|
|
676
|
+
const $ = cheerio.load(html);
|
|
677
|
+
const head = $("head");
|
|
678
|
+
const clientSettingsRaw = head.attr("data-client-settings");
|
|
679
|
+
if (!clientSettingsRaw) throw new Error("未找到 data-client-settings 属性");
|
|
680
|
+
const clientSettings = JSON.parse(clientSettingsRaw.replace(/"/g, '"'));
|
|
681
|
+
const version = clientSettings?.bundleInfo?.v;
|
|
682
|
+
if (!version) throw new Error("未找到 bundleInfo.v");
|
|
683
|
+
const targetScope = encodeURIComponent(JSON.stringify({
|
|
684
|
+
audienceMode: "adult",
|
|
685
|
+
browser: { browserType: "edgeChromium", version: "142", ismobile: "false" },
|
|
686
|
+
deviceFormFactor: "desktop",
|
|
687
|
+
domain: "www.msn.com",
|
|
688
|
+
locale: { content: { language: "zh", market: "cn" }, display: { language: "zh", market: "cn" } },
|
|
689
|
+
os: "windows",
|
|
690
|
+
modes: { audienceMode: "adult" },
|
|
691
|
+
platform: "web",
|
|
692
|
+
pageType: "finance::financetools::financecurrencyconverter",
|
|
693
|
+
pageExperiments: ["prg-cmc-river"]
|
|
694
|
+
}));
|
|
695
|
+
const apiUrl = `https://assets.msn.com/resolver/api/resolve/v3/config/?expType=AppConfig&apptype=finance&v=${version}&targetScope=${targetScope}`;
|
|
696
|
+
const apiResp = await fetch(apiUrl);
|
|
697
|
+
if (!apiResp.ok) throw new Error(`Failed to fetch config API: ${apiResp.status}`);
|
|
698
|
+
const json = await apiResp.json();
|
|
699
|
+
const mvpAPIkey = json?.configs?.["shared/msn-ns/CommonAutoSuggest/default"]?.properties?.["mvpAPIkey"] ?? null;
|
|
700
|
+
return mvpAPIkey;
|
|
701
|
+
} catch (err) {
|
|
702
|
+
console.error(err);
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
__name(updateMvpAPIKey, "updateMvpAPIKey");
|
|
707
|
+
var mvpAPIKey;
|
|
708
|
+
async function getMvpAPIKey() {
|
|
709
|
+
return mvpAPIKey ? mvpAPIKey : updateMvpAPIKey();
|
|
710
|
+
}
|
|
711
|
+
__name(getMvpAPIKey, "getMvpAPIKey");
|
|
712
|
+
async function getExchangeGuids(symbols, apikey) {
|
|
713
|
+
try {
|
|
714
|
+
const url = `https://assets.msn.cn/service/Finance/IdMap?apikey=${apikey}&MStarIds=${encodeURIComponent(symbols)}`;
|
|
715
|
+
const resp = await fetch(url);
|
|
716
|
+
if (!resp.ok) throw new Error(`请求失败: ${resp.status}`);
|
|
717
|
+
const data = await resp.json();
|
|
718
|
+
const inputSymbols = symbols.split(",");
|
|
719
|
+
const guidMap = new Map(data.map((item) => [item.mStarId, item.guid]));
|
|
720
|
+
return inputSymbols.map((sym) => guidMap.get(sym) || "");
|
|
721
|
+
} catch (err) {
|
|
722
|
+
console.error(err);
|
|
723
|
+
return [];
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
__name(getExchangeGuids, "getExchangeGuids");
|
|
727
|
+
async function getQuotes(ids, apiKey) {
|
|
728
|
+
try {
|
|
729
|
+
const url = `https://assets.msn.com/service/Finance/Quotes?apikey=${encodeURIComponent(apiKey)}&cm=zh-cn&it=edgeid&ids=${encodeURIComponent(ids)}&wrapodata=false`;
|
|
730
|
+
const resp = await fetch(url);
|
|
731
|
+
if (!resp.ok) throw new Error(`请求失败: ${resp.status}`);
|
|
732
|
+
const data = await resp.json();
|
|
733
|
+
let quotes = [];
|
|
734
|
+
if (Array.isArray(data)) {
|
|
735
|
+
if (Array.isArray(data[0])) {
|
|
736
|
+
quotes = data.flat();
|
|
737
|
+
} else {
|
|
738
|
+
quotes = data;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const inputIds = ids.split(",");
|
|
742
|
+
const quoteMap = new Map(
|
|
743
|
+
quotes.map((q) => [
|
|
744
|
+
q.instrumentId,
|
|
745
|
+
{
|
|
746
|
+
price: q.price,
|
|
747
|
+
symbolName: q.localizedAttributes?.["zh-cn"]?.symbolName || q.symbol
|
|
748
|
+
}
|
|
749
|
+
])
|
|
750
|
+
);
|
|
751
|
+
return inputIds.map((id) => quoteMap.get(id) ?? { price: 0, symbolName: "" });
|
|
752
|
+
} catch (err) {
|
|
753
|
+
console.error(err);
|
|
754
|
+
return [];
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
__name(getQuotes, "getQuotes");
|
|
758
|
+
async function getMonthMSNClosePrices(ids, apiKey) {
|
|
759
|
+
const url = `https://assets.msn.com/service/Finance/QuoteSummary?apikey=${apiKey}&ids=${ids}&intents=Charts,Exchanges&type=1M1H&wrapodata=false`;
|
|
760
|
+
const res = await fetch(url);
|
|
761
|
+
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
|
|
762
|
+
let data = await res.json();
|
|
763
|
+
if (Array.isArray(data) && data.length && Array.isArray(data[0])) {
|
|
764
|
+
data = data.flat();
|
|
765
|
+
}
|
|
766
|
+
if (!Array.isArray(data)) {
|
|
767
|
+
throw new Error("Unexpected API response format");
|
|
768
|
+
}
|
|
769
|
+
return data.map((item) => {
|
|
770
|
+
const series = item.chart?.series;
|
|
771
|
+
return {
|
|
772
|
+
prices: series?.prices || [],
|
|
773
|
+
timeStamps: series?.timeStamps || []
|
|
774
|
+
};
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
__name(getMonthMSNClosePrices, "getMonthMSNClosePrices");
|
|
778
|
+
async function drawOneMonthChartSkia(prices, timeStamps, width = 1200, height = 600) {
|
|
779
|
+
if (!prices.length || !timeStamps.length || prices.length !== timeStamps.length) return;
|
|
780
|
+
const dataPoints = timeStamps.map((t, i) => ({ x: new Date(t), y: prices[i] }));
|
|
781
|
+
const canvas = new import_skia_canvas.Canvas(width, height);
|
|
782
|
+
const whiteBackground = {
|
|
783
|
+
id: "whiteBackground",
|
|
784
|
+
beforeDraw(chart) {
|
|
785
|
+
const { ctx, width: width2, height: height2 } = chart;
|
|
786
|
+
ctx.save();
|
|
787
|
+
ctx.fillStyle = "#ffffff";
|
|
788
|
+
ctx.fillRect(0, 0, width2, height2);
|
|
789
|
+
ctx.restore();
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
new import_chart.Chart(canvas, {
|
|
793
|
+
type: "line",
|
|
794
|
+
data: {
|
|
795
|
+
datasets: [{
|
|
796
|
+
label: "兑换汇率",
|
|
797
|
+
data: dataPoints,
|
|
798
|
+
showLine: true,
|
|
799
|
+
// ⭐ 不连线,只画点
|
|
800
|
+
pointRadius: 0,
|
|
801
|
+
// 小点
|
|
802
|
+
pointBorderWidth: 0,
|
|
803
|
+
// ⭐ 去掉外边框 → 变成实心点
|
|
804
|
+
borderWidth: 2,
|
|
805
|
+
borderColor: "blue",
|
|
806
|
+
backgroundColor: "rgba(0,0,255,0.1)",
|
|
807
|
+
tension: 0.2
|
|
808
|
+
}]
|
|
809
|
+
},
|
|
810
|
+
options: {
|
|
811
|
+
responsive: false,
|
|
812
|
+
scales: {
|
|
813
|
+
x: {
|
|
814
|
+
type: "time",
|
|
815
|
+
time: {
|
|
816
|
+
parser: "YYYY-MM-DD HH:mm:ss",
|
|
817
|
+
tooltipFormat: "YYYY/MM/DD HH:mm",
|
|
818
|
+
displayFormats: {
|
|
819
|
+
hour: "HH:mm",
|
|
820
|
+
day: "MM-DD"
|
|
821
|
+
},
|
|
822
|
+
unit: "day"
|
|
823
|
+
},
|
|
824
|
+
title: { display: true, text: "日期" }
|
|
825
|
+
},
|
|
826
|
+
y: {
|
|
827
|
+
title: { display: true, text: "价格" }
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
layout: {
|
|
831
|
+
padding: { top: 20, bottom: 20, left: 20, right: 40 }
|
|
832
|
+
}
|
|
833
|
+
},
|
|
834
|
+
plugins: [whiteBackground]
|
|
835
|
+
});
|
|
836
|
+
return await canvas.toBuffer("png");
|
|
837
|
+
}
|
|
838
|
+
__name(drawOneMonthChartSkia, "drawOneMonthChartSkia");
|
|
621
839
|
|
|
622
840
|
// src/index.ts
|
|
623
841
|
var import_node_path2 = __toESM(require("node:path"));
|
|
624
842
|
var import_mime_types = __toESM(require("mime-types"));
|
|
843
|
+
|
|
844
|
+
// package.json
|
|
845
|
+
var package_default = {
|
|
846
|
+
name: "koishi-plugin-starfx-bot",
|
|
847
|
+
description: "StarFreedomX机器人的小功能,自用",
|
|
848
|
+
contributors: [
|
|
849
|
+
"StarFreedomX <starfreedomx@outlook.com>"
|
|
850
|
+
],
|
|
851
|
+
version: "0.19.0",
|
|
852
|
+
main: "lib/index.js",
|
|
853
|
+
typings: "lib/index.d.ts",
|
|
854
|
+
files: [
|
|
855
|
+
"lib",
|
|
856
|
+
"dist",
|
|
857
|
+
"assets"
|
|
858
|
+
],
|
|
859
|
+
license: "MIT",
|
|
860
|
+
homepage: "https://github.com/StarFreedomX/starfx-bot",
|
|
861
|
+
repository: {
|
|
862
|
+
type: "git",
|
|
863
|
+
url: "git+https://github.com/StarFreedomX/starfx-bot.git"
|
|
864
|
+
},
|
|
865
|
+
scripts: {},
|
|
866
|
+
keywords: [
|
|
867
|
+
"chatbot",
|
|
868
|
+
"koishi",
|
|
869
|
+
"plugin"
|
|
870
|
+
],
|
|
871
|
+
koishi: {
|
|
872
|
+
description: {
|
|
873
|
+
zh: "starfx的机器人小插件,目前支持: 明日方舟封印功能 闲鱼“卖掉了”功能 艾特我/他又不说话 我才不是机器人! 我也喜欢你! BanG Dream!边框绘制 群语录 roll 待机人数记录 房间号记录 推特查看原图 复读 echo 撤回 查汇率 黑白名单配置"
|
|
874
|
+
},
|
|
875
|
+
service: {},
|
|
876
|
+
preview: true,
|
|
877
|
+
hidden: false
|
|
878
|
+
},
|
|
879
|
+
peerDependencies: {
|
|
880
|
+
koishi: "^4.18.7"
|
|
881
|
+
},
|
|
882
|
+
dependencies: {
|
|
883
|
+
axios: "^1.9.0",
|
|
884
|
+
"chart.js": "^4.5.1",
|
|
885
|
+
"chartjs-adapter-dayjs-3": "^1.2.3",
|
|
886
|
+
cheerio: "^1.0.0",
|
|
887
|
+
dayjs: "^1.11.19",
|
|
888
|
+
"http-proxy-agent": "^7.0.2",
|
|
889
|
+
"https-proxy-agent": "^7.0.6",
|
|
890
|
+
jimp: "^1.6.0",
|
|
891
|
+
luxon: "^3.7.2",
|
|
892
|
+
"mime-types": "^3.0.1",
|
|
893
|
+
"rss-parser": "^3.13.0",
|
|
894
|
+
sharp: "^0.34.1",
|
|
895
|
+
"skia-canvas": "^3.0.8"
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
// src/index.ts
|
|
625
900
|
var name = "starfx-bot";
|
|
626
901
|
var baseDir;
|
|
627
902
|
var assetsDir;
|
|
628
903
|
var starfxLogger = new import_koishi2.Logger("starfx-bot");
|
|
629
|
-
var inject = ["database"];
|
|
630
904
|
var repeatContextMap = /* @__PURE__ */ new Map();
|
|
631
905
|
var Config2 = import_koishi2.Schema.intersect([
|
|
632
906
|
import_koishi2.Schema.object({
|
|
@@ -652,7 +926,9 @@ var Config2 = import_koishi2.Schema.intersect([
|
|
|
652
926
|
saveReadyAsFile: import_koishi2.Schema.string().description("写入待机人数的本地地址,留空则不写入"),
|
|
653
927
|
roomNumber: import_koishi2.Schema.boolean().default(false).description("主跑房间号记录功能"),
|
|
654
928
|
saveRoomAsFile: import_koishi2.Schema.string().description("写入房间号的本地地址,留空则不写入"),
|
|
655
|
-
forward: import_koishi2.Schema.boolean().default(true).description("消息转发功能")
|
|
929
|
+
forward: import_koishi2.Schema.boolean().default(true).description("消息转发功能"),
|
|
930
|
+
searchExchangeRate: import_koishi2.Schema.boolean().default(false).description("查汇率功能"),
|
|
931
|
+
intervalGetExchangeRate: import_koishi2.Schema.boolean().default(false).description("汇率定时推送功能").hidden()
|
|
656
932
|
}).description("指令小功能"),
|
|
657
933
|
import_koishi2.Schema.object({
|
|
658
934
|
atNotSay: import_koishi2.Schema.boolean().default(true).description("开启‘艾特我又不说话’功能"),
|
|
@@ -674,8 +950,8 @@ var Config2 = import_koishi2.Schema.intersect([
|
|
|
674
950
|
repeatPossibility: import_koishi2.Schema.number().role("slider").min(0).max(1).step(0.01).default(0.3).description("复读发生概率")
|
|
675
951
|
}).description("复读功能"),
|
|
676
952
|
import_koishi2.Schema.object({
|
|
677
|
-
|
|
678
|
-
|
|
953
|
+
filePathToBase64: import_koishi2.Schema.boolean().default(false).description("在消息发送前检查是否有file://,如果有那么转换为base64再发送"),
|
|
954
|
+
originImg: import_koishi2.Schema.boolean().default(false).description("根据链接获取原图开关")
|
|
679
955
|
}).description("自用功能"),
|
|
680
956
|
import_koishi2.Schema.union([
|
|
681
957
|
import_koishi2.Schema.object({
|
|
@@ -877,6 +1153,21 @@ function apply(ctx, cfg) {
|
|
|
877
1153
|
}
|
|
878
1154
|
});
|
|
879
1155
|
}
|
|
1156
|
+
if (cfg.searchExchangeRate) {
|
|
1157
|
+
ctx.command("查汇率 [exchangeParam:string]").action(async ({ session }, exchangeParam) => {
|
|
1158
|
+
if (detectControl(controlJson, session.guildId, "exchangeRate")) {
|
|
1159
|
+
return await getExchangeRate(ctx, cfg, session, exchangeParam);
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
if (cfg.intervalGetExchangeRate) {
|
|
1164
|
+
ctx.command("开启汇率推送 [exchangeParam:string]").action(async ({ session }, exchangeParam) => {
|
|
1165
|
+
if (detectControl(controlJson, session.guildId, "exchangeRate")) {
|
|
1166
|
+
const exchangeRatePath = import_node_path2.default.join(assetsDir, "exchangeRate.json");
|
|
1167
|
+
return await intervalGetExchangeRate(ctx, cfg, session, exchangeParam, exchangeRatePath);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
880
1171
|
if (cfg.filePathToBase64) {
|
|
881
1172
|
let isLocalPath = function(src) {
|
|
882
1173
|
return /^([A-Za-z]:\\|\\\\|\/home\/|\/root\/)/.test(src);
|
|
@@ -945,16 +1236,27 @@ function apply(ctx, cfg) {
|
|
|
945
1236
|
});
|
|
946
1237
|
}
|
|
947
1238
|
function initAssets() {
|
|
948
|
-
const
|
|
949
|
-
assetsDir =
|
|
950
|
-
if (!fs2.existsSync(
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1239
|
+
const defaultAssetsDir = import_node_path2.default.join(__dirname, "../assets");
|
|
1240
|
+
assetsDir = import_node_path2.default.join(ctx.baseDir, "data/starfx-bot/assets");
|
|
1241
|
+
if (!fs2.existsSync(assetsDir)) fs2.mkdirSync(assetsDir, { recursive: true });
|
|
1242
|
+
const versionFile = import_node_path2.default.join(assetsDir, "plugin_version.json");
|
|
1243
|
+
let localVersion = "0";
|
|
1244
|
+
if (fs2.existsSync(versionFile)) {
|
|
1245
|
+
try {
|
|
1246
|
+
localVersion = JSON.parse(fs2.readFileSync(versionFile, "utf-8")).version || "0";
|
|
1247
|
+
} catch {
|
|
1248
|
+
}
|
|
954
1249
|
}
|
|
955
|
-
|
|
956
|
-
if (
|
|
957
|
-
|
|
1250
|
+
const pluginVersion = package_default.version;
|
|
1251
|
+
if (pluginVersion > localVersion) {
|
|
1252
|
+
try {
|
|
1253
|
+
if (fs2.existsSync(defaultAssetsDir)) {
|
|
1254
|
+
fs2.cpSync(defaultAssetsDir, assetsDir, { recursive: true, force: true });
|
|
1255
|
+
}
|
|
1256
|
+
fs2.writeFileSync(versionFile, JSON.stringify({ version: pluginVersion }));
|
|
1257
|
+
} catch (err) {
|
|
1258
|
+
console.error("initAssets copy failed:", err);
|
|
1259
|
+
}
|
|
958
1260
|
}
|
|
959
1261
|
}
|
|
960
1262
|
__name(initAssets, "initAssets");
|
|
@@ -966,7 +1268,6 @@ __name(apply, "apply");
|
|
|
966
1268
|
apply,
|
|
967
1269
|
assetsDir,
|
|
968
1270
|
baseDir,
|
|
969
|
-
inject,
|
|
970
1271
|
name,
|
|
971
1272
|
repeatContextMap,
|
|
972
1273
|
starfxLogger,
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Context, h, Session } from "koishi";
|
|
2
2
|
import sharp from "sharp";
|
|
3
3
|
import { Config } from "./index";
|
|
4
|
+
import 'chartjs-adapter-dayjs-3';
|
|
4
5
|
interface FeatureControl {
|
|
5
6
|
[feature: string]: {
|
|
6
7
|
whitelist: boolean;
|
|
@@ -128,6 +129,8 @@ export declare function drawBanGDream(avatar: string, inputOptions?: {
|
|
|
128
129
|
starNum: number;
|
|
129
130
|
border: string;
|
|
130
131
|
}): Promise<string>;
|
|
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>;
|
|
131
134
|
export declare function parseJsonControl(text: string): FeatureControl | null;
|
|
132
135
|
export declare function detectControl(controlJson: FeatureControl, guildId: string, funName: string): boolean;
|
|
133
136
|
export declare function handleRoll(session: Session): string;
|
|
@@ -147,4 +150,13 @@ export declare function test(url: string): Promise<void>;
|
|
|
147
150
|
export declare function writeMap(map: Map<any, any>, dest: string): void;
|
|
148
151
|
export declare function readMap(url: string): Map<any, any>;
|
|
149
152
|
export declare function ready(session: Session, cfg: Config, param: string, readyMap: Map<string, string[]>): string;
|
|
153
|
+
/**
|
|
154
|
+
* 绘制近一个月收盘价走势图
|
|
155
|
+
* @param prices 收盘价数组
|
|
156
|
+
* @param timeStamps ISO 时间戳数组
|
|
157
|
+
* @param width 图表宽度
|
|
158
|
+
* @param height 图表高度
|
|
159
|
+
* @returns buffer
|
|
160
|
+
*/
|
|
161
|
+
export declare function drawOneMonthChartSkia(prices: number[], timeStamps: string[], width?: number, height?: number): Promise<Buffer>;
|
|
150
162
|
export {};
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"contributors": [
|
|
5
5
|
"StarFreedomX <starfreedomx@outlook.com>"
|
|
6
6
|
],
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.19.0",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"typings": "lib/index.d.ts",
|
|
10
10
|
"files": [
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"koishi": {
|
|
28
28
|
"description": {
|
|
29
|
-
"zh": "starfx
|
|
29
|
+
"zh": "starfx的机器人小插件,目前支持: 明日方舟封印功能 闲鱼“卖掉了”功能 艾特我/他又不说话 我才不是机器人! 我也喜欢你! BanG Dream!边框绘制 群语录 roll 待机人数记录 房间号记录 推特查看原图 复读 echo 撤回 查汇率 黑白名单配置"
|
|
30
30
|
},
|
|
31
31
|
"service": {},
|
|
32
32
|
"preview": true,
|
|
@@ -37,12 +37,17 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"axios": "^1.9.0",
|
|
40
|
+
"chart.js": "^4.5.1",
|
|
41
|
+
"chartjs-adapter-dayjs-3": "^1.2.3",
|
|
40
42
|
"cheerio": "^1.0.0",
|
|
43
|
+
"dayjs": "^1.11.19",
|
|
41
44
|
"http-proxy-agent": "^7.0.2",
|
|
42
45
|
"https-proxy-agent": "^7.0.6",
|
|
43
46
|
"jimp": "^1.6.0",
|
|
47
|
+
"luxon": "^3.7.2",
|
|
44
48
|
"mime-types": "^3.0.1",
|
|
45
49
|
"rss-parser": "^3.13.0",
|
|
46
|
-
"sharp": "^0.34.1"
|
|
50
|
+
"sharp": "^0.34.1",
|
|
51
|
+
"skia-canvas": "^3.0.8"
|
|
47
52
|
}
|
|
48
53
|
}
|
package/readme.md
CHANGED
|
@@ -13,14 +13,19 @@ StarFreedomX机器人的小功能,自用
|
|
|
13
13
|
* 我也喜欢你!
|
|
14
14
|
* BanG Dream!边框绘制
|
|
15
15
|
* 群语录
|
|
16
|
+
* roll
|
|
17
|
+
* 待机人数记录
|
|
18
|
+
* 房间号记录
|
|
19
|
+
* 推特查看原图
|
|
16
20
|
* 复读
|
|
17
21
|
* echo
|
|
18
22
|
* 撤回
|
|
23
|
+
* 汇率推送
|
|
19
24
|
* 黑白名单配置
|
|
20
25
|
|
|
21
26
|
## List to Do
|
|
22
27
|
|
|
23
|
-
* 修改BanG Dream!边框的绘制为使用sharp
|
|
28
|
+
* 修改BanG Dream!边框的绘制为使用sharp等库,加快速度
|
|
24
29
|
|
|
25
30
|
## 语录tag可视化控制
|
|
26
31
|
|
|
@@ -60,6 +65,7 @@ StarFreedomX机器人的小功能,自用
|
|
|
60
65
|
| `originImg` | rss获取推特原图 |
|
|
61
66
|
| `sendLocalImage` | 自定义指令发送图片功能(自定义指令也可配置,键为自定义的指令名称) |
|
|
62
67
|
| `forward` | 消息转发功能 |
|
|
68
|
+
| `exchangeRate` | 汇率播报功能 |
|
|
63
69
|
|
|
64
70
|
---
|
|
65
71
|
|
|
@@ -108,3 +114,5 @@ StarFreedomX机器人的小功能,自用
|
|
|
108
114
|
| `0.17.1` | 修复语录链接跨群bug |
|
|
109
115
|
| `0.18.0` | 新增定时echo |
|
|
110
116
|
| `0.18.1` | 修复艾特不说话功能 |
|
|
117
|
+
| `0.18.2` | 不直接操作插件目录,防止更新问题 |
|
|
118
|
+
| `0.19.0` | 新增查汇率功能 |
|