async def say_from_matrix(self, user_id, room_id, event_id, body, emote=False): namespace = self.config['appservice.namespace'] if user_id.startswith(f"@{namespace}"): return self.log.debug(f"room ID = {room_id}") self.log.debug(f"user ID = {user_id}") room_name = None for k, v in self.rooms.items(): if v.get("room_id") == room_id: room_name = k if room_name is None: return # room_name = list(v.get('name') for _, v in self.rooms.items() if v.get('room_id') == room_id)[0] room_data = self.rooms.get(room_name) channel = room_data.get('name') stored_room_id = room_data.get('room_id') enabled = room_data.get('enabled') if enabled is False: self.log.debug(f"room id: {stored_room_id} active: {enabled}") return user_name = self.appserv.intent.user(user_id=UserID(user_id)).localpart domain = self.appserv.intent.user(user_id=UserID(user_id)).domain self.log.debug(f"User Name = {user_name}") if user_name.startswith("_discord"): domain = "discord" user_name = user_name.lstrip("_discord_") elif user_name.startswith("freenode_"): domain = "frenode.org" user_name = user_name.lstrip("freenode_") if len(user_name) > 15: user_name = user_name[:15] # if emote is True: # self.bot.say_ex(user_name, domain, channel, body) # else: self.bot.say_from(user_name, domain, channel, body) await self.appserv.intent.mark_read(room_id=room_id, event_id=event_id)
async def int_handle_event(self, evt: Event) -> None: if isinstance(evt, StateEvent) and evt.type == EventType.ROOM_MEMBER and self.e2ee: await self.e2ee.handle_member_event(evt) if self.filter_matrix_event(evt): return self.log.trace("Received event: %s", evt) start_time = time.time() if evt.type == EventType.ROOM_MEMBER: evt: StateEvent prev_content = evt.unsigned.prev_content or MemberStateEventContent() prev_membership = prev_content.membership if prev_content else Membership.JOIN if evt.content.membership == Membership.INVITE: await self.int_handle_invite(evt.room_id, UserID(evt.state_key), evt.sender, evt.event_id) elif evt.content.membership == Membership.LEAVE: if prev_membership == Membership.BAN: await self.handle_unban(evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id) elif prev_membership == Membership.INVITE: if evt.sender == evt.state_key: await self.handle_reject(evt.room_id, UserID(evt.state_key), evt.content.reason, evt.event_id) else: await self.handle_disinvite(evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id) elif evt.sender == evt.state_key: await self.handle_leave(evt.room_id, UserID(evt.state_key), evt.event_id) else: await self.handle_kick(evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id) elif evt.content.membership == Membership.BAN: await self.handle_ban(evt.room_id, UserID(evt.state_key), evt.sender, evt.content.reason, evt.event_id) elif evt.content.membership == Membership.JOIN: if prev_membership != Membership.JOIN: await self.handle_join(evt.room_id, UserID(evt.state_key), evt.event_id) else: await self.handle_member_info_change(evt.room_id, UserID(evt.state_key), evt.content, prev_content, evt.event_id) elif evt.type in (EventType.ROOM_MESSAGE, EventType.STICKER): evt: MessageEvent if evt.type != EventType.ROOM_MESSAGE: evt.content.msgtype = MessageType(str(evt.type)) await self.handle_message(evt.room_id, evt.sender, evt.content, evt.event_id) elif evt.type == EventType.ROOM_ENCRYPTED and self.e2ee: await self.handle_encrypted(evt) elif evt.type == EventType.ROOM_ENCRYPTION: await self.handle_encryption(evt) else: if evt.type.is_state and isinstance(evt, StateEvent): await self.handle_state_event(evt) else: await self.handle_event(evt) await self.log_event_handle_duration(evt, time.time() - start_time)
def verify_token(self, token: str) -> UserID: token = verify_token(self.secret_key, token) if token: if token.get("expiry", 0) < int(time.time()): raise InvalidTokenError("Access token has expired") return UserID(token.get("mxid")) raise InvalidTokenError("Access token is invalid")
def __init__(self, server: str, domain: str, as_token: str, hs_token: str, bot_localpart: str, id: str, loop: Optional[asyncio.AbstractEventLoop] = None, log: Optional[Union[logging.Logger, str]] = None, verify_ssl: bool = True, tls_cert: Optional[str] = None, tls_key: Optional[str] = None, query_user: QueryFunc = None, query_alias: QueryFunc = None, real_user_content_key: Optional[ str] = "net.maunium.appservice.puppet", state_store: ASStateStore = None, aiohttp_params: Dict = None, ephemeral_events: bool = False) -> None: super().__init__(ephemeral_events=ephemeral_events) self.server = server self.domain = domain self.id = id self.verify_ssl = verify_ssl self.tls_cert = tls_cert self.tls_key = tls_key self.as_token = as_token self.hs_token = hs_token self.bot_mxid = UserID(f"@{bot_localpart}:{domain}") self.real_user_content_key: str = real_user_content_key if not state_store: file = state_store if isinstance(state_store, str) else "mx-state.json" self.state_store = FileASStateStore(path=file, binary=False) elif isinstance(state_store, ASStateStore): self.state_store = state_store else: raise ValueError(f"Unsupported state store {type(state_store)}") self._http_session = None self._intent = None self.loop = loop or asyncio.get_event_loop() self.log = (logging.getLogger(log) if isinstance(log, str) else log or logging.getLogger("mautrix_appservice")) self.query_user = query_user or self.query_user self.query_alias = query_alias or self.query_alias self.live = True self.ready = False self.app = web.Application(loop=self.loop, **aiohttp_params if aiohttp_params else {}) self.app.router.add_route("GET", "/_matrix/mau/live", self._liveness_probe) self.app.router.add_route("GET", "/_matrix/mau/ready", self._readiness_probe) self.register_routes(self.app) self.matrix_event_handler(self.state_store.update_state)
async def int_handle_invite(self, evt: StateEvent) -> None: self.log.debug(f"{evt.sender} invited {evt.state_key} to {evt.room_id}") inviter = await self.bridge.get_user(evt.sender) if inviter is None: self.log.exception(f"Failed to find user with Matrix ID {evt.sender}") return elif evt.state_key == self.az.bot_mxid: await self.accept_bot_invite(evt.room_id, inviter) return puppet = await self.bridge.get_puppet(UserID(evt.state_key)) if puppet: await self.handle_puppet_invite(evt.room_id, puppet, inviter, evt) return await self.handle_invite(evt.room_id, UserID(evt.state_key), inviter, evt)
async def handle_matrix_upgrade(self, sender: UserID, new_room: RoomID, event_id: EventID) -> None: _, server = self.main_intent.parse_user_id(sender) old_room = self.mxid self.migrate_and_save_matrix(new_room) await self.main_intent.join_room(new_room, servers=[server]) entity: Optional[TypeInputPeer] = None user: Optional[AbstractUser] = None if self.bot and self.has_bot: user = self.bot entity = await self.get_input_entity(self.bot) if not entity: user_mxids = await self.main_intent.get_room_members(self.mxid) for user_str in user_mxids: user_id = UserID(user_str) if user_id == self.az.bot_mxid: continue user = u.User.get_by_mxid(user_id, create=False) if user and user.tgid: entity = await self.get_input_entity(user) if entity: break if not entity: self.log.error("Failed to fully migrate to upgraded Matrix room: " "no Telegram user found.") return await self.update_matrix_room(user, entity, direct=self.peer_type == "user") self.log.info(f"{sender} upgraded room from {old_room} to {self.mxid}") await self._send_delivery_receipt(event_id, room_id=old_room)
async def login(evt: CommandEvent) -> EventID: override_sender = False if len(evt.args) > 0 and evt.sender.is_admin: evt.sender = await u.User.get_by_mxid(UserID(evt.args[0])).ensure_started() override_sender = True if await evt.sender.is_logged_in(): return await evt.reply(f"You are already logged in as {evt.sender.human_tg_id}.") allow_matrix_login = evt.config["bridge.allow_matrix_login"] if allow_matrix_login and not override_sender: evt.sender.command_status = { "next": enter_phone_or_token, "action": "Login", } nb = "**N.B. Logging in grants the bridge full access to your Telegram account.**" if evt.config["appservice.public.enabled"]: prefix = evt.config["appservice.public.external"] url = f"{prefix}/login?token={evt.public_website.make_token(evt.sender.mxid, '/login')}" if override_sender: return await evt.reply(f"[Click here to log in]({url}) as " f"[{evt.sender.mxid}](https://matrix.to/#/{evt.sender.mxid}).") elif allow_matrix_login: return await evt.reply(f"[Click here to log in]({url}). Alternatively, send your phone" f" number (or bot auth token) here to log in.\n\n{nb}") return await evt.reply(f"[Click here to log in]({url}).\n\n{nb}") elif allow_matrix_login: if override_sender: return await evt.reply( "This bridge instance does not allow you to log in outside of Matrix. " "Logging in as another user inside Matrix is not currently possible.") return await evt.reply("Please send your phone number (or bot auth token) here to start " f"the login process.\n\n{nb}") return await evt.reply("This bridge instance has been configured to not allow logging in.")
async def cleanup_room(cls, intent: IntentAPI, room_id: RoomID, message: str, puppets_only: bool = False) -> None: try: members = await intent.get_room_members(room_id) except MatrixRequestError: members = [] for user in members: puppet = p.Puppet.get_by_mxid(UserID(user), create=False) if user != intent.mxid and (not puppets_only or puppet): try: if puppet: await puppet.default_mxid_intent.leave_room(room_id) else: await intent.kick_user(room_id, user, message) except (MatrixRequestError, IntentError): pass try: await intent.leave_room(room_id) except (MatrixRequestError, IntentError): cls.log.warning( f"Failed to leave room {room_id} when cleaning up room", exc_info=True)
async def _login_with_shared_secret(cls, mxid: UserID) -> Optional[str]: _, server = cls.az.intent.parse_user_id(mxid) try: secret = cls.login_shared_secret_map[server] except KeyError: return None try: base_url = cls.homeserver_url_map[server] except KeyError: if server == cls.az.domain: base_url = cls.az.intent.api.base_url else: return None password = hmac.new(secret, mxid.encode("utf-8"), hashlib.sha512).hexdigest() url = base_url / str(Path.login) resp = await cls.az.http_session.post( url, data=json.dumps({ "type": str(LoginType.PASSWORD), "initial_device_display_name": cls.login_device_name, "device_id": cls.login_device_name, "identifier": { "type": "m.id.user", "user": mxid, }, "password": password }), headers={"Content-Type": "application/json"}) data = await resp.json() return data["access_token"]
async def test_handle_delegated_handler( self, command_processor: CommandProcessor, boolean2: Tuple[bool, bool], mocker: MockFixture) -> None: mocker.patch('mautrix_telegram.user.config', self.config) mocker.patch('mautrix.bridge.commands.handler.command_handlers', { "help": AsyncMock(), "unknown-command": AsyncMock() }) sender = u.User(UserID("@sender:example.org")) sender.command_status = {"foo": AsyncMock(), "next": AsyncMock()} result = await command_processor.handle( room_id=RoomID("#mock_room:example.org"), event_id=EventID("$H45H:example.org"), sender=sender, # u.User command="foo", args=[], is_management=boolean2[0], is_portal=boolean2[1]) assert result is None command_handlers = mautrix.bridge.commands.handler.command_handlers command_handlers["help"].mock.assert_not_called() # type: ignore command_handlers["unknown-command"].mock.assert_not_called( ) # type: ignore sender.command_status["foo"].mock.assert_not_called() # type: ignore sender.command_status["next"].mock.assert_called_once() # type: ignore
async def set_ghost_avatar(evt: CommandEvent) -> Optional[EventID]: try: mxc_uri = ContentURI(evt.args[0]) except (KeyError, IndexError): return await evt.reply( "**Usage:** `$cmdprefix+sp set-avatar <mxc_uri> [mxid]`") if not mxc_uri.startswith("mxc://"): return await evt.reply("The URI has to start with mxc://.") if len(evt.args) > 1: # TODO support parsing mention pills instead of requiring a plaintext mxid puppet = await evt.processor.bridge.get_puppet(UserID(evt.args[1])) if puppet is None: return await evt.reply("The given mxid was not a valid ghost user." ) intent = puppet.intent elif evt.is_portal: intent = evt.portal.main_intent if intent == evt.az.intent: return await evt.reply( "No mxid given and the main intent is not a ghost user.") else: return await evt.reply("No mxid given and not in a portal.") try: return await intent.set_avatar_url(mxc_uri) except (MatrixRequestError, IntentError): evt.log.exception("Failed to set avatar.") return await evt.reply( "Failed to set avatar (see logs for more details).")
async def handle_command(self, message: Message) -> None: def reply(reply_text: str) -> Awaitable[Message]: return self.client.send_message(message.chat_id, reply_text, reply_to=message.id) text = message.message if self.match_command(text, "start"): pcm = config["bridge.relaybot.private_chat.message"] if pcm: await reply(pcm) return elif self.match_command(text, "id"): await self.handle_command_id(message, reply) return elif message.is_private: return portal = po.Portal.get_by_entity(message.to_id) is_portal_cmd = self.match_command(text, "portal") is_invite_cmd = self.match_command(text, "invite") if is_portal_cmd or is_invite_cmd: if not await self.check_can_use_commands(message, reply): return if is_portal_cmd: await self.handle_command_portal(portal, reply) elif is_invite_cmd: try: mxid = text[text.index(" ") + 1:] except ValueError: mxid = "" await self.handle_command_invite(portal, reply, mxid_input=UserID(mxid))
async def set_ghost_display_name(evt: CommandEvent) -> Optional[EventID]: if len(evt.args) > 1: # This allows whitespaces in the name puppet = await evt.processor.bridge.get_puppet( UserID(evt.args[len(evt.args) - 1])) if puppet is None: return await evt.reply( "The given mxid was not a valid ghost user. " "If the display name has whitespaces mxid is required") intent = puppet.intent displayname = " ".join(evt.args[:-1]) elif evt.is_portal: intent = evt.portal.main_intent if intent == evt.az.intent: return await evt.reply( "No mxid given and the main intent is not a ghost user.") displayname = evt.args[0] else: return await evt.reply("No mxid given and not in a portal.") try: return await intent.set_displayname(displayname) except (MatrixRequestError, IntentError): evt.log.exception("Failed to set display name.") return await evt.reply( "Failed to set display name (see logs for more details).")
async def handle_command(self, message: Message) -> None: def reply(reply_text: str) -> Awaitable[Message]: return self.client.send_message(message.to_id, reply_text, reply_to=message.id) text = message.message if self.match_command(text, "id"): await self.handle_command_id(message, reply) return portal = po.Portal.get_by_entity(message.to_id) if self.match_command(text, "portal"): if not await self.check_can_use_commands(message, reply): return await self.handle_command_portal(portal, reply) elif self.match_command(text, "invite"): if not await self.check_can_use_commands(message, reply): return try: mxid = text[text.index(" ") + 1:] except ValueError: mxid = "" await self.handle_command_invite(portal, reply, mxid_input=UserID(mxid))
async def update_state(self, evt: StateEvent) -> None: if evt.type == EventType.ROOM_POWER_LEVELS: await self.set_power_levels(evt.room_id, evt.content) elif evt.type == EventType.ROOM_MEMBER: await self.set_member(evt.room_id, UserID(evt.state_key), evt.content) elif evt.type == EventType.ROOM_ENCRYPTION: await self.set_encryption_info(evt.room_id, evt.content)
async def handle_event(self, evt: Event) -> None: if self.filter_matrix_event(evt): return self.log.debug("Received event: %s", evt) if evt.type == EventType.ROOM_MEMBER: evt: StateEvent if evt.content.membership == Membership.INVITE: await self.handle_invite(evt.room_id, UserID(evt.state_key), evt.sender) elif evt.type in (EventType.ROOM_MESSAGE, EventType.STICKER): evt: MessageEvent if evt.type != EventType.ROOM_MESSAGE: evt.content.msgtype = MessageType(str(evt.type)) await self.handle_message(evt.room_id, evt.sender, evt.content, evt.event_id) elif evt.type == EventType.ROOM_REDACTION: evt: RedactionEvent await self.handle_redaction(evt.room_id, evt.sender, evt.redacts) elif evt.type == EventType.PRESENCE: await self.handle_presence(evt) elif evt.type == EventType.TYPING: await self.handle_typing(evt) elif evt.type == EventType.RECEIPT: await self.handle_receipt(evt)
def test_reply_with_cmdprefix(self, command_processor: CommandProcessor, mocker: MockFixture) -> None: mocker.patch("mautrix_telegram.user.config", self.config) evt = CommandEvent( processor=command_processor, room_id=RoomID("#mock_room:example.org"), event_id=EventID("$H45H:example.org"), sender=u.User(UserID("@sender:example.org")), command="help", args=[], is_management=False, is_portal=False, ) mock_az = command_processor.az evt.reply("$cmdprefix+sp ....$cmdprefix+sp...$cmdprefix $cmdprefix", allow_html=False, render_markdown=False) mock_az.intent.send_notice.assert_called_with( RoomID("#mock_room:example.org"), "tg ....tg+sp...tg tg", html=None, )
async def check_token(self, request: web.Request) -> 'u.User': try: token = request.headers["Authorization"] token = token[len("Bearer "):] except KeyError: raise web.HTTPBadRequest( text='{"error": "Missing Authorization header"}', headers=self._headers) except IndexError: raise web.HTTPBadRequest( text='{"error": "Malformed Authorization header"}', headers=self._headers) if token != self.shared_secret: raise web.HTTPForbidden(text='{"error": "Invalid token"}', headers=self._headers) try: user_id = request.query["user_id"] except KeyError: raise web.HTTPBadRequest( text='{"error": "Missing user_id query param"}', headers=self._headers) if not self.bridge.signal.is_connected: await self.bridge.signal.wait_for_connected() return await u.User.get_by_mxid(UserID(user_id))
def verify_token(self, token: str, endpoint: str = "/login") -> UserID | None: token = verify_token(self.secret_key, token) if token and (token.get("expiry", 0) > int(time.time()) and token.get("endpoint", None) == endpoint): return UserID(token.get("mxid", None)) return None
def get_mxid_from_id(cls, address: Address) -> UserID: if address.uuid: identifier = str(address.uuid).lower() elif address.number: identifier = f"phone_{address.number.lstrip('+')}" else: raise ValueError("Empty address") return UserID(cls.mxid_template.format_full(identifier))
async def login_qr(evt: CommandEvent) -> EventID: login_as = evt.sender if len(evt.args) > 0 and evt.sender.is_admin: login_as = await u.User.get_by_mxid(UserID(evt.args[0])) if not qrcode or not QRLogin: return await evt.reply("This bridge instance does not support logging in with a QR code.") if await login_as.is_logged_in(): return await evt.reply(f"You are already logged in as {login_as.human_tg_id}.") await login_as.ensure_started(even_if_no_session=True) qr_login = QRLogin(login_as.client, ignored_ids=[]) qr_event_id: EventID | None = None async def upload_qr() -> None: nonlocal qr_event_id buffer = io.BytesIO() image = qrcode.make(qr_login.url) size = image.pixel_size image.save(buffer, "PNG") qr = buffer.getvalue() mxc = await evt.az.intent.upload_media(qr, "image/png", "login-qr.png", len(qr)) content = MediaMessageEventContent( body=qr_login.url, url=mxc, msgtype=MessageType.IMAGE, info=ImageInfo(mimetype="image/png", size=len(qr), width=size, height=size), ) if qr_event_id: content.set_edit(qr_event_id) await evt.az.intent.send_message(evt.room_id, content) else: content.set_reply(evt.event_id) qr_event_id = await evt.az.intent.send_message(evt.room_id, content) retries = 4 while retries > 0: await qr_login.recreate() await upload_qr() try: user = await qr_login.wait() break except asyncio.TimeoutError: retries -= 1 except SessionPasswordNeededError: evt.sender.command_status = { "next": enter_password, "login_as": login_as if login_as != evt.sender else None, "action": "Login (password entry)", } return await evt.reply( "Your account has two-factor authentication. Please send your password here." ) else: timeout = TextMessageEventContent(body="Login timed out", msgtype=MessageType.TEXT) timeout.set_edit(qr_event_id) return await evt.az.intent.send_message(evt.room_id, timeout) return await _finish_sign_in(evt, user, login_as=login_as)
async def test_permission_granted( self, is_management: bool, puppet_whitelisted: bool, matrix_puppet_whitelisted: bool, is_admin: bool, is_logged_in: bool, command_processor: CommandProcessor, boolean: bool, mocker: MockFixture, ) -> None: mocker.patch("mautrix_telegram.user.config", self.config) command = "testcmd" mock_handler = Mock() command_handler = CommandHandler( handler=mock_handler, needs_auth=False, needs_puppeting=False, needs_matrix_puppeting=False, needs_admin=False, management_only=False, name=command, help_text="No real command", help_args="mock mockmock", help_section=HelpSection("Mock Section", 42, ""), ) sender = u.User(UserID("@sender:example.org")) sender.puppet_whitelisted = puppet_whitelisted sender.matrix_puppet_whitelisted = matrix_puppet_whitelisted sender.is_admin = is_admin mocker.patch.object(u.User, 'is_logged_in', return_value=is_logged_in) event = CommandEvent( processor=command_processor, room_id=RoomID("#mock_room:example.org"), event_id=EventID("$H45H:example.org"), sender=sender, command=command, args=[], content=Mock(), is_management=is_management, is_portal=boolean, ) assert not await command_handler.get_permission_error(event) assert command_handler.has_permission( HelpCacheKey(is_management=is_management, puppet_whitelisted=puppet_whitelisted, matrix_puppet_whitelisted=matrix_puppet_whitelisted, is_admin=is_admin, is_logged_in=is_logged_in, is_portal=boolean))
def get_permissions(self, mxid: UserID) -> Tuple[bool, bool, str]: permissions = self["bridge.permissions"] or {} if mxid in permissions: return self._get_permissions(mxid) homeserver = mxid[mxid.index(":") + 1:] if homeserver in permissions: return self._get_permissions(homeserver) return self._get_permissions("*")
async def handle_event(self, event: Event) -> None: self.log.debug("Handle event") domain = self.config['homeserver.domain'] namespace = self.config['appservice.namespace'] event_type: str = event.get("type", "m.unknown") room_id: Optional[RoomID] = event.get("room_id", None) event_id: Optional[EventID] = event.get("event_id", None) sender: Optional[UserID] = event.get("sender", None) content: Dict = event.get("content", {}) self.log.debug(f"Event {event}") self.log.debug(f"Event type: {event.type}") self.log.debug(f"Event room_id: {room_id}") self.log.debug(f"Event sender: {sender}") self.log.debug(f"Event content: {content}") if event.type == EventType.ROOM_MEMBER: event: StateEvent prev_content = event.unsigned.prev_content or MemberStateEventContent( ) prev_membership = prev_content.membership if prev_content else Membership.JOIN if event.content.membership == Membership.LEAVE: if event.sender == event.state_key: await self.sl.matrix_user_left(UserID(event.state_key), event.room_id, event.event_id) elif event.content.membership == Membership.JOIN: if prev_membership != Membership.JOIN: await self.sl.matrix_user_joined(UserID(event.state_key), event.room_id, event.event_id) elif event.type in (EventType.ROOM_MESSAGE, EventType.STICKER): event: MessageEvent if event.type != EventType.ROOM_MESSAGE: event.content.msgtype = MessageType(str(event.type)) await self.handle_message(event.room_id, event.sender, event.content, event.event_id)
async def get_portal_by_mxid(self, request: web.Request) -> web.Response: err = self.check_authorization(request) if err is not None: return err mxid = request.match_info["mxid"] portal = Portal.get_by_mxid(mxid) if not portal: return self.get_error_response(404, "portal_not_found", "Portal with given Matrix ID not found.") return await self._get_portal_response(UserID(request.query.get("user_id", "")), portal)
async def saidex(self, user, room, message): domain = self.config['homeserver.domain'] namespace = self.config['appservice.namespace'] matrix_id = f"@{namespace}_{user.lower()}:{domain}" room_id = self.rooms[room]["room_id"] user = self.appserv.intent.user(UserID(matrix_id)) await user.send_emote(room_id, message)
def test_reply(self, command_processor: CommandProcessor, mocker: MockFixture) -> None: mocker.patch("mautrix_telegram.user.config", self.config) evt = CommandEvent( processor=command_processor, room_id=RoomID("#mock_room:example.org"), event_id=EventID("$H45H:example.org"), sender=u.User(UserID("@sender:example.org")), command="help", args=[], content=Mock(), is_management=True, is_portal=False, ) mock_az = command_processor.az message = "**This** <i>was</i><br/><strong>all</strong>fun*!" # html, no markdown evt.reply(message, allow_html=True, render_markdown=False) mock_az.intent.send_notice.assert_called_with( RoomID("#mock_room:example.org"), "**This** <i>was</i><br/><strong>all</strong>fun*!", html="**This** <i>was</i><br/><strong>all</strong>fun*!\n", ) # html, markdown (default) evt.reply(message, allow_html=True, render_markdown=True) mock_az.intent.send_notice.assert_called_with( RoomID("#mock_room:example.org"), "**This** <i>was</i><br/><strong>all</strong>fun*!", html=("<p><strong>This</strong> <i>was</i><br/>" "<strong>all</strong>fun*!</p>\n"), ) # no html, no markdown evt.reply(message, allow_html=False, render_markdown=False) mock_az.intent.send_notice.assert_called_with( RoomID("#mock_room:example.org"), "**This** <i>was</i><br/><strong>all</strong>fun*!", html=None, ) # no html, markdown evt.reply(message, allow_html=False, render_markdown=True) mock_az.intent.send_notice.assert_called_with( RoomID("#mock_room:example.org"), "**This** <i>was</i><br/><strong>all</strong>fun*!", html="<p><strong>This</strong> <i>was</i><br/>" "<strong>all</strong>fun*!</p>\n")
async def join_matrix_room(self, room, clients): self.log.debug("joining matrix room join from lobby") self.log.debug(room) room_id = self.rooms[room]["room_id"] for client in clients: if client != "appservice": domain = self.config['homeserver.domain'] namespace = self.config['appservice.namespace'] matrix_id = f"@{namespace}_{client.lower()}:{domain}" user = self.appserv.intent.user(UserID(matrix_id)) await user.join_room_by_id(room_id=room_id)
async def test_permissions_denied( self, needs_auth: bool, needs_puppeting: bool, needs_matrix_puppeting: bool, needs_admin: bool, management_only: bool, command_processor: CommandProcessor, boolean: bool, mocker: MockFixture, ) -> None: mocker.patch("mautrix_telegram.user.config", self.config) command = "testcmd" mock_handler = Mock() command_handler = CommandHandler( handler=mock_handler, needs_auth=needs_auth, needs_puppeting=needs_puppeting, needs_matrix_puppeting=needs_matrix_puppeting, needs_admin=needs_admin, management_only=management_only, name=command, help_text="No real command", help_args="mock mockmock", help_section=HelpSection("Mock Section", 42, ""), ) sender = u.User(UserID("@sender:example.org")) sender.puppet_whitelisted = False sender.matrix_puppet_whitelisted = False sender.is_admin = False event = CommandEvent( processor=command_processor, room_id=RoomID("#mock_room:example.org"), event_id=EventID("$H45H:example.org"), sender=sender, command=command, args=[], content=Mock(), is_management=False, is_portal=boolean, ) assert await command_handler.get_permission_error(event) assert not command_handler.has_permission( HelpCacheKey(False, False, False, False, False, False))
async def get_room_members( self, room_id: RoomID, allowed_memberships: Tuple[Membership, ...] = (Membership.JOIN, ) ) -> List[UserID]: if len(allowed_memberships ) == 1 and allowed_memberships[0] == Membership.JOIN: memberships = await self.get_joined_members(room_id) return list(memberships.keys()) member_events = await self.get_members(room_id) return [ UserID(evt.state_key) for evt in member_events if evt.content.membership in allowed_memberships ]