from __future__ import annotations import time from collections import defaultdict from typing import Any class PollResultTracker: """投票结果跟踪和聚合。""" def __init__(self): self._polls: dict[str, dict[str, Any]] = {} def init_poll( self, poll_id: str, question: str, options: list[str], post_id: str = "", chat_id: str = "", ) -> None: self._polls[poll_id] = { "poll_id": poll_id, "question": question, "options": options, "post_id": post_id, "chat_id": chat_id, "votes": defaultdict(set), "voters": {}, "created_at": time.monotonic(), "ended": False, } def record_vote(self, poll_id: str, option: str, user_id: str) -> bool: poll = self._polls.get(poll_id) if not poll or poll["ended"]: return False for existing_opt, voters in poll["votes"].items(): if user_id in voters: voters.discard(user_id) poll["votes"][option].add(user_id) poll["voters"][user_id] = option return True def end_poll(self, poll_id: str) -> dict | None: poll = self._polls.get(poll_id) if not poll: return None poll["ended"] = True return poll def get_results(self, poll_id: str) -> dict | None: poll = self._polls.get(poll_id) if not poll: return None results = {"question": poll["question"], "options": {}} for option in poll["options"]: results["options"][option] = len(poll["votes"].get(option, set())) results["total_votes"] = len(poll["voters"]) results["ended"] = poll["ended"] return results def format_results_message(self, poll_id: str) -> str: results = self.get_results(poll_id) if not results: return "未找到投票" max_opt_len = max((len(opt) for opt in results["options"]), default=0) lines = [f"**📊 投票结果: {results['question']}**\n"] total = max(results["total_votes"], 1) for option, count in results["options"].items(): bar_len = max(1, int(20 * count / total)) bar = "█" * bar_len + "░" * (20 - bar_len) lines.append(f"{option.ljust(max_opt_len)} [{bar}] {count} 票") lines.append(f"\n总票数: {results['total_votes']}") if results["ended"]: lines.append("\n此投票已结束") return "\n".join(lines) _tracker = PollResultTracker() def get_poll_tracker() -> PollResultTracker: return _tracker async def handle_poll_completion( adapter: Any, poll_id: str, chat_id: str, ) -> dict: tracker = getattr(adapter, "_poll_tracker", None) or get_poll_tracker() poll = tracker.end_poll(poll_id) if not poll: return {"error": "Poll not found"} results_msg = tracker.format_results_message(poll_id) if adapter and hasattr(adapter, "send"): from yuxi.channels.models import ChannelIdentity, ChannelResponse channel_type = getattr(adapter, "channel_type", None) channel_id = getattr(adapter, "channel_id", "mattermost") response = ChannelResponse( identity=ChannelIdentity( channel_id=channel_id, channel_type=channel_type, channel_user_id="system", channel_chat_id=chat_id or poll.get("chat_id", ""), ), content=results_msg, reply_to_message_id=poll.get("post_id", ""), metadata={"poll_result": True, "poll_id": poll_id}, ) result = await adapter.send(response) return {"success": result.success, "message_id": result.message_id} return {"results_message": results_msg}