--- /dev/null
+# -*- coding: utf-8 -*-
+import logging
+from collections import defaultdict
+from operator import itemgetter
+
+from distbot.bot.worker import Worker
+from distbot.common.action import Action
+from distbot.common.message import get_nick_from_message, get_words
+
+logger = logging.getLogger(__name__)
+
+
+class Voting(Worker):
+ # TODO: reporting, close-vote, autoclose?
+ binding_keys = ["vote.*", "nick.new-voting.*.#", "nick.close-voting"]
+ description = "start a voting using nick: new-voting <subject> <option(s)>, vote with vote 1/2/... if there are multiple or vote y/vote n for singular options. Use quotes as usual."
+
+
+ def __init__(self, actionqueue, queue="work"):
+ super().__init__(actionqueue, queue)
+ self.options = ["CDU", "SPD", "Trump", "nope"]
+ self.vote_subject = "Wer gewinnt die Wahl in Bielefeld 2024?"
+ self.votes = set([("TRex", "nope"), ("niemand", "Trump"), ("eggy", "CDU"), ("Saxman", "Trump"), ("urlbug", "Trump")])
+
+ def format_result(self):
+ results = defaultdict(int)
+ for user, vote in self.votes:
+ results[vote] += 1
+ return ", ".join([f"{option}: {count}" for option, count in sorted(results.items(), key=itemgetter(1), reverse=True)])
+
+
+ def close_voting(self):
+ if self.vote_subject:
+ result_msg = f"Voting '{self.vote_subject}' (with options {', '.join(self.options)}) is now closed. Votes: {str(self.format_result())}"
+ self.vote_subject = None
+ self.options.clear()
+ self.votes.clear()
+ else:
+ result_msg = "No active voting to close."
+ return Action(msg=result_msg)
+
+ def get_options(self):
+ binary = len(self.options) == 1
+ if binary:
+ voting_options = ["y", "n"]
+ else:
+ voting_options = list(map(str, range(1, len(self.options)+1)))
+ return voting_options
+
+ def parse_body(self, msg):
+ words = get_words(msg)
+
+ if words[0] == "vote":
+ if not self.vote_subject:
+ return Action(msg="No active voting in progress.")
+ sender = get_nick_from_message(msg)
+ vote = words[1]
+
+ if vote in self.options:
+ self.votes.add((sender, vote))
+ elif vote.isdigit() and 1 <= int(vote) <= len(self.options):
+ index = int(vote) - 1
+ self.votes.add((sender, self.options[index]))
+ elif len(self.options) == 1 and vote in "yn":
+ self.votes.add((sender, vote))
+ else:
+ return Action(msg="Invalid vote. Please use 'vote <option>' or 'vote <index> (e.g., vote 1)'.")
+
+ elif words[0] == "new-voting":
+ if self.vote_subject:
+ return Action(msg="Voting in progress. Close that one first.")
+ else:
+ self.vote_subject = words[1]
+ self.options = words[2:]
+ valid_options = ", ".join(self.get_options())
+ return Action(msg=f"Voting started. Please respond with any of {valid_options}")
+
+ elif words[0] == "close-voting":
+ if not self.vote_subject:
+ return Action(msg="No active voting in progress.")
+ else:
+ return self.close_voting()
+
+ALL = [Voting]
\ No newline at end of file
--- /dev/null
+import pytest
+from distbot.plugins import votepoll
+
+
+@pytest.mark.parametrize(
+ argnames=["words", "expected", "duration"],
+ argvalues=[
+ (["all", "vs", "nothing"], ["all", "nothing"], None),
+ (["all", "vs", "nothing", "vs"], ["all", "nothing"], None),
+ (["all", "vs", "nothing", "vs", "1000"], ["all", "nothing", "1000"], None),
+ (["all", "vs", "nothing", "vs", "emacs", "1000"], ["all", "nothing", "emacs"], 1000),
+ ]
+)
+def test_parse_words_for_poll_options(words, expected, duration):
+ result, d = votepoll.VotePoll.parse_words_for_poll_options(words)
+ assert result == expected
+ assert d == duration
+
+
+def test_votes():
+ assert votepoll.Poll(["a", "b", "c"], {}, 0.0).get_votes("a") == 0
+ assert votepoll.Poll(["a", "b", "c"], {"someone": "a"}, 0.0).get_votes("a") == 1
+
+
+def test_status_report():
+ report = votepoll.Poll(["a", "b", "c"], {"someone": "a"}, 0.0).status_report()
+ assert report == """**Poll status:**
+ * A - a: 1
+ * B - b: 0
+ * C - c: 0
+**Winner:** a (someone)"""
+
+
+def test_status_report_tie():
+ report = votepoll.Poll(["a", "b", "c"], {"someone": "a", "noone": "b"}, 0.0).status_report()
+ assert report == """**Poll status:**
+ * A - a: 1
+ * B - b: 1
+ * C - c: 0
+**Winners:** a (someone), b (noone)"""