handle RoomSendError response event after sending message to Matrix

This commit is contained in:
HgO 2022-10-26 18:02:23 +02:00
parent b3003d8287
commit 6e589d4e23
3 changed files with 117 additions and 30 deletions

View file

@ -1,7 +1,14 @@
import logging import logging
from typing import Dict, Optional, TypedDict, Union from typing import Dict, Optional, TypedDict, Union
from nio import AsyncClient, ErrorResponse, Response, RoomSendResponse from nio import (
AsyncClient,
ErrorResponse,
Response,
RoomSendError,
RoomSendResponse,
SendRetryError,
)
from typing_extensions import NotRequired from typing_extensions import NotRequired
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -62,32 +69,16 @@ async def send_text_to_room(
if reply_to_event_id: if reply_to_event_id:
content["m.relates_to"] = {"m.in_reply_to": {"event_id": reply_to_event_id}} content["m.relates_to"] = {"m.in_reply_to": {"event_id": reply_to_event_id}}
return await matrix_client.room_send( response_event = await matrix_client.room_send(
room_id, room_id,
"m.room.message", "m.room.message",
content, content,
ignore_unverified_devices=True, ignore_unverified_devices=True,
) )
if isinstance(response_event, RoomSendError):
def make_pill(user_id: str, displayname: str = None) -> str: raise SendRetryError(response_event.error)
"""Convert a user ID (and optionally a display name) to a formatted user 'pill' return response_event
Args:
user_id: The MXID of the user.
displayname: An optional displayname. Clients like Element will figure out the
correct display name no matter what, but other clients may not. If not
provided, the MXID will be used instead.
Returns:
The formatted user pill.
"""
if not displayname:
# Use the user ID as the displayname if not provided
displayname = user_id
return f'<a href="https://matrix.to/#/{user_id}">{displayname}</a>'
async def react_to_event( async def react_to_event(
@ -121,13 +112,17 @@ async def react_to_event(
} }
} }
return await client.room_send( response_event = await client.room_send(
room_id, room_id,
"m.reaction", "m.reaction",
content, content,
ignore_unverified_devices=True, ignore_unverified_devices=True,
) )
if isinstance(response_event, RoomSendError):
raise SendRetryError(response_event.error)
return response_event
def strip_fallback(content: str) -> str: def strip_fallback(content: str) -> str:
index = 0 index = 0

View file

@ -1,7 +1,11 @@
# This file holds custom error types that you can define for your application. # This file holds custom error types that you can define for your application.
class ConfigError(Exception): class MatrixAlertbotError(Exception):
pass
class ConfigError(MatrixAlertbotError):
"""An error encountered during reading the config file.""" """An error encountered during reading the config file."""
pass pass
@ -25,7 +29,7 @@ class RequiredConfigKeyError(ConfigError):
pass pass
class AlertmanagerError(Exception): class AlertmanagerError(MatrixAlertbotError):
"""An error encountered with Alertmanager.""" """An error encountered with Alertmanager."""
pass pass

View file

@ -4,7 +4,11 @@ from unittest.mock import Mock
import nio import nio
from matrix_alertbot.chat_functions import send_text_to_room, strip_fallback from matrix_alertbot.chat_functions import (
react_to_event,
send_text_to_room,
strip_fallback,
)
from tests.utils import make_awaitable from tests.utils import make_awaitable
@ -32,6 +36,61 @@ class ChatFunctionsTestCase(unittest.IsolatedAsyncioTestCase):
message = strip_fallback(fake_body) message = strip_fallback(fake_body)
self.assertEqual(fake_body, message) self.assertEqual(fake_body, message)
async def test_react_to_event(self) -> None:
fake_response = Mock(spec=nio.RoomSendResponse)
fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_matrix_client.room_send = Mock(return_value=make_awaitable(fake_response))
fake_room_id = "!abcdefgh:example.com"
fake_event_id = "some event id"
fake_reaction_text = "some reaction"
response = await react_to_event(
fake_matrix_client, fake_room_id, fake_event_id, fake_reaction_text
)
fake_matrix_client.room_send.assert_called_once_with(
fake_room_id,
"m.reaction",
{
"m.relates_to": {
"rel_type": "m.annotation",
"event_id": fake_event_id,
"key": fake_reaction_text,
}
},
ignore_unverified_devices=True,
)
self.assertEqual(fake_response, response)
async def test_react_to_event_return_room_send_error(self) -> None:
fake_response = Mock(spec=nio.RoomSendError)
fake_response.error = "some error"
fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_matrix_client.room_send.return_value = make_awaitable(
fake_response
)
fake_room_id = "!abcdefgh:example.com"
fake_event_id = "some event id"
fake_reaction_text = "some reaction"
with self.assertRaises(nio.SendRetryError):
await react_to_event(
fake_matrix_client, fake_room_id, fake_event_id, fake_reaction_text
)
fake_matrix_client.room_send.assert_called_once_with(
fake_room_id,
"m.reaction",
{
"m.relates_to": {
"rel_type": "m.annotation",
"event_id": fake_event_id,
"key": fake_reaction_text,
}
},
ignore_unverified_devices=True,
)
async def test_send_text_to_room_as_notice(self) -> None: async def test_send_text_to_room_as_notice(self) -> None:
fake_response = Mock(spec=nio.RoomSendResponse) fake_response = Mock(spec=nio.RoomSendResponse)
fake_matrix_client = Mock(spec=nio.AsyncClient) fake_matrix_client = Mock(spec=nio.AsyncClient)
@ -60,7 +119,7 @@ class ChatFunctionsTestCase(unittest.IsolatedAsyncioTestCase):
async def test_send_text_to_room_as_message(self) -> None: async def test_send_text_to_room_as_message(self) -> None:
fake_response = Mock(spec=nio.RoomSendResponse) fake_response = Mock(spec=nio.RoomSendResponse)
fake_matrix_client = Mock(spec=nio.AsyncClient) fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_matrix_client.room_send = Mock(return_value=make_awaitable(fake_response)) fake_matrix_client.room_send.return_value = make_awaitable(fake_response)
fake_room_id = "!abcdefgh:example.com" fake_room_id = "!abcdefgh:example.com"
fake_plaintext_body = "some plaintext message" fake_plaintext_body = "some plaintext message"
fake_html_body = "some html message" fake_html_body = "some html message"
@ -89,7 +148,7 @@ class ChatFunctionsTestCase(unittest.IsolatedAsyncioTestCase):
async def test_send_text_to_room_in_reply_to_event(self) -> None: async def test_send_text_to_room_in_reply_to_event(self) -> None:
fake_response = Mock(spec=nio.RoomSendResponse) fake_response = Mock(spec=nio.RoomSendResponse)
fake_matrix_client = Mock(spec=nio.AsyncClient) fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_matrix_client.room_send = Mock(return_value=make_awaitable(fake_response)) fake_matrix_client.room_send.return_value = make_awaitable(fake_response)
fake_room_id = "!abcdefgh:example.com" fake_room_id = "!abcdefgh:example.com"
fake_plaintext_body = "some plaintext message" fake_plaintext_body = "some plaintext message"
fake_html_body = "some html message" fake_html_body = "some html message"
@ -119,9 +178,8 @@ class ChatFunctionsTestCase(unittest.IsolatedAsyncioTestCase):
async def test_send_text_to_room_raise_send_retry_error(self) -> None: async def test_send_text_to_room_raise_send_retry_error(self) -> None:
fake_matrix_client = Mock(spec=nio.AsyncClient) fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_matrix_client.room_send = Mock( fake_matrix_client.room_send.side_effect = send_room_raise_send_retry_error
side_effect=send_room_raise_send_retry_error
)
fake_room_id = "!abcdefgh:example.com" fake_room_id = "!abcdefgh:example.com"
fake_plaintext_body = "some plaintext message" fake_plaintext_body = "some plaintext message"
fake_html_body = "some html message" fake_html_body = "some html message"
@ -133,6 +191,36 @@ class ChatFunctionsTestCase(unittest.IsolatedAsyncioTestCase):
fake_plaintext_body, fake_plaintext_body,
fake_html_body, fake_html_body,
) )
fake_matrix_client.room_send.assert_called_once_with(
fake_room_id,
"m.room.message",
{
"msgtype": "m.notice",
"format": "org.matrix.custom.html",
"body": fake_plaintext_body,
"formatted_body": fake_html_body,
},
ignore_unverified_devices=True,
)
async def test_send_text_to_room_return_room_send_error(self) -> None:
fake_response = Mock(spec=nio.RoomSendError)
fake_response.error = "some error"
fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_matrix_client.room_send.return_value = make_awaitable(fake_response)
fake_room_id = "!abcdefgh:example.com"
fake_plaintext_body = "some plaintext message"
fake_html_body = "some html message"
with self.assertRaises(nio.SendRetryError):
await send_text_to_room(
fake_matrix_client,
fake_room_id,
fake_plaintext_body,
fake_html_body,
)
fake_matrix_client.room_send.assert_called_once_with( fake_matrix_client.room_send.assert_called_once_with(
fake_room_id, fake_room_id,
"m.room.message", "m.room.message",