koishi-plugin-delta-force 1.2.3 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -473,6 +473,11 @@ var ApiService = class {
473
473
  async getFriendInfo(frameworkToken, openid) {
474
474
  return this.request("GET", "/df/person/friendinfo", { frameworkToken, openid });
475
475
  }
476
+ // ==================== 统计相关接口 ====================
477
+ /** 获取用户统计信息 */
478
+ async getUserStats(clientID) {
479
+ return this.request("GET", "/stats/users", { clientID });
480
+ }
476
481
  // ==================== 战报相关接口 ====================
477
482
  /** 获取日报 */
478
483
  async getDailyReport(frameworkToken, type, date) {
@@ -1395,11 +1400,16 @@ var commandRules = [
1395
1400
  return platform;
1396
1401
  }, "args")
1397
1402
  },
1398
- // 角色绑定: ^\^(角色绑定|绑定角色|绑定|bind)\s*([a-zA-Z0-9\-]+)?$
1403
+ // 手动绑定Token: ^\^绑定\s+([a-zA-Z0-9\-]+)$ (必须有token参数)
1399
1404
  {
1400
- pattern: /^\^(角色绑定|绑定角色|绑定|bind)\s*([a-zA-Z0-9\-]+)?$/i,
1401
- command: "df.bind",
1402
- args: /* @__PURE__ */ __name((match) => match[2] || "", "args")
1405
+ pattern: /^\^绑定\s+([a-zA-Z0-9\-]+)$/i,
1406
+ command: "df.bindtoken",
1407
+ args: /* @__PURE__ */ __name((match) => match[1] || "", "args")
1408
+ },
1409
+ // 角色绑定: ^\^(角色绑定|绑定角色|bind)$ (不带token参数)
1410
+ {
1411
+ pattern: /^\^(角色绑定|绑定角色|bind)$/i,
1412
+ command: "df.bind"
1403
1413
  },
1404
1414
  // 账号列表: ^\^(账号|账号列表|account)$
1405
1415
  {
@@ -1451,12 +1461,6 @@ var commandRules = [
1451
1461
  pattern: /^\^(网页|web|网站)(登陆|登录)$/i,
1452
1462
  command: "df.weblogin"
1453
1463
  },
1454
- // 手动绑定Token: ^\^绑定\s+([a-zA-Z0-9\-]+)$
1455
- {
1456
- pattern: /^\^绑定\s+([a-zA-Z0-9\-]+)$/i,
1457
- command: "df.bindtoken",
1458
- args: /* @__PURE__ */ __name((match) => match[1] || "", "args")
1459
- },
1460
1464
  // 删除账号: ^\^删除账号\s*(\d+)$
1461
1465
  {
1462
1466
  pattern: /^\^删除账号\s*(\d+)$/i,
@@ -1566,6 +1570,29 @@ var commandRules = [
1566
1570
  pattern: /^\^(服务器状态|health)$/i,
1567
1571
  command: "df.health"
1568
1572
  },
1573
+ // 个人数据: ^\^(数据|data|个人数据)\s*(.*)$
1574
+ {
1575
+ pattern: /^\^(数据|data|个人数据)\s*(.*)$/i,
1576
+ command: "df.data",
1577
+ args: /* @__PURE__ */ __name((match) => match[2]?.trim() || "", "args")
1578
+ },
1579
+ // 用户统计: ^\^用户统计$
1580
+ {
1581
+ pattern: /^\^用户统计$/i,
1582
+ command: "df.stats"
1583
+ },
1584
+ // 出红记录: ^\^(出红记录|大红记录|藏品记录)\s*(.*)$
1585
+ {
1586
+ pattern: /^\^(出红记录|大红记录|藏品记录)\s*(.*)$/i,
1587
+ command: "df.redrecord",
1588
+ args: /* @__PURE__ */ __name((match) => match[2]?.trim() || "", "args")
1589
+ },
1590
+ // 大红收藏: ^\^(大红收藏|大红藏品|大红海报|藏品海报)\s*(\d*)$
1591
+ {
1592
+ pattern: /^\^(大红收藏|大红藏品|大红海报|藏品海报)\s*(\d*)$/i,
1593
+ command: "df.redcollection",
1594
+ args: /* @__PURE__ */ __name((match) => match[2]?.trim() || "", "args")
1595
+ },
1569
1596
  // ==================== 价格工具 ====================
1570
1597
  // 价格历史: ^\^(价格历史|历史价格)\s+(.+)$
1571
1598
  {
@@ -1637,6 +1664,11 @@ var commandRules = [
1637
1664
  pattern: /^\^(帮助|菜单|功能|help)$/i,
1638
1665
  command: "df.help"
1639
1666
  },
1667
+ // 娱乐帮助: ^\^娱乐(帮助|菜单|功能)$
1668
+ {
1669
+ pattern: /^\^娱乐(帮助|菜单|功能)$/i,
1670
+ command: "df.entertainment"
1671
+ },
1640
1672
  // ==================== 资源管理 ====================
1641
1673
  // 资源状态: ^\^资源状态$
1642
1674
  {
@@ -1861,10 +1893,18 @@ var Renderer = class {
1861
1893
  const resPath = this.resourcesPath.replace(/\\/g, "/");
1862
1894
  const templateData = {
1863
1895
  ...data,
1896
+ // _res_path 带末尾斜杠,与模板中的路径拼接保持一致
1864
1897
  _res_path: `file:///${resPath}/`,
1865
1898
  // 布局路径 (与云崽版保持一致)
1866
1899
  commonLayout: path.join(this.templatesPath, "common", "common.html"),
1867
- defaultLayout: path.join(this.resourcesPath, "common", "layout", "default.html")
1900
+ defaultLayout: path.join(this.resourcesPath, "common", "layout", "default.html"),
1901
+ // 系统变量 (与云崽版保持一致)
1902
+ sys: {
1903
+ scale: "style=transform:scale(1)",
1904
+ copyright: "Created By Koishi & Delta-Force-Plugin",
1905
+ ...data.sys || {}
1906
+ },
1907
+ copyright: data.copyright || "Created By Koishi & Delta-Force-Plugin"
1868
1908
  };
1869
1909
  this.ctx.logger("delta-force").info("渲染模板:", htmlPath);
1870
1910
  this.ctx.logger("delta-force").info("资源路径:", templateData._res_path);
@@ -1910,12 +1950,19 @@ var Renderer = class {
1910
1950
  */
1911
1951
  async render(templateName, data, config = {}) {
1912
1952
  try {
1913
- const templateDir = path.join(this.templatesPath, templateName);
1914
- const htmlPath = path.join(templateDir, `${templateName}.html`);
1953
+ let templateDir;
1954
+ let htmlPath;
1955
+ if (templateName === "help") {
1956
+ templateDir = path.join(this.resourcesPath, "help");
1957
+ htmlPath = path.join(templateDir, "index.html");
1958
+ } else {
1959
+ templateDir = path.join(this.templatesPath, templateName);
1960
+ htmlPath = path.join(templateDir, `${templateName}.html`);
1961
+ }
1915
1962
  if (!fs.existsSync(htmlPath)) {
1916
1963
  return {
1917
1964
  success: false,
1918
- error: `模板文件不存在: ${templateName}`
1965
+ error: `模板文件不存在: ${templateName} (${htmlPath})`
1919
1966
  };
1920
1967
  }
1921
1968
  const html = this.renderTemplate(htmlPath, data);
@@ -1924,7 +1971,7 @@ var Renderer = class {
1924
1971
  timeout = 6e4
1925
1972
  } = config;
1926
1973
  const page = await this.ctx.puppeteer.page();
1927
- const tempHtmlPath = path.join(this.templatesPath, templateName, `_temp_${Date.now()}.html`);
1974
+ const tempHtmlPath = path.join(templateDir, `_temp_${Date.now()}.html`);
1928
1975
  fs.writeFileSync(tempHtmlPath, html, "utf-8");
1929
1976
  try {
1930
1977
  await page.setViewport({
@@ -1937,7 +1984,7 @@ var Renderer = class {
1937
1984
  waitUntil: "networkidle2",
1938
1985
  timeout
1939
1986
  });
1940
- await page.evaluate(() => new Promise((resolve3) => setTimeout(resolve3, 200)));
1987
+ await page.evaluate(() => new Promise((resolve4) => setTimeout(resolve4, 200)));
1941
1988
  const container = await page.$(".container");
1942
1989
  let screenshot;
1943
1990
  if (container) {
@@ -2052,7 +2099,7 @@ var ResourceManager = class {
2052
2099
  * 延迟函数
2053
2100
  */
2054
2101
  sleep(ms) {
2055
- return new Promise((resolve3) => setTimeout(resolve3, ms));
2102
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
2056
2103
  }
2057
2104
  /**
2058
2105
  * 检查资源是否已下载
@@ -2338,20 +2385,36 @@ function decode(str) {
2338
2385
  }
2339
2386
  __name(decode, "decode");
2340
2387
  async function handleApiError(response, session) {
2341
- if (!response) {
2342
- await session.send("请求失败,请稍后重试");
2388
+ if (!response || typeof response !== "object") {
2389
+ await session.send("请求失败,API未返回任何数据或数据格式错误。");
2343
2390
  return true;
2344
2391
  }
2345
- if (response.code !== 0 && !response.success) {
2346
- const errorMsg = response.msg || response.message || "未知错误";
2347
- if (response.code === 401 || response.code === 403) {
2348
- await session.send("认证失败,请重新登录");
2349
- return true;
2350
- }
2351
- if (response.code === 404) {
2352
- await session.send("未找到相关数据");
2353
- return true;
2392
+ if (String(response.code) === "1000" || String(response.code) === "1001") {
2393
+ await session.send("API Key无效或已过期,请联系机器人管理员检查配置。");
2394
+ return true;
2395
+ }
2396
+ if (String(response.code) === "1100") {
2397
+ await session.send("APIKey权限不足,请机器人升级订阅后使用。");
2398
+ return true;
2399
+ }
2400
+ const data = response.data;
2401
+ if (data?.ret === 101) {
2402
+ await session.send("登录已失效,请重新登录。");
2403
+ return true;
2404
+ }
2405
+ if (data?.ret === 99998) {
2406
+ await session.send("您尚未绑定游戏大区,请先使用绑定命令进行绑定。");
2407
+ return true;
2408
+ }
2409
+ if (response.success === false && (response.message?.includes("未找到有效token") || response.message?.includes("缺少frameworkToken参数"))) {
2410
+ await session.send("当前激活的账号无效,请重新登陆账号或切换有效账号。");
2411
+ return true;
2412
+ }
2413
+ if (response.success === false) {
2414
+ if (response.message && (response.message.includes("上传成功") || response.message.includes("查询成功") || response.message.includes("操作成功") || response.message.includes("删除成功") || response.message.includes("更新成功"))) {
2415
+ return false;
2354
2416
  }
2417
+ const errorMsg = response.msg || response.message || "未知错误";
2355
2418
  await session.send(`操作失败: ${errorMsg}`);
2356
2419
  return true;
2357
2420
  }
@@ -2359,7 +2422,7 @@ async function handleApiError(response, session) {
2359
2422
  }
2360
2423
  __name(handleApiError, "handleApiError");
2361
2424
  function sleep(ms) {
2362
- return new Promise((resolve3) => setTimeout(resolve3, ms));
2425
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
2363
2426
  }
2364
2427
  __name(sleep, "sleep");
2365
2428
  async function getUserDisplayInfo(api, token, sessionUserId, fallbackName) {
@@ -3439,10 +3502,10 @@ function registerWeeklyCommands(ctx, api, dataManager, renderer) {
3439
3502
  const nicknameMap = /* @__PURE__ */ new Map();
3440
3503
  const avatarMap = /* @__PURE__ */ new Map();
3441
3504
  if (allTeammateOpenIDs.size > 0) {
3442
- const promises = Array.from(allTeammateOpenIDs).map(
3505
+ const promises2 = Array.from(allTeammateOpenIDs).map(
3443
3506
  (openid) => api.getFriendInfo(token, openid)
3444
3507
  );
3445
- const results = await Promise.allSettled(promises);
3508
+ const results = await Promise.allSettled(promises2);
3446
3509
  results.forEach((result, index) => {
3447
3510
  const openid = Array.from(allTeammateOpenIDs)[index];
3448
3511
  if (result.status === "fulfilled" && result.value?.success && result.value.data) {
@@ -4451,9 +4514,9 @@ function formatDuration(value, unit = "seconds") {
4451
4514
  } else {
4452
4515
  totalMinutes = numValue;
4453
4516
  }
4454
- const h6 = Math.floor(totalMinutes / 60);
4517
+ const h7 = Math.floor(totalMinutes / 60);
4455
4518
  const m = Math.floor(totalMinutes % 60);
4456
- return `${h6}小时${m}分钟`;
4519
+ return `${h7}小时${m}分钟`;
4457
4520
  }
4458
4521
  __name(formatDuration, "formatDuration");
4459
4522
 
@@ -4877,7 +4940,7 @@ var FLOW_TYPE_NAMES = {
4877
4940
  2: "道具",
4878
4941
  3: "货币"
4879
4942
  };
4880
- function registerFlowsCommands(ctx, api) {
4943
+ function registerFlowsCommands(ctx, api, renderer) {
4881
4944
  const logger = ctx.logger("delta-force");
4882
4945
  ctx.command("df.flows [type:string] [page:number]", "查看交易流水").alias("df.流水").option("all", "-a 查询所有页").usage("类型: 设备、道具、货币\n示例: df.flows 货币 2").action(async ({ session, options }, type, page) => {
4883
4946
  const userId = session.userId;
@@ -4886,7 +4949,7 @@ function registerFlowsCommands(ctx, api) {
4886
4949
  if (!token) {
4887
4950
  return "您尚未登录,请先使用 df.login 登录";
4888
4951
  }
4889
- const isAll = options?.all || false;
4952
+ const isAll = options?.all || type?.toLowerCase() === "all";
4890
4953
  const pageNum = page || 1;
4891
4954
  let typeValue;
4892
4955
  if (type && type.toLowerCase() !== "all") {
@@ -4899,16 +4962,18 @@ function registerFlowsCommands(ctx, api) {
4899
4962
  await session.send("正在查询流水记录,请稍候...");
4900
4963
  try {
4901
4964
  if (typeValue) {
4902
- const result = await queryFlowsByType(api, token, typeValue, pageNum, isAll, logger);
4903
- return result;
4965
+ return await renderFlowsByType(api, renderer, token, typeValue, pageNum, isAll, session, logger);
4904
4966
  }
4905
- const results = [];
4906
4967
  for (const [typeName, typeVal] of Object.entries(FLOW_TYPE_MAP)) {
4907
- const result = await queryFlowsByType(api, token, typeVal, pageNum, false, logger);
4908
- results.push(`【${typeName}流水】
4968
+ const result = await renderFlowsByType(api, renderer, token, typeVal, pageNum, false, session, logger);
4969
+ if (typeof result === "string") {
4970
+ await session.send(`【${typeName}流水】
4909
4971
  ${result}`);
4972
+ } else {
4973
+ await session.send(result);
4974
+ }
4910
4975
  }
4911
- return results.join("\n\n");
4976
+ return;
4912
4977
  } catch (error) {
4913
4978
  logger.error("查询流水失败:", error);
4914
4979
  return `查询失败: ${error.message}`;
@@ -4916,84 +4981,151 @@ ${result}`);
4916
4981
  });
4917
4982
  }
4918
4983
  __name(registerFlowsCommands, "registerFlowsCommands");
4919
- async function queryFlowsByType(api, token, typeValue, page, isAll, logger) {
4984
+ async function renderFlowsByType(api, renderer, token, typeValue, page, isAll, session, logger) {
4920
4985
  const typeName = FLOW_TYPE_NAMES[typeValue];
4986
+ let allRecords = [];
4987
+ let totalPages = 1;
4988
+ let playerInfo = null;
4921
4989
  if (isAll) {
4922
- const allRecords = [];
4923
4990
  let currentPage = 1;
4924
4991
  while (true) {
4925
- const res2 = await api.getFlows(token, typeValue, currentPage);
4926
- if (!res2 || res2.code !== 0) break;
4927
- const data2 = res2.data?.[0];
4928
- if (!data2) break;
4929
- const arrKey2 = typeValue === 1 ? "LoginArr" : typeValue === 2 ? "itemArr" : "iMoneyArr";
4930
- const records2 = data2[arrKey2];
4931
- if (!records2 || records2.length === 0) break;
4932
- allRecords.push(...records2);
4992
+ const res = await api.getFlows(token, typeValue, currentPage);
4993
+ if (!res || res.success === false && String(res.code) !== "0") break;
4994
+ const data = res.data?.[0];
4995
+ if (!data) break;
4996
+ if (typeValue === 1 && currentPage === 1 && data.vRoleName) {
4997
+ playerInfo = {
4998
+ vRoleName: data.vRoleName,
4999
+ Level: data.Level,
5000
+ loginDay: data.loginDay
5001
+ };
5002
+ }
5003
+ const arrKey = typeValue === 1 ? "LoginArr" : typeValue === 2 ? "itemArr" : "iMoneyArr";
5004
+ const records = data[arrKey];
5005
+ if (!records || records.length === 0) break;
5006
+ allRecords.push(...records);
4933
5007
  currentPage++;
4934
5008
  if (currentPage > 50) break;
4935
5009
  }
4936
- if (allRecords.length === 0) {
5010
+ totalPages = currentPage - 1;
5011
+ } else {
5012
+ const res = await api.getFlows(token, typeValue, page);
5013
+ if (!res || res.success === false && String(res.code) !== "0") {
5014
+ return `查询失败: ${res?.msg || res?.message || "未知错误"}`;
5015
+ }
5016
+ const data = res.data?.[0];
5017
+ if (!data) {
4937
5018
  return "暂无记录";
4938
5019
  }
4939
- return formatFlowRecords(typeValue, allRecords, `全部 (${currentPage - 1}页)`);
4940
- }
4941
- const res = await api.getFlows(token, typeValue, page);
4942
- if (!res || res.code !== 0) {
4943
- return `查询失败: ${res?.msg || res?.message || "未知错误"}`;
5020
+ if (typeValue === 1 && data.vRoleName) {
5021
+ playerInfo = {
5022
+ vRoleName: data.vRoleName,
5023
+ Level: data.Level,
5024
+ loginDay: data.loginDay
5025
+ };
5026
+ }
5027
+ const arrKey = typeValue === 1 ? "LoginArr" : typeValue === 2 ? "itemArr" : "iMoneyArr";
5028
+ const records = data[arrKey];
5029
+ if (!records || records.length === 0) {
5030
+ return "当前页无记录";
5031
+ }
5032
+ allRecords = records;
4944
5033
  }
4945
- const data = res.data?.[0];
4946
- if (!data) {
5034
+ if (allRecords.length === 0) {
4947
5035
  return "暂无记录";
4948
5036
  }
4949
- const arrKey = typeValue === 1 ? "LoginArr" : typeValue === 2 ? "itemArr" : "iMoneyArr";
4950
- const records = data[arrKey];
4951
- if (!records || records.length === 0) {
4952
- return "当前页无记录";
4953
- }
4954
- return formatFlowRecords(typeValue, records, `第 ${page} 页`);
5037
+ const templateData = prepareTemplateData(allRecords, typeValue, isAll ? `全部` : page, typeName, playerInfo, isAll);
5038
+ const imageResult = await renderer.renderToMessage("flows", templateData);
5039
+ return imageResult;
4955
5040
  }
4956
- __name(queryFlowsByType, "queryFlowsByType");
4957
- function formatFlowRecords(typeValue, records, pageInfo) {
4958
- const lines = [`${pageInfo} (共 ${records.length} 条)`];
4959
- lines.push("━━━━━━━━━━━━━━━");
4960
- const displayRecords = records.slice(0, 20);
5041
+ __name(renderFlowsByType, "renderFlowsByType");
5042
+ function prepareTemplateData(records, typeValue, page, typeName, playerInfo, isAllPages) {
5043
+ const templateData = {
5044
+ typeName,
5045
+ typeValue,
5046
+ page
5047
+ };
5048
+ const groupByColumns = /* @__PURE__ */ __name((arr, itemsPerColumn, isAll = false) => {
5049
+ const columns = [[], [], [], [], []];
5050
+ arr.forEach((item, index) => {
5051
+ const columnIndex = index % 5;
5052
+ if (isAll || columns[columnIndex].length < itemsPerColumn) {
5053
+ columns[columnIndex].push(item);
5054
+ } else {
5055
+ for (let i = 0; i < 5; i++) {
5056
+ const col = columns[(columnIndex + i + 1) % 5];
5057
+ if (col.length < itemsPerColumn) {
5058
+ col.push(item);
5059
+ break;
5060
+ }
5061
+ }
5062
+ }
5063
+ });
5064
+ return columns.filter((col) => col.length > 0);
5065
+ }, "groupByColumns");
4961
5066
  switch (typeValue) {
4962
5067
  case 1:
4963
- displayRecords.forEach((record, index) => {
4964
- const r = record;
4965
- lines.push(`${index + 1}. ${r.indtEventTime || "未知时间"}`);
4966
- lines.push(` IP: ${r.vClientIP || "未知"} | 设备: ${r.SystemHardware || "未知"}`);
5068
+ if (playerInfo) {
5069
+ templateData.playerInfo = playerInfo;
5070
+ }
5071
+ const loginRecords = records.map((r, i) => {
5072
+ const record = r;
5073
+ return {
5074
+ index: i + 1,
5075
+ indtEventTime: record.indtEventTime || "",
5076
+ outdtEventTime: record.outdtEventTime || "",
5077
+ vClientIP: record.vClientIP || "未知",
5078
+ SystemHardware: record.SystemHardware || "未知"
5079
+ };
4967
5080
  });
5081
+ templateData.loginColumns = groupByColumns(loginRecords, 5, isAllPages);
5082
+ const deviceStats = {};
5083
+ const ipStats = {};
5084
+ records.forEach((r) => {
5085
+ const record = r;
5086
+ const device = record.SystemHardware || "未知设备";
5087
+ const ip = record.vClientIP || "未知IP";
5088
+ deviceStats[device] = (deviceStats[device] || 0) + 1;
5089
+ ipStats[ip] = (ipStats[ip] || 0) + 1;
5090
+ });
5091
+ templateData.totalCount = records.length;
5092
+ templateData.deviceStats = Object.entries(deviceStats).map(([name2, count]) => ({ name: name2, count })).sort((a, b) => b.count - a.count);
5093
+ templateData.ipStats = Object.entries(ipStats).map(([ip, count]) => ({ ip, count })).sort((a, b) => b.count - a.count);
4968
5094
  break;
4969
5095
  case 2:
4970
- displayRecords.forEach((record, index) => {
4971
- const r = record;
4972
- const change = r.AddOrReduce || "";
4973
- const changeSymbol = change.startsWith("+") ? "📈" : "📉";
4974
- lines.push(`${index + 1}. ${r.dtEventTime || "未知时间"}`);
4975
- lines.push(` ${changeSymbol} ${r.Name || "未知物品"} ${change}`);
4976
- lines.push(` 原因: ${decodeReason(r.Reason)}`);
5096
+ const itemRecords = records.map((r, i) => {
5097
+ const record = r;
5098
+ const addOrReduce = String(record.AddOrReduce || "");
5099
+ return {
5100
+ index: i + 1,
5101
+ dtEventTime: record.dtEventTime || "",
5102
+ Name: record.Name || "未知物品",
5103
+ AddOrReduce: addOrReduce,
5104
+ Reason: decodeReason(record.Reason),
5105
+ changeType: addOrReduce.startsWith("+") ? "positive" : "negative"
5106
+ };
4977
5107
  });
5108
+ templateData.itemColumns = groupByColumns(itemRecords, 10, isAllPages);
4978
5109
  break;
4979
5110
  case 3:
4980
- displayRecords.forEach((record, index) => {
4981
- const r = record;
4982
- const change = r.AddOrReduce || "";
4983
- const changeSymbol = change.startsWith("+") ? "📈" : "📉";
4984
- lines.push(`${index + 1}. ${r.dtEventTime || "未知时间"}`);
4985
- lines.push(` ${changeSymbol} ${change} | 余额: ${r.leftMoney || "未知"}`);
4986
- lines.push(` 原因: ${decodeReason(r.Reason)}`);
5111
+ const moneyRecords = records.map((r, i) => {
5112
+ const record = r;
5113
+ const addOrReduce = String(record.AddOrReduce || "");
5114
+ return {
5115
+ index: i + 1,
5116
+ dtEventTime: record.dtEventTime || "",
5117
+ AddOrReduce: addOrReduce,
5118
+ leftMoney: record.leftMoney || "未知",
5119
+ Reason: decodeReason(record.Reason),
5120
+ changeType: addOrReduce.startsWith("+") ? "positive" : "negative"
5121
+ };
4987
5122
  });
5123
+ templateData.moneyColumns = groupByColumns(moneyRecords, 10, isAllPages);
4988
5124
  break;
4989
5125
  }
4990
- if (records.length > 20) {
4991
- lines.push(`
4992
- ... 还有 ${records.length - 20} 条记录未显示`);
4993
- }
4994
- return lines.join("\n");
5126
+ return templateData;
4995
5127
  }
4996
- __name(formatFlowRecords, "formatFlowRecords");
5128
+ __name(prepareTemplateData, "prepareTemplateData");
4997
5129
  function decodeReason(reason) {
4998
5130
  if (!reason) return "未知原因";
4999
5131
  try {
@@ -5020,7 +5152,18 @@ var COLOR_TO_QUALITY = {
5020
5152
  "绿": "普通"
5021
5153
  };
5022
5154
  var SUPPORTED_TYPES = ["干员皮肤", "喷漆", "挂饰", "典藏枪皮", "枪皮", "载具", "头像", "军牌"];
5023
- function registerCollectionCommands(ctx, api) {
5155
+ var CATEGORY_BG_MAP = {
5156
+ "干员皮肤": "operator-skin",
5157
+ "喷漆": "property-gx-li3.webp",
5158
+ "挂饰": "property-gx-li2.webp",
5159
+ "典藏枪皮": "property-jz-bg.webp",
5160
+ "枪皮": "property-jz-bg.webp",
5161
+ "载具": "property-qx-bg2.webp",
5162
+ "头像": "property-gx-li3.webp",
5163
+ "军牌": "property-jz-bg.webp",
5164
+ "其他资产": "property-gx-li3.webp"
5165
+ };
5166
+ function registerCollectionCommands(ctx, api, renderer) {
5024
5167
  const logger = ctx.logger("delta-force");
5025
5168
  ctx.command("df.collection [type:string]", "查看个人藏品").alias("df.藏品").alias("df.资产").usage(`支持的类型: ${SUPPORTED_TYPES.join("、")}
5026
5169
  不指定类型则查询所有藏品`).action(async ({ session }, typeFilter) => {
@@ -5037,7 +5180,7 @@ function registerCollectionCommands(ctx, api) {
5037
5180
  api.getCollectionMap()
5038
5181
  ]);
5039
5182
  if (await handleApiError(collectionRes, session)) return;
5040
- if (!collectionMapRes || collectionMapRes.code !== 0) {
5183
+ if (!collectionMapRes || collectionMapRes.success === false && String(collectionMapRes.code) !== "0") {
5041
5184
  logger.warn("获取藏品对照表失败:", collectionMapRes?.message);
5042
5185
  return "获取藏品基础信息失败,无法展示您的资产";
5043
5186
  }
@@ -5052,12 +5195,11 @@ function registerCollectionCommands(ctx, api) {
5052
5195
  collectionMapRes.data.map((item) => [String(item.id), item])
5053
5196
  );
5054
5197
  const categorizedItems = {};
5055
- const availableTypes = /* @__PURE__ */ new Set();
5198
+ const qualityOrder = ["传说", "史诗", "稀有", "普通", "其他"];
5056
5199
  allUserItems.forEach((item) => {
5057
5200
  const itemInfo = collectionMap.get(item.ItemId);
5058
5201
  if (!itemInfo) return;
5059
5202
  const primaryCategory = itemInfo.type || "其他资产";
5060
- availableTypes.add(primaryCategory);
5061
5203
  if (typeFilter && !primaryCategory.includes(typeFilter) && !typeFilter.includes(primaryCategory)) {
5062
5204
  return;
5063
5205
  }
@@ -5071,8 +5213,9 @@ function registerCollectionCommands(ctx, api) {
5071
5213
  categorizedItems[primaryCategory][quality].push({
5072
5214
  name: itemInfo.name,
5073
5215
  id: item.ItemId,
5074
- quality,
5075
- qualityLevel: QUALITY_CONFIG[quality]?.level || 1
5216
+ imageUrl: `https://playerhub.df.qq.com/playerhub/60004/object/${item.ItemId}.png`,
5217
+ qualityLevel: QUALITY_CONFIG[quality]?.level || 1,
5218
+ category: primaryCategory
5076
5219
  });
5077
5220
  });
5078
5221
  if (typeFilter && Object.keys(categorizedItems).length === 0) {
@@ -5080,45 +5223,50 @@ function registerCollectionCommands(ctx, api) {
5080
5223
 
5081
5224
  支持的查询类型: ${SUPPORTED_TYPES.join("、")}`;
5082
5225
  }
5083
- const typeName = typeFilter || "所有藏品";
5084
- const lines = [`【${typeName}】`];
5085
- lines.push("━━━━━━━━━━━━━━━");
5086
- const qualityStats = {};
5226
+ const categories = [];
5227
+ const qualityStatsMap = {};
5087
5228
  let totalCount = 0;
5088
- const qualityOrder = ["传说", "史诗", "稀有", "普通", "其他"];
5089
5229
  for (const category in categorizedItems) {
5090
5230
  const categoryItems = categorizedItems[category];
5231
+ const categoryItemsList = [];
5091
5232
  let categoryCount = 0;
5092
- const categoryLines = [];
5093
5233
  qualityOrder.forEach((quality) => {
5094
- const items = categoryItems[quality];
5095
- if (items && items.length > 0) {
5096
- categoryCount += items.length;
5097
- totalCount += items.length;
5098
- qualityStats[quality] = (qualityStats[quality] || 0) + items.length;
5099
- const qualityIcon = getQualityIcon(quality);
5100
- categoryLines.push(` ${qualityIcon} ${quality} (${items.length}件)`);
5101
- const displayItems = items.slice(0, 5);
5102
- displayItems.forEach((item) => {
5103
- categoryLines.push(` • ${item.name}`);
5104
- });
5105
- if (items.length > 5) {
5106
- categoryLines.push(` ... 还有 ${items.length - 5} 件`);
5234
+ if (categoryItems[quality] && categoryItems[quality].length > 0) {
5235
+ const qualityItems = categoryItems[quality];
5236
+ categoryCount += qualityItems.length;
5237
+ totalCount += qualityItems.length;
5238
+ if (!qualityStatsMap[quality]) {
5239
+ qualityStatsMap[quality] = 0;
5107
5240
  }
5241
+ qualityStatsMap[quality] += qualityItems.length;
5242
+ categoryItemsList.push(...qualityItems);
5108
5243
  }
5109
5244
  });
5110
5245
  if (categoryCount > 0) {
5111
- lines.push(`
5112
- 【${category}】(${categoryCount}件)`);
5113
- lines.push(...categoryLines);
5246
+ categories.push({
5247
+ name: category,
5248
+ items: categoryItemsList,
5249
+ count: categoryCount,
5250
+ bgImage: CATEGORY_BG_MAP[category] || "property-gx-li3.webp"
5251
+ });
5114
5252
  }
5115
5253
  }
5116
- lines.unshift(`总计: ${totalCount} 件藏品`);
5117
- const statsLine = qualityOrder.filter((q) => qualityStats[q]).map((q) => `${getQualityIcon(q)}${qualityStats[q]}`).join(" | ");
5118
- if (statsLine) {
5119
- lines.splice(2, 0, statsLine);
5254
+ if (categories.length === 0) {
5255
+ return "未能解析到您的任何藏品信息";
5120
5256
  }
5121
- return lines.join("\n");
5257
+ const qualityStats = qualityOrder.filter((quality) => qualityStatsMap[quality] && qualityStatsMap[quality] > 0).map((quality) => ({
5258
+ level: QUALITY_CONFIG[quality]?.level || 1,
5259
+ count: qualityStatsMap[quality]
5260
+ }));
5261
+ const typeName = typeFilter || "所有藏品";
5262
+ const templateData = {
5263
+ typeName,
5264
+ totalCount,
5265
+ qualityStats,
5266
+ categories
5267
+ };
5268
+ const imageResult = await renderer.renderToMessage("collection", templateData);
5269
+ return imageResult;
5122
5270
  } catch (error) {
5123
5271
  logger.error("查询藏品失败:", error);
5124
5272
  return `查询失败: ${error.message}`;
@@ -5126,21 +5274,6 @@ function registerCollectionCommands(ctx, api) {
5126
5274
  });
5127
5275
  }
5128
5276
  __name(registerCollectionCommands, "registerCollectionCommands");
5129
- function getQualityIcon(quality) {
5130
- switch (quality) {
5131
- case "传说":
5132
- return "🟠";
5133
- case "史诗":
5134
- return "🟣";
5135
- case "稀有":
5136
- return "🔵";
5137
- case "普通":
5138
- return "🟢";
5139
- default:
5140
- return "⚪";
5141
- }
5142
- }
5143
- __name(getQualityIcon, "getQualityIcon");
5144
5277
 
5145
5278
  // src/commands/info/place.ts
5146
5279
  init_database();
@@ -5169,7 +5302,19 @@ var PLACE_TYPE_NAMES = {
5169
5302
  "diving": "潜水中心"
5170
5303
  };
5171
5304
  var PLACE_TYPE_ORDER = ["storage", "control", "workbench", "tech", "shoot", "training", "pharmacy", "armory", "collect", "diving"];
5172
- function registerPlaceCommands(ctx, api) {
5305
+ var TYPE_IMAGE_MAP = {
5306
+ "storage": "仓库.png",
5307
+ "control": "指挥中心.png",
5308
+ "workbench": "工作台.png",
5309
+ "tech": "技术中心.png",
5310
+ "shoot": "靶场.png",
5311
+ "training": "训练中心.png",
5312
+ "pharmacy": "制药台.png",
5313
+ "armory": "防具台.png",
5314
+ "collect": "收藏室.png",
5315
+ "diving": "潜水中心.png"
5316
+ };
5317
+ function registerPlaceCommands(ctx, api, renderer) {
5173
5318
  const logger = ctx.logger("delta-force");
5174
5319
  ctx.command("df.place [type:string] [level:number]", "查看特勤处设施信息").alias("df.特勤处").alias("df.特勤处信息").usage(`支持的设施类型: ${Object.keys(PLACE_TYPE_MAP).join("、")}
5175
5320
  示例:
@@ -5211,10 +5356,45 @@ function registerPlaceCommands(ctx, api) {
5211
5356
  if (places.length === 0) {
5212
5357
  return "未能查询到任何特勤处设施信息";
5213
5358
  }
5359
+ const userDisplayInfo = await getUserDisplayInfo(api, token, userId, session.username || "用户");
5214
5360
  if (placeType) {
5215
- return formatPlacesByType(places, placeType, level, relateMap || {});
5361
+ return await renderPlacesByType(places, placeType, level, relateMap || {}, userDisplayInfo, renderer, session, logger);
5362
+ }
5363
+ const groupedByType = {};
5364
+ places.forEach((place) => {
5365
+ const pType = place.placeType || "unknown";
5366
+ if (!groupedByType[pType]) {
5367
+ groupedByType[pType] = [];
5368
+ }
5369
+ groupedByType[pType].push(place);
5370
+ });
5371
+ const sortedTypes = Object.keys(groupedByType).sort((a, b) => {
5372
+ const indexA = PLACE_TYPE_ORDER.indexOf(a);
5373
+ const indexB = PLACE_TYPE_ORDER.indexOf(b);
5374
+ if (indexA === -1 && indexB === -1) return a.localeCompare(b);
5375
+ if (indexA === -1) return 1;
5376
+ if (indexB === -1) return -1;
5377
+ return indexA - indexB;
5378
+ });
5379
+ for (const pType of sortedTypes) {
5380
+ const typePlaces = groupedByType[pType];
5381
+ if (typePlaces.length === 0) continue;
5382
+ const maxLevel = Math.max(...typePlaces.map((p) => p.level || 0));
5383
+ const maxLevelPlace = typePlaces.find((p) => p.level === maxLevel);
5384
+ if (!maxLevelPlace) continue;
5385
+ const processedPlace = processPlace(maxLevelPlace, relateMap || {});
5386
+ const placeTypeName = PLACE_TYPE_NAMES[pType] || pType;
5387
+ const templateData = {
5388
+ userName: userDisplayInfo.userName,
5389
+ userAvatar: userDisplayInfo.userAvatar || userDisplayInfo.qqAvatarUrl,
5390
+ qqAvatarUrl: userDisplayInfo.qqAvatarUrl,
5391
+ placeTypeName,
5392
+ places: [processedPlace]
5393
+ };
5394
+ const imageResult = await renderer.renderToMessage("placeInfo", templateData);
5395
+ await session.send(imageResult);
5216
5396
  }
5217
- return formatAllPlaces(places, relateMap || {});
5397
+ return;
5218
5398
  } catch (error) {
5219
5399
  logger.error("查询特勤处信息失败:", error);
5220
5400
  return `查询失败: ${error.message}`;
@@ -5253,7 +5433,7 @@ function registerPlaceCommands(ctx, api) {
5253
5433
  });
5254
5434
  }
5255
5435
  __name(registerPlaceCommands, "registerPlaceCommands");
5256
- function formatPlacesByType(places, placeType, targetLevel, relateMap) {
5436
+ async function renderPlacesByType(places, placeType, targetLevel, relateMap, userDisplayInfo, renderer, session, logger) {
5257
5437
  const typeName = PLACE_TYPE_NAMES[placeType] || placeType;
5258
5438
  const groupedByLevel = {};
5259
5439
  places.forEach((place) => {
@@ -5267,6 +5447,7 @@ function formatPlacesByType(places, placeType, targetLevel, relateMap) {
5267
5447
  if (targetLevel !== void 0) {
5268
5448
  let levelPlaces = groupedByLevel[targetLevel];
5269
5449
  let actualLevel = targetLevel;
5450
+ let needNotify = false;
5270
5451
  if (!levelPlaces || levelPlaces.length === 0) {
5271
5452
  if (sortedLevels.length === 0) {
5272
5453
  return `未找到 ${typeName} 的设施信息`;
@@ -5274,114 +5455,141 @@ function formatPlacesByType(places, placeType, targetLevel, relateMap) {
5274
5455
  const maxLevel = Math.max(...sortedLevels);
5275
5456
  levelPlaces = groupedByLevel[maxLevel];
5276
5457
  actualLevel = maxLevel;
5458
+ needNotify = true;
5277
5459
  if (!levelPlaces || levelPlaces.length === 0) {
5278
5460
  return `未找到 ${typeName} 的设施信息`;
5279
5461
  }
5280
5462
  }
5281
5463
  const place = levelPlaces[0];
5282
- const lines2 = [];
5283
- if (actualLevel !== targetLevel) {
5284
- lines2.push(`⚠️ 未找到等级 ${targetLevel},显示最高等级 ${actualLevel}`);
5285
- lines2.push("");
5286
- }
5287
- lines2.push(`【${typeName} - Lv.${actualLevel}】`);
5288
- lines2.push("━━━━━━━━━━━━━━━");
5289
- lines2.push(...formatPlaceDetail(place, relateMap));
5290
- return lines2.join("\n");
5291
- }
5292
- const lines = [`【${typeName}】`];
5293
- lines.push(`共 ${places.length} 个设施,${sortedLevels.length} 个等级`);
5294
- lines.push("━━━━━━━━━━━━━━━");
5295
- sortedLevels.forEach((level) => {
5464
+ const processedPlace = processPlace(place, relateMap);
5465
+ const templateData = {
5466
+ userName: userDisplayInfo.userName,
5467
+ userAvatar: userDisplayInfo.userAvatar || userDisplayInfo.qqAvatarUrl,
5468
+ qqAvatarUrl: userDisplayInfo.qqAvatarUrl,
5469
+ placeTypeName: typeName,
5470
+ places: [processedPlace]
5471
+ };
5472
+ if (needNotify) {
5473
+ await session.send(`未找到 ${typeName} 等级 ${targetLevel},已返回最高等级 ${actualLevel}。`);
5474
+ }
5475
+ return await renderer.renderToMessage("placeInfo", templateData);
5476
+ }
5477
+ for (const level of sortedLevels) {
5296
5478
  const levelPlaces = groupedByLevel[level];
5297
- if (levelPlaces.length === 0) return;
5479
+ if (levelPlaces.length === 0) continue;
5298
5480
  const place = levelPlaces[0];
5299
- lines.push("");
5300
- lines.push(`📍 Lv.${level}`);
5301
- lines.push(...formatPlaceDetail(place, relateMap));
5302
- });
5303
- return lines.join("\n");
5304
- }
5305
- __name(formatPlacesByType, "formatPlacesByType");
5306
- function formatAllPlaces(places, relateMap) {
5307
- const groupedByType = {};
5308
- places.forEach((place) => {
5309
- const type = place.placeType || "unknown";
5310
- if (!groupedByType[type]) {
5311
- groupedByType[type] = [];
5312
- }
5313
- groupedByType[type].push(place);
5314
- });
5315
- const sortedTypes = Object.keys(groupedByType).sort((a, b) => {
5316
- const indexA = PLACE_TYPE_ORDER.indexOf(a);
5317
- const indexB = PLACE_TYPE_ORDER.indexOf(b);
5318
- if (indexA === -1 && indexB === -1) return a.localeCompare(b);
5319
- if (indexA === -1) return 1;
5320
- if (indexB === -1) return -1;
5321
- return indexA - indexB;
5322
- });
5323
- const lines = ["【特勤处设施总览】"];
5324
- lines.push("━━━━━━━━━━━━━━━");
5325
- sortedTypes.forEach((type) => {
5326
- const typePlaces = groupedByType[type];
5327
- const typeName = PLACE_TYPE_NAMES[type] || type;
5328
- const maxLevel = Math.max(...typePlaces.map((p) => p.level || 0));
5329
- const levelCount = new Set(typePlaces.map((p) => p.level || 0)).size;
5330
- lines.push(`📍 ${typeName}: 最高 Lv.${maxLevel} (共 ${levelCount} 级)`);
5331
- });
5332
- lines.push("");
5333
- lines.push("使用 df.place <设施名> 查看详细信息");
5334
- return lines.join("\n");
5481
+ const processedPlace = processPlace(place, relateMap);
5482
+ const templateData = {
5483
+ userName: userDisplayInfo.userName,
5484
+ userAvatar: userDisplayInfo.userAvatar || userDisplayInfo.qqAvatarUrl,
5485
+ qqAvatarUrl: userDisplayInfo.qqAvatarUrl,
5486
+ placeTypeName: typeName,
5487
+ places: [processedPlace]
5488
+ };
5489
+ const imageResult = await renderer.renderToMessage("placeInfo", templateData);
5490
+ await session.send(imageResult);
5491
+ }
5492
+ return "";
5335
5493
  }
5336
- __name(formatAllPlaces, "formatAllPlaces");
5337
- function formatPlaceDetail(place, relateMap) {
5338
- const lines = [];
5494
+ __name(renderPlacesByType, "renderPlacesByType");
5495
+ function processPlace(place, relateMap) {
5496
+ const placeTypeValue = place.placeType || "";
5497
+ let displayName = place.placeName || "";
5498
+ if (!/[\u4e00-\u9fa5]/.test(displayName)) {
5499
+ displayName = PLACE_TYPE_NAMES[placeTypeValue] || displayName || "未知设施";
5500
+ }
5501
+ const imageFileName = TYPE_IMAGE_MAP[placeTypeValue] || null;
5502
+ const imageUrl = imageFileName ? `imgs/place/${imageFileName}` : null;
5503
+ const processedPlace = {
5504
+ displayName,
5505
+ level: place.level || 0,
5506
+ imageUrl,
5507
+ upgradeInfo: null,
5508
+ upgradeRequired: [],
5509
+ unlockInfo: null,
5510
+ detail: place.detail || ""
5511
+ };
5339
5512
  if (place.upgradeInfo) {
5340
- const { condition, hafCount } = place.upgradeInfo;
5341
- if (condition && condition !== "无" && condition !== "默认解锁") {
5342
- lines.push(`升级条件: ${condition}`);
5343
- }
5344
- if (hafCount && hafCount > 0) {
5345
- lines.push(`升级费用: ${hafCount.toLocaleString()} 烽火币`);
5513
+ let conditionText = place.upgradeInfo.condition || "无";
5514
+ let conditions = [];
5515
+ let levelCondition = null;
5516
+ if (conditionText && conditionText !== "无" && conditionText !== "默认解锁") {
5517
+ const allConditions = conditionText.split(/[;;]/).map((c) => c.trim()).filter((c) => c.length > 0);
5518
+ allConditions.forEach((condition) => {
5519
+ if (/解锁等级|等级\d+/.test(condition)) {
5520
+ levelCondition = condition;
5521
+ } else {
5522
+ conditions.push(condition);
5523
+ }
5524
+ });
5346
5525
  }
5526
+ processedPlace.upgradeInfo = {
5527
+ condition: conditionText,
5528
+ conditions,
5529
+ levelCondition,
5530
+ hafCount: place.upgradeInfo.hafCount || 0,
5531
+ hafCountFormatted: place.upgradeInfo.hafCount && place.upgradeInfo.hafCount > 0 ? place.upgradeInfo.hafCount.toLocaleString() : "0"
5532
+ };
5347
5533
  }
5348
5534
  if (place.upgradeRequired && place.upgradeRequired.length > 0) {
5349
- lines.push("升级材料:");
5350
- place.upgradeRequired.forEach((req) => {
5535
+ processedPlace.upgradeRequired = place.upgradeRequired.map((req) => {
5351
5536
  const itemInfo = relateMap[String(req.objectID)];
5352
- const itemName = itemInfo?.objectName || `物品(${req.objectID})`;
5353
- lines.push(` • ${itemName} x${req.count}`);
5537
+ const itemName = itemInfo ? itemInfo.objectName : `物品ID: ${req.objectID}`;
5538
+ const imgUrl = itemInfo?.pic || (req.objectID ? `https://playerhub.df.qq.com/playerhub/60004/object/${req.objectID}.png` : null);
5539
+ return {
5540
+ objectName: itemName,
5541
+ count: req.count,
5542
+ imageUrl: imgUrl
5543
+ };
5354
5544
  });
5355
5545
  }
5356
5546
  if (place.unlockInfo) {
5357
- const { properties, props } = place.unlockInfo;
5358
- if (properties?.list && properties.list.length > 0) {
5359
- lines.push("解锁属性:");
5360
- properties.list.forEach((prop) => {
5361
- lines.push(` • ${prop}`);
5547
+ const unlockData = {
5548
+ properties: [],
5549
+ props: []
5550
+ };
5551
+ const properties = place.unlockInfo.properties?.list || [];
5552
+ if (properties.length > 0) {
5553
+ unlockData.properties = properties.map((prop) => {
5554
+ if (typeof prop === "string") {
5555
+ return prop;
5556
+ } else if (prop && typeof prop === "object") {
5557
+ return prop.name || prop.objectName || prop.desc || JSON.stringify(prop);
5558
+ }
5559
+ return String(prop);
5362
5560
  });
5363
5561
  }
5364
- if (props && props.length > 0) {
5365
- lines.push("解锁道具:");
5366
- props.forEach((prop) => {
5367
- let itemName = "未知道具";
5368
- if (prop.objectID) {
5369
- const itemInfo = relateMap[String(prop.objectID)];
5370
- itemName = itemInfo?.objectName || prop.name || prop.objectName || `物品(${prop.objectID})`;
5371
- } else if (prop.name || prop.objectName) {
5372
- itemName = prop.name || prop.objectName || "未知道具";
5562
+ const props = place.unlockInfo.props || [];
5563
+ if (props.length > 0) {
5564
+ unlockData.props = props.map((prop) => {
5565
+ if (typeof prop === "string") {
5566
+ return { objectName: prop, imageUrl: null, count: null };
5567
+ } else if (prop && typeof prop === "object") {
5568
+ let objectName = "未知道具";
5569
+ let imgUrl = null;
5570
+ if (prop.objectID) {
5571
+ const itemInfo = relateMap[String(prop.objectID)];
5572
+ objectName = itemInfo && itemInfo.objectName ? itemInfo.objectName : `物品ID: ${prop.objectID}`;
5573
+ imgUrl = itemInfo?.pic || `https://playerhub.df.qq.com/playerhub/60004/object/${prop.objectID}.png`;
5574
+ } else if (prop.name || prop.objectName) {
5575
+ objectName = prop.name || prop.objectName || "未知道具";
5576
+ }
5577
+ return {
5578
+ objectName,
5579
+ imageUrl: imgUrl,
5580
+ count: prop.count || null
5581
+ };
5373
5582
  }
5374
- const countStr = prop.count ? ` x${prop.count}` : "";
5375
- lines.push(` • ${itemName}${countStr}`);
5583
+ return { objectName: String(prop), imageUrl: null, count: null };
5376
5584
  });
5377
5585
  }
5586
+ if (unlockData.properties.length > 0 || unlockData.props.length > 0) {
5587
+ processedPlace.unlockInfo = unlockData;
5588
+ }
5378
5589
  }
5379
- if (lines.length === 0) {
5380
- lines.push("暂无详细信息");
5381
- }
5382
- return lines;
5590
+ return processedPlace;
5383
5591
  }
5384
- __name(formatPlaceDetail, "formatPlaceDetail");
5592
+ __name(processPlace, "processPlace");
5385
5593
 
5386
5594
  // src/commands/info/ban.ts
5387
5595
  init_database();
@@ -5455,13 +5663,13 @@ function getArmyTypeById(id) {
5455
5663
  }
5456
5664
  __name(getArmyTypeById, "getArmyTypeById");
5457
5665
  var ARMY_TYPE_ORDER = ["突击", "工程", "支援", "侦察"];
5458
- function registerOperatorCommands(ctx, api, dataManager) {
5666
+ function registerOperatorCommands(ctx, api, dataManager, renderer) {
5459
5667
  const logger = ctx.logger("delta-force");
5460
5668
  ctx.command("df.operators", "查看干员列表").alias("df.干员列表").action(async ({ session }) => {
5461
5669
  await session.send("正在查询干员列表,请稍候...");
5462
5670
  try {
5463
5671
  const res = await api.getOperators();
5464
- if (!res || res.code !== 0) {
5672
+ if (!res || res.success === false && String(res.code) !== "0") {
5465
5673
  return `查询失败: ${res?.msg || res?.message || "未知错误"}`;
5466
5674
  }
5467
5675
  const operators = res.data;
@@ -5510,7 +5718,7 @@ function registerOperatorCommands(ctx, api, dataManager) {
5510
5718
  await session.send(`正在查询干员「${operatorName}」的信息,请稍候...`);
5511
5719
  try {
5512
5720
  const res = await api.getOperator();
5513
- if (!res || res.code !== 0) {
5721
+ if (!res || res.success === false && String(res.code) !== "0") {
5514
5722
  return `查询失败: ${res?.msg || res?.message || "未知错误"}`;
5515
5723
  }
5516
5724
  const operators = res.data;
@@ -5519,8 +5727,8 @@ function registerOperatorCommands(ctx, api, dataManager) {
5519
5727
  }
5520
5728
  const matchedOperators = operators.filter((op) => {
5521
5729
  const opName = op.operator || "";
5522
- const fullName = op.fullName || "";
5523
- return opName.includes(operatorName) || fullName.includes(operatorName) || operatorName.includes(opName) || operatorName.includes(fullName);
5730
+ const fullName2 = op.fullName || "";
5731
+ return opName.includes(operatorName) || fullName2.includes(operatorName) || operatorName.includes(opName) || operatorName.includes(fullName2);
5524
5732
  });
5525
5733
  if (matchedOperators.length === 0) {
5526
5734
  return `未找到干员「${operatorName}」的信息,请检查干员名称是否正确`;
@@ -5528,43 +5736,39 @@ function registerOperatorCommands(ctx, api, dataManager) {
5528
5736
  let operator = matchedOperators.find(
5529
5737
  (op) => op.operator === operatorName || op.fullName === operatorName
5530
5738
  ) || matchedOperators[0];
5531
- let multiMatchHint = "";
5532
5739
  if (matchedOperators.length > 1) {
5533
5740
  const names = matchedOperators.map((op) => op.operator || op.fullName).join("、");
5534
- multiMatchHint = `⚠️ 找到多个匹配: ${names}
5535
- 显示第一个匹配结果
5536
-
5537
- `;
5538
- }
5539
- const lines = [];
5540
- if (multiMatchHint) {
5541
- lines.push(multiMatchHint);
5542
- }
5543
- lines.push(`【${operator.operator || "未知干员"}】`);
5544
- if (operator.fullName) {
5545
- lines.push(`全名: ${operator.fullName}`);
5546
- }
5547
- if (operator.armyType) {
5548
- lines.push(`兵种: ${operator.armyType}`);
5741
+ await session.send(`找到多个匹配的干员:${names},将显示第一个匹配结果。`);
5549
5742
  }
5550
- if (operator.armyTypeDesc) {
5551
- lines.push(`兵种描述: ${operator.armyTypeDesc}`);
5743
+ let englishName = "";
5744
+ const fullName = operator.fullName || "";
5745
+ const englishMatch = fullName.match(/[A-Za-z\s·]+/);
5746
+ if (englishMatch) {
5747
+ englishName = englishMatch[0].trim().toUpperCase();
5552
5748
  }
5553
- if (operator.abilitiesList && operator.abilitiesList.length > 0) {
5554
- lines.push("");
5555
- lines.push("━━━ 技能列表 ━━━");
5556
- operator.abilitiesList.forEach((ability, index) => {
5557
- lines.push("");
5558
- lines.push(`【${ability.abilityName || "未知技能"}】`);
5559
- if (ability.abilityTypeCN || ability.abilityType) {
5560
- lines.push(`类型: ${ability.abilityTypeCN || ability.abilityType}`);
5561
- }
5562
- if (ability.abilityDesc) {
5563
- lines.push(`描述: ${ability.abilityDesc}`);
5564
- }
5565
- });
5749
+ const templateData = {
5750
+ operatorName: operator.operator || "未知干员",
5751
+ fullName,
5752
+ englishName,
5753
+ operatorPic: operator.pic || "",
5754
+ background: "",
5755
+ armyType: operator.armyType || "",
5756
+ armyTypeDesc: operator.armyTypeDesc || "",
5757
+ abilitiesList: (operator.abilitiesList || []).map((ability) => ({
5758
+ abilityName: ability.abilityName || "未知技能",
5759
+ abilityType: ability.abilityType || "",
5760
+ abilityTypeCN: ability.abilityTypeCN || ability.abilityType || "",
5761
+ abilityDesc: ability.abilityDesc || "",
5762
+ abilityPic: ability.abilityPic || ""
5763
+ }))
5764
+ };
5765
+ try {
5766
+ const imageResult = await renderer.renderToMessage("operator", templateData);
5767
+ return imageResult;
5768
+ } catch (renderError) {
5769
+ logger.error("渲染干员信息失败:", renderError);
5770
+ return `渲染失败: ${renderError.message}`;
5566
5771
  }
5567
- return lines.join("\n");
5568
5772
  } catch (error) {
5569
5773
  logger.error("查询干员信息失败:", error);
5570
5774
  return `查询失败: ${error.message}`;
@@ -5672,6 +5876,825 @@ function formatOfflineStatus(errorInfo) {
5672
5876
  }
5673
5877
  __name(formatOfflineStatus, "formatOfflineStatus");
5674
5878
 
5879
+ // src/commands/info/personalData.ts
5880
+ init_database();
5881
+ function registerPersonalDataCommands(ctx, api, dataManager, renderer) {
5882
+ const logger = ctx.logger("delta-force");
5883
+ ctx.command("df.data [args:text]", "查看个人游戏数据统计").alias("df.数据").alias("df.个人数据").usage("参数说明:\n sol/烽火 - 仅查询烽火地带\n mp/全面 - 仅查询全面战场\n 数字 - 指定赛季\n all - 所有赛季\n示例: df.data sol 7").action(async ({ session }, args) => {
5884
+ const userId = session.userId;
5885
+ const platform = session.platform;
5886
+ const token = await getActiveToken(ctx, userId, platform);
5887
+ if (!token) {
5888
+ return "您尚未登录,请先使用 df.login 登录";
5889
+ }
5890
+ let mode = "";
5891
+ let season = 7;
5892
+ if (args) {
5893
+ const argList = args.split(" ").filter(Boolean);
5894
+ for (const arg of argList) {
5895
+ if (["烽火", "烽火地带", "sol", "摸金"].includes(arg)) {
5896
+ mode = "sol";
5897
+ } else if (["全面", "全面战场", "战场", "mp"].includes(arg)) {
5898
+ mode = "mp";
5899
+ } else if (["all", "全部"].includes(arg.toLowerCase())) {
5900
+ season = "all";
5901
+ } else if (!isNaN(Number(arg))) {
5902
+ season = parseInt(arg);
5903
+ }
5904
+ }
5905
+ }
5906
+ await session.send("正在查询个人数据,请稍候...");
5907
+ try {
5908
+ const res = await api.getPersonalData(token, mode, season);
5909
+ if (!res) {
5910
+ return "查询数据失败,请检查网络或联系管理员。";
5911
+ }
5912
+ if (res.success === false) {
5913
+ return `查询数据失败: ${res.message || "未知API错误"}`;
5914
+ }
5915
+ let solDetail = null;
5916
+ let mpDetail = null;
5917
+ if (mode) {
5918
+ const singleModeData = res.data?.data;
5919
+ const innerData = singleModeData?.data;
5920
+ if (innerData?.solDetail) solDetail = innerData.solDetail;
5921
+ if (innerData?.mpDetail) mpDetail = innerData.mpDetail;
5922
+ } else {
5923
+ const allModesData = res.data;
5924
+ const solData = allModesData?.sol;
5925
+ const mpData = allModesData?.mp;
5926
+ if (solData?.data) {
5927
+ const solInner = solData.data?.data;
5928
+ if (solInner?.solDetail) solDetail = solInner.solDetail;
5929
+ }
5930
+ if (mpData?.data) {
5931
+ const mpInner = mpData.data?.data;
5932
+ if (mpInner?.mpDetail) mpDetail = mpInner.mpDetail;
5933
+ }
5934
+ }
5935
+ if (!solDetail && !mpDetail) {
5936
+ return "暂未查询到该账号的游戏数据。";
5937
+ }
5938
+ const userDisplayInfo = await getUserDisplayInfo(api, token, userId, session.username || "用户");
5939
+ const now = /* @__PURE__ */ new Date();
5940
+ const currentDate = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
5941
+ const templateData = {
5942
+ nickname: userDisplayInfo.userName,
5943
+ userName: userDisplayInfo.userName,
5944
+ userAvatar: userDisplayInfo.userAvatar,
5945
+ userId,
5946
+ qqAvatarUrl: userDisplayInfo.qqAvatarUrl,
5947
+ currentDate,
5948
+ season: season === "all" ? "全部" : season
5949
+ };
5950
+ if ((!mode || mode === "sol") && solDetail) {
5951
+ const solData = solDetail;
5952
+ const solRank = solData.levelScore ? dataManager.getRankByScore(solData.levelScore, "sol") : "-";
5953
+ const solRankImage = solRank !== "-" ? dataManager.getRankImage(solData.levelScore, "sol") : null;
5954
+ const totalGameTime = /* @__PURE__ */ __name((seconds) => {
5955
+ if (!seconds || isNaN(seconds)) return "0分钟";
5956
+ const hours = Math.floor(seconds / 3600);
5957
+ const minutes = Math.floor(seconds % 3600 / 60);
5958
+ return `${hours}小时${minutes}分钟`;
5959
+ }, "totalGameTime");
5960
+ const formatGainedPrice = /* @__PURE__ */ __name((price) => {
5961
+ if (!price || isNaN(price)) return "-";
5962
+ return `${(price / 1e6).toFixed(2)}M`;
5963
+ }, "formatGainedPrice");
5964
+ const formatKd = /* @__PURE__ */ __name((kd) => {
5965
+ if (kd === null || kd === void 0 || isNaN(kd)) return "-";
5966
+ return (kd / 100).toFixed(2);
5967
+ }, "formatKd");
5968
+ const formatPrice3 = /* @__PURE__ */ __name((price) => {
5969
+ if (!price || isNaN(price)) return "-";
5970
+ if (price >= 1e9) {
5971
+ return (price / 1e9).toFixed(2) + "B";
5972
+ } else if (price >= 1e6) {
5973
+ return (price / 1e6).toFixed(2) + "M";
5974
+ } else if (price >= 1e3) {
5975
+ return (price / 1e3).toFixed(1) + "K";
5976
+ } else {
5977
+ return price.toFixed(0);
5978
+ }
5979
+ }, "formatPrice");
5980
+ const mapOrder = ["零号大坝", "长弓溪谷", "巴克什", "航天基地", "潮汐监狱"];
5981
+ const solMapListRaw = (solData.mapList || []).map((map) => {
5982
+ const mapName = dataManager.getMapName(map.mapID);
5983
+ const mapImage = dataManager.getMapImagePath(mapName, "sol");
5984
+ const baseMapName = mapName.replace(/-?(常规|机密|绝密|水淹|适应)$/, "");
5985
+ return {
5986
+ mapID: map.mapID,
5987
+ totalCount: map.totalCount || 0,
5988
+ mapName,
5989
+ baseMapName,
5990
+ mapImage
5991
+ };
5992
+ });
5993
+ const mapGroups = {};
5994
+ solMapListRaw.forEach((map) => {
5995
+ if (!mapGroups[map.baseMapName]) {
5996
+ mapGroups[map.baseMapName] = [];
5997
+ }
5998
+ mapGroups[map.baseMapName].push(map);
5999
+ });
6000
+ let solMapList = mapOrder.filter((baseName) => mapGroups[baseName] && mapGroups[baseName].length > 0).map((baseName) => {
6001
+ const maps = mapGroups[baseName];
6002
+ maps.sort((a, b) => (b.totalCount || 0) - (a.totalCount || 0));
6003
+ return {
6004
+ baseMapName: baseName,
6005
+ maps
6006
+ };
6007
+ });
6008
+ const resultList = [];
6009
+ let pendingSingleGroups = [];
6010
+ const mergePendingGroups = /* @__PURE__ */ __name(() => {
6011
+ if (pendingSingleGroups.length > 1) {
6012
+ const mergedMaps = pendingSingleGroups.flatMap((sg) => sg.maps);
6013
+ resultList.push({ baseMapName: "merged", maps: mergedMaps });
6014
+ pendingSingleGroups = [];
6015
+ } else if (pendingSingleGroups.length === 1) {
6016
+ resultList.push(pendingSingleGroups[0]);
6017
+ pendingSingleGroups = [];
6018
+ }
6019
+ }, "mergePendingGroups");
6020
+ solMapList.forEach((group) => {
6021
+ if (group.maps && group.maps.length === 1) {
6022
+ pendingSingleGroups.push(group);
6023
+ } else {
6024
+ mergePendingGroups();
6025
+ resultList.push(group);
6026
+ }
6027
+ });
6028
+ mergePendingGroups();
6029
+ solMapList = resultList;
6030
+ const collectionIDs = (solData.redCollectionDetail || []).map((item) => String(item.objectID));
6031
+ const weaponIDs = (solData.gunPlayList || []).map((weapon) => String(weapon.objectID));
6032
+ const allObjectIDs = [.../* @__PURE__ */ new Set([...collectionIDs, ...weaponIDs])];
6033
+ const objectNameMap = {};
6034
+ if (allObjectIDs.length > 0) {
6035
+ try {
6036
+ const idsString = allObjectIDs.join(",");
6037
+ const searchRes = await api.searchObject("", idsString);
6038
+ if (searchRes?.success && searchRes?.data) {
6039
+ const keywords = searchRes.data?.keywords;
6040
+ if (keywords && Array.isArray(keywords)) {
6041
+ keywords.forEach((item) => {
6042
+ if (item.objectID) {
6043
+ const id = String(item.objectID);
6044
+ const name2 = item.name || item.objectName;
6045
+ if (name2) {
6046
+ objectNameMap[id] = name2;
6047
+ }
6048
+ }
6049
+ });
6050
+ logger.debug(`成功获取 ${Object.keys(objectNameMap).length}/${allObjectIDs.length} 个物品名称`);
6051
+ }
6052
+ }
6053
+ } catch (error) {
6054
+ logger.warn("获取物品名称失败:", error);
6055
+ }
6056
+ }
6057
+ const solRedCollection = (solData.redCollectionDetail || []).map((item) => ({
6058
+ objectID: item.objectID,
6059
+ objectName: objectNameMap[String(item.objectID)] || item.objectName || `物品(${item.objectID})`,
6060
+ imageUrl: `https://playerhub.df.qq.com/playerhub/60004/object/${item.objectID}.png`,
6061
+ price: item.price || 0,
6062
+ priceFormatted: formatPrice3(item.price)
6063
+ })).sort((a, b) => b.price - a.price).slice(0, 10);
6064
+ const solGunPlayList = (solData.gunPlayList || []).map((weapon) => ({
6065
+ objectID: weapon.objectID,
6066
+ weaponName: objectNameMap[String(weapon.objectID)] || `武器(${weapon.objectID})`,
6067
+ imageUrl: `https://playerhub.df.qq.com/playerhub/60004/object/${weapon.objectID}.png`,
6068
+ totalPrice: weapon.totalPrice || 0,
6069
+ totalPriceFormatted: weapon.totalPrice ? (weapon.totalPrice / 1e6).toFixed(2) + "M" : "-",
6070
+ escapeRate: weapon.fightCount > 0 ? (weapon.escapeCount / weapon.fightCount * 100).toFixed(1) + "%" : "-"
6071
+ })).sort((a, b) => b.totalPrice - a.totalPrice).slice(0, 10);
6072
+ templateData.solDetail = {
6073
+ ...solData,
6074
+ totalGameTime: totalGameTime(solData.totalGameTime),
6075
+ totalGainedPriceFormatted: formatGainedPrice(solData.totalGainedPrice),
6076
+ profitLossRatioFormatted: solData.profitLossRatio ? (solData.profitLossRatio / 1e5).toFixed(1) + "K" : "-",
6077
+ lowKD: formatKd(solData.lowKillDeathRatio),
6078
+ medKD: formatKd(solData.medKillDeathRatio),
6079
+ highKD: formatKd(solData.highKillDeathRatio),
6080
+ totalFight: solData.totalFight || 0,
6081
+ totalKill: solData.totalKill || 0,
6082
+ userRank: solData.userRank || "-",
6083
+ mapList: solMapList,
6084
+ redCollectionList: solRedCollection,
6085
+ gunPlayList: solGunPlayList
6086
+ };
6087
+ templateData.solRank = solRank;
6088
+ templateData.solRankImage = solRankImage;
6089
+ }
6090
+ if ((!mode || mode === "mp") && mpDetail) {
6091
+ const mpData = mpDetail;
6092
+ const mpRank = mpData.levelScore ? dataManager.getRankByScore(mpData.levelScore, "tdm") : "-";
6093
+ const mpRankImage = mpRank !== "-" ? dataManager.getRankImage(mpData.levelScore, "tdm") : null;
6094
+ const mpMapList = (mpData.mapList || []).map((map) => {
6095
+ const mapName = dataManager.getMapName(map.mapID);
6096
+ const mapImage = dataManager.getMapImagePath(mapName, "mp");
6097
+ return {
6098
+ mapID: map.mapID,
6099
+ totalCount: map.totalCount || 0,
6100
+ mapName,
6101
+ mapImage
6102
+ };
6103
+ }).sort((a, b) => b.totalCount - a.totalCount).slice(0, 10);
6104
+ const formatMPGameTime = /* @__PURE__ */ __name((minutes) => {
6105
+ if (!minutes || isNaN(minutes)) return "0分钟";
6106
+ const hours = Math.floor(minutes / 60);
6107
+ const mins = minutes % 60;
6108
+ return `${hours}小时${mins}分钟`;
6109
+ }, "formatMPGameTime");
6110
+ templateData.mpDetail = {
6111
+ ...mpData,
6112
+ totalGameTime: formatMPGameTime(mpData.totalGameTime),
6113
+ avgKillPerMinuteFormatted: mpData.avgKillPerMinute ? (mpData.avgKillPerMinute / 100).toFixed(2) : "-",
6114
+ avgScorePerMinuteFormatted: mpData.avgScorePerMinute ? (mpData.avgScorePerMinute / 100).toFixed(2) : "-",
6115
+ totalFight: mpData.totalFight || 0,
6116
+ totalWin: mpData.totalWin || 0,
6117
+ totalVehicleDestroyed: mpData.totalVehicleDestroyed || 0,
6118
+ mapList: mpMapList
6119
+ };
6120
+ templateData.mpRank = mpRank;
6121
+ templateData.mpRankImage = mpRankImage;
6122
+ }
6123
+ const hasBothModes = templateData.solDetail && templateData.mpDetail;
6124
+ if (hasBothModes) {
6125
+ const solTemplateData = {
6126
+ ...templateData,
6127
+ solDetail: templateData.solDetail,
6128
+ solRank: templateData.solRank,
6129
+ solRankImage: templateData.solRankImage,
6130
+ mpDetail: null
6131
+ };
6132
+ const solImage = await renderer.renderToMessage("personalData", solTemplateData, { width: 2e3 });
6133
+ await session.send(solImage);
6134
+ const mpTemplateData = {
6135
+ ...templateData,
6136
+ mpDetail: templateData.mpDetail,
6137
+ mpRank: templateData.mpRank,
6138
+ mpRankImage: templateData.mpRankImage,
6139
+ solDetail: null
6140
+ };
6141
+ const mpImage = await renderer.renderToMessage("personalData", mpTemplateData, { width: 2e3 });
6142
+ await session.send(mpImage);
6143
+ return;
6144
+ } else {
6145
+ const imageResult = await renderer.renderToMessage("personalData", templateData, { width: 2e3 });
6146
+ return imageResult;
6147
+ }
6148
+ } catch (error) {
6149
+ logger.error("查询个人数据失败:", error);
6150
+ return `查询失败: ${error.message}`;
6151
+ }
6152
+ });
6153
+ }
6154
+ __name(registerPersonalDataCommands, "registerPersonalDataCommands");
6155
+
6156
+ // src/commands/info/stats.ts
6157
+ function registerStatsCommands(ctx, config, api) {
6158
+ const logger = ctx.logger("delta-force");
6159
+ ctx.command("df.stats", "查看用户统计信息(管理员)").alias("df.用户统计").action(async ({ session }) => {
6160
+ await session.send("正在获取用户统计信息,请稍候...");
6161
+ try {
6162
+ const clientID = config.clientID;
6163
+ if (!clientID) {
6164
+ return "系统配置错误:clientID未配置,请联系管理员。";
6165
+ }
6166
+ const res = await api.getUserStats(clientID);
6167
+ if (await handleApiError(res, session)) return;
6168
+ if (!res.data) {
6169
+ return "获取统计信息失败:API返回数据为空";
6170
+ }
6171
+ const { accessLevel, ...data } = res;
6172
+ const statsData = res.data;
6173
+ if (accessLevel === "admin") {
6174
+ return displayAdminStats(statsData);
6175
+ } else {
6176
+ return displayUserStats(statsData);
6177
+ }
6178
+ } catch (error) {
6179
+ logger.error("获取用户统计失败:", error);
6180
+ return `获取用户统计失败: ${error.message}`;
6181
+ }
6182
+ });
6183
+ }
6184
+ __name(registerStatsCommands, "registerStatsCommands");
6185
+ function displayAdminStats(data) {
6186
+ const { users, api, subscription, loginMethods, platform, security } = data;
6187
+ const lines = ["【三角洲行动 - 全站用户统计】"];
6188
+ lines.push("权限级别:超级管理员");
6189
+ lines.push("");
6190
+ if (users) {
6191
+ lines.push("📊 用户统计");
6192
+ lines.push(`总用户数: ${users.total}`);
6193
+ lines.push(`邮箱已验证: ${users.emailVerified}`);
6194
+ lines.push(`邮箱未验证: ${users.emailUnverified}`);
6195
+ lines.push("");
6196
+ }
6197
+ if (api) {
6198
+ lines.push("🔑 API密钥统计");
6199
+ lines.push(`总密钥数: ${api.totalKeys}`);
6200
+ lines.push(`活跃密钥: ${api.activeKeys}`);
6201
+ lines.push(`非活跃密钥: ${api.inactiveKeys}`);
6202
+ lines.push("");
6203
+ }
6204
+ if (subscription) {
6205
+ lines.push("💎 订阅统计");
6206
+ lines.push(`专业用户: ${subscription.proUsers}`);
6207
+ lines.push(`免费用户: ${subscription.freeUsers}`);
6208
+ lines.push(`总订阅数: ${subscription.totalSubscriptions}`);
6209
+ lines.push("");
6210
+ }
6211
+ if (loginMethods) {
6212
+ lines.push("🔐 登录方式统计");
6213
+ Object.entries(loginMethods).forEach(([method, stats]) => {
6214
+ const methodName = getMethodDisplayName(method);
6215
+ lines.push(`${methodName}: ${stats.total} (有效: ${stats.valid}, 无效: ${stats.invalid})`);
6216
+ });
6217
+ lines.push("");
6218
+ }
6219
+ if (platform) {
6220
+ lines.push("🔗 平台绑定统计");
6221
+ lines.push(`总绑定数: ${platform.totalBindings}`);
6222
+ lines.push(`已绑定用户: ${platform.boundUsers}`);
6223
+ lines.push(`未绑定用户: ${platform.unboundUsers}`);
6224
+ lines.push("");
6225
+ }
6226
+ if (security) {
6227
+ lines.push("🛡️ 安全统计");
6228
+ lines.push(`24小时内密码重置: ${security.passwordResets24h}`);
6229
+ lines.push(`7天内密码重置: ${security.passwordResets7d}`);
6230
+ lines.push(`总安全事件: ${security.totalSecurityEvents}`);
6231
+ if (security.recentSecurityEvents && security.recentSecurityEvents.length > 0) {
6232
+ lines.push("最近安全事件:");
6233
+ security.recentSecurityEvents.forEach((event) => {
6234
+ const severity = getSeverityDisplayName(event.severity);
6235
+ const action = getActionDisplayName(event.action);
6236
+ lines.push(` • ${action}: ${event.count}次 (${severity})`);
6237
+ });
6238
+ }
6239
+ }
6240
+ return lines.join("\n").trim();
6241
+ }
6242
+ __name(displayAdminStats, "displayAdminStats");
6243
+ function displayUserStats(data) {
6244
+ const { userInfo, loginMethods, api } = data;
6245
+ const lines = ["【三角洲行动 - 个人统计信息】"];
6246
+ lines.push("权限级别:普通用户");
6247
+ lines.push("");
6248
+ if (userInfo) {
6249
+ lines.push("账号统计");
6250
+ lines.push(`总账号数: ${userInfo.totalAccounts}`);
6251
+ lines.push(`已绑定账号: ${userInfo.boundAccounts}`);
6252
+ lines.push(`未绑定账号: ${userInfo.unboundAccounts}`);
6253
+ lines.push("");
6254
+ }
6255
+ if (loginMethods) {
6256
+ lines.push("登录方式统计");
6257
+ Object.entries(loginMethods).forEach(([method, stats]) => {
6258
+ const methodName = getMethodDisplayName(method);
6259
+ lines.push(`${methodName}: ${stats.total} (有效: ${stats.valid}, 无效: ${stats.invalid})`);
6260
+ });
6261
+ lines.push("");
6262
+ }
6263
+ if (api) {
6264
+ lines.push("API密钥统计");
6265
+ lines.push(`总密钥数: ${api.totalKeys}`);
6266
+ lines.push(`活跃密钥: ${api.activeKeys}`);
6267
+ lines.push(`非活跃密钥: ${api.inactiveKeys}`);
6268
+ }
6269
+ return lines.join("\n").trim();
6270
+ }
6271
+ __name(displayUserStats, "displayUserStats");
6272
+ function getMethodDisplayName(method) {
6273
+ const methodNames = {
6274
+ "qq": "QQ登录",
6275
+ "wechat": "微信登录",
6276
+ "wegame": "WeGame登录",
6277
+ "wegameWechat": "WeGame微信登录",
6278
+ "qqsafe": "QQ安全中心",
6279
+ "qqCk": "QQ Cookie登录"
6280
+ };
6281
+ return methodNames[method] || method;
6282
+ }
6283
+ __name(getMethodDisplayName, "getMethodDisplayName");
6284
+ function getSeverityDisplayName(severity) {
6285
+ const severityNames = {
6286
+ "low": "低",
6287
+ "medium": "中",
6288
+ "high": "高",
6289
+ "critical": "严重"
6290
+ };
6291
+ return severityNames[severity] || severity;
6292
+ }
6293
+ __name(getSeverityDisplayName, "getSeverityDisplayName");
6294
+ function getActionDisplayName(action) {
6295
+ const actionNames = {
6296
+ "password_reset": "密码重置",
6297
+ "login_failed": "登录失败",
6298
+ "account_locked": "账号锁定",
6299
+ "suspicious_activity": "可疑活动",
6300
+ "api_abuse": "API滥用"
6301
+ };
6302
+ return actionNames[action] || action;
6303
+ }
6304
+ __name(getActionDisplayName, "getActionDisplayName");
6305
+
6306
+ // src/commands/info/redRecord.ts
6307
+ init_database();
6308
+ function formatNumber2(num) {
6309
+ if (typeof num !== "number" || isNaN(num)) {
6310
+ return "0";
6311
+ }
6312
+ if (num >= 1e9) {
6313
+ return (num / 1e9).toFixed(1).replace(/\.0$/, "") + "B";
6314
+ } else if (num >= 1e6) {
6315
+ return (num / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
6316
+ } else if (num >= 1e3) {
6317
+ return (num / 1e3).toFixed(1).replace(/\.0$/, "") + "K";
6318
+ } else {
6319
+ return num.toString();
6320
+ }
6321
+ }
6322
+ __name(formatNumber2, "formatNumber");
6323
+ function registerRedRecordCommands(ctx, api, dataManager, renderer) {
6324
+ const logger = ctx.logger("delta-force");
6325
+ ctx.command("df.redrecord [itemName:text]", "查看藏品解锁记录").alias("df.出红记录").alias("df.大红记录").alias("df.藏品记录").usage("示例:\n df.redrecord - 查看所有出红记录\n df.redrecord 物品名 - 查看指定物品的记录").action(async ({ session }, itemName) => {
6326
+ const userId = session.userId;
6327
+ const platform = session.platform;
6328
+ const token = await getActiveToken(ctx, userId, platform);
6329
+ if (!token) {
6330
+ return "您尚未登录,请先使用 df.login 登录";
6331
+ }
6332
+ if (itemName && itemName.trim()) {
6333
+ return await getRedByName(api, dataManager, renderer, token, itemName.trim(), userId, session, logger);
6334
+ }
6335
+ return await getRedRecordList(api, dataManager, renderer, token, userId, session, logger);
6336
+ });
6337
+ }
6338
+ __name(registerRedRecordCommands, "registerRedRecordCommands");
6339
+ async function getRedByName(api, dataManager, renderer, token, itemName, userId, session, logger) {
6340
+ await session.send(`正在搜索物品"${itemName}"的藏品记录...`);
6341
+ try {
6342
+ const searchRes = await api.searchObject(itemName, "");
6343
+ if (!searchRes || searchRes.success === false) {
6344
+ return `搜索物品失败: ${searchRes?.message || "未知错误"}`;
6345
+ }
6346
+ const items = searchRes.data?.keywords;
6347
+ if (!Array.isArray(items) || items.length === 0) {
6348
+ return `未找到名为"${itemName}"的物品,请检查名称是否正确`;
6349
+ }
6350
+ const targetItem = items[0];
6351
+ const objectId = String(targetItem.objectID);
6352
+ if (!objectId) {
6353
+ return "获取物品ID失败,无法查询记录";
6354
+ }
6355
+ const [recordRes, userDisplayInfo] = await Promise.all([
6356
+ api.getRedRecord(token, objectId),
6357
+ getUserDisplayInfo(api, token, userId, session.username || "用户")
6358
+ ]);
6359
+ if (!recordRes || recordRes.success === false) {
6360
+ return `获取藏品记录失败: ${recordRes?.message || "数据格式错误"}`;
6361
+ }
6362
+ const recordData = recordRes.data;
6363
+ const itemData = recordData?.itemData;
6364
+ if (!itemData || !itemData.list || itemData.list.length === 0) {
6365
+ return `物品"${targetItem.objectName}"暂无解锁记录`;
6366
+ }
6367
+ const sortedRecords = itemData.list.sort((a, b) => new Date(a.time).getTime() - new Date(b.time).getTime());
6368
+ const firstRecord = sortedRecords[0];
6369
+ const firstUnlockMapName = dataManager.getMapName(firstRecord.mapid);
6370
+ const firstUnlockMapBg = dataManager.getMapImagePath(firstUnlockMapName, "sol") || "";
6371
+ const latestRecords = sortedRecords.slice(-20).reverse();
6372
+ const records = latestRecords.map((record) => {
6373
+ const mapName = dataManager.getMapName(record.mapid);
6374
+ return {
6375
+ time: record.time,
6376
+ map: mapName,
6377
+ count: record.num || 1
6378
+ };
6379
+ });
6380
+ const itemImageUrl = `https://playerhub.df.qq.com/playerhub/60004/object/${objectId}.png`;
6381
+ const renderData = {
6382
+ userName: userDisplayInfo.userName,
6383
+ userRank: "未知段位",
6384
+ userRankImage: null,
6385
+ userAvatar: userDisplayInfo.userAvatar,
6386
+ userId,
6387
+ qqAvatarUrl: userDisplayInfo.qqAvatarUrl,
6388
+ itemName: targetItem.objectName,
6389
+ itemType: targetItem.objectType || (targetItem.grade ? `GRADE ${targetItem.grade}` : ""),
6390
+ itemImageUrl,
6391
+ firstUnlockTime: firstRecord.time,
6392
+ firstUnlockMap: firstUnlockMapName,
6393
+ firstUnlockMapBg,
6394
+ records,
6395
+ recordCount: itemData.total || records.length
6396
+ };
6397
+ const imageResult = await renderer.renderToMessage("redRecord", renderData, { width: 650 });
6398
+ return imageResult;
6399
+ } catch (error) {
6400
+ logger.error("指定藏品记录查询失败:", error);
6401
+ return `查询失败: ${error.message}`;
6402
+ }
6403
+ }
6404
+ __name(getRedByName, "getRedByName");
6405
+ async function getRedRecordList(api, dataManager, renderer, token, userId, session, logger) {
6406
+ await session.send("正在获取您的藏品解锁记录,请稍候...");
6407
+ try {
6408
+ const [userDisplayInfo, res] = await Promise.all([
6409
+ getUserDisplayInfo(api, token, userId, session.username || "用户"),
6410
+ api.getRedList(token)
6411
+ ]);
6412
+ if (!res || res.success === false) {
6413
+ return `获取藏品记录失败: ${res?.message || "数据格式错误"}`;
6414
+ }
6415
+ const data = res.data;
6416
+ const records = data?.records;
6417
+ if (!records?.list || records.list.length === 0) {
6418
+ return "您还没有任何藏品解锁记录";
6419
+ }
6420
+ const itemIds = records.list.map((item) => String(item.itemId));
6421
+ const [itemMap, priceMap] = await Promise.all([
6422
+ getItemNameMap(api, itemIds),
6423
+ getItemPriceMap(api, itemIds)
6424
+ ]);
6425
+ const itemStats = /* @__PURE__ */ new Map();
6426
+ records.list.forEach((record) => {
6427
+ const itemId = String(record.itemId);
6428
+ const itemName = itemMap.get(itemId) || `未知物品(${itemId})`;
6429
+ const itemPrice = priceMap.get(itemId) || 0;
6430
+ const num = record.num || 1;
6431
+ if (itemStats.has(itemId)) {
6432
+ const stat = itemStats.get(itemId);
6433
+ stat.count += num;
6434
+ stat.totalValue += itemPrice * num;
6435
+ } else {
6436
+ itemStats.set(itemId, {
6437
+ name: itemName,
6438
+ count: num,
6439
+ totalValue: itemPrice * num,
6440
+ imageUrl: `https://playerhub.df.qq.com/playerhub/60004/object/${itemId}.png`
6441
+ });
6442
+ }
6443
+ });
6444
+ const redGodCount = itemStats.size;
6445
+ let redTotalCount = 0;
6446
+ let redTotalValue = 0;
6447
+ itemStats.forEach((stat) => {
6448
+ redTotalCount += stat.count;
6449
+ redTotalValue += stat.totalValue;
6450
+ });
6451
+ const allSortedRecords = Array.from(itemStats.values()).sort((a, b) => b.totalValue - a.totalValue).map((item) => ({
6452
+ name: item.name,
6453
+ count: item.count,
6454
+ value: formatNumber2(item.totalValue),
6455
+ imageUrl: item.imageUrl
6456
+ }));
6457
+ const sortedRecords = allSortedRecords.slice(0, 6);
6458
+ let unlockedCount = 0;
6459
+ try {
6460
+ const allCollectionsRes = await api.getObjectList("props", "collection");
6461
+ if (allCollectionsRes?.data) {
6462
+ const keywords = allCollectionsRes.data?.keywords;
6463
+ if (keywords) {
6464
+ const allRedCollections = keywords.filter((item) => item.grade === 6);
6465
+ const collectedIds = new Set(itemIds);
6466
+ const uncollectedItems = allRedCollections.filter((item) => !collectedIds.has(String(item.objectID)));
6467
+ unlockedCount = uncollectedItems.length;
6468
+ }
6469
+ }
6470
+ } catch (error) {
6471
+ }
6472
+ const renderData = {
6473
+ userName: userDisplayInfo.userName,
6474
+ userRank: "未知段位",
6475
+ userRankImage: null,
6476
+ userAvatar: userDisplayInfo.userAvatar,
6477
+ userId,
6478
+ qqAvatarUrl: userDisplayInfo.qqAvatarUrl,
6479
+ statistics: {
6480
+ redGodCount: redGodCount.toString(),
6481
+ redTotalCount: redTotalCount.toString(),
6482
+ redTotalValue: formatNumber2(redTotalValue),
6483
+ unlockedCount: unlockedCount > 0 ? unlockedCount.toString() : ""
6484
+ },
6485
+ records: sortedRecords,
6486
+ totalRecords: allSortedRecords.length
6487
+ };
6488
+ const imageResult = await renderer.renderToMessage("redRecordList", renderData, { width: 650 });
6489
+ return imageResult;
6490
+ } catch (error) {
6491
+ logger.error("藏品记录列表查询失败:", error);
6492
+ return `查询失败: ${error.message}`;
6493
+ }
6494
+ }
6495
+ __name(getRedRecordList, "getRedRecordList");
6496
+ async function getItemNameMap(api, itemIds) {
6497
+ const itemMap = /* @__PURE__ */ new Map();
6498
+ const uniqueIds = [...new Set(itemIds)];
6499
+ try {
6500
+ const batchRes = await api.searchObject("", uniqueIds.join(","));
6501
+ if (batchRes?.success && batchRes?.data) {
6502
+ const keywords = batchRes.data?.keywords;
6503
+ if (keywords) {
6504
+ keywords.forEach((item) => {
6505
+ itemMap.set(String(item.objectID), item.objectName);
6506
+ });
6507
+ }
6508
+ }
6509
+ } catch (error) {
6510
+ }
6511
+ return itemMap;
6512
+ }
6513
+ __name(getItemNameMap, "getItemNameMap");
6514
+ async function getItemPriceMap(api, itemIds) {
6515
+ const priceMap = /* @__PURE__ */ new Map();
6516
+ const uniqueIds = [...new Set(itemIds)];
6517
+ try {
6518
+ const batchRes = await api.searchObject("", uniqueIds.join(","));
6519
+ if (batchRes?.success && batchRes?.data) {
6520
+ const keywords = batchRes.data?.keywords;
6521
+ if (keywords) {
6522
+ keywords.forEach((item) => {
6523
+ priceMap.set(String(item.objectID), item.avgPrice || 0);
6524
+ });
6525
+ }
6526
+ }
6527
+ } catch (error) {
6528
+ }
6529
+ return priceMap;
6530
+ }
6531
+ __name(getItemPriceMap, "getItemPriceMap");
6532
+
6533
+ // src/commands/info/redCollection.ts
6534
+ init_database();
6535
+ function formatNumber3(num) {
6536
+ if (typeof num !== "number" || isNaN(num)) {
6537
+ return "0";
6538
+ }
6539
+ if (num >= 1e9) {
6540
+ return (num / 1e9).toFixed(1).replace(/\.0$/, "") + "B";
6541
+ } else if (num >= 1e6) {
6542
+ return (num / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
6543
+ } else if (num >= 1e3) {
6544
+ return (num / 1e3).toFixed(1).replace(/\.0$/, "") + "K";
6545
+ } else {
6546
+ return num.toString();
6547
+ }
6548
+ }
6549
+ __name(formatNumber3, "formatNumber");
6550
+ function registerRedCollectionCommands(ctx, api, dataManager, renderer) {
6551
+ const logger = ctx.logger("delta-force");
6552
+ ctx.command("df.redcollection [season:number]", "查看大红收藏数据").alias("df.大红收藏").alias("df.大红藏品").alias("df.大红海报").alias("df.藏品海报").usage("参数说明:\n 数字 - 指定赛季(如 7)\n 不填 - 查询所有赛季\n示例: df.redcollection 7").action(async ({ session }, season) => {
6553
+ const userId = session.userId;
6554
+ const platform = session.platform;
6555
+ const token = await getActiveToken(ctx, userId, platform);
6556
+ if (!token) {
6557
+ return "您尚未登录,请先使用 df.login 登录";
6558
+ }
6559
+ let seasonId = "all";
6560
+ let seasonDisplay = "所有赛季";
6561
+ if (season !== void 0 && !isNaN(season)) {
6562
+ seasonId = season;
6563
+ seasonDisplay = `S${season}赛季`;
6564
+ }
6565
+ await session.send("正在获取大红收藏数据,请稍候...");
6566
+ try {
6567
+ const userDisplayInfo = await getUserDisplayInfo(api, token, userId, session.username || "用户");
6568
+ const [personalDataRes, titleRes] = await Promise.all([
6569
+ api.getPersonalData(token, "", seasonId),
6570
+ api.getTitle(token)
6571
+ ]);
6572
+ if (await handleApiError(personalDataRes, session)) return;
6573
+ if (await handleApiError(titleRes, session)) return;
6574
+ if (!personalDataRes.success || !personalDataRes.data) {
6575
+ return "获取个人数据失败:API返回数据格式异常";
6576
+ }
6577
+ if (!titleRes.success || !titleRes.data) {
6578
+ return "获取大红称号失败:API返回数据格式异常";
6579
+ }
6580
+ let solDetail = null;
6581
+ const allModesData = personalDataRes.data;
6582
+ const solData = allModesData?.sol;
6583
+ if (solData?.data) {
6584
+ const solInner = solData.data?.data;
6585
+ if (solInner?.solDetail) {
6586
+ solDetail = solInner.solDetail;
6587
+ }
6588
+ }
6589
+ if (!solDetail) {
6590
+ return "没有找到烽火地带游戏数据,请确保您已经在游戏中进行过烽火地带模式的对局。";
6591
+ }
6592
+ const titleData = titleRes.data;
6593
+ const title = titleData.title || "血色会计";
6594
+ const subtitle = titleData.subtitle || '"能把肾上腺素换算成子弹汇率的鬼才"';
6595
+ const unlockDesc = titleData.unlockDesc || "总价值突破800万且持有医疗/能源类大红收藏品";
6596
+ const redTotalMoney = solDetail.redTotalMoney || 0;
6597
+ const redTotalCount = solDetail.redTotalCount || 0;
6598
+ const redCollectionDetail = solDetail.redCollectionDetail || [];
6599
+ if (redCollectionDetail.length === 0) {
6600
+ return "您还没有任何大红收藏品,快去游戏中获取一些稀有收藏品吧!";
6601
+ }
6602
+ const uniqueObjectIds = new Set(redCollectionDetail.map((item) => item.objectID));
6603
+ const redGodCount = uniqueObjectIds.size;
6604
+ const sortedCollections = redCollectionDetail.sort((a, b) => (b.price || 0) - (a.price || 0)).slice(0, 6);
6605
+ const objectIds = sortedCollections.map((item) => String(item.objectID));
6606
+ const objectNames = {};
6607
+ if (objectIds.length > 0) {
6608
+ try {
6609
+ const searchRes = await api.searchObject("", objectIds.join(","));
6610
+ if (searchRes?.data) {
6611
+ const keywords = searchRes.data?.keywords;
6612
+ if (keywords) {
6613
+ keywords.forEach((obj) => {
6614
+ objectNames[String(obj.objectID)] = obj.objectName;
6615
+ });
6616
+ }
6617
+ }
6618
+ } catch (error) {
6619
+ logger.warn("获取物品名称失败,将使用物品ID显示:", error);
6620
+ }
6621
+ }
6622
+ const topCollections = sortedCollections.map((item, index) => ({
6623
+ rank: index + 1,
6624
+ name: objectNames[String(item.objectID)] || `物品${item.objectID}`,
6625
+ count: item.count || 1,
6626
+ value: formatNumber3(item.price || 0),
6627
+ imageUrl: `https://playerhub.df.qq.com/playerhub/60004/object/${item.objectID}.png`
6628
+ }));
6629
+ let unlockedCollections = [];
6630
+ let unlockedCount = 0;
6631
+ try {
6632
+ const allCollectionsRes = await api.getObjectList("props", "collection");
6633
+ if (allCollectionsRes?.data) {
6634
+ const keywords = allCollectionsRes.data?.keywords;
6635
+ if (keywords) {
6636
+ const allRedCollections = keywords.filter((item) => item.grade === 6);
6637
+ const collectedIds = new Set(redCollectionDetail.map((item) => item.objectID));
6638
+ const uncollectedItems = allRedCollections.filter((item) => !collectedIds.has(item.objectID));
6639
+ unlockedCount = uncollectedItems.length;
6640
+ if (uncollectedItems.length > 0) {
6641
+ const shuffled = uncollectedItems.sort(() => 0.5 - Math.random());
6642
+ unlockedCollections = shuffled.slice(0, 3).map((item) => ({
6643
+ name: item.objectName,
6644
+ objectID: item.objectID,
6645
+ price: formatNumber3(item.avgPrice || 0),
6646
+ imageUrl: `https://playerhub.df.qq.com/playerhub/60004/object/${item.objectID}.png`
6647
+ }));
6648
+ }
6649
+ }
6650
+ }
6651
+ } catch (error) {
6652
+ logger.warn("获取未解锁藏品失败:", error);
6653
+ unlockedCount = 74 - redGodCount;
6654
+ }
6655
+ let userRank = "未知段位";
6656
+ let userRankImage = null;
6657
+ try {
6658
+ const personalInfoRes = await api.getPersonalInfo(token);
6659
+ if (personalInfoRes?.data) {
6660
+ const careerData = personalInfoRes.data?.careerData;
6661
+ if (careerData?.rankpoint) {
6662
+ userRank = dataManager.getRankByScore(careerData.rankpoint, "sol");
6663
+ userRankImage = dataManager.getRankImage(careerData.rankpoint, "sol");
6664
+ }
6665
+ }
6666
+ } catch (error) {
6667
+ }
6668
+ const renderData = {
6669
+ userName: userDisplayInfo.userName,
6670
+ userRank,
6671
+ userRankImage,
6672
+ userAvatar: userDisplayInfo.userAvatar,
6673
+ userId,
6674
+ qqAvatarUrl: userDisplayInfo.qqAvatarUrl,
6675
+ title,
6676
+ subtitle,
6677
+ unlockDesc,
6678
+ seasonDisplay,
6679
+ statistics: {
6680
+ redGodCount: redGodCount.toString(),
6681
+ redTotalCount: redTotalCount.toString(),
6682
+ redTotalValue: formatNumber3(redTotalMoney),
6683
+ unlockedCount: unlockedCount.toString()
6684
+ },
6685
+ topCollections,
6686
+ unlockedCollections
6687
+ };
6688
+ const imageResult = await renderer.renderToMessage("redCollection", renderData, { width: 1145 });
6689
+ return imageResult;
6690
+ } catch (error) {
6691
+ logger.error("查询大红收藏失败:", error);
6692
+ return `查询大红收藏失败: ${error.message}`;
6693
+ }
6694
+ });
6695
+ }
6696
+ __name(registerRedCollectionCommands, "registerRedCollectionCommands");
6697
+
5675
6698
  // src/commands/tools/price.ts
5676
6699
  function registerPriceCommands(ctx, api) {
5677
6700
  const logger = ctx.logger("delta-force");
@@ -7334,18 +8357,539 @@ async function pollTaskStatus(taskId, api, logger) {
7334
8357
  }
7335
8358
  __name(pollTaskStatus, "pollTaskStatus");
7336
8359
 
7337
- // src/config.ts
8360
+ // src/commands/system/help.ts
7338
8361
  var import_koishi6 = require("koishi");
7339
- var Config = import_koishi6.Schema.object({
7340
- apiKey: import_koishi6.Schema.string().required().role("secret").description("API密钥,在 https://df.shallow.ink/api-keys 创建"),
7341
- clientID: import_koishi6.Schema.string().required().description("客户端ID,从管理页面获取"),
7342
- apiBaseUrl: import_koishi6.Schema.union([
7343
- import_koishi6.Schema.const("https://df-api.shallow.ink").description("默认 CDN(推荐)"),
7344
- import_koishi6.Schema.const("https://df-api-eo.shallow.ink").description("EdgeOne CDN"),
7345
- import_koishi6.Schema.const("https://df-api-esa.shallow.ink").description("ESA CDN"),
7346
- import_koishi6.Schema.string()
8362
+ var fs3 = __toESM(require("fs"));
8363
+ var path3 = __toESM(require("path"));
8364
+ var defaultHelpCfg = {
8365
+ title: "三角洲行动 帮助",
8366
+ subTitle: "DeltaForce-Plugin HELP",
8367
+ themeName: "default",
8368
+ colWidth: 420,
8369
+ colCount: 2,
8370
+ twoColumnLayout: true,
8371
+ bgBlur: true
8372
+ };
8373
+ var entertainmentHelpCfg = {
8374
+ title: "三角洲娱乐 帮助",
8375
+ subTitle: "DeltaForce-Plugin ENTERTAINMENT",
8376
+ themeName: "default",
8377
+ colWidth: 280,
8378
+ colCount: 3,
8379
+ twoColumnLayout: false,
8380
+ bgBlur: true
8381
+ };
8382
+ var entertainmentHelpList = [
8383
+ { group: "所有命令统一使用 ^ 前缀,例如 ^娱乐帮助" },
8384
+ {
8385
+ group: "语音播放",
8386
+ list: [
8387
+ { icon: 87, title: "^语音", desc: "随机播放语音" },
8388
+ { icon: 87, title: "^语音 [角色名/标签]", desc: "播放指定角色/标签语音" },
8389
+ { icon: 87, title: "^语音 [角色] [场景]", desc: "播放指定场景语音" },
8390
+ { icon: 87, title: "^语音 [角色] [场景] [动作]", desc: "播放指定动作语音" },
8391
+ { icon: 78, title: "^语音列表 | ^语音分类", desc: "查看可用角色/分类信息" },
8392
+ { icon: 79, title: "^标签列表 | ^语音统计", desc: "查看特殊标签/音频统计" }
8393
+ ]
8394
+ },
8395
+ {
8396
+ group: "鼠鼠音乐",
8397
+ list: [
8398
+ { icon: 87, title: "^鼠鼠音乐 [关键词]", desc: "随机播放/搜索播放音乐" },
8399
+ { icon: 88, title: "^鼠鼠音乐列表 [页码]", desc: "查看热度排行榜" },
8400
+ { icon: 98, title: "^鼠鼠语音", desc: "播放鼠鼠语音" },
8401
+ { icon: 89, title: "^鼠鼠歌单 [名称]", desc: "查看指定歌单" },
8402
+ { icon: 90, title: "^点歌 [序号]", desc: "播放列表中的歌曲" },
8403
+ { icon: 45, title: "^歌词", desc: "查看鼠鼠音乐歌词" },
8404
+ { icon: 78, title: "^音乐缓存统计", desc: "查看音乐缓存统计" },
8405
+ { icon: 78, title: "^清理音乐缓存", desc: "清空所有音乐缓存(仅主人)" }
8406
+ ]
8407
+ },
8408
+ {
8409
+ group: "TTS语音合成",
8410
+ list: [
8411
+ { icon: 87, title: "^tts [角色] [情感] 文本", desc: "合成并发送语音(角色、情感、文本用空格分隔)" },
8412
+ { icon: 87, title: "^tts 麦晓雯 开心 你好呀!", desc: "示例:使用指定角色和情感合成语音" },
8413
+ { icon: 78, title: "^tts状态", desc: "查看TTS服务状态和预设信息" },
8414
+ { icon: 78, title: "^tts角色列表 | ^tts预设列表", desc: "查看所有可用的角色预设列表" },
8415
+ { icon: 78, title: "^tts角色详情 [角色ID]", desc: "查看指定角色的详细信息" },
8416
+ { icon: 78, title: "^tts帮助", desc: "查看TTS功能详细使用说明" },
8417
+ { icon: 64, title: "^tts上传", desc: "上传上次合成的语音文件" }
8418
+ ]
8419
+ }
8420
+ ];
8421
+ var defaultHelpList = {
8422
+ fullWidth: [
8423
+ {
8424
+ order: 1,
8425
+ group: "所有命令统一使用 ^ 前缀,例如 ^帮助"
8426
+ },
8427
+ {
8428
+ order: 2,
8429
+ group: "此为基础菜单,其他功能请使用 ^娱乐帮助 查看娱乐菜单"
8430
+ }
8431
+ ],
8432
+ left: [
8433
+ {
8434
+ order: 1,
8435
+ group: "账号相关",
8436
+ list: [
8437
+ { icon: 80, title: "^账号", desc: "查看已绑定token列表" },
8438
+ { icon: 71, title: "^账号切换 [序号]", desc: "激活指定序号账号" },
8439
+ { icon: 86, title: "^绑定 [token]", desc: "绑定token" },
8440
+ { icon: 48, title: "^解绑 [序号]", desc: "解绑指定序号token" },
8441
+ { icon: 47, title: "^删除 [序号]", desc: "删除QQ/微信登录数据" },
8442
+ { icon: 49, title: "^(微信/QQ)刷新", desc: "刷新微信/QQ token" },
8443
+ { icon: 64, title: "^(QQ/微信)登陆", desc: "通过QQ/微信扫码登录" },
8444
+ { icon: 62, title: "^(WeGame/wegame微信)登陆", desc: "登录WeGame(QQ/微信扫码)" },
8445
+ { icon: 61, title: "^安全中心登陆", desc: "通过安全中心扫码登录" },
8446
+ { icon: 71, title: "^(QQ/微信)授权登陆 [code]", desc: "通过授权码登录" },
8447
+ { icon: 52, title: "^网页登陆", desc: "通过网页方式登录" },
8448
+ { icon: 80, title: "^ck登陆 [cookies]", desc: "通过cookie登录(^ck登陆查看帮助)" },
8449
+ { icon: 78, title: "^信息", desc: "查询个人详细信息" },
8450
+ { icon: 71, title: "^UID", desc: "查询个人UID" }
8451
+ ]
8452
+ },
8453
+ {
8454
+ order: 2,
8455
+ group: "游戏数据",
8456
+ list: [
8457
+ { icon: 41, title: "^藏品 [类型]", desc: "查询个人仓库中的皮肤、饰品等非货币资产(支持类型筛选:干员皮肤、喷漆、挂饰、典藏枪皮、枪皮、载具、头像、军牌)" },
8458
+ { icon: 48, title: "^货币", desc: "查询游戏内货币信息" },
8459
+ { icon: 55, title: "^数据 [模式] [赛季]", desc: "查询个人统计数据(支持模式和赛季)" },
8460
+ { icon: 66, title: "^战绩 [模式] [页码]", desc: "查询战绩(全面/烽火)" },
8461
+ { icon: 78, title: "^地图统计 [模式] [赛季/地图名]", desc: "查询地图统计数据(支持模式筛选、赛季查询、地图搜索)" },
8462
+ { icon: 53, title: "^流水 [类型/all] [页码/all]", desc: "查询交易流水(设备/道具/货币/all)" },
8463
+ { icon: 79, title: "^出红记录 [物品名]", desc: "查询藏品解锁记录(可指定物品)" },
8464
+ { icon: 42, title: "^昨日收益 [模式]", desc: "查询昨日收益和物资统计" }
8465
+ ]
8466
+ },
8467
+ {
8468
+ order: 3,
8469
+ group: "房间管理",
8470
+ list: [
8471
+ { icon: 28, title: "^房间列表", desc: "查询房间列表" },
8472
+ { icon: 23, title: "^创建房间", desc: "创建房间" },
8473
+ { icon: 26, title: "^加入房间 [房间号]", desc: "加入房间" },
8474
+ { icon: 37, title: "^退出房间 [房间号]", desc: "退出房间" },
8475
+ { icon: 56, title: "^踢人 [序号]", desc: "踢出房间成员" },
8476
+ { icon: 64, title: "^房间信息", desc: "查询当前房间信息" },
8477
+ { icon: 62, title: "^房间地图列表", desc: "查询房间地图列表" },
8478
+ { icon: 78, title: "^房间标签列表", desc: "查询房间标签列表" }
8479
+ ]
8480
+ },
8481
+ {
8482
+ order: 4,
8483
+ group: "价格/利润查询",
8484
+ list: [
8485
+ { icon: 61, title: "^价格历史 | ^当前价格 [物品名/ID]", desc: "查询物品历史/当前价格" },
8486
+ { icon: 61, title: "^材料价格 [物品ID]", desc: "查询制造材料最低价格" },
8487
+ { icon: 61, title: "^利润历史 [物品名/ID/场所]", desc: "查询制造利润历史记录" },
8488
+ { icon: 61, title: "^利润排行 [类型] [场所] [数量]", desc: "查询利润排行榜V1" },
8489
+ { icon: 61, title: "^最高利润 [类型] [场所] [物品ID]", desc: "查询最高利润排行榜V2" },
8490
+ { icon: 62, title: "^特勤处利润 [类型]", desc: "查询特勤处四个场所利润TOP3" }
8491
+ ]
8492
+ }
8493
+ ],
8494
+ right: [
8495
+ {
8496
+ order: 1,
8497
+ group: "战报与推送",
8498
+ list: [
8499
+ { icon: 86, title: "^日报 [模式]", desc: "查询日报数据(全面/烽火)" },
8500
+ { icon: 86, title: "^周报 [模式] [日期] [展示]", desc: "查询每周战报" },
8501
+ { icon: 46, title: "^每日密码", desc: "查询今日密码" },
8502
+ { icon: 86, title: "^开启/关闭日报推送", desc: "在本群开启/关闭日报推送" },
8503
+ { icon: 37, title: "^开启/关闭周报推送", desc: "在本群开启/关闭周报推送" },
8504
+ { icon: 86, title: "^开启/关闭每日密码推送", desc: "开启/关闭每日密码推送" },
8505
+ { icon: 86, title: "^开启/关闭特勤处推送", desc: "开启/关闭特勤处制造完成推送" },
8506
+ { icon: 86, title: "^订阅 战绩 [模式]", desc: "订阅战绩(sol/mp/both)" },
8507
+ { icon: 80, title: "^取消订阅 战绩", desc: "取消战绩订阅" },
8508
+ { icon: 78, title: "^订阅状态 战绩", desc: "查看订阅和推送状态" },
8509
+ { icon: 61, title: "^开启/关闭私信订阅推送 战绩 [筛选]", desc: "开启/关闭私信推送(可选筛选)" },
8510
+ { icon: 61, title: "^开启/关闭本群订阅推送 战绩 [筛选]", desc: "开启/关闭本群推送(可选筛选)" },
8511
+ { icon: 79, title: "筛选条件", desc: "百万撤离/百万战损/天才少年" }
8512
+ ]
8513
+ },
8514
+ {
8515
+ order: 2,
8516
+ group: "社区改枪码",
8517
+ list: [
8518
+ { icon: 86, title: "^改枪码上传 [改枪码] [描述] [模式] [是否公开] [配件信息]", desc: "上传改枪方案" },
8519
+ { icon: 86, title: "^改枪码列表 [武器名]", desc: "查询改枪方案列表" },
8520
+ { icon: 86, title: "^改枪码详情 [方案ID]", desc: "查询改枪方案详情" },
8521
+ { icon: 86, title: "^改枪码点赞 | ^改枪码点踩 [方案ID]", desc: "点赞/点踩改枪方案" },
8522
+ { icon: 86, title: "^改枪码收藏 | ^改枪码取消收藏 [方案ID]", desc: "收藏/取消收藏改枪方案" },
8523
+ { icon: 86, title: "^改枪码收藏列表", desc: "查看已收藏的改枪方案" },
8524
+ { icon: 86, title: "^改枪码更新 | ^改枪码删除 [方案ID] [参数]", desc: "更新/删除已上传的改枪方案" },
8525
+ { icon: 78, title: "网站上传修改", desc: "https://df.shallow.ink/solutions" }
8526
+ ]
8527
+ },
8528
+ {
8529
+ order: 3,
8530
+ group: "实用工具",
8531
+ list: [
8532
+ { icon: 61, title: "^ai锐评 [模式]", desc: "使用AI锐评烽火地带和全面战场数据" },
8533
+ { icon: 61, title: "^ai评价 [模式] [预设] [音色]", desc: "使用其他AI预设来评价烽火地带和全面战场数据,音色可选" },
8534
+ { icon: 78, title: "^ai预设列表", desc: "查看所有可用的AI评价预设" },
8535
+ { icon: 41, title: "^违规记录", desc: "登录QQ安全中心后可查询历史违规" },
8536
+ { icon: 48, title: "^特勤处状态", desc: "查询特勤处制造状态" },
8537
+ { icon: 71, title: "^特勤处信息 [场所]", desc: "查询特勤处设施升级信息" },
8538
+ { icon: 71, title: "^物品列表 [一级分类] [二级分类] [页码]", desc: "获取物品列表(默认props/collection)" },
8539
+ { icon: 86, title: "^物品搜索 [名称/ID]", desc: "搜索游戏内物品" },
8540
+ { icon: 48, title: "^大红收藏 [赛季数字]", desc: "生成大红收集海报(支持赛季)" },
8541
+ { icon: 40, title: "^文章列表 | ^文章详情 [ID]", desc: "查看文章列表/详情" },
8542
+ { icon: 71, title: "^健康状态", desc: "查询游戏健康状态信息" },
8543
+ { icon: 78, title: "^干员 [名称]", desc: "查询干员详细信息" },
8544
+ { icon: 78, title: "^干员列表", desc: "查询所有干员列表(按兵种分组)" }
8545
+ ]
8546
+ }
8547
+ ]
8548
+ };
8549
+ function generateTextHelp() {
8550
+ const lines = [
8551
+ "三角洲行动插件帮助",
8552
+ "使用 ^xxx 格式触发指令",
8553
+ "",
8554
+ "【账号相关】",
8555
+ "• ^登录 / ^QQ登录 / ^微信登录 / ^wegame登录",
8556
+ "• ^账号 - 账号列表",
8557
+ "• ^切换 <序号> - 切换账号",
8558
+ "• ^解绑 <序号> - 解绑账号",
8559
+ "",
8560
+ "【信息查询】",
8561
+ "• ^信息 - 查看个人信息",
8562
+ "• ^uid - 查看UID",
8563
+ "• ^货币 - 货币信息",
8564
+ "• ^藏品 [类型] - 个人藏品",
8565
+ "• ^数据 [模式] [赛季] - 个人数据",
8566
+ "• ^流水 [类型] [页码] - 交易流水",
8567
+ "• ^出红记录 [物品名] - 藏品解锁记录",
8568
+ "• ^大红收藏 [赛季] - 大红收藏海报",
8569
+ "",
8570
+ "【战报】",
8571
+ "• ^日报 [sol/mp] - 查看日报",
8572
+ "• ^周报 [sol/mp] - 查看周报",
8573
+ "• ^战绩 [sol/mp] [页码] - 查看战绩",
8574
+ "• ^地图统计 [模式] - 地图统计",
8575
+ "",
8576
+ "【工具】",
8577
+ "• ^每日密码 - 每日密码",
8578
+ "• ^物品搜索 <名称> - 搜索物品",
8579
+ "• ^价格 <名称> - 查询价格",
8580
+ "• ^干员 <名称> - 干员详情",
8581
+ "• ^特勤处 [设施] [等级] - 特勤处信息",
8582
+ "• ^AI锐评 [模式] - AI锐评战绩",
8583
+ "",
8584
+ "【娱乐】",
8585
+ "• ^语音 [角色] - 随机语音",
8586
+ "• ^tts <角色> <文本> - TTS合成",
8587
+ "• ^娱乐帮助 - 查看娱乐功能帮助",
8588
+ "",
8589
+ "提示: 请先使用 ^资源下载 下载资源以启用图片渲染"
8590
+ ];
8591
+ return lines.join("\n");
8592
+ }
8593
+ __name(generateTextHelp, "generateTextHelp");
8594
+ function generateTextEntertainmentHelp() {
8595
+ const lines = [
8596
+ "三角洲娱乐帮助",
8597
+ "",
8598
+ "【语音播放】",
8599
+ "• ^语音 - 随机播放语音",
8600
+ "• ^语音 [角色名/标签] - 播放指定角色语音",
8601
+ "• ^语音列表 - 查看可用角色列表",
8602
+ "• ^标签列表 - 查看特殊标签",
8603
+ "",
8604
+ "【TTS语音合成】",
8605
+ "• ^tts [角色] [情感] 文本 - 合成语音",
8606
+ "• ^tts角色列表 - 查看TTS角色预设",
8607
+ "• ^tts状态 - 查看TTS服务状态",
8608
+ "",
8609
+ "【鼠鼠音乐】",
8610
+ "• ^鼠鼠音乐 [关键词] - 播放音乐",
8611
+ "• ^鼠鼠音乐列表 - 查看热度排行榜",
8612
+ "",
8613
+ "提示: 请先使用 ^资源下载 下载资源以启用图片渲染"
8614
+ ];
8615
+ return lines.join("\n");
8616
+ }
8617
+ __name(generateTextEntertainmentHelp, "generateTextEntertainmentHelp");
8618
+ function isHelpResourcesReady(resourcesPath) {
8619
+ const helpTemplatePath = path3.join(resourcesPath, "help", "index.html");
8620
+ const helpImgsPath = path3.join(resourcesPath, "help", "imgs");
8621
+ return fs3.existsSync(helpTemplatePath) && fs3.existsSync(helpImgsPath);
8622
+ }
8623
+ __name(isHelpResourcesReady, "isHelpResourcesReady");
8624
+ function registerHelpCommands(ctx, renderer) {
8625
+ const logger = ctx.logger("delta-force");
8626
+ const downloadedResourcesPath = path3.join(ctx.baseDir, "data", "delta-force", "resources");
8627
+ const localResourcesPath = path3.resolve(__dirname, "../../resources");
8628
+ let resourcesPath;
8629
+ if (fs3.existsSync(path3.join(downloadedResourcesPath, "help", "index.html"))) {
8630
+ resourcesPath = downloadedResourcesPath;
8631
+ logger.debug("帮助命令使用下载的资源路径:", downloadedResourcesPath);
8632
+ } else {
8633
+ resourcesPath = localResourcesPath;
8634
+ logger.debug("帮助命令使用本地资源路径:", localResourcesPath);
8635
+ }
8636
+ function processGroup(group, isMaster) {
8637
+ if (group.masterOnly && !isMaster) {
8638
+ return null;
8639
+ }
8640
+ if (group.list && Array.isArray(group.list)) {
8641
+ group.list.forEach((help) => {
8642
+ const icon = (help.icon || 0) * 1;
8643
+ if (!icon) {
8644
+ help.css = "display:none";
8645
+ } else {
8646
+ const x = (icon - 1) % 10;
8647
+ const y = Math.floor((icon - x - 1) / 10);
8648
+ help.css = `background-position:-${x * 50}px -${y * 50}px`;
8649
+ }
8650
+ });
8651
+ }
8652
+ return group;
8653
+ }
8654
+ __name(processGroup, "processGroup");
8655
+ function processAndSortGroups(groups, isMaster) {
8656
+ const DEFAULT_ORDER = 999;
8657
+ return groups.map((group) => processGroup({ ...group }, isMaster)).filter((g) => g !== null).sort((a, b) => (a.order || DEFAULT_ORDER) - (b.order || DEFAULT_ORDER));
8658
+ }
8659
+ __name(processAndSortGroups, "processAndSortGroups");
8660
+ function getGroupOrder(group) {
8661
+ return group.order || 999;
8662
+ }
8663
+ __name(getGroupOrder, "getGroupOrder");
8664
+ function processHelpList(helpList, helpCfg, isMaster) {
8665
+ let leftGroups = [];
8666
+ let rightGroups = [];
8667
+ let topFullWidthGroups = [];
8668
+ let bottomFullWidthGroups = [];
8669
+ let helpGroup = [];
8670
+ const ORDER_THRESHOLD = 50;
8671
+ if (helpList && typeof helpList === "object" && !Array.isArray(helpList)) {
8672
+ const newList = helpList;
8673
+ if (newList.fullWidth && Array.isArray(newList.fullWidth)) {
8674
+ const sorted = processAndSortGroups(newList.fullWidth, isMaster);
8675
+ sorted.forEach((group) => {
8676
+ if (getGroupOrder(group) < ORDER_THRESHOLD) {
8677
+ topFullWidthGroups.push(group);
8678
+ } else {
8679
+ bottomFullWidthGroups.push(group);
8680
+ }
8681
+ });
8682
+ }
8683
+ if (newList.left && Array.isArray(newList.left)) {
8684
+ leftGroups.push(...processAndSortGroups(newList.left, isMaster));
8685
+ }
8686
+ if (newList.right && Array.isArray(newList.right)) {
8687
+ rightGroups.push(...processAndSortGroups(newList.right, isMaster));
8688
+ }
8689
+ helpGroup = [...topFullWidthGroups, ...bottomFullWidthGroups, ...leftGroups, ...rightGroups];
8690
+ } else if (Array.isArray(helpList)) {
8691
+ helpList.forEach((group) => {
8692
+ const processed = processGroup({ ...group }, isMaster);
8693
+ if (processed) {
8694
+ helpGroup.push(processed);
8695
+ }
8696
+ });
8697
+ if (helpCfg.twoColumnLayout) {
8698
+ const normalGroups = [];
8699
+ helpGroup.forEach((group) => {
8700
+ if (group.fullWidth) {
8701
+ if (getGroupOrder(group) < ORDER_THRESHOLD) {
8702
+ topFullWidthGroups.push(group);
8703
+ } else {
8704
+ bottomFullWidthGroups.push(group);
8705
+ }
8706
+ } else if (group.column === "left") {
8707
+ leftGroups.push(group);
8708
+ } else if (group.column === "right") {
8709
+ rightGroups.push(group);
8710
+ } else {
8711
+ normalGroups.push(group);
8712
+ }
8713
+ });
8714
+ if (normalGroups.length > 0) {
8715
+ const totalGroups = leftGroups.length + rightGroups.length + normalGroups.length;
8716
+ const targetLeftCount = Math.ceil(totalGroups / 2);
8717
+ normalGroups.forEach((group) => {
8718
+ if (leftGroups.length < targetLeftCount) {
8719
+ leftGroups.push(group);
8720
+ } else {
8721
+ rightGroups.push(group);
8722
+ }
8723
+ });
8724
+ }
8725
+ }
8726
+ }
8727
+ return { leftGroups, rightGroups, topFullWidthGroups, bottomFullWidthGroups, helpGroup };
8728
+ }
8729
+ __name(processHelpList, "processHelpList");
8730
+ async function getThemeData(helpCfg) {
8731
+ const colCount = Math.min(5, Math.max(parseInt(String(helpCfg.colCount)) || 3, 2));
8732
+ const colWidth = Math.min(600, Math.max(200, parseInt(String(helpCfg.colWidth)) || 350));
8733
+ const twoColumnLayout = helpCfg.twoColumnLayout === true;
8734
+ const themeName = helpCfg.themeName || "default";
8735
+ const sidePadding = 30;
8736
+ const columnGap = 20;
8737
+ let width;
8738
+ if (twoColumnLayout) {
8739
+ const tableWidth = colCount * colWidth;
8740
+ width = tableWidth * 2 + columnGap + sidePadding;
8741
+ } else {
8742
+ width = colCount * colWidth + sidePadding;
8743
+ }
8744
+ const themePath = themeName;
8745
+ const bgPath = path3.join(resourcesPath, "help", "imgs", themePath, "bg.jpg");
8746
+ const iconPath = path3.join(resourcesPath, "help", "imgs", themePath, "icon.png");
8747
+ let bgExists = false;
8748
+ let iconExists = false;
8749
+ try {
8750
+ await fs3.promises.access(bgPath);
8751
+ bgExists = true;
8752
+ } catch {
8753
+ logger.debug(`帮助背景图片不存在: ${bgPath}`);
8754
+ }
8755
+ try {
8756
+ await fs3.promises.access(iconPath);
8757
+ iconExists = true;
8758
+ } catch {
8759
+ logger.debug(`帮助图标图片不存在: ${iconPath}`);
8760
+ }
8761
+ const pathToFileUrl = /* @__PURE__ */ __name((filePath) => {
8762
+ if (!filePath) return "";
8763
+ const normalizedPath = path3.resolve(filePath).replace(/\\/g, "/");
8764
+ return `file:///${normalizedPath}`;
8765
+ }, "pathToFileUrl");
8766
+ const theme = {
8767
+ bg: bgExists ? pathToFileUrl(bgPath) : "",
8768
+ icon: iconExists ? pathToFileUrl(iconPath) : ""
8769
+ };
8770
+ const ret = [];
8771
+ const bodyFontFamily = "Microsoft YaHei, SimHei, Arial, sans-serif";
8772
+ const bodyBgImage = theme.bg ? `background-image:url("${theme.bg}");background-repeat:no-repeat;background-size:cover;` : "";
8773
+ ret.push(`body{width:${width}px;font-family:${bodyFontFamily};${bodyBgImage}}`);
8774
+ const containerBgImage = theme.bg ? `background-image:url("${theme.bg}");background-position:top left;background-repeat:no-repeat;background-size:100% auto;` : "";
8775
+ ret.push(`.container{width:${width}px;${containerBgImage}}`);
8776
+ const iconBgImage = theme.icon ? `background-image:url("${theme.icon}");background-size:500px auto;` : "";
8777
+ ret.push(`.help-icon{${iconBgImage}}`);
8778
+ ret.push(`.help-table .td,.help-table .th{width:${100 / colCount}%}`);
8779
+ if (twoColumnLayout) {
8780
+ ret.push(`
8781
+ .help-content-wrapper{display:flex;gap:${columnGap}px;width:100%;}
8782
+ .help-column{flex:1;min-width:0;}
8783
+ .help-column .cont-box{width:100%;}
8784
+ `);
8785
+ }
8786
+ ret.push(".head-box .title{font-size:50px}");
8787
+ ret.push(".help-group{font-size:18px}");
8788
+ ret.push(".help-title{font-size:16px}");
8789
+ ret.push(".help-desc{font-size:13px}");
8790
+ ret.push(".help-table .td,.help-table .th{font-size:14px}");
8791
+ ret.push(".help-title,.help-group{color:#ceb78b}");
8792
+ ret.push(".help-desc{color:#eee}");
8793
+ ret.push(".cont-box{background:rgba(43, 52, 61, 0.8)}");
8794
+ ret.push(`.cont-box{backdrop-filter:${helpCfg.bgBlur !== false ? "blur(3px)" : "none"}}`);
8795
+ ret.push(".help-group{background:rgba(34, 41, 51, .4)}");
8796
+ ret.push(".help-table .tr:nth-child(odd){background:rgba(34, 41, 51, .2)}");
8797
+ ret.push(".help-table .tr:nth-child(even){background:rgba(34, 41, 51, .4)}");
8798
+ return {
8799
+ style: `<style>${ret.join("\n")}</style>`,
8800
+ colCount,
8801
+ width
8802
+ };
8803
+ }
8804
+ __name(getThemeData, "getThemeData");
8805
+ ctx.command("df.help", "查看三角洲行动帮助").alias("df.帮助").alias("df.菜单").action(async ({ session }) => {
8806
+ const isMaster = false;
8807
+ if (!isHelpResourcesReady(resourcesPath)) {
8808
+ logger.debug("帮助资源未下载,使用文字版");
8809
+ return generateTextHelp();
8810
+ }
8811
+ try {
8812
+ const helpCfg = defaultHelpCfg;
8813
+ const helpList = defaultHelpList;
8814
+ const { leftGroups, rightGroups, topFullWidthGroups, bottomFullWidthGroups, helpGroup } = processHelpList(helpList, helpCfg, isMaster);
8815
+ const themeData = await getThemeData(helpCfg);
8816
+ const templateData = {
8817
+ helpCfg,
8818
+ helpGroup,
8819
+ leftGroups,
8820
+ rightGroups,
8821
+ topFullWidthGroups,
8822
+ bottomFullWidthGroups,
8823
+ ...themeData,
8824
+ colCount: themeData.colCount,
8825
+ themePath: helpCfg.themeName || "default",
8826
+ element: "default",
8827
+ bgType: 1
8828
+ };
8829
+ const imageResult = await renderer.render("help", templateData, { width: themeData.width });
8830
+ if (imageResult.success && imageResult.image) {
8831
+ return import_koishi6.h.image(imageResult.image, "image/png");
8832
+ }
8833
+ return imageResult.error || "渲染帮助菜单失败";
8834
+ } catch (error) {
8835
+ logger.error("生成帮助菜单失败:", error);
8836
+ return `生成帮助菜单失败: ${error.message}`;
8837
+ }
8838
+ });
8839
+ ctx.command("df.entertainment", "查看娱乐功能帮助").alias("df.娱乐帮助").alias("df.娱乐菜单").action(async ({ session }) => {
8840
+ const isMaster = false;
8841
+ if (!isHelpResourcesReady(resourcesPath)) {
8842
+ logger.debug("帮助资源未下载,使用文字版");
8843
+ return generateTextEntertainmentHelp();
8844
+ }
8845
+ try {
8846
+ const helpCfg = entertainmentHelpCfg;
8847
+ const helpGroup = [];
8848
+ entertainmentHelpList.forEach((group) => {
8849
+ const processed = processGroup({ ...group }, isMaster);
8850
+ if (processed) {
8851
+ helpGroup.push(processed);
8852
+ }
8853
+ });
8854
+ const themeData = await getThemeData(helpCfg);
8855
+ const templateData = {
8856
+ helpCfg,
8857
+ helpGroup,
8858
+ leftGroups: [],
8859
+ rightGroups: [],
8860
+ topFullWidthGroups: [],
8861
+ bottomFullWidthGroups: [],
8862
+ ...themeData,
8863
+ colCount: themeData.colCount,
8864
+ themePath: helpCfg.themeName || "default",
8865
+ element: "default",
8866
+ bgType: 1
8867
+ };
8868
+ const imageResult = await renderer.render("help", templateData, { width: themeData.width });
8869
+ if (imageResult.success && imageResult.image) {
8870
+ return import_koishi6.h.image(imageResult.image, "image/png");
8871
+ }
8872
+ return imageResult.error || "渲染娱乐帮助菜单失败";
8873
+ } catch (error) {
8874
+ logger.error("生成娱乐帮助菜单失败:", error);
8875
+ return `生成娱乐帮助菜单失败: ${error.message}`;
8876
+ }
8877
+ });
8878
+ }
8879
+ __name(registerHelpCommands, "registerHelpCommands");
8880
+
8881
+ // src/config.ts
8882
+ var import_koishi7 = require("koishi");
8883
+ var Config = import_koishi7.Schema.object({
8884
+ apiKey: import_koishi7.Schema.string().required().role("secret").description("API密钥,在 https://df.shallow.ink/api-keys 创建"),
8885
+ clientID: import_koishi7.Schema.string().required().description("客户端ID,从管理页面获取"),
8886
+ apiBaseUrl: import_koishi7.Schema.union([
8887
+ import_koishi7.Schema.const("https://df-api.shallow.ink").description("默认 CDN(推荐)"),
8888
+ import_koishi7.Schema.const("https://df-api-eo.shallow.ink").description("EdgeOne CDN"),
8889
+ import_koishi7.Schema.const("https://df-api-esa.shallow.ink").description("ESA CDN"),
8890
+ import_koishi7.Schema.string()
7347
8891
  ]).default("https://df-api.shallow.ink").description("API 基础地址"),
7348
- useGhProxy: import_koishi6.Schema.boolean().default(false).description("使用 GH-Proxy 加速下载(国内用户推荐开启)")
8892
+ useGhProxy: import_koishi7.Schema.boolean().default(false).description("使用 GH-Proxy 加速下载(国内用户推荐开启)")
7349
8893
  });
7350
8894
 
7351
8895
  // src/index.ts
@@ -7371,72 +8915,6 @@ function apply(ctx, config) {
7371
8915
  logger.warn("国内用户推荐开启 useGhProxy 配置项以加速下载");
7372
8916
  }
7373
8917
  ctx.command("df", "三角洲行动").alias("三角洲");
7374
- ctx.command("df.help", "查看帮助").action(async () => {
7375
- return `三角洲行动插件
7376
- 使用 ^xxx 格式触发指令
7377
-
7378
- 【账号相关】
7379
- • ^登录 / ^QQ登录 / ^微信登录 / ^wegame登录
7380
- • ^账号 - 账号列表
7381
- • ^切换 <序号> - 切换账号
7382
- • ^解绑 <序号> - 解绑账号
7383
-
7384
- 【信息查询】
7385
- • ^信息 - 查看个人信息
7386
- • ^uid - 查看UID
7387
- • ^货币 - 货币信息
7388
- • ^流水 [类型] [页码] - 交易流水
7389
- • ^藏品 [类型] - 个人藏品
7390
- • ^日报 [sol/mp] - 查看日报
7391
- • ^周报 [sol/mp] - 查看周报
7392
- • ^战绩 [sol/mp] [页码] - 查看战绩
7393
- • ^地图统计 [模式] - 地图统计
7394
- • ^特勤处 [设施] [等级] - 特勤处信息
7395
- • ^封号记录 - 违规记录(需qqsafe)
7396
- • ^干员列表 - 干员列表
7397
- • ^干员 <名称> - 干员详情
7398
- • ^服务器状态 - API服务器状态
7399
-
7400
- 【物品价格】
7401
- • ^物品搜索 <名称> - 搜索物品
7402
- • ^价格 <名称> - 查询价格
7403
- • ^价格历史 <名称> - 价格历史
7404
- • ^利润排行 - 利润排行
7405
- • ^特勤利润 - 特勤处利润
7406
- • ^材料价格 - 材料价格
7407
-
7408
- 【AI功能】
7409
- • ^AI锐评 [模式] - AI锐评战绩
7410
- • ^AI预设列表 - 查看预设列表
7411
-
7412
- 【工具】
7413
- • ^每日密码 - 每日密码
7414
- • ^帮助 - 查看帮助
7415
-
7416
- 【改枪方案】
7417
- • ^改枪码列表 [武器名] - 方案列表
7418
- • ^改枪码详情 <ID> - 方案详情
7419
- • ^上传改枪码 <码> - 上传方案
7420
- • ^改枪码收藏 - 我的收藏
7421
-
7422
- 【开黑房间】
7423
- • ^房间列表 [模式] - 房间列表
7424
- • ^创建房间 <模式> - 创建房间
7425
- • ^加入房间 <ID> [密码] - 加入房间
7426
- • ^房间信息 - 当前房间信息
7427
-
7428
- 【语音功能】
7429
- • ^语音 [角色] [场景] - 随机语音
7430
- • ^语音列表 - 角色列表
7431
- • ^标签列表 - 特殊标签
7432
- • ^tts <角色> [情感] <文本> - TTS合成
7433
- • ^tts角色列表 - TTS角色
7434
-
7435
- 【资源管理】
7436
- • ^资源状态 - 查看资源状态
7437
- • ^资源下载 / ^资源更新 [github/gitee] - 下载/更新资源
7438
- • ^资源清理 - 清理资源目录`;
7439
- });
7440
8918
  registerLoginCommands(ctx, config, api);
7441
8919
  registerInfoCommands(ctx, api, dataManager, renderer);
7442
8920
  registerDailyCommands(ctx, api, dataManager, renderer);
@@ -7449,17 +8927,22 @@ function apply(ctx, config) {
7449
8927
  registerObjectCommands(ctx, api);
7450
8928
  registerPriceCommands(ctx, api);
7451
8929
  registerMoneyCommands(ctx, api);
7452
- registerFlowsCommands(ctx, api);
7453
- registerCollectionCommands(ctx, api);
7454
- registerPlaceCommands(ctx, api);
8930
+ registerFlowsCommands(ctx, api, renderer);
8931
+ registerCollectionCommands(ctx, api, renderer);
8932
+ registerPlaceCommands(ctx, api, renderer);
7455
8933
  registerBanCommands(ctx, api);
7456
- registerOperatorCommands(ctx, api, dataManager);
8934
+ registerOperatorCommands(ctx, api, dataManager, renderer);
7457
8935
  registerHealthCommands(ctx, api);
8936
+ registerPersonalDataCommands(ctx, api, dataManager, renderer);
8937
+ registerStatsCommands(ctx, config, api);
8938
+ registerRedRecordCommands(ctx, api, dataManager, renderer);
8939
+ registerRedCollectionCommands(ctx, api, dataManager, renderer);
7458
8940
  registerSolutionCommands(ctx, config, api);
7459
8941
  registerRoomCommands(ctx, config, api, dataManager);
7460
8942
  registerVoiceCommands(ctx, api, dataManager);
7461
8943
  registerTtsCommands(ctx, api);
7462
8944
  registerResourcesCommands(ctx, config, resourceManager);
8945
+ registerHelpCommands(ctx, renderer);
7463
8946
  logger.info("三角洲行动插件加载完成");
7464
8947
  }
7465
8948
  __name(apply, "apply");