matrix.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. # mautrix-instagram - A Matrix-Instagram puppeting bridge.
  2. # Copyright (C) 2020 Tulir Asokan
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU Affero General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. from typing import List, Union, TYPE_CHECKING
  17. from mautrix.bridge import BaseMatrixHandler
  18. from mautrix.types import (Event, ReactionEvent, RoomID, EventID, UserID, ReactionEventContent,
  19. RelationType, EventType, ReceiptEvent, TypingEvent, PresenceEvent,
  20. RedactionEvent, SingleReceiptEventContent)
  21. from .db import Message as DBMessage
  22. from . import portal as po, user as u
  23. if TYPE_CHECKING:
  24. from .__main__ import InstagramBridge
  25. class MatrixHandler(BaseMatrixHandler):
  26. def __init__(self, bridge: 'InstagramBridge') -> None:
  27. prefix, suffix = bridge.config["bridge.username_template"].format(userid=":").split(":")
  28. homeserver = bridge.config["homeserver.domain"]
  29. self.user_id_prefix = f"@{prefix}"
  30. self.user_id_suffix = f"{suffix}:{homeserver}"
  31. super().__init__(bridge=bridge)
  32. async def send_welcome_message(self, room_id: RoomID, inviter: 'u.User') -> None:
  33. await super().send_welcome_message(room_id, inviter)
  34. if not inviter.notice_room:
  35. inviter.notice_room = room_id
  36. await inviter.update()
  37. await self.az.intent.send_notice(room_id, "This room has been marked as your "
  38. "Instagram bridge notice room.")
  39. async def handle_leave(self, room_id: RoomID, user_id: UserID, event_id: EventID) -> None:
  40. portal = await po.Portal.get_by_mxid(room_id)
  41. if not portal:
  42. return
  43. user = await u.User.get_by_mxid(user_id, create=False)
  44. if not user:
  45. return
  46. await portal.handle_matrix_leave(user)
  47. @staticmethod
  48. async def handle_redaction(room_id: RoomID, user_id: UserID, event_id: EventID,
  49. redaction_event_id: EventID) -> None:
  50. user = await u.User.get_by_mxid(user_id)
  51. if not user:
  52. return
  53. portal = await po.Portal.get_by_mxid(room_id)
  54. if not portal:
  55. return
  56. await portal.handle_matrix_redaction(user, event_id, redaction_event_id)
  57. @classmethod
  58. async def handle_reaction(cls, room_id: RoomID, user_id: UserID, event_id: EventID,
  59. content: ReactionEventContent) -> None:
  60. if content.relates_to.rel_type != RelationType.ANNOTATION:
  61. cls.log.debug(f"Ignoring m.reaction event in {room_id} from {user_id} with unexpected "
  62. f"relation type {content.relates_to.rel_type}")
  63. return
  64. user = await u.User.get_by_mxid(user_id)
  65. if not user:
  66. return
  67. portal = await po.Portal.get_by_mxid(room_id)
  68. if not portal:
  69. return
  70. await portal.handle_matrix_reaction(user, event_id, content.relates_to.event_id,
  71. content.relates_to.key)
  72. async def handle_read_receipt(self, user: 'u.User', portal: 'po.Portal', event_id: EventID,
  73. data: SingleReceiptEventContent) -> None:
  74. message = await DBMessage.get_by_mxid(event_id, portal.mxid)
  75. if not message or message.is_internal:
  76. return
  77. user.log.debug(f"Marking {message.item_id} in {portal.thread_id} as read")
  78. await user.mqtt.mark_seen(portal.thread_id, message.item_id)
  79. @staticmethod
  80. async def handle_typing(room_id: RoomID, typing: List[UserID]) -> None:
  81. portal = await po.Portal.get_by_mxid(room_id)
  82. if not portal:
  83. return
  84. await portal.handle_matrix_typing(set(typing))
  85. async def handle_event(self, evt: Event) -> None:
  86. if evt.type == EventType.ROOM_REDACTION:
  87. evt: RedactionEvent
  88. await self.handle_redaction(evt.room_id, evt.sender, evt.redacts, evt.event_id)
  89. elif evt.type == EventType.REACTION:
  90. evt: ReactionEvent
  91. await self.handle_reaction(evt.room_id, evt.sender, evt.event_id, evt.content)
  92. async def handle_ephemeral_event(self, evt: Union[ReceiptEvent, PresenceEvent, TypingEvent]
  93. ) -> None:
  94. if evt.type == EventType.TYPING:
  95. await self.handle_typing(evt.room_id, evt.content.user_ids)
  96. else:
  97. await super().handle_ephemeral_event(evt)