diff --git a/sample.config.yaml b/sample.config.yaml index 5ad9e4e..816d01f 100644 --- a/sample.config.yaml +++ b/sample.config.yaml @@ -34,11 +34,13 @@ storage: # Vetting options vetting: + # The main space, where new recruits will be invited to + main_space_id: "!xxx:xxx" # Internal room id for the main vetting room # Commands will be run here - room_id: "!xxx:xxx" - # A space which will house all the vetting rooms - space_id: "!xxx:xxx" + vetting_room_id: "!xxx:xxx" + # A sub-space which will house all the vetting rooms + vetting_space_id: "!xxx:xxx" # Voting time in seconds voting_time: 172800 # Requirements for getting accepted diff --git a/vetting_bot/callbacks.py b/vetting_bot/callbacks.py index 2918120..9464f68 100644 --- a/vetting_bot/callbacks.py +++ b/vetting_bot/callbacks.py @@ -6,6 +6,7 @@ from nio import ( JoinError, MatrixRoom, MegolmEvent, + ReactionEvent, RoomGetEventError, RoomMessageText, UnknownEvent, @@ -187,25 +188,41 @@ class Callbacks: # red_x_and_lock_emoji, # ) + async def reaction(self, room: MatrixRoom, event: ReactionEvent): + """Callback when a reaction event is received. + + Args: + room (MatrixRoom): The room the reaction was sent in. + event (ReactionEvent): The event itself. + """ + + # Ignore own events + if event.sender == self.client.user_id: + return + + if room.room_id == self.config.vetting_room_id: + if event.key == "confirm": + # Check which user the reaction is for (if any) + self.store.cursor.execute( + "SELECT mxid FROM vetting WHERE decision_event_id = ?", + (event.reacts_to,), + ) + row = self.store.cursor.fetchone() + if row is None: + return + + # Invite the user + logger.info("Inviting new user (%s) to the Federation.", row[0]) + await self.client.room_invite(self.config.main_space_id, row[0]) + async def unknown(self, room: MatrixRoom, event: UnknownEvent) -> None: """Callback for when an event with a type that is unknown to matrix-nio is received. - Currently this is used for reaction events, which are not yet part of a released - matrix spec (and are thus unknown to nio). Args: room: The room the reaction was sent in. event: The event itself. """ - if event.type == "m.reaction": - # Get the ID of the event this was a reaction to - relation_dict = event.source.get("content", {}).get("m.relates_to", {}) - - reacted_to = relation_dict.get("event_id") - if reacted_to and relation_dict.get("rel_type") == "m.annotation": - await self._reaction(room, event, reacted_to) - return - logger.debug( f"Got unknown event with type to {event.type} from {event.sender} in {room.room_id}." ) diff --git a/vetting_bot/config.py b/vetting_bot/config.py index b5c93d3..74d4cc6 100644 --- a/vetting_bot/config.py +++ b/vetting_bot/config.py @@ -107,13 +107,23 @@ class Config: self.command_prefix = self._get_cfg(["command_prefix"], default="!c") + " " # Vetting setup - self.vetting_room_id = self._get_cfg(["vetting", "room_id"], required=True) - if not re.match("!.*:.*", self.vetting_room_id): - raise ConfigError("vetting.room_id must be in the form !xxx:domain") + self.main_space_id = self._get_cfg(["vetting", "main_space_id"], required=True) + if not re.match("!.*:.*", self.main_space_id): + raise ConfigError("vetting.main_space_id must be in the form !xxx:domain") - self.vetting_space_id = self._get_cfg(["vetting", "space_id"], required=True) + self.vetting_room_id = self._get_cfg( + ["vetting", "vetting_room_id"], required=True + ) + if not re.match("!.*:.*", self.vetting_room_id): + raise ConfigError("vetting.vetting_room_id must be in the form !xxx:domain") + + self.vetting_space_id = self._get_cfg( + ["vetting", "vetting_space_id"], required=True + ) if not re.match("!.*:.*", self.vetting_space_id): - raise ConfigError("vetting.space_id must be in the form !xxx:domain") + raise ConfigError( + "vetting.vetting_space_id must be in the form !xxx:domain" + ) self.voting_time = int(self._get_cfg(["vetting", "voting_time"], required=True)) diff --git a/vetting_bot/main.py b/vetting_bot/main.py index 3745ad2..9ee0570 100644 --- a/vetting_bot/main.py +++ b/vetting_bot/main.py @@ -12,6 +12,7 @@ from nio import ( LocalProtocolError, LoginError, MegolmEvent, + ReactionEvent, RoomMessageText, UnknownEvent, ) @@ -68,6 +69,7 @@ async def main(): callbacks.invite_event_filtered_callback, (InviteMemberEvent,) ) client.add_event_callback(callbacks.decryption_failure, (MegolmEvent,)) + client.add_event_callback(callbacks.reaction, (ReactionEvent,)) client.add_event_callback(callbacks.unknown, (UnknownEvent,)) # Keep trying to reconnect on failure (with some time in-between) diff --git a/vetting_bot/storage.py b/vetting_bot/storage.py index b912688..1d60734 100644 --- a/vetting_bot/storage.py +++ b/vetting_bot/storage.py @@ -112,7 +112,8 @@ class Storage: vetting_create_time INT(12), voting_start_time INT(12), poll_event_id VARCHAR(255), - vote_ended BOOLEAN NOT NULL DEFAULT FALSE + vote_ended BOOLEAN NOT NULL DEFAULT FALSE, + decision_event_id VARCHAR(255) ) """ ) diff --git a/vetting_bot/timer.py b/vetting_bot/timer.py index 49e4fd0..3374bb8 100644 --- a/vetting_bot/timer.py +++ b/vetting_bot/timer.py @@ -175,7 +175,10 @@ class Timer: ) # Finally - update database - # self.store.cursor.execute( - # "UPDATE vetting SET vote_ended = 1 WHERE mxid = ?", - # (mxid,), - # ) + self.store.cursor.execute( + "UPDATE vetting SET vote_ended = 1, decision_event_id = ? WHERE mxid = ?", + ( + decision_resp.event_id, + mxid, + ), + )