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:
2026-09-01 13:13:40 +08:00
co-authored by Claude
parent 1783c60afa
commit b61d09f09f
3201 changed files with 160436 additions and 171 deletions
@@ -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")