Merge branch 'telethon-1.17'

This commit is contained in:
Tulir Asokan
2020-10-22 17:42:25 +03:00
6 changed files with 58 additions and 39 deletions
+6 -2
View File
@@ -31,7 +31,7 @@ from telethon.tl.types import (
UpdateEditChannelMessage, UpdateEditMessage, UpdateNewChannelMessage, UpdateReadHistoryOutbox, UpdateEditChannelMessage, UpdateEditMessage, UpdateNewChannelMessage, UpdateReadHistoryOutbox,
UpdateShortChatMessage, UpdateShortMessage, UpdateUserName, UpdateUserPhoto, UpdateUserStatus, UpdateShortChatMessage, UpdateShortMessage, UpdateUserName, UpdateUserPhoto, UpdateUserStatus,
UpdateUserTyping, User, UserStatusOffline, UserStatusOnline, UpdateReadHistoryInbox, UpdateUserTyping, User, UserStatusOffline, UserStatusOnline, UpdateReadHistoryInbox,
UpdateReadChannelInbox) UpdateReadChannelInbox, MessageEmpty)
from mautrix.types import UserID, PresenceState from mautrix.types import UserID, PresenceState
from mautrix.errors import MatrixError from mautrix.errors import MatrixError
@@ -164,6 +164,7 @@ class AbstractUser(ABC):
request_retries=config["telegram.connection.request_retries"], request_retries=config["telegram.connection.request_retries"],
connection=connection, connection=connection,
proxy=proxy, proxy=proxy,
raise_last_call_error=True,
loop=self.loop, loop=self.loop,
base_logger=base_logger base_logger=base_logger
@@ -387,12 +388,15 @@ class AbstractUser(ABC):
elif isinstance(update, (UpdateNewMessage, UpdateNewChannelMessage, elif isinstance(update, (UpdateNewMessage, UpdateNewChannelMessage,
UpdateEditMessage, UpdateEditChannelMessage)): UpdateEditMessage, UpdateEditChannelMessage)):
update = update.message update = update.message
if isinstance(update, MessageEmpty):
return update, None, None
if isinstance(update.to_id, PeerUser) and not update.out: if isinstance(update.to_id, PeerUser) and not update.out:
portal = po.Portal.get_by_tgid(update.from_id, peer_type="user", portal = po.Portal.get_by_tgid(update.from_id, peer_type="user",
tg_receiver=self.tgid) tg_receiver=self.tgid)
else: else:
portal = po.Portal.get_by_entity(update.to_id, receiver_id=self.tgid) portal = po.Portal.get_by_entity(update.to_id, receiver_id=self.tgid)
sender = pu.Puppet.get(update.from_id) if update.from_id else None sender = (pu.Puppet.get(TelegramID(update.from_id.user_id))
if isinstance(update.from_id, PeerUser) else None)
else: else:
self.log.warning("Unexpected message type in User#get_message_details: " self.log.warning("Unexpected message type in User#get_message_details: "
f"{type(update)}") f"{type(update)}")
+18 -16
View File
@@ -22,7 +22,7 @@ from telethon.tl.types import (MessageEntityMention, MessageEntityMentionName, M
MessageEntityEmail, MessageEntityTextUrl, MessageEntityBold, MessageEntityEmail, MessageEntityTextUrl, MessageEntityBold,
MessageEntityItalic, MessageEntityCode, MessageEntityPre, MessageEntityItalic, MessageEntityCode, MessageEntityPre,
MessageEntityBotCommand, MessageEntityHashtag, MessageEntityCashtag, MessageEntityBotCommand, MessageEntityHashtag, MessageEntityCashtag,
MessageEntityPhone, TypeMessageEntity, PeerChannel, MessageEntityPhone, TypeMessageEntity, PeerChannel, PeerChat,
MessageEntityBlockquote, MessageEntityStrike, MessageFwdHeader, MessageEntityBlockquote, MessageEntityStrike, MessageFwdHeader,
MessageEntityUnderline, PeerUser) MessageEntityUnderline, PeerUser)
from telethon.tl.custom import Message from telethon.tl.custom import Message
@@ -45,11 +45,11 @@ log: logging.Logger = logging.getLogger("mau.fmt.tg")
def telegram_reply_to_matrix(evt: Message, source: 'AbstractUser') -> Optional[RelatesTo]: def telegram_reply_to_matrix(evt: Message, source: 'AbstractUser') -> Optional[RelatesTo]:
if evt.reply_to_msg_id: if evt.reply_to:
space = (evt.to_id.channel_id space = (evt.peer_id.channel_id
if isinstance(evt, Message) and isinstance(evt.to_id, PeerChannel) if isinstance(evt, Message) and isinstance(evt.peer_id, PeerChannel)
else source.tgid) else source.tgid)
msg = DBMessage.get_one_by_tgid(TelegramID(evt.reply_to_msg_id), space) msg = DBMessage.get_one_by_tgid(TelegramID(evt.reply_to.reply_to_msg_id), space)
if msg: if msg:
return RelatesTo(rel_type=RelationType.REFERENCE, event_id=msg.mxid) return RelatesTo(rel_type=RelationType.REFERENCE, event_id=msg.mxid)
return None return None
@@ -61,15 +61,15 @@ async def _add_forward_header(source: 'AbstractUser', content: TextMessageEventC
content.format = Format.HTML content.format = Format.HTML
content.formatted_body = escape(content.body) content.formatted_body = escape(content.body)
fwd_from_html, fwd_from_text = None, None fwd_from_html, fwd_from_text = None, None
if fwd_from.from_id: if isinstance(fwd_from.from_id, PeerUser):
user = u.User.get_by_tgid(TelegramID(fwd_from.from_id)) user = u.User.get_by_tgid(TelegramID(fwd_from.from_id.user_id))
if user: if user:
fwd_from_text = user.displayname or user.mxid fwd_from_text = user.displayname or user.mxid
fwd_from_html = (f"<a href='https://matrix.to/#/{user.mxid}'>" fwd_from_html = (f"<a href='https://matrix.to/#/{user.mxid}'>"
f"{escape(fwd_from_text)}</a>") f"{escape(fwd_from_text)}</a>")
if not fwd_from_text: if not fwd_from_text:
puppet = pu.Puppet.get(TelegramID(fwd_from.from_id), create=False) puppet = pu.Puppet.get(TelegramID(fwd_from.from_id.user_id), create=False)
if puppet and puppet.displayname: if puppet and puppet.displayname:
fwd_from_text = puppet.displayname or puppet.mxid fwd_from_text = puppet.displayname or puppet.mxid
fwd_from_html = (f"<a href='https://matrix.to/#/{puppet.mxid}'>" fwd_from_html = (f"<a href='https://matrix.to/#/{puppet.mxid}'>"
@@ -77,14 +77,16 @@ async def _add_forward_header(source: 'AbstractUser', content: TextMessageEventC
if not fwd_from_text: if not fwd_from_text:
try: try:
user = await source.client.get_entity(PeerUser(fwd_from.from_id)) user = await source.client.get_entity(fwd_from.from_id)
if user: if user:
fwd_from_text = pu.Puppet.get_displayname(user, False) fwd_from_text = pu.Puppet.get_displayname(user, False)
fwd_from_html = f"<b>{escape(fwd_from_text)}</b>" fwd_from_html = f"<b>{escape(fwd_from_text)}</b>"
except (ValueError, RPCError): except (ValueError, RPCError):
fwd_from_text = fwd_from_html = "unknown user" fwd_from_text = fwd_from_html = "unknown user"
elif fwd_from.channel_id: elif isinstance(fwd_from.from_id, (PeerChannel, PeerChat)):
portal = po.Portal.get_by_tgid(TelegramID(fwd_from.channel_id)) from_id = (fwd_from.from_id.chat_id if isinstance(fwd_from.from_id, PeerChat)
else fwd_from.from_id.channel_id)
portal = po.Portal.get_by_tgid(TelegramID(from_id))
if portal: if portal:
fwd_from_text = portal.title fwd_from_text = portal.title
if portal.alias: if portal.alias:
@@ -94,7 +96,7 @@ async def _add_forward_header(source: 'AbstractUser', content: TextMessageEventC
fwd_from_html = f"channel <b>{escape(fwd_from_text)}</b>" fwd_from_html = f"channel <b>{escape(fwd_from_text)}</b>"
else: else:
try: try:
channel = await source.client.get_entity(PeerChannel(fwd_from.channel_id)) channel = await source.client.get_entity(fwd_from.from_id)
if channel: if channel:
fwd_from_text = f"channel {channel.title}" fwd_from_text = f"channel {channel.title}"
fwd_from_html = f"channel <b>{escape(channel.title)}</b>" fwd_from_html = f"channel <b>{escape(channel.title)}</b>"
@@ -116,11 +118,11 @@ async def _add_forward_header(source: 'AbstractUser', content: TextMessageEventC
async def _add_reply_header(source: 'AbstractUser', content: TextMessageEventContent, evt: Message, async def _add_reply_header(source: 'AbstractUser', content: TextMessageEventContent, evt: Message,
main_intent: IntentAPI): main_intent: IntentAPI):
space = (evt.to_id.channel_id space = (evt.peer_id.channel_id
if isinstance(evt, Message) and isinstance(evt.to_id, PeerChannel) if isinstance(evt, Message) and isinstance(evt.peer_id, PeerChannel)
else source.tgid) else source.tgid)
msg = DBMessage.get_one_by_tgid(TelegramID(evt.reply_to_msg_id), space) msg = DBMessage.get_one_by_tgid(TelegramID(evt.reply_to.reply_to_msg_id), space)
if not msg: if not msg:
return return
@@ -162,7 +164,7 @@ async def telegram_to_matrix(evt: Message, source: "AbstractUser",
if evt.fwd_from: if evt.fwd_from:
await _add_forward_header(source, content, evt.fwd_from) await _add_forward_header(source, content, evt.fwd_from)
if evt.reply_to_msg_id and not no_reply_fallback: if evt.reply_to and not no_reply_fallback:
await _add_reply_header(source, content, evt, main_intent) await _add_reply_header(source, content, evt, main_intent)
if isinstance(evt, Message) and evt.post and evt.post_author: if isinstance(evt, Message) and evt.post and evt.post_author:
+22 -10
View File
@@ -87,9 +87,9 @@ class PortalMatrix(BasePortal, ABC):
message = await self._get_state_change_message(event, user, **kwargs) message = await self._get_state_change_message(event, user, **kwargs)
if not message: if not message:
return return
response = await self.bot.client.send_message( message, entities = formatter.matrix_to_telegram(message)
self.peer, message, response = await self.bot.client.send_message(self.peer, message,
parse_mode=self._matrix_event_to_entities) formatting_entities=entities)
space = self.tgid if self.peer_type == "channel" else self.bot.tgid space = self.tgid if self.peer_type == "channel" else self.bot.tgid
self.dedup.check(response, (event_id, space)) self.dedup.check(response, (event_id, space))
@@ -231,18 +231,22 @@ class PortalMatrix(BasePortal, ABC):
async def _handle_matrix_text(self, sender_id: TelegramID, event_id: EventID, async def _handle_matrix_text(self, sender_id: TelegramID, event_id: EventID,
space: TelegramID, client: 'MautrixTelegramClient', space: TelegramID, client: 'MautrixTelegramClient',
content: TextMessageEventContent, reply_to: TelegramID) -> None: content: TextMessageEventContent, reply_to: TelegramID) -> None:
if content.formatted_body and content.format == Format.HTML:
message, entities = formatter.matrix_to_telegram(content.formatted_body)
else:
message, entities = formatter.matrix_text_to_telegram(content.body)
async with self.send_lock(sender_id): async with self.send_lock(sender_id):
lp = self.get_config("telegram_link_preview") lp = self.get_config("telegram_link_preview")
if content.get_edit(): if content.get_edit():
orig_msg = DBMessage.get_by_mxid(content.get_edit(), self.mxid, space) orig_msg = DBMessage.get_by_mxid(content.get_edit(), self.mxid, space)
if orig_msg: if orig_msg:
response = await client.edit_message(self.peer, orig_msg.tgid, content, response = await client.edit_message(self.peer, orig_msg.tgid, message,
parse_mode=self._matrix_event_to_entities, formatting_entities=entities,
link_preview=lp) link_preview=lp)
self._add_telegram_message_to_db(event_id, space, -1, response) self._add_telegram_message_to_db(event_id, space, -1, response)
return return
response = await client.send_message(self.peer, content, reply_to=reply_to, response = await client.send_message(self.peer, message, reply_to=reply_to,
parse_mode=self._matrix_event_to_entities, formatting_entities=entities,
link_preview=lp) link_preview=lp)
self._add_telegram_message_to_db(event_id, space, 0, response) self._add_telegram_message_to_db(event_id, space, 0, response)
await self._send_delivery_receipt(event_id) await self._send_delivery_receipt(event_id)
@@ -297,7 +301,13 @@ class PortalMatrix(BasePortal, ABC):
media = InputMediaUploadedDocument(file=file_handle, attributes=attributes, media = InputMediaUploadedDocument(file=file_handle, attributes=attributes,
mime_type=mime or "application/octet-stream") mime_type=mime or "application/octet-stream")
caption, entities = self._matrix_event_to_entities(caption) if caption else (None, None) if caption:
if caption.formatted_body and caption.format == Format.HTML:
caption, entities = formatter.matrix_to_telegram(caption.formatted_body)
else:
caption, entities = formatter.matrix_text_to_telegram(content.body)
else:
caption, entities = None, None
async with self.send_lock(sender_id): async with self.send_lock(sender_id):
if await self._matrix_document_edit(client, content, space, caption, media, event_id): if await self._matrix_document_edit(client, content, space, caption, media, event_id):
@@ -336,7 +346,7 @@ class PortalMatrix(BasePortal, ABC):
except (KeyError, ValueError): except (KeyError, ValueError):
self.log.exception("Failed to parse location") self.log.exception("Failed to parse location")
return None return None
caption, entities = self._matrix_event_to_entities(content) caption, entities = formatter.matrix_text_to_telegram(content.body)
media = MessageMediaGeo(geo=GeoPoint(lat, long, access_hash=0)) media = MessageMediaGeo(geo=GeoPoint(lat, long, access_hash=0))
async with self.send_lock(sender_id): async with self.send_lock(sender_id):
@@ -372,7 +382,8 @@ class PortalMatrix(BasePortal, ABC):
await self._handle_matrix_message(sender, content, event_id) await self._handle_matrix_message(sender, content, event_id)
except RPCError as e: except RPCError as e:
if config["bridge.delivery_error_reports"]: if config["bridge.delivery_error_reports"]:
await self._send_bridge_error(f"\u26a0 Your message may not have been bridged: {e}") await self._send_bridge_error(
f"\u26a0 Your message may not have been bridged: {e}")
raise raise
async def _handle_matrix_message(self, sender: 'u.User', content: MessageEventContent, async def _handle_matrix_message(self, sender: 'u.User', content: MessageEventContent,
@@ -586,6 +597,7 @@ class PortalMatrix(BasePortal, ABC):
self.log.warning(f"Failed to set room name", exc_info=True) self.log.warning(f"Failed to set room name", exc_info=True)
return ok return ok
def init(context: Context) -> None: def init(context: Context) -> None:
global config global config
config = context.config config = context.config
+1 -1
View File
@@ -29,7 +29,7 @@ from telethon.tl.types import (
ChatParticipantCreator, ChannelParticipantCreator, UserProfilePhoto, UserProfilePhotoEmpty) ChatParticipantCreator, ChannelParticipantCreator, UserProfilePhoto, UserProfilePhotoEmpty)
from mautrix.errors import MForbidden from mautrix.errors import MForbidden
from mautrix.types import (RoomID, UserID, RoomCreatePreset, EventType, Membership, Member, from mautrix.types import (RoomID, UserID, RoomCreatePreset, EventType, Membership,
PowerLevelStateEventContent, RoomTopicStateEventContent, PowerLevelStateEventContent, RoomTopicStateEventContent,
RoomNameStateEventContent, RoomAvatarStateEventContent, RoomNameStateEventContent, RoomAvatarStateEventContent,
StateEventContent, EventID) StateEventContent, EventID)
+9 -8
View File
@@ -99,8 +99,7 @@ class PortalTelegram(BasePortal, ABC):
encrypt=self.encrypted) encrypt=self.encrypted)
if not file: if not file:
return None return None
if self.get_config("inline_images") and (evt.message if self.get_config("inline_images") and (evt.message or evt.fwd_from or evt.reply_to):
or evt.fwd_from or evt.reply_to_msg_id):
content = await formatter.telegram_to_matrix( content = await formatter.telegram_to_matrix(
evt, source, self.main_intent, evt, source, self.main_intent,
prefix_html=f"<img src='{file.mxc}' alt='Inline Telegram photo'/><br/>", prefix_html=f"<img src='{file.mxc}' alt='Inline Telegram photo'/><br/>",
@@ -439,12 +438,12 @@ class PortalTelegram(BasePortal, ABC):
"max_file_size": min(config["bridge.max_document_size"], 2000) * 1024 * 1024 "max_file_size": min(config["bridge.max_document_size"], 2000) * 1024 * 1024
} }
async def backfill(self, source: 'AbstractUser', is_initial: bool = False, async def backfill(self, source: 'u.User', is_initial: bool = False,
limit: Optional[int] = None, last_id: Optional[int] = None) -> None: limit: Optional[int] = None, last_id: Optional[int] = None) -> None:
async with self.backfill_method_lock: async with self.backfill_method_lock:
await self._locked_backfill(source, is_initial, limit, last_id) await self._locked_backfill(source, is_initial, limit, last_id)
async def _locked_backfill(self, source: 'AbstractUser', is_initial: bool = False, async def _locked_backfill(self, source: 'u.User', is_initial: bool = False,
limit: Optional[int] = None, last_id: Optional[int] = None) -> None: limit: Optional[int] = None, last_id: Optional[int] = None) -> None:
limit = limit or (config["bridge.backfill.initial_limit"] if is_initial limit = limit or (config["bridge.backfill.initial_limit"] if is_initial
else config["bridge.backfill.missed_limit"]) else config["bridge.backfill.missed_limit"])
@@ -481,7 +480,7 @@ class PortalTelegram(BasePortal, ABC):
with self.backfill_lock: with self.backfill_lock:
await self._backfill(source, min_id, limit) await self._backfill(source, min_id, limit)
async def _backfill(self, source: 'AbstractUser', min_id: Optional[int], limit: int) -> None: async def _backfill(self, source: 'u.User', min_id: Optional[int], limit: int) -> None:
self.backfill_leave = set() self.backfill_leave = set()
if ((self.peer_type == "user" and self.tgid != source.tgid if ((self.peer_type == "user" and self.tgid != source.tgid
and config["bridge.backfill.invite_own_puppet"])): and config["bridge.backfill.invite_own_puppet"])):
@@ -514,7 +513,8 @@ class PortalTelegram(BasePortal, ABC):
self.log.debug(f"Iterating all messages starting with {min_id} (approx: {limit})") self.log.debug(f"Iterating all messages starting with {min_id} (approx: {limit})")
messages = client.iter_messages(entity, reverse=True, min_id=min_id) messages = client.iter_messages(entity, reverse=True, min_id=min_id)
async for message in messages: async for message in messages:
sender = p.Puppet.get(message.from_id) if message.from_id else None sender = (p.Puppet.get(message.from_id.user_id)
if isinstance(message.from_id, PeerUser) else None)
# TODO handle service messages? # TODO handle service messages?
await self.handle_telegram_message(source, sender, message) await self.handle_telegram_message(source, sender, message)
count += 1 count += 1
@@ -522,7 +522,8 @@ class PortalTelegram(BasePortal, ABC):
self.log.debug(f"Fetching up to {limit} most recent messages") self.log.debug(f"Fetching up to {limit} most recent messages")
messages = await client.get_messages(entity, limit=limit) messages = await client.get_messages(entity, limit=limit)
for message in reversed(messages): for message in reversed(messages):
sender = p.Puppet.get(message.from_id) if message.from_id else None sender = (p.Puppet.get(TelegramID(message.from_id.user_id))
if isinstance(message.from_id, PeerUser) else None)
await self.handle_telegram_message(source, sender, message) await self.handle_telegram_message(source, sender, message)
count += 1 count += 1
return count return count
@@ -533,7 +534,7 @@ class PortalTelegram(BasePortal, ABC):
self.log.trace("Got telegram message %d, but no room exists, creating...", evt.id) self.log.trace("Got telegram message %d, but no room exists, creating...", evt.id)
await self.create_matrix_room(source, invites=[source.mxid], update_if_exists=False) await self.create_matrix_room(source, invites=[source.mxid], update_if_exists=False)
if (self.peer_type == "user" and sender.tgid == self.tg_receiver if (self.peer_type == "user" and sender and sender.tgid == self.tg_receiver
and not sender.is_real_user and not await self.az.state_store.is_joined(self.mxid, and not sender.is_real_user and not await self.az.state_store.is_joined(self.mxid,
sender.mxid)): sender.mxid)):
self.log.debug(f"Ignoring private chat message {evt.id}@{source.tgid} as receiver does" self.log.debug(f"Ignoring private chat message {evt.id}@{source.tgid} as receiver does"
+2 -2
View File
@@ -5,6 +5,6 @@ python-magic>=0.4,<0.5
commonmark>=0.8,<0.10 commonmark>=0.8,<0.10
aiohttp>=3,<3.7 aiohttp>=3,<3.7
yarl<1.6 yarl<1.6
mautrix==0.8.0.beta7 mautrix==0.8.0.beta9
telethon>=1.16,<1.17 telethon>=1.17,<1.18
telethon-session-sqlalchemy>=0.2.14,<0.3 telethon-session-sqlalchemy>=0.2.14,<0.3