Use deterministic event IDs for backfill on hungryserv

This commit is contained in:
Tulir Asokan
2022-11-18 18:59:37 +02:00
parent 7c8cf3cb50
commit 4ca5bfb1ab
4 changed files with 39 additions and 7 deletions
+1 -1
View File
@@ -672,7 +672,7 @@ class AbstractUser(ABC):
if isinstance(update, MessageService): if isinstance(update, MessageService):
if isinstance(update.action, MessageActionChannelMigrateFrom): if isinstance(update.action, MessageActionChannelMigrateFrom):
self.log.trace( self.log.debug(
"Received %s in %s by %d, unregistering portal...", "Received %s in %s by %d, unregistering portal...",
update.action, update.action,
portal.tgid_log, portal.tgid_log,
+16 -3
View File
@@ -2580,7 +2580,7 @@ class Portal(DBPortal, BasePortal):
self, source: au.AbstractUser, sender: p.Puppet | None, evt: Message self, source: au.AbstractUser, sender: p.Puppet | None, evt: Message
) -> None: ) -> None:
if not self.mxid: if not self.mxid:
self.log.trace("Ignoring edit to %d as chat has no Matrix room", evt.id) self.log.debug("Ignoring edit to %d as chat has no Matrix room", evt.id)
return return
elif hasattr(evt, "media") and isinstance(evt.media, MessageMediaGame): elif hasattr(evt, "media") and isinstance(evt.media, MessageMediaGame):
self.log.debug("Ignoring game message edit event") self.log.debug("Ignoring game message edit event")
@@ -2892,7 +2892,14 @@ class Portal(DBPortal, BasePortal):
if sender: if sender:
await add_member(intent, sender.displayname, sender.avatar_url) await add_member(intent, sender.displayname, sender.avatar_url)
is_bot = sender.is_bot if sender else False is_bot = sender.is_bot if sender else False
converted = await self._msg_conv.convert(source, intent, is_bot, msg, client=client) converted = await self._msg_conv.convert(
source,
intent,
is_bot,
msg,
client=client,
deterministic_reply_id=self.bridge.homeserver_software.is_hungry,
)
return converted, intent return converted, intent
async def _wrap_batch_msg( async def _wrap_batch_msg(
@@ -2901,6 +2908,7 @@ class Portal(DBPortal, BasePortal):
msg: Message, msg: Message,
converted: putil.ConvertedMessage, converted: putil.ConvertedMessage,
caption: bool = False, caption: bool = False,
event_id: EventID | None = None,
) -> BatchSendEvent: ) -> BatchSendEvent:
if caption: if caption:
content = converted.caption content = converted.caption
@@ -2917,6 +2925,7 @@ class Portal(DBPortal, BasePortal):
timestamp=int(msg.date.timestamp() * 1000), timestamp=int(msg.date.timestamp() * 1000),
content=content, content=content,
type=event_type, type=event_type,
event_id=event_id,
) )
async def _backfill_messages( async def _backfill_messages(
@@ -2940,6 +2949,7 @@ class Portal(DBPortal, BasePortal):
else set() else set()
) )
before_first_msg_timestamp = 0 before_first_msg_timestamp = 0
tg_space = self.tgid if self.peer_type == "channel" else source.tgid
async def add_member(intent: IntentAPI, displayname: str, avatar_url: ContentURI) -> None: async def add_member(intent: IntentAPI, displayname: str, avatar_url: ContentURI) -> None:
if self.bridge.homeserver_software.is_hungry or intent.mxid in added_members: if self.bridge.homeserver_software.is_hungry or intent.mxid in added_members:
@@ -2993,7 +3003,10 @@ class Portal(DBPortal, BasePortal):
converted, intent = await self._convert_batch_msg(source, client, msg, add_member) converted, intent = await self._convert_batch_msg(source, client, msg, add_member)
if converted is None: if converted is None:
continue continue
events.append(await self._wrap_batch_msg(intent, msg, converted)) d_event_id = None
if self.bridge.homeserver_software.is_hungry:
d_event_id = self._msg_conv.deterministic_event_id(tg_space, msg.id)
events.append(await self._wrap_batch_msg(intent, msg, converted, event_id=d_event_id))
intents.append(intent) intents.append(intent)
metas.append(msg) metas.append(msg)
if converted.caption: if converted.caption:
@@ -18,6 +18,7 @@ from __future__ import annotations
from typing import Any, NamedTuple from typing import Any, NamedTuple
import base64 import base64
import codecs import codecs
import hashlib
import html import html
import mimetypes import mimetypes
import unicodedata import unicodedata
@@ -63,6 +64,7 @@ from telethon.utils import decode_waveform
from mautrix.appservice import IntentAPI from mautrix.appservice import IntentAPI
from mautrix.types import ( from mautrix.types import (
EventID,
EventType, EventType,
Format, Format,
ImageInfo, ImageInfo,
@@ -150,6 +152,7 @@ class TelegramMessageConverter:
is_bot: bool, is_bot: bool,
evt: Message, evt: Message,
no_reply_fallback: bool = False, no_reply_fallback: bool = False,
deterministic_reply_id: bool = False,
client: MautrixTelegramClient | None = None, client: MautrixTelegramClient | None = None,
) -> ConvertedMessage | None: ) -> ConvertedMessage | None:
if not client: if not client:
@@ -180,7 +183,13 @@ class TelegramMessageConverter:
converted.caption.external_url = converted.content.external_url converted.caption.external_url = converted.content.external_url
if self.portal.get_config("caption_in_message"): if self.portal.get_config("caption_in_message"):
self._caption_to_message(converted) self._caption_to_message(converted)
await self._set_reply(source, evt, converted.content, no_fallback=no_reply_fallback) await self._set_reply(
source,
evt,
converted.content,
no_fallback=no_reply_fallback,
deterministic_id=deterministic_reply_id,
)
return converted return converted
@staticmethod @staticmethod
@@ -227,12 +236,19 @@ class TelegramMessageConverter:
raise ValueError("Portal has invalid peer type") raise ValueError("Portal has invalid peer type")
return base64.b64encode(play_id).decode("utf-8").rstrip("=") return base64.b64encode(play_id).decode("utf-8").rstrip("=")
def deterministic_event_id(self, space: TelegramID, msg_id: TelegramID) -> EventID:
hash_content = f"{self.portal.mxid}/telegram/{space}/{msg_id}"
hashed = hashlib.sha256(hash_content.encode("utf-8")).digest()
b64hash = base64.urlsafe_b64encode(hashed).decode("utf-8").rstrip("=")
return EventID(f"${b64hash}:telegram.org")
async def _set_reply( async def _set_reply(
self, self,
source: au.AbstractUser, source: au.AbstractUser,
evt: Message, evt: Message,
content: MessageEventContent, content: MessageEventContent,
no_fallback: bool = False, no_fallback: bool = False,
deterministic_id: bool = False,
) -> None: ) -> None:
if not evt.reply_to: if not evt.reply_to:
return return
@@ -241,8 +257,11 @@ class TelegramMessageConverter:
if isinstance(evt, Message) and isinstance(evt.peer_id, PeerChannel) if isinstance(evt, Message) and isinstance(evt.peer_id, PeerChannel)
else source.tgid else source.tgid
) )
msg = await DBMessage.get_one_by_tgid(TelegramID(evt.reply_to.reply_to_msg_id), space) reply_to_id = TelegramID(evt.reply_to.reply_to_msg_id)
msg = await DBMessage.get_one_by_tgid(reply_to_id, space)
if not msg or msg.mx_room != self.portal.mxid: if not msg or msg.mx_room != self.portal.mxid:
if deterministic_id:
content.set_reply(self.deterministic_event_id(space, reply_to_id))
return return
elif not isinstance(content, TextMessageEventContent) or no_fallback: elif not isinstance(content, TextMessageEventContent) or no_fallback:
# Not a text message, just set the reply metadata and return # Not a text message, just set the reply metadata and return
+1 -1
View File
@@ -3,7 +3,7 @@ python-magic>=0.4,<0.5
commonmark>=0.8,<0.10 commonmark>=0.8,<0.10
aiohttp>=3,<4 aiohttp>=3,<4
yarl>=1,<2 yarl>=1,<2
mautrix>=0.18.6,<0.19 mautrix>=0.18.8,<0.19
#telethon>=1.25.4,<1.26 #telethon>=1.25.4,<1.26
tulir-telethon==1.26.0a10 tulir-telethon==1.26.0a10
asyncpg>=0.20,<0.27 asyncpg>=0.20,<0.27