From bdf43517cb72ec2c98fef78cdd68ec844d2bff6a Mon Sep 17 00:00:00 2001 From: Malte E Date: Tue, 23 Aug 2022 22:02:01 +0200 Subject: [PATCH] catch ProofRequiredError, add submit_challenge command --- mausignald/signald.py | 3 +++ mautrix_signal/commands/auth.py | 38 +++++++++++++++++++++++++++++++++ mautrix_signal/portal.py | 7 ++++++ 3 files changed, 48 insertions(+) diff --git a/mausignald/signald.py b/mausignald/signald.py index a34f2489..7769e43f 100644 --- a/mausignald/signald.py +++ b/mausignald/signald.py @@ -510,3 +510,6 @@ async def find_uuid(self, username: str, number: str) -> UUID | None: "resolve_address", partial=Address(number=number).serialize(), account=username ) return Address.deserialize(resp).uuid + + async def submit_challenge(self, username: str, captcha_token: str, challenge: str) -> None: + await self.request_v1("submit_challenge", captcha_token=captcha_token, challenge=challenge) diff --git a/mautrix_signal/commands/auth.py b/mautrix_signal/commands/auth.py index b91a36e6..14417402 100644 --- a/mautrix_signal/commands/auth.py +++ b/mautrix_signal/commands/auth.py @@ -292,3 +292,41 @@ async def remove_linked_device(evt: CommandEvent) -> EventID: except AuthorizationFailedError as e: return await evt.reply(f"{e} Only the primary device can remove linked devices.") return await evt.reply("Device removed") + + +@command_handler( + needs_auth=True, + management_only=True, + help_section=SECTION_AUTH, + help_text="submit a captcha challenge if you have been rate-limited", +) +async def submit_challenge(evt: CommandEvent) -> EventID: + if len(evt.args) == 0: + return await evt.reply("**Usage:** `$cmdprefix+sp submit_challenge `") + challenge = evt.args[0] + evt.sender.command_status = { + "action": "SubmitChallenge", + "room_id": evt.room_id, + "username": evt.sender.username, + "next": enter_challenge_captcha, + "challenge": challenge, + } + await evt.reply( + "Please follow the instructions at https://signald.org/articles/captcha/ " + "to obtain a captcha token and paste it here." + ) + + +async def enter_challenge_captcha(evt: CommandEvent) -> None: + captcha = evt.args[0] + username = evt.sender.command_status["username"] + challenge = evt.sender.command_status["challenge"] + try: + evt.bridge.signal.submit_challenge(username, captcha, challenge) + except UnexpectedResponse as e: + if e.resp_type == "error": + await evt.reply(e.data) + else: + raise + else: + await evt.reply("captcha challenge completed") diff --git a/mautrix_signal/portal.py b/mautrix_signal/portal.py index 5ea3eb30..0dde059f 100644 --- a/mautrix_signal/portal.py +++ b/mautrix_signal/portal.py @@ -49,6 +49,8 @@ Mention, MessageData, Profile, + ProofRequiredError, + ProofRequiredType, Quote, QuotedAttachment, Reaction, @@ -331,6 +333,11 @@ async def handle_matrix_message( status, event_id, self.mxid, EventType.ROOM_MESSAGE, message.msgtype, error=e ) await sender.handle_auth_failure(e) + if isinstance(e, ProofRequiredError) and e.options == ProofRequiredType.RECAPTCHA: + await self.main_intent.send_notice( + "Your Message was not bridged because you have been rate limited by Signal." + "You can complete a captcha by sending `captcha_challenge {e.token}` to your management room" + ) await self._send_error_notice("message", e) asyncio.create_task(self._send_message_status(event_id, e))