Add poll response counter

This commit is contained in:
Panoramic 2024-06-11 22:33:43 +03:00
parent ad28cbd963
commit a304f0a34d
Signed by: Panoramic
GPG Key ID: 29FEDD73E66D32F1
3 changed files with 124 additions and 4 deletions

View File

@ -41,6 +41,9 @@ vetting:
space_id: "!xxx:xxx" space_id: "!xxx:xxx"
# Voting time in seconds # Voting time in seconds
voting_time: 172800 voting_time: 172800
# Requirements for getting accepted
min_yes_votes: 1
max_no_votes: 0
# Logging setup # Logging setup
logging: logging:

View File

@ -117,6 +117,13 @@ class Config:
self.voting_time = int(self._get_cfg(["vetting", "voting_time"], required=True)) self.voting_time = int(self._get_cfg(["vetting", "voting_time"], required=True))
self.min_yes_votes = int(
self._get_cfg(["vetting", "min_yes_votes"], required=True)
)
self.max_no_votes = int(
self._get_cfg(["vetting", "max_no_votes"], required=True)
)
def _get_cfg( def _get_cfg(
self, self,
path: List[str], path: List[str],

View File

@ -2,8 +2,15 @@ import asyncio
import logging import logging
import time import time
from nio import AsyncClient, RoomSendError from nio import (
AsyncClient,
RoomMessagesError,
RoomSendError,
RoomSendResponse,
UnknownEvent,
)
from vetting_bot.chat_functions import react_to_event, send_text_to_room
from vetting_bot.config import Config from vetting_bot.config import Config
from vetting_bot.storage import Storage from vetting_bot.storage import Storage
@ -48,6 +55,8 @@ class Timer:
asyncio.create_task(_task()) asyncio.create_task(_task())
async def _end_poll(self, mxid: str, poll_event_id: str): async def _end_poll(self, mxid: str, poll_event_id: str):
logger.info("Ending poll for %s - %s", mxid, poll_event_id)
# Send poll end event
event_content = { event_content = {
"m.relates_to": { "m.relates_to": {
"rel_type": "m.reference", "rel_type": "m.reference",
@ -65,7 +74,108 @@ class Timer:
logger.error(poll_resp, stack_info=True) logger.error(poll_resp, stack_info=True)
return return
self.store.cursor.execute( # Gather votes
"UPDATE vetting SET vote_ended = 1 WHERE mxid = ?", message_filter = {
(mxid,), # "types": ["org.matrix.msc3381.poll.response"], # this doesn't work for some reason :/
"rooms": [self.config.vetting_room_id],
}
vote_count = {
"yes": 0,
"no": 0,
"blank": 0,
}
users_voted = set()
# Loop until we find all events that could be related to the poll
# (max 20 times: 20 * 20 = up to 400 events deep or until we find the poll event)
start_token = ""
for _ in range(0, 20):
logger.debug("Requesting events")
message_resp = await self.client.room_messages(
room_id=self.config.vetting_room_id,
start=start_token,
limit=20,
message_filter=message_filter,
)
if isinstance(message_resp, RoomMessagesError):
logging.error(message_resp, stack_info=True)
text = "Unable to gather votes."
await send_text_to_room(self.client, self.config.vetting_room_id, text)
return
# Resume next request where this ends
start_token = message_resp.end
# Count votes
for event in message_resp.chunk:
# Only process poll response events
if not isinstance(event, UnknownEvent):
continue
if event.type != "org.matrix.msc3381.poll.response":
continue
content = event.source.get("content")
try:
# Check if this response is for the correct poll
related_event_id = content["m.relates_to"]["event_id"]
if related_event_id != poll_event_id:
continue
# Add vote to count
answer = content["org.matrix.msc3381.poll.response"]["answers"][0]
# Only count the last poll response event
if event.sender in users_voted:
continue
users_voted.add(event.sender)
vote_count[answer] += 1
except KeyError:
pass
# Check if we found the initial poll event
if any([event.event_id == poll_event_id for event in message_resp.chunk]):
break
votes_responses = "".join(
[f"\n{answer.title()}: {count};" for answer, count in vote_count.items()]
) )
# Make the decision by checking requirements
decision = (
vote_count["yes"] >= self.config.min_yes_votes
and vote_count["no"] <= self.config.max_no_votes
)
decision_text = (
"Confirm inviting this person to the Federation by reacting."
if decision
else "Votes do not match the requirements, not inviting."
)
text = (
f"Voting for `{mxid}` has ended. Counted votes are:\n"
f"{votes_responses}\n\n{decision_text}"
)
decision_resp = await send_text_to_room(
self.client, self.config.vetting_room_id, text
)
if not isinstance(decision_resp, RoomSendResponse):
logger.error(decision_resp)
return
if decision:
await react_to_event(
self.client,
self.config.vetting_room_id,
decision_resp.event_id,
"confirm",
)
# Finally - update database
# self.store.cursor.execute(
# "UPDATE vetting SET vote_ended = 1 WHERE mxid = ?",
# (mxid,),
# )