handle RoomSendError response event after sending message to Matrix
This commit is contained in:
parent
b3003d8287
commit
6e589d4e23
3 changed files with 117 additions and 30 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue