ok
This commit is contained in:
@@ -1,9 +1,46 @@
|
|||||||
# Gok
|
# 王者数据查询工具
|
||||||
|
|
||||||
AstrBot 插件模板
|
## 目录
|
||||||
|
|
||||||
A template plugin for AstrBot plugin feature
|
- [王者荣耀数据查询工具](#-王者荣耀数据查询工具)
|
||||||
|
- [目录](#-目录)
|
||||||
|
- [简介](#-简介)
|
||||||
|
- [更新记录](#-更新记录)
|
||||||
|
- [功能特点](#-功能特点)
|
||||||
|
- [插件配置](#-插件配置)
|
||||||
|
- [使用方式](#-使用方式)
|
||||||
|
|
||||||
# 支持
|
## 简介
|
||||||
|
|
||||||
[帮助文档](https://astrbot.app)
|
主要是自用,查询战绩,在游戏中隐藏的战绩也可以查。
|
||||||
|
|
||||||
|
插件功能绝大部分都是用AI写的,又进行了调试。
|
||||||
|
|
||||||
|
插件数据依赖应天API提供接口,需要自行支持创建token密钥才可使用。(每天免费10000次调用)
|
||||||
|
|
||||||
|
## 更新记录
|
||||||
|
|
||||||
|
version: 1.0.0:大部分重要功能已经实现
|
||||||
|
|
||||||
|
## 功能特点
|
||||||
|
|
||||||
|
- **处理接口数据**:将接口返回的数据处理成,玩家能读懂的消息发送
|
||||||
|
- **HTML渲染图片**:利用astrbot里面的文转图服务,处理HTML页面渲染成图片
|
||||||
|
|
||||||
|
## 插件配置
|
||||||
|
|
||||||
|
**指令前缀**
|
||||||
|
|
||||||
|
开启后必须要在指令功能前面加上下面配置的前缀才能正常触发。关闭后直接只有指令功能即可。
|
||||||
|
|
||||||
|
**ytapi_token**
|
||||||
|
|
||||||
|
前往https://api.t1qq.com 注册并创建Token(免费)
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
在聊天中输入 **王者功能** 即可获取插件所有的功能使用说明。
|
||||||
|
|
||||||
|
在使用 **王者战绩** 和 **王者资料** 两个功能时后面可以直接输入营地ID进行查询,或者输入提前自定义好的角色来查询。
|
||||||
|
|
||||||
|
指令 **角色查看**、**角色添加**、**角色修改**、**角色删除**、**角色查询** 就是用来操作角色数据的,给王者营地ID起一个别名,方便自己记忆,也方便查询。
|
||||||
+24
-1
@@ -1,3 +1,26 @@
|
|||||||
{
|
{
|
||||||
|
"prefix": {
|
||||||
|
"description": "指令前缀",
|
||||||
|
"type": "object",
|
||||||
|
"items": {
|
||||||
|
"enable": {
|
||||||
|
"description": "是否启用",
|
||||||
|
"type": "bool",
|
||||||
|
"default": false,
|
||||||
|
"hint": "是否启用指令前缀检查功能"
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"description": "前缀内容",
|
||||||
|
"type": "string",
|
||||||
|
"default": "王者荣耀",
|
||||||
|
"hint": "为了避免和其他插件的指令冲突可以增加指令的前缀,为空默认值为王者。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ytapi_token": {
|
||||||
|
"description": "应天API Token",
|
||||||
|
"type": "string",
|
||||||
|
"default": "",
|
||||||
|
"hint": "前往https://api.t1qq.com注册并创建Token(免费)。"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,300 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
import base64
|
|
||||||
from astrbot.api import logger
|
|
||||||
|
|
||||||
from .request import APIClient
|
|
||||||
from .async_mysql import AsyncMySQL
|
|
||||||
from .function_basic import load_template,extract_fields
|
|
||||||
|
|
||||||
class WZRYFunction:
|
|
||||||
def __init__(self, api_config,db: AsyncMySQL ):
|
|
||||||
self.__api = APIClient()
|
|
||||||
self.__db = db
|
|
||||||
self.__api_config = api_config
|
|
||||||
|
|
||||||
async def _SELECT_ID(self,name: str):
|
|
||||||
"""
|
|
||||||
查询ID
|
|
||||||
"""
|
|
||||||
sql = "SELECT id FROM wzydid WHERE name = %s or id = %s"
|
|
||||||
sqlid = await self.__db.fetch_one(sql, (name,name))
|
|
||||||
return sqlid["id"] if sqlid else None
|
|
||||||
|
|
||||||
async def all_user(self):
|
|
||||||
"""
|
|
||||||
查询所有用户
|
|
||||||
"""
|
|
||||||
sql = "SELECT id,name FROM wzydid"
|
|
||||||
sqlid = await self.__db.fetch_all(sql)
|
|
||||||
return_data = "营地ID\t\t昵称\n"
|
|
||||||
for m in sqlid:
|
|
||||||
return_data += f"{m['id']}\t{m['name']}\n"
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
async def add_user(self,id: str ,name: str):
|
|
||||||
"""
|
|
||||||
添加用户
|
|
||||||
"""
|
|
||||||
self_id = await self._SELECT_ID(id)
|
|
||||||
if self_id is not None:
|
|
||||||
return "该用户已存在,无需重复添加"
|
|
||||||
sql = "INSERT INTO wzydid (id,name) VALUES (%s, %s)"
|
|
||||||
rowcount = await self.__db.execute(sql, (id,name))
|
|
||||||
if rowcount > 0:
|
|
||||||
return f"id:{id}昵称:{name}\n添加成功"
|
|
||||||
else:
|
|
||||||
return f"id:{id}昵称:{name}\n添加失败,请稍后再试"
|
|
||||||
|
|
||||||
async def update_user(self,id: str ,name: str):
|
|
||||||
"""
|
|
||||||
更新用户
|
|
||||||
"""
|
|
||||||
self_id = await self._SELECT_ID(id)
|
|
||||||
if self_id is None:
|
|
||||||
return "该用户不存在,请先添加用户"
|
|
||||||
sql = "UPDATE wzydid SET name = %s WHERE id = %s"
|
|
||||||
rowcount = await self.__db.execute(sql, (name,id))
|
|
||||||
if rowcount > 0:
|
|
||||||
return f"id:{id}昵称:{name}\n更新成功"
|
|
||||||
else:
|
|
||||||
return f"id:{id}昵称:{name}\n更新失败,请稍后再试"
|
|
||||||
|
|
||||||
async def delete_user(self,id: str ):
|
|
||||||
"""
|
|
||||||
删除用户
|
|
||||||
"""
|
|
||||||
self_id = await self._SELECT_ID(id)
|
|
||||||
if self_id is None:
|
|
||||||
return "该用户不存在,无需删除"
|
|
||||||
sql = "DELETE FROM wzydid WHERE id = %s"
|
|
||||||
rowcount = await self.__db.execute(sql, (id,))
|
|
||||||
if rowcount > 0:
|
|
||||||
return f"id:{id}\n删除成功"
|
|
||||||
else:
|
|
||||||
return f"id:{id}\n删除失败,请稍后再试"
|
|
||||||
|
|
||||||
async def zhanji(self,name: str ,option: str):
|
|
||||||
"""
|
|
||||||
战绩查询
|
|
||||||
"""
|
|
||||||
return_data = {
|
|
||||||
"code": 0,
|
|
||||||
"msg": "功能函数未执行",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
#在配置文件中获取接口配置
|
|
||||||
api_config = self.__api_config["wzry_zhanji"]
|
|
||||||
# ID查询
|
|
||||||
sql_id = await self._SELECT_ID(name)
|
|
||||||
if sql_id is None:
|
|
||||||
return_data["msg"] = "未查询到该用户,请确认输入正确的昵称或营地ID"
|
|
||||||
return return_data
|
|
||||||
#更新参数
|
|
||||||
api_config["params"]["id"] = sql_id
|
|
||||||
api_config["params"]["option"] = option
|
|
||||||
# 需要提取的字段
|
|
||||||
fields = ["gametime","killcnt","deadcnt","assistcnt","gameresult","mvpcnt","losemvp","mapName",
|
|
||||||
"oldMasterMatchScore","newMasterMatchScore","usedTime","winNum","failNum","roleJobName","stars","desc",
|
|
||||||
"gradeGame","heroIcon","godLikeCnt", "firstBlood","hero1TripleKillCnt","hero1UltraKillCnt","hero1RampageCnt","evaluateUrlV3","mvpUrlV3"]
|
|
||||||
# 处理返回数据
|
|
||||||
try:
|
|
||||||
# 获取数据
|
|
||||||
data = await self.__api.get(api_config["url"],api_config["params"],"data")
|
|
||||||
if not data:
|
|
||||||
return_data["msg"] = "获取接口信息失败"
|
|
||||||
return return_data
|
|
||||||
# 提取字段
|
|
||||||
result = extract_fields(data["list"], fields)
|
|
||||||
result = result[:25]
|
|
||||||
# 数据处理
|
|
||||||
for m in result:
|
|
||||||
minutes = m["usedTime"] // 60
|
|
||||||
seconds = m["usedTime"] % 60
|
|
||||||
m["time_str"] = f"{minutes}:{seconds:02d}"
|
|
||||||
|
|
||||||
return_data["data"] = result
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"处理数据时出错: {e}")
|
|
||||||
return_data["msg"] = "处理接口返回信息时出错"
|
|
||||||
# 加载模板
|
|
||||||
try:
|
|
||||||
return_data["temp"] = load_template("wangzhezhanji.html")
|
|
||||||
except FileNotFoundError as e:
|
|
||||||
logger.error(f"加载模板失败: {e}")
|
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
|
||||||
return return_data
|
|
||||||
return_data["code"] = 200
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
async def ziliao(self, name: str):
|
|
||||||
return_data = {
|
|
||||||
"code": 0,
|
|
||||||
"msg": "功能函数未执行",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
# 查询 ID
|
|
||||||
sql_id = await self._SELECT_ID(name)
|
|
||||||
|
|
||||||
if sql_id is None:
|
|
||||||
return_data["msg"] = "未查询到该用户,请确认输入正确的昵称或营地ID"
|
|
||||||
return return_data
|
|
||||||
api_config = self.__api_config["wzry_ziliao"]
|
|
||||||
api_config["params"]["id"] = sql_id
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = await self.__api.get(api_config["url"], api_config["params"])
|
|
||||||
# 转 base64
|
|
||||||
return_data["data"]["img_base64"] = base64.b64encode(data).decode("utf-8")
|
|
||||||
return_data["code"] = 200
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"处理数据时出错: {e}")
|
|
||||||
return_data["msg"] = "处理接口返回信息时出错"
|
|
||||||
|
|
||||||
# 加载模板
|
|
||||||
try:
|
|
||||||
return_data["temp"] = load_template("wzry_zl.html")
|
|
||||||
except FileNotFoundError:
|
|
||||||
logger.error(f"加载模板失败")
|
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
|
||||||
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
async def bilei_all(self):
|
|
||||||
"""
|
|
||||||
查询仇人列表
|
|
||||||
"""
|
|
||||||
return_data = {
|
|
||||||
"code": 0,
|
|
||||||
"msg": "功能函数未执行",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
# 查询所有
|
|
||||||
try:
|
|
||||||
sql = "SELECT * FROM wzrybl"
|
|
||||||
sqlid = await self.__db.fetch_all(sql)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"SQL执行错误: {e}")
|
|
||||||
return_data["msg"] = "处理数据库信息时出错"
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
# 加载模板
|
|
||||||
try:
|
|
||||||
return_data["temp"] = load_template("bilei.html")
|
|
||||||
except FileNotFoundError as e:
|
|
||||||
logger.error(f"加载模板失败: {e}")
|
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
|
||||||
return return_data
|
|
||||||
return_data["data"]["lists"] = sqlid
|
|
||||||
return_data["code"] = 200
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
async def bilei_add(self,name:str, text:str, user:str):
|
|
||||||
"""
|
|
||||||
仇人 添加
|
|
||||||
"""
|
|
||||||
return_data = {
|
|
||||||
"code": 0,
|
|
||||||
"msg": "功能函数未执行",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
# 获取系统时间
|
|
||||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
# 插入数据
|
|
||||||
try:
|
|
||||||
sql = "INSERT INTO wzrybl (name, text, time, user) VALUES (%s, %s, %s, %s)"
|
|
||||||
await self.__db.execute(sql, (name, text, now, user))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"SQL执行错误: {e}")
|
|
||||||
return_data["msg"] = "处理数据库信息时出错"
|
|
||||||
return return_data
|
|
||||||
return_data["msg"] = "添加成功\n"
|
|
||||||
return_data["msg"] += "仇人名称:"+name+"\n"
|
|
||||||
return_data["msg"] += "仇人备注:"+text+"\n"
|
|
||||||
return_data["msg"] += "添加时间:"+now+"\n"
|
|
||||||
return_data["msg"] += "记录人:"+user+"\n"
|
|
||||||
return_data["code"] = 200
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
async def bilei_select(self,name:str):
|
|
||||||
"""
|
|
||||||
仇人 查找
|
|
||||||
"""
|
|
||||||
return_data = {
|
|
||||||
"code": 0,
|
|
||||||
"msg": "功能函数未执行",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
# 查询数据
|
|
||||||
try:
|
|
||||||
sql = "SELECT * FROM wzrybl WHERE name LIKE %s"
|
|
||||||
sqlid = await self.__db.fetch_all(sql, ("%"+name+"%",))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"SQL执行错误: {e}")
|
|
||||||
return_data["msg"] = "处理数据库信息时出错"
|
|
||||||
return return_data
|
|
||||||
# 加载模板
|
|
||||||
try:
|
|
||||||
return_data["temp"] = load_template("bilei.html")
|
|
||||||
except FileNotFoundError as e:
|
|
||||||
logger.error(f"加载模板失败: {e}")
|
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
|
||||||
return return_data
|
|
||||||
return_data["data"]["lists"] = sqlid
|
|
||||||
return_data["code"] = 200
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
|
|
||||||
async def bilei_update(self, id:int, name:str, text:str, user:str):
|
|
||||||
"""
|
|
||||||
仇人 修改 ID 名称 备注
|
|
||||||
"""
|
|
||||||
return_data = {
|
|
||||||
"code": 0,
|
|
||||||
"msg": "功能函数未执行",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
# 获取系统时间
|
|
||||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
# 查询数据
|
|
||||||
try:
|
|
||||||
sql = "UPDATE wzrybl SET name = %s, text = %s, time = %s, user = %s WHERE id = %s"
|
|
||||||
rowcount = await self.__db.execute(sql, (name, text, now, user, id))
|
|
||||||
if rowcount > 0:
|
|
||||||
return_data["msg"] = "修改成功\n"
|
|
||||||
return_data["msg"] += "仇人名称:"+name+"\n"
|
|
||||||
return_data["msg"] += "仇人备注:"+text+"\n"
|
|
||||||
return_data["msg"] += "修改时间:"+now+"\n"
|
|
||||||
return_data["msg"] += "记录人:"+user+"\n"
|
|
||||||
return_data["code"] = 200
|
|
||||||
else:
|
|
||||||
return_data["msg"] = "修改失败,未找到对应记录"
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"SQL执行错误: {e}")
|
|
||||||
return_data["msg"] = "处理数据库信息时出错"
|
|
||||||
return return_data
|
|
||||||
return return_data
|
|
||||||
|
|
||||||
async def bilei_delete(self, id:int):
|
|
||||||
"""
|
|
||||||
仇人 删除 ID
|
|
||||||
"""
|
|
||||||
return_data = {
|
|
||||||
"code": 0,
|
|
||||||
"msg": "功能函数未执行",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
# 查询数据
|
|
||||||
try:
|
|
||||||
sql = "DELETE FROM wzrybl WHERE id = %s"
|
|
||||||
rowcount = await self.__db.execute(sql, (id,))
|
|
||||||
if rowcount > 0:
|
|
||||||
return_data["msg"] = "删除成功\n"
|
|
||||||
return_data["code"] = 200
|
|
||||||
else:
|
|
||||||
return_data["msg"] = "删除失败,未找到对应记录"
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"SQL执行错误: {e}")
|
|
||||||
return_data["msg"] = "处理数据库信息时出错"
|
|
||||||
return return_data
|
|
||||||
return return_data
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,90 +0,0 @@
|
|||||||
import aiomysql
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncMySQL:
|
|
||||||
def __init__(self, db_config: dict):
|
|
||||||
self.db_config = db_config
|
|
||||||
self.pool = None
|
|
||||||
|
|
||||||
async def init_pool(self):
|
|
||||||
"""初始化连接池"""
|
|
||||||
if self.pool is None:
|
|
||||||
self.pool = await aiomysql.create_pool(**self.db_config)
|
|
||||||
|
|
||||||
async def close_pool(self):
|
|
||||||
"""关闭连接池"""
|
|
||||||
if self.pool:
|
|
||||||
self.pool.close()
|
|
||||||
await self.pool.wait_closed()
|
|
||||||
self.pool = None
|
|
||||||
|
|
||||||
async def fetch_one(self, sql: str, params=None):
|
|
||||||
"""查询单条数据"""
|
|
||||||
await self.init_pool()
|
|
||||||
async with self.pool.acquire() as conn: # type: ignore
|
|
||||||
async with conn.cursor(aiomysql.DictCursor) as cursor:
|
|
||||||
await cursor.execute(sql, params or ())
|
|
||||||
return await cursor.fetchone()
|
|
||||||
|
|
||||||
async def fetch_all(self, sql: str, params=None):
|
|
||||||
"""查询多条数据"""
|
|
||||||
await self.init_pool()
|
|
||||||
async with self.pool.acquire() as conn: # type: ignore
|
|
||||||
async with conn.cursor(aiomysql.DictCursor) as cursor:
|
|
||||||
await cursor.execute(sql, params or ())
|
|
||||||
return await cursor.fetchall()
|
|
||||||
|
|
||||||
async def execute(self, sql: str, params=None):
|
|
||||||
"""执行 SQL(insert/update/delete)"""
|
|
||||||
await self.init_pool()
|
|
||||||
async with self.pool.acquire() as conn: # type: ignore
|
|
||||||
async with conn.cursor() as cursor:
|
|
||||||
await cursor.execute(sql, params or ())
|
|
||||||
await conn.commit()
|
|
||||||
return cursor.rowcount
|
|
||||||
|
|
||||||
async def executemany(self, sql: str, params_list):
|
|
||||||
"""批量执行 SQL"""
|
|
||||||
await self.init_pool()
|
|
||||||
async with self.pool.acquire() as conn: # type: ignore
|
|
||||||
async with conn.cursor() as cursor:
|
|
||||||
await cursor.executemany(sql, params_list)
|
|
||||||
await conn.commit()
|
|
||||||
return cursor.rowcount
|
|
||||||
|
|
||||||
async def truncate_table(self, table_name: str):
|
|
||||||
"""清空指定表"""
|
|
||||||
await self.init_pool()
|
|
||||||
async with self.pool.acquire() as conn: # type: ignore
|
|
||||||
async with conn.cursor() as cursor:
|
|
||||||
sql = f"TRUNCATE TABLE `{table_name}`"
|
|
||||||
await cursor.execute(sql)
|
|
||||||
await conn.commit()
|
|
||||||
return True
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
# 新增:自动生成 SQL 的增删改功能
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
async def insert_record(self, table: str, data: dict):
|
|
||||||
"""插入记录:data 是 dict"""
|
|
||||||
keys = ", ".join(f"`{k}`" for k in data.keys())
|
|
||||||
placeholders = ", ".join(["%s"] * len(data))
|
|
||||||
sql = f"INSERT INTO `{table}` ({keys}) VALUES ({placeholders})"
|
|
||||||
return await self.execute(sql, tuple(data.values()))
|
|
||||||
|
|
||||||
async def update_record(self, table: str, data: dict, where: dict):
|
|
||||||
"""更新记录:data、where 都是 dict"""
|
|
||||||
set_clause = ", ".join(f"`{k}`=%s" for k in data.keys())
|
|
||||||
where_clause = " AND ".join(f"`{k}`=%s" for k in where.keys())
|
|
||||||
|
|
||||||
sql = f"UPDATE `{table}` SET {set_clause} WHERE {where_clause}"
|
|
||||||
|
|
||||||
params = tuple(data.values()) + tuple(where.values())
|
|
||||||
return await self.execute(sql, params)
|
|
||||||
|
|
||||||
async def delete_record(self, table: str, where: dict):
|
|
||||||
"""删除记录:where 是 dict"""
|
|
||||||
where_clause = " AND ".join(f"`{k}`=%s" for k in where.keys())
|
|
||||||
sql = f"DELETE FROM `{table}` WHERE {where_clause}"
|
|
||||||
return await self.execute(sql, tuple(where.values()))
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import aiofiles
|
||||||
|
|
||||||
|
async def load_template(template_name: str) -> str:
|
||||||
|
"""
|
||||||
|
异步加载模板内容(非阻塞)
|
||||||
|
"""
|
||||||
|
plugin_dir = Path(__file__).parent.parent
|
||||||
|
template_path = plugin_dir / "templates" / template_name
|
||||||
|
|
||||||
|
if not template_path.exists():
|
||||||
|
raise FileNotFoundError(f"模板文件不存在: {template_path}")
|
||||||
|
|
||||||
|
async with aiofiles.open(template_path, "r", encoding="utf-8") as f:
|
||||||
|
return await f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def extract_fields(data_list, fields):
|
||||||
|
"""
|
||||||
|
从字典列表中提取多个字段
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data_list (list[dict]): 包含字典的列表
|
||||||
|
fields (list[str]): 要提取的字段名列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[dict]: 只包含指定字段的新字典列表
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
try:
|
||||||
|
for item in data_list:
|
||||||
|
extracted = {field: item.get(field) for field in fields}
|
||||||
|
result.append(extracted)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"提取字段时出错: {e}")
|
||||||
|
return []
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from matplotlib.font_manager import FontProperties
|
|
||||||
from io import BytesIO
|
|
||||||
import base64
|
|
||||||
|
|
||||||
def load_template(template_name):
|
|
||||||
"""
|
|
||||||
从模板文件加载模板内容
|
|
||||||
|
|
||||||
Args:
|
|
||||||
template_name: 模板文件名(不带路径)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 模板内容
|
|
||||||
"""
|
|
||||||
# 获取模板文件路径
|
|
||||||
plugin_dir = Path(__file__).parent.parent
|
|
||||||
template_path = plugin_dir / "templates" / template_name
|
|
||||||
|
|
||||||
# 检查文件是否存在
|
|
||||||
if not template_path.exists():
|
|
||||||
raise FileNotFoundError(f"模板文件不存在: {template_path}")
|
|
||||||
|
|
||||||
# 读取模板内容
|
|
||||||
with open(template_path, 'r', encoding='utf-8') as f:
|
|
||||||
return f.read()
|
|
||||||
|
|
||||||
|
|
||||||
def flatten_field(data_list, field_name):
|
|
||||||
"""
|
|
||||||
提取并扁平化指定字段的值
|
|
||||||
|
|
||||||
从一个字典列表中,收集指定字段的所有值。
|
|
||||||
如果字段的值是列表,则会被展开(扁平化)加入结果;
|
|
||||||
如果字段的值是单个元素,则直接加入结果。
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data_list (list[dict]): 数据列表,每个元素是字典
|
|
||||||
field_name (str): 要提取的字段名
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list: 扁平化后的字段值列表
|
|
||||||
"""
|
|
||||||
extracted_data = []
|
|
||||||
for item in data_list:
|
|
||||||
if field_name in item and item[field_name]:
|
|
||||||
# 如果是 list,展开
|
|
||||||
if isinstance(item[field_name], list):
|
|
||||||
extracted_data.extend(item[field_name])
|
|
||||||
else:
|
|
||||||
extracted_data.append(item[field_name])
|
|
||||||
return extracted_data
|
|
||||||
|
|
||||||
|
|
||||||
def extract_fields(data_list, fields):
|
|
||||||
"""
|
|
||||||
从字典列表中提取多个字段
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data_list (list[dict]): 包含字典的列表
|
|
||||||
fields (list[str]): 要提取的字段名列表
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[dict]: 只包含指定字段的新字典列表
|
|
||||||
"""
|
|
||||||
result = []
|
|
||||||
try:
|
|
||||||
for item in data_list:
|
|
||||||
extracted = {field: item.get(field) for field in fields}
|
|
||||||
result.append(extracted)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"提取字段时出错: {e}")
|
|
||||||
return []
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def extract_field(data_list, field_name):
|
|
||||||
"""
|
|
||||||
从列表中的字典提取指定字段的所有值
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data_list (list): 数据列表,每个元素是 dict
|
|
||||||
field_name (str): 要提取的字段名
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list: 提取出来的字段值列表
|
|
||||||
"""
|
|
||||||
return [item[field_name] for item in data_list if field_name in item]
|
|
||||||
|
|
||||||
|
|
||||||
def gold_to_string(gold_amount):
|
|
||||||
"""
|
|
||||||
将金钱数值转换为字符串表示形式
|
|
||||||
|
|
||||||
Args:
|
|
||||||
gold_amount (int): 金钱数值,单位为铜币
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 格式化后的金钱字符串,例如 "1金2银3铜"
|
|
||||||
"""
|
|
||||||
if not gold_amount:
|
|
||||||
return "无价格"
|
|
||||||
|
|
||||||
parts = []
|
|
||||||
started = False # 标记是否已经遇到第一个非零位
|
|
||||||
|
|
||||||
bricks = gold_amount // 100000000
|
|
||||||
gold = (gold_amount % 100000000) // 10000
|
|
||||||
silver = (gold_amount % 10000) // 100
|
|
||||||
copper = gold_amount % 100
|
|
||||||
|
|
||||||
for value, unit in [(bricks, "砖"), (gold, "金"), (silver, "银"), (copper, "铜")]:
|
|
||||||
if value != 0:
|
|
||||||
started = True
|
|
||||||
if started:
|
|
||||||
parts.append(f"{value}{unit}")
|
|
||||||
|
|
||||||
return "".join(parts)
|
|
||||||
|
|
||||||
|
|
||||||
def plot_line_chart_base64(data, x_field, y_field, title=None, reverse_x=False):
|
|
||||||
"""
|
|
||||||
根据列表数据绘制折线图,返回 base64 图片字符串。
|
|
||||||
|
|
||||||
:param data: list[dict] 数据列表
|
|
||||||
:param x_field: str X轴字段
|
|
||||||
:param y_field: str Y轴字段
|
|
||||||
:param title: str 图表标题(可选)
|
|
||||||
:param reverse_x: bool 是否反转 X 轴方向(默认 False:从左往右;True:从右往左)
|
|
||||||
:return: str base64 图片字符串,可直接放到 <img src="..."> 中
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
raise ValueError("数据列表不能为空")
|
|
||||||
|
|
||||||
# 字体设置(支持中文)
|
|
||||||
plt.rcParams['font.sans-serif'] = ['SimHei']
|
|
||||||
plt.rcParams['axes.unicode_minus'] = False
|
|
||||||
|
|
||||||
# 提取 X / Y 数据
|
|
||||||
x_values = [str(item.get(x_field, "")) for item in data]
|
|
||||||
y_values = [float(item.get(y_field, 0)) for item in data]
|
|
||||||
|
|
||||||
# 创建画布
|
|
||||||
plt.figure(figsize=(8, 5))
|
|
||||||
plt.plot(x_values, y_values, marker='o', color='#4a90e2', linewidth=2)
|
|
||||||
|
|
||||||
# 反转 X 轴
|
|
||||||
if reverse_x:
|
|
||||||
plt.gca().invert_xaxis()
|
|
||||||
|
|
||||||
# 标题与标签
|
|
||||||
if title is None:
|
|
||||||
title = f"{y_field} 折线图"
|
|
||||||
|
|
||||||
plt.grid(True, linestyle='--', alpha=0.5)
|
|
||||||
plt.xticks(rotation=30)
|
|
||||||
plt.tight_layout()
|
|
||||||
|
|
||||||
# 转 Base64
|
|
||||||
buffer = BytesIO()
|
|
||||||
plt.savefig(buffer, format='png', dpi=150)
|
|
||||||
plt.close()
|
|
||||||
|
|
||||||
img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
|
|
||||||
return f"data:image/png;base64,{img_base64}"
|
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# pyright: reportArgumentType=false
|
||||||
|
# pyright: reportAttributeAccessIssue=false
|
||||||
|
# pyright: reportIndexIssue=false
|
||||||
|
# pyright: reportOptionalMemberAccess=false
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import pathlib
|
||||||
|
import inspect
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Union
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from astrbot.api.event import filter, AstrMessageEvent, MessageEventResult, MessageChain
|
||||||
|
from astrbot.api.star import Context, Star, register, StarTools
|
||||||
|
from astrbot.api import logger
|
||||||
|
from astrbot.api import AstrBotConfig
|
||||||
|
import astrbot.api.message_components as Comp
|
||||||
|
from astrbot.core.utils.session_waiter import (
|
||||||
|
SessionController,
|
||||||
|
session_waiter,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .gok_data import GOKServer
|
||||||
|
|
||||||
|
|
||||||
|
class GOKCommands(Star):
|
||||||
|
def __init__(self, gok_data:GOKServer):
|
||||||
|
self.gokfun = gok_data
|
||||||
|
|
||||||
|
|
||||||
|
async def plain_msg(self, event: AstrMessageEvent, action):
|
||||||
|
"""最终将数据整理成文本发送"""
|
||||||
|
data= await action()
|
||||||
|
try:
|
||||||
|
if data["code"] == 200:
|
||||||
|
await event.send( event.plain_result(data["data"]))
|
||||||
|
else:
|
||||||
|
await event.send(event.plain_result(data["msg"]))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"功能函数执行错误: {e}")
|
||||||
|
await event.send(event.plain_result("猪脑过载,请稍后再试"))
|
||||||
|
|
||||||
|
|
||||||
|
async def T2I_image_msg(self, event: AstrMessageEvent, action):
|
||||||
|
"""最终将数据渲染成图片发送"""
|
||||||
|
data = await action()
|
||||||
|
try:
|
||||||
|
if data["code"] == 200:
|
||||||
|
url = await self.html_render(data["temp"], data["data"], options={})
|
||||||
|
await event.send(event.image_result(url))
|
||||||
|
else:
|
||||||
|
await event.send(event.plain_result(data["msg"]))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"功能函数执行错误: {e}")
|
||||||
|
await event.send(event.plain_result("猪脑过载,请稍后再试"))
|
||||||
|
|
||||||
|
|
||||||
|
async def image_msg(self, event: AstrMessageEvent, action):
|
||||||
|
"""最终将数据整理成图片发送"""
|
||||||
|
data = await action()
|
||||||
|
try:
|
||||||
|
if data["code"] == 200:
|
||||||
|
await event.send(event.image_result(data["data"]))
|
||||||
|
else:
|
||||||
|
await event.send(event.plain_result(data["msg"]))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"功能函数执行错误: {e}")
|
||||||
|
await event.send(event.plain_result("猪脑过载,请稍后再试"))
|
||||||
|
|
||||||
|
|
||||||
|
async def gok_helps(self, event: AstrMessageEvent):
|
||||||
|
"""王者功能"""
|
||||||
|
return await self.T2I_image_msg(event, self.gokfun.helps)
|
||||||
|
|
||||||
|
|
||||||
|
async def gok_zhanji(self, event: AstrMessageEvent,name: str,option:str = 0):
|
||||||
|
"""王者战绩"""
|
||||||
|
return await self.T2I_image_msg(event, lambda: self.gokfun.zhanji(name ,option))
|
||||||
|
|
||||||
|
|
||||||
|
async def gok_ziliao(self, event: AstrMessageEvent,name: str):
|
||||||
|
"""王者资料"""
|
||||||
|
return await self.T2I_image_msg(event, lambda: self.gokfun.ziliao(name))
|
||||||
|
|
||||||
|
|
||||||
|
async def gok_user_all(self, event: AstrMessageEvent):
|
||||||
|
"""角色查看"""
|
||||||
|
return await self.T2I_image_msg(event, self.gokfun.all)
|
||||||
|
|
||||||
|
|
||||||
|
async def gok_user_add(self, event: AstrMessageEvent, gokid: int, name: str):
|
||||||
|
"""角色添加 王者营地ID 名称"""
|
||||||
|
return await self.plain_msg(event, lambda: self.gokfun.add(gokid,name))
|
||||||
|
|
||||||
|
|
||||||
|
async def gok_user_update(self, event: AstrMessageEvent, gokid: int, name: str):
|
||||||
|
"""角色修改 王者营地ID 名称"""
|
||||||
|
return await self.plain_msg(event, lambda: self.gokfun.update(gokid,name))
|
||||||
|
|
||||||
|
|
||||||
|
async def gok_user_delete(self, event: AstrMessageEvent, gokid:int):
|
||||||
|
"""角色删除 王者营地ID"""
|
||||||
|
return await self.plain_msg(event, lambda: self.gokfun.delete(gokid))
|
||||||
|
|
||||||
|
|
||||||
|
async def gok_user_select(self, event: AstrMessageEvent, gokid):
|
||||||
|
"""角色查询 王者营地ID"""
|
||||||
|
return await self.T2I_image_msg(event, lambda: self.gokfun.select(gokid))
|
||||||
@@ -0,0 +1,448 @@
|
|||||||
|
# pyright: reportArgumentType=false
|
||||||
|
# pyright: reportAttributeAccessIssue=false
|
||||||
|
# pyright: reportIndexIssue=false
|
||||||
|
# pyright: reportOptionalMemberAccess=false
|
||||||
|
# pyright: reportCallIssue=false
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, Any, Optional, List, Union
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from astrbot.api import logger
|
||||||
|
from astrbot.api import AstrBotConfig
|
||||||
|
|
||||||
|
from .request import APIClient
|
||||||
|
from .sqlite import AsyncSQLiteDB
|
||||||
|
from .fun_basic import load_template,extract_fields
|
||||||
|
|
||||||
|
class GOKServer:
|
||||||
|
def __init__(self, api_config, config:AstrBotConfig, sqlite:AsyncSQLiteDB ):
|
||||||
|
self._api = APIClient()
|
||||||
|
# 引用API配置文件
|
||||||
|
self._api_config = api_config
|
||||||
|
# 引用插件配置文件
|
||||||
|
self._config = config
|
||||||
|
# 引用数据库类
|
||||||
|
self._sql_db = sqlite
|
||||||
|
|
||||||
|
# 获取配置中的 Token
|
||||||
|
self.token = self._config.get("ytapi_token", "")
|
||||||
|
if self.token == "":
|
||||||
|
logger.info("获取配置token失败,请正确填写token,否则部分功能无法正常使用")
|
||||||
|
else:
|
||||||
|
logger.debug(f"获取配置token成功。{self.token}")
|
||||||
|
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
"""释放底层 APIClient 资源"""
|
||||||
|
if self._api:
|
||||||
|
await self._api.close()
|
||||||
|
self._api = None
|
||||||
|
|
||||||
|
|
||||||
|
def _init_return_data(self) -> Dict[str, Any]:
|
||||||
|
"""初始化标准的返回数据结构"""
|
||||||
|
return {
|
||||||
|
"code": 0,
|
||||||
|
"msg": "功能函数未执行",
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def _base_request(
|
||||||
|
self,
|
||||||
|
config_key: str,
|
||||||
|
method: str,
|
||||||
|
params: Optional[Dict[str, Any]] = None,
|
||||||
|
out_key: Optional[str] = "data"
|
||||||
|
) -> Optional[Any]:
|
||||||
|
"""
|
||||||
|
基础请求封装,处理配置获取和API调用。
|
||||||
|
|
||||||
|
:param config_key: 配置字典中对应 API 的键名。
|
||||||
|
:param method: HTTP方法 ('GET' 或 'POST')。
|
||||||
|
:param params: 请求参数或 Body 数据。
|
||||||
|
:param out_key: 响应数据中需要提取的字段。
|
||||||
|
:return: 成功时返回提取后的数据,失败时返回 None。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
api_config = self._api_config.get(config_key)
|
||||||
|
if not api_config:
|
||||||
|
logger.error(f"配置文件中未找到 key: {config_key}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 复制 params,避免修改原始配置模板
|
||||||
|
request_params = api_config.get("params", {}).copy()
|
||||||
|
if params:
|
||||||
|
request_params.update(params)
|
||||||
|
|
||||||
|
url = api_config.get("url", "")
|
||||||
|
if not url:
|
||||||
|
logger.error(f"API配置缺少 URL: {config_key}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if method.upper() == 'POST':
|
||||||
|
data = await self._api.post(url, data=request_params, out_key=out_key)
|
||||||
|
else: # 默认为 GET
|
||||||
|
data = await self._api.get(url, params=request_params, out_key=out_key)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
logger.warning(f"获取接口信息失败或返回空数据: {config_key}")
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"基础请求调用出错 ({config_key}): {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# --- 业务功能函数 ---
|
||||||
|
async def helps(self) -> Dict[str, Any]:
|
||||||
|
"""功能"""
|
||||||
|
return_data = self._init_return_data()
|
||||||
|
|
||||||
|
# 加载模板
|
||||||
|
try:
|
||||||
|
return_data["temp"] = await load_template("helps.html")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"加载模板失败: {e}")
|
||||||
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
return_data["code"] = 200
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def add(self,gokid: int, name: str) -> Dict[str, Any]:
|
||||||
|
"""角色添加 王者营地ID 角色"""
|
||||||
|
return_data = self._init_return_data()
|
||||||
|
|
||||||
|
# 添加数据
|
||||||
|
try:
|
||||||
|
await self._sql_db.insert(
|
||||||
|
"users",
|
||||||
|
{
|
||||||
|
"gokid": gokid,
|
||||||
|
"name": name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"添加角色失败: {e}")
|
||||||
|
return_data["msg"] = "添加角色失败"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
return_data["data"] = (
|
||||||
|
"角色添加成功\n"
|
||||||
|
f"王者营地ID:{gokid}\n"
|
||||||
|
f"角色名称:{name}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
return_data["code"] = 200
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def all(self) -> Dict[str, Any]:
|
||||||
|
"""角色查看"""
|
||||||
|
return_data = self._init_return_data()
|
||||||
|
|
||||||
|
|
||||||
|
# 查询数据
|
||||||
|
try:
|
||||||
|
data = await self._sql_db.select_all("users")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"查看角色失败: {e}")
|
||||||
|
return_data["msg"] = "查看角色失败"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return_data["msg"] = "未找到角色数据"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
# 加载模板
|
||||||
|
try:
|
||||||
|
return_data["temp"] = await load_template("jueshe.html")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"加载模板失败: {e}")
|
||||||
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# 数据处理
|
||||||
|
return_data["data"]["lists"] = data
|
||||||
|
|
||||||
|
return_data["code"] = 200
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def select(self, name) -> Dict[str, Any]:
|
||||||
|
"""角色查询 名称"""
|
||||||
|
return_data = self._init_return_data()
|
||||||
|
|
||||||
|
# 判断输入是否为整数
|
||||||
|
try:
|
||||||
|
int(name)
|
||||||
|
if int(name) >=100000000:
|
||||||
|
like = "gokid LIKE ?"
|
||||||
|
else:
|
||||||
|
raise Exception("id数据异常")
|
||||||
|
except (ValueError, TypeError, Exception):
|
||||||
|
like = "name LIKE ?"
|
||||||
|
|
||||||
|
# 模糊拼接
|
||||||
|
like_name = f"%{name}%"
|
||||||
|
|
||||||
|
# 查询数据
|
||||||
|
try:
|
||||||
|
data = await self._sql_db.select_all(
|
||||||
|
"users",
|
||||||
|
like,
|
||||||
|
(like_name,)
|
||||||
|
)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"查询角色失败: {e}")
|
||||||
|
return_data["msg"] = "查询角色失败"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return_data["msg"] = "未查询到角色数据"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
# 加载模板
|
||||||
|
try:
|
||||||
|
return_data["temp"] = await load_template("jueshe.html")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"加载模板失败: {e}")
|
||||||
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# 数据处理
|
||||||
|
return_data["data"]["lists"] = data
|
||||||
|
|
||||||
|
return_data["code"] = 200
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def update(self, gokid:int, name: str) -> Dict[str, Any]:
|
||||||
|
"""角色修改 王者营地ID 角色"""
|
||||||
|
return_data = self._init_return_data()
|
||||||
|
|
||||||
|
data = await self._sql_db.select_one(
|
||||||
|
"users",
|
||||||
|
"gokid=?",
|
||||||
|
(gokid,)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return_data["msg"] = "没有当前ID"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
# 修改数据
|
||||||
|
try:
|
||||||
|
await self._sql_db.update(
|
||||||
|
"users",
|
||||||
|
{
|
||||||
|
"name": name,
|
||||||
|
},
|
||||||
|
"gokid=?",
|
||||||
|
(gokid,)
|
||||||
|
)
|
||||||
|
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"避雷修改失败: {e}")
|
||||||
|
return_data["msg"] = "避雷修改失败"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
return_data["data"] = (
|
||||||
|
"角色修改成功\n"
|
||||||
|
f"王者营地ID:{gokid}\n"
|
||||||
|
f"角色名称:{name}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
return_data["code"] = 200
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def delete(self, gokid:int) -> Dict[str, Any]:
|
||||||
|
"""角色删除 王者营地ID"""
|
||||||
|
return_data = self._init_return_data()
|
||||||
|
|
||||||
|
data = await self._sql_db.select_one(
|
||||||
|
"users",
|
||||||
|
"gokid=?",
|
||||||
|
(gokid,)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return_data["msg"] = "没有当前ID"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# 删除
|
||||||
|
try:
|
||||||
|
await self._sql_db.delete(
|
||||||
|
"users",
|
||||||
|
"gokid=?",
|
||||||
|
(gokid,)
|
||||||
|
)
|
||||||
|
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"角色删除失败: {e}")
|
||||||
|
return_data["msg"] = "角色删除失败"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
return_data["data"] = f"角色删除成功。王者营地ID:{gokid}"
|
||||||
|
|
||||||
|
return_data["code"] = 200
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def get_gokid(self,name: str):
|
||||||
|
# 判断输入是否为整数
|
||||||
|
try:
|
||||||
|
# 直接返回输入
|
||||||
|
int(name)
|
||||||
|
if int(name) >=100000000:
|
||||||
|
gokid = name
|
||||||
|
return gokid
|
||||||
|
else:
|
||||||
|
raise Exception("输入不是ID")
|
||||||
|
except (ValueError, TypeError, Exception):
|
||||||
|
# 查询数据
|
||||||
|
try:
|
||||||
|
data = await self._sql_db.select_one(
|
||||||
|
"users",
|
||||||
|
"name=?",
|
||||||
|
(name,)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
gokid = None
|
||||||
|
return gokid
|
||||||
|
|
||||||
|
gokid = data['gokid']
|
||||||
|
return gokid
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"查询角色失败: {e}")
|
||||||
|
gokid = None
|
||||||
|
return gokid
|
||||||
|
|
||||||
|
|
||||||
|
async def zhanji(self,name: str ,option: str):
|
||||||
|
"""
|
||||||
|
战绩查询
|
||||||
|
"""
|
||||||
|
return_data = self._init_return_data()
|
||||||
|
|
||||||
|
# 获取配置中的 Token
|
||||||
|
token = self._config.get("ytapi_token", "")
|
||||||
|
if token == "":
|
||||||
|
return_data["msg"] = "系统未配置API访问Token"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# ID查询
|
||||||
|
gokid = await self.get_gokid(name)
|
||||||
|
|
||||||
|
if not gokid :
|
||||||
|
return_data["msg"] = "未查询到该用户,请确认输入正确的角色或营地ID"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
#更新参数
|
||||||
|
params = {"id": gokid, "option": option, "key": token}
|
||||||
|
|
||||||
|
# 需要提取的字段
|
||||||
|
fields = ["gametime","killcnt","deadcnt","assistcnt","gameresult","mvpcnt","losemvp","mapName",
|
||||||
|
"oldMasterMatchScore","newMasterMatchScore","usedTime","winNum","failNum","roleJobName","stars","desc",
|
||||||
|
"gradeGame","heroIcon","godLikeCnt", "firstBlood","hero1TripleKillCnt","hero1UltraKillCnt","hero1RampageCnt","evaluateUrlV3","mvpUrlV3"]
|
||||||
|
|
||||||
|
# 获取数据
|
||||||
|
data: Optional[List[Dict[str, Any]]] = await self._base_request("gok_zhanji", "GET", params=params)
|
||||||
|
if not data:
|
||||||
|
return_data["msg"] = "获取接口信息失败"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# 处理返回数据
|
||||||
|
try:
|
||||||
|
|
||||||
|
# 提取字段
|
||||||
|
result = extract_fields(data['list'], fields)
|
||||||
|
result = result[:25]
|
||||||
|
# 数据处理
|
||||||
|
for m in result:
|
||||||
|
minutes = m["usedTime"] // 60
|
||||||
|
seconds = m["usedTime"] % 60
|
||||||
|
m["time_str"] = f"{minutes}:{seconds:02d}"
|
||||||
|
|
||||||
|
return_data["data"]["data"] = result
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"处理数据时出错: {e}")
|
||||||
|
return_data["msg"] = "处理接口返回信息时出错"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# 加载模板
|
||||||
|
try:
|
||||||
|
return_data["temp"] = await load_template("wangzhezhanji.html")
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
logger.error(f"加载模板失败: {e}")
|
||||||
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
return_data["code"] = 200
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
|
async def ziliao(self, name: str):
|
||||||
|
return_data = self._init_return_data()
|
||||||
|
# 获取配置中的 Token
|
||||||
|
token = self._config.get("ytapi_token", "")
|
||||||
|
if token == "":
|
||||||
|
return_data["msg"] = "系统未配置API访问Token"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# ID查询
|
||||||
|
gokid = await self.get_gokid(name)
|
||||||
|
|
||||||
|
if not gokid :
|
||||||
|
return_data["msg"] = "未查询到该用户,请确认输入正确的角色或营地ID"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
#更新参数
|
||||||
|
params = {"id": gokid, "key": token}
|
||||||
|
|
||||||
|
# 获取数据
|
||||||
|
data: Optional[List[Dict[str, Any]]] = await self._base_request("gok_ziliao", "GET", params=params, out_key="")
|
||||||
|
logger.debug(data)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return_data["msg"] = "获取接口信息失败"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 转 base64
|
||||||
|
return_data["data"]["img_base64"] = base64.b64encode(data).decode("utf-8")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"处理数据时出错: {e}")
|
||||||
|
return_data["msg"] = "处理接口返回信息时出错"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
# 加载模板
|
||||||
|
try:
|
||||||
|
return_data["temp"] = await load_template("wzry_zl.html")
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error(f"加载模板失败")
|
||||||
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
return_data["code"] = 200
|
||||||
|
|
||||||
|
return return_data
|
||||||
|
|
||||||
|
|
||||||
+6
-7
@@ -1,9 +1,10 @@
|
|||||||
# core/request.py
|
# core/request.py
|
||||||
import json
|
import json
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
import asyncio
|
||||||
from typing import Optional, Dict, Any, Union, List
|
from typing import Optional, Dict, Any, Union, List
|
||||||
|
|
||||||
from aiohttp import ClientTimeout, ClientSession
|
from aiohttp import ClientTimeout, ClientSession
|
||||||
|
|
||||||
from astrbot.api import logger
|
from astrbot.api import logger
|
||||||
|
|
||||||
class APIClient:
|
class APIClient:
|
||||||
@@ -32,6 +33,7 @@ class APIClient:
|
|||||||
"""关闭 Session"""
|
"""关闭 Session"""
|
||||||
if self._session and not self._session.closed:
|
if self._session and not self._session.closed:
|
||||||
await self._session.close()
|
await self._session.close()
|
||||||
|
self._session = None
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
await self.get_session()
|
await self.get_session()
|
||||||
@@ -74,23 +76,20 @@ class APIClient:
|
|||||||
"""处理响应:自动识别二进制或JSON"""
|
"""处理响应:自动识别二进制或JSON"""
|
||||||
try:
|
try:
|
||||||
logger.debug(f"响应状态: {response.status}")
|
logger.debug(f"响应状态: {response.status}")
|
||||||
response.raise_for_status() # 如果是 4xx/5xx 直接抛出异常
|
response.raise_for_status()
|
||||||
|
|
||||||
content_type = response.headers.get('Content-Type', '').lower()
|
content_type = response.headers.get('Content-Type', '').lower()
|
||||||
|
|
||||||
# 处理二进制流 (图片、文件等)
|
|
||||||
if 'image' in content_type or 'octet-stream' in content_type:
|
if 'image' in content_type or 'octet-stream' in content_type:
|
||||||
return await response.read()
|
return await response.read()
|
||||||
|
|
||||||
# 处理 JSON
|
|
||||||
# 优先尝试标准 json 解析
|
|
||||||
try:
|
try:
|
||||||
data = await response.json()
|
data = await response.json()
|
||||||
except Exception:
|
except Exception:
|
||||||
# 容错:有些 API 返回 text/html 但内容是 JSON
|
|
||||||
text = await response.text()
|
text = await response.text()
|
||||||
try:
|
try:
|
||||||
data = json.loads(text)
|
loop = asyncio.get_running_loop()
|
||||||
|
data = await loop.run_in_executor(None, json.loads, text)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
logger.error(f"无法解析响应为 JSON。原始内容: {text[:100]}...")
|
logger.error(f"无法解析响应为 JSON。原始内容: {text[:100]}...")
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# pyright: reportOptionalMemberAccess=false
|
||||||
|
|
||||||
|
import aiosqlite
|
||||||
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncSQLiteDB:
|
||||||
|
def __init__(self, db_path: str = "data.db"):
|
||||||
|
self.db_path = db_path
|
||||||
|
self.conn: Optional[aiosqlite.Connection] = None
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 生命周期
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
async def connect(self):
|
||||||
|
self.conn = await aiosqlite.connect(self.db_path)
|
||||||
|
self.conn.row_factory = aiosqlite.Row
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
if self.conn:
|
||||||
|
await self.conn.close()
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# 基础执行
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
async def execute(self, sql: str, params: Tuple = ()):
|
||||||
|
async with self.conn.execute(sql, params):
|
||||||
|
await self.conn.commit()
|
||||||
|
|
||||||
|
async def fetch_one(self, sql: str, params: Tuple = ()) -> Optional[Dict[str, Any]]:
|
||||||
|
async with self.conn.execute(sql, params) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return dict(row) if row else None
|
||||||
|
|
||||||
|
async def fetch_all(self, sql: str, params: Tuple = ()) -> List[Dict[str, Any]]:
|
||||||
|
async with self.conn.execute(sql, params) as cursor:
|
||||||
|
rows = await cursor.fetchall()
|
||||||
|
return [dict(r) for r in rows]
|
||||||
|
|
||||||
|
# ======================
|
||||||
|
# CRUD
|
||||||
|
# ======================
|
||||||
|
|
||||||
|
async def insert(self, table: str, data: Dict[str, Any]):
|
||||||
|
keys = ", ".join(data.keys())
|
||||||
|
placeholders = ", ".join(["?"] * len(data))
|
||||||
|
sql = f"INSERT INTO {table} ({keys}) VALUES ({placeholders})"
|
||||||
|
await self.execute(sql, tuple(data.values()))
|
||||||
|
|
||||||
|
async def update(self, table: str, data: Dict[str, Any], where: str, params: Tuple):
|
||||||
|
set_clause = ", ".join([f"{k}=?" for k in data.keys()])
|
||||||
|
sql = f"UPDATE {table} SET {set_clause} WHERE {where}"
|
||||||
|
await self.execute(sql, tuple(data.values()) + params)
|
||||||
|
|
||||||
|
async def delete(self, table: str, where: str, params: Tuple):
|
||||||
|
sql = f"DELETE FROM {table} WHERE {where}"
|
||||||
|
await self.execute(sql, params)
|
||||||
|
|
||||||
|
async def select_one(self, table: str, where: str = "", params: Tuple = ()):
|
||||||
|
sql = f"SELECT * FROM {table}"
|
||||||
|
if where:
|
||||||
|
sql += f" WHERE {where}"
|
||||||
|
return await self.fetch_one(sql, params)
|
||||||
|
|
||||||
|
async def select_all(self, table: str, where: str = "", params: Tuple = ()):
|
||||||
|
sql = f"SELECT * FROM {table}"
|
||||||
|
if where:
|
||||||
|
sql += f" WHERE {where}"
|
||||||
|
return await self.fetch_all(sql, params)
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import http.client
|
|
||||||
|
|
||||||
conn = http.client.HTTPSConnection("api.t1qq.com")
|
|
||||||
payload = ''
|
|
||||||
headers = {
|
|
||||||
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
|
|
||||||
'Accept': '*/*',
|
|
||||||
'Host': 'api.t1qq.com',
|
|
||||||
'Connection': 'keep-alive'
|
|
||||||
}
|
|
||||||
conn.request("GET", "/api/tool/wzrr/ydtp?key=vBpEzoiC9z5A9c9Nn83IhLn6M9&id=489048724", payload, headers)
|
|
||||||
res = conn.getresponse()
|
|
||||||
data = res.read()
|
|
||||||
print(data)
|
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
{
|
{
|
||||||
"wzry_zhanji":{
|
"gok_zhanji":{
|
||||||
"url":"https://api.t1qq.com/api/tool/wzrr/morebattle",
|
"url":"https://api.t1qq.com/api/tool/wzrr/morebattle",
|
||||||
"method":"GET",
|
"method":"GET",
|
||||||
"description":"王者荣耀战绩查询",
|
"description":"王者荣耀战绩查询",
|
||||||
"params":{
|
"params":{
|
||||||
"key": "vBpEzoiC9z5A9c9Nn83IhLn6M9",
|
"key": "",
|
||||||
"id": "489048724",
|
"id": "",
|
||||||
"option": "1"
|
"option": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"wzry_ziliao":{
|
"gok_ziliao":{
|
||||||
"url":"https://api.t1qq.com/api/tool/wzrr/ydtp",
|
"url":"https://api.t1qq.com/api/tool/wzrr/ydtp",
|
||||||
"method":"GET",
|
"method":"GET",
|
||||||
"description":"王者荣耀资料查询",
|
"description":"王者荣耀资料查询",
|
||||||
"params":{
|
"params":{
|
||||||
"key": "vBpEzoiC9z5A9c9Nn83IhLn6M9",
|
"key": "",
|
||||||
"id": "489048724"
|
"id": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,218 +1,195 @@
|
|||||||
|
# pyright: reportOptionalMemberAccess=false
|
||||||
|
# pyright: reportCallIssue=false
|
||||||
|
# pyright: reportArgumentType=false
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import asyncio
|
import inspect
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from astrbot.api.event import filter, AstrMessageEvent, MessageEventResult, MessageChain
|
from astrbot.api.event import filter, AstrMessageEvent, MessageEventResult, MessageChain
|
||||||
from astrbot.api.star import Context, Star, register, StarTools
|
from astrbot.api.star import Context, Star, register, StarTools
|
||||||
from astrbot.api import logger
|
from astrbot.api import logger
|
||||||
from astrbot.api import AstrBotConfig
|
from astrbot.api import AstrBotConfig
|
||||||
|
|
||||||
|
from .core.sqlite import AsyncSQLiteDB
|
||||||
from .core.async_mysql import AsyncMySQL
|
from .core.gok_data import GOKServer
|
||||||
from .core.WZRYFunction import WZRYFunction
|
from .core.gok_commands import GOKCommands
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@register("astrbot_plugin_gok",
|
@register("astrbot_plugin_gok",
|
||||||
"飞翔大野猪",
|
"fxdyz",
|
||||||
"通过接口接口获取王者荣耀游戏数据",
|
"通过接口获取王者荣耀游戏数据",
|
||||||
"1.0.0",
|
"1.0.0",
|
||||||
"https://github.com/qsc20001102/astrbot_plugin_gok"
|
"https://github.com/qsc20001102/astrbot_plugin_gok.git"
|
||||||
)
|
)
|
||||||
class GokApiPlugin(Star):
|
class GokApiPlugin(Star):
|
||||||
def __init__(self, context: Context, config: AstrBotConfig):
|
def __init__(self, context: Context, config: AstrBotConfig):
|
||||||
super().__init__(context)
|
super().__init__(context)
|
||||||
#获取配置
|
#获取配置
|
||||||
self.conf = config
|
self.conf = config
|
||||||
|
|
||||||
# 本地数据存储路径
|
# 本地数据存储路径
|
||||||
self.local_data_dir = StarTools.get_data_dir("astrbot_plugin_gok")
|
self.local_data_dir = StarTools.get_data_dir("astrbot_plugin_gok")
|
||||||
# api数据文件
|
|
||||||
self.api_file_path = Path(__file__).parent / "api_config.json"
|
# SQLite本地路径
|
||||||
# 读取文件内容
|
self.sqlite_path = Path(self.local_data_dir) /"sqlite.db"
|
||||||
|
logger.info(f"SQLite数据文件路径:{self.sqlite_path}")
|
||||||
|
|
||||||
|
# 读取API配置文件
|
||||||
|
self.api_file_path = Path(__file__).parent / "data" / "api_config.json"
|
||||||
with open(self.api_file_path, 'r', encoding='utf-8') as f:
|
with open(self.api_file_path, 'r', encoding='utf-8') as f:
|
||||||
self.api_config = json.load(f)
|
self.api_config = json.load(f)
|
||||||
|
|
||||||
# 初始化数据
|
# 初始化数据
|
||||||
logger.info("王者荣耀插件初始化完成")
|
# 声明指令集
|
||||||
|
self.command_map = {}
|
||||||
|
# 指令前缀功能
|
||||||
|
self.prefix_en = self.conf.get("prefix").get("enable")
|
||||||
|
self.prefix_text = self.conf.get("prefix").get("text")
|
||||||
|
if not self.prefix_text:
|
||||||
|
self.prefix_text = "王者"
|
||||||
|
if self.prefix_en:
|
||||||
|
logger.info(f"已启用指令前缀功能,前缀为:{self.prefix_text}")
|
||||||
|
else:
|
||||||
|
logger.info(f"未启用指令前缀功能。")
|
||||||
|
|
||||||
|
logger.info("GOK 插件初始化完成")
|
||||||
|
|
||||||
|
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
"""可选择实现异步的插件初始化方法,当实例化该插件类之后会自动调用该方法。"""
|
"""可选择实现异步的插件初始化方法,当实例化该插件类之后会自动调用该方法。"""
|
||||||
# 数据库配置
|
|
||||||
db_config = {
|
|
||||||
'host': '38.12.28.24',
|
|
||||||
'port': 3306,
|
|
||||||
'user': 'asrtbot',
|
|
||||||
'password': 'qsc102341',
|
|
||||||
'db': 'asrtbot',
|
|
||||||
'charset': 'utf8mb4',
|
|
||||||
'autocommit': True
|
|
||||||
}
|
|
||||||
#创建类实例
|
|
||||||
self.db = AsyncMySQL(db_config)
|
|
||||||
self.wzry = WZRYFunction(self.api_config,self.db)
|
|
||||||
# 周期函数调用
|
|
||||||
|
|
||||||
|
|
||||||
logger.info("王者荣耀插件创建实例完成")
|
|
||||||
|
|
||||||
|
|
||||||
@filter.command_group("王者")
|
|
||||||
def wz(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@wz.command("列表")
|
|
||||||
async def wz_yingdilist(self, event: AstrMessageEvent):
|
|
||||||
"""王者 用户列表"""
|
|
||||||
try:
|
try:
|
||||||
data = await self.wzry.all_user()
|
# sqlite 实例化
|
||||||
logger.info(f"王者荣耀营地列表查询结果{data}")
|
self.sql_db = AsyncSQLiteDB(self.sqlite_path)
|
||||||
yield event.plain_result(data)
|
await self.sql_db.connect()
|
||||||
|
await self.sql_db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS users(
|
||||||
|
gokid INTEGER,
|
||||||
|
name TEXT
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
# 王者功能 实例化
|
||||||
|
self.gokfun = GOKServer(self.api_config, self.conf, self.sql_db)
|
||||||
|
# 发送消息实例化
|
||||||
|
self.gokmag = GOKCommands(self.gokfun)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
logger.error(f"功能模块初始化失败: {e}")
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
raise
|
||||||
|
|
||||||
@wz.command("添加")
|
# 指令集
|
||||||
async def wz_adduesr(self, event: AstrMessageEvent,id: str,name: str):
|
self.ini_command_map()
|
||||||
"""王者 添加用户"""
|
|
||||||
try:
|
|
||||||
data = await self.wzry.add_user(id,name)
|
|
||||||
yield event.plain_result(data)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
@wz.command("修改")
|
|
||||||
async def wz_updateuser(self, event: AstrMessageEvent,id: str,name: str):
|
|
||||||
"""王者 修改用户"""
|
|
||||||
try:
|
|
||||||
data = await self.wzry.update_user(id,name)
|
|
||||||
yield event.plain_result(data)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
@wz.command("删除")
|
|
||||||
async def wz_deleteuser(self, event: AstrMessageEvent,id: str):
|
|
||||||
"""王者 删除用户"""
|
|
||||||
try:
|
|
||||||
data = await self.wzry.delete_user(id)
|
|
||||||
yield event.plain_result(data)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
@wz.command("战绩")
|
|
||||||
async def wz_zhanji(self, event: AstrMessageEvent,name: str = "飞翔大野猪",option: str = "0"):
|
|
||||||
"""王者 战绩 营地ID 对局类型"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = await self.wzry.zhanji(name,option)
|
|
||||||
# logger.info(f"王者荣耀战绩查询结果{data}")
|
|
||||||
#prompt = f"{data['data']}\n"
|
|
||||||
#prompt += f"以上数据是获取到的王者荣耀战绩信息,请根据这些数据生成一两句话的战绩总结,突出玩家的优势和特点,语言风格轻松幽默,适合在游戏群内分享。"
|
|
||||||
if data["code"] == 200:
|
|
||||||
url = await self.html_render(data["temp"],{"data":data["data"]}, options={})
|
|
||||||
yield event.image_result(url)
|
|
||||||
else:
|
|
||||||
yield event.plain_result(data["msg"])
|
|
||||||
return
|
|
||||||
#provider_id = await self.context.get_current_chat_provider_id(umo=event.unified_msg_origin)
|
|
||||||
#llm_resp = await self.context.llm_generate(chat_provider_id=provider_id, prompt=prompt,)
|
|
||||||
#out = llm_resp.completion_text
|
|
||||||
#yield event.plain_result(f"{out}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
|
|
||||||
@wz.command("资料")
|
|
||||||
async def wz_ziliao(self, event: AstrMessageEvent,name: str = "489048724"):
|
|
||||||
"""王者 战绩 营地ID 对局类型"""
|
|
||||||
try:
|
|
||||||
data = await self.wzry.ziliao(name)
|
|
||||||
logger.info(f"输出结果{data}")
|
|
||||||
if data["code"] == 200:
|
|
||||||
url = await self.html_render(data["temp"], data["data"], options={})
|
|
||||||
yield event.image_result(url)
|
|
||||||
else:
|
|
||||||
yield event.plain_result(data["msg"])
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
@filter.command_group("仇人")
|
|
||||||
def bilei(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@bilei.command("列表")
|
|
||||||
async def bilei_list(self, event: AstrMessageEvent,):
|
|
||||||
"""仇人 列表"""
|
|
||||||
try:
|
|
||||||
data = await self.wzry.bilei_all()
|
|
||||||
if data["code"] == 200:
|
|
||||||
url = await self.html_render(data["temp"], data["data"], options={})
|
|
||||||
yield event.image_result(url)
|
|
||||||
else:
|
|
||||||
yield event.plain_result(data["msg"])
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
|
|
||||||
@bilei.command("添加")
|
|
||||||
async def bilei_add(self, event: AstrMessageEvent,name: str, text: str):
|
|
||||||
"""仇人 添加 名称 备注"""
|
|
||||||
try:
|
|
||||||
data = await self.wzry.bilei_add(name, text, event.get_sender_name())
|
|
||||||
yield event.plain_result(data["msg"])
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
|
|
||||||
@bilei.command("查找")
|
|
||||||
async def bilei_select(self, event: AstrMessageEvent,name: str):
|
|
||||||
"""仇人 查找 名称(模糊查找)"""
|
|
||||||
try:
|
|
||||||
data = await self.wzry.bilei_select(name)
|
|
||||||
if data["code"] == 200:
|
|
||||||
url = await self.html_render(data["temp"], data["data"], options={})
|
|
||||||
yield event.image_result(url)
|
|
||||||
else:
|
|
||||||
yield event.plain_result(data["msg"])
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
|
|
||||||
@bilei.command("修改")
|
|
||||||
async def bilei_update(self, event: AstrMessageEvent,id:int, name: str, text: str):
|
|
||||||
"""仇人 修改 ID 名称 备注"""
|
|
||||||
try:
|
|
||||||
data = await self.wzry.bilei_update(id, name, text, event.get_sender_name())
|
|
||||||
yield event.plain_result(data["msg"])
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
@bilei.command("删除")
|
|
||||||
async def bilei_delete(self, event: AstrMessageEvent,id:int):
|
|
||||||
"""仇人 删除 ID """
|
|
||||||
try:
|
|
||||||
data = await self.wzry.bilei_delete(id)
|
|
||||||
yield event.plain_result(data["msg"])
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"功能函数执行错误: {e}")
|
|
||||||
yield event.plain_result("猪脑过载,请稍后再试")
|
|
||||||
|
|
||||||
|
logger.info("GOK 异步插件初始化完成")
|
||||||
|
|
||||||
async def terminate(self):
|
async def terminate(self):
|
||||||
"""可选择实现异步的插件销毁方法,当插件被卸载/停用时会调用。"""
|
"""可选择实现异步的插件销毁方法,当插件被卸载/停用时会调用。"""
|
||||||
await self.db.close_pool()
|
if self.gokfun:
|
||||||
# 后台进程销毁
|
await self.gokfun.close()
|
||||||
logger.info("王者荣耀插件已卸载/停用")
|
self.gokfun = None
|
||||||
|
|
||||||
|
if self.sql_db:
|
||||||
|
await self.sql_db.close()
|
||||||
|
self.sql_db = None
|
||||||
|
|
||||||
|
logger.info("GOK 插件已卸载/停用")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_message(self, text: str) -> list[str] | None:
|
||||||
|
"""消息解析"""
|
||||||
|
text = text.strip()
|
||||||
|
if not text:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 前缀模式
|
||||||
|
if self.prefix_en:
|
||||||
|
prefix = self.prefix_text
|
||||||
|
if text.startswith(prefix):
|
||||||
|
text = text[len(prefix):].strip()
|
||||||
|
else:
|
||||||
|
# 非前缀消息,直接忽略
|
||||||
|
return None
|
||||||
|
|
||||||
|
return text.split()
|
||||||
|
|
||||||
|
|
||||||
|
async def _call_with_auto_args(self, handler, event: AstrMessageEvent, args: list[str]):
|
||||||
|
"""指令执行函数"""
|
||||||
|
sig = inspect.signature(handler)
|
||||||
|
params = list(sig.parameters.values())
|
||||||
|
|
||||||
|
call_args = []
|
||||||
|
arg_index = 0
|
||||||
|
|
||||||
|
for p in params:
|
||||||
|
if p.name == "self":
|
||||||
|
continue
|
||||||
|
|
||||||
|
if p.name == "event":
|
||||||
|
call_args.append(event)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if arg_index < len(args):
|
||||||
|
raw = args[arg_index]
|
||||||
|
arg_index += 1
|
||||||
|
try:
|
||||||
|
if p.annotation is int:
|
||||||
|
call_args.append(int(raw))
|
||||||
|
elif p.annotation is float:
|
||||||
|
call_args.append(float(raw))
|
||||||
|
else:
|
||||||
|
call_args.append(raw)
|
||||||
|
except Exception:
|
||||||
|
call_args.append(p.default)
|
||||||
|
else:
|
||||||
|
if p.default is not inspect._empty:
|
||||||
|
call_args.append(p.default)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"缺少参数: {p.name}")
|
||||||
|
|
||||||
|
# 只允许 coroutine
|
||||||
|
return await handler(*call_args)
|
||||||
|
|
||||||
|
|
||||||
|
@filter.event_message_type(filter.EventMessageType.ALL)
|
||||||
|
async def on_all_message(self, event: AstrMessageEvent):
|
||||||
|
"""解析所有消息"""
|
||||||
|
if not self.command_map:
|
||||||
|
logger.debug("插件尚未初始化完成,忽略消息")
|
||||||
|
return
|
||||||
|
parts = self.parse_message(event.message_str)
|
||||||
|
if not parts:
|
||||||
|
logger.debug("未触发指令,忽略消息")
|
||||||
|
return
|
||||||
|
|
||||||
|
cmd, *args = parts
|
||||||
|
handler = self.command_map.get(cmd)
|
||||||
|
if not handler:
|
||||||
|
logger.debug("指令函数为空,忽略消息")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
event.stop_event()
|
||||||
|
ret = await self._call_with_auto_args(handler, event, args)
|
||||||
|
if ret is not None:
|
||||||
|
yield ret
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f"指令执行失败: {cmd}, error={e}")
|
||||||
|
yield event.plain_result("参数错误或执行失败")
|
||||||
|
|
||||||
|
|
||||||
|
def ini_command_map(self):
|
||||||
|
"""初始化指令集"""
|
||||||
|
self.command_map = {
|
||||||
|
"王者功能": self.gokmag.gok_helps,
|
||||||
|
"王者战绩": self.gokmag.gok_zhanji,
|
||||||
|
"王者资料": self.gokmag.gok_ziliao,
|
||||||
|
"角色查看": self.gokmag.gok_user_all,
|
||||||
|
"角色添加": self.gokmag.gok_user_add,
|
||||||
|
"角色修改": self.gokmag.gok_user_update,
|
||||||
|
"角色删除": self.gokmag.gok_user_delete,
|
||||||
|
"角色查询": self.gokmag.gok_user_select
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
name: astrbot_plugin_gok # 这是你的插件的唯一识别名。
|
name: astrbot_plugin_gok
|
||||||
display_name: 王者荣耀数据查询工具 # 插件的显示名称
|
display_name: 王者荣耀游戏数据查询工具
|
||||||
desc: 王者荣耀的战绩、资料等内容的查询 # 插件简短描述
|
desc: 王者荣耀的战绩、资料等内容的查询
|
||||||
version: v1.0.0 # 插件版本号。格式:v1.1.1 或者 v1.1
|
version: v1.0.0
|
||||||
author: 飞翔大野猪 # 作者
|
author: 飞翔大野猪
|
||||||
repo: https://github.com/qsc20001102/astrbot_plugin_gok # 插件的仓库地址
|
repo: https://github.com/qsc20001102/astrbot_plugin_gok
|
||||||
|
|||||||
+1
-2
@@ -1,2 +1 @@
|
|||||||
aiomysql
|
aiosqlite
|
||||||
matplotlib
|
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>王者插件功能</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 40px 20px;
|
||||||
|
background: linear-gradient(180deg, #f5f7fb 0%, #eef1f7 100%);
|
||||||
|
color: #2c2c33;
|
||||||
|
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页面主体宽度控制 */
|
||||||
|
.container {
|
||||||
|
max-width: 980px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部标题 */
|
||||||
|
.header {
|
||||||
|
margin-bottom: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 42px;
|
||||||
|
margin: 0 0 10px;
|
||||||
|
color: #1f2a44;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header p {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #5a627a;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分组 */
|
||||||
|
.group {
|
||||||
|
margin-bottom: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-title {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
padding-left: 14px;
|
||||||
|
border-left: 6px solid #4f7cff;
|
||||||
|
color: #1f2a44;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 单列 */
|
||||||
|
.command-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 指令行 */
|
||||||
|
.command {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 14px 18px;
|
||||||
|
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 4px;
|
||||||
|
height: 100%;
|
||||||
|
background: #4f7cff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmd-name {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2b3a67;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmd-usage {
|
||||||
|
font-size: 22px;
|
||||||
|
color: #1f2a44;
|
||||||
|
background: #f0f3ff;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-family: Consolas, "Courier New", monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 提示 */
|
||||||
|
.tip {
|
||||||
|
margin-top: 40px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #5a5f73;
|
||||||
|
background: #fffbe6;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-left: 6px solid #ffcc00;
|
||||||
|
border-radius: 10px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 小屏适配 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.cmd-name {
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<h1>王者插件功能</h1>
|
||||||
|
<p>astrbot_plugin_gok 指令说明与使用示例</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="group">
|
||||||
|
<div class="group-title">基础功能</div>
|
||||||
|
<div class="command-grid">
|
||||||
|
<div class="command"><div class="cmd-name">王者功能</div><div class="cmd-usage">王者功能</div></div>
|
||||||
|
<div class="command"><div class="cmd-name">王者战绩</div><div class="cmd-usage">王者战绩 角色/营地ID</div></div>
|
||||||
|
<div class="command"><div class="cmd-name">王者资料</div><div class="cmd-usage">王者资料 角色/营地ID</div></div>
|
||||||
|
<div class="command"><div class="cmd-name">角色查看</div><div class="cmd-usage">角色查看</div></div>
|
||||||
|
<div class="command"><div class="cmd-name">角色添加</div><div class="cmd-usage">角色添加 营地ID 角色</div></div>
|
||||||
|
<div class="command"><div class="cmd-name">角色修改</div><div class="cmd-usage">角色修改 营地ID 角色</div></div>
|
||||||
|
<div class="command"><div class="cmd-name">角色删除</div><div class="cmd-usage">角色删除 营地ID</div></div>
|
||||||
|
<div class="command"><div class="cmd-name">角色查询</div><div class="cmd-usage">角色查询 营地ID/角色</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tip">
|
||||||
|
<strong>使用提示:</strong><br>
|
||||||
|
1. 指令里面的角色指的是自定义的查询名称<br>
|
||||||
|
2. 战绩和资料的指令必须 token 可以使用<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>王者荣耀 — 仇人列表</title>
|
<title>角色查看</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 32px;
|
font-size: 48px;
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
color: #222;
|
color: #222;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
@@ -27,26 +27,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 66%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
text-align: left; /* 不影响表格内容 */
|
text-align: left;
|
||||||
|
margin: 10px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
background: #d93939;
|
background: #d93939;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 18px;
|
font-size: 27px;
|
||||||
}
|
}
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
padding: 14px 12px;
|
padding: 14px 12px;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
font-size: 16px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr:hover {
|
tbody tr:hover {
|
||||||
@@ -65,27 +66,21 @@
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>仇人列表</h1>
|
<h1>角色查看</h1>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>王者营地ID</th>
|
||||||
<th>名称</th>
|
<th>角色名称</th>
|
||||||
<th>描述</th>
|
|
||||||
<th>时间</th>
|
|
||||||
<th>记录者</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for m in lists %}
|
{% for m in lists %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ m.id }}</td>
|
<td>{{ m.gokid }}</td>
|
||||||
<td>{{ m.name }}</td>
|
<td>{{ m.name }}</td>
|
||||||
<td class="text-col">{{ m.text }}</td>
|
|
||||||
<td>{{ m.time }}</td>
|
|
||||||
<td>{{ m.user }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<!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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 1000px;
|
|
||||||
margin: 0 auto;
|
|
||||||
text-align: center; /* ★ 让容器内标题更自然居中 */
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 32px;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
color: #222;
|
|
||||||
font-weight: 800;
|
|
||||||
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);
|
|
||||||
margin-top: 10px;
|
|
||||||
text-align: left; /* 不影响表格内容 */
|
|
||||||
}
|
|
||||||
|
|
||||||
thead {
|
|
||||||
background: #d93939;
|
|
||||||
color: white;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
padding: 14px 12px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody tr:hover {
|
|
||||||
background: #fff2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-col {
|
|
||||||
max-width: 360px;
|
|
||||||
line-height: 1.5em;
|
|
||||||
word-break: break-word;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<h1>仇人列表</h1>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>名称</th>
|
|
||||||
<th>描述</th>
|
|
||||||
<th>时间</th>
|
|
||||||
<th>记录者</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{% for m in lists %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ m.id }}</td>
|
|
||||||
<td>{{ m.name }}</td>
|
|
||||||
<td class="text-col">{{ m.text }}</td>
|
|
||||||
<td>{{ m.time }}</td>
|
|
||||||
<td>{{ m.user }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import http.client
|
|
||||||
|
|
||||||
conn = http.client.HTTPSConnection("api.t1qq.com")
|
|
||||||
payload = ''
|
|
||||||
headers = {
|
|
||||||
'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
|
|
||||||
'Accept': '*/*',
|
|
||||||
'Host': 'api.t1qq.com',
|
|
||||||
'Connection': 'keep-alive'
|
|
||||||
}
|
|
||||||
conn.request("GET", "/api/tool/wzrr/ydtp?key=vBpEzoiC9z5A9c9Nn83IhLn6M9&id=489048724", payload, headers)
|
|
||||||
res = conn.getresponse()
|
|
||||||
data = res.read()
|
|
||||||
print(data)
|
|
||||||
Reference in New Issue
Block a user