This commit is contained in:
2026-06-24 13:01:52 +08:00
parent f0e535c8ca
commit d8ceb93ea7
9 changed files with 996 additions and 426 deletions
+10
View File
@@ -1,5 +1,15 @@
## 更新记录 ## 更新记录
### version: 2.6
新增 **资历 角色名称 [服务器]** 指令,支持两轮对话选择资历总览或分类总览,并以图片形式展示角色资历点数完成进度。
新增资历菜单与点数基础数据缓存,减少长期稳定接口的重复请求。
调整功能帮助页分类,指令分组改为日常、官方、角色、职业、休闲、阵营、副本、活动、交易、杂项、排行、推送和避雷。
调整指令触发词:**诛恶事件** 改为 **诛恶****排行** 改为 **名剑排行**
### version: 2.5 ### version: 2.5
增加部分新指令,调整功能页面 增加部分新指令,调整功能页面
+38 -13
View File
@@ -2,24 +2,27 @@
基于 [AstrBot](https://docs.astrbot.app/) 框架开发的剑网三游戏数据查询插件。插件主要通过 JX3API、JX3BOX、茶馆等外部接口获取游戏数据,整理为文本消息、富媒体消息,或通过 `HTML + Jinja2` 模板渲染成图片后发送。 基于 [AstrBot](https://docs.astrbot.app/) 框架开发的剑网三游戏数据查询插件。插件主要通过 JX3API、JX3BOX、茶馆等外部接口获取游戏数据,整理为文本消息、富媒体消息,或通过 `HTML + Jinja2` 模板渲染成图片后发送。
当前插件以“查询型功能”为主,覆盖日常、奇遇、名剑、阵营、交易、职业、排行、副本、活动、名片、百战、推送和本地避雷等场景。部分功能需要配置 JX3API Token,少量职业/角色类接口还需要推栏 ticket。 当前插件以“查询型功能”为主,覆盖日常、官方、角色、职业、休闲、阵营、副本、活动、交易、杂项、排行、推送和本地避雷等场景。部分功能需要配置 JX3API Token,少量职业/角色类接口还需要推栏 ticket。
## 功能概览 ## 功能概览
插件内置 `功能` 指令,会以图片形式展示完整指令列表、参数说明和是否需要令牌。当前帮助页按 16 个功能分组维护,约 87 条指令。 插件内置 `功能` 指令,会以图片形式展示完整指令列表、参数说明和是否需要令牌。当前帮助页按 13 个功能分组维护,约 88 条指令。
主要功能包括: 主要功能包括:
- 日常与官方:日常、月历、名望、公告、维护、区服、开服、状态、技改 - 日常功能:日常、月历、名望、诛恶
- 职业与角色:角色、阵眼、奇穴、技能、小药、宏、配装、资历排行、试炼排行 - 官方功能:公告、维护、技改、区服、开服、状态
- 奇遇:角色奇遇、未做奇遇、奇遇统计、近期奇遇、奇遇汇总、奇遇攻略 - 角色功能:角色、资历、名片、所有名片、奇遇、未做奇遇、战绩、精耐
- 名剑:战绩、名剑排行、名剑统计 - 职业功能:小药、宏、配装、阵眼、奇穴、技能
- 阵营:沙盘、阵营拍卖、阵营事件、关隘首领、帮战记录、统战 YY - 休闲功能:烟花、拜师、收徒、随机名片、攻略、奇遇统计、近期奇遇、奇遇汇总
- 交易:金价、外观物价、交易行、贴吧物价 - 阵营功能:沙盘、阵营拍卖、阵营事件、关隘首领、帮战记录、统战 YY
- 活动:扶摇、诛恶事件、本日赤兔、本周赤兔、刷马、的卢 - 副本功能:百战、招募、掉落、解密
- 其他:拜师、收徒、名片、百战、精耐、招募、掉落、解密、八卦、骗子查询 - 活动功能:扶摇、本日赤兔、本周赤兔、刷马、的卢
- 本地功能:避雷添加、查看、查询、修改、删除 - 交易功能:金价、物价、交易行、贴吧物价
- 杂项功能:科举、花价、装饰、器物、八卦、骗子、骚话。
- 排行功能:名剑统计、名剑排行、资历排行、试炼排行和各类风云录榜单。
- 推送功能:开服推送、新闻推送、刷马推送、赤兔推送。 - 推送功能:开服推送、新闻推送、刷马推送、赤兔推送。
- 避雷功能:避雷查看、添加、删除、修改、查询。
## 安装与依赖 ## 安装与依赖
@@ -43,7 +46,7 @@ pip install -r requirements.txt
- `apscheduler`:后台推送任务调度。 - `apscheduler`:后台推送任务调度。
- `matplotlib`:部分图像/数据展示依赖。 - `matplotlib`:部分图像/数据展示依赖。
插件元信息在 `metadata.yaml` 中维护,要求 AstrBot 版本 `>=4.11.0` 插件元信息在 `metadata.yaml` 中维护,当前版本为 `v2.6`要求 AstrBot 版本 `>=4.11.0`
## 插件配置 ## 插件配置
@@ -166,9 +169,12 @@ pip install -r requirements.txt
```text ```text
日常 日常
诛恶
资历 飞翔大野猪
奇遇 飞翔大野猪 奇遇 飞翔大野猪
未做奇遇 飞翔大野猪 梦江南 未做奇遇 飞翔大野猪 梦江南
战绩 飞翔大野猪 梦江南 33 战绩 飞翔大野猪 梦江南 33
名剑排行 50 33
贴吧物价 狐金 5 梦江南 贴吧物价 狐金 5 梦江南
统战 梦江南 统战 梦江南
``` ```
@@ -234,6 +240,7 @@ astrbot_plugin_jx3/
- `image_msg()`:直接发送图片 URL 或图片数据。 - `image_msg()`:直接发送图片 URL 或图片数据。
- `plain_chain()`:发送文本 + 图片等富媒体消息链。 - `plain_chain()`:发送文本 + 图片等富媒体消息链。
- `handler_plain_image_msg()`:先发文本,再发图片,适用于部分组合型功能。 - `handler_plain_image_msg()`:先发文本,再发图片,适用于部分组合型功能。
- `handler_zili_msg()`:资历查询专用两轮会话,先发送 `0-18` 分类菜单,再按用户选择渲染资历进度图片。
所有可选服务器参数都会通过 `serverdefault()` 补齐默认服务器。 所有可选服务器参数都会通过 `serverdefault()` 补齐默认服务器。
@@ -332,10 +339,28 @@ html_renderer.render_custom_template()
- 避雷记录。 - 避雷记录。
- 推送任务状态缓存。 - 推送任务状态缓存。
- 资历菜单与资历点数基础数据缓存,默认缓存 30 天,接口失败时可使用旧缓存兜底。
`core/sqlite.py` 封装了异步增删改查;`core/bilei_data.py` 基于该封装实现避雷数据管理。 `core/sqlite.py` 封装了异步增删改查;`core/bilei_data.py` 基于该封装实现避雷数据管理。
### 8. 后台推送 ### 8. 资历查询
`资历 角色名称 [服务器]` 是 2.6 新增的两轮对话功能。
第一轮会发送固定分类菜单,用户输入 `0-18` 后进入第二轮:
- `0`:展示角色资历总览,并列出 18 个大类的完成进度。
- `1-18`:展示对应大类总览,并列出该大类下所有子类的完成进度。
实现流程:
1. 通过角色信息接口 `jx3_jueshexinxi` 获取角色 `globalId`
2.`globalId` 请求 JX3BOX 角色资历接口,获取已完成资历 ID 列表。
3. 从资历菜单接口和资历点数接口读取基础数据,并缓存到本地 SQLite。
4. 展开菜单中单个 ID 和数组 ID,按资历点数计算 `已完成点数 / 总点数` 与百分比。
5. 使用 `templates/zili.html` 渲染进度条图片。
### 9. 后台推送
`core/async_task.py` 使用 APScheduler 实现轮询推送。 `core/async_task.py` 使用 APScheduler 实现轮询推送。
+266 -3
View File
@@ -1,5 +1,5 @@
import json import json
from datetime import datetime from datetime import datetime, timedelta
from typing import Dict, Any, Optional, List, Union from typing import Dict, Any, Optional, List, Union
from collections import defaultdict from collections import defaultdict
@@ -34,8 +34,32 @@ TONG_RANK_NAMES = {
"本周浩气五十强", "本周浩气五十强",
} }
ACHIEVEMENT_CHOICES = [
(0, None, "资历总览"),
(1, "1", "杂闻总览"),
(2, "2", "武学总览"),
(3, "3", "修为总览"),
(4, "4", "装备总览"),
(5, "5", "技艺总览"),
(6, "6", "阅读总览"),
(7, "7", "任务总览"),
(8, "8", "足迹总览"),
(9, "9", "战斗总览"),
(10, "10", "声望总览"),
(11, "11", "秘境总览"),
(12, "12", "帮会总览"),
(13, "13", "阵营总览"),
(14, "15", "节日总览"),
(15, "16", "活动总览"),
(16, "17", "风雨江湖路总览"),
(17, "40", "家园总览"),
(18, "41", "剑侠录总览"),
]
ACHIEVEMENT_CHOICE_MAP = {index: (menu_id, title) for index, menu_id, title in ACHIEVEMENT_CHOICES}
class JX3Service: class JX3Service:
def __init__(self, api_data_path: str, config: AstrBotConfig, sqlite: AsyncSQLiteDB): def __init__(self, api_data_path: str, config: AstrBotConfig, sqlite: AsyncSQLiteDB, cache_sqlite: Optional[AsyncSQLiteDB] = None):
# 加载API配置文件 # 加载API配置文件
try: try:
with open(api_data_path, 'r', encoding='utf-8') as f: with open(api_data_path, 'r', encoding='utf-8') as f:
@@ -49,6 +73,7 @@ class JX3Service:
self._config = config self._config = config
# 引用sqlite # 引用sqlite
self._sql_db = sqlite self._sql_db = sqlite
self._cache_db = cache_sqlite or sqlite
# 获取配置中的 Token # 获取配置中的 Token
self.token = self._config.get("jx3api_token", "") self.token = self._config.get("jx3api_token", "")
@@ -2386,6 +2411,244 @@ class JX3Service:
return return_data return return_data
async def _load_achievement_cache(self, key: str) -> tuple[Optional[Dict[str, Any]], bool]:
"""读取资历基础数据缓存,返回数据和是否已过期"""
try:
row = await self._cache_db.select_one("achievement_cache", "key=?", (key,))
except Exception as e:
logger.error(f"读取资历缓存失败: {e}")
return None, True
if not row:
return None, True
try:
payload = json.loads(row.get("content", "{}"))
updated_at = datetime.strptime(row.get("updated_at", ""), "%Y-%m-%d %H:%M:%S")
expired = datetime.now() - updated_at > timedelta(days=30)
return payload, expired
except Exception as e:
logger.error(f"解析资历缓存失败: {e}")
return None, True
async def _save_achievement_cache(self, key: str, payload: Dict[str, Any]):
"""写入资历基础数据缓存"""
try:
await self._cache_db.execute(
"""
INSERT INTO achievement_cache (key, content, updated_at)
VALUES (?, ?, ?)
ON CONFLICT(key) DO UPDATE SET
content=excluded.content,
updated_at=excluded.updated_at
""",
(
key,
json.dumps(payload, ensure_ascii=False),
datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
),
)
except Exception as e:
logger.error(f"写入资历缓存失败: {e}")
async def _get_achievement_base_data(self, cache_key: str, config_key: str) -> Optional[Dict[str, Any]]:
"""获取资历菜单或点数数据,优先使用未过期缓存"""
cached, expired = await self._load_achievement_cache(cache_key)
if cached and not expired:
return cached
data: Optional[Dict[str, Any]] = await self._base_request(config_key, "GET")
if data and isinstance(data, dict):
await self._save_achievement_cache(cache_key, data)
return data
if cached:
logger.warning(f"资历基础数据接口失败,使用旧缓存: {cache_key}")
return cached
return None
def _flatten_achievement_ids(self, values: Any) -> list[int]:
"""展开菜单中的单个资历 ID 和数组资历 ID"""
result = []
if isinstance(values, list):
for item in values:
result.extend(self._flatten_achievement_ids(item))
return result
try:
result.append(int(values))
except (TypeError, ValueError):
pass
return result
def _category_achievement_ids(self, category: Dict[str, Any]) -> list[int]:
"""提取分类及其子分类包含的资历 ID"""
ids = self._flatten_achievement_ids(category.get("achievements", []))
for child in category.get("children", []) or []:
if isinstance(child, dict):
ids.extend(self._flatten_achievement_ids(child.get("achievements", [])))
return ids
def _build_achievement_progress(
self,
name: str,
achievement_ids: list[int],
point_map: Dict[str, Any],
completed_ids: set[int],
) -> Dict[str, Any]:
"""按资历点数计算完成进度"""
unique_ids = set(achievement_ids)
total_points = 0
completed_points = 0
completed_count = 0
for achievement_id in unique_ids:
try:
point = int(point_map.get(str(achievement_id), 0) or 0)
except (TypeError, ValueError):
point = 0
total_points += point
if achievement_id in completed_ids:
completed_count += 1
completed_points += point
percent = round(completed_points / total_points * 100, 2) if total_points else 0
return {
"name": name,
"total_points": total_points,
"completed_points": completed_points,
"percent": percent,
"percent_text": f"{percent:.2f}%",
"achievement_count": len(unique_ids),
"completed_count": completed_count,
}
async def zili(self, name: str, server: str, choice: int) -> Dict[str, Any]:
"""角色资历"""
return_data = self._init_return_data()
if choice not in ACHIEVEMENT_CHOICE_MAP:
return_data["msg"] = "无效序号,结束会话"
return return_data
role_params = {"server": server, "name": name, "token": self.token}
role_data: Optional[Dict[str, Any]] = await self._base_request(
"jx3_jueshexinxi", "GET", params=role_params
)
if not role_data or not isinstance(role_data, dict):
return_data["msg"] = "未查询到角色"
return return_data
global_id = role_data.get("globalId")
if not global_id:
return_data["msg"] = "无法获取角色全区 ID"
return return_data
achievement_data = await self._api.get(
"https://next2.jx3box.com/api/next2/user-achievements",
params={"jx3id": global_id},
out_key="data",
)
if not achievement_data or not isinstance(achievement_data, dict):
return_data["msg"] = "未查询到资历数据"
return return_data
completed_ids = set()
achievements = achievement_data.get("achievements") or ""
if isinstance(achievements, str):
for item in achievements.split(","):
try:
completed_ids.add(int(item.strip()))
except ValueError:
continue
elif isinstance(achievements, list):
completed_ids = set(self._flatten_achievement_ids(achievements))
menu_payload = await self._get_achievement_base_data(
"achievement_menus",
"jx3box_achievement_menus",
)
point_payload = await self._get_achievement_base_data(
"achievement_points",
"jx3box_achievement_points",
)
if not menu_payload or not point_payload:
return_data["msg"] = "基础资历数据获取失败"
return return_data
menus = menu_payload.get("menus", {})
points = point_payload.get("points", {})
if not isinstance(menus, dict) or not isinstance(points, dict):
return_data["msg"] = "基础资历数据格式异常"
return return_data
selected_menu_id, selected_title = ACHIEVEMENT_CHOICE_MAP[choice]
items = []
if choice == 0:
total_ids = []
for _, menu_id, title in ACHIEVEMENT_CHOICES[1:]:
category = menus.get(str(menu_id), {})
if not isinstance(category, dict):
continue
category_ids = self._category_achievement_ids(category)
total_ids.extend(category_ids)
items.append(
self._build_achievement_progress(title, category_ids, points, completed_ids)
)
summary = self._build_achievement_progress(selected_title, total_ids, points, completed_ids)
else:
category = menus.get(str(selected_menu_id), {})
if not isinstance(category, dict) or not category:
return_data["msg"] = "未找到该资历分类"
return return_data
category_ids = self._category_achievement_ids(category)
summary = self._build_achievement_progress(selected_title, category_ids, points, completed_ids)
for child in category.get("children", []) or []:
if not isinstance(child, dict):
continue
child_ids = self._flatten_achievement_ids(child.get("achievements", []))
items.append(
self._build_achievement_progress(child.get("name", "未命名"), child_ids, points, completed_ids)
)
try:
return_data["temp"] = await load_template("zili.html")
except FileNotFoundError as e:
logger.error(f"加载模板失败: {e}")
return_data["msg"] = "系统错误:模板文件不存在"
return return_data
return_data["data"] = {
"title": selected_title,
"summary": summary,
"items": items,
"role_name": role_data.get("roleName", name),
"server": role_data.get("serverName", server),
"zone": role_data.get("zoneName", ""),
"force_name": role_data.get("forceName", ""),
"camp_name": role_data.get("campName", ""),
"tong_name": role_data.get("tongName", ""),
"updated_at": achievement_data.get("updated_at", ""),
"update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
return_data["code"] = 200
return return_data
async def jueshe(self,name: str, server: str) -> Dict[str, Any]: async def jueshe(self,name: str, server: str) -> Dict[str, Any]:
"""角色""" """角色"""
return_data = self._init_return_data() return_data = self._init_return_data()
@@ -2408,7 +2671,7 @@ class JX3Service:
f"服务器:{data.get('zoneName', '')}·{data.get('serverName', '')}\n" f"服务器:{data.get('zoneName', '')}·{data.get('serverName', '')}\n"
f"名称:{data.get('roleName', '')}\n" f"名称:{data.get('roleName', '')}\n"
f"角色ID{data.get('roleId', '')}\n" f"角色ID{data.get('roleId', '')}\n"
f"全区ID{data.get('globalRoleId', '')}\n" f"全区ID{data.get('globalId', '')}\n"
f"职业:{data.get('forceName', '')}·{data.get('bodyName', '')}\n" f"职业:{data.get('forceName', '')}·{data.get('bodyName', '')}\n"
f"帮会:{data.get('tongName', '')}\n" f"帮会:{data.get('tongName', '')}\n"
f"阵营:{data.get('campName', '')}\n" f"阵营:{data.get('campName', '')}\n"
+86
View File
@@ -13,6 +13,29 @@ from .bilei_data import BiLeidata
class MessageBuilder: class MessageBuilder:
"""回复消息构建""" """回复消息构建"""
ZILI_MENU_TEXT = (
"请选择资历查询类型:\n"
"0:资历总览\n"
"1:杂闻总览\n"
"2:武学总览\n"
"3:修为总览\n"
"4:装备总览\n"
"5:技艺总览\n"
"6:阅读总览\n"
"7:任务总览\n"
"8:足迹总览\n"
"9:战斗总览\n"
"10:声望总览\n"
"11:秘境总览\n"
"12:帮会总览\n"
"13:阵营总览\n"
"14:节日总览\n"
"15:活动总览\n"
"16:风雨江湖路总览\n"
"17:家园总览\n"
"18:剑侠录总览"
)
def __init__(self, server: str, jx3api: JX3Service, bilei: BiLeidata, jx3at: AsyncTask, icons: dict[str, dict[str, str]]): def __init__(self, server: str, jx3api: JX3Service, bilei: BiLeidata, jx3at: AsyncTask, icons: dict[str, dict[str, str]]):
self.server = server self.server = server
self.jx3api = jx3api self.jx3api = jx3api
@@ -187,6 +210,64 @@ class MessageBuilder:
await event.send(event.plain_result("猪脑过载,请稍后再试")) await event.send(event.plain_result("猪脑过载,请稍后再试"))
async def handler_zili_msg(self, event: AstrMessageEvent, name: str, server: str):
"""资历查询专用两轮会话,第一轮文本菜单,第二轮图片"""
try:
await event.send(event.plain_result(self.ZILI_MENU_TEXT))
user_id = event.get_sender_id()
@session_waiter(timeout=30)
async def zili_select_waiter(controller: SessionController, new_event: AstrMessageEvent):
if new_event.get_sender_id() != user_id:
return
msg = new_event.get_message_str().strip()
if not msg.isdigit():
await new_event.send(MessageChain().message("输入异常,结束会话"))
controller.stop()
return
choice = int(msg)
if choice < 0 or choice > 18:
await new_event.send(MessageChain().message("无效序号,结束会话"))
controller.stop()
return
try:
data = await self.jx3api.zili(name, server, choice)
if data["code"] != 200:
await new_event.send(MessageChain().message(data.get("msg", "获取资历数据失败")))
controller.stop()
return
options = {
"quality": 100,
"device_scale_factor_level": "normal",
"full_page": True,
"omit_background": False,
"type": "jpeg"
}
data["data"]["icons"] = self.icons
url = await self.html_render(data["temp"], data["data"], options=options)
await new_event.send(new_event.image_result(url))
except Exception as e:
logger.error(f"资历查询执行错误: {e}")
await new_event.send(MessageChain().message("猪脑过载,请稍后再试"))
controller.stop()
try:
await zili_select_waiter(event)
except TimeoutError:
await event.send(event.plain_result("选择超时,已结束会话"))
except Exception:
logger.error("资历选择发生异常", exc_info=True)
except Exception as e:
logger.error(f"资历会话执行错误: {e}")
await event.send(event.plain_result("猪脑过载,请稍后再试"))
async def jx3_helps(self, event: AstrMessageEvent): async def jx3_helps(self, event: AstrMessageEvent):
"""剑三 功能""" """剑三 功能"""
return await self.T2I_image_msg(event, self.jx3api.helps) return await self.T2I_image_msg(event, self.jx3api.helps)
@@ -292,6 +373,11 @@ class MessageBuilder:
return await self.plain_msg(event, self.jx3api.shaohua) return await self.plain_msg(event, self.jx3api.shaohua)
async def jx3_zili(self, event: AstrMessageEvent, name: str, server: str = ""):
"""剑三 资历 角色名称 服务器"""
return await self.handler_zili_msg(event, name, self.serverdefault(server))
async def jx3_jiemi(self, event: AstrMessageEvent): async def jx3_jiemi(self, event: AstrMessageEvent):
"""剑三 解密""" """剑三 解密"""
return await self.plain_msg(event, self.jx3api.jiemi) return await self.plain_msg(event, self.jx3api.jiemi)
+17
View File
@@ -591,6 +591,23 @@
"mount": "", "mount": "",
"star": "1" "star": "1"
} }
},
"jx3box_achievement_menus":{
"url":"https://node.jx3box.com/api/node/achievement/menus",
"method":"GET",
"description":"魔盒资历菜单列表",
"params":{
"general": "1",
"client": "std"
}
},
"jx3box_achievement_points":{
"url":"https://node.jx3box.com/api/node/achievement/points",
"method":"GET",
"description":"魔盒资历点数列表",
"params":{
"client": "std"
}
} }
+17 -4
View File
@@ -17,7 +17,7 @@ from .core.fun_basic import load_as_base64
@register("astrbot_plugin_jx3", @register("astrbot_plugin_jx3",
"fxdyz", "fxdyz",
"通过调用剑网三API接口获取游戏数据,处理发送。", "通过调用剑网三API接口获取游戏数据,处理发送。",
"2.5", "2.6",
"https://github.com/qsc20001102/astrbot_plugin_jx3api" "https://github.com/qsc20001102/astrbot_plugin_jx3api"
) )
class Jx3ApiPlugin(Star): class Jx3ApiPlugin(Star):
@@ -57,6 +57,7 @@ class Jx3ApiPlugin(Star):
# 数据库初始化 # 数据库初始化
await self.init_bilei_data() await self.init_bilei_data()
await self.init_tuishong_data() await self.init_tuishong_data()
await self.init_achievement_cache_data()
# 连接插件数据 # 连接插件数据
await self.plugin_sql_db.connect() await self.plugin_sql_db.connect()
@@ -142,7 +143,7 @@ class Jx3ApiPlugin(Star):
self.plugin_sql_db = AsyncSQLiteDB(str(self.plugin_data_path)) self.plugin_sql_db = AsyncSQLiteDB(str(self.plugin_data_path))
# 剑网三功能实例化 # 剑网三功能实例化
self.bilei = BiLeidata(self.local_sql_db) self.bilei = BiLeidata(self.local_sql_db)
self.jx3api = JX3Service(str(self.api_data_path), self.conf, self.plugin_sql_db) self.jx3api = JX3Service(str(self.api_data_path), self.conf, self.plugin_sql_db, self.local_sql_db)
self.jx3at = AsyncTask(cast(Context, self.context), self.conf, self.jx3api, self.local_sql_db) self.jx3at = AsyncTask(cast(Context, self.context), self.conf, self.jx3api, self.local_sql_db)
self.jx3cmd = MessageBuilder(self.server, self.jx3api, self.bilei, self.jx3at, self.icons) self.jx3cmd = MessageBuilder(self.server, self.jx3api, self.bilei, self.jx3at, self.icons)
self.jx3cmd = MessageBuilder(self.server, self.jx3api, self.bilei, self.jx3at, self.icons) self.jx3cmd = MessageBuilder(self.server, self.jx3api, self.bilei, self.jx3at, self.icons)
@@ -182,6 +183,17 @@ class Jx3ApiPlugin(Star):
""") """)
async def init_achievement_cache_data(self):
"""初始化资历基础数据缓存表"""
await self.local_sql_db.execute("""
CREATE TABLE IF NOT EXISTS achievement_cache(
key TEXT PRIMARY KEY,
content TEXT NOT NULL,
updated_at TEXT NOT NULL
)
""")
def ini_command_map(self): def ini_command_map(self):
"""初始化指令集""" """初始化指令集"""
self.command_map = { self.command_map = {
@@ -206,6 +218,7 @@ class Jx3ApiPlugin(Star):
"资历排行": self.jx3cmd.jx3_zilipaixing, "资历排行": self.jx3cmd.jx3_zilipaixing,
"试炼排行": self.jx3cmd.jx3_shilianpaixing, "试炼排行": self.jx3cmd.jx3_shilianpaixing,
"骚话": self.jx3cmd.jx3_shaohua, "骚话": self.jx3cmd.jx3_shaohua,
"资历": self.jx3cmd.jx3_zili,
"解密": self.jx3cmd.jx3_jiemi, "解密": self.jx3cmd.jx3_jiemi,
"沙盘": self.jx3cmd.jx3_shapan, "沙盘": self.jx3cmd.jx3_shapan,
"攻略": self.jx3cmd.jx3_qiyugonglue, "攻略": self.jx3cmd.jx3_qiyugonglue,
@@ -213,7 +226,7 @@ class Jx3ApiPlugin(Star):
"配装": self.jx3cmd.jx3_peizhuang, "配装": self.jx3cmd.jx3_peizhuang,
"百战": self.jx3cmd.jx3_baizhan, "百战": self.jx3cmd.jx3_baizhan,
"扶摇": self.jx3cmd.jx3_fuyaojjiutian, "扶摇": self.jx3cmd.jx3_fuyaojjiutian,
"诛恶事件": self.jx3cmd.jx3_zhueevent, "诛恶": self.jx3cmd.jx3_zhueevent,
"本日赤兔": self.jx3cmd.jx3_benrichitu, "本日赤兔": self.jx3cmd.jx3_benrichitu,
"本周赤兔": self.jx3cmd.jx3_benzhouchitu, "本周赤兔": self.jx3cmd.jx3_benzhouchitu,
"拍卖": self.jx3cmd.jx3_zhengyingpaimai, "拍卖": self.jx3cmd.jx3_zhengyingpaimai,
@@ -230,7 +243,7 @@ class Jx3ApiPlugin(Star):
"近期奇遇": self.jx3cmd.jx3_jinqiqiyu, "近期奇遇": self.jx3cmd.jx3_jinqiqiyu,
"奇遇汇总": self.jx3cmd.jx3_qiyuhuizong, "奇遇汇总": self.jx3cmd.jx3_qiyuhuizong,
"战绩": self.jx3cmd.jx3_zhanji, "战绩": self.jx3cmd.jx3_zhanji,
"排行": self.jx3cmd.jx3_mingjianpaihang, "名剑排行": self.jx3cmd.jx3_mingjianpaihang,
"名剑统计": self.jx3cmd.jx3_mingjiantongji, "名剑统计": self.jx3cmd.jx3_mingjiantongji,
"招募": self.jx3cmd.jx3_tuanduizhaomu, "招募": self.jx3cmd.jx3_tuanduizhaomu,
"拜师": self.jx3cmd.jx3_baishi, "拜师": self.jx3cmd.jx3_baishi,
+1 -1
View File
@@ -1,7 +1,7 @@
name: astrbot_plugin_jx3 name: astrbot_plugin_jx3
display_name: 剑网三游戏数据查询工具 display_name: 剑网三游戏数据查询工具
desc: 通过接口调用剑网三API接口获取游戏数据,处理发送。 desc: 通过接口调用剑网三API接口获取游戏数据,处理发送。
version: v2.5 version: v2.6
author: 飞翔大野猪 author: 飞翔大野猪
repo: https://github.com/qsc20001102/astrbot_plugin_jx3 repo: https://github.com/qsc20001102/astrbot_plugin_jx3
astrbot_version: ">=4.11.0" astrbot_version: ">=4.11.0"
+285 -335
View File
@@ -369,8 +369,8 @@ body {
<h1>剑三机器人功能指令</h1> <h1>剑三机器人功能指令</h1>
<p>按使用场景重新分组,保留每条指令的名称、使用示例与说明。卡片底色表示指令是否需要令牌,免令牌与需令牌功能一眼区分。</p> <p>按使用场景重新分组,保留每条指令的名称、使用示例与说明。卡片底色表示指令是否需要令牌,免令牌与需令牌功能一眼区分。</p>
<div class="hero-stats"> <div class="hero-stats">
<div class="stat"><b>87</b><span>条指令</span></div> <div class="stat"><b>88</b><span>条指令</span></div>
<div class="stat"><b>16</b><span>个功能分组</span></div> <div class="stat"><b>13</b><span>个功能分组</span></div>
</div> </div>
</section> </section>
@@ -394,9 +394,9 @@ body {
<h2>日常功能</h2> <h2>日常功能</h2>
</div> </div>
<div class="section-meta"> <div class="section-meta">
<span class="mini">3</span> <span class="mini">4</span>
<span class="mini">免令牌 3</span> <span class="mini">免令牌 3</span>
<span class="mini">需令牌 1</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
@@ -428,137 +428,13 @@ body {
<p class="desc">查询指定名望势力事件。</p> <p class="desc">查询指定名望势力事件。</p>
</article> </article>
</div>
</div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>休闲功能</h2>
</div>
<div class="section-meta">
<span class="mini">4 条</span>
<span class="mini">免令牌 1</span>
<span class="mini">需令牌 3</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">随机骚话</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">骚话</code>
<p class="desc">随机返回一句趣味骚话,用于群内互动。</p>
</article>
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">烟花记录</div> <div class="cmd-name">诛恶</div>
<div class="source-tag">需令牌</div> <div class="source-tag">需令牌</div>
</div> </div>
<code class="usage">烟花 角色名 [服务器]</code> <code class="usage">诛恶</code>
<p class="desc">查询指定角色烟花记录。</p> <p class="desc">查询诛恶事件服务器、地图和时间记录。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">拜师列表</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">拜师 [搜索关键词] [服务器]</code>
<p class="desc">查询徒弟找师傅信息。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">收徒列表</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">收徒 [搜索关键词] [服务器]</code>
<p class="desc">查询师傅找徒弟信息。</p>
</article>
</div>
</div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>杂项功能</h2>
</div>
<div class="section-meta">
<span class="mini">7 条</span>
<span class="mini">免令牌 4</span>
<span class="mini">需令牌 3</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">科举答题</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">科举 内容</code>
<p class="desc">按题目关键字查询科举答案。</p>
</article>
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">家园鲜花</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">花价 名称 [服务器] [家园地图]</code>
<p class="desc">查询家园鲜花价格,支持服务器与家园地图筛选。</p>
</article>
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">家园装饰</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">装饰 名称</code>
<p class="desc">查询家园装饰物名称、来源与相关信息。</p>
</article>
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">器物图谱</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">器物 地图名称</code>
<p class="desc">查询指定地图可收集的器物图谱。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">八卦记录</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">八卦 类型</code>
<p class="desc">查询 818 / 616 / 避雷等八卦记录。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">解密</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">解密</code>
<p class="desc">查询当前机关解密结果与下轮时间。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">骗子查询</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">骗子 QQ号</code>
<p class="desc">按 QQ 号查询骗子记录。</p>
</article> </article>
</div> </div>
@@ -573,7 +449,6 @@ body {
<div class="section-meta"> <div class="section-meta">
<span class="mini">6 条</span> <span class="mini">6 条</span>
<span class="mini">免令牌 6</span> <span class="mini">免令牌 6</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
@@ -638,70 +513,34 @@ body {
<div class="section"> <div class="section">
<div class="section-head"> <div class="section-head">
<div class="title-wrap"> <div class="title-wrap">
<span class="icon"></span> <span class="icon"></span>
<h2>交易功能</h2> <h2>角色功能</h2>
</div> </div>
<div class="section-meta"> <div class="section-meta">
<span class="mini">4</span> <span class="mini">8</span>
<span class="mini">需令牌 8</span>
<span class="mini">需令牌 4</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">金价查询</div> <div class="cmd-name">角色信息</div>
<div class="source-tag">需令牌</div> <div class="source-tag">需令牌</div>
</div> </div>
<code class="usage">金价 [服务器]</code> <code class="usage">角色 名称 [服务器]</code>
<p class="desc">查询指定服务器金价行情</p> <p class="desc">查询角色基础信息</p>
</article> </article>
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">外观物价</div> <div class="cmd-name">角色资历</div>
<div class="source-tag">需令牌</div> <div class="source-tag">需令牌</div>
</div> </div>
<code class="usage">物价 外观名称 [服务器]</code> <code class="usage">资历 角色名 [服务器]</code>
<p class="desc">查询指定外观在服务器内的物价行情</p> <p class="desc">按分类查询角色资历点数完成进度</p>
</article> </article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">交易行</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">交易行 物品名称 [服务器]</code>
<p class="desc">查询指定物品交易行价格与挂单信息。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">贴吧物价</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">贴吧物价 物品名称 [记录数量] [服务器]</code>
<p class="desc">查询指定物品的贴吧交易记录。</p>
</article>
</div>
</div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>名片功能</h2>
</div>
<div class="section-meta">
<span class="mini">3 条</span>
<span class="mini">需令牌 3</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">角色名片</div> <div class="cmd-name">角色名片</div>
@@ -722,11 +561,38 @@ body {
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">随机名片</div> <div class="cmd-name">角色奇遇</div>
<div class="source-tag">需令牌</div> <div class="source-tag">需令牌</div>
</div> </div>
<code class="usage">随机 职业 体型 [服务器]</code> <code class="usage">奇遇 角色名 [服务器]</code>
<p class="desc">按职业、体型、服务器随机抽取名片</p> <p class="desc">查询指定角色已触发奇遇</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">未做奇遇</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">未做奇遇 角色名 [服务器]</code>
<p class="desc">查询指定角色尚未完成的奇遇。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">名剑战绩</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">战绩 角色名 [服务器] [模式]</code>
<p class="desc">查询角色名剑大会战绩,支持 22 / 33 模式。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">角色百战</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">精耐 角色名 [服务器]</code>
<p class="desc">查询角色百战精力、耐力和技能列表。</p>
</article> </article>
</div> </div>
@@ -739,9 +605,9 @@ body {
<h2>职业功能</h2> <h2>职业功能</h2>
</div> </div>
<div class="section-meta"> <div class="section-meta">
<span class="mini">7</span> <span class="mini">6</span>
<span class="mini">免令牌 3</span> <span class="mini">免令牌 3</span>
<span class="mini">需令牌 4</span> <span class="mini">需令牌 3</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
@@ -773,15 +639,6 @@ body {
<p class="desc">查询指定心法配装,支持 PvP / PvE 筛选。</p> <p class="desc">查询指定心法配装,支持 PvP / PvE 筛选。</p>
</article> </article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">角色信息</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">角色 名称 [服务器]</code>
<p class="desc">查询角色基础信息。</p>
</article>
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">心法阵眼</div> <div class="cmd-name">心法阵眼</div>
@@ -812,6 +669,95 @@ body {
</div> </div>
</div> </div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>休闲功能</h2>
</div>
<div class="section-meta">
<span class="mini">8 条</span>
<span class="mini">免令牌 1</span>
<span class="mini">需令牌 7</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">烟花记录</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">烟花 角色名 [服务器]</code>
<p class="desc">查询指定角色烟花记录。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">拜师列表</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">拜师 [搜索关键词] [服务器]</code>
<p class="desc">查询徒弟找师傅信息。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">收徒列表</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">收徒 [搜索关键词] [服务器]</code>
<p class="desc">查询师傅找徒弟信息。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">随机名片</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">随机名片 职业 体型 [服务器]</code>
<p class="desc">按职业、体型、服务器随机抽取名片。</p>
</article>
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">奇遇攻略</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">攻略 奇遇名称</code>
<p class="desc">查询指定奇遇的触发条件、奖励与流程攻略。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">奇遇统计</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">奇遇统计 奇遇名称 [服务器]</code>
<p class="desc">统计指定奇遇触发次数</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">近期奇遇</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">近期奇遇 [服务器]</code>
<p class="desc">查询指定服务器最近触发的奇遇记录</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">奇遇汇总</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">奇遇汇总 [天数] [服务器]</code>
<p class="desc">统计指定服务器最近一段时间的奇遇触发汇总</p>
</article>
</div>
</div>
<div class="section"> <div class="section">
<div class="section-head"> <div class="section-head">
<div class="title-wrap"> <div class="title-wrap">
@@ -890,13 +836,21 @@ body {
<h2>副本功能</h2> <h2>副本功能</h2>
</div> </div>
<div class="section-meta"> <div class="section-meta">
<span class="mini">2</span> <span class="mini">4</span>
<span class="mini">需令牌 4</span>
<span class="mini">需令牌 2</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">百战首领</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">百战</code>
<p class="desc">查询本期百战异闻录首领与关卡信息。</p>
</article>
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">副本招募</div> <div class="cmd-name">副本招募</div>
@@ -915,110 +869,13 @@ body {
<p class="desc">查询团队秘境贵重物品掉落记录</p> <p class="desc">查询团队秘境贵重物品掉落记录</p>
</article> </article>
</div>
</div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>奇遇功能</h2>
</div>
<div class="section-meta">
<span class="mini">6 条</span>
<span class="mini">免令牌 1</span>
<span class="mini">需令牌 5</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">奇遇攻略</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">攻略 奇遇名称</code>
<p class="desc">查询指定奇遇的触发条件、奖励与流程攻略。</p>
</article>
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">角色奇遇</div> <div class="cmd-name">解密</div>
<div class="source-tag">需令牌</div> <div class="source-tag">需令牌</div>
</div> </div>
<code class="usage">奇遇 角色名 [服务器]</code> <code class="usage">解密</code>
<p class="desc">查询指定角色已触发奇遇</p> <p class="desc">查询当前机关解密结果与下轮时间</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">未做奇遇</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">未做奇遇 角色名 [服务器]</code>
<p class="desc">查询指定角色尚未完成的奇遇。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">奇遇统计</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">奇遇统计 奇遇名称 [服务器]</code>
<p class="desc">统计指定奇遇触发次数</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">近期奇遇</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">近期奇遇 [服务器]</code>
<p class="desc">查询指定服务器最近触发的奇遇记录</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">奇遇汇总</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">奇遇汇总 [天数] [服务器]</code>
<p class="desc">统计指定服务器最近一段时间的奇遇触发汇总</p>
</article>
</div>
</div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>百战功能</h2>
</div>
<div class="section-meta">
<span class="mini">2 条</span>
<span class="mini">需令牌 2</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">百战首领</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">百战</code>
<p class="desc">查询本期百战异闻录首领与关卡信息。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">角色百战</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">精耐 角色名 [服务器]</code>
<p class="desc">查询角色百战精力、耐力和技能列表。</p>
</article> </article>
</div> </div>
@@ -1031,9 +888,8 @@ body {
<h2>活动功能</h2> <h2>活动功能</h2>
</div> </div>
<div class="section-meta"> <div class="section-meta">
<span class="mini">6</span> <span class="mini">5</span>
<span class="mini">需令牌 5</span>
<span class="mini">需令牌 6</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
@@ -1047,15 +903,6 @@ body {
<p class="desc">查询指定服务器扶摇九天活动记录。</p> <p class="desc">查询指定服务器扶摇九天活动记录。</p>
</article> </article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">诛恶事件</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">诛恶事件</code>
<p class="desc">查询诛恶事件服务器、地图和时间记录。</p>
</article>
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">本日赤兔</div> <div class="cmd-name">本日赤兔</div>
@@ -1098,26 +945,148 @@ body {
<div class="section"> <div class="section">
<div class="section-head"> <div class="section-head">
<div class="title-wrap"> <div class="title-wrap">
<span class="icon"></span> <span class="icon"></span>
<h2>名剑功能</h2> <h2>交易功能</h2>
</div> </div>
<div class="section-meta"> <div class="section-meta">
<span class="mini">3</span> <span class="mini">4</span>
<span class="mini">需令牌 4</span>
<span class="mini">需令牌 3</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">名剑战绩</div> <div class="cmd-name">金价查询</div>
<div class="source-tag">需令牌</div> <div class="source-tag">需令牌</div>
</div> </div>
<code class="usage">战绩 角色名 [服务器] [模式]</code> <code class="usage">金价 [服务器]</code>
<p class="desc">查询角色名剑大会战绩,支持 22 / 33 模式</p> <p class="desc">查询指定服务器金价行情</p>
</article> </article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">外观物价</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">物价 外观名称 [服务器]</code>
<p class="desc">查询指定外观在服务器内的物价行情。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">交易行</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">交易行 物品名称 [服务器]</code>
<p class="desc">查询指定物品交易行价格与挂单信息。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">贴吧物价</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">贴吧物价 物品名称 [记录数量] [服务器]</code>
<p class="desc">查询指定物品的贴吧交易记录。</p>
</article>
</div>
</div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>杂项功能</h2>
</div>
<div class="section-meta">
<span class="mini">7 条</span>
<span class="mini">免令牌 5</span>
<span class="mini">需令牌 2</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">科举答题</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">科举 内容</code>
<p class="desc">按题目关键字查询科举答案。</p>
</article>
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">家园鲜花</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">花价 名称 [服务器] [家园地图]</code>
<p class="desc">查询家园鲜花价格,支持服务器与家园地图筛选。</p>
</article>
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">家园装饰</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">装饰 名称</code>
<p class="desc">查询家园装饰物名称、来源与相关信息。</p>
</article>
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">器物图谱</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">器物 地图名称</code>
<p class="desc">查询指定地图可收集的器物图谱。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">八卦记录</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">八卦 类型</code>
<p class="desc">查询 818 / 616 / 避雷等八卦记录。</p>
</article>
<article class="cmd-card advanced-source">
<div class="cmd-top">
<div class="cmd-name">骗子查询</div>
<div class="source-tag">需令牌</div>
</div>
<code class="usage">骗子 QQ号</code>
<p class="desc">按 QQ 号查询骗子记录。</p>
</article>
<article class="cmd-card basic-source">
<div class="cmd-top">
<div class="cmd-name">随机骚话</div>
<div class="source-tag">免令牌</div>
</div>
<code class="usage">骚话</code>
<p class="desc">随机返回一句趣味骚话,用于群内互动。</p>
</article>
</div>
</div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>排行功能</h2>
</div>
<div class="section-meta">
<span class="mini">21 条</span>
<span class="mini">需令牌 21</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">名剑统计</div> <div class="cmd-name">名剑统计</div>
@@ -1132,27 +1101,10 @@ body {
<div class="cmd-name">名剑排行</div> <div class="cmd-name">名剑排行</div>
<div class="source-tag">需令牌</div> <div class="source-tag">需令牌</div>
</div> </div>
<code class="usage">排行 [数量] [模式]</code> <code class="usage">名剑排行 [数量] [模式]</code>
<p class="desc">查询名剑大会排名,支持 22 / 33 模式。</p> <p class="desc">查询名剑大会排名,支持 22 / 33 模式。</p>
</article> </article>
</div>
</div>
<div class="section">
<div class="section-head">
<div class="title-wrap">
<span class="icon"></span>
<h2>排行功能</h2>
</div>
<div class="section-meta">
<span class="mini">19 条</span>
<span class="mini">需令牌 19</span>
</div>
</div>
<div class="command-grid">
<article class="cmd-card advanced-source"> <article class="cmd-card advanced-source">
<div class="cmd-top"> <div class="cmd-top">
<div class="cmd-name">资历排行</div> <div class="cmd-name">资历排行</div>
@@ -1336,7 +1288,6 @@ body {
<div class="section-meta"> <div class="section-meta">
<span class="mini">4 条</span> <span class="mini">4 条</span>
<span class="mini">免令牌 4</span> <span class="mini">免令牌 4</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
@@ -1389,7 +1340,6 @@ body {
<div class="section-meta"> <div class="section-meta">
<span class="mini">5 条</span> <span class="mini">5 条</span>
<span class="mini">免令牌 5</span> <span class="mini">免令牌 5</span>
</div> </div>
</div> </div>
<div class="command-grid"> <div class="command-grid">
+206
View File
@@ -0,0 +1,206 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>资历查询</title>
<style>
body {
font-family: "Microsoft YaHei", Arial, sans-serif;
background: #f1f2f6;
margin: 0;
padding: 22px;
color: #262b36;
}
.container {
width: 980px;
margin: 0 auto;
}
.header {
background: #ffffff;
border-radius: 14px;
padding: 22px 26px;
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
margin-bottom: 16px;
}
.title-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
}
h1 {
margin: 0;
font-size: 32px;
line-height: 1.2;
color: #222;
font-weight: 800;
}
.role {
display: flex;
align-items: center;
gap: 10px;
color: #565d70;
font-size: 16px;
white-space: nowrap;
}
.force-icon {
width: 34px;
height: 34px;
border-radius: 50%;
object-fit: contain;
}
.meta {
margin-top: 12px;
color: #7a7f99;
font-size: 15px;
line-height: 1.7;
}
.summary {
background: #ffffff;
border-radius: 14px;
padding: 22px 26px;
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
margin-bottom: 16px;
}
.summary-top,
.item-top {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 14px;
}
.summary-name {
font-size: 24px;
font-weight: 800;
color: #222;
}
.summary-percent {
font-size: 34px;
line-height: 1;
font-weight: 900;
color: #d93939;
font-variant-numeric: tabular-nums;
}
.score {
margin-top: 8px;
color: #656d80;
font-size: 15px;
font-variant-numeric: tabular-nums;
}
.progress {
height: 14px;
margin-top: 14px;
border-radius: 999px;
overflow: hidden;
background: #e7e9ef;
}
.progress-fill {
height: 100%;
min-width: 3px;
border-radius: 999px;
background: linear-gradient(90deg, #d93939, #f59e42);
}
.list {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
.item {
background: #ffffff;
border-radius: 12px;
padding: 15px 16px;
box-shadow: 0 3px 12px rgba(0,0,0,0.06);
}
.item-name {
min-width: 0;
font-size: 17px;
font-weight: 800;
color: #303543;
word-break: break-word;
}
.item-percent {
color: #d93939;
font-size: 18px;
font-weight: 900;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.item .score {
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="title-row">
<h1>{{ title }}</h1>
<div class="role">
{% if icons.sect.get(force_name) %}
<img class="force-icon" src="{{ icons.sect[force_name] }}" alt="{{ force_name }}">
{% endif %}
<span>{{ role_name }}</span>
</div>
</div>
<div class="meta">
{{ zone }} · {{ server }} · {{ force_name }} · {{ camp_name }} · 帮会:{{ tong_name }}<br>
资历更新时间:{{ updated_at or "未知" }} · 查询时间:{{ update_time }}
</div>
</div>
<div class="summary">
<div class="summary-top">
<div class="summary-name">{{ summary.name }}</div>
<div class="summary-percent">{{ summary.percent_text }}</div>
</div>
<div class="score">
{{ summary.completed_points }} / {{ summary.total_points }} 点
· {{ summary.completed_count }} / {{ summary.achievement_count }} 项
</div>
<div class="progress">
<div class="progress-fill" style="width: {{ summary.percent }}%;"></div>
</div>
</div>
<div class="list">
{% for item in items %}
<div class="item">
<div class="item-top">
<div class="item-name">{{ item.name }}</div>
<div class="item-percent">{{ item.percent_text }}</div>
</div>
<div class="score">
{{ item.completed_points }} / {{ item.total_points }} 点
· {{ item.completed_count }} / {{ item.achievement_count }} 项
</div>
<div class="progress">
<div class="progress-fill" style="width: {{ item.percent }}%;"></div>
</div>
</div>
{% endfor %}
</div>
</div>
</body>
</html>