koishi-plugin-warframe 1.8.8 → 1.10.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
@@ -1286,6 +1286,8 @@ var messages = {
1286
1286
  "relic.notFound": "未找到对应遗物信息",
1287
1287
  "arbitration.invalidDayRange": "天数需小于等于14且大于0",
1288
1288
  "bounty.unavailable": "当前该地点暂无可用赏金",
1289
+ "nightwave.unavailable": "当前没有午夜电波",
1290
+ "sortie.unavailable": "当前没有突击",
1289
1291
  "voidTrader.drifting": "虚空商人仍在未知地带漂流...",
1290
1292
  "voidTrader.arriving": "距离虚空商人到达还有: {time}",
1291
1293
  "riven.imageNotFound": "未检测到图片",
@@ -1609,7 +1611,7 @@ async function getHotRivenWeapons2() {
1609
1611
  __name(getHotRivenWeapons2, "getHotRivenWeapons");
1610
1612
 
1611
1613
  // src/warframe/services/wf-service.ts
1612
- var import_warframe_public_export_plus8 = require("warframe-public-export-plus");
1614
+ var import_warframe_public_export_plus10 = require("warframe-public-export-plus");
1613
1615
 
1614
1616
  // src/warframe/assets/index.ts
1615
1617
  var {
@@ -1663,6 +1665,31 @@ function extractSyndicateMissionsRaw(json) {
1663
1665
  }
1664
1666
  }
1665
1667
  __name(extractSyndicateMissionsRaw, "extractSyndicateMissionsRaw");
1668
+ function extractSeasonInfoRaw(json) {
1669
+ try {
1670
+ const data = JSON.parse(json);
1671
+ const info = data.SeasonInfo;
1672
+ if (!info || typeof info !== "object") {
1673
+ return void 0;
1674
+ }
1675
+ return {
1676
+ ...info,
1677
+ ActiveChallenges: Array.isArray(info.ActiveChallenges) ? info.ActiveChallenges : []
1678
+ };
1679
+ } catch {
1680
+ return void 0;
1681
+ }
1682
+ }
1683
+ __name(extractSeasonInfoRaw, "extractSeasonInfoRaw");
1684
+ function extractSortieRaw(json) {
1685
+ try {
1686
+ const data = JSON.parse(json);
1687
+ return Array.isArray(data.Sorties) && data.Sorties.length > 0 ? data.Sorties[0] : void 0;
1688
+ } catch {
1689
+ return void 0;
1690
+ }
1691
+ }
1692
+ __name(extractSortieRaw, "extractSortieRaw");
1666
1693
  async function fetchWorldStateJson() {
1667
1694
  return fetchAsyncText(`${apiBase}worldState.php`);
1668
1695
  }
@@ -1931,6 +1958,21 @@ function fissureTierNumToNumber(a) {
1931
1958
  return typeof a === "string" ? Number(a.charAt(5)) : a;
1932
1959
  }
1933
1960
  __name(fissureTierNumToNumber, "fissureTierNumToNumber");
1961
+ async function translateSortieBoss(key) {
1962
+ const { sortieBoss } = await import("warframe-worldstate-data/utilities");
1963
+ return sortieBoss(key, "zh");
1964
+ }
1965
+ __name(translateSortieBoss, "translateSortieBoss");
1966
+ async function translateSortieFaction(key) {
1967
+ const { sortieFaction } = await import("warframe-worldstate-data/utilities");
1968
+ return sortieFaction(key, "zh");
1969
+ }
1970
+ __name(translateSortieFaction, "translateSortieFaction");
1971
+ async function translateSortieModifier(key) {
1972
+ const { sortieModifier } = await import("warframe-worldstate-data/utilities");
1973
+ return sortieModifier(key, "zh");
1974
+ }
1975
+ __name(translateSortieModifier, "translateSortieModifier");
1934
1976
  function getVoidTraderItem(i) {
1935
1977
  const itemNameKey = getVoidTraderItemName(i.uniqueName);
1936
1978
  const itemName = !itemNameKey ? i.item : typeof itemNameKey === "string" ? import_warframe_public_export_plus3.dict_zh[itemNameKey] ?? (itemNameKey.includes("/") ? i.item : itemNameKey) : relicToFullNameZH(itemNameKey.era, itemNameKey.category);
@@ -2006,6 +2048,8 @@ var globalWorldState = createAsyncCache(async () => {
2006
2048
  throw new Error("获取游戏信息失败");
2007
2049
  }
2008
2050
  const syndicateMissionsRaw = extractSyndicateMissionsRaw(json);
2051
+ const seasonInfoRaw = extractSeasonInfoRaw(json);
2052
+ const sortieRaw = extractSortieRaw(json);
2009
2053
  const worldState = await getWorldState(json);
2010
2054
  const fissures = [];
2011
2055
  const rjFissures = [];
@@ -2026,6 +2070,8 @@ var globalWorldState = createAsyncCache(async () => {
2026
2070
  return {
2027
2071
  raw: worldState,
2028
2072
  syndicateMissionsRaw,
2073
+ seasonInfoRaw,
2074
+ sortieRaw,
2029
2075
  fissures,
2030
2076
  spFissures,
2031
2077
  rjFissures
@@ -2146,6 +2192,23 @@ var rivenStatFixFactor = {
2146
2192
  }
2147
2193
  };
2148
2194
 
2195
+ // src/warframe/data/wf/sortie.ts
2196
+ var import_warframe_public_export_plus7 = require("warframe-public-export-plus");
2197
+ var MODE_NAME_KEY2 = "/Lotus/Language/Menu/SortieMissionName";
2198
+ var SORTIE_ENEMY_LEVELS = [
2199
+ { minLevel: 50, maxLevel: 60 },
2200
+ { minLevel: 65, maxLevel: 80 },
2201
+ { minLevel: 80, maxLevel: 100 }
2202
+ ];
2203
+ function getSortieModeName() {
2204
+ return import_warframe_public_export_plus7.dict_zh[MODE_NAME_KEY2] ?? dictZhExtra[MODE_NAME_KEY2] ?? MODE_NAME_KEY2;
2205
+ }
2206
+ __name(getSortieModeName, "getSortieModeName");
2207
+ function getSortieEnemyLevels(index) {
2208
+ return SORTIE_ENEMY_LEVELS[index] ?? SORTIE_ENEMY_LEVELS[SORTIE_ENEMY_LEVELS.length - 1];
2209
+ }
2210
+ __name(getSortieEnemyLevels, "getSortieEnemyLevels");
2211
+
2149
2212
  // src/warframe/infrastructure/wfm-client.ts
2150
2213
  var import_wfm_api_client = require("wfm-api-client");
2151
2214
  function softFailPlugin() {
@@ -2337,7 +2400,7 @@ async function extractTextFromImage(image, secret) {
2337
2400
  __name(extractTextFromImage, "extractTextFromImage");
2338
2401
 
2339
2402
  // src/warframe/infrastructure/wf/bounty-adapter.ts
2340
- var import_warframe_public_export_plus7 = require("warframe-public-export-plus");
2403
+ var import_warframe_public_export_plus8 = require("warframe-public-export-plus");
2341
2404
  var bountyLocationTag = {
2342
2405
  cetus: "CetusSyndicate",
2343
2406
  fortuna: "SolarisSyndicate",
@@ -2502,13 +2565,13 @@ function resolveBlueprintNameViaDictZh(pathLeaf) {
2502
2565
  }
2503
2566
  const needle = `${base}Name`.toLowerCase();
2504
2567
  let translated;
2505
- for (const key of Object.keys(import_warframe_public_export_plus7.dict_zh)) {
2568
+ for (const key of Object.keys(import_warframe_public_export_plus8.dict_zh)) {
2506
2569
  if (!key.toLowerCase().endsWith(needle)) {
2507
2570
  continue;
2508
2571
  }
2509
2572
  const boundary = key.length - needle.length;
2510
2573
  if (boundary === 0 || key[boundary - 1] === "/" || key[boundary - 1] === "_") {
2511
- translated = import_warframe_public_export_plus7.dict_zh[key];
2574
+ translated = import_warframe_public_export_plus8.dict_zh[key];
2512
2575
  break;
2513
2576
  }
2514
2577
  }
@@ -2527,69 +2590,69 @@ function resolveExportItemNameZh(sourceKey) {
2527
2590
  return `${creditMatch[1]} 现金`;
2528
2591
  }
2529
2592
  const fixedKey = sourceKey.replace("/StoreItems", "");
2530
- const flavour = import_warframe_public_export_plus7.ExportFlavour[fixedKey];
2593
+ const flavour = import_warframe_public_export_plus8.ExportFlavour[fixedKey];
2531
2594
  if (flavour?.name) {
2532
- return import_warframe_public_export_plus7.dict_zh[flavour.name] ?? flavour.name;
2595
+ return import_warframe_public_export_plus8.dict_zh[flavour.name] ?? flavour.name;
2533
2596
  }
2534
- const weapon = import_warframe_public_export_plus7.ExportWeapons[fixedKey];
2597
+ const weapon = import_warframe_public_export_plus8.ExportWeapons[fixedKey];
2535
2598
  if (weapon?.name) {
2536
- return import_warframe_public_export_plus7.dict_zh[weapon.name] ?? weapon.name;
2599
+ return import_warframe_public_export_plus8.dict_zh[weapon.name] ?? weapon.name;
2537
2600
  }
2538
- const mod = import_warframe_public_export_plus7.ExportUpgrades[fixedKey];
2601
+ const mod = import_warframe_public_export_plus8.ExportUpgrades[fixedKey];
2539
2602
  if (mod?.name) {
2540
- return import_warframe_public_export_plus7.dict_zh[mod.name] ?? mod.name;
2603
+ return import_warframe_public_export_plus8.dict_zh[mod.name] ?? mod.name;
2541
2604
  }
2542
- const arcane = import_warframe_public_export_plus7.ExportArcanes[fixedKey];
2605
+ const arcane = import_warframe_public_export_plus8.ExportArcanes[fixedKey];
2543
2606
  if (arcane?.name) {
2544
- return import_warframe_public_export_plus7.dict_zh[arcane.name] ?? arcane.name;
2607
+ return import_warframe_public_export_plus8.dict_zh[arcane.name] ?? arcane.name;
2545
2608
  }
2546
- const fusion = import_warframe_public_export_plus7.ExportFusionBundles[fixedKey];
2609
+ const fusion = import_warframe_public_export_plus8.ExportFusionBundles[fixedKey];
2547
2610
  if (fusion?.name) {
2548
- return import_warframe_public_export_plus7.dict_zh[fusion.name] ?? fusion.name;
2611
+ return import_warframe_public_export_plus8.dict_zh[fusion.name] ?? fusion.name;
2549
2612
  }
2550
- const relic = import_warframe_public_export_plus7.ExportRelics[fixedKey];
2613
+ const relic = import_warframe_public_export_plus8.ExportRelics[fixedKey];
2551
2614
  if (relic) {
2552
- const era = import_warframe_public_export_plus7.dict_zh[relicEraToTransKey(relic.era)] ?? toPascalCase(relic.era);
2615
+ const era = import_warframe_public_export_plus8.dict_zh[relicEraToTransKey(relic.era)] ?? toPascalCase(relic.era);
2553
2616
  return `${era} ${relic.category} 遗物`;
2554
2617
  }
2555
- const skin = import_warframe_public_export_plus7.ExportCustoms[fixedKey];
2618
+ const skin = import_warframe_public_export_plus8.ExportCustoms[fixedKey];
2556
2619
  if (skin?.name) {
2557
- return import_warframe_public_export_plus7.dict_zh[skin.name] ?? skin.name;
2620
+ return import_warframe_public_export_plus8.dict_zh[skin.name] ?? skin.name;
2558
2621
  }
2559
- const recipe = import_warframe_public_export_plus7.ExportRecipes[fixedKey];
2622
+ const recipe = import_warframe_public_export_plus8.ExportRecipes[fixedKey];
2560
2623
  if (recipe?.resultType) {
2561
- const recipeResult = import_warframe_public_export_plus7.ExportKeys[recipe.resultType] ?? import_warframe_public_export_plus7.ExportResources[recipe.resultType] ?? import_warframe_public_export_plus7.ExportWeapons[recipe.resultType] ?? import_warframe_public_export_plus7.ExportWarframes[recipe.resultType];
2624
+ const recipeResult = import_warframe_public_export_plus8.ExportKeys[recipe.resultType] ?? import_warframe_public_export_plus8.ExportResources[recipe.resultType] ?? import_warframe_public_export_plus8.ExportWeapons[recipe.resultType] ?? import_warframe_public_export_plus8.ExportWarframes[recipe.resultType];
2562
2625
  if (recipeResult?.name) {
2563
- const translated = import_warframe_public_export_plus7.dict_zh[recipeResult.name] ?? recipeResult.name;
2626
+ const translated = import_warframe_public_export_plus8.dict_zh[recipeResult.name] ?? recipeResult.name;
2564
2627
  return translated.includes("蓝图") ? translated : `${translated} 蓝图`;
2565
2628
  }
2566
2629
  }
2567
- const virtual = import_warframe_public_export_plus7.ExportVirtuals[fixedKey];
2630
+ const virtual = import_warframe_public_export_plus8.ExportVirtuals[fixedKey];
2568
2631
  if (virtual?.name) {
2569
- return import_warframe_public_export_plus7.dict_zh[virtual.name] ?? virtual.name;
2632
+ return import_warframe_public_export_plus8.dict_zh[virtual.name] ?? virtual.name;
2570
2633
  }
2571
- const resource = import_warframe_public_export_plus7.ExportResources[fixedKey];
2634
+ const resource = import_warframe_public_export_plus8.ExportResources[fixedKey];
2572
2635
  if (resource?.name) {
2573
- return import_warframe_public_export_plus7.dict_zh[resource.name] ?? resource.name;
2636
+ return import_warframe_public_export_plus8.dict_zh[resource.name] ?? resource.name;
2574
2637
  }
2575
- const gear = import_warframe_public_export_plus7.ExportGear[fixedKey];
2638
+ const gear = import_warframe_public_export_plus8.ExportGear[fixedKey];
2576
2639
  if (gear?.name) {
2577
- return import_warframe_public_export_plus7.dict_zh[gear.name] ?? gear.name;
2640
+ return import_warframe_public_export_plus8.dict_zh[gear.name] ?? gear.name;
2578
2641
  }
2579
- const bundle = import_warframe_public_export_plus7.ExportBundles[sourceKey] ?? import_warframe_public_export_plus7.ExportBundles[fixedKey];
2642
+ const bundle = import_warframe_public_export_plus8.ExportBundles[sourceKey] ?? import_warframe_public_export_plus8.ExportBundles[fixedKey];
2580
2643
  if (bundle?.name) {
2581
- return import_warframe_public_export_plus7.dict_zh[bundle.name] ?? bundle.name;
2644
+ return import_warframe_public_export_plus8.dict_zh[bundle.name] ?? bundle.name;
2582
2645
  }
2583
- const boosterPack = import_warframe_public_export_plus7.ExportBoosterPacks[fixedKey];
2646
+ const boosterPack = import_warframe_public_export_plus8.ExportBoosterPacks[fixedKey];
2584
2647
  if (boosterPack?.name) {
2585
- return import_warframe_public_export_plus7.dict_zh[boosterPack.name] ?? boosterPack.name;
2648
+ return import_warframe_public_export_plus8.dict_zh[boosterPack.name] ?? boosterPack.name;
2586
2649
  }
2587
2650
  const pathLeaf = fixedKey.split("/").pop() ?? sourceKey;
2588
2651
  return resolveBlueprintNameViaDictZh(pathLeaf) ?? pathLeaf;
2589
2652
  }
2590
2653
  __name(resolveExportItemNameZh, "resolveExportItemNameZh");
2591
2654
  function rewardsFromDeck(rewardsKey) {
2592
- const deck = import_warframe_public_export_plus7.ExportRewards[rewardsKey];
2655
+ const deck = import_warframe_public_export_plus8.ExportRewards[rewardsKey];
2593
2656
  if (!deck?.length) {
2594
2657
  return [];
2595
2658
  }
@@ -2622,9 +2685,9 @@ function jobName(job) {
2622
2685
  return chamber ? `隔离库 ${chamber}` : "隔离库";
2623
2686
  }
2624
2687
  if (job.jobType) {
2625
- const bounty = import_warframe_public_export_plus7.ExportBounties[job.jobType];
2688
+ const bounty = import_warframe_public_export_plus8.ExportBounties[job.jobType];
2626
2689
  if (bounty?.name) {
2627
- return import_warframe_public_export_plus7.dict_zh[bounty.name] ?? bounty.name;
2690
+ return import_warframe_public_export_plus8.dict_zh[bounty.name] ?? bounty.name;
2628
2691
  }
2629
2692
  return job.jobType.split("/").pop() ?? "未知赏金";
2630
2693
  }
@@ -2632,9 +2695,9 @@ function jobName(job) {
2632
2695
  }
2633
2696
  __name(jobName, "jobName");
2634
2697
  function boardTitle(location, tag) {
2635
- const syndicate = import_warframe_public_export_plus7.ExportSyndicates[tag];
2698
+ const syndicate = import_warframe_public_export_plus8.ExportSyndicates[tag];
2636
2699
  if (syndicate?.name) {
2637
- const name2 = import_warframe_public_export_plus7.dict_zh[syndicate.name] ?? syndicate.name;
2700
+ const name2 = import_warframe_public_export_plus8.dict_zh[syndicate.name] ?? syndicate.name;
2638
2701
  return name2.endsWith("赏金") ? name2 : `${name2}赏金`;
2639
2702
  }
2640
2703
  return locationFallbackTitle[location];
@@ -2671,15 +2734,15 @@ function findRawSyndicateMission(missions, location) {
2671
2734
  }
2672
2735
  __name(findRawSyndicateMission, "findRawSyndicateMission");
2673
2736
  function formatOracleMission(nodeKey) {
2674
- const region = import_warframe_public_export_plus7.ExportRegions[nodeKey];
2737
+ const region = import_warframe_public_export_plus8.ExportRegions[nodeKey];
2675
2738
  if (!region?.name) {
2676
2739
  return nodeKey;
2677
2740
  }
2678
- const name2 = import_warframe_public_export_plus7.dict_zh[region.name] ?? region.name;
2741
+ const name2 = import_warframe_public_export_plus8.dict_zh[region.name] ?? region.name;
2679
2742
  if (hexNodesWithoutMissionSuffix.has(nodeKey) || !region.missionName) {
2680
2743
  return name2;
2681
2744
  }
2682
- const mission = import_warframe_public_export_plus7.dict_zh[region.missionName] ?? region.missionName;
2745
+ const mission = import_warframe_public_export_plus8.dict_zh[region.missionName] ?? region.missionName;
2683
2746
  if (!mission || mission === name2) {
2684
2747
  return name2;
2685
2748
  }
@@ -2687,21 +2750,21 @@ function formatOracleMission(nodeKey) {
2687
2750
  }
2688
2751
  __name(formatOracleMission, "formatOracleMission");
2689
2752
  function formatOracleChallenge(challengePath) {
2690
- const challenge = import_warframe_public_export_plus7.ExportChallenges[challengePath];
2753
+ const challenge = import_warframe_public_export_plus8.ExportChallenges[challengePath];
2691
2754
  if (!challenge?.description) {
2692
2755
  return challengePath.split("/").pop() ?? challengePath;
2693
2756
  }
2694
- const raw = import_warframe_public_export_plus7.dict_zh[challenge.description] ?? challenge.description;
2757
+ const raw = import_warframe_public_export_plus8.dict_zh[challenge.description] ?? challenge.description;
2695
2758
  const lastLine = raw.split(/\r?\n/).pop() ?? raw;
2696
2759
  return lastLine.split("|COUNT|").join(String(challenge.requiredCount ?? 0));
2697
2760
  }
2698
2761
  __name(formatOracleChallenge, "formatOracleChallenge");
2699
2762
  function zarimanFactionZh(faction) {
2700
2763
  if (faction === "FC_GRINEER") {
2701
- return import_warframe_public_export_plus7.dict_zh["/Lotus/Language/Game/Faction_GrineerUC"] ?? "Grineer";
2764
+ return import_warframe_public_export_plus8.dict_zh["/Lotus/Language/Game/Faction_GrineerUC"] ?? "Grineer";
2702
2765
  }
2703
2766
  if (faction === "FC_CORPUS") {
2704
- return import_warframe_public_export_plus7.dict_zh["/Lotus/Language/Game/Faction_CorpusUC"] ?? "Corpus";
2767
+ return import_warframe_public_export_plus8.dict_zh["/Lotus/Language/Game/Faction_CorpusUC"] ?? "Corpus";
2705
2768
  }
2706
2769
  return void 0;
2707
2770
  }
@@ -2744,6 +2807,101 @@ function adaptOracleBountyBoard(location, cycle) {
2744
2807
  }
2745
2808
  __name(adaptOracleBountyBoard, "adaptOracleBountyBoard");
2746
2809
 
2810
+ // src/warframe/infrastructure/wf/nightwave-adapter.ts
2811
+ var import_warframe_public_export_plus9 = require("warframe-public-export-plus");
2812
+ var RADIO_LEGION_TITLE_KEY = "/Lotus/Language/Syndicates/RadioLegionTitle";
2813
+ function timestampMs(value) {
2814
+ const raw = value?.$date?.$numberLong;
2815
+ if (raw === void 0 || raw === "") {
2816
+ return 0;
2817
+ }
2818
+ const ms = Number(raw);
2819
+ return Number.isFinite(ms) ? ms : 0;
2820
+ }
2821
+ __name(timestampMs, "timestampMs");
2822
+ function remainingLabel(expiry, now) {
2823
+ return msToHumanReadable(Math.max(expiry - now, 0));
2824
+ }
2825
+ __name(remainingLabel, "remainingLabel");
2826
+ function translate(key) {
2827
+ return import_warframe_public_export_plus9.dict_zh[key] ?? dictZhExtra[key] ?? key;
2828
+ }
2829
+ __name(translate, "translate");
2830
+ function seasonTitle(affiliationTag) {
2831
+ const syndicate = import_warframe_public_export_plus9.ExportSyndicates[affiliationTag];
2832
+ if (syndicate?.name) {
2833
+ return translate(syndicate.name);
2834
+ }
2835
+ return translate(RADIO_LEGION_TITLE_KEY);
2836
+ }
2837
+ __name(seasonTitle, "seasonTitle");
2838
+ function classifyChallenge(path, raw) {
2839
+ if (raw.Daily || path.includes("/Daily/")) {
2840
+ return "daily";
2841
+ }
2842
+ if (raw.Permanent || /permanent/i.test(path)) {
2843
+ return "permanent";
2844
+ }
2845
+ if (/hard/i.test(path)) {
2846
+ return "elite";
2847
+ }
2848
+ return "weekly";
2849
+ }
2850
+ __name(classifyChallenge, "classifyChallenge");
2851
+ function challengeFallbackName(path) {
2852
+ return path.split("/").pop() ?? path;
2853
+ }
2854
+ __name(challengeFallbackName, "challengeFallbackName");
2855
+ function adaptChallenge(raw, now) {
2856
+ const path = raw.Challenge;
2857
+ if (!path) {
2858
+ return void 0;
2859
+ }
2860
+ const expiry = timestampMs(raw.Expiry);
2861
+ if (expiry <= now) {
2862
+ return void 0;
2863
+ }
2864
+ const kind = classifyChallenge(path, raw);
2865
+ const remaining = remainingLabel(expiry, now);
2866
+ const exported = import_warframe_public_export_plus9.ExportNightwave.challenges[path];
2867
+ if (!exported) {
2868
+ return {
2869
+ name: challengeFallbackName(path),
2870
+ description: "",
2871
+ kind,
2872
+ remaining
2873
+ };
2874
+ }
2875
+ const description = translate(exported.description).split("|COUNT|").join(String(exported.required));
2876
+ return {
2877
+ name: translate(exported.name),
2878
+ description,
2879
+ kind,
2880
+ remaining,
2881
+ standing: exported.standing
2882
+ };
2883
+ }
2884
+ __name(adaptChallenge, "adaptChallenge");
2885
+ function adaptNightwave(raw, now = Date.now()) {
2886
+ const expiry = timestampMs(raw.Expiry);
2887
+ const challenges = [];
2888
+ for (const entry of raw.ActiveChallenges) {
2889
+ const adapted = adaptChallenge(entry, now);
2890
+ if (adapted) {
2891
+ challenges.push(adapted);
2892
+ }
2893
+ }
2894
+ return {
2895
+ title: seasonTitle(raw.AffiliationTag),
2896
+ season: raw.Season,
2897
+ phase: raw.Phase,
2898
+ expiry,
2899
+ remaining: remainingLabel(expiry, now),
2900
+ challenges
2901
+ };
2902
+ }
2903
+ __name(adaptNightwave, "adaptNightwave");
2904
+
2747
2905
  // src/warframe/services/wf-service.ts
2748
2906
  async function getRelic(input) {
2749
2907
  if (!input) {
@@ -2805,7 +2963,7 @@ function getArbitrations(day = 3) {
2805
2963
  return {
2806
2964
  ok: true,
2807
2965
  data: weekArbys.filter((a) => arbyRewards[a.node]).map((a) => {
2808
- const obj = regionToShort(import_warframe_public_export_plus8.ExportRegions[a.node], import_warframe_public_export_plus8.dict_zh);
2966
+ const obj = regionToShort(import_warframe_public_export_plus10.ExportRegions[a.node], import_warframe_public_export_plus10.dict_zh);
2809
2967
  return {
2810
2968
  ...obj,
2811
2969
  time: new Date(a.time * 1e3).toLocaleString("zh-cn", {
@@ -2827,15 +2985,15 @@ function getArbitrations(day = 3) {
2827
2985
  }
2828
2986
  __name(getArbitrations, "getArbitrations");
2829
2987
  async function adaptArchonHunt(source) {
2830
- const name2 = import_warframe_public_export_plus8.dict_zh[`/Lotus/Language/Narmer/${removeSpace(source.boss)}`] ?? source.boss;
2988
+ const name2 = import_warframe_public_export_plus10.dict_zh[`/Lotus/Language/Narmer/${removeSpace(source.boss)}`] ?? source.boss;
2831
2989
  const missions = await Promise.all(
2832
2990
  (source.missions ?? []).map(async (mission, index) => {
2833
2991
  const receivedType = await getMissionTypeKey(mission.type);
2834
- const type = import_warframe_public_export_plus8.dict_zh[import_warframe_public_export_plus8.ExportMissionTypes[receivedType]?.name ?? ""] ?? mission.type;
2992
+ const type = import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? mission.type;
2835
2993
  const solNodeKey = await getSolNodeKey(mission.nodeKey);
2836
- const region = solNodeKey ? import_warframe_public_export_plus8.ExportRegions[solNodeKey] : void 0;
2994
+ const region = solNodeKey ? import_warframe_public_export_plus10.ExportRegions[solNodeKey] : void 0;
2837
2995
  const levels = getArchonHuntEnemyLevels(index);
2838
- const node = region ? regionToShort(region, import_warframe_public_export_plus8.dict_zh) : {
2996
+ const node = region ? regionToShort(region, import_warframe_public_export_plus10.dict_zh) : {
2839
2997
  name: mission.node ?? mission.nodeKey,
2840
2998
  system: "",
2841
2999
  type: "",
@@ -2860,6 +3018,86 @@ async function adaptArchonHunt(source) {
2860
3018
  };
2861
3019
  }
2862
3020
  __name(adaptArchonHunt, "adaptArchonHunt");
3021
+ function mongoDateMs2(value) {
3022
+ const raw = value?.$date?.$numberLong;
3023
+ if (!raw) {
3024
+ return void 0;
3025
+ }
3026
+ const ms = Number(raw);
3027
+ return Number.isFinite(ms) ? ms : void 0;
3028
+ }
3029
+ __name(mongoDateMs2, "mongoDateMs");
3030
+ async function adaptSortie(source, dates) {
3031
+ const expiryMs = dates?.expiry?.getTime() ?? mongoDateMs2(source.Expiry) ?? 0;
3032
+ const missions = await Promise.all(
3033
+ (source.Variants ?? []).map(async (variant, index) => {
3034
+ const missionType = variant.missionType ?? "";
3035
+ const receivedType = await getMissionTypeKey(missionType) || missionType;
3036
+ const type = import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? missionType;
3037
+ const nodeId = variant.node ?? "";
3038
+ const solNodeKey = await getSolNodeKey(nodeId) || nodeId;
3039
+ const region = solNodeKey ? import_warframe_public_export_plus10.ExportRegions[solNodeKey] : void 0;
3040
+ const levels = getSortieEnemyLevels(index);
3041
+ const node = region ? {
3042
+ ...regionToShort(region, import_warframe_public_export_plus10.dict_zh),
3043
+ minLevel: levels.minLevel,
3044
+ maxLevel: levels.maxLevel
3045
+ } : {
3046
+ name: nodeId,
3047
+ system: "",
3048
+ type: "",
3049
+ faction: "",
3050
+ minLevel: levels.minLevel,
3051
+ maxLevel: levels.maxLevel
3052
+ };
3053
+ return {
3054
+ type,
3055
+ node,
3056
+ modifier: await translateSortieModifier(variant.modifierType ?? "")
3057
+ };
3058
+ })
3059
+ );
3060
+ const bossKey = source.Boss ?? "";
3061
+ return {
3062
+ modeName: getSortieModeName(),
3063
+ boss: await translateSortieBoss(bossKey),
3064
+ faction: await translateSortieFaction(bossKey),
3065
+ expiry: expiryMs,
3066
+ remaining: msToHumanReadable(expiryMs - Date.now()),
3067
+ missions
3068
+ };
3069
+ }
3070
+ __name(adaptSortie, "adaptSortie");
3071
+ async function getSortieFrom(snapshot) {
3072
+ if (!snapshot?.raw) {
3073
+ return failure("common.fetchFailed", true);
3074
+ }
3075
+ const raw = snapshot.sortieRaw;
3076
+ if (!raw) {
3077
+ return failure("sortie.unavailable");
3078
+ }
3079
+ const parsed = snapshot.raw.sortie;
3080
+ const expiryMs = parsed?.expiry?.getTime() ?? mongoDateMs2(raw.Expiry);
3081
+ if (expiryMs === void 0 || expiryMs <= Date.now()) {
3082
+ return failure("sortie.unavailable");
3083
+ }
3084
+ return {
3085
+ ok: true,
3086
+ data: await adaptSortie(raw, {
3087
+ activation: parsed?.activation,
3088
+ expiry: parsed?.expiry
3089
+ })
3090
+ };
3091
+ }
3092
+ __name(getSortieFrom, "getSortieFrom");
3093
+ async function getSortie() {
3094
+ try {
3095
+ return await getSortieFrom(await globalWorldState.get());
3096
+ } catch {
3097
+ return failure("common.fetchFailed", true);
3098
+ }
3099
+ }
3100
+ __name(getSortie, "getSortie");
2863
3101
  async function getWeekly() {
2864
3102
  const { raw: worldState } = await globalWorldState.get();
2865
3103
  if (!worldState) {
@@ -2888,7 +3126,7 @@ async function getWeekly() {
2888
3126
  const deepArchimMissions = await Promise.all(
2889
3127
  deepArchim.missions.map(async (m) => {
2890
3128
  const receivedType = await getMissionTypeKey(m.missionType);
2891
- const type = import_warframe_public_export_plus8.dict_zh[import_warframe_public_export_plus8.ExportMissionTypes[receivedType]?.name ?? ""] ?? m.missionType;
3129
+ const type = import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? m.missionType;
2892
3130
  const deviation = stringToDebuff(
2893
3131
  m.deviation.key,
2894
3132
  m.deviation.name,
@@ -2916,7 +3154,7 @@ async function getWeekly() {
2916
3154
  const temporalArchimMissions = await Promise.all(
2917
3155
  temporalArchim.missions.map(async (m) => {
2918
3156
  const receivedType = await getMissionTypeKey(m.missionType);
2919
- const type = import_warframe_public_export_plus8.dict_zh[import_warframe_public_export_plus8.ExportMissionTypes[receivedType]?.name ?? ""] ?? receivedType;
3157
+ const type = import_warframe_public_export_plus10.dict_zh[import_warframe_public_export_plus10.ExportMissionTypes[receivedType]?.name ?? ""] ?? receivedType;
2920
3158
  const deviation = stringToDebuff(
2921
3159
  m.deviation.key,
2922
3160
  m.deviation.name,
@@ -2950,6 +3188,22 @@ async function getWeekly() {
2950
3188
  };
2951
3189
  }
2952
3190
  __name(getWeekly, "getWeekly");
3191
+ function resolveNightwave(raw, now = Date.now()) {
3192
+ if (!raw) {
3193
+ return failure("nightwave.unavailable");
3194
+ }
3195
+ return { ok: true, data: adaptNightwave(raw, now) };
3196
+ }
3197
+ __name(resolveNightwave, "resolveNightwave");
3198
+ async function getNightwave() {
3199
+ try {
3200
+ const { seasonInfoRaw } = await globalWorldState.get();
3201
+ return resolveNightwave(seasonInfoRaw);
3202
+ } catch {
3203
+ return failure("common.fetchFailed", true);
3204
+ }
3205
+ }
3206
+ __name(getNightwave, "getNightwave");
2953
3207
  async function getEnvironment() {
2954
3208
  const { raw: worldState } = await globalWorldState.get();
2955
3209
  if (!worldState) {
@@ -3015,8 +3269,8 @@ function getCircuitWeek() {
3015
3269
  const week = Math.trunc((Date.now() - EPOCH) / 6048e5);
3016
3270
  const index1 = (week + 1) % incarnons.length;
3017
3271
  const index2 = (week + 8) % warframes.length;
3018
- const incarnons2 = incarnons.map((v) => v.map((i) => import_warframe_public_export_plus8.dict_zh[i]));
3019
- const warframes2 = warframes.map((v) => v.map((i) => import_warframe_public_export_plus8.dict_zh[i]));
3272
+ const incarnons2 = incarnons.map((v) => v.map((i) => import_warframe_public_export_plus10.dict_zh[i]));
3273
+ const warframes2 = warframes.map((v) => v.map((i) => import_warframe_public_export_plus10.dict_zh[i]));
3020
3274
  return {
3021
3275
  currentIncarnons: index1,
3022
3276
  currentWarframes: index2,
@@ -3403,7 +3657,7 @@ function analyzeRivenStat(parseResult) {
3403
3657
  __name(analyzeRivenStat, "analyzeRivenStat");
3404
3658
 
3405
3659
  // src/warframe/services/wfm-service/index.ts
3406
- var import_warframe_public_export_plus9 = require("warframe-public-export-plus");
3660
+ var import_warframe_public_export_plus11 = require("warframe-public-export-plus");
3407
3661
 
3408
3662
  // src/warframe/data/wfm/globalDucatnator.ts
3409
3663
  var globalDucatnatorIDDict = createAsyncCache(async () => {
@@ -4227,7 +4481,7 @@ async function getRivenOrders(input) {
4227
4481
  }
4228
4482
  __name(getRivenOrders, "getRivenOrders");
4229
4483
  async function applyRelicData(relic) {
4230
- const tier = import_warframe_public_export_plus9.dict_zh[relic.tierKey] ?? relic.tier;
4484
+ const tier = import_warframe_public_export_plus11.dict_zh[relic.tierKey] ?? relic.tier;
4231
4485
  const { globalItemGameRefDict } = await globalItemData.get();
4232
4486
  const wfmDict = await globalDucatnatorIDDict.get();
4233
4487
  const loadedItems = relic.items.map((element) => {
@@ -4316,7 +4570,7 @@ var primedModHistory = createAsyncCache(async () => {
4316
4570
  }, 432e5);
4317
4571
 
4318
4572
  // src/warframe/services/world-state-refresh.ts
4319
- var import_warframe_public_export_plus10 = require("warframe-public-export-plus");
4573
+ var import_warframe_public_export_plus12 = require("warframe-public-export-plus");
4320
4574
  var ALLOWED_FISSURE_MISSION_TYPES = /* @__PURE__ */ new Set(["MT_EXTERMINATION", "MT_DEFENSE", "MT_ARTIFACT"]);
4321
4575
  function time(value) {
4322
4576
  return value?.getTime() ?? 0;
@@ -4335,7 +4589,7 @@ function fissureId(value) {
4335
4589
  __name(fissureId, "fissureId");
4336
4590
  async function fissureMissionType(fissure) {
4337
4591
  const nodeKey = await getSolNodeKey(fissure.nodeKey);
4338
- return import_warframe_public_export_plus10.ExportRegions[nodeKey]?.missionType;
4592
+ return import_warframe_public_export_plus12.ExportRegions[nodeKey]?.missionType;
4339
4593
  }
4340
4594
  __name(fissureMissionType, "fissureMissionType");
4341
4595
  function dailyDealId(value) {
@@ -5295,6 +5549,96 @@ function WeeklyComponent(archon, deepArchimedea, temporalArchimedea) {
5295
5549
  );
5296
5550
  }
5297
5551
  __name(WeeklyComponent, "WeeklyComponent");
5552
+ function NightwaveComponent(board) {
5553
+ const kindLabel = {
5554
+ daily: "每日",
5555
+ weekly: "每周",
5556
+ elite: "精英",
5557
+ permanent: "精选"
5558
+ };
5559
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
5560
+ "div",
5561
+ {
5562
+ style: "width:560px;background-color:var(--wf-bg-card);border-radius:var(--wf-radius);padding:10px 12px;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);",
5563
+ children: [
5564
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: "font-size:18px;font-weight:bold;color:var(--wf-text-primary);margin:0 0 2px 0;text-align:center;", children: board.title }),
5565
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: "text-align:center;font-size:12px;color:var(--wf-text-secondary);margin-bottom:2px;", children: `第${board.season + 1}季 · 剩余${board.remaining}` }),
5566
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: "text-align:center;font-size:10px;color:var(--wf-text-muted);margin-bottom:8px;", children: "剩余时间不准确,最多可能偏差数月" }),
5567
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: "display:flex;flex-direction:column;gap:4px;", children: board.challenges.map((challenge) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
5568
+ "div",
5569
+ {
5570
+ style: "padding:6px 8px;background-color:var(--wf-bg-subtle);border-radius:var(--wf-radius-sm);",
5571
+ children: [
5572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: "display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;", children: [
5573
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: "font-size:11px;color:var(--wf-accent);", children: kindLabel[challenge.kind] }),
5574
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: "font-size:13px;font-weight:700;color:var(--wf-text-primary);flex:1;", children: challenge.name }),
5575
+ challenge.standing === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: "font-size:11px;color:var(--wf-accent);", children: `${challenge.standing} 声望` }),
5576
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: "font-size:11px;color:var(--wf-text-muted);", children: [
5577
+ "剩余",
5578
+ challenge.remaining
5579
+ ] })
5580
+ ] }),
5581
+ challenge.description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: "font-size:11px;line-height:1.35;color:var(--wf-text-body);margin-top:2px;", children: challenge.description }) : null
5582
+ ]
5583
+ }
5584
+ )) })
5585
+ ]
5586
+ }
5587
+ );
5588
+ }
5589
+ __name(NightwaveComponent, "NightwaveComponent");
5590
+ function SortieComponent(sortie) {
5591
+ const timeLeft = sortie.expiry - Date.now();
5592
+ const timeColor = timeLeft > 36e5 ? "var(--wf-success)" : timeLeft > 6e5 ? "var(--wf-info)" : "var(--wf-danger)";
5593
+ const sectionStyle = `
5594
+ border-radius: var(--wf-radius);
5595
+ border: 1px solid var(--wf-border);
5596
+ padding: 12px 14px;
5597
+ background-color: var(--wf-bg-subtle);
5598
+ box-shadow: var(--wf-shadow-inner);
5599
+ color: var(--wf-text-body);
5600
+ `;
5601
+ const missionCardStyle = `
5602
+ border-radius: var(--wf-radius-md);
5603
+ border: 1px solid var(--wf-border);
5604
+ padding: 8px 10px;
5605
+ margin-bottom: 8px;
5606
+ background-color: var(--wf-bg-card);
5607
+ `;
5608
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
5609
+ "div",
5610
+ {
5611
+ style: "width:320px;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);",
5612
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { style: sectionStyle, children: [
5613
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: "font-size:16px;font-weight:600;color:var(--wf-text-primary);margin-bottom:8px;", children: `${sortie.modeName}: ${sortie.boss}${sortie.faction ? ` (${sortie.faction})` : ""}` }),
5614
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
5615
+ "div",
5616
+ {
5617
+ style: `font-size:13px;margin-bottom:8px;color:${timeColor};`,
5618
+ children: [
5619
+ "剩余",
5620
+ sortie.remaining
5621
+ ]
5622
+ }
5623
+ ),
5624
+ sortie.missions.map((mission) => {
5625
+ const location = [mission.node.system, mission.node.name].filter(Boolean).join(" · ");
5626
+ const levels = mission.node.minLevel && mission.node.maxLevel ? `Lv.${mission.node.minLevel}-${mission.node.maxLevel}` : "";
5627
+ const mapAndLevel = [location, levels].filter(Boolean).join(" · ");
5628
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: missionCardStyle, children: [
5629
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: "font-size:13px;font-weight:600;margin-bottom:4px;color:var(--wf-text-body);", children: mission.type }),
5630
+ mapAndLevel ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: "font-size:12px;color:var(--wf-text-secondary);", children: mapAndLevel }) : null,
5631
+ mission.modifier ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: "font-size:12px;color:var(--wf-text-secondary);", children: [
5632
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: "font-weight:600;margin-right:4px;color:var(--wf-text-body);", children: "条件:" }),
5633
+ mission.modifier
5634
+ ] }) : null
5635
+ ] });
5636
+ })
5637
+ ] })
5638
+ }
5639
+ );
5640
+ }
5641
+ __name(SortieComponent, "SortieComponent");
5298
5642
  function RelicComponent(relic) {
5299
5643
  const gold = relic.items.filter((i) => i.rarity === "RARE");
5300
5644
  const silver = relic.items.filter((i) => i.rarity === "UNCOMMON");
@@ -6252,6 +6596,20 @@ function createWfCommands(deps) {
6252
6596
  )
6253
6597
  );
6254
6598
  }, "weeklyCommand"),
6599
+ nightwaveCommand: /* @__PURE__ */ __name(async (_action) => {
6600
+ const result = await getNightwave();
6601
+ if (!result.ok) {
6602
+ return t(result);
6603
+ }
6604
+ return render(NightwaveComponent(result.data));
6605
+ }, "nightwaveCommand"),
6606
+ sortieCommand: /* @__PURE__ */ __name(async (_action) => {
6607
+ const result = await getSortie();
6608
+ if (!result.ok) {
6609
+ return t(result);
6610
+ }
6611
+ return render(SortieComponent(result.data));
6612
+ }, "sortieCommand"),
6255
6613
  weeklyRivenCommand: /* @__PURE__ */ __name(async (_action, minPrice) => {
6256
6614
  const threshold = minPrice && minPrice > 0 ? minPrice : 100;
6257
6615
  const result = await getWeeklyRivens(threshold);
@@ -7021,6 +7379,8 @@ function setupCommands(ctx, deps) {
7021
7379
  ctx.command("bounty-cavia", "科维兽赏金").alias("科维兽").alias("科维兽赏金").alias("赏金科维兽").alias("cavia").action(wf.bountyCaviaCommand);
7022
7380
  ctx.command("bounty-hex", "六人组赏金").alias("六人组").alias("六人组赏金").alias("赏金六人组").alias("1999赏金").alias("hex").action(wf.bountyHexCommand);
7023
7381
  ctx.command("weekly", "周常任务").alias("周常").alias("科研").alias("时光科研").alias("深层科研").alias("执行官").action(wf.weeklyCommand);
7382
+ ctx.command("nightwave", "当前午夜电波").alias("电波").alias("午夜电波").action(wf.nightwaveCommand);
7383
+ ctx.command("sortie", "每日突击").alias("突击").alias("每日突击").action(wf.sortieCommand);
7024
7384
  ctx.command("circuit", "本周回廊战甲及灵化之源").alias("灵化之源").alias("灵化").action(wf.circuitCommand);
7025
7385
  ctx.command("riven <img:image>", "分析紫卡截图").action(wf.rivenCommand);
7026
7386
  ctx.command(