Add HeXi bot codebase: custom plugins, web frontends, tests
- hexi core: message handling, rate limiting, cooldown, plugin manager - Custom plugins: BF stats, daily check-in, quotes, persona cards, etc. - Community plugins vendored under hexi/plugins with local fixes - Web admin frontends (learning-chat, persona-admin), unified hexi/web - Tests for rate_limit/cooldown/memes/persona; poetry.lock Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import json
|
||||
from fastapi import FastAPI, HTTPException, Header, Form, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# ✅ 添加 CORS 支持
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.post("/designation/page")
|
||||
async def designation_page():
|
||||
return "1"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(app, host="0.0.0.0", port=16661, log_level="info")
|
||||
@@ -0,0 +1,133 @@
|
||||
import sqlite3
|
||||
import os
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
|
||||
class TableManager:
|
||||
def __init__(self, db_path: str = 'user_data.db'):
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
self.db_path = os.path.join(base_dir, 'user_data.db')
|
||||
|
||||
def _execute(self, sql: str, params: tuple = ()) -> sqlite3.Cursor:
|
||||
"""执行SQL语句的通用方法"""
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
conn.row_factory = sqlite3.Row
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(sql, params)
|
||||
conn.commit()
|
||||
return cursor
|
||||
|
||||
|
||||
class UserManager(TableManager):
|
||||
def add_user(self, qq_id: str, ea_player_name: str, ea_player_id: str, ea_user_id: str, designation: str,
|
||||
medals: str) -> int:
|
||||
"""添加用户"""
|
||||
cursor = self._execute(
|
||||
"INSERT INTO users (qq_id, ea_player_name,ea_player_id,ea_user_id, designation,medals) VALUES (?, ?, ?, ?, ?,?)",
|
||||
(qq_id, ea_player_name, ea_player_id, ea_user_id, designation, medals)
|
||||
)
|
||||
return cursor.lastrowid
|
||||
|
||||
def update_user(self, qq_id: str, ea_player_name: str, ea_player_id: str, ea_user_id: str, designation: str,
|
||||
medals: str) -> int:
|
||||
"""修改用户"""
|
||||
cursor = self._execute(
|
||||
"UPDATE users SET ea_player_name = ?, ea_player_id = ?,ea_user_id = ? WHERE qq_id = ?",
|
||||
(ea_player_name, ea_player_id, ea_user_id, qq_id)
|
||||
)
|
||||
return cursor.rowcount
|
||||
|
||||
def get_user_by_qq(self, qq_id: str) -> Optional[Dict[str, Any]]:
|
||||
cursor = self._execute(
|
||||
"SELECT * FROM users WHERE qq_id = ?",
|
||||
(qq_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
def get_user_by_ea_id(self, ea_user_id: str) -> Optional[Dict[str, Any]]:
|
||||
cursor = self._execute(
|
||||
"SELECT * FROM users WHERE ea_user_id = ?",
|
||||
(ea_user_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
def delete_user_by_qq(self, qq_id: str) -> Optional[Dict[str, Any]]:
|
||||
cursor = self._execute(
|
||||
"DELETE FROM users WHERE qq_id = ?",
|
||||
(qq_id,)
|
||||
)
|
||||
return cursor.rowcount
|
||||
|
||||
def update_dog_tags(self, qq_id: int, medals: str) -> bool:
|
||||
"""更新用户的狗牌列表"""
|
||||
self._execute(
|
||||
"UPDATE users SET medals = ? WHERE id = ?",
|
||||
(medals, qq_id)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
class DesignationManager(TableManager):
|
||||
def create_designation(self, name: str, icon_path: str) -> int:
|
||||
"""创建新标签"""
|
||||
cursor = self._execute(
|
||||
"INSERT INTO designation (name,icon_path) VALUES (?,?)",
|
||||
(name, icon_path)
|
||||
)
|
||||
return cursor.lastrowid
|
||||
|
||||
def get_all_designation(self) -> List[Dict[str, Any]]:
|
||||
"""获取所有标签"""
|
||||
cursor = self._execute("SELECT * FROM designation")
|
||||
return [dict(row) for row in cursor.fetchall()]
|
||||
|
||||
def get_designation_by_id_list(self, ids: List[int]) -> List[Dict[str, Any]]:
|
||||
"""根据 ID 列表获取标签"""
|
||||
|
||||
if not ids:
|
||||
return []
|
||||
|
||||
placeholders = ",".join(["?"] * len(ids))
|
||||
sql = f"""
|
||||
SELECT *
|
||||
FROM designation
|
||||
WHERE id IN ({placeholders})
|
||||
"""
|
||||
|
||||
cursor = self._execute(sql, ids)
|
||||
return [dict(row) for row in cursor.fetchall()]
|
||||
|
||||
|
||||
# class QueryRecordManager(TableManager):
|
||||
# def log_query(self, user_id: str, target_id: str, status: str) -> int:
|
||||
# """记录查询操作"""
|
||||
# cursor = self._execute(
|
||||
# """INSERT INTO query_record
|
||||
# (user_id, target_id, status)
|
||||
# VALUES (?, ?, ?)""",
|
||||
# (user_id, target_id, status)
|
||||
# )
|
||||
# return cursor.lastrowid
|
||||
#
|
||||
# def get_user_history(self, user_id: str) -> List[Dict[str, Any]]:
|
||||
# """获取用户查询历史"""
|
||||
# cursor = self._execute(
|
||||
# "SELECT * FROM query_record WHERE user_id = ?",
|
||||
# (user_id,)
|
||||
# )
|
||||
# return [dict(row) for row in cursor.fetchall()]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tag_db = DesignationManager()
|
||||
# tag_id = tag_db.create_designation("DEV","DEV.png")
|
||||
# tag_id = tag_db.create_designation("DICE", "DICE.png")
|
||||
# tag_id = tag_db.create_designation("IC", "IC.png")
|
||||
# tag_id = tag_db.create_designation("OWNER", "OWNER.png")
|
||||
# print(f"Created tag with ID: {tag_id}")
|
||||
|
||||
record_id = tag_db.get_designation_by_id_list([1, 2, 3])
|
||||
|
||||
print(f"Logged query with ID: {record_id}")
|
||||
@@ -0,0 +1,127 @@
|
||||
import os
|
||||
import sqlite3
|
||||
from typing import List, Tuple
|
||||
|
||||
from nonebot import logger
|
||||
|
||||
|
||||
class DatabaseManager:
|
||||
def __init__(self, db_path: str = 'user_data.db'):
|
||||
self.db_path = db_path
|
||||
self.expected_tables = {
|
||||
'users': [
|
||||
('id', 'INTEGER PRIMARY KEY AUTOINCREMENT'),
|
||||
('qq_id', 'TEXT NOT NULL'),
|
||||
('ea_player_name', 'TEXT UNIQUE'),
|
||||
('ea_player_id', 'TEXT UNIQUE'),
|
||||
('ea_user_id', 'TEXT UNIQUE'),
|
||||
('designation', 'TEXT NOT NULL'),
|
||||
('medals', 'TEXT NOT NULL'),
|
||||
('create_time', 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP')
|
||||
],
|
||||
'designation': [
|
||||
('id', 'INTEGER PRIMARY KEY'),
|
||||
('name', 'TEXT NOT NULL'),
|
||||
('icon_path', 'TEXT NOT NULL'),
|
||||
('create_time', 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP')
|
||||
],
|
||||
'medals': [
|
||||
('id', 'INTEGER PRIMARY KEY'),
|
||||
('name', 'TEXT NOT NULL'),
|
||||
('icon_path', 'TEXT NOT NULL'),
|
||||
('create_time', 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP')
|
||||
]
|
||||
,
|
||||
'query_record': [
|
||||
('id', 'INTEGER PRIMARY KEY'),
|
||||
('user_id', 'TEXT NOT NULL'),
|
||||
('ea_player_name', 'TEXT NOT NULL'),
|
||||
('ea_player_id', 'TEXT NOT NULL'),
|
||||
('ea_user_id', 'TEXT NOT NULL'),
|
||||
('status', 'TEXT NOT NULL'),
|
||||
('create_time', 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP')
|
||||
]
|
||||
}
|
||||
|
||||
def initialize_database(self) -> bool:
|
||||
"""初始化数据库并创建表结构"""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 创建所有表
|
||||
for table_name, columns in self.expected_tables.items():
|
||||
columns_sql = ', '.join(f'{col} {typ}' for col, typ in columns)
|
||||
cursor.execute(f'''
|
||||
CREATE TABLE IF NOT EXISTS {table_name} (
|
||||
{columns_sql}
|
||||
)
|
||||
''')
|
||||
|
||||
conn.commit()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Database initialization failed: {str(e)}")
|
||||
if os.path.exists(self.db_path):
|
||||
conn.close() # 确保连接关闭
|
||||
for _ in range(3): # 重试机制
|
||||
try:
|
||||
os.remove(self.db_path)
|
||||
break
|
||||
except PermissionError:
|
||||
time.sleep(0.5)
|
||||
return False
|
||||
|
||||
def _verify_database_integrity(self, conn: sqlite3.Connection) -> bool:
|
||||
"""验证数据库完整性"""
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 检查表是否存在
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
||||
existing_tables = {row[0] for row in cursor.fetchall()}
|
||||
|
||||
if existing_tables != set(self.expected_tables.keys()):
|
||||
logger.warning(f"Missing tables: {set(self.expected_tables.keys()) - existing_tables}")
|
||||
return False
|
||||
|
||||
# 检查每个表的结构
|
||||
for table_name, expected_columns in self.expected_tables.items():
|
||||
cursor.execute(f"PRAGMA table_info({table_name})")
|
||||
actual_columns = [(row[1], row[2]) for row in cursor.fetchall()]
|
||||
|
||||
if not self._compare_columns(actual_columns, expected_columns):
|
||||
logger.warning(f"Table {table_name} structure mismatch")
|
||||
return False
|
||||
|
||||
# 执行完整性检查
|
||||
cursor.execute("PRAGMA integrity_check")
|
||||
integrity_result = cursor.fetchone()
|
||||
if integrity_result[0] != 'ok':
|
||||
logger.warning(f"Integrity check failed: {integrity_result}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _compare_columns(self, actual: List[Tuple], expected: List[Tuple]) -> bool:
|
||||
"""比较实际列与预期列是否匹配"""
|
||||
if len(actual) != len(expected):
|
||||
return False
|
||||
|
||||
for (act_col, act_type), (exp_col, exp_type) in zip(actual, expected):
|
||||
if act_col.lower() != exp_col.lower():
|
||||
return False
|
||||
if not act_type.upper().startswith(exp_type.upper()):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# 初始化
|
||||
if __name__ == '__main__':
|
||||
db_manager = DatabaseManager()
|
||||
if db_manager.initialize_database():
|
||||
print("Database initialized successfully")
|
||||
print(f"Database file created at: {os.path.abspath(db_manager.db_path)}")
|
||||
else:
|
||||
print("Database initialization failed")
|
||||
Reference in New Issue
Block a user