__main__.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. # mautrix-signal - A Matrix-Signal 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 Dict, Any
  17. import asyncio
  18. import logging
  19. from mautrix.bridge import Bridge
  20. from mautrix.bridge.state_store.asyncpg import PgBridgeStateStore
  21. from mautrix.types import RoomID, UserID
  22. from mautrix.util.async_db import Database
  23. from .version import version, linkified_version
  24. from .config import Config
  25. from .db import upgrade_table, init as init_db
  26. from .matrix import MatrixHandler
  27. from .signal import SignalHandler
  28. from .user import User
  29. from .portal import Portal
  30. from .puppet import Puppet
  31. from .web import ProvisioningAPI
  32. from . import commands
  33. class SignalBridge(Bridge):
  34. module = "mautrix_signal"
  35. name = "mautrix-signal"
  36. command = "python -m mautrix-signal"
  37. description = "A Matrix-Signal puppeting bridge."
  38. repo_url = "https://github.com/mautrix/signal"
  39. real_user_content_key = "net.maunium.signal.puppet"
  40. version = version
  41. markdown_version = linkified_version
  42. config_class = Config
  43. matrix_class = MatrixHandler
  44. db: Database
  45. matrix: MatrixHandler
  46. signal: SignalHandler
  47. config: Config
  48. state_store: PgBridgeStateStore
  49. provisioning_api: ProvisioningAPI
  50. periodic_sync_task: asyncio.Task
  51. def make_state_store(self) -> None:
  52. self.state_store = PgBridgeStateStore(self.db, self.get_puppet, self.get_double_puppet)
  53. def prepare_db(self) -> None:
  54. self.db = Database(self.config["appservice.database"], upgrade_table=upgrade_table,
  55. loop=self.loop, db_args=self.config["appservice.database_opts"])
  56. init_db(self.db)
  57. def prepare_bridge(self) -> None:
  58. self.signal = SignalHandler(self)
  59. super().prepare_bridge()
  60. cfg = self.config["bridge.provisioning"]
  61. self.provisioning_api = ProvisioningAPI(self, cfg["shared_secret"])
  62. self.az.app.add_subapp(cfg["prefix"], self.provisioning_api.app)
  63. async def start(self) -> None:
  64. await self.db.start()
  65. await self.state_store.upgrade_table.upgrade(self.db.pool)
  66. if self.matrix.e2ee:
  67. self.matrix.e2ee.crypto_db.override_pool(self.db.pool)
  68. User.init_cls(self)
  69. self.add_startup_actions(Puppet.init_cls(self))
  70. Portal.init_cls(self)
  71. if self.config["bridge.resend_bridge_info"]:
  72. self.add_startup_actions(self.resend_bridge_info())
  73. self.add_startup_actions(self.signal.start())
  74. await super().start()
  75. self.periodic_sync_task = asyncio.create_task(self._periodic_sync_loop())
  76. @staticmethod
  77. async def _actual_periodic_sync_loop(log: logging.Logger, interval: int) -> None:
  78. while True:
  79. try:
  80. await asyncio.sleep(interval)
  81. except asyncio.CancelledError:
  82. return
  83. log.info("Executing periodic syncs")
  84. for user in User.by_username.values():
  85. try:
  86. await user.sync()
  87. except asyncio.CancelledError:
  88. return
  89. except Exception:
  90. log.exception("Error while syncing %s", user.mxid)
  91. async def _periodic_sync_loop(self) -> None:
  92. log = logging.getLogger("mau.periodic_sync")
  93. interval = self.config["bridge.periodic_sync"]
  94. if interval <= 0:
  95. log.debug("Periodic sync is not enabled")
  96. return
  97. log.debug("Starting periodic sync loop")
  98. await self._actual_periodic_sync_loop(log, interval)
  99. log.debug("Periodic sync stopped")
  100. def prepare_stop(self) -> None:
  101. self.add_shutdown_actions(self.signal.stop())
  102. for puppet in Puppet.by_custom_mxid.values():
  103. puppet.stop()
  104. async def resend_bridge_info(self) -> None:
  105. self.config["bridge.resend_bridge_info"] = False
  106. self.config.save()
  107. self.log.info("Re-sending bridge info state event to all portals")
  108. async for portal in Portal.all_with_room():
  109. await portal.update_bridge_info()
  110. self.log.info("Finished re-sending bridge info state events")
  111. async def get_user(self, user_id: UserID, create: bool = True) -> User:
  112. return await User.get_by_mxid(user_id, create=create)
  113. async def get_portal(self, room_id: RoomID) -> Portal:
  114. return await Portal.get_by_mxid(room_id)
  115. async def get_puppet(self, user_id: UserID, create: bool = False) -> Puppet:
  116. return await Puppet.get_by_mxid(user_id, create=create)
  117. async def get_double_puppet(self, user_id: UserID) -> Puppet:
  118. return await Puppet.get_by_custom_mxid(user_id)
  119. def is_bridge_ghost(self, user_id: UserID) -> bool:
  120. return bool(Puppet.get_id_from_mxid(user_id))
  121. async def count_logged_in_users(self) -> int:
  122. return len([user for user in User.by_username.values() if user.username])
  123. async def manhole_global_namespace(self, user_id: UserID) -> Dict[str, Any]:
  124. return {
  125. **await super().manhole_global_namespace(user_id),
  126. "User": User,
  127. "Portal": Portal,
  128. "Puppet": Puppet,
  129. }
  130. SignalBridge().run()