重置数据存贮结构
This commit is contained in:
2
PWF
2
PWF
Submodule PWF updated: 14ce7e6e3f...a1b3f51b61
@@ -1,39 +1,17 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import time
|
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
|
||||||
from PWF.Convention.Runtime.Config import *
|
from PWF.Convention.Runtime.Config import *
|
||||||
from PWF.Convention.Runtime.Architecture import Architecture
|
from PWF.Convention.Runtime.Architecture import Architecture
|
||||||
from PWF.Convention.Runtime.File import ToolFile
|
|
||||||
from PWF.Convention.Runtime.GlobalConfig import ProjectConfig
|
from PWF.Convention.Runtime.GlobalConfig import ProjectConfig
|
||||||
|
|
||||||
|
from PWF.CoreModules.plugin_interface import DatabaseModel, get_db
|
||||||
|
|
||||||
from .WPSAPI import WPSAPI
|
from .WPSAPI import WPSAPI
|
||||||
|
|
||||||
config = ProjectConfig()
|
config = ProjectConfig()
|
||||||
|
|
||||||
|
|
||||||
class UserConfigEntry(BaseModel):
|
|
||||||
chat_id: int
|
|
||||||
user_id: int
|
|
||||||
data: Dict[str, Any] = Field(default_factory=dict)
|
|
||||||
updated_at: int = Field(default_factory=lambda: int(time.time()))
|
|
||||||
|
|
||||||
|
|
||||||
class UserConfigStore(BaseModel):
|
|
||||||
entries: List[UserConfigEntry] = Field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
class WPSConfigAPI(WPSAPI):
|
class WPSConfigAPI(WPSAPI):
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self._lock = asyncio.Lock()
|
|
||||||
self._store = UserConfigStore()
|
|
||||||
self._entries: Dict[Tuple[int, int], UserConfigEntry] = {}
|
|
||||||
self._setting = self._build_setting()
|
|
||||||
self._load_store()
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def dependencies(self) -> List[Type]:
|
def dependencies(self) -> List[Type]:
|
||||||
return [WPSAPI]
|
return [WPSAPI]
|
||||||
@@ -42,12 +20,17 @@ class WPSConfigAPI(WPSAPI):
|
|||||||
def is_enable_plugin(self) -> bool:
|
def is_enable_plugin(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _build_setting(self) -> ToolFile:
|
@override
|
||||||
data_file: ToolFile = config.GetFile("user_configs.json", False)
|
def register_db_model(self) -> DatabaseModel:
|
||||||
data_file.TryCreateParentPath()
|
return DatabaseModel(
|
||||||
if data_file.Exists() == False:
|
table_name="user_info",
|
||||||
data_file.SaveAsJson(self._store)
|
column_defs={
|
||||||
return data_file
|
"user_id": "INTEGER",
|
||||||
|
"username": "TEXT DEFAULT ''",
|
||||||
|
"userurl": "TEXT DEFAULT ''",
|
||||||
|
"userpoint": "INTEGER DEFAULT 0"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
def wake_up(self) -> None:
|
def wake_up(self) -> None:
|
||||||
@@ -68,15 +51,7 @@ class WPSConfigAPI(WPSAPI):
|
|||||||
if action == "get" and len(tokens) >= 2:
|
if action == "get" and len(tokens) >= 2:
|
||||||
key = tokens[1].lower()
|
key = tokens[1].lower()
|
||||||
return await self._handle_get(chat_id, user_id, key)
|
return await self._handle_get(chat_id, user_id, key)
|
||||||
# if action in {"adjust", "add", "sub"} and len(tokens) >= 3:
|
|
||||||
# key = tokens[1].lower()
|
|
||||||
# delta_token = tokens[2]
|
|
||||||
# reason = " ".join(tokens[3:]).strip() if len(tokens) > 3 else ""
|
|
||||||
# signed_delta = self._parse_delta(action, delta_token)
|
|
||||||
# if signed_delta is None:
|
|
||||||
# return "❌ 积分变更失败: delta 需要是整数"
|
|
||||||
# return await self._handle_adjust(chat_id, user_id, key, signed_delta, reason)
|
|
||||||
|
|
||||||
return self._help_message()
|
return self._help_message()
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -115,28 +90,22 @@ class WPSConfigAPI(WPSAPI):
|
|||||||
await self._set_value(chat_id, user_id, key, value)
|
await self._set_value(chat_id, user_id, key, value)
|
||||||
return "✅ 已更新个人URL"
|
return "✅ 已更新个人URL"
|
||||||
|
|
||||||
# if key == "user.point":
|
|
||||||
# try:
|
|
||||||
# # points = int(value)
|
|
||||||
# return "❌ 无权限设置积分"
|
|
||||||
# except ValueError:
|
|
||||||
# return "❌ 积分必须是整数"
|
|
||||||
# await self._set_value(chat_id, user_id, key, points)
|
|
||||||
# return f"✅ 已将积分设置为 {points}"
|
|
||||||
|
|
||||||
# return "❌ 未识别的配置键,请使用 user.name / user.url / user.point"
|
|
||||||
return "❌ 未识别的配置键,请使用 user.name / user.url"
|
return "❌ 未识别的配置键,请使用 user.name / user.url"
|
||||||
|
|
||||||
async def _handle_get(self, chat_id: int, user_id: int, key: str) -> str:
|
async def _handle_get(self, chat_id: int, user_id: int, key: str) -> str:
|
||||||
entry = self._get_entry(chat_id, user_id, create=False)
|
record = self._get_user_record(user_id)
|
||||||
if entry is None or key not in entry.data:
|
|
||||||
if key == "user.point":
|
|
||||||
return "当前积分: 0"
|
|
||||||
return f"⚠️ 尚未设置 {key}"
|
|
||||||
|
|
||||||
value = entry.data.get(key)
|
|
||||||
if key == "user.point":
|
if key == "user.point":
|
||||||
return f"当前积分: {int(value)}"
|
points = record.get("userpoint") if record else 0
|
||||||
|
return f"当前积分: {self._coerce_int(points)}"
|
||||||
|
if key == "user.name":
|
||||||
|
value = record.get("username") if record else None
|
||||||
|
elif key == "user.url":
|
||||||
|
value = record.get("userurl") if record else None
|
||||||
|
else:
|
||||||
|
return "❌ 未识别的配置键,请使用 user.name / user.url"
|
||||||
|
|
||||||
|
if value is None or value == "":
|
||||||
|
return f"⚠️ 尚未设置 {key}"
|
||||||
return f"当前 {key} = {value}"
|
return f"当前 {key} = {value}"
|
||||||
|
|
||||||
async def _handle_adjust(self, chat_id: int, user_id: int, key: str, delta: int, reason: str) -> str:
|
async def _handle_adjust(self, chat_id: int, user_id: int, key: str, delta: int, reason: str) -> str:
|
||||||
@@ -149,66 +118,51 @@ class WPSConfigAPI(WPSAPI):
|
|||||||
return f"✅ {detail},当前积分: {new_value}"
|
return f"✅ {detail},当前积分: {new_value}"
|
||||||
|
|
||||||
async def _set_value(self, chat_id: int, user_id: int, key: str, value: Any) -> None:
|
async def _set_value(self, chat_id: int, user_id: int, key: str, value: Any) -> None:
|
||||||
entry = self._get_entry(chat_id, user_id, create=True)
|
if key == "user.name":
|
||||||
entry.data[key] = value
|
self._update_user_field(user_id, "username", value)
|
||||||
entry.updated_at = int(time.time())
|
elif key == "user.url":
|
||||||
await self._save_store()
|
self._update_user_field(user_id, "userurl", value)
|
||||||
|
|
||||||
async def _adjust_points(self, chat_id: int, user_id: int, delta: int, reason: str) -> int:
|
async def _adjust_points(self, chat_id: int, user_id: int, delta: int, reason: str) -> int:
|
||||||
entry = self._get_entry(chat_id, user_id, create=True)
|
return self._adjust_db_points(user_id, delta)
|
||||||
current = self._coerce_int(entry.data.get("user.point", 0))
|
|
||||||
new_value = current + delta
|
|
||||||
entry.data["user.point"] = new_value
|
|
||||||
history: List[Dict[str, Any]] = entry.data.setdefault("user.point_history", []) # type: ignore[assignment]
|
|
||||||
history.append({
|
|
||||||
"delta": delta,
|
|
||||||
"reason": reason,
|
|
||||||
"timestamp": int(time.time()),
|
|
||||||
})
|
|
||||||
if len(history) > 100:
|
|
||||||
entry.data["user.point_history"] = history[-100:]
|
|
||||||
entry.updated_at = int(time.time())
|
|
||||||
await self._save_store()
|
|
||||||
return new_value
|
|
||||||
|
|
||||||
def _get_entry(self, chat_id: int, user_id: int, create: bool) -> Optional[UserConfigEntry]:
|
def _get_user_record(self, user_id: int) -> Optional[Dict[str, Any]]:
|
||||||
key = (chat_id, user_id)
|
cursor = self._db.conn.cursor()
|
||||||
if key in self._entries:
|
cursor.execute(
|
||||||
return self._entries[key]
|
"SELECT user_id, username, userurl, userpoint FROM user_info WHERE user_id = ?",
|
||||||
if not create:
|
(user_id,)
|
||||||
return None
|
)
|
||||||
entry = UserConfigEntry(chat_id=chat_id, user_id=user_id)
|
row = cursor.fetchone()
|
||||||
self._entries[key] = entry
|
return dict(row) if row else None
|
||||||
self._store.entries.append(entry)
|
|
||||||
return entry
|
|
||||||
|
|
||||||
def _ignore_lock_save_store(self) -> None:
|
def _ensure_user_row(self, user_id: int) -> None:
|
||||||
self._store.entries = list(self._entries.values())
|
cursor = self._db.conn.cursor()
|
||||||
try:
|
cursor.execute(
|
||||||
#EasySave.Write(self._store, setting=self._setting)
|
"INSERT INTO user_info (user_id) VALUES (?) ON CONFLICT(user_id) DO NOTHING",
|
||||||
self._setting.SaveAsJson(self._store.model_dump())
|
(user_id,)
|
||||||
except Exception as exc:
|
)
|
||||||
config.Log("Error", f"ConfigPlugin 保存失败: {exc}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
async def _save_store(self) -> None:
|
def _update_user_field(self, user_id: int, column: str, value: Any) -> None:
|
||||||
async with self._lock:
|
self._ensure_user_row(user_id)
|
||||||
self._ignore_lock_save_store()
|
cursor = self._db.conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
f"""
|
||||||
|
INSERT INTO user_info (user_id, {column}) VALUES (?, ?)
|
||||||
|
ON CONFLICT(user_id) DO UPDATE SET {column} = excluded.{column}
|
||||||
|
""",
|
||||||
|
(user_id, value)
|
||||||
|
)
|
||||||
|
|
||||||
def _load_store(self) -> None:
|
def _adjust_db_points(self, user_id: int, delta: int) -> int:
|
||||||
try:
|
self._ensure_user_row(user_id)
|
||||||
store = UserConfigStore.model_validate(self._setting.LoadAsJson())
|
cursor = self._db.conn.cursor()
|
||||||
self._store = store or UserConfigStore()
|
cursor.execute(
|
||||||
except FileNotFoundError:
|
"UPDATE user_info SET userpoint = COALESCE(userpoint, 0) + ? WHERE user_id = ?",
|
||||||
self._store = UserConfigStore()
|
(delta, user_id)
|
||||||
except Exception as exc:
|
)
|
||||||
config.Log("Error", f"{traceback.format_exc()}")
|
cursor.execute("SELECT userpoint FROM user_info WHERE user_id = ?", (user_id,))
|
||||||
self._store = UserConfigStore()
|
row = cursor.fetchone()
|
||||||
|
return self._coerce_int(row[0] if row else 0)
|
||||||
self._entries = {
|
|
||||||
(entry.chat_id, entry.user_id): entry
|
|
||||||
for entry in self._store.entries
|
|
||||||
}
|
|
||||||
|
|
||||||
def _coerce_int(self, value: Any) -> int:
|
def _coerce_int(self, value: Any) -> int:
|
||||||
try:
|
try:
|
||||||
@@ -217,23 +171,24 @@ class WPSConfigAPI(WPSAPI):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
def get_user_name(self, chat_id: int, user_id: int) -> Optional[str]:
|
def get_user_name(self, chat_id: int, user_id: int) -> Optional[str]:
|
||||||
entry = self._entries.get((chat_id, user_id))
|
record = self._get_user_record(user_id)
|
||||||
if not entry:
|
if not record:
|
||||||
return f"user_{user_id}"
|
return f"user_{user_id}"
|
||||||
return str(entry.data.get("user.name", f"user_{user_id}"))
|
value = record.get("username")
|
||||||
|
return str(value) if value else f"user_{user_id}"
|
||||||
|
|
||||||
def get_user_url(self, chat_id: int, user_id: int) -> Optional[str]:
|
def get_user_url(self, chat_id: int, user_id: int) -> Optional[str]:
|
||||||
entry = self._entries.get((chat_id, user_id))
|
record = self._get_user_record(user_id)
|
||||||
if not entry:
|
if not record:
|
||||||
return None
|
return None
|
||||||
value = entry.data.get("user.url")
|
value = record.get("userurl")
|
||||||
return str(value) if value is not None else None
|
return str(value) if value else None
|
||||||
|
|
||||||
def get_user_points(self, chat_id: int, user_id: int) -> int:
|
def get_user_points(self, chat_id: int, user_id: int) -> int:
|
||||||
entry = self._entries.get((chat_id, user_id))
|
record = self._get_user_record(user_id)
|
||||||
if not entry:
|
if not record:
|
||||||
return 0
|
return 0
|
||||||
return self._coerce_int(entry.data.get("user.point", 0))
|
return self._coerce_int(record.get("userpoint", 0))
|
||||||
|
|
||||||
async def adjust_user_points(self, chat_id: int, user_id: int, delta: int, reason: str = "") -> int:
|
async def adjust_user_points(self, chat_id: int, user_id: int, delta: int, reason: str = "") -> int:
|
||||||
return await self._adjust_points(chat_id, user_id, delta, reason)
|
return await self._adjust_points(chat_id, user_id, delta, reason)
|
||||||
|
|||||||
Reference in New Issue
Block a user