diff --git a/CHANGELOG.md b/CHANGELOG.md index d8c94d6..220d610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ ## 更新记录 +### version: 3.4.5(2026-09-05): + +新增 `跨服名剑 服务器 [模式]` 指令,接入 JX3API 跨服名剑榜;竞技模式支持 `0=2v2`、`1=3v3`、`2=5v5`,默认为 `1`。榜单按接口数组顺序生成排名,使用统一数据表格模板展示队伍名称、积分、胜场、败场和总场数,并同步更新功能帮助与 README。 + +新增 `武林争霸 服务器 [阵营]` 指令,接入 JX3API 武林争霸赛帮会榜;阵营支持 `1=浩气`、`2=恶人`,默认为 `1`。榜单按接口数组顺序生成排名,使用统一数据表格模板展示帮会、帮主、所属服务器、阵营、胜负场、积分和最后比赛时间。 + +新增 `捕快荣誉 服务器` 和 `江湖浪客 服务器` 指令,分别接入 JX3API `/rank/constable` 与 `/rank/outlaw`。两个榜单复用同一数据处理流程和统一表格模板,按接口数组顺序生成排名,展示角色、帮会、门派、阵营和抓捕数;江湖浪客榜额外展示敌对数。 + +新增 `决斗挑战 服务器 [模式]` 指令,接入 JX3API `/rank/wanted`;模式支持 `1=公开`、`2=私密`,默认为 `1`。榜单按接口数组顺序生成排名,展示被悬赏角色、门派、赏金、剩余时间和发起人,并将 `timeOut` 结束时间戳转换为动态剩余时长。 + +统一图片列表中的阵营列:浩气盟、恶人谷和中立改为显示 `templates/img` 内对应图标,不再显示阵营文本;空值或未识别阵营使用“未知”图标兜底。 + ### version: 3.4.4(2026-09-05): 烟花查询在角色名称后新增可选的“条数”参数,默认展示前 50 条,支持指定正整数条数;沿用已绑定会话省略服务器的用法,并同步更新功能帮助和 README。上游接口未提供条数参数,查询结果在本地截取后生成图片。 diff --git a/README.md b/README.md index 1e22ae4..f79380e 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,11 @@ JX3API Token 可从 [JX3API](https://www.jx3api.com/) 获取。推栏 Ticket 通 | `战绩 服务器 角色 [模式]` | 角色名剑战绩,模式可选 `22`、`33`、`55`,默认 `33`;图片 | Token + Ticket | | `名剑排行 [模式] [数量]` | 名剑大会排行,默认 `33`、50 条;图片 | Token + Ticket | | `名剑统计 [模式]` | 名剑门派统计,模式默认 `33`;图片 | Token + Ticket | +| `跨服名剑 服务器 [模式]` | 跨服名剑队伍榜单,模式可选 `0`(2v2)、`1`(3v3)、`2`(5v5),默认 `1`;图片 | Token(LV.2) | +| `武林争霸 服务器 [阵营]` | 武林争霸赛帮会榜,阵营可选 `1`(浩气)、`2`(恶人),默认 `1`;图片 | Token(LV.2) | +| `捕快荣誉 服务器` | 捕快荣誉榜,展示角色、帮会、门派、阵营和抓捕数;图片 | Token(LV.2) | +| `江湖浪客 服务器` | 江湖浪客榜,展示角色、帮会、门派、阵营、抓捕数和敌对数;图片 | Token(LV.2) | +| `决斗挑战 服务器 [模式]` | 决斗挑战悬赏榜,模式可选 `1`(公开)、`2`(私密),默认 `1`;图片 | Token(LV.2) | | `帮会排行 服务器` | 回复序号选择神兵宝甲或爱心帮会榜单,最多保留前 50 条;图片 | Token | | `阵营排行 服务器` | 回复序号选择赛季、上周或本周阵营榜单,最多保留前 50 条;图片 | Token | | `其他排行 服务器` | 回复序号选择名士、老江湖、名师等其他榜单,最多保留前 50 条;图片 | Token | @@ -522,7 +527,7 @@ git diff --check ## 当前版本状态 -以下内容是对 v3.4.4 当前源码的静态核对结果,部署和二次开发前应注意: +以下内容是对 v3.4.5 当前源码的静态核对结果,部署和二次开发前应注意: 1. 查询图片使用浅色高对比主题和放大的内容区域;渲染清晰度、JPEG/PNG 格式及 JPEG 质量由 `image_render_quality` 配置组控制,提高清晰度或使用 PNG 会增加图片体积与渲染耗时。 2. `帮会排行`、`阵营排行`、`其他排行` 使用固定的“数字序号 → 榜单名称”首轮菜单,次轮查询统一截取接口返回的前 50 条;取得有效选择后会先停止超时计时器,避免渲染期间重复输出。 diff --git a/_conf_schema.json b/_conf_schema.json index 74f22e8..b8a852c 100644 --- a/_conf_schema.json +++ b/_conf_schema.json @@ -53,7 +53,7 @@ "1.3", "1.8" ], - "default": "1.3", + "default": "1.0", "hint": "数值越高越清晰,同时图片更大、渲染耗时更多。" }, "format": { diff --git a/core/jx3api_data.py b/core/jx3api_data.py index 4bf0cb6..9152eec 100644 --- a/core/jx3api_data.py +++ b/core/jx3api_data.py @@ -560,6 +560,162 @@ class JX3APIService: template="mingjiantongji.html" ) + async def kuafumingjian(self, server: str, mode: int = 1) -> Dict[str, Any]: + """跨服名剑榜。""" + mode_names = {0: "2v2", 1: "3v3", 2: "5v5"} + + async def processor(data: Any, return_data: Dict[str, Any]) -> None: + items = data.get("data") or [] + if not isinstance(items, list): + items = [] + + return_data["data"] = { + "items": [item for item in items if isinstance(item, dict)], + "name": data.get("name") or "跨服名剑榜", + "server": data.get("server") or server or "全区", + "mode_name": mode_names[mode], + "update_time": format_time(data.get("time")), + } + + return await self._request_api( + path="/rank/arena", + params={"server": server, "mode": mode, "token": self.token}, + processor=processor, + template="kuafumingjian.html", + ) + + async def wulinzhengba(self, server: str, camp: int = 1) -> Dict[str, Any]: + """武林争霸赛帮会榜。""" + camp_names = {1: "浩气盟", 2: "恶人谷"} + + def format_match_time(value: Any) -> str: + try: + total_seconds = int(value) + except (TypeError, ValueError): + return "" + if total_seconds < 0: + return "" + + hours, remainder = divmod(total_seconds, 3600) + minutes, seconds = divmod(remainder, 60) + if hours: + return f"{hours}时{minutes:02d}分{seconds:02d}秒" + return f"{minutes}分{seconds:02d}秒" + + async def processor(data: Any, return_data: Dict[str, Any]) -> None: + items = data.get("data") or [] + if not isinstance(items, list): + items = [] + + rank_items = [] + for item in items: + if not isinstance(item, dict): + continue + rank_item = item.copy() + rank_item["lastMatchTimeText"] = format_match_time( + item.get("lastMatchTime") + ) + rank_items.append(rank_item) + + return_data["data"] = { + "items": rank_items, + "name": data.get("name") or "武林争霸赛", + "server": data.get("server") or server or "全区", + "camp_name": camp_names[camp], + "update_time": format_time(data.get("time")), + } + + return await self._request_api( + path="/rank/championship", + params={"server": server, "camp": camp, "token": self.token}, + processor=processor, + template="wulinzhengba.html", + ) + + async def _bounty_rank(self,server: str,path: str,fallback_name: str,show_hostile_count: bool,) -> Dict[str, Any]: + """处理捕快荣誉和江湖浪客共用的榜单结构。""" + + async def processor(data: Any, return_data: Dict[str, Any]) -> None: + items = data.get("data") or [] + if not isinstance(items, list): + items = [] + + return_data["data"] = { + "items": [item for item in items if isinstance(item, dict)], + "name": data.get("name") or fallback_name, + "server": data.get("server") or server or "全区", + "show_hostile_count": show_hostile_count, + "update_time": format_time(data.get("time")), + } + + return await self._request_api( + path=path, + params={"server": server, "token": self.token}, + processor=processor, + template="bounty_rank.html", + ) + + async def bukairongyu(self, server: str) -> Dict[str, Any]: + """捕快荣誉榜。""" + return await self._bounty_rank( + server=server, + path="/rank/constable", + fallback_name="捕快荣誉榜", + show_hostile_count=False, + ) + + async def jianghulangke(self, server: str) -> Dict[str, Any]: + """江湖浪客榜。""" + return await self._bounty_rank( + server=server, + path="/rank/outlaw", + fallback_name="江湖浪客榜", + show_hostile_count=True, + ) + + async def juedoutiaozhan(self,server: str,mode: int = 1,) -> Dict[str, Any]: + """决斗挑战榜。""" + mode_names = {1: "公开", 2: "私密"} + + async def processor(data: Any, return_data: Dict[str, Any]) -> None: + items = data.get("data") or [] + if not isinstance(items, list): + items = [] + + now_timestamp = int(datetime.now().timestamp()) + rank_items = [] + for item in items: + if not isinstance(item, dict): + continue + rank_item = item.copy() + try: + timeout = int(item.get("timeOut")) + except (TypeError, ValueError): + remaining_time = "" + else: + remaining_time = ( + format_remaining(timeout) + if timeout > now_timestamp + else "已结束" + ) + rank_item["remainingTime"] = remaining_time + rank_items.append(rank_item) + + return_data["data"] = { + "items": rank_items, + "name": data.get("name") or "决斗挑战榜", + "server": data.get("server") or server or "全区", + "mode_name": mode_names[mode], + "update_time": format_time(data.get("time")), + } + + return await self._request_api( + path="/rank/wanted", + params={"server": server, "mode": mode, "token": self.token}, + processor=processor, + template="juedoutiaozhan.html", + ) + async def banghui_rank_menu(self) -> Dict[str, str]: """帮会排行榜选择内容。""" diff --git a/core/message.py b/core/message.py index de0cc6f..5cce9ef 100644 --- a/core/message.py +++ b/core/message.py @@ -349,6 +349,47 @@ class MessageBuilder: """ 名剑统计 模式""" return await self.T2I_image_msg(event, lambda: self.jx3api.mingjiantongji(mode)) + async def kuafumingjian(self, event: AstrMessageEvent, server: str, mode: int = 1,): + """跨服名剑 服务器 [模式]。""" + if mode not in {0, 1, 2}: + return event.plain_result("竞技模式仅支持 0=2v2、1=3v3、2=5v5") + return await self.T2I_image_msg( + event, + lambda: self.jx3api.kuafumingjian(server, mode), + ) + + async def wulinzhengba(self,event: AstrMessageEvent,server: str,camp: int = 1,): + """武林争霸 服务器 [阵营]。""" + if camp not in {1, 2}: + return event.plain_result("阵营仅支持 1=浩气、2=恶人") + return await self.T2I_image_msg( + event, + lambda: self.jx3api.wulinzhengba(server, camp), + ) + + async def bukairongyu(self, event: AstrMessageEvent, server: str): + """捕快荣誉 服务器。""" + return await self.T2I_image_msg( + event, + lambda: self.jx3api.bukairongyu(server), + ) + + async def jianghulangke(self, event: AstrMessageEvent, server: str): + """江湖浪客 服务器。""" + return await self.T2I_image_msg( + event, + lambda: self.jx3api.jianghulangke(server), + ) + + async def juedoutiaozhan(self,event: AstrMessageEvent,server: str,mode: int = 1,): + """决斗挑战 服务器 [模式]。""" + if mode not in {1, 2}: + return event.plain_result("模式仅支持 1=公开、2=私密") + return await self.T2I_image_msg( + event, + lambda: self.jx3api.juedoutiaozhan(server, mode), + ) + async def banghuipaihang(self, event: AstrMessageEvent, server: str): """帮会排行 服务器。""" return await self.handler_plain_image_msg( diff --git a/main.py b/main.py index 4073b23..e0a8e4b 100644 --- a/main.py +++ b/main.py @@ -25,7 +25,7 @@ PLUGIN_NAME = "astrbot_plugin_jx3" @register("astrbot_plugin_jx3", "fxdyz", "聚合剑网三游戏数据,提供查询、图片渲染、本地避雷和实时事件推送。", - "3.4.4", + "3.4.5", "https://github.com/qsc20001102/astrbot_plugin_jx3" ) class Jx3ApiPlugin(Star): @@ -254,6 +254,11 @@ class Jx3ApiPlugin(Star): "战绩": self. jx3cmd.zhanji, "名剑排行": self. jx3cmd.mingjianpaihang, "名剑统计": self. jx3cmd.mingjiantongji, + "跨服名剑": self.jx3cmd.kuafumingjian, + "武林争霸": self.jx3cmd.wulinzhengba, + "捕快荣誉": self.jx3cmd.bukairongyu, + "江湖浪客": self.jx3cmd.jianghulangke, + "决斗挑战": self.jx3cmd.juedoutiaozhan, "帮会排行": self. jx3cmd.banghuipaihang, "阵营排行": self. jx3cmd.zhenyingpaihang, "其他排行": self. jx3cmd.qitapaihang, diff --git a/metadata.yaml b/metadata.yaml index 760b859..fbf9326 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -1,7 +1,7 @@ name: astrbot_plugin_jx3 display_name: 剑网三游戏数据查询工具 desc: 聚合剑网三游戏数据,提供查询、图片渲染、本地避雷和实时事件推送。 -version: 3.4.4 +version: 3.4.5 author: 飞翔大野猪 repo: https://github.com/qsc20001102/astrbot_plugin_jx3 astrbot_version: ">=4.24.1" diff --git a/templates/pages/bounty_rank.html b/templates/pages/bounty_rank.html new file mode 100644 index 0000000..9361d1c --- /dev/null +++ b/templates/pages/bounty_rank.html @@ -0,0 +1,46 @@ +{# template-title: 悬赏排行榜 #} +{# template-components: data-table #} + + +
+

{{ name }}

+
{{ server }}{% if update_time %} · 更新时间:{{ update_time }}{% endif %}
+ + + + + + + + + + + {% if show_hostile_count %}{% endif %} + + + + + {% for item in items %} + + + + + + {% set camp_name = item.campName or '未知' %} + + + {% if show_hostile_count %}{% endif %} + + {% endfor %} + +
排名名称帮会门派阵营抓捕数敌对数
{{ loop.index }}{{ item.name }}{{ item.tongName }} +
+ {% if icons.sect.get(item.forceName) %} + {{ item.forceName }} + {% endif %} + {{ item.forceName }} +
+
+ {{ camp_name }} + {{ item.score }}{{ item.customValue0 }}
+
diff --git a/templates/pages/dilujilu.html b/templates/pages/dilujilu.html index 681f15b..ea1e188 100644 --- a/templates/pages/dilujilu.html +++ b/templates/pages/dilujilu.html @@ -33,11 +33,17 @@ {{ m.refreshTime }} {{ m.captureTime }} {{ m.captureRoleName }} - {{ m.captureCampName }} + {% set capture_camp_name = m.captureCampName or '未知' %} + + {{ capture_camp_name }} + {{ m.auctionTime }} {{ m.auctionAmount }} {{ m.auctionRoleName }} - {{ m.auctionCampName }} + {% set auction_camp_name = m.auctionCampName or '未知' %} + + {{ auction_camp_name }} + {% endfor %} diff --git a/templates/pages/guanaishouling.html b/templates/pages/guanaishouling.html index 0f9145f..a944e94 100644 --- a/templates/pages/guanaishouling.html +++ b/templates/pages/guanaishouling.html @@ -30,7 +30,10 @@ class="camp-haoqi" {% endif %} > - {{ item.camp_name }} + {% set camp_name = item.camp_name or '未知' %} + + {{ camp_name }} + {{ item.castle }} {{ item.str_status }} {{ item.start_time }} diff --git a/templates/pages/helps.html b/templates/pages/helps.html index 167444d..dc7aec1 100644 --- a/templates/pages/helps.html +++ b/templates/pages/helps.html @@ -4,7 +4,7 @@
剑网三综合查询插件
-
18 个功能分区 · 92 个触发指令
+
18 个功能分区 · 97 个触发指令
参数之间使用空格分隔 @@ -30,17 +30,21 @@
-
02

名剑相关

3 个触发词
+
02

名剑相关

4 个触发词
名剑战绩
战绩 服务器 角色 [模式]
名剑排行
名剑排行 [模式] [数量]
名剑统计
名剑统计 [模式]
+
跨服名剑
跨服名剑 服务器 [模式]
-
03

排行榜单

4 个触发词
+
03

排行榜单

8 个触发词
+
武林争霸
武林争霸 服务器 [阵营]
+
悬赏排行
捕快荣誉 服务器江湖浪客 服务器
+
决斗挑战
决斗挑战 服务器 [模式]
分类排行
帮会排行 服务器阵营排行 服务器其他排行 服务器
试炼排行
试炼排行 服务器 心法
diff --git a/templates/pages/juedoutiaozhan.html b/templates/pages/juedoutiaozhan.html new file mode 100644 index 0000000..fde36d1 --- /dev/null +++ b/templates/pages/juedoutiaozhan.html @@ -0,0 +1,41 @@ +{# template-title: 决斗挑战榜 #} +{# template-components: data-table #} + + +
+

{{ name }}

+
{{ server }} · {{ mode_name }}{% if update_time %} · 更新时间:{{ update_time }}{% endif %}
+ + + + + + + + + + + + + + + {% for item in items %} + + + + + + + + + {% endfor %} + +
排名被悬赏门派赏金剩余时间发起人
{{ loop.index }}{{ item.name }} +
+ {% if icons.sect.get(item.forceName) %} + {{ item.forceName }} + {% endif %} + {{ item.forceName }} +
+
{{ item.money }}{{ item.remainingTime }}{{ item.payerName }}
+
diff --git a/templates/pages/kuafumingjian.html b/templates/pages/kuafumingjian.html new file mode 100644 index 0000000..c4f21e3 --- /dev/null +++ b/templates/pages/kuafumingjian.html @@ -0,0 +1,34 @@ +{# template-title: 跨服名剑榜 #} +{# template-components: data-table #} + + +
+

{{ name }}

+
{{ server }} · {{ mode_name }}{% if update_time %} · 更新时间:{{ update_time }}{% endif %}
+ + + + + + + + + + + + + + + {% for item in items %} + + + + + + + + + {% endfor %} + +
排名队伍名称积分胜场败场总场数
{{ loop.index }}{{ item.corpsName }}{{ item.corpsLevel }}{{ item.seasonWinCount }}{{ item.seasonLoseCount }}{{ item.seasonTotalCount }}
+
diff --git a/templates/pages/rank_role.html b/templates/pages/rank_role.html index 058a556..b915b42 100644 --- a/templates/pages/rank_role.html +++ b/templates/pages/rank_role.html @@ -33,7 +33,10 @@ {{ item.name }} {{ item.level }} - {{ item.campName }} + {% set camp_name = item.campName or '未知' %} + + {{ camp_name }} + {{ item.tongName }} {{ item.totalScore }} diff --git a/templates/pages/shitu.html b/templates/pages/shitu.html index 9dd9c2c..06d7cbf 100644 --- a/templates/pages/shitu.html +++ b/templates/pages/shitu.html @@ -38,7 +38,10 @@ {{ item.roleLevel }} {{ item.bodyName }} {{ item.tongName }} - {{ item.campName }} + {% set camp_name = item.campName or '未知' %} + + {{ camp_name }} + {{ item.comment }} {% endfor %} diff --git a/templates/pages/wulinzhengba.html b/templates/pages/wulinzhengba.html new file mode 100644 index 0000000..5cbef90 --- /dev/null +++ b/templates/pages/wulinzhengba.html @@ -0,0 +1,43 @@ +{# template-title: 武林争霸赛 #} +{# template-components: data-table #} + + +
+

{{ name }}

+
{{ server }} · {{ camp_name }}{% if update_time %} · 更新时间:{{ update_time }}{% endif %}
+ + + + + + + + + + + + + + + + + + {% for item in items %} + + + + + + {% set camp_name = item.campName or '未知' %} + + + + + + + {% endfor %} + +
排名帮会名称帮主所属服务器阵营胜场败场积分最后比赛时间
{{ loop.index }}{{ item.tongName }}{{ item.masterName }}{{ item.serverName }} + {{ camp_name }} + {{ item.winCount }}{{ item.loseCount }}{{ item.totalScore }}{{ item.lastMatchTimeText }}
+
diff --git a/templates/pages/zhengyingpaimai.html b/templates/pages/zhengyingpaimai.html index c638c22..d8959f3 100644 --- a/templates/pages/zhengyingpaimai.html +++ b/templates/pages/zhengyingpaimai.html @@ -29,7 +29,10 @@ {{ m.server }} {{ m.itemName }} {{ m.roleName }} - {{ m.campName }} + {% set camp_name = m.campName or '未知' %} + + {{ camp_name }} + {{ m.itemAmount }} {{ m.time }} diff --git a/templates/pages/zhenyingevent.html b/templates/pages/zhenyingevent.html index 08b5b63..abda0dc 100644 --- a/templates/pages/zhenyingevent.html +++ b/templates/pages/zhenyingevent.html @@ -20,13 +20,16 @@ {% for item in items %} - {{ item.campName }} + {% set camp_name = item.campName or '未知' %} + + {{ camp_name }} + {{ item.fenxianName }} {{ item.friendName }} {{ item.roleName }} diff --git a/templates/sect/万灵.png b/templates/sect/万灵.png new file mode 100644 index 0000000..683458e Binary files /dev/null and b/templates/sect/万灵.png differ diff --git a/templates/styles/components.css b/templates/styles/components.css index b7a65eb..75a1a42 100644 --- a/templates/styles/components.css +++ b/templates/styles/components.css @@ -353,6 +353,14 @@ background: var(--jx3-surface-muted); } +.jx3-template .data-camp-icon { + display: block; + width: 46px; + height: 40px; + margin: 0 auto; + object-fit: contain; +} + .jx3-template .data-table tr.camp-haoqi { color: var(--jx3-blue); }