From 41291220169f850798c658a789ec3f5960b5156d Mon Sep 17 00:00:00 2001 From: ninemine <1371605831@qq.com> Date: Sat, 8 Nov 2025 14:10:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=BF=90=E5=8A=BF=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .tasks/2025-11-08_2_fortune-system.md | 34 ++++++++ Assets/config.json | 4 +- PWF | 2 +- Plugins/WPSAPI.py | 3 +- Plugins/WPSFortuneSystem.py | 117 ++++++++++++++++++++++++++ 5 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 .tasks/2025-11-08_2_fortune-system.md create mode 100644 Plugins/WPSFortuneSystem.py diff --git a/.tasks/2025-11-08_2_fortune-system.md b/.tasks/2025-11-08_2_fortune-system.md new file mode 100644 index 0000000..68f60b4 --- /dev/null +++ b/.tasks/2025-11-08_2_fortune-system.md @@ -0,0 +1,34 @@ +# 背景 +文件名:2025-11-08_2_fortune-system.md +创建于:2025-11-08_13:42:44 +创建者:liubai095\\asus +主分支:main +任务分支:未创建 +Yolo模式:Off + +# 任务描述 +在 `Plugins/WPSFortuneSystem.py` 中开发一个与运气相关的系统,提供一个无参数指令“运势”,输出 -1 与 1 之间的数值及对应阶段文本,该数值基于当前小时与 `user_id` 哈希计算后乘以系数以保持稳定。将该数值计算封装为可复用接口,供其他允许被运气影响的系统调用,以影响结果。 + +# 项目概览 +新WPS Bot 插件系统,包含核心模块与多种插件,需与现有插件接口保持兼容。 + +# 分析 +当前插件体系通过 `PluginInterface.execute` 将实例注册到 `Architecture`,并在回调路由中按首个指令词定位插件;`WPSAPI` 及 `BasicWPSInterface` 已封装 webhook 回复、@ 解析等能力,提供统一的 Markdown 推送流程。运势插件继承该体系即可获得命令入口,并允许其他模块通过 `Architecture.Get(WPSFortuneSystem)` 访问其接口。 + +# 提议的解决方案 +方案A:在 `WPSFortuneSystem` 内实现 `FortuneCalculator`,以 `datetime.now().hour` 与 `user_id` 组合后的哈希映射到 (-1, 1),再借助细粒度阈值表输出阶段文本;对外暴露 `get_fortune_info(user_id, dt=None)`,同时注册 `运势`/`fortune` 指令返回 Markdown。 +方案B:将哈希映射与阶段判定抽离到 `Plugins/utils/fortune_service.py` 等独立模块,插件只负责命令交互;其他系统可直接导入服务或通过 `Architecture` 获取实例,需留意插件加载顺序与依赖。 + +# 当前执行步骤:"3. 分析任务相关代码" + +# 任务进度 +2025-11-08_14:01:11 +- 已修改:Plugins/WPSFortuneSystem.py +- 更改:实现整点哈希运势计算、阶段判定与指令回调 +- 原因:提供可复用的运势接口与“运势”指令 +- 阻碍因素:无 +- 状态:成功 + +# 最终审查 +- 代码审查:实现与计划一致,运势计算、阶段判定及接口公开范围均符合预期。 +- 测试情况:通过。 diff --git a/Assets/config.json b/Assets/config.json index 10c5b46..144c54c 100644 --- a/Assets/config.json +++ b/Assets/config.json @@ -15,6 +15,8 @@ "plugin_dir": "Plugins", "host": "0.0.0.0", "port": 8000, - "verbose": false + "verbose": false, + "scheduler_tick_ms": 60000, + "scheduler_max_batch": 1000 } } \ No newline at end of file diff --git a/PWF b/PWF index b5fe233..16ef75c 160000 --- a/PWF +++ b/PWF @@ -1 +1 @@ -Subproject commit b5fe23342df074068a87446c9094036e2e36ba92 +Subproject commit 16ef75c3ce9132b189d5876531bfaba789e815e7 diff --git a/Plugins/WPSAPI.py b/Plugins/WPSAPI.py index bcef8eb..76bca0c 100644 --- a/Plugins/WPSAPI.py +++ b/Plugins/WPSAPI.py @@ -1,13 +1,14 @@ from PWF.Convention.Runtime.Config import * from PWF.CoreModules.plugin_interface import PluginInterface from PWF.CoreModules.flags import * +from PWF.Convention.Runtime.Architecture import Architecture from PWF.Convention.Runtime.GlobalConfig import ProjectConfig from PWF.Convention.Runtime.Web import ToolURL from PWF.Convention.Runtime.String import LimitStringLength import httpx import re -logger = ProjectConfig() +logger: ProjectConfig = Architecture.Get(ProjectConfig) MAIN_WEBHOOK_URL = logger.FindItem("main_webhook_url", "") logger.SaveProperties() diff --git a/Plugins/WPSFortuneSystem.py b/Plugins/WPSFortuneSystem.py new file mode 100644 index 0000000..16fc990 --- /dev/null +++ b/Plugins/WPSFortuneSystem.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +import hashlib +from datetime import datetime +from functools import lru_cache +from typing import Any, Dict, List, Tuple, Type, override + +from PWF.Convention.Runtime.Architecture import Architecture +from PWF.Convention.Runtime.GlobalConfig import ConsoleFrontColor, ProjectConfig + +from .WPSAPI import WPSAPI + +logger: ProjectConfig = Architecture.Get(ProjectConfig) + +_HASH_BYTES = 16 +_HASH_MAX = (1 << (_HASH_BYTES * 8)) - 1 +_FORTUNE_STAGE_TABLE: List[Tuple[float, str]] = [ + (-0.9, "厄运深谷"), + (-0.7, "凶多吉少"), + (-0.5, "多有波折"), + (-0.3, "略显低迷"), + (-0.1, "风平浪静"), + (0.1, "小有起色"), + (0.3, "渐入佳境"), + (0.5, "好运上扬"), + (0.7, "顺风顺水"), + (0.9, "鸿运当头"), + (1.01, "天命所归"), +] + + +class WPSFortuneSystem(WPSAPI): + """基于整点哈希的运势系统,可供其他模块复用""" + + @override + def dependencies(self) -> List[Type]: + return [WPSAPI] + + @override + def is_enable_plugin(self) -> bool: + return True + + @override + def wake_up(self) -> None: + logger.Log("Info", f"{ConsoleFrontColor.GREEN}WPSFortuneSystem 插件已加载{ConsoleFrontColor.RESET}") + self.register_plugin("运势") + self.register_plugin("fortune") + + @override + async def callback(self, message: str, chat_id: int, user_id: int) -> str | None: + fortune_message = self._format_fortune_message(user_id) + return await self.send_markdown_message(fortune_message, chat_id, user_id) + + def get_fortune_value(self, user_id: int, dt: datetime | None = None) -> float: + hour_dt, hour_key = self._resolve_hour(dt) + return self._compute_fortune_value(user_id, hour_key) + + def get_fortune_stage(self, user_id: int, dt: datetime | None = None) -> str: + value = self.get_fortune_value(user_id, dt) + return self._match_stage(value) + + def get_fortune_info(self, user_id: int, dt: datetime | None = None) -> Dict[str, Any]: + hour_dt, hour_key = self._resolve_hour(dt) + value = self._compute_fortune_value(user_id, hour_key) + stage = self._match_stage(value) + return { + "value": value, + "stage": stage, + "hour_key": hour_key, + "hour_label": hour_dt.strftime("%Y-%m-%d %H:00"), + "timestamp": hour_dt.isoformat(), + } + + def _resolve_hour(self, dt: datetime | None) -> Tuple[datetime, str]: + target_dt = dt or datetime.now() + hour_dt = target_dt.replace(minute=0, second=0, microsecond=0) + return hour_dt, hour_dt.isoformat() + + def _format_fortune_message(self, user_id: int) -> str: + info = self.get_fortune_info(user_id) + value_display = f"{info['value']:.4f}" + return ( + "# 🎲 运势占卜\n" + f"- 运势值:`{value_display}`\n" + f"- 运势阶段:{info['stage']}\n" + f"- 基准整点:{info['hour_label']}\n" + "> 运势每整点刷新,允许运气加成的系统会复用同一结果。" + ) + + def _match_stage(self, value: float) -> str: + for upper_bound, label in _FORTUNE_STAGE_TABLE: + if value < upper_bound: + return label + return _FORTUNE_STAGE_TABLE[-1][1] + + @staticmethod + @lru_cache(maxsize=2048) + def _cached_hash_value(user_id: int, hour_key: str) -> float: + payload = f"{user_id}:{hour_key}".encode("utf-8") + digest = hashlib.sha256(payload).digest()[:_HASH_BYTES] + integer = int.from_bytes(digest, "big") + normalized = integer / _HASH_MAX if _HASH_MAX else 0.0 + mapped = normalized * 2 - 1 + return max(-0.9999, min(0.9999, mapped)) + + def _compute_fortune_value(self, user_id: int, hour_key: str) -> float: + try: + return self._cached_hash_value(user_id, hour_key) + except Exception as exc: + logger.Log( + "Warning", + f"{ConsoleFrontColor.YELLOW}计算运势时出现异常: {exc}{ConsoleFrontColor.RESET}", + ) + return 0.0 + + +__all__ = ["WPSFortuneSystem"]