"""Main logic implementation""" import traceback from typing import Any import html from nio import AsyncClient from nio import JoinResponse, RoomSendResponse, RoomSendError from nio import MatrixInvitedRoom, InviteMemberEvent from nio import MatrixRoom, RoomMessageText from nio import OlmUnverifiedDeviceError from datatypes import * import util # # DATA # _client: AsyncClient # # PRIVATE # async def _verify_all_devices() -> None: """Verifies all known devices""" for user_id in _client.device_store.users: for device_id, olm_device in _client.device_store[user_id].items(): # can't trust ourselves if device_id == _client.device_id and user_id == _client.user_id: continue # they are already verified if olm_device.verified: continue # verify them _client.verify_device(olm_device) def _handle_html_in_kwargs(kwargs: dict[str, Any]) -> None: """Modified `kwargs` in-place so that `formatted_body` appears if needed""" if "formatted_body" in kwargs or "body" not in kwargs: return is_html, text_without_html = util.check_and_remove_html(kwargs["body"]) if not is_html: return kwargs["format"] = "org.matrix.custom.html" kwargs["formatted_body"] = kwargs["body"] kwargs["body"] = text_without_html async def _send_message_to(room_id: str, message_type: MessageType, **kwargs) -> str: """Sends a message to the room and returns event_id. This function automatically detects `body` key in `kwargs` and checks if it is a valid HTML. If it is a valid HTML, it will send it as such. Moreover, `body` attribute will be cleaned from any HTML tags, so that the text will be looking well. `formatted_body` attribute is added automatically and you should not add it manually. """ try: # handle HTML _handle_html_in_kwargs(kwargs) # try to send the message result = await _client.room_send( room_id=room_id, message_type="m.room.message", content={ "msgtype": message_type.value, **kwargs } ) # success if type(result) is RoomSendResponse: return result.event_id # error elif type(result) is RoomSendError: raise Exception(result) # unknown error else: raise RuntimeError() except OlmUnverifiedDeviceError as e: # verify everyone and retry await _verify_all_devices() return await _send_message_to(room_id, message_type, **kwargs) except: raise async def _send_text_to(room_id: str, text: str) -> str: """Sends a text message to the room. `text` may be HTML""" return await _send_message_to( room_id=room_id, message_type=MessageType.TEXT, body=text ) # # CALLBACKS # async def _message_callback(room: MatrixRoom, event: RoomMessageText) -> None: """Handle commands received from Matrix""" try: # do not process messages sent by ourselves if event.sender == _client.user_id: return # prepare response response = "Получено сообщение" response += f"

Room ID: {html.escape(room.room_id)}" response += f"
Sender: {html.escape(event.sender)}" print(await _send_text_to(room.room_id, response)) except: traceback.print_exc() async def _invite_callback(room: MatrixInvitedRoom, event: InviteMemberEvent) -> None: """Happens when the bot is invited to somewhere""" try: result = await _client.join(room.room_id) if type(result) is JoinResponse: util.log_info(f"Joined the room {room.room_id}") else: util.log_error(f"Can't join room {room.room_id}") except: traceback.print_exc() async def _generic_test_callback(*args, **kwargs) -> None: """Use this callback to check argument types""" print("GENERIC TEST CALLBACK") for a in args: print(f" - {type(a)}") for k in kwargs: print(f" * {k} = {kwargs[k]}") # # PUBLIC # async def setup(client: AsyncClient) -> None: global _client _client = client client.add_event_callback(_message_callback, RoomMessageText) # type: ignore client.add_event_callback(_invite_callback, InviteMemberEvent) # type: ignore async def stop() -> None: """Stop all ongoing processes""" pass