diff --git a/README.md b/README.md
index b7b3df6..bf9ab9d 100644
--- a/README.md
+++ b/README.md
@@ -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起一个别名,方便自己记忆,也方便查询。
\ No newline at end of file
diff --git a/_conf_schema.json b/_conf_schema.json
index 544b7b4..da62a0d 100644
--- a/_conf_schema.json
+++ b/_conf_schema.json
@@ -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(免费)。"
+ }
}
\ No newline at end of file
diff --git a/core/WZRYFunction.py b/core/WZRYFunction.py
deleted file mode 100644
index 143e2aa..0000000
--- a/core/WZRYFunction.py
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/core/__pycache__/__init__.cpython-313.pyc b/core/__pycache__/__init__.cpython-313.pyc
deleted file mode 100644
index e008624..0000000
Binary files a/core/__pycache__/__init__.cpython-313.pyc and /dev/null differ
diff --git a/core/__pycache__/request.cpython-313.pyc b/core/__pycache__/request.cpython-313.pyc
deleted file mode 100644
index 506f62b..0000000
Binary files a/core/__pycache__/request.cpython-313.pyc and /dev/null differ
diff --git a/core/async_mysql.py b/core/async_mysql.py
deleted file mode 100644
index 75a89f1..0000000
--- a/core/async_mysql.py
+++ /dev/null
@@ -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()))
\ No newline at end of file
diff --git a/core/fun_basic.py b/core/fun_basic.py
new file mode 100644
index 0000000..87246c5
--- /dev/null
+++ b/core/fun_basic.py
@@ -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
+
+
+
+
diff --git a/core/function_basic.py b/core/function_basic.py
deleted file mode 100644
index 5886593..0000000
--- a/core/function_basic.py
+++ /dev/null
@@ -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 图片字符串,可直接放到 中
- """
- 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}"
\ No newline at end of file
diff --git a/core/gok_commands.py b/core/gok_commands.py
new file mode 100644
index 0000000..7e8a04e
--- /dev/null
+++ b/core/gok_commands.py
@@ -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))
\ No newline at end of file
diff --git a/core/gok_data.py b/core/gok_data.py
new file mode 100644
index 0000000..618a2a7
--- /dev/null
+++ b/core/gok_data.py
@@ -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
+
+
diff --git a/core/request.py b/core/request.py
index 7b43000..e204057 100644
--- a/core/request.py
+++ b/core/request.py
@@ -1,9 +1,10 @@
# core/request.py
import json
import aiohttp
+import asyncio
from typing import Optional, Dict, Any, Union, List
-
from aiohttp import ClientTimeout, ClientSession
+
from astrbot.api import logger
class APIClient:
@@ -32,6 +33,7 @@ class APIClient:
"""关闭 Session"""
if self._session and not self._session.closed:
await self._session.close()
+ self._session = None
async def __aenter__(self):
await self.get_session()
@@ -74,23 +76,20 @@ class APIClient:
"""处理响应:自动识别二进制或JSON"""
try:
logger.debug(f"响应状态: {response.status}")
- response.raise_for_status() # 如果是 4xx/5xx 直接抛出异常
+ response.raise_for_status()
content_type = response.headers.get('Content-Type', '').lower()
- # 处理二进制流 (图片、文件等)
if 'image' in content_type or 'octet-stream' in content_type:
return await response.read()
- # 处理 JSON
- # 优先尝试标准 json 解析
try:
data = await response.json()
except Exception:
- # 容错:有些 API 返回 text/html 但内容是 JSON
text = await response.text()
try:
- data = json.loads(text)
+ loop = asyncio.get_running_loop()
+ data = await loop.run_in_executor(None, json.loads, text)
except json.JSONDecodeError:
logger.error(f"无法解析响应为 JSON。原始内容: {text[:100]}...")
return None
diff --git a/core/sqlite.py b/core/sqlite.py
new file mode 100644
index 0000000..8f342ff
--- /dev/null
+++ b/core/sqlite.py
@@ -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)
\ No newline at end of file
diff --git a/core/test.py b/core/test.py
deleted file mode 100644
index ab2efbd..0000000
--- a/core/test.py
+++ /dev/null
@@ -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)
\ No newline at end of file
diff --git a/api_config.json b/data/api_config.json
similarity index 64%
rename from api_config.json
rename to data/api_config.json
index 78917f8..0c39dbe 100644
--- a/api_config.json
+++ b/data/api_config.json
@@ -1,21 +1,21 @@
{
- "wzry_zhanji":{
+ "gok_zhanji":{
"url":"https://api.t1qq.com/api/tool/wzrr/morebattle",
"method":"GET",
"description":"王者荣耀战绩查询",
"params":{
- "key": "vBpEzoiC9z5A9c9Nn83IhLn6M9",
- "id": "489048724",
+ "key": "",
+ "id": "",
"option": "1"
}
},
- "wzry_ziliao":{
+ "gok_ziliao":{
"url":"https://api.t1qq.com/api/tool/wzrr/ydtp",
"method":"GET",
"description":"王者荣耀资料查询",
"params":{
- "key": "vBpEzoiC9z5A9c9Nn83IhLn6M9",
- "id": "489048724"
+ "key": "",
+ "id": ""
}
}
}
\ No newline at end of file
diff --git a/logo.png b/logo.png
new file mode 100644
index 0000000..b7c3cb2
Binary files /dev/null and b/logo.png differ
diff --git a/main.py b/main.py
index 8202b43..e02f5f6 100644
--- a/main.py
+++ b/main.py
@@ -1,218 +1,195 @@
+# pyright: reportOptionalMemberAccess=false
+# pyright: reportCallIssue=false
+# pyright: reportArgumentType=false
+
import json
-import asyncio
+import inspect
from pathlib import Path
-from datetime import datetime
+
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
-
-from .core.async_mysql import AsyncMySQL
-from .core.WZRYFunction import WZRYFunction
-
+from .core.sqlite import AsyncSQLiteDB
+from .core.gok_data import GOKServer
+from .core.gok_commands import GOKCommands
@register("astrbot_plugin_gok",
- "飞翔大野猪",
- "通过接口接口获取王者荣耀游戏数据",
+ "fxdyz",
+ "通过接口获取王者荣耀游戏数据",
"1.0.0",
- "https://github.com/qsc20001102/astrbot_plugin_gok"
+ "https://github.com/qsc20001102/astrbot_plugin_gok.git"
)
class GokApiPlugin(Star):
def __init__(self, context: Context, config: AstrBotConfig):
super().__init__(context)
#获取配置
self.conf = config
+
# 本地数据存储路径
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:
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):
"""可选择实现异步的插件初始化方法,当实例化该插件类之后会自动调用该方法。"""
- # 数据库配置
- 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:
- data = await self.wzry.all_user()
- logger.info(f"王者荣耀营地列表查询结果{data}")
- yield event.plain_result(data)
+ # sqlite 实例化
+ self.sql_db = AsyncSQLiteDB(self.sqlite_path)
+ 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:
- logger.error(f"功能函数执行错误: {e}")
- yield event.plain_result("猪脑过载,请稍后再试")
+ logger.error(f"功能模块初始化失败: {e}")
+ raise
- @wz.command("添加")
- async def wz_adduesr(self, event: AstrMessageEvent,id: str,name: str):
- """王者 添加用户"""
- 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("猪脑过载,请稍后再试")
+ # 指令集
+ self.ini_command_map()
+ logger.info("GOK 异步插件初始化完成")
async def terminate(self):
"""可选择实现异步的插件销毁方法,当插件被卸载/停用时会调用。"""
- await self.db.close_pool()
- # 后台进程销毁
- logger.info("王者荣耀插件已卸载/停用")
\ No newline at end of file
+ if self.gokfun:
+ await self.gokfun.close()
+ 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
+ }
+
+
+
diff --git a/metadata.yaml b/metadata.yaml
index bd7f119..9bd270e 100644
--- a/metadata.yaml
+++ b/metadata.yaml
@@ -1,6 +1,6 @@
-name: astrbot_plugin_gok # 这是你的插件的唯一识别名。
-display_name: 王者荣耀数据查询工具 # 插件的显示名称
-desc: 王者荣耀的战绩、资料等内容的查询 # 插件简短描述
-version: v1.0.0 # 插件版本号。格式:v1.1.1 或者 v1.1
-author: 飞翔大野猪 # 作者
-repo: https://github.com/qsc20001102/astrbot_plugin_gok # 插件的仓库地址
+name: astrbot_plugin_gok
+display_name: 王者荣耀游戏数据查询工具
+desc: 王者荣耀的战绩、资料等内容的查询
+version: v1.0.0
+author: 飞翔大野猪
+repo: https://github.com/qsc20001102/astrbot_plugin_gok
diff --git a/requirements.txt b/requirements.txt
index 3132c6a..aee3d98 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1 @@
-aiomysql
-matplotlib
\ No newline at end of file
+aiosqlite
\ No newline at end of file
diff --git a/templates/helps.html b/templates/helps.html
new file mode 100644
index 0000000..ff0d7ed
--- /dev/null
+++ b/templates/helps.html
@@ -0,0 +1,157 @@
+
+
+
astrbot_plugin_gok 指令说明与使用示例
+| ID | -名称 | -描述 | -时间 | -记录者 | -
|---|---|---|---|---|
| {{ m.id }} | -{{ m.name }} | -{{ m.text }} | -{{ m.time }} | -{{ m.user }} | -