diff --git a/mautrix/bridge/config.py b/mautrix/bridge/config.py index 5f55ca91..113b148c 100644 --- a/mautrix/bridge/config.py +++ b/mautrix/bridge/config.py @@ -123,6 +123,7 @@ def do_update(self, helper: ConfigUpdateHelper) -> None: copy("bridge.encryption.rotation.messages") copy("bridge.relay.enabled") + copy("bridge.relay.user_distinguishers") copy_dict("bridge.relay.message_formats", override_existing_map=False) copy("manhole.enabled") diff --git a/mautrix/bridge/portal.py b/mautrix/bridge/portal.py index 0d4b110f..32b7e4bf 100644 --- a/mautrix/bridge/portal.py +++ b/mautrix/bridge/portal.py @@ -295,6 +295,7 @@ async def apply_relay_message_format( "formatted_body": content["formatted_body"], "body": content.body, "message": content.body, + "distinguisher": self._get_distinguisher(sender.mxid), } content.body = Template(tpl).safe_substitute(tpl_args) if self.relay_formatted_body and "formatted_body" in content: @@ -420,6 +421,26 @@ async def schedule_disappearing(self) -> None: await msg.update() asyncio.create_task(self._disappear_event(msg)) + @staticmethod + def hash_user_id(val: UserID) -> int: + """ + A simple Matrix user ID hashing algorithm that matches what Element does. + Args: + val: the Matrix user ID. + Returns: + A 32-bit hash of the user ID. + """ + out = 0 + for char in val: + out = (out << 5) - out + ord(char) + # Emulate JS's 32-bit signed bitwise OR `hash |= 0` + out = (out & 2**31 - 1) - (out & 2**31) + return abs(out) + + def _get_distinguisher(self, user_id: UserID) -> str: + ruds = self.config["bridge.relay.user_distinguishers"] or [] + return ruds[self.hash_user_id(user_id) % len(ruds)] if ruds else "" + async def _send_message( self, intent: IntentAPI,