koishi-plugin-warframe 1.13.0 → 1.14.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.js CHANGED
@@ -1288,7 +1288,8 @@ var messages = {
1288
1288
  "bounty.unavailable": "当前该地点暂无可用赏金",
1289
1289
  "invasion.unavailable": "当前没有入侵",
1290
1290
  "alert.unavailable": "当前没有警报",
1291
- "steelpath.unavailable": "钢铁精华商店获取失败",
1291
+ "calendar.unavailable": "当前没有1999日历",
1292
+ "steelEssence.unavailable": "钢铁精华商店获取失败",
1292
1293
  "nightwave.unavailable": "当前没有午夜电波",
1293
1294
  "sortie.unavailable": "当前没有突击",
1294
1295
  "voidTrader.drifting": "虚空商人仍在未知地带漂流...",
@@ -1614,7 +1615,7 @@ async function getHotRivenWeapons2() {
1614
1615
  __name(getHotRivenWeapons2, "getHotRivenWeapons");
1615
1616
 
1616
1617
  // src/warframe/services/wf-service.ts
1617
- var import_warframe_public_export_plus10 = require("warframe-public-export-plus");
1618
+ var import_warframe_public_export_plus11 = require("warframe-public-export-plus");
1618
1619
 
1619
1620
  // src/warframe/assets/index.ts
1620
1621
  var {
@@ -1702,6 +1703,15 @@ function extractInvasionsRaw(json) {
1702
1703
  }
1703
1704
  }
1704
1705
  __name(extractInvasionsRaw, "extractInvasionsRaw");
1706
+ function extractCalendarRaw(json) {
1707
+ try {
1708
+ const data = JSON.parse(json);
1709
+ return Array.isArray(data.KnownCalendarSeasons) && data.KnownCalendarSeasons.length > 0 ? data.KnownCalendarSeasons[0] : void 0;
1710
+ } catch {
1711
+ return void 0;
1712
+ }
1713
+ }
1714
+ __name(extractCalendarRaw, "extractCalendarRaw");
1705
1715
  async function fetchWorldStateJson() {
1706
1716
  return fetchAsyncText(`${apiBase}worldState.php`);
1707
1717
  }
@@ -2076,6 +2086,7 @@ var globalWorldState = createAsyncCache(async () => {
2076
2086
  const seasonInfoRaw = extractSeasonInfoRaw(json);
2077
2087
  const sortieRaw = extractSortieRaw(json);
2078
2088
  const invasionsRaw = extractInvasionsRaw(json);
2089
+ const calendarRaw = extractCalendarRaw(json);
2079
2090
  const worldState = await getWorldState(json);
2080
2091
  const fissures = [];
2081
2092
  const rjFissures = [];
@@ -2099,6 +2110,7 @@ var globalWorldState = createAsyncCache(async () => {
2099
2110
  seasonInfoRaw,
2100
2111
  sortieRaw,
2101
2112
  invasionsRaw,
2113
+ calendarRaw,
2102
2114
  fissures,
2103
2115
  spFissures,
2104
2116
  rjFissures
@@ -2579,11 +2591,11 @@ function rarityFromStageProbabilities(probability, uniqueSortedDesc) {
2579
2591
  }
2580
2592
  __name(rarityFromStageProbabilities, "rarityFromStageProbabilities");
2581
2593
  var blueprintNameCache = /* @__PURE__ */ new Map();
2582
- function resolveBlueprintNameViaDictZh(pathLeaf) {
2583
- if (!/Blueprint$/i.test(pathLeaf)) {
2594
+ function resolveBlueprintNameViaDictZh(pathLeaf2) {
2595
+ if (!/Blueprint$/i.test(pathLeaf2)) {
2584
2596
  return void 0;
2585
2597
  }
2586
- const base = pathLeaf.replace(/Blueprint$/i, "");
2598
+ const base = pathLeaf2.replace(/Blueprint$/i, "");
2587
2599
  if (!base) {
2588
2600
  return void 0;
2589
2601
  }
@@ -2674,8 +2686,8 @@ function resolveExportItemNameZh(sourceKey) {
2674
2686
  if (boosterPack?.name) {
2675
2687
  return import_warframe_public_export_plus8.dict_zh[boosterPack.name] ?? boosterPack.name;
2676
2688
  }
2677
- const pathLeaf = fixedKey.split("/").pop() ?? sourceKey;
2678
- return resolveBlueprintNameViaDictZh(pathLeaf) ?? pathLeaf;
2689
+ const pathLeaf2 = fixedKey.split("/").pop() ?? sourceKey;
2690
+ return resolveBlueprintNameViaDictZh(pathLeaf2) ?? pathLeaf2;
2679
2691
  }
2680
2692
  __name(resolveExportItemNameZh, "resolveExportItemNameZh");
2681
2693
  function rewardsFromDeck(rewardsKey) {
@@ -2834,8 +2846,188 @@ function adaptOracleBountyBoard(location, cycle) {
2834
2846
  }
2835
2847
  __name(adaptOracleBountyBoard, "adaptOracleBountyBoard");
2836
2848
 
2837
- // src/warframe/infrastructure/wf/nightwave-adapter.ts
2849
+ // src/warframe/infrastructure/wf/calendar-adapter.ts
2838
2850
  var import_warframe_public_export_plus9 = require("warframe-public-export-plus");
2851
+ var CALENDAR_SEASON_HEADER_KEY = "/Lotus/Language/1999/CalendarSeasonHeader";
2852
+ var CALENDAR_HEADER_KEY = "/Lotus/Language/1999/CalendarHeader";
2853
+ var SEASON_KEYS = {
2854
+ CST_SPRING: "/Lotus/Language/1999/CalendarSeasonSpring",
2855
+ CST_SUMMER: "/Lotus/Language/1999/CalendarSeasonSummer",
2856
+ CST_FALL: "/Lotus/Language/1999/CalendarSeasonFall",
2857
+ CST_WINTER: "/Lotus/Language/1999/CalendarSeasonWinter"
2858
+ };
2859
+ var EVENT_TYPE_KEYS = {
2860
+ CET_CHALLENGE: {
2861
+ kind: "challenge",
2862
+ labelKey: "/Lotus/Language/1999/CalendarEvent_Challenge"
2863
+ },
2864
+ CET_UPGRADE: {
2865
+ kind: "upgrade",
2866
+ labelKey: "/Lotus/Language/1999/CalendarEvent_Modifiers"
2867
+ },
2868
+ CET_REWARD: {
2869
+ kind: "reward",
2870
+ labelKey: "/Lotus/Language/1999/CalendarEvent_Reward"
2871
+ },
2872
+ CET_PLOT: {
2873
+ kind: "birthday",
2874
+ labelKey: "/Lotus/Language/1999/CalendarEvent_BDay"
2875
+ }
2876
+ };
2877
+ function calendarTimestampMs(value) {
2878
+ const raw = value?.$date?.$numberLong;
2879
+ if (raw === void 0 || raw === "") {
2880
+ return 0;
2881
+ }
2882
+ const ms = Number(raw);
2883
+ return Number.isFinite(ms) ? ms : 0;
2884
+ }
2885
+ __name(calendarTimestampMs, "calendarTimestampMs");
2886
+ function translate(key, fallback = key) {
2887
+ return stripLotusMarkup(import_warframe_public_export_plus9.dict_zh[key] ?? dictZhExtra[key] ?? fallback);
2888
+ }
2889
+ __name(translate, "translate");
2890
+ function seasonLabel(season) {
2891
+ const key = SEASON_KEYS[season];
2892
+ if (!key) {
2893
+ return season.replace(/^CST_/, "");
2894
+ }
2895
+ return translate(key);
2896
+ }
2897
+ __name(seasonLabel, "seasonLabel");
2898
+ function calendarDateLabel(dayOfYear) {
2899
+ const date = new Date(Date.UTC(1999, 0, 1));
2900
+ date.setUTCDate(dayOfYear);
2901
+ return `${date.getUTCFullYear()}年${date.getUTCMonth() + 1}月${date.getUTCDate()}日`;
2902
+ }
2903
+ __name(calendarDateLabel, "calendarDateLabel");
2904
+ function pathLeaf(path) {
2905
+ return path.split("/").pop() ?? path;
2906
+ }
2907
+ __name(pathLeaf, "pathLeaf");
2908
+ function stripLotusMarkup(text) {
2909
+ const withoutPipes = text.replaceAll("|OPEN_COLOR|", "").replaceAll("|CLOSE_COLOR|", "");
2910
+ const withoutTags = withoutPipes.split("<").map((part, index) => {
2911
+ if (index === 0) {
2912
+ return part;
2913
+ }
2914
+ const close = part.indexOf(">");
2915
+ return close === -1 ? "" : part.slice(close + 1);
2916
+ }).join("");
2917
+ return withoutTags.replaceAll("<", "").replaceAll(">", "").replace(/\s+/g, " ").trim();
2918
+ }
2919
+ __name(stripLotusMarkup, "stripLotusMarkup");
2920
+ function adaptChallenge(path) {
2921
+ const exported = import_warframe_public_export_plus9.ExportChallenges[path];
2922
+ if (!exported?.name) {
2923
+ return { name: pathLeaf(path) };
2924
+ }
2925
+ const descriptionKey = exported.description;
2926
+ const description = descriptionKey ? translate(descriptionKey).split("|COUNT|").join(String(exported.requiredCount ?? 0)) : void 0;
2927
+ return {
2928
+ name: translate(exported.name),
2929
+ ...description ? { description } : {}
2930
+ };
2931
+ }
2932
+ __name(adaptChallenge, "adaptChallenge");
2933
+ function adaptUpgrade(path) {
2934
+ const leaf = pathLeaf(path);
2935
+ const name2 = translate(`/Lotus/Language/1999/${leaf}Name`, leaf);
2936
+ const description = translate(`/Lotus/Language/1999/${leaf}Desc`, "");
2937
+ return {
2938
+ name: name2,
2939
+ ...description ? { description } : {}
2940
+ };
2941
+ }
2942
+ __name(adaptUpgrade, "adaptUpgrade");
2943
+ var MESSENGER_NAME_ALIASES = {
2944
+ Amir: "Jabir"
2945
+ };
2946
+ function plotCharacterId(raw) {
2947
+ const fromConvo = raw.dialogueConvo?.match(/^([A-Za-z]+)BirthdayConvo$/)?.[1];
2948
+ if (fromConvo) {
2949
+ return fromConvo;
2950
+ }
2951
+ return pathLeaf(raw.dialogueName ?? "").match(/^([A-Za-z]+)Dialogue/)?.[1];
2952
+ }
2953
+ __name(plotCharacterId, "plotCharacterId");
2954
+ function plotCharacterName(id) {
2955
+ if (!id) {
2956
+ return void 0;
2957
+ }
2958
+ const keyIds = [id];
2959
+ const alias = MESSENGER_NAME_ALIASES[id];
2960
+ if (alias) {
2961
+ keyIds.push(alias);
2962
+ }
2963
+ for (const keyId of keyIds) {
2964
+ const key = `/Lotus/Language/1999/Messenger${keyId}Name`;
2965
+ const text = import_warframe_public_export_plus9.dict_zh[key] ?? dictZhExtra[key];
2966
+ if (text) {
2967
+ return stripLotusMarkup(text);
2968
+ }
2969
+ }
2970
+ return void 0;
2971
+ }
2972
+ __name(plotCharacterName, "plotCharacterName");
2973
+ function adaptPlot(raw) {
2974
+ const id = plotCharacterId(raw);
2975
+ const name2 = plotCharacterName(id) ?? id ?? pathLeaf(raw.dialogueConvo ?? raw.dialogueName ?? "");
2976
+ const description = translate("/Lotus/Language/1999/CalendarEvent_BirthdayLabel").split("|NAME|").join(name2);
2977
+ return {
2978
+ name: name2,
2979
+ ...description ? { description } : {}
2980
+ };
2981
+ }
2982
+ __name(adaptPlot, "adaptPlot");
2983
+ function adaptEvent(raw) {
2984
+ const type = raw.type ?? "";
2985
+ const mapped = EVENT_TYPE_KEYS[type];
2986
+ const kind = mapped?.kind ?? "other";
2987
+ const kindLabel = mapped ? translate(mapped.labelKey) : type;
2988
+ if (raw.challenge) {
2989
+ return { kind, kindLabel, ...adaptChallenge(raw.challenge) };
2990
+ }
2991
+ if (raw.upgrade) {
2992
+ return { kind, kindLabel, ...adaptUpgrade(raw.upgrade) };
2993
+ }
2994
+ if (raw.reward) {
2995
+ return { kind, kindLabel, name: stripLotusMarkup(resolveExportItemNameZh(raw.reward)) };
2996
+ }
2997
+ if (raw.dialogueName || raw.dialogueConvo) {
2998
+ return { kind, kindLabel, ...adaptPlot(raw) };
2999
+ }
3000
+ return void 0;
3001
+ }
3002
+ __name(adaptEvent, "adaptEvent");
3003
+ function adaptCalendar(raw, now = Date.now()) {
3004
+ const seasonCode = raw?.Season ?? "";
3005
+ const season = seasonLabel(seasonCode);
3006
+ const header = translate(CALENDAR_SEASON_HEADER_KEY, translate(CALENDAR_HEADER_KEY));
3007
+ const title = header.includes("|SEASON|") ? header.split("|SEASON|").join(season) : header;
3008
+ const expiry = calendarTimestampMs(raw?.Expiry);
3009
+ const days = [...raw?.Days ?? []].map((entry) => {
3010
+ const day = entry.day ?? 0;
3011
+ const events = (entry.events ?? []).map(adaptEvent).filter((event) => event !== void 0);
3012
+ return { day, events };
3013
+ }).filter((entry) => entry.day > 0).sort((a, b) => a.day - b.day);
3014
+ const taggedDays = days.filter((entry) => entry.events.length > 0).map((entry) => ({
3015
+ day: entry.day,
3016
+ dateLabel: calendarDateLabel(entry.day),
3017
+ events: entry.events
3018
+ }));
3019
+ return {
3020
+ title,
3021
+ season,
3022
+ remaining: msToHumanReadable(Math.max(expiry - now, 0)),
3023
+ expiry,
3024
+ days: taggedDays
3025
+ };
3026
+ }
3027
+ __name(adaptCalendar, "adaptCalendar");
3028
+
3029
+ // src/warframe/infrastructure/wf/nightwave-adapter.ts
3030
+ var import_warframe_public_export_plus10 = require("warframe-public-export-plus");
2839
3031
  var RADIO_LEGION_TITLE_KEY = "/Lotus/Language/Syndicates/RadioLegionTitle";
2840
3032
  function timestampMs(value) {
2841
3033
  const raw = value?.$date?.$numberLong;
@@ -2850,16 +3042,16 @@ function remainingLabel(expiry, now) {
2850
3042
  return msToHumanReadable(Math.max(expiry - now, 0));
2851
3043
  }
2852
3044
  __name(remainingLabel, "remainingLabel");
2853
- function translate(key) {
2854
- return import_warframe_public_export_plus9.dict_zh[key] ?? dictZhExtra[key] ?? key;
3045
+ function translate2(key) {
3046
+ return import_warframe_public_export_plus10.dict_zh[key] ?? dictZhExtra[key] ?? key;
2855
3047
  }
2856
- __name(translate, "translate");
3048
+ __name(translate2, "translate");
2857
3049
  function seasonTitle(affiliationTag) {
2858
- const syndicate = import_warframe_public_export_plus9.ExportSyndicates[affiliationTag];
3050
+ const syndicate = import_warframe_public_export_plus10.ExportSyndicates[affiliationTag];
2859
3051
  if (syndicate?.name) {
2860
- return translate(syndicate.name);
3052
+ return translate2(syndicate.name);
2861
3053
  }
2862
- return translate(RADIO_LEGION_TITLE_KEY);
3054
+ return translate2(RADIO_LEGION_TITLE_KEY);
2863
3055
  }
2864
3056
  __name(seasonTitle, "seasonTitle");
2865
3057
  function classifyChallenge(path, raw) {
@@ -2879,7 +3071,7 @@ function challengeFallbackName(path) {
2879
3071
  return path.split("/").pop() ?? path;
2880
3072
  }
2881
3073
  __name(challengeFallbackName, "challengeFallbackName");
2882
- function adaptChallenge(raw, now) {
3074
+ function adaptChallenge2(raw, now) {
2883
3075
  const path = raw.Challenge;
2884
3076
  if (!path) {
2885
3077
  return void 0;
@@ -2890,7 +3082,7 @@ function adaptChallenge(raw, now) {
2890
3082
  }
2891
3083
  const kind = classifyChallenge(path, raw);
2892
3084
  const remaining = remainingLabel(expiry, now);
2893
- const exported = import_warframe_public_export_plus9.ExportNightwave.challenges[path];
3085
+ const exported = import_warframe_public_export_plus10.ExportNightwave.challenges[path];
2894
3086
  if (!exported) {
2895
3087
  return {
2896
3088
  name: challengeFallbackName(path),
@@ -2899,21 +3091,21 @@ function adaptChallenge(raw, now) {
2899
3091
  remaining
2900
3092
  };
2901
3093
  }
2902
- const description = translate(exported.description).split("|COUNT|").join(String(exported.required));
3094
+ const description = translate2(exported.description).split("|COUNT|").join(String(exported.required));
2903
3095
  return {
2904
- name: translate(exported.name),
3096
+ name: translate2(exported.name),
2905
3097
  description,
2906
3098
  kind,
2907
3099
  remaining,
2908
3100
  standing: exported.standing
2909
3101
  };
2910
3102
  }
2911
- __name(adaptChallenge, "adaptChallenge");
3103
+ __name(adaptChallenge2, "adaptChallenge");
2912
3104
  function adaptNightwave(raw, now = Date.now()) {
2913
3105
  const expiry = timestampMs(raw.Expiry);
2914
3106
  const challenges = [];
2915
3107
  for (const entry of raw.ActiveChallenges) {
2916
- const adapted = adaptChallenge(entry, now);
3108
+ const adapted = adaptChallenge2(entry, now);
2917
3109
  if (adapted) {
2918
3110
  challenges.push(adapted);
2919
3111
  }
@@ -2990,7 +3182,7 @@ function getArbitrations(day = 3) {
2990
3182
  return {
2991
3183
  ok: true,
2992
3184
  data: weekArbys.filter((a) => arbyRewards[a.node]).map((a) => {
2993
- const obj = regionToShort(import_warframe_public_export_plus10.ExportRegions[a.node], import_warframe_public_export_plus10.dict_zh);
3185
+ const obj = regionToShort(import_warframe_public_export_plus11.ExportRegions[a.node], import_warframe_public_export_plus11.dict_zh);
2994
3186
  return {
2995
3187
  ...obj,
2996
3188
  time: new Date(a.time * 1e3).toLocaleString("zh-cn", {
@@ -3012,15 +3204,15 @@ function getArbitrations(day = 3) {
3012
3204
  }
3013
3205
  __name(getArbitrations, "getArbitrations");
3014
3206
  async function adaptArchonHunt(source) {
3015
- const name2 = import_warframe_public_export_plus10.dict_zh[`/Lotus/Language/Narmer/${removeSpace(source.boss)}`] ?? source.boss;
3207
+ const name2 = import_warframe_public_export_plus11.dict_zh[`/Lotus/Language/Narmer/${removeSpace(source.boss)}`] ?? source.boss;
3016
3208
  const missions = await Promise.all(
3017
3209
  (source.missions ?? []).map(async (mission, index) => {
3018
3210
  const receivedType = await getMissionTypeKey(mission.type);
3019
- const type = import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? mission.type;
3211
+ const type = import_warframe_public_export_plus11.dict_zh[import_warframe_public_export_plus11.ExportMissionTypes[receivedType]?.name ?? ""] ?? mission.type;
3020
3212
  const solNodeKey = await getSolNodeKey(mission.nodeKey);
3021
- const region = solNodeKey ? import_warframe_public_export_plus10.ExportRegions[solNodeKey] : void 0;
3213
+ const region = solNodeKey ? import_warframe_public_export_plus11.ExportRegions[solNodeKey] : void 0;
3022
3214
  const levels = getArchonHuntEnemyLevels(index);
3023
- const node = region ? regionToShort(region, import_warframe_public_export_plus10.dict_zh) : {
3215
+ const node = region ? regionToShort(region, import_warframe_public_export_plus11.dict_zh) : {
3024
3216
  name: mission.node ?? mission.nodeKey,
3025
3217
  system: "",
3026
3218
  type: "",
@@ -3060,13 +3252,13 @@ async function adaptSortie(source, dates) {
3060
3252
  (source.Variants ?? []).map(async (variant, index) => {
3061
3253
  const missionType = variant.missionType ?? "";
3062
3254
  const receivedType = await getMissionTypeKey(missionType) || missionType;
3063
- const type = import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? missionType;
3255
+ const type = import_warframe_public_export_plus11.dict_zh[import_warframe_public_export_plus11.ExportMissionTypes[receivedType]?.name ?? ""] ?? missionType;
3064
3256
  const nodeId = variant.node ?? "";
3065
3257
  const solNodeKey = await getSolNodeKey(nodeId) || nodeId;
3066
- const region = solNodeKey ? import_warframe_public_export_plus10.ExportRegions[solNodeKey] : void 0;
3258
+ const region = solNodeKey ? import_warframe_public_export_plus11.ExportRegions[solNodeKey] : void 0;
3067
3259
  const levels = getSortieEnemyLevels(index);
3068
3260
  const node = region ? {
3069
- ...regionToShort(region, import_warframe_public_export_plus10.dict_zh),
3261
+ ...regionToShort(region, import_warframe_public_export_plus11.dict_zh),
3070
3262
  minLevel: levels.minLevel,
3071
3263
  maxLevel: levels.maxLevel
3072
3264
  } : {
@@ -3153,7 +3345,7 @@ async function getWeekly() {
3153
3345
  const deepArchimMissions = await Promise.all(
3154
3346
  deepArchim.missions.map(async (m) => {
3155
3347
  const receivedType = await getMissionTypeKey(m.missionType);
3156
- const type = import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? m.missionType;
3348
+ const type = import_warframe_public_export_plus11.dict_zh[import_warframe_public_export_plus11.ExportMissionTypes[receivedType]?.name ?? ""] ?? m.missionType;
3157
3349
  const deviation = stringToDebuff(
3158
3350
  m.deviation.key,
3159
3351
  m.deviation.name,
@@ -3181,7 +3373,7 @@ async function getWeekly() {
3181
3373
  const temporalArchimMissions = await Promise.all(
3182
3374
  temporalArchim.missions.map(async (m) => {
3183
3375
  const receivedType = await getMissionTypeKey(m.missionType);
3184
- const type = import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? receivedType;
3376
+ const type = import_warframe_public_export_plus11.dict_zh[import_warframe_public_export_plus11.ExportMissionTypes[receivedType]?.name ?? ""] ?? receivedType;
3185
3377
  const deviation = stringToDebuff(
3186
3378
  m.deviation.key,
3187
3379
  m.deviation.name,
@@ -3296,8 +3488,8 @@ function getCircuitWeek() {
3296
3488
  const week = Math.trunc((Date.now() - EPOCH) / 6048e5);
3297
3489
  const index1 = (week + 1) % incarnons.length;
3298
3490
  const index2 = (week + 8) % warframes.length;
3299
- const incarnons2 = incarnons.map((v) => v.map((i) => import_warframe_public_export_plus10.dict_zh[i]));
3300
- const warframes2 = warframes.map((v) => v.map((i) => import_warframe_public_export_plus10.dict_zh[i]));
3491
+ const incarnons2 = incarnons.map((v) => v.map((i) => import_warframe_public_export_plus11.dict_zh[i]));
3492
+ const warframes2 = warframes.map((v) => v.map((i) => import_warframe_public_export_plus11.dict_zh[i]));
3301
3493
  return {
3302
3494
  currentIncarnons: index1,
3303
3495
  currentWarframes: index2,
@@ -3335,9 +3527,9 @@ function invasionSide(faction, reward) {
3335
3527
  })),
3336
3528
  ...reward.credits && reward.credits > 0 ? [{ name: resolveExportItemNameZh(`/${reward.credits}Credits`), count: 1 }] : []
3337
3529
  ];
3338
- const nameKey = faction ? import_warframe_public_export_plus10.ExportFactions[faction]?.name ?? faction : void 0;
3530
+ const nameKey = faction ? import_warframe_public_export_plus11.ExportFactions[faction]?.name ?? faction : void 0;
3339
3531
  return {
3340
- faction: nameKey ? import_warframe_public_export_plus10.dict_zh[nameKey] ?? dictZhExtra[nameKey] ?? "" : "",
3532
+ faction: nameKey ? import_warframe_public_export_plus11.dict_zh[nameKey] ?? dictZhExtra[nameKey] ?? "" : "",
3341
3533
  tone: invasionTone(faction),
3342
3534
  rewards
3343
3535
  };
@@ -3358,9 +3550,9 @@ async function adaptInvasions(rawInvasions) {
3358
3550
  raw.DefenderMissionInfo?.faction
3359
3551
  ].some((faction) => /infest/i.test(faction ?? ""));
3360
3552
  const nodeId = raw.Node;
3361
- const solNodeKey = import_warframe_public_export_plus10.ExportRegions[nodeId] ? nodeId : await getSolNodeKey(nodeId);
3362
- const region = solNodeKey ? import_warframe_public_export_plus10.ExportRegions[solNodeKey] : void 0;
3363
- const short = region ? regionToShort(region, import_warframe_public_export_plus10.dict_zh) : void 0;
3553
+ const solNodeKey = import_warframe_public_export_plus11.ExportRegions[nodeId] ? nodeId : await getSolNodeKey(nodeId);
3554
+ const region = solNodeKey ? import_warframe_public_export_plus11.ExportRegions[solNodeKey] : void 0;
3555
+ const short = region ? regionToShort(region, import_warframe_public_export_plus11.dict_zh) : void 0;
3364
3556
  const locTag = raw.LocTag ?? "";
3365
3557
  drafts.push({
3366
3558
  id: raw._id?.$oid ?? raw._id?.$id ?? raw.Node,
@@ -3368,7 +3560,7 @@ async function adaptInvasions(rawInvasions) {
3368
3560
  node: short?.name ?? "",
3369
3561
  systemIndex: region?.systemIndex ?? Number.MAX_SAFE_INTEGER,
3370
3562
  locTag,
3371
- campaign: locTag ? import_warframe_public_export_plus10.dict_zh[locTag] ?? dictZhExtra[locTag] ?? await translateLanguageString(locTag) : "",
3563
+ campaign: locTag ? import_warframe_public_export_plus11.dict_zh[locTag] ?? dictZhExtra[locTag] ?? await translateLanguageString(locTag) : "",
3372
3564
  count,
3373
3565
  goal,
3374
3566
  attacker: invasionSide(raw.DefenderMissionInfo?.faction ?? raw.Faction, raw.AttackerReward),
@@ -3383,7 +3575,7 @@ async function adaptInvasions(rawInvasions) {
3383
3575
  groups.set(key, [...groups.get(key) ?? [], draft]);
3384
3576
  }
3385
3577
  return {
3386
- title: import_warframe_public_export_plus10.dict_zh[INVASION_TITLE_KEY] ?? dictZhExtra[INVASION_TITLE_KEY] ?? INVASION_TITLE_KEY,
3578
+ title: import_warframe_public_export_plus11.dict_zh[INVASION_TITLE_KEY] ?? dictZhExtra[INVASION_TITLE_KEY] ?? INVASION_TITLE_KEY,
3387
3579
  planets: [...groups.values()].sort((a, b) => a[0].systemIndex - b[0].systemIndex).map((list) => {
3388
3580
  const boss = list.filter((draft) => draft.locTag === INFESTED_INVASION_BOSS_TAG);
3389
3581
  const ordered = [...boss, ...list.filter((draft) => draft.locTag !== INFESTED_INVASION_BOSS_TAG)];
@@ -3450,8 +3642,8 @@ async function adaptAlerts(alerts = [], now = Date.now()) {
3450
3642
  const receivedType = await getMissionTypeKey(typeKey);
3451
3643
  const nodeKey = mission.nodeKey ?? "";
3452
3644
  const solNodeKey = await getSolNodeKey(nodeKey);
3453
- const region = solNodeKey ? import_warframe_public_export_plus10.ExportRegions[solNodeKey] : void 0;
3454
- const short = region ? regionToShort(region, import_warframe_public_export_plus10.dict_zh) : {
3645
+ const region = solNodeKey ? import_warframe_public_export_plus11.ExportRegions[solNodeKey] : void 0;
3646
+ const short = region ? regionToShort(region, import_warframe_public_export_plus11.dict_zh) : {
3455
3647
  name: nodeKey,
3456
3648
  system: "",
3457
3649
  type: "",
@@ -3483,9 +3675,9 @@ async function adaptAlerts(alerts = [], now = Date.now()) {
3483
3675
  count: 1
3484
3676
  });
3485
3677
  }
3486
- const nightmare = mission.nightmare ? import_warframe_public_export_plus10.dict_zh[ALERT_NIGHTMARE_KEY] ?? dictZhExtra[ALERT_NIGHTMARE_KEY] : void 0;
3678
+ const nightmare = mission.nightmare ? import_warframe_public_export_plus11.dict_zh[ALERT_NIGHTMARE_KEY] ?? dictZhExtra[ALERT_NIGHTMARE_KEY] : void 0;
3487
3679
  adapted.push({
3488
- type: import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? typeKey,
3680
+ type: import_warframe_public_export_plus11.dict_zh[import_warframe_public_export_plus11.ExportMissionTypes[receivedType]?.name ?? ""] ?? typeKey,
3489
3681
  node: {
3490
3682
  ...short,
3491
3683
  minLevel: mission.minEnemyLevel ?? 0,
@@ -3498,7 +3690,7 @@ async function adaptAlerts(alerts = [], now = Date.now()) {
3498
3690
  });
3499
3691
  }
3500
3692
  return {
3501
- title: import_warframe_public_export_plus10.dict_zh[ALERT_TITLE_KEY] ?? dictZhExtra[ALERT_TITLE_KEY] ?? ALERT_TITLE_KEY,
3693
+ title: import_warframe_public_export_plus11.dict_zh[ALERT_TITLE_KEY] ?? dictZhExtra[ALERT_TITLE_KEY] ?? ALERT_TITLE_KEY,
3502
3694
  alerts: adapted
3503
3695
  };
3504
3696
  }
@@ -3519,11 +3711,34 @@ async function getAlerts() {
3519
3711
  }
3520
3712
  }
3521
3713
  __name(getAlerts, "getAlerts");
3522
- var STEEL_PATH_TESHIN_KEY = "/Lotus/Language/Bosses/Teshin";
3523
- var STEEL_PATH_ESSENCE_KEY = "/Lotus/Language/Resources/SteelEssence";
3524
- var STEEL_PATH_EPOCH_MS = Date.parse("2020-11-16T00:00:00.000Z");
3525
- var STEEL_PATH_WEEK_SECONDS = 604800;
3526
- function steelPathWeekExpiryUtc(now) {
3714
+ function adaptCalendar2(raw, now = Date.now()) {
3715
+ return adaptCalendar(raw, now);
3716
+ }
3717
+ __name(adaptCalendar2, "adaptCalendar");
3718
+ async function getCalendarFrom(snapshot, now = Date.now()) {
3719
+ if (!snapshot?.raw) {
3720
+ return failure("common.fetchFailed", true);
3721
+ }
3722
+ const data = adaptCalendar2(snapshot.calendarRaw, now);
3723
+ if (data.days.length === 0 || data.expiry <= now) {
3724
+ return failure("calendar.unavailable");
3725
+ }
3726
+ return { ok: true, data };
3727
+ }
3728
+ __name(getCalendarFrom, "getCalendarFrom");
3729
+ async function getCalendar() {
3730
+ try {
3731
+ return await getCalendarFrom(await globalWorldState.get());
3732
+ } catch {
3733
+ return failure("common.fetchFailed", true);
3734
+ }
3735
+ }
3736
+ __name(getCalendar, "getCalendar");
3737
+ var STEEL_ESSENCE_TESHIN_KEY = "/Lotus/Language/Bosses/Teshin";
3738
+ var STEEL_ESSENCE_KEY = "/Lotus/Language/Resources/SteelEssence";
3739
+ var STEEL_ESSENCE_EPOCH_MS = Date.parse("2020-11-16T00:00:00.000Z");
3740
+ var STEEL_ESSENCE_WEEK_SECONDS = 604800;
3741
+ function steelEssenceWeekExpiryUtc(now) {
3527
3742
  const date = new Date(now);
3528
3743
  const offset = date.getUTCDay() === 0 ? 6 : date.getUTCDay() - 1;
3529
3744
  return Date.UTC(
@@ -3536,18 +3751,18 @@ function steelPathWeekExpiryUtc(now) {
3536
3751
  0
3537
3752
  );
3538
3753
  }
3539
- __name(steelPathWeekExpiryUtc, "steelPathWeekExpiryUtc");
3540
- function steelPathRotationIndex(now, length) {
3754
+ __name(steelEssenceWeekExpiryUtc, "steelEssenceWeekExpiryUtc");
3755
+ function steelEssenceRotationIndex(now, length) {
3541
3756
  if (length <= 0) {
3542
3757
  return 0;
3543
3758
  }
3544
- const elapsed = Math.max(0, now - STEEL_PATH_EPOCH_MS) / 1e3;
3759
+ const elapsed = Math.max(0, now - STEEL_ESSENCE_EPOCH_MS) / 1e3;
3545
3760
  return Math.floor(
3546
- elapsed % (length * STEEL_PATH_WEEK_SECONDS) / STEEL_PATH_WEEK_SECONDS
3761
+ elapsed % (length * STEEL_ESSENCE_WEEK_SECONDS) / STEEL_ESSENCE_WEEK_SECONDS
3547
3762
  );
3548
3763
  }
3549
- __name(steelPathRotationIndex, "steelPathRotationIndex");
3550
- async function adaptSteelPath(raw = {}, now = Date.now()) {
3764
+ __name(steelEssenceRotationIndex, "steelEssenceRotationIndex");
3765
+ async function adaptSteelEssence(raw = {}, now = Date.now()) {
3551
3766
  const catalogs = await getSteelPathCatalogs();
3552
3767
  const rotationZh = catalogs.zh.rotation;
3553
3768
  const rotationEn = catalogs.en.rotation;
@@ -3557,16 +3772,16 @@ async function adaptSteelPath(raw = {}, now = Date.now()) {
3557
3772
  index = rotationZh.findIndex((item) => item.name === currentName);
3558
3773
  }
3559
3774
  if (index < 0) {
3560
- index = steelPathRotationIndex(now, rotationZh.length);
3775
+ index = steelEssenceRotationIndex(now, rotationZh.length);
3561
3776
  }
3562
3777
  const current = rotationZh[index] ?? {
3563
3778
  name: currentName,
3564
3779
  cost: raw.currentReward?.cost ?? 0
3565
3780
  };
3566
3781
  const upcoming = rotationZh.length > 1 ? rotationZh.map((_, offset) => rotationZh[(index + offset + 1) % rotationZh.length]).filter((item) => item !== void 0).slice(0, rotationZh.length - 1) : [];
3567
- const expiry = raw.expiry?.getTime() ?? steelPathWeekExpiryUtc(now);
3568
- const teshin = import_warframe_public_export_plus10.dict_zh[STEEL_PATH_TESHIN_KEY] ?? dictZhExtra[STEEL_PATH_TESHIN_KEY] ?? STEEL_PATH_TESHIN_KEY;
3569
- const essence = import_warframe_public_export_plus10.dict_zh[STEEL_PATH_ESSENCE_KEY] ?? dictZhExtra[STEEL_PATH_ESSENCE_KEY] ?? STEEL_PATH_ESSENCE_KEY;
3782
+ const expiry = raw.expiry?.getTime() ?? steelEssenceWeekExpiryUtc(now);
3783
+ const teshin = import_warframe_public_export_plus11.dict_zh[STEEL_ESSENCE_TESHIN_KEY] ?? dictZhExtra[STEEL_ESSENCE_TESHIN_KEY] ?? STEEL_ESSENCE_TESHIN_KEY;
3784
+ const essence = import_warframe_public_export_plus11.dict_zh[STEEL_ESSENCE_KEY] ?? dictZhExtra[STEEL_ESSENCE_KEY] ?? STEEL_ESSENCE_KEY;
3570
3785
  return {
3571
3786
  title: `${teshin} · ${essence}商店`,
3572
3787
  costLabel: essence,
@@ -3576,30 +3791,30 @@ async function adaptSteelPath(raw = {}, now = Date.now()) {
3576
3791
  upcoming
3577
3792
  };
3578
3793
  }
3579
- __name(adaptSteelPath, "adaptSteelPath");
3580
- async function getSteelPathFrom(snapshot, now = Date.now()) {
3794
+ __name(adaptSteelEssence, "adaptSteelEssence");
3795
+ async function getSteelEssenceFrom(snapshot, now = Date.now()) {
3581
3796
  if (!snapshot?.raw) {
3582
3797
  return failure("common.fetchFailed", true);
3583
3798
  }
3584
3799
  const offerings = snapshot.raw.steelPath;
3585
3800
  if (!offerings) {
3586
- return failure("steelpath.unavailable");
3801
+ return failure("steelEssence.unavailable");
3587
3802
  }
3588
- const data = await adaptSteelPath(offerings, now);
3803
+ const data = await adaptSteelEssence(offerings, now);
3589
3804
  if (!data.current.name || data.expiry <= now) {
3590
- return failure("steelpath.unavailable");
3805
+ return failure("steelEssence.unavailable");
3591
3806
  }
3592
3807
  return { ok: true, data };
3593
3808
  }
3594
- __name(getSteelPathFrom, "getSteelPathFrom");
3595
- async function getSteelPath() {
3809
+ __name(getSteelEssenceFrom, "getSteelEssenceFrom");
3810
+ async function getSteelEssence() {
3596
3811
  try {
3597
- return await getSteelPathFrom(await globalWorldState.get());
3812
+ return await getSteelEssenceFrom(await globalWorldState.get());
3598
3813
  } catch {
3599
3814
  return failure("common.fetchFailed", true);
3600
3815
  }
3601
3816
  }
3602
- __name(getSteelPath, "getSteelPath");
3817
+ __name(getSteelEssence, "getSteelEssence");
3603
3818
  async function getFissures() {
3604
3819
  const { fissures } = await globalWorldState.get();
3605
3820
  return fissures ? { ok: true, data: fissures } : failure("common.fetchFailed", true);
@@ -3978,7 +4193,7 @@ function analyzeRivenStat(parseResult) {
3978
4193
  __name(analyzeRivenStat, "analyzeRivenStat");
3979
4194
 
3980
4195
  // src/warframe/services/wfm-service/index.ts
3981
- var import_warframe_public_export_plus11 = require("warframe-public-export-plus");
4196
+ var import_warframe_public_export_plus12 = require("warframe-public-export-plus");
3982
4197
 
3983
4198
  // src/warframe/data/wfm/globalDucatnator.ts
3984
4199
  var globalDucatnatorIDDict = createAsyncCache(async () => {
@@ -4802,7 +5017,7 @@ async function getRivenOrders(input) {
4802
5017
  }
4803
5018
  __name(getRivenOrders, "getRivenOrders");
4804
5019
  async function applyRelicData(relic) {
4805
- const tier = import_warframe_public_export_plus11.dict_zh[relic.tierKey] ?? relic.tier;
5020
+ const tier = import_warframe_public_export_plus12.dict_zh[relic.tierKey] ?? relic.tier;
4806
5021
  const { globalItemGameRefDict } = await globalItemData.get();
4807
5022
  const wfmDict = await globalDucatnatorIDDict.get();
4808
5023
  const loadedItems = relic.items.map((element) => {
@@ -4891,7 +5106,7 @@ var primedModHistory = createAsyncCache(async () => {
4891
5106
  }, 432e5);
4892
5107
 
4893
5108
  // src/warframe/services/world-state-refresh.ts
4894
- var import_warframe_public_export_plus12 = require("warframe-public-export-plus");
5109
+ var import_warframe_public_export_plus13 = require("warframe-public-export-plus");
4895
5110
  var ALLOWED_FISSURE_MISSION_TYPES = /* @__PURE__ */ new Set(["MT_EXTERMINATION", "MT_DEFENSE", "MT_ARTIFACT"]);
4896
5111
  function time(value) {
4897
5112
  return value?.getTime() ?? 0;
@@ -4910,7 +5125,7 @@ function fissureId(value) {
4910
5125
  __name(fissureId, "fissureId");
4911
5126
  async function fissureMissionType(fissure) {
4912
5127
  const nodeKey = await getSolNodeKey(fissure.nodeKey);
4913
- return import_warframe_public_export_plus12.ExportRegions[nodeKey]?.missionType;
5128
+ return import_warframe_public_export_plus13.ExportRegions[nodeKey]?.missionType;
4914
5129
  }
4915
5130
  __name(fissureMissionType, "fissureMissionType");
4916
5131
  function dailyDealId(value) {
@@ -6949,7 +7164,39 @@ function AlertComponent(board) {
6949
7164
  );
6950
7165
  }
6951
7166
  __name(AlertComponent, "AlertComponent");
6952
- function SteelPathComponent(board) {
7167
+ function CalendarComponent(board) {
7168
+ const timeLeft = board.expiry - Date.now();
7169
+ const timeColor = timeLeft > 36e5 ? "var(--wf-success)" : timeLeft > 6e5 ? "var(--wf-info)" : "var(--wf-danger)";
7170
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
7171
+ "div",
7172
+ {
7173
+ style: "width:480px;background-color:var(--wf-bg-card);border-radius:var(--wf-radius);padding:10px;box-shadow:var(--wf-shadow-card);border:1px solid var(--wf-border);font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;color:var(--wf-text-body);",
7174
+ children: [
7175
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: "font-size:18px;font-weight:bold;color:var(--wf-text-primary);margin:0 0 4px 0;text-align:center;", children: board.title }),
7176
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: `font-size:12px;text-align:center;margin-bottom:8px;color:${timeColor};`, children: [
7177
+ "剩余",
7178
+ board.remaining
7179
+ ] }),
7180
+ board.days.map((day) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
7181
+ "div",
7182
+ {
7183
+ style: "padding:6px 8px;background-color:var(--wf-bg-subtle);border-radius:var(--wf-radius-sm);margin-bottom:4px;",
7184
+ children: [
7185
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: "font-size:11px;font-weight:600;color:var(--wf-text-secondary);margin-bottom:4px;", children: day.dateLabel }),
7186
+ day.events.map((event) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: "display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-bottom:2px;", children: [
7187
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: "font-size:11px;color:var(--wf-accent);", children: event.kindLabel }),
7188
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: "font-size:13px;font-weight:600;color:var(--wf-text-body);", children: event.name }),
7189
+ event.description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: "font-size:11px;color:var(--wf-text-secondary);", children: event.description }) : null
7190
+ ] }))
7191
+ ]
7192
+ }
7193
+ ))
7194
+ ]
7195
+ }
7196
+ );
7197
+ }
7198
+ __name(CalendarComponent, "CalendarComponent");
7199
+ function SteelEssenceComponent(board) {
6953
7200
  const timeLeft = board.expiry - Date.now();
6954
7201
  const timeColor = timeLeft > 36e5 ? "var(--wf-success)" : timeLeft > 6e5 ? "var(--wf-info)" : "var(--wf-danger)";
6955
7202
  const currentCardStyle = `
@@ -7001,7 +7248,7 @@ function SteelPathComponent(board) {
7001
7248
  }
7002
7249
  );
7003
7250
  }
7004
- __name(SteelPathComponent, "SteelPathComponent");
7251
+ __name(SteelEssenceComponent, "SteelEssenceComponent");
7005
7252
 
7006
7253
  // src/commands/wf.ts
7007
7254
  function createWfCommands(deps) {
@@ -7060,13 +7307,20 @@ function createWfCommands(deps) {
7060
7307
  }
7061
7308
  return render(AlertComponent(result.data));
7062
7309
  }, "alertCommand"),
7063
- steelPathCommand: /* @__PURE__ */ __name(async (_action) => {
7064
- const result = await getSteelPath();
7310
+ calendarCommand: /* @__PURE__ */ __name(async (_action) => {
7311
+ const result = await getCalendar();
7312
+ if (!result.ok) {
7313
+ return t(result);
7314
+ }
7315
+ return render(CalendarComponent(result.data));
7316
+ }, "calendarCommand"),
7317
+ steelEssenceCommand: /* @__PURE__ */ __name(async (_action) => {
7318
+ const result = await getSteelEssence();
7065
7319
  if (!result.ok) {
7066
7320
  return t(result);
7067
7321
  }
7068
- return render(SteelPathComponent(result.data));
7069
- }, "steelPathCommand"),
7322
+ return render(SteelEssenceComponent(result.data));
7323
+ }, "steelEssenceCommand"),
7070
7324
  steelPathFissureCommand: /* @__PURE__ */ __name(async (_action) => {
7071
7325
  const result = await getSteelPathFissures();
7072
7326
  if (!result.ok) {
@@ -7916,7 +8170,8 @@ function setupCommands(ctx, deps) {
7916
8170
  ctx.command("nightwave", "当前午夜电波").alias("电波").alias("午夜电波").action(wf.nightwaveCommand);
7917
8171
  ctx.command("sortie", "每日突击").alias("突击").alias("每日突击").action(wf.sortieCommand);
7918
8172
  ctx.command("circuit", "本周回廊战甲及灵化之源").alias("灵化之源").alias("灵化").action(wf.circuitCommand);
7919
- ctx.command("steelpath", "钢铁精华商店").alias("steel-path").alias("teshin").alias("钢铁精华").alias("钢铁之路").action(wf.steelPathCommand);
8173
+ ctx.command("steel-essence", "钢铁精华商店").alias("钢铁精华").action(wf.steelEssenceCommand);
8174
+ ctx.command("1999calendar", "当前1999日历").alias("日历").alias("1999日历").alias("霍瓦尼亚日历").action(wf.calendarCommand);
7920
8175
  ctx.command("riven <img:image>", "分析紫卡截图").action(wf.rivenCommand);
7921
8176
  ctx.command(
7922
8177
  "riven-stat <weaponType:string> <statType:string> <disposition:number>",