diff --git a/mautrix_appservice/appservice.py b/mautrix_appservice/appservice.py index 70d883b3..6cf35da0 100644 --- a/mautrix_appservice/appservice.py +++ b/mautrix_appservice/appservice.py @@ -106,7 +106,7 @@ class AppService: try: response = self.query_user(user_id) - except: + except Exception: self.log.exception("Exception in user query handler") return web.Response(status=500) @@ -122,7 +122,7 @@ class AppService: try: response = self.query_alias(alias) - except: + except Exception: self.log.exception("Exception in alias query handler") return web.Response(status=500) @@ -164,7 +164,7 @@ class AppService: for handler in self.event_handlers: try: handler(event) - except: + except Exception: self.log.exception("Exception in Matrix event handler") def matrix_event_handler(self, func): diff --git a/mautrix_appservice/intent_api.py b/mautrix_appservice/intent_api.py index a578d45e..67c7d7ff 100644 --- a/mautrix_appservice/intent_api.py +++ b/mautrix_appservice/intent_api.py @@ -25,11 +25,8 @@ from matrix_client.errors import MatrixRequestError class HTTPAPI(MatrixHttpApi): def __init__(self, base_url, domain=None, bot_mxid=None, token=None, identity=None, log=None, state_store=None): - self.base_url = base_url + super().__init__(base_url, token, identity) self.domain = domain - self.token = token - self.identity = identity - self.txn_id = 0 self.bot_mxid = bot_mxid self.intent_log = log.getChild("intent") self.log = log.getChild("api") @@ -51,25 +48,17 @@ class HTTPAPI(MatrixHttpApi): def intent(self, user): return IntentAPI(user, self.user(user), self, self.state_store, self.intent_log) - def _send(self, method, path, content=None, query_params={}, headers={}, + def _send(self, method, path, content=None, query_params=None, headers=None, api_path="/_matrix/client/r0"): if not query_params: query_params = {} query_params["user_id"] = self.identity log_content = content if not isinstance(content, bytes) else f"<{len(content)} bytes>" self.log.debug("%s %s %s", method, path, log_content) - return super()._send(method, path, content, query_params, headers, api_path=api_path) + return super()._send(method, path, content, query_params, headers or {}, api_path=api_path) def create_room(self, alias=None, is_public=False, name=None, topic=None, is_direct=False, - invitees=(), initial_state=[]): - """Perform /createRoom. - Args: - alias (str): Optional. The room alias name to set for this room. - is_public (bool): Optional. The public/private visibility. - name (str): Optional. The name for the room. - topic (str): Optional. The topic for the room. - invitees (list): Optional. The list of user IDs to invite. - """ + invitees=(), initial_state=None): content = { "visibility": "public" if is_public else "private" } @@ -106,9 +95,10 @@ class HTTPAPI(MatrixHttpApi): class ChildHTTPAPI(HTTPAPI): def __init__(self, user, parent): - self.identity = user - self.token = parent.token self.base_url = parent.base_url + self.token = parent.token + self.identity = user + self.validate_cert = True self.validate_cert = parent.validate_cert self.log = parent.log self.domain = parent.domain @@ -133,7 +123,7 @@ def matrix_error_code(err): try: data = json.loads(err.content) return data["errcode"] - except: + except Exception: return err.content @@ -189,10 +179,10 @@ class IntentAPI: # region Room actions def create_room(self, alias=None, is_public=False, name=None, topic=None, is_direct=False, - invitees=(), initial_state=[]): + invitees=(), initial_state=None): self.ensure_registered() return self.client.create_room(alias, is_public, name, topic, is_direct, invitees, - initial_state) + initial_state or {}) def invite(self, room_id, user_id): self.ensure_joined(room_id) @@ -251,31 +241,31 @@ class IntentAPI: def send_emote(self, room_id, text, html=None): return self.send_text(room_id, text, html, "m.emote") - def send_image(self, room_id, url, info={}, text=None): - return self.send_file(room_id, url, info, text, "m.image") + def send_image(self, room_id, url, info=None, text=None): + return self.send_file(room_id, url, info or {}, text, "m.image") - def send_file(self, room_id, url, info={}, text=None, type="m.file"): + def send_file(self, room_id, url, info=None, text=None, file_type="m.file"): return self.send_message(room_id, { - "msgtype": type, + "msgtype": file_type, "url": url, "body": text or "Uploaded file", - "info": info, + "info": info or {}, }) - def send_text(self, room_id, text, html=None, type="m.text"): + def send_text(self, room_id, text, html=None, msgtype="m.text"): if html: if not text: text = html return self.send_message(room_id, { "body": text, - "msgtype": type, + "msgtype": msgtype, "format": "org.matrix.custom.html", "formatted_body": html or text, }) else: return self.send_message(room_id, { "body": text, - "msgtype": type, + "msgtype": msgtype, }) def send_message(self, room_id, body): @@ -290,15 +280,15 @@ class IntentAPI: self.ensure_joined(room_id) return self.client.kick_user(room_id, user_id, message) - def send_event(self, room_id, type, body, txn_id=None): + def send_event(self, room_id, event_type, body, txn_id=None): self.ensure_joined(room_id) - self._ensure_has_power_level_for(room_id, type) - return self.client.send_message_event(room_id, type, body, txn_id) + self._ensure_has_power_level_for(room_id, event_type) + return self.client.send_message_event(room_id, event_type, body, txn_id) - def send_state_event(self, room_id, type, body, state_key=""): + def send_state_event(self, room_id, event_type, body, state_key=""): self.ensure_joined(room_id) - self._ensure_has_power_level_for(room_id, type) - return self.client.send_state_event(room_id, type, body, state_key) + self._ensure_has_power_level_for(room_id, event_type) + return self.client.send_state_event(room_id, event_type, body, state_key) def join_room(self, room_id): return self.ensure_joined(room_id, ignore_cache=True) @@ -359,7 +349,8 @@ class IntentAPI: elif not self.bot: self.log.warning( f"Power level of {self.mxid} is not enough for {event_type} in {room_id}") - # raise IntentError(f"Power level of {self.mxid} is not enough for {event_type} in {room_id}") + # raise IntentError(f"Power level of {self.mxid} is not enough" + # + f"for {event_type} in {room_id}") return # TODO implement diff --git a/mautrix_appservice/state_store.py b/mautrix_appservice/state_store.py index a2c9bb37..03cab88a 100644 --- a/mautrix_appservice/state_store.py +++ b/mautrix_appservice/state_store.py @@ -42,13 +42,13 @@ class StateStore: def load(self, file): if isinstance(file, str): try: - input = open(file, "r") + input_source = open(file, "r") except FileNotFoundError: return else: - input = file + input_source = file - data = json.load(input) + data = json.load(input_source) if "registrations" in data: self.registrations = set(data["registrations"]) if "memberships" in data: @@ -57,7 +57,7 @@ class StateStore: self.power_levels = data["power_levels"] if isinstance(file, str): - input.close() + input_source.close() def _autosave(self): if self.autosave_file: @@ -104,7 +104,7 @@ class StateStore: return has >= required def set_power_level(self, room, user, level): - if not room in self.power_levels: + if room not in self.power_levels: self.power_levels[room] = { "users": {}, "events": {}, diff --git a/mautrix_telegram/commands.py b/mautrix_telegram/commands.py index e0499ae4..e3577ac4 100644 --- a/mautrix_telegram/commands.py +++ b/mautrix_telegram/commands.py @@ -47,7 +47,7 @@ class CommandHandler: with self.handler(sender, room, command, args, is_management, is_portal) as handle_command: try: handle_command(self, sender, args) - except: + except Exception: self.reply("Fatal error while handling command. Check logs for more details.") self.log.exception(f"Fatal error handling command " + f"'$cmdprefix {command} {''.join(args)}' from {sender.mxid}") @@ -158,7 +158,7 @@ class CommandHandler: } return self.reply("Your account has two-factor authentication." "Please send your password here.") - except: + except Exception: self.log.exception() return self.reply("Unhandled exception while sending code." "Check console for more details.") @@ -177,7 +177,7 @@ class CommandHandler: return self.reply(f"Successfully logged in as @{user.username}") except PasswordHashInvalidError: return self.reply("Incorrect password.") - except: + except Exception: self.log.exception() return self.reply("Unhandled exception while sending password. " "Check console for more details.") @@ -199,9 +199,9 @@ class CommandHandler: return self.reply("**Usage:** `$cmdprefix+sp search [-r|--remote] ") elif not sender.logged_in: return self.reply("This command requires you to be logged in.") - force_remote = False + # force_remote = False if args[0] in {"-r", "--remote"}: - force_remote = True + # force_remote = True args.pop(0) query = " ".join(args) if len(query) < 5: @@ -298,7 +298,7 @@ class CommandHandler: if arg.startswith("joinchat/"): invite_hash = arg[len("joinchat/"):] try: - check = sender.client(CheckChatInviteRequest(invite_hash)) + sender.client(CheckChatInviteRequest(invite_hash)) except InviteHashInvalidError: return self.reply("Invalid invite link.") except InviteHashExpiredError: diff --git a/mautrix_telegram/config.py b/mautrix_telegram/config.py index f766deda..f2f81426 100644 --- a/mautrix_telegram/config.py +++ b/mautrix_telegram/config.py @@ -14,16 +14,16 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import ruamel.yaml +from ruamel.yaml import YAML import random import string -yaml = ruamel.yaml.YAML() +yaml = YAML() class DictWithRecursion: - def __init__(self, data={}): - self._data = data + def __init__(self, data=None): + self._data = data or {} def _recursive_get(self, data, key, default_value): if '.' in key: diff --git a/mautrix_telegram/formatter.py b/mautrix_telegram/formatter.py index ca6ba027..004cea3a 100644 --- a/mautrix_telegram/formatter.py +++ b/mautrix_telegram/formatter.py @@ -53,12 +53,12 @@ class MatrixParser(HTMLParser): self._open_tags.appendleft(tag) self._open_tags_meta.appendleft(0) attrs = dict(attrs) - EntityType = None + entity_type = None args = {} if tag == "strong" or tag == "b": - EntityType = MessageEntityBold + entity_type = MessageEntityBold elif tag == "em" or tag == "i": - EntityType = MessageEntityItalic + entity_type = MessageEntityItalic elif tag == "code": try: pre = self._building_entities["pre"] @@ -67,9 +67,9 @@ class MatrixParser(HTMLParser): except KeyError: pass except KeyError: - EntityType = MessageEntityCode + entity_type = MessageEntityCode elif tag == "pre": - EntityType = MessageEntityPre + entity_type = MessageEntityPre args["language"] = "" elif tag == "a": try: @@ -86,13 +86,13 @@ class MatrixParser(HTMLParser): if not user: return if user.username: - EntityType = MessageEntityMention + entity_type = MessageEntityMention url = f"@{user.username}" else: - EntityType = MessageEntityMentionName + entity_type = MessageEntityMentionName args["user_id"] = user.tgid - elif reply and self._user_id and ( - len(self.entities) == 0 and len(self._building_entities) == 0): + elif reply and self._user_id and (len(self.entities) == 0 + and len(self._building_entities) == 0): room_id = reply.group(1) message_id = reply.group(2) message = DBMessage.query.filter(DBMessage.mxid == message_id @@ -100,25 +100,25 @@ class MatrixParser(HTMLParser): and DBMessage.user == self._user_id).one_or_none() if not message: return - EntityType = MessageEntityReply + entity_type = MessageEntityReply args["msg_id"] = message.tgid self._building_reply = True url = None elif url.startswith("mailto:"): url = url[len("mailto:"):] - EntityType = MessageEntityEmail + entity_type = MessageEntityEmail else: if self.get_starttag_text() == url: - EntityType = MessageEntityUrl + entity_type = MessageEntityUrl else: - EntityType = MessageEntityTextUrl + entity_type = MessageEntityTextUrl args["url"] = url url = None self._open_tags_meta.popleft() self._open_tags_meta.appendleft(url) - if EntityType and tag not in self._building_entities: - self._building_entities[tag] = EntityType(offset=len(self.text), length=0, **args) + if entity_type and tag not in self._building_entities: + self._building_entities[tag] = entity_type(offset=len(self.text), length=0, **args) def _list_depth(self): depth = 0 @@ -183,7 +183,7 @@ def matrix_to_telegram(html, user_id=None): parser = MatrixParser(user_id) parser.feed(html) return parser.text, parser.entities - except: + except Exception: log.exception("Failed to convert Matrix format:\nhtml=%s", html) @@ -197,18 +197,20 @@ def telegram_event_to_matrix(evt, source): if evt.fwd_from: if not html: html = escape(text) - id = evt.fwd_from.from_id - user = u.User.get_by_tgid(id) + from_id = evt.fwd_from.from_id + user = u.User.get_by_tgid(from_id) if user: fwd_from = f"{user.mxid}" else: - puppet = p.Puppet.get(id, create=False) + puppet = p.Puppet.get(from_id, create=False) if puppet and puppet.displayname: fwd_from = f"{puppet.displayname}" else: - user = source.client.get_entity(id) + user = source.client.get_entity(from_id) if user: fwd_from = p.Puppet.get_displayname(user, format=False) + else: + fwd_from = None if not fwd_from: fwd_from = "Unknown user" html = (f"Forwarded message from {fwd_from}
" @@ -232,7 +234,7 @@ def telegram_event_to_matrix(evt, source): def telegram_to_matrix(text, entities): try: return _telegram_to_matrix(text, entities) - except: + except Exception: log.exception("Failed to convert Telegram format:\n" "message=%s\n" "entities=%s", diff --git a/mautrix_telegram/portal.py b/mautrix_telegram/portal.py index 8de57792..d3d8bb19 100644 --- a/mautrix_telegram/portal.py +++ b/mautrix_telegram/portal.py @@ -87,12 +87,14 @@ class Portal: self._main_intent = puppet.intent if direct else self.az.intent return self._main_intent - def invite_matrix(self, users=[]): + def invite_matrix(self, users): if isinstance(users, str): self.main_intent.invite(self.mxid, users) - else: + elif isinstance(users, list): for user in users: self.main_intent.invite(self.mxid, user) + else: + raise ValueError("Invalid invite identifier given to invite_matrix()") def update_after_create(self, user, entity, direct, puppet=None): if not direct: @@ -106,7 +108,7 @@ class Portal: puppet.update_info(user, entity) puppet.intent.join_room(self.mxid) - def create_matrix_room(self, user, entity=None, invites=[], update_if_exists=True): + def create_matrix_room(self, user, entity=None, invites=None, update_if_exists=True): if not entity: entity = user.client.get_entity(self.peer) self.log.debug("Fetched data: %s", entity) @@ -115,7 +117,7 @@ class Portal: if self.mxid: if update_if_exists: self.update_after_create(user, entity, direct) - self.invite_matrix(invites) + self.invite_matrix(invites or []) return self.mxid self.log.debug(f"Creating room for {self.tgid_log}") @@ -141,8 +143,8 @@ class Portal: if alias: # TODO properly handle existing room aliases intent.remove_room_alias(alias) - room = intent.create_room(alias=alias, is_public=public, invitees=invites, name=self.title, - is_direct=direct) + room = intent.create_room(alias=alias, is_public=public, invitees=invites or [], + name=self.title, is_direct=direct) if not room: raise Exception(f"Failed to create room for {self.tgid_log}") @@ -166,7 +168,7 @@ class Portal: return config.get("bridge.alias_template", "telegram_{groupname}").format( groupname=username) - def sync_telegram_users(self, source, users=[]): + def sync_telegram_users(self, source, users): for entity in users: puppet = p.Puppet.get(entity.id) puppet.update_info(source, entity) @@ -243,8 +245,8 @@ class Portal: @staticmethod def _get_largest_photo_size(photo): - return max(photo.sizes, key=(lambda photo: ( - len(photo.bytes) if isinstance(photo, PhotoCachedSize) else photo.size))) + return max(photo.sizes, key=(lambda photo2: ( + len(photo2.bytes) if isinstance(photo2, PhotoCachedSize) else photo2.size))) def update_avatar(self, user, photo): photo_id = f"{photo.volume_id}-{photo.local_id}" @@ -293,8 +295,8 @@ class Portal: # endregion # region Matrix event handling - def _get_file_meta(self, body, mime): - file_name = None + @staticmethod + def _get_file_meta(body, mime): try: current_extension = body[body.rindex("."):] if mimetypes.types_map[current_extension] == mime: @@ -510,14 +512,14 @@ class Portal: sender.intent.set_typing(self.mxid, is_typing=False) return sender.intent.send_image(self.mxid, uploaded["content_uri"], info=info, text=name) - @staticmethod - def convert_webp(file, to="png"): + def convert_webp(self, file, to="png"): try: image = Image.open(BytesIO(file)).convert("RGBA") new_file = BytesIO() image.save(new_file, to) return f"image/{to}", new_file.getvalue() - except: + except Exception: + self.log.exception(f"Failed to convert webp to {to}") return "image/webp", file def handle_telegram_document(self, source, sender, media): @@ -551,7 +553,7 @@ class Portal: type = "m.image" sender.intent.set_typing(self.mxid, is_typing=False) return sender.intent.send_file(self.mxid, uploaded["content_uri"], info=info, text=name, - type=type) + file_type=type) def handle_telegram_location(self, source, sender, location): long = location.long @@ -566,7 +568,8 @@ class Portal: url = f"https://maps.google.com/?q={lat},{long}" formatted_body = f"Location: {body}" - # At least Riot ignores formatting in m.location messages, so we'll add a plaintext link. + # At least riot-web ignores formatting in m.location messages, + # so we'll add a plaintext link. body = f"Location: {body}\n{url}" return sender.intent.send_message(self.mxid, { diff --git a/mautrix_telegram/puppet.py b/mautrix_telegram/puppet.py index 9dab2891..734ea435 100644 --- a/mautrix_telegram/puppet.py +++ b/mautrix_telegram/puppet.py @@ -75,6 +75,7 @@ class Puppet: } preferences = config.get("bridge.displayname_preference", ["full name", "username", "phone"]) + name = None for preference in preferences: name = data[preference] if name: diff --git a/mautrix_telegram/user.py b/mautrix_telegram/user.py index efd87c81..0bfc0801 100644 --- a/mautrix_telegram/user.py +++ b/mautrix_telegram/user.py @@ -62,7 +62,7 @@ class User: return self.logged_in and self.whitelisted def get_input_entity(self, user): - return user.client.get_input_entity(InputUser(user_id=self.tgid)) + return user.client.get_input_entity(InputUser(user_id=self.tgid, access_hash=0)) # region Database conversion @@ -202,7 +202,7 @@ class User: def update_catch(self, update): try: self.update(update) - except: + except Exception: self.log.exception("Failed to handle Telegram update") def update(self, update):