新增运势系统
This commit is contained in:
34
.tasks/2025-11-08_2_fortune-system.md
Normal file
34
.tasks/2025-11-08_2_fortune-system.md
Normal file
@@ -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
|
||||
- 更改:实现整点哈希运势计算、阶段判定与指令回调
|
||||
- 原因:提供可复用的运势接口与“运势”指令
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
# 最终审查
|
||||
- 代码审查:实现与计划一致,运势计算、阶段判定及接口公开范围均符合预期。
|
||||
- 测试情况:通过。
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
2
PWF
2
PWF
Submodule PWF updated: b5fe23342d...16ef75c3ce
@@ -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()
|
||||
|
||||
|
||||
117
Plugins/WPSFortuneSystem.py
Normal file
117
Plugins/WPSFortuneSystem.py
Normal file
@@ -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"]
|
||||
Reference in New Issue
Block a user