OK
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
## 更新记录
|
||||
|
||||
### version: 2.5:
|
||||
|
||||
增加部分新指令,调整功能页面
|
||||
|
||||
### version: 2.4:
|
||||
|
||||
增加大量新指令
|
||||
|
||||
@@ -1,45 +1,387 @@
|
||||
# 剑网三数据查询工具
|
||||
# astrbot_plugin_jx3
|
||||
|
||||
## 简介
|
||||
基于 [AstrBot](https://docs.astrbot.app/) 框架开发的剑网三游戏数据查询插件。插件主要通过 JX3API、JX3BOX、茶馆等外部接口获取游戏数据,整理为文本消息、富媒体消息,或通过 `HTML + Jinja2` 模板渲染成图片后发送。
|
||||
|
||||
仿照剑网三主流的机器人写了个插件,主要是自用,没有群管理相关的功能,在任何群聊都是使用的一套数据。
|
||||
当前插件以“查询型功能”为主,覆盖日常、奇遇、名剑、阵营、交易、职业、排行、副本、活动、名片、百战、推送和本地避雷等场景。部分功能需要配置 JX3API Token,少量职业/角色类接口还需要推栏 ticket。
|
||||
|
||||
插件功能绝大部分都是用AI写的,自己进行了调试。
|
||||
## 功能概览
|
||||
|
||||
目前此插件查询功能还没做完,后续会继续完善。
|
||||
插件内置 `功能` 指令,会以图片形式展示完整指令列表、参数说明和是否需要令牌。当前帮助页按 16 个功能分组维护,约 87 条指令。
|
||||
|
||||
此插件大部分数据依赖jx3api提供的接口,只有进阶功能需要向其购买token才可使用。其他功能是免token的。
|
||||
部分进阶功能除了购买token以外还需要自己去抓包推栏app,获得推栏app的ticket才可正常使用。
|
||||
主要功能包括:
|
||||
|
||||
## 功能特点
|
||||
- 日常与官方:日常、月历、名望、公告、维护、区服、开服、状态、技改。
|
||||
- 职业与角色:角色、阵眼、奇穴、技能、小药、宏、配装、资历排行、试炼排行。
|
||||
- 奇遇:角色奇遇、未做奇遇、奇遇统计、近期奇遇、奇遇汇总、奇遇攻略。
|
||||
- 名剑:战绩、名剑排行、名剑统计。
|
||||
- 阵营:沙盘、阵营拍卖、阵营事件、关隘首领、帮战记录、统战 YY。
|
||||
- 交易:金价、外观物价、交易行、贴吧物价。
|
||||
- 活动:扶摇、诛恶事件、本日赤兔、本周赤兔、刷马、的卢。
|
||||
- 其他:拜师、收徒、名片、百战、精耐、招募、掉落、解密、八卦、骗子查询。
|
||||
- 本地功能:避雷添加、查看、查询、修改、删除。
|
||||
- 推送功能:开服推送、新闻推送、刷马推送、赤兔推送。
|
||||
|
||||
- **后台循环请求**:通过后台一个任务循环请求接口,记录判断。推送最新消息状态
|
||||
- **处理接口数据**:将接口返回的数据处理成,玩家能读懂的消息发送
|
||||
- **HTML渲染图片**:利用astrbot里面的文转图服务,处理HTML页面渲染成图片
|
||||
## 安装与依赖
|
||||
|
||||
将插件目录放入 AstrBot 的插件目录中,例如:
|
||||
|
||||
```text
|
||||
data/plugins/astrbot_plugin_jx3
|
||||
```
|
||||
|
||||
安装依赖:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
当前依赖:
|
||||
|
||||
- `aiohttp`:异步 HTTP 请求。
|
||||
- `aiofiles`:异步文件处理。
|
||||
- `aiosqlite`:本地 SQLite 数据库。
|
||||
- `apscheduler`:后台推送任务调度。
|
||||
- `matplotlib`:部分图像/数据展示依赖。
|
||||
|
||||
插件元信息在 `metadata.yaml` 中维护,要求 AstrBot 版本 `>=4.11.0`。
|
||||
|
||||
## 插件配置
|
||||
|
||||
**指令前缀**
|
||||
配置结构由 `_conf_schema.json` 定义,主要配置项如下。
|
||||
|
||||
开启后必须要在指令功能前面加上下面配置的前缀才能正常触发。关闭后直接只有指令功能即可。
|
||||
### 指令前缀
|
||||
|
||||
**默认服务器**
|
||||
```json
|
||||
{
|
||||
"prefix": {
|
||||
"enable": true,
|
||||
"text": "剑三"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
配置后在所有功能指令中需要输入服务器参数的可以忽略不写,将按照配置的内容进行补充,填写官方名称。
|
||||
- `enable`:是否启用指令前缀检查。
|
||||
- `text`:指令前缀内容,默认 `剑三`。
|
||||
|
||||
**JX3API Token**
|
||||
启用后,用户需要发送类似 `剑三 日常`、`剑三 奇遇 角色名` 才能触发。关闭后可直接发送 `日常`、`奇遇 角色名`。
|
||||
|
||||
使用进阶的功能必须配置,购买在https://www.jx3api.com。
|
||||
### 默认服务器
|
||||
|
||||
**推栏ticket**
|
||||
```json
|
||||
{
|
||||
"server": "梦江南"
|
||||
}
|
||||
```
|
||||
|
||||
在进阶功能中,有部分功能依赖推栏APP的数据,所以需要自己抓包推栏的ticket。
|
||||
大致操作下载一个抓包工具,打开推栏APP,做一些查询操作。去抓包工具看看发送的请求,参数里面就有ticket。
|
||||
许多指令都带有 `[服务器]` 可选参数。用户未填写服务器时,插件会使用这里配置的默认服务器。
|
||||
|
||||
**推送功能**
|
||||
示例:
|
||||
|
||||
配置是否启用,本质就是不停的去接口问数据,数据又更新就推送出来。设定循环请求的间隔时间,不建议太短。下面配置推送的对话列表要要完整的会话ID。例如:QQ:GroupMessage:123456
|
||||
```text
|
||||
奇遇 飞翔大野猪
|
||||
```
|
||||
|
||||
等价于:
|
||||
|
||||
```text
|
||||
奇遇 飞翔大野猪 梦江南
|
||||
```
|
||||
|
||||
前提是默认服务器配置为 `梦江南`。
|
||||
|
||||
### JX3API Token
|
||||
|
||||
```json
|
||||
{
|
||||
"jx3api_token": ""
|
||||
}
|
||||
```
|
||||
|
||||
需要令牌的功能必须配置该字段。Token 需要在 [JX3API](https://www.jx3api.com/) 注册并购买后获取。
|
||||
|
||||
帮助页中标记为“需令牌”的指令,如果未配置 Token,可能无法正常返回数据。
|
||||
|
||||
### 推栏 Ticket
|
||||
|
||||
```json
|
||||
{
|
||||
"jx3api_ticket": ""
|
||||
}
|
||||
```
|
||||
|
||||
部分接口需要推栏 ticket,例如部分职业、角色、学校类接口。该值通常需要通过抓包推栏 App 的请求参数获得。
|
||||
|
||||
### 推送配置
|
||||
|
||||
插件支持 4 类后台推送:
|
||||
|
||||
- `kfts`:开服监控。
|
||||
- `xwts`:新闻资讯。
|
||||
- `smts`:刷马消息。
|
||||
- `ctts`:赤兔消息。
|
||||
|
||||
每类推送配置都包含:
|
||||
|
||||
- `enable`:是否启用。
|
||||
- `time`:轮询间隔,单位秒。
|
||||
- `umos`:推送目标会话 ID 列表。
|
||||
|
||||
示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"kfts": {
|
||||
"enable": false,
|
||||
"time": 60,
|
||||
"umos": ["QQ:GroupMessage:123456"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`umos` 需要填写完整会话 ID,可通过 AstrBot 的 `/std` 等方式获取。推送本质是后台定时请求接口,检测状态变化后向配置的会话发送消息,不建议将轮询间隔设置得过短。
|
||||
|
||||
## 使用方式
|
||||
|
||||
在聊天中输入 **功能** 即可获取插件所有的功能使用说明。
|
||||
发送:
|
||||
|
||||
```text
|
||||
功能
|
||||
```
|
||||
|
||||
即可获取完整指令帮助图。
|
||||
|
||||
如果开启了指令前缀,则发送:
|
||||
|
||||
```text
|
||||
剑三 功能
|
||||
```
|
||||
|
||||
指令参数说明:
|
||||
|
||||
- `[]` 表示可选参数。
|
||||
- `[服务器]` 未填写时使用插件默认服务器。
|
||||
- 数量、天数、模式等参数未填写时使用对应功能的默认值。
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
日常
|
||||
奇遇 飞翔大野猪
|
||||
未做奇遇 飞翔大野猪 梦江南
|
||||
战绩 飞翔大野猪 梦江南 33
|
||||
贴吧物价 狐金 5 梦江南
|
||||
统战 梦江南
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
|
||||
```text
|
||||
astrbot_plugin_jx3/
|
||||
├─ main.py # AstrBot 插件入口,初始化配置、资源、服务和指令分发
|
||||
├─ metadata.yaml # 插件元信息
|
||||
├─ _conf_schema.json # AstrBot 后台配置 schema
|
||||
├─ requirements.txt # Python 依赖
|
||||
├─ data/
|
||||
│ ├─ api_config.json # 外部接口配置,按功能 key 维护 URL、method、默认参数
|
||||
│ └─ plugin_data.db # 插件随包数据,主要用于内置数据
|
||||
├─ core/
|
||||
│ ├─ jx3_data.py # 业务数据层,调用接口、整理数据、选择模板
|
||||
│ ├─ message.py # 消息构建层,负责文本、图片、富媒体回复
|
||||
│ ├─ request.py # aiohttp 请求封装,统一处理 GET/POST 和接口返回
|
||||
│ ├─ async_task.py # APScheduler 后台推送任务
|
||||
│ ├─ bilei_data.py # 本地避雷数据增删改查
|
||||
│ ├─ sqlite.py # SQLite 异步封装
|
||||
│ └─ fun_basic.py # 模板加载、图片 base64、通用格式化工具
|
||||
└─ templates/
|
||||
├─ helps.html # 功能帮助页
|
||||
├─ *.html # 各图片指令的 HTML/Jinja2 模板
|
||||
├─ img/ # 通用图片资源
|
||||
├─ sect/ # 门派/心法图标
|
||||
└─ serendipity/ # 奇遇图标
|
||||
```
|
||||
|
||||
## 核心实现逻辑
|
||||
|
||||
### 1. 插件初始化
|
||||
|
||||
`main.py` 中的 `Jx3ApiPlugin` 是插件入口。
|
||||
|
||||
初始化时会完成以下工作:
|
||||
|
||||
1. 读取 AstrBot 插件配置,包括前缀、默认服务器、Token、Ticket 和推送配置。
|
||||
2. 计算本地数据目录、插件数据目录、模板目录和接口配置文件路径。
|
||||
3. 加载 `templates/img`、`templates/sect`、`templates/serendipity` 中的图片,并转为 base64,供 HTML 模板直接引用。
|
||||
4. 初始化 SQLite、JX3Service、AsyncTask、BiLeidata、MessageBuilder。
|
||||
5. 在异步初始化阶段创建本地表,并启动后台推送任务。
|
||||
6. 构建 `command_map`,将中文触发词映射到对应的消息处理函数。
|
||||
|
||||
### 2. 指令分发
|
||||
|
||||
用户消息进入插件后,`main.py` 会:
|
||||
|
||||
1. 根据配置判断是否需要指令前缀。
|
||||
2. 将消息按空格切分为 `指令 + 参数`。
|
||||
3. 在 `command_map` 中查找对应处理函数。
|
||||
4. 使用 `inspect.signature` 根据函数参数数量自动传入用户参数。
|
||||
5. 找不到指令时不处理,参数错误时返回提示。
|
||||
|
||||
### 3. 消息构建
|
||||
|
||||
`core/message.py` 的 `MessageBuilder` 负责把业务数据发出去,主要有几类输出:
|
||||
|
||||
- `plain_msg()`:发送纯文本。
|
||||
- `T2I_image_msg()`:把业务数据传入 HTML 模板,调用 AstrBot 的 HTML 渲染能力生成图片。
|
||||
- `image_msg()`:直接发送图片 URL 或图片数据。
|
||||
- `plain_chain()`:发送文本 + 图片等富媒体消息链。
|
||||
- `handler_plain_image_msg()`:先发文本,再发图片,适用于部分组合型功能。
|
||||
|
||||
所有可选服务器参数都会通过 `serverdefault()` 补齐默认服务器。
|
||||
|
||||
### 4. 数据请求与业务处理
|
||||
|
||||
`core/jx3_data.py` 是主要业务层。每个功能通常对应一个异步方法,例如:
|
||||
|
||||
```python
|
||||
async def jinqiqiyu(self, server: str) -> Dict[str, Any]:
|
||||
...
|
||||
```
|
||||
|
||||
业务方法通常遵循统一结构:
|
||||
|
||||
1. 调用 `_init_return_data()` 创建标准返回对象。
|
||||
2. 构造接口参数,填入 `server`、`name`、`token`、`ticket` 等。
|
||||
3. 通过 `_base_request(config_key, method, params)` 读取 `data/api_config.json` 中的接口配置并发起请求。
|
||||
4. 判断接口返回是否为空或结构异常。
|
||||
5. 整理时间戳、字段名、分组、列表、统计值等模板需要的数据。
|
||||
6. 文本功能直接写入 `return_data["data"]`。
|
||||
7. 图片功能额外加载对应 HTML 模板并写入 `return_data["temp"]`。
|
||||
8. 成功时设置 `return_data["code"] = 200`。
|
||||
|
||||
标准返回结构大致为:
|
||||
|
||||
```python
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "错误提示",
|
||||
"data": {},
|
||||
"temp": "",
|
||||
"icons": {}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 接口请求封装
|
||||
|
||||
`core/request.py` 中的 `APIClient` 负责实际 HTTP 请求。
|
||||
|
||||
实现特点:
|
||||
|
||||
- 复用 `aiohttp.ClientSession`。
|
||||
- 支持 GET 和 POST。
|
||||
- 自动识别 JSON、图片或二进制响应。
|
||||
- 对 JX3API 常见返回结构做基础校验。
|
||||
- 支持通过 `out_key` 抽取返回对象中的指定字段,默认通常取 `data`。
|
||||
|
||||
`JX3Service._base_request()` 会在业务层进一步统一:
|
||||
|
||||
- 根据 `data/api_config.json` 查找接口。
|
||||
- 合并接口默认参数和运行时参数。
|
||||
- 根据配置 method 调用 GET 或 POST。
|
||||
- 捕获异常并输出日志。
|
||||
|
||||
### 6. 图片渲染
|
||||
|
||||
图片输出功能使用 `templates/*.html`。
|
||||
|
||||
典型数据流:
|
||||
|
||||
```text
|
||||
JX3Service 整理数据
|
||||
↓
|
||||
MessageBuilder.T2I_image_msg()
|
||||
↓
|
||||
注入 icons
|
||||
↓
|
||||
html_renderer.render_custom_template()
|
||||
↓
|
||||
发送图片
|
||||
```
|
||||
|
||||
模板里可以使用:
|
||||
|
||||
- `icons.img`:通用图标。
|
||||
- `icons.sect`:门派/心法图标。
|
||||
- `icons.serendipity`:奇遇图标。
|
||||
|
||||
例如门派图标常见写法:
|
||||
|
||||
```jinja2
|
||||
{% set icon = icons.sect.get(item.forceName) %}
|
||||
{% if icon %}
|
||||
<img src="{{ icon }}">
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### 7. 本地数据库
|
||||
|
||||
插件使用两个 SQLite 数据库:
|
||||
|
||||
- 插件目录下的 `data/plugin_data.db`:随插件提供的内置数据。
|
||||
- AstrBot 数据目录中的 `local_data.db`:运行期本地数据。
|
||||
|
||||
本地数据主要用于:
|
||||
|
||||
- 避雷记录。
|
||||
- 推送任务状态缓存。
|
||||
|
||||
`core/sqlite.py` 封装了异步增删改查;`core/bilei_data.py` 基于该封装实现避雷数据管理。
|
||||
|
||||
### 8. 后台推送
|
||||
|
||||
`core/async_task.py` 使用 APScheduler 实现轮询推送。
|
||||
|
||||
当前支持:
|
||||
|
||||
- 开服监控。
|
||||
- 新闻资讯。
|
||||
- 刷马消息。
|
||||
- 赤兔消息。
|
||||
|
||||
推送逻辑是:
|
||||
|
||||
1. 初始化时读取配置和本地旧状态。
|
||||
2. 对启用且配置了推送目标的任务创建 interval job。
|
||||
3. 定时调用对应业务方法。
|
||||
4. 比较新旧状态。
|
||||
5. 状态变化时向配置会话发送消息,并更新本地状态。
|
||||
|
||||
## 新增功能开发流程
|
||||
|
||||
新增一个查询功能通常需要改动 4 到 5 个位置:
|
||||
|
||||
1. 在 `data/api_config.json` 添加接口配置。
|
||||
2. 在 `core/jx3_data.py` 添加业务方法,完成请求、数据整理和错误处理。
|
||||
3. 在 `core/message.py` 添加消息包装方法,选择文本、图片或富媒体输出。
|
||||
4. 在 `main.py` 的 `command_map` 注册中文触发指令。
|
||||
5. 如果是图片输出,新增 `templates/xxx.html`。
|
||||
6. 在 `templates/helps.html` 增加帮助卡片并更新统计。
|
||||
|
||||
建议保持以下约定:
|
||||
|
||||
- 方法名使用拼音或稳定英文名,避免与现有方法冲突。
|
||||
- 接口配置 key 使用 `jx3_功能名` 形式。
|
||||
- 文本功能失败时返回清晰的 `msg`。
|
||||
- 图片功能返回空数据时不要渲染空图,直接返回文本提示。
|
||||
- 可选服务器统一通过 `MessageBuilder.serverdefault()` 处理。
|
||||
- 时间戳尽量在业务层格式化后传给模板。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 本插件依赖多个外部接口,接口变更、网络异常、Token 权限不足都会影响结果。
|
||||
- JX3API 数据源可能存在延迟或遗漏,奇遇、掉落、排行榜等数据不保证 100% 完整。
|
||||
- Token 与 Ticket 属于敏感配置,不要提交到仓库或公开日志。
|
||||
- 后台推送会持续请求接口,轮询间隔不要设置过短。
|
||||
- HTML 模板渲染依赖 AstrBot 的文转图能力,运行环境需要支持对应渲染服务。
|
||||
|
||||
## License
|
||||
|
||||
本项目遵循仓库内 `LICENSE` 文件声明的许可协议。
|
||||
|
||||
@@ -971,6 +971,62 @@ class JX3Service:
|
||||
return return_data
|
||||
|
||||
|
||||
async def shilianpaixing(self, name: str, server: str) -> Dict[str, Any]:
|
||||
"""试炼排行"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {
|
||||
"server": server,
|
||||
"name": name,
|
||||
"token": self.token,
|
||||
}
|
||||
|
||||
data: Optional[Dict[str, Any]] = await self._base_request(
|
||||
"jx3_shilianpaixing", "GET", params=params
|
||||
)
|
||||
|
||||
if not data or not isinstance(data, dict):
|
||||
return_data["msg"] = "未查询到试炼排行信息"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
items = data.get("data", [])
|
||||
if not isinstance(items, list) or not items:
|
||||
return_data["msg"] = "未查询到试炼排行信息"
|
||||
return return_data
|
||||
|
||||
update_time = data.get("time")
|
||||
if update_time:
|
||||
try:
|
||||
update_time = datetime.fromtimestamp(int(update_time)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except (TypeError, ValueError, OSError):
|
||||
update_time = str(update_time)
|
||||
else:
|
||||
update_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
return_data["data"] = {
|
||||
"items": items,
|
||||
"name": data.get("name", name),
|
||||
"server": data.get("server", server),
|
||||
"update_time": update_time
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"处理试炼排行数据失败: {e}")
|
||||
return_data["msg"] = "处理试炼排行数据失败"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
return_data["temp"] = await load_template("shilianpaixing.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
return return_data
|
||||
|
||||
return_data["code"] = 200
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def shaohua(self) -> Dict[str, Any]:
|
||||
"""骚话"""
|
||||
return_data = self._init_return_data()
|
||||
@@ -994,6 +1050,47 @@ class JX3Service:
|
||||
return return_data
|
||||
|
||||
|
||||
async def jiemi(self) -> Dict[str, Any]:
|
||||
"""解密"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {"token": self.token}
|
||||
|
||||
data: Optional[Dict[str, Any]] = await self._base_request(
|
||||
"jx3_jiemi", "GET", params=params
|
||||
)
|
||||
|
||||
if not data or not isinstance(data, dict):
|
||||
return_data["msg"] = "未查询到解密信息"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
curr = data.get("curr", {})
|
||||
next_data = data.get("next", {})
|
||||
if not isinstance(curr, dict):
|
||||
curr = {}
|
||||
if not isinstance(next_data, dict):
|
||||
next_data = {}
|
||||
|
||||
return_data["data"] = "\n".join([
|
||||
"解密",
|
||||
f"当前时间:{data.get('time', '')}",
|
||||
f"当前节点:{curr.get('node', '')}",
|
||||
f"当前结果:{curr.get('data', '')}",
|
||||
f"下轮节点:{next_data.get('node', '')}",
|
||||
f"下轮结果:{next_data.get('data', '')}",
|
||||
f"剩余时间:{data.get('cdtn', '')}",
|
||||
])
|
||||
except Exception as e:
|
||||
logger.error(f"处理解密数据失败: {e}")
|
||||
return_data["msg"] = "处理解密数据失败"
|
||||
return return_data
|
||||
|
||||
return_data["code"] = 200
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def shapan(self, server: str ) -> Dict[str, Any]:
|
||||
"""区服沙盘"""
|
||||
return_data = self._init_return_data()
|
||||
@@ -1098,6 +1195,182 @@ class JX3Service:
|
||||
return return_data
|
||||
|
||||
|
||||
async def zhueevent(self) -> Dict[str, Any]:
|
||||
"""诛恶事件"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {"token": self.token}
|
||||
|
||||
data: Optional[List[Dict[str, Any]]] = await self._base_request(
|
||||
"jx3_zhueevent", "GET", params=params
|
||||
)
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
return_data["msg"] = "未查询到诛恶事件信息"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
items = []
|
||||
for item in data:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
event_time = item.get("time")
|
||||
if event_time:
|
||||
try:
|
||||
item["time"] = datetime.fromtimestamp(int(event_time)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except (TypeError, ValueError, OSError):
|
||||
item["time"] = str(event_time)
|
||||
else:
|
||||
item["time"] = ""
|
||||
|
||||
items.append(item)
|
||||
|
||||
if not items:
|
||||
return_data["msg"] = "未查询到诛恶事件信息"
|
||||
return return_data
|
||||
|
||||
return_data["data"] = {
|
||||
"items": items,
|
||||
"update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"处理诛恶事件数据失败: {e}")
|
||||
return_data["msg"] = "处理诛恶事件数据失败"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
return_data["temp"] = await load_template("zhueevent.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
return return_data
|
||||
|
||||
return_data["code"] = 200
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def benrichitu(self) -> Dict[str, Any]:
|
||||
"""本日赤兔"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {"token": self.token}
|
||||
|
||||
data: Optional[List[Dict[str, Any]]] = await self._base_request(
|
||||
"jx3_benrichitu", "GET", params=params
|
||||
)
|
||||
|
||||
def no_data() -> Dict[str, Any]:
|
||||
return_data["code"] = 200
|
||||
return_data["data"] = "本日赤兔 暂无数据"
|
||||
return return_data
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
return no_data()
|
||||
|
||||
try:
|
||||
items = []
|
||||
for item in data:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
date_value = item.get("date", "")
|
||||
if date_value:
|
||||
try:
|
||||
date_text = datetime.fromtimestamp(int(date_value)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except (TypeError, ValueError, OSError):
|
||||
date_text = str(date_value)
|
||||
else:
|
||||
date_text = ""
|
||||
|
||||
items.append({
|
||||
"date": date_text,
|
||||
"server": item.get("server", ""),
|
||||
"map_name": item.get("map_name", ""),
|
||||
})
|
||||
|
||||
if not items:
|
||||
return no_data()
|
||||
|
||||
result_lines = ["本日赤兔"]
|
||||
for item in items:
|
||||
result_lines.extend([
|
||||
f"时间:{item['date']}",
|
||||
f"区服:{item['server']}",
|
||||
f"地图:{item['map_name']}",
|
||||
"",
|
||||
])
|
||||
|
||||
return_data["code"] = 200
|
||||
return_data["data"] = "\n".join(result_lines).rstrip()
|
||||
except Exception as e:
|
||||
logger.error(f"处理本日赤兔数据失败: {e}")
|
||||
return no_data()
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def benzhouchitu(self) -> Dict[str, Any]:
|
||||
"""本周赤兔"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {"token": self.token}
|
||||
|
||||
data: Optional[List[Dict[str, Any]]] = await self._base_request(
|
||||
"jx3_benzhouchitu", "GET", params=params
|
||||
)
|
||||
|
||||
def no_data() -> Dict[str, Any]:
|
||||
return_data["code"] = 200
|
||||
return_data["data"] = "本周赤兔 暂无数据"
|
||||
return return_data
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
return no_data()
|
||||
|
||||
try:
|
||||
items = []
|
||||
for item in data:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
date_value = item.get("date", "")
|
||||
if date_value:
|
||||
try:
|
||||
date_text = datetime.fromtimestamp(int(date_value)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except (TypeError, ValueError, OSError):
|
||||
date_text = str(date_value)
|
||||
else:
|
||||
date_text = ""
|
||||
|
||||
items.append({
|
||||
"date": date_text,
|
||||
"server": item.get("server", ""),
|
||||
"map_name": item.get("map_name", ""),
|
||||
})
|
||||
|
||||
if not items:
|
||||
return no_data()
|
||||
|
||||
result_lines = ["本周赤兔"]
|
||||
for item in items:
|
||||
result_lines.extend([
|
||||
f"时间:{item['date']}",
|
||||
f"区服:{item['server']}",
|
||||
f"地图:{item['map_name']}",
|
||||
"",
|
||||
])
|
||||
|
||||
return_data["code"] = 200
|
||||
return_data["data"] = "\n".join(result_lines).rstrip()
|
||||
except Exception as e:
|
||||
logger.error(f"处理本周赤兔数据失败: {e}")
|
||||
return no_data()
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def zhengyingpaimai(self, server: str, name: str) -> Dict[str, Any]:
|
||||
"""阵营拍卖"""
|
||||
return_data = self._init_return_data()
|
||||
@@ -1138,6 +1411,299 @@ class JX3Service:
|
||||
return return_data
|
||||
|
||||
|
||||
async def zhenyingevent(self) -> Dict[str, Any]:
|
||||
"""阵营事件"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {"token": self.token}
|
||||
|
||||
data: Optional[List[Dict[str, Any]]] = await self._base_request(
|
||||
"jx3_zhenyingevent", "GET", params=params
|
||||
)
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
return_data["msg"] = "未查询到阵营事件信息"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
items = []
|
||||
for item in data:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
seize_time = item.get("seize_time")
|
||||
if seize_time:
|
||||
try:
|
||||
item["seize_time"] = datetime.fromtimestamp(int(seize_time)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except (TypeError, ValueError, OSError):
|
||||
item["seize_time"] = str(seize_time)
|
||||
else:
|
||||
item["seize_time"] = ""
|
||||
|
||||
items.append(item)
|
||||
|
||||
if not items:
|
||||
return_data["msg"] = "未查询到阵营事件信息"
|
||||
return return_data
|
||||
|
||||
return_data["data"] = {
|
||||
"items": items,
|
||||
"update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"处理阵营事件数据失败: {e}")
|
||||
return_data["msg"] = "处理阵营事件数据失败"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
return_data["temp"] = await load_template("zhenyingevent.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
return return_data
|
||||
|
||||
return_data["code"] = 200
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def guanaishouling(self) -> Dict[str, Any]:
|
||||
"""关隘首领"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {"token": self.token}
|
||||
|
||||
data: Optional[List[Dict[str, Any]]] = await self._base_request(
|
||||
"jx3_guanaishouling", "GET", params=params
|
||||
)
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
return_data["msg"] = "未查询到关隘首领信息"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
now_ts = int(datetime.now().timestamp())
|
||||
|
||||
def format_time(value: Any) -> str:
|
||||
if not value:
|
||||
return ""
|
||||
try:
|
||||
return datetime.fromtimestamp(int(value)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except (TypeError, ValueError, OSError):
|
||||
return str(value)
|
||||
|
||||
def format_remaining(end_time: Any) -> str:
|
||||
if not end_time:
|
||||
return ""
|
||||
try:
|
||||
seconds = max(0, int(end_time) - now_ts)
|
||||
except (TypeError, ValueError):
|
||||
return str(end_time)
|
||||
|
||||
hours = seconds // 3600
|
||||
minutes = (seconds % 3600) // 60
|
||||
secs = seconds % 60
|
||||
return f"{hours}时{minutes:02d}分{secs:02d}秒"
|
||||
|
||||
groups = []
|
||||
for group in data:
|
||||
if not isinstance(group, dict):
|
||||
continue
|
||||
|
||||
records = group.get("data", [])
|
||||
if not isinstance(records, list) or not records:
|
||||
continue
|
||||
|
||||
parsed_records = []
|
||||
for item in records:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
end_time = item.get("end_time")
|
||||
parsed_records.append({
|
||||
"camp_name": item.get("camp_name", ""),
|
||||
"castle": item.get("castle", ""),
|
||||
"str_status": item.get("str_status", ""),
|
||||
"start_time": format_time(item.get("start_time")),
|
||||
"end_time": format_time(end_time),
|
||||
"remaining_time": format_remaining(end_time),
|
||||
})
|
||||
|
||||
if parsed_records:
|
||||
groups.append({
|
||||
"server": group.get("server", ""),
|
||||
"records": parsed_records,
|
||||
})
|
||||
|
||||
if not groups:
|
||||
return_data["msg"] = "未查询到关隘首领信息"
|
||||
return return_data
|
||||
|
||||
return_data["data"] = {
|
||||
"groups": groups,
|
||||
"update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"处理关隘首领数据失败: {e}")
|
||||
return_data["msg"] = "处理关隘首领数据失败"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
return_data["temp"] = await load_template("guanaishouling.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
return return_data
|
||||
|
||||
return_data["code"] = 200
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def bangzhanjilu(self, server: str) -> Dict[str, Any]:
|
||||
"""帮战记录"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {
|
||||
"server": server,
|
||||
"token": self.token,
|
||||
}
|
||||
|
||||
data: Optional[List[Dict[str, Any]]] = await self._base_request(
|
||||
"jx3_bangzhanjilu", "GET", params=params
|
||||
)
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
return_data["msg"] = "未查询到帮战记录"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
def format_time(value: Any) -> str:
|
||||
if not value:
|
||||
return ""
|
||||
try:
|
||||
return datetime.fromtimestamp(int(value)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except (TypeError, ValueError, OSError):
|
||||
return str(value)
|
||||
|
||||
def format_duration(value: Any) -> str:
|
||||
if value in (None, ""):
|
||||
return ""
|
||||
try:
|
||||
seconds = max(0, int(value))
|
||||
except (TypeError, ValueError):
|
||||
return str(value)
|
||||
|
||||
hours = seconds // 3600
|
||||
minutes = (seconds % 3600) // 60
|
||||
secs = seconds % 60
|
||||
return f"{hours}时{minutes:02d}分{secs:02d}秒"
|
||||
|
||||
items = []
|
||||
for item in data:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
items.append({
|
||||
"zoneName": item.get("zoneName", ""),
|
||||
"serverName": item.get("serverName", ""),
|
||||
"declaringTongName": item.get("declaringTongName", ""),
|
||||
"acceptingTongName": item.get("acceptingTongName", ""),
|
||||
"startTime": format_time(item.get("startTime")),
|
||||
"matchDuration": format_duration(item.get("matchDuration")),
|
||||
"endTime": format_time(item.get("endTime")),
|
||||
})
|
||||
|
||||
if not items:
|
||||
return_data["msg"] = "未查询到帮战记录"
|
||||
return return_data
|
||||
|
||||
return_data["data"] = {
|
||||
"items": items,
|
||||
"server": server,
|
||||
"update_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"处理帮战记录数据失败: {e}")
|
||||
return_data["msg"] = "处理帮战记录数据失败"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
return_data["temp"] = await load_template("bangzhanjilu.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
return return_data
|
||||
|
||||
return_data["code"] = 200
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def tongzhanyy(self, server: str) -> Dict[str, Any]:
|
||||
"""统战歪歪"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
params = {
|
||||
"server": server,
|
||||
"token": self.token,
|
||||
}
|
||||
|
||||
data: Optional[List[Dict[str, Any]]] = await self._base_request(
|
||||
"jx3_tongzhanyy", "GET", params=params
|
||||
)
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
return_data["msg"] = "未查询到统战歪歪信息"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
lines = ["统战歪歪"]
|
||||
has_channel = False
|
||||
|
||||
for group in data:
|
||||
if not isinstance(group, dict):
|
||||
continue
|
||||
|
||||
group_server = group.get("server", "")
|
||||
channels = group.get("data", [])
|
||||
if not isinstance(channels, list) or not channels:
|
||||
continue
|
||||
|
||||
if len(lines) > 1:
|
||||
lines.append("")
|
||||
lines.append(f"服务器:{group_server}")
|
||||
|
||||
for item in channels:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
has_channel = True
|
||||
short_id = item.get("esid") or item.get("asid", "")
|
||||
lines.extend([
|
||||
f"阵营:{item.get('campName', '')}",
|
||||
f"频道ID:{item.get('sid', '')}",
|
||||
f"短位ID:{short_id}",
|
||||
f"在线人数:{item.get('users', '')}",
|
||||
f"频道名:{item.get('snick', '')}",
|
||||
"",
|
||||
])
|
||||
|
||||
if not has_channel:
|
||||
return_data["msg"] = "未查询到统战歪歪信息"
|
||||
return return_data
|
||||
|
||||
return_data["data"] = "\n".join(lines).rstrip()
|
||||
except Exception as e:
|
||||
logger.error(f"处理统战歪歪数据失败: {e}")
|
||||
return_data["msg"] = "处理统战歪歪数据失败"
|
||||
return return_data
|
||||
|
||||
return_data["code"] = 200
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def dilujilu(self, server: str) -> Dict[str, Any]:
|
||||
"""的卢记录"""
|
||||
return_data = self._init_return_data()
|
||||
@@ -2234,6 +2800,79 @@ class JX3Service:
|
||||
return return_data
|
||||
|
||||
|
||||
async def tiebawujia(self, name: str, limit: int = 5, server: str = "") -> Dict[str, Any]:
|
||||
"""贴吧物价"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
try:
|
||||
limit = int(limit)
|
||||
except (TypeError, ValueError):
|
||||
return_data["msg"] = "记录数量必须是数字"
|
||||
return return_data
|
||||
|
||||
if limit < 1 or limit > 50:
|
||||
return_data["msg"] = "贴吧物价记录数量必须在 1-50 之间"
|
||||
return return_data
|
||||
|
||||
params = {
|
||||
"server": server,
|
||||
"name": name,
|
||||
"limit": limit,
|
||||
"token": self.token,
|
||||
}
|
||||
|
||||
data: Optional[List[Dict[str, Any]]] = await self._base_request(
|
||||
"jx3_tiebawujia", "GET", params=params
|
||||
)
|
||||
|
||||
if not data or not isinstance(data, list):
|
||||
return_data["msg"] = "未查询到贴吧物价记录"
|
||||
return return_data
|
||||
|
||||
try:
|
||||
lines = [
|
||||
f"贴吧物价:{name}",
|
||||
f"服务器:{server}",
|
||||
f"记录数:{len(data)}",
|
||||
"",
|
||||
]
|
||||
|
||||
for index, item in enumerate(data, start=1):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
item_time = item.get("time", "")
|
||||
if item_time:
|
||||
try:
|
||||
item_time = datetime.fromtimestamp(int(item_time)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except (TypeError, ValueError, OSError):
|
||||
item_time = str(item_time)
|
||||
|
||||
lines.extend([
|
||||
f"{index}. {item.get('name', '')}",
|
||||
f"区服:{item.get('zone', '')} 服务器:{item.get('server', '')}",
|
||||
f"内容:{item.get('context', '')}",
|
||||
f"回复:{item.get('reply', '')} 楼层:{item.get('floor', '')}",
|
||||
f"时间:{item_time}",
|
||||
f"链接:https://tieba.baidu.com/p/{item.get('url', '')}",
|
||||
"",
|
||||
])
|
||||
|
||||
if len(lines) <= 4:
|
||||
return_data["msg"] = "未查询到贴吧物价记录"
|
||||
return return_data
|
||||
|
||||
return_data["data"] = "\n".join(lines).rstrip()
|
||||
except Exception as e:
|
||||
logger.error(f"处理贴吧物价数据失败: {e}")
|
||||
return_data["msg"] = "处理贴吧物价数据失败"
|
||||
return return_data
|
||||
|
||||
return_data["code"] = 200
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
async def diaoluo(self, name: str, limit: int = 20, server: str = "") -> Dict[str, Any]:
|
||||
"""物品掉落记录"""
|
||||
return_data = self._init_return_data()
|
||||
|
||||
@@ -282,11 +282,21 @@ class MessageBuilder:
|
||||
return await self.T2I_image_msg(event, lambda: self.jx3api.zilipaixing(school, self.serverdefault(server)))
|
||||
|
||||
|
||||
async def jx3_shilianpaixing(self, event: AstrMessageEvent, name: str, server: str = ""):
|
||||
"""剑三 试炼排行 心法 服务器"""
|
||||
return await self.T2I_image_msg(event, lambda: self.jx3api.shilianpaixing(name, self.serverdefault(server)))
|
||||
|
||||
|
||||
async def jx3_shaohua(self, event: AstrMessageEvent,):
|
||||
"""剑三 骚话"""
|
||||
return await self.plain_msg(event, self.jx3api.shaohua)
|
||||
|
||||
|
||||
async def jx3_jiemi(self, event: AstrMessageEvent):
|
||||
"""剑三 解密"""
|
||||
return await self.plain_msg(event, self.jx3api.jiemi)
|
||||
|
||||
|
||||
async def jx3_shapan(self, event: AstrMessageEvent,server: str = ""):
|
||||
"""剑三 沙盘 服务器"""
|
||||
return await self.image_msg(event, lambda: self.jx3api.shapan(self.serverdefault(server)))
|
||||
@@ -302,11 +312,46 @@ class MessageBuilder:
|
||||
return await self.plain_msg(event, lambda: self.jx3api.fuyaojjiutian( self.serverdefault(server)))
|
||||
|
||||
|
||||
async def jx3_zhueevent(self, event: AstrMessageEvent):
|
||||
"""剑三 诛恶事件"""
|
||||
return await self.T2I_image_msg(event, self.jx3api.zhueevent)
|
||||
|
||||
|
||||
async def jx3_benrichitu(self, event: AstrMessageEvent):
|
||||
"""剑三 本日赤兔"""
|
||||
return await self.plain_msg(event, self.jx3api.benrichitu)
|
||||
|
||||
|
||||
async def jx3_benzhouchitu(self, event: AstrMessageEvent):
|
||||
"""剑三 本周赤兔"""
|
||||
return await self.plain_msg(event, self.jx3api.benzhouchitu)
|
||||
|
||||
|
||||
async def jx3_zhengyingpaimai(self, event: AstrMessageEvent,name: str = "玄晶", server: str = ""):
|
||||
"""剑三 阵营拍卖 物品名称 服务器"""
|
||||
return await self.T2I_image_msg(event, lambda: self.jx3api.zhengyingpaimai( self.serverdefault(server), name))
|
||||
|
||||
|
||||
async def jx3_zhenyingevent(self, event: AstrMessageEvent):
|
||||
"""剑三 阵营事件"""
|
||||
return await self.T2I_image_msg(event, self.jx3api.zhenyingevent)
|
||||
|
||||
|
||||
async def jx3_guanaishouling(self, event: AstrMessageEvent):
|
||||
"""剑三 关隘首领"""
|
||||
return await self.T2I_image_msg(event, self.jx3api.guanaishouling)
|
||||
|
||||
|
||||
async def jx3_bangzhanjilu(self, event: AstrMessageEvent, server: str = ""):
|
||||
"""剑三 帮战记录 服务器"""
|
||||
return await self.T2I_image_msg(event, lambda: self.jx3api.bangzhanjilu(self.serverdefault(server)))
|
||||
|
||||
|
||||
async def jx3_tongzhanyy(self, event: AstrMessageEvent, server: str = ""):
|
||||
"""剑三 统战歪歪 服务器"""
|
||||
return await self.plain_msg(event, lambda: self.jx3api.tongzhanyy(self.serverdefault(server)))
|
||||
|
||||
|
||||
async def jx3_dilujilu(self, event: AstrMessageEvent,server: str = ""):
|
||||
"""剑三 的卢 服务器"""
|
||||
return await self.T2I_image_msg(event, lambda: self.jx3api.dilujilu( self.serverdefault(server)))
|
||||
@@ -520,6 +565,19 @@ class MessageBuilder:
|
||||
return await self.T2I_image_msg(event, lambda: self.jx3api.jiaoyihang(Name, self.serverdefault(server)))
|
||||
|
||||
|
||||
async def jx3_tiebawujia(self, event: AstrMessageEvent, name: str = "狐金", limit_or_server: str = "5", server: str = ""):
|
||||
"""剑三 贴吧物价 物品名称 数量 服务器"""
|
||||
limit = 5
|
||||
target_server = server
|
||||
|
||||
if str(limit_or_server).isdigit():
|
||||
limit = int(limit_or_server)
|
||||
else:
|
||||
target_server = limit_or_server
|
||||
|
||||
return await self.plain_msg(event, lambda: self.jx3api.tiebawujia(name, limit, self.serverdefault(target_server)))
|
||||
|
||||
|
||||
async def jx3_diaoluo(self, event: AstrMessageEvent, name: str = "玄晶", limit_or_server: str = "20", server: str = ""):
|
||||
"""剑三 掉落 物品名称 数量 服务器"""
|
||||
limit = 20
|
||||
|
||||
@@ -147,6 +147,16 @@
|
||||
"token":""
|
||||
}
|
||||
},
|
||||
"jx3_shilianpaixing":{
|
||||
"url":"https://www.jx3api.com/data/rank/trials",
|
||||
"method":"GET",
|
||||
"description":"查询指定心法的木桩试炼排行榜",
|
||||
"params":{
|
||||
"server": "梦江南",
|
||||
"name": "花间游",
|
||||
"token":""
|
||||
}
|
||||
},
|
||||
"jx3_shaohua":{
|
||||
"url":"https://www.jx3api.com/data/saohua/random",
|
||||
"method":"GET",
|
||||
@@ -155,6 +165,14 @@
|
||||
"name": "万花"
|
||||
}
|
||||
},
|
||||
"jx3_jiemi":{
|
||||
"url":"https://www.jx3api.com/data/mech/calculator",
|
||||
"method":"GET",
|
||||
"description":"查询机关解密结果",
|
||||
"params":{
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"aijx3_shapan":{
|
||||
"url":"https://www.jianxiachaguan.cn/api2/aijx3-jxcg/game/get-sand-table-img",
|
||||
"method":"POST",
|
||||
@@ -180,6 +198,30 @@
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_zhueevent":{
|
||||
"url":"https://www.jx3api.com/data/smite/records",
|
||||
"method":"GET",
|
||||
"description":"查询诛恶事件记录",
|
||||
"params":{
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_benrichitu":{
|
||||
"url":"https://www.jx3api.com/data/chitu/records",
|
||||
"method":"GET",
|
||||
"description":"查询本日赤兔记录",
|
||||
"params":{
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_benzhouchitu":{
|
||||
"url":"https://www.jx3api.com/data/chitu/week/records",
|
||||
"method":"GET",
|
||||
"description":"查询本周赤兔记录",
|
||||
"params":{
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_zhengyingpaimai":{
|
||||
"url":"https://www.jx3api.com/data/auction/records",
|
||||
"method":"GET",
|
||||
@@ -191,6 +233,40 @@
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_zhenyingevent":{
|
||||
"url":"https://www.jx3api.com/data/fenxian/records",
|
||||
"method":"GET",
|
||||
"description":"查询跨服阵营事件记录",
|
||||
"params":{
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_guanaishouling":{
|
||||
"url":"https://www.jx3api.com/data/mine/cart",
|
||||
"method":"GET",
|
||||
"description":"查询各服务器关隘首领刷新记录",
|
||||
"params":{
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_bangzhanjilu":{
|
||||
"url":"https://www.jx3api.com/data/battle/records",
|
||||
"method":"GET",
|
||||
"description":"查询指定服务器帮战记录",
|
||||
"params":{
|
||||
"server": "梦江南",
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_tongzhanyy":{
|
||||
"url":"https://www.jx3api.com/data/duowan/statistics",
|
||||
"method":"GET",
|
||||
"description":"查询指定服务器统战歪歪信息",
|
||||
"params":{
|
||||
"server": "梦江南",
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_dilujilu":{
|
||||
"url":"https://www.jx3api.com/data/steed/records",
|
||||
"method":"GET",
|
||||
@@ -424,6 +500,17 @@
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_tiebawujia":{
|
||||
"url":"https://www.jx3api.com/data/tieba/item/records",
|
||||
"method":"GET",
|
||||
"description":"查询指定物品的贴吧价格记录",
|
||||
"params":{
|
||||
"server": "梦江南",
|
||||
"name": "狐金",
|
||||
"limit": 5,
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"jx3_diaoluo":{
|
||||
"url":"https://www.jx3api.com/data/reward/statistics",
|
||||
"method":"GET",
|
||||
|
||||
@@ -17,7 +17,7 @@ from .core.fun_basic import load_as_base64
|
||||
@register("astrbot_plugin_jx3",
|
||||
"fxdyz",
|
||||
"通过调用剑网三API接口获取游戏数据,处理发送。",
|
||||
"2.3",
|
||||
"2.5",
|
||||
"https://github.com/qsc20001102/astrbot_plugin_jx3api"
|
||||
)
|
||||
class Jx3ApiPlugin(Star):
|
||||
@@ -204,14 +204,23 @@ class Jx3ApiPlugin(Star):
|
||||
"奇穴": self.jx3cmd.jx3_qixue,
|
||||
"技能": self.jx3cmd.jx3_jineng,
|
||||
"资历排行": self.jx3cmd.jx3_zilipaixing,
|
||||
"试炼排行": self.jx3cmd.jx3_shilianpaixing,
|
||||
"骚话": self.jx3cmd.jx3_shaohua,
|
||||
"解密": self.jx3cmd.jx3_jiemi,
|
||||
"沙盘": self.jx3cmd.jx3_shapan,
|
||||
"攻略": self.jx3cmd.jx3_qiyugonglue,
|
||||
"宏": self.jx3cmd.jx3_hong,
|
||||
"配装": self.jx3cmd.jx3_peizhuang,
|
||||
"百战": self.jx3cmd.jx3_baizhan,
|
||||
"扶摇": self.jx3cmd.jx3_fuyaojjiutian,
|
||||
"诛恶事件": self.jx3cmd.jx3_zhueevent,
|
||||
"本日赤兔": self.jx3cmd.jx3_benrichitu,
|
||||
"本周赤兔": self.jx3cmd.jx3_benzhouchitu,
|
||||
"拍卖": self.jx3cmd.jx3_zhengyingpaimai,
|
||||
"阵营事件": self.jx3cmd.jx3_zhenyingevent,
|
||||
"关隘首领": self.jx3cmd.jx3_guanaishouling,
|
||||
"帮战记录": self.jx3cmd.jx3_bangzhanjilu,
|
||||
"统战": self.jx3cmd.jx3_tongzhanyy,
|
||||
"的卢": self.jx3cmd.jx3_dilujilu,
|
||||
"烟花": self.jx3cmd.jx3_yanhuachaxun,
|
||||
"骗子": self.jx3cmd.jx3_pianzhi,
|
||||
@@ -253,6 +262,7 @@ class Jx3ApiPlugin(Star):
|
||||
"物价": self.jx3cmd.jx3_wujia,
|
||||
"八卦": self.jx3cmd.jx3_bagua,
|
||||
"交易行": self.jx3cmd.jx3_jiaoyihang,
|
||||
"贴吧物价": self.jx3cmd.jx3_tiebawujia,
|
||||
"掉落": self.jx3cmd.jx3_diaoluo,
|
||||
"开服推送": self.jx3cmd.jx3_kaifhujiank,
|
||||
"新闻推送": self.jx3cmd.jx3_xinwenzhixun,
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
name: astrbot_plugin_jx3
|
||||
display_name: 剑网三游戏数据查询工具
|
||||
desc: 通过接口调用剑网三API接口获取游戏数据,处理发送。
|
||||
version: v2.4
|
||||
version: v2.5
|
||||
author: 飞翔大野猪
|
||||
repo: https://github.com/qsc20001102/astrbot_plugin_jx3
|
||||
astrbot_version: ">=4.11.0"
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
<!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: 20px;
|
||||
color: #2c2c33;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1180px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 8px 0;
|
||||
color: #222;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-bottom: 24px;
|
||||
color: #7a7f99;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: #d93939;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 11px 9px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.zone-col,
|
||||
.server-col {
|
||||
width: 90px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tong-col {
|
||||
min-width: 150px;
|
||||
max-width: 220px;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.time-col {
|
||||
min-width: 148px;
|
||||
color: #60657a;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.duration-col {
|
||||
width: 120px;
|
||||
color: #d93939;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<h1>帮战记录</h1>
|
||||
<div class="meta">{{ server }} | 更新时间:{{ update_time }}</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>区服</th>
|
||||
<th>服务器</th>
|
||||
<th>宣战帮会</th>
|
||||
<th>应战帮会</th>
|
||||
<th>开始时间</th>
|
||||
<th>持续时间</th>
|
||||
<th>结束时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td class="zone-col">{{ item.zoneName }}</td>
|
||||
<td class="server-col">{{ item.serverName }}</td>
|
||||
<td class="tong-col">{{ item.declaringTongName }}</td>
|
||||
<td class="tong-col">{{ item.acceptingTongName }}</td>
|
||||
<td class="time-col">{{ item.startTime }}</td>
|
||||
<td class="duration-col">{{ item.matchDuration }}</td>
|
||||
<td class="time-col">{{ item.endTime }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,177 @@
|
||||
<!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: 20px;
|
||||
color: #2c2c33;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1180px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 8px 0;
|
||||
color: #222;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-bottom: 24px;
|
||||
color: #7a7f99;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 0 0 12px 0;
|
||||
color: #222;
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.section-title::before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 5px;
|
||||
height: 28px;
|
||||
background: #d93939;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: #d93939;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 11px 9px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
tbody tr.camp-eren {
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
tbody tr.camp-haoqi {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.camp-col,
|
||||
.status-col {
|
||||
width: 90px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.castle-col {
|
||||
min-width: 120px;
|
||||
font-weight: 800;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
tr.camp-eren .castle-col {
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
tr.camp-haoqi .castle-col {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.time-col,
|
||||
.remain-col {
|
||||
min-width: 150px;
|
||||
color: #60657a;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.remain-col {
|
||||
color: #d93939;
|
||||
font-weight: 800;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<h1>关隘首领</h1>
|
||||
<div class="meta">更新时间:{{ update_time }}</div>
|
||||
|
||||
{% for group in groups %}
|
||||
<div class="section">
|
||||
<div class="section-title">{{ group.server }}</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>归属</th>
|
||||
<th>关隘名称</th>
|
||||
<th>状态</th>
|
||||
<th>开始时间</th>
|
||||
<th>结束时间</th>
|
||||
<th>剩余时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for item in group.records %}
|
||||
<tr
|
||||
{% if item.camp_name == '恶人谷' %}
|
||||
class="camp-eren"
|
||||
{% elif item.camp_name == '浩气盟' %}
|
||||
class="camp-haoqi"
|
||||
{% endif %}
|
||||
>
|
||||
<td class="camp-col">{{ item.camp_name }}</td>
|
||||
<td class="castle-col">{{ item.castle }}</td>
|
||||
<td class="status-col">{{ item.str_status }}</td>
|
||||
<td class="time-col">{{ item.start_time }}</td>
|
||||
<td class="time-col">{{ item.end_time }}</td>
|
||||
<td class="remain-col">{{ item.remaining_time }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+104
-14
@@ -321,13 +321,13 @@ body {
|
||||
.usage {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
padding: 5px 8px;
|
||||
padding: 6px 9px;
|
||||
border-radius: 9px;
|
||||
background: rgba(23, 32, 51, 0.055);
|
||||
color: #24324f;
|
||||
font-family: Consolas, "SFMono-Regular", "Courier New", monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -369,7 +369,7 @@ body {
|
||||
<h1>剑三机器人功能指令</h1>
|
||||
<p>按使用场景重新分组,保留每条指令的名称、使用示例与说明。卡片底色表示指令是否需要令牌,免令牌与需令牌功能一眼区分。</p>
|
||||
<div class="hero-stats">
|
||||
<div class="stat"><b>77</b><span>条指令</span></div>
|
||||
<div class="stat"><b>87</b><span>条指令</span></div>
|
||||
<div class="stat"><b>16</b><span>个功能分组</span></div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -491,9 +491,9 @@ body {
|
||||
<h2>杂项功能</h2>
|
||||
</div>
|
||||
<div class="section-meta">
|
||||
<span class="mini">6 条</span>
|
||||
<span class="mini">7 条</span>
|
||||
<span class="mini">免令牌 4</span>
|
||||
<span class="mini">需令牌 2</span>
|
||||
<span class="mini">需令牌 3</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="command-grid">
|
||||
@@ -543,6 +543,15 @@ body {
|
||||
<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>
|
||||
@@ -633,9 +642,9 @@ body {
|
||||
<h2>交易功能</h2>
|
||||
</div>
|
||||
<div class="section-meta">
|
||||
<span class="mini">3 条</span>
|
||||
<span class="mini">4 条</span>
|
||||
|
||||
<span class="mini">需令牌 3</span>
|
||||
<span class="mini">需令牌 4</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="command-grid">
|
||||
@@ -667,6 +676,15 @@ body {
|
||||
<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>
|
||||
|
||||
@@ -801,9 +819,9 @@ body {
|
||||
<h2>阵营功能</h2>
|
||||
</div>
|
||||
<div class="section-meta">
|
||||
<span class="mini">2 条</span>
|
||||
<span class="mini">6 条</span>
|
||||
<span class="mini">免令牌 1</span>
|
||||
<span class="mini">需令牌 1</span>
|
||||
<span class="mini">需令牌 5</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="command-grid">
|
||||
@@ -826,6 +844,42 @@ body {
|
||||
<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">查询指定服务器阵营统战 YY 信息。</p>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -977,9 +1031,9 @@ body {
|
||||
<h2>活动功能</h2>
|
||||
</div>
|
||||
<div class="section-meta">
|
||||
<span class="mini">3 条</span>
|
||||
<span class="mini">6 条</span>
|
||||
|
||||
<span class="mini">需令牌 3</span>
|
||||
<span class="mini">需令牌 6</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="command-grid">
|
||||
@@ -993,6 +1047,33 @@ body {
|
||||
<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>
|
||||
@@ -1065,9 +1146,9 @@ body {
|
||||
<h2>排行功能</h2>
|
||||
</div>
|
||||
<div class="section-meta">
|
||||
<span class="mini">18 条</span>
|
||||
<span class="mini">19 条</span>
|
||||
|
||||
<span class="mini">需令牌 18</span>
|
||||
<span class="mini">需令牌 19</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="command-grid">
|
||||
@@ -1081,6 +1162,15 @@ body {
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<!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: 20px;
|
||||
color: #2c2c33;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 8px 0;
|
||||
color: #222;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-bottom: 24px;
|
||||
color: #7a7f99;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: #d93939;
|
||||
color: white;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.rank-col,
|
||||
.number-col {
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.rank-col {
|
||||
width: 60px;
|
||||
color: #d93939;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.role-col {
|
||||
min-width: 180px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.score-col {
|
||||
color: #d93939;
|
||||
font-weight: 800;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<h1>{{ name }} 试炼排行</h1>
|
||||
<div class="meta">{{ server }} | 更新时间:{{ update_time }}</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>排名</th>
|
||||
<th>角色名</th>
|
||||
<th>装备分数</th>
|
||||
<th>最高层数</th>
|
||||
<th>排名积分</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td class="rank-col">{{ loop.index }}</td>
|
||||
<td class="role-col">{{ item.role_name }}</td>
|
||||
<td class="number-col">{{ item.equip_score }}</td>
|
||||
<td class="number-col">{{ item.max_level }}</td>
|
||||
<td class="number-col score-col">{{ item.total_score }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,151 @@
|
||||
<!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: 20px;
|
||||
color: #2c2c33;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1080px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 8px 0;
|
||||
color: #222;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-bottom: 24px;
|
||||
color: #7a7f99;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: #d93939;
|
||||
color: white;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
tbody tr.camp-eren {
|
||||
background: #fff2f2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
tbody tr.camp-haoqi {
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.camp-col {
|
||||
width: 90px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.fenxian-col,
|
||||
.server-col {
|
||||
width: 130px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.role-col {
|
||||
max-width: 320px;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
tr.camp-eren .role-col {
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
tr.camp-haoqi .role-col {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.time-col {
|
||||
min-width: 150px;
|
||||
color: #60657a;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<h1>阵营事件</h1>
|
||||
<div class="meta">更新时间:{{ update_time }}</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>阵营</th>
|
||||
<th>分线</th>
|
||||
<th>服务器</th>
|
||||
<th>角色名称</th>
|
||||
<th>时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for item in items %}
|
||||
<tr
|
||||
{% if item.camp_name == '恶人谷' %}
|
||||
class="camp-eren"
|
||||
{% elif item.camp_name == '浩气盟' %}
|
||||
class="camp-haoqi"
|
||||
{% endif %}
|
||||
>
|
||||
<td class="camp-col">{{ item.camp_name }}</td>
|
||||
<td class="fenxian-col">{{ item.fenxian_name }}</td>
|
||||
<td class="server-col">{{ item.friend_name }}</td>
|
||||
<td class="role-col">{{ item.role_name }}</td>
|
||||
<td class="time-col">{{ item.seize_time }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,116 @@
|
||||
<!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: 20px;
|
||||
color: #2c2c33;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
margin: 0 0 8px 0;
|
||||
color: #222;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin-bottom: 24px;
|
||||
color: #7a7f99;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: #d93939;
|
||||
color: white;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.server-col {
|
||||
width: 160px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-weight: 800;
|
||||
color: #d93939;
|
||||
}
|
||||
|
||||
.map-col {
|
||||
max-width: 300px;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.time-col {
|
||||
min-width: 160px;
|
||||
color: #60657a;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
<h1>诛恶事件</h1>
|
||||
<div class="meta">更新时间:{{ update_time }}</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>服务器</th>
|
||||
<th>地图</th>
|
||||
<th>时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td class="server-col">{{ item.server }}</td>
|
||||
<td class="map-col">{{ item.map_name }}</td>
|
||||
<td class="time-col">{{ item.time }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user