Update mautrix-python & copy latest goodies from mautrix-facebook

This commit is contained in:
Andrew Ferrazzutti 2022-04-02 23:16:53 -04:00
parent c8803bc8cc
commit 3900e666ff
9 changed files with 91 additions and 109 deletions

View File

@ -110,9 +110,9 @@ class Message:
event_ids: list[EventID],
timestamp: int,
mx_room: RoomID,
) -> None:
) -> list[Message]:
if not event_ids:
return
return []
columns = [col.strip('"') for col in cls.columns.split(", ")]
records = [
(mxid, mx_room, ktid, index, kt_chat, kt_receiver, timestamp)
@ -123,6 +123,7 @@ class Message:
await conn.copy_records_to_table("message", records=records, columns=columns)
else:
await conn.executemany(cls._insert_query, records)
return [Message(*record) for record in records]
async def insert(self) -> None:

View File

@ -43,6 +43,16 @@ class Reaction:
return None
return cls(**row)
@classmethod
async def get_by_message_ktid(cls, kt_msgid: str, kt_receiver: int) -> dict[int, Reaction]:
q = (
"SELECT mxid, mx_room, kt_msgid, kt_receiver, kt_sender, reaction "
"FROM reaction WHERE kt_msgid=$1 AND kt_receiver=$2"
)
rows = await cls.db.fetch(q, kt_msgid, kt_receiver)
row_gen = (cls._from_row(row) for row in rows)
return {react.kt_sender: react for react in row_gen}
@classmethod
async def get_by_ktid(cls, kt_msgid: str, kt_receiver: int, kt_sender: int) -> Reaction | None:
q = (

View File

@ -19,6 +19,9 @@ homeserver:
status_endpoint: null
# Endpoint for reporting per-message status.
message_send_checkpoint_endpoint: null
# Whether asynchronous uploads via MSC2246 should be enabled for media.
# Requires a media repo that supports MSC2246.
async_media: false
# Application service host/registration related details
# Changing these values requires regeneration of the registration.

View File

@ -64,80 +64,6 @@ class MatrixHandler(BaseMatrixHandler):
room_id, "This room has been marked as your KakaoTalk bridge notice room."
)
async def handle_puppet_invite(
self, room_id: RoomID, puppet: pu.Puppet, invited_by: u.User, event_id: EventID
) -> None:
intent = puppet.default_mxid_intent
self.log.debug(f"{invited_by.mxid} invited puppet for {puppet.ktid} to {room_id}")
if not await invited_by.is_logged_in():
await intent.error_and_leave(
room_id,
text="Please log in before inviting KakaoTalk puppets to private chats.",
)
return
portal = await po.Portal.get_by_mxid(room_id)
if portal:
if portal.is_direct:
await intent.error_and_leave(
room_id, text="You can not invite additional users to private chats."
)
return
# TODO add KakaoTalk inviting
# await portal.invite_kakaotalk(inviter, puppet)
# await intent.join_room(room_id)
return
await intent.join_room(room_id)
try:
members = await intent.get_room_members(room_id)
except MatrixError:
self.log.exception(f"Failed to get member list after joining {room_id}")
await intent.leave_room(room_id)
return
if len(members) > 2:
# TODO add KakaoTalk group creating
await intent.send_notice(
room_id, "You can not invite KakaoTalk puppets to multi-user rooms."
)
await intent.leave_room(room_id)
return
portal = await po.Portal.get_by_ktid(
puppet.ktid, fb_receiver=invited_by.ktid # TODO kt_type=??
)
if portal.mxid:
try:
await intent.invite_user(portal.mxid, invited_by.mxid, check_cache=False)
await intent.send_notice(
room_id,
text=f"You already have a private chat with me in room {portal.mxid}",
html=(
"You already have a private chat with me: "
f"<a href='https://matrix.to/#/{portal.mxid}'>Link to room</a>"
),
)
await intent.leave_room(room_id)
return
except MatrixError:
pass
portal.mxid = room_id
e2be_ok = await portal.check_dm_encryption()
await portal.save()
if e2be_ok is True:
evt_type, content = await self.e2ee.encrypt(
room_id,
EventType.ROOM_MESSAGE,
TextMessageEventContent(
msgtype=MessageType.NOTICE,
body="Portal to private chat created and end-to-bridge encryption enabled.",
),
)
await intent.send_message_event(room_id, evt_type, content)
else:
message = "Portal to private chat created."
if e2be_ok is False:
message += "\n\nWarning: Failed to enable end-to-bridge encryption"
await intent.send_notice(room_id, message)
async def handle_invite(
self, room_id: RoomID, user_id: UserID, invited_by: u.User, event_id: EventID
) -> None:

View File

@ -217,9 +217,14 @@ class Portal(DBPortal, BasePortal):
async def delete(self) -> None:
if self.mxid:
await DBMessage.delete_all_by_room(self.mxid)
self.by_mxid.pop(self.mxid, None)
self.by_ktid.pop(self._ktid_full, None)
self.by_mxid.pop(self.mxid, None)
await super().delete()
self.mxid = None
self.name_set = False
self.avatar_set = False
self.relay_user_id = None
self.encrypted = False
await super().save()
# endregion
# region Properties
@ -265,6 +270,11 @@ class Portal(DBPortal, BasePortal):
)
return self._main_intent
async def get_dm_puppet(self) -> p.Puppet | None:
if not self.is_direct:
return None
return await p.Puppet.get_by_ktid(self.kt_sender)
# endregion
# region Chat info updating
@ -362,7 +372,12 @@ class Portal(DBPortal, BasePortal):
data, decryption_info = encrypt_attachment(data)
upload_mime_type = "application/octet-stream"
filename = None
url = await intent.upload_media(data, mime_type=upload_mime_type, filename=filename)
url = await intent.upload_media(
data,
mime_type=upload_mime_type,
filename=filename,
async_upload=cls.config["homeserver.async_media"],
)
if decryption_info:
decryption_info.url = url
return url, info, decryption_info
@ -436,6 +451,15 @@ class Portal(DBPortal, BasePortal):
self.avatar_set = False
return True
async def update_info_from_puppet(self, puppet: p.Puppet | None = None) -> bool:
if not self.is_direct:
return False
if not puppet:
puppet = await self.get_dm_puppet()
changed = await self._update_name(puppet.name)
changed = await self._update_photo_from_puppet(puppet) or changed
return changed
"""
async def sync_per_room_nick(self, puppet: p.Puppet, name: str) -> None:
intent = puppet.intent_for(self)
@ -457,31 +481,41 @@ class Portal(DBPortal, BasePortal):
)
"""
async def _update_participant(
self, source: u.User, participant: UserInfoUnion
) -> bool:
# TODO nick map?
self.log.trace("Syncing participant %s", participant.id)
puppet = await p.Puppet.get_by_ktid(participant.userId)
await puppet.update_info_from_participant(source, participant)
changed = False
if self.is_direct and self._kt_sender == puppet.ktid and self.encrypted:
changed = await self._update_info_from_puppet(puppet.name) or changed
if self.mxid:
if puppet.ktid != self.kt_receiver or puppet.is_real_user:
await puppet.intent_for(self).ensure_joined(self.mxid, bot=self.main_intent)
#if puppet.ktid in nick_map:
# await self.sync_per_room_nick(puppet, nick_map[puppet.ktid])
return changed
async def _update_participants(self, source: u.User, participants: list[UserInfoUnion] | None = None) -> bool:
# TODO nick map?
if participants is None:
self.log.debug("Called _update_participants with no participants, fetching them now...")
participants = await source.client.get_participants(self.channel_props)
changed = False
if not self._main_intent:
assert self.is_direct, "_main_intent for non-direct chat portal should have been set already"
self._kt_sender = participants[
0 if self.kt_type == KnownChannelType.MemoChat or participants[0].userId != source.ktid else 1
].userId
self._main_intent = (await p.Puppet.get_by_ktid(self._kt_sender)).default_mxid_intent
self._main_intent = (await self.get_dm_puppet()).default_mxid_intent
else:
self._kt_sender = (await p.Puppet.get_by_mxid(self._main_intent.mxid)).ktid if self.is_direct else None
# TODO nick_map?
for participant in participants:
puppet = await p.Puppet.get_by_ktid(participant.userId)
await puppet.update_info_from_participant(source, participant)
if self.is_direct and self._kt_sender == puppet.ktid and self.encrypted:
changed = await self._update_name(puppet.name) or changed
changed = await self._update_photo_from_puppet(puppet) or changed
if self.mxid:
if puppet.ktid != self.kt_receiver or puppet.is_real_user:
await puppet.intent_for(self).ensure_joined(self.mxid, bot=self.main_intent)
#if puppet.ktid in nick_map:
# await self.sync_per_room_nick(puppet, nick_map[puppet.ktid])
sync_tasks = [
self._update_participant(source, pcp) for pcp in participants
]
changed = any(await asyncio.gather(*sync_tasks))
return changed
# endregion
@ -833,7 +867,7 @@ class Portal(DBPortal, BasePortal):
if message.relates_to.rel_type == RelationType.REPLY:
reply_to_msg = await DBMessage.get_by_mxid(message.relates_to.event_id, self.mxid)
if reply_to_msg:
reply_to = reply_to_msg.fbid
reply_to = reply_to_msg.ktid
else:
self.log.warning(
f"Couldn't find reply target {message.relates_to.event_id}"
@ -928,16 +962,6 @@ class Portal(DBPortal, BasePortal):
)
self._typing = users
async def enable_dm_encryption(self) -> bool:
ok = await super().enable_dm_encryption()
if ok:
try:
puppet = await p.Puppet.get_by_ktid(self.ktid)
await self.main_intent.set_room_name(self.mxid, puppet.name)
except Exception:
self.log.warning(f"Failed to set room name", exc_info=True)
return ok
# endregion
# region KakaoTalk event handling
@ -976,6 +1000,7 @@ class Portal(DBPortal, BasePortal):
sender: p.Puppet,
message: Chatlog,
) -> None:
# TODO Backfill!! This avoids timing conflicts on startup sync
self.log.debug(f"Handling KakaoTalk event {message.logId}")
if not self.mxid:
mxid = await self.create_matrix_room(source)
@ -1015,6 +1040,7 @@ class Portal(DBPortal, BasePortal):
self.log.warning(f"Unhandled KakaoTalk message {message.logId}")
return
self.log.debug(f"Handled KakaoTalk message {message.logId} -> {event_ids}")
# TODO Might have to handle remote reactions on messages created by bulk_create
await DBMessage.bulk_create(
ktid=message.logId,
kt_chat=self.ktid,
@ -1232,7 +1258,7 @@ class Portal(DBPortal, BasePortal):
messages = await source.client.get_chats(
self.channel_props,
after_log_id,
limit
limit,
)
if not messages:
self.log.debug("Didn't get any messages from server")

View File

@ -204,8 +204,8 @@ class Puppet(DBPuppet, BasePuppet):
return True
return False
@staticmethod
async def reupload_avatar(
self,
source: u.User,
intent: IntentAPI,
url: str,
@ -214,7 +214,9 @@ class Puppet(DBPuppet, BasePuppet):
async with source.client.get(url) as resp:
data = await resp.read()
mime = magic.mimetype(data)
return await intent.upload_media(data, mime_type=mime)
return await intent.upload_media(
data, mime_type=mime, async_upload=self.config["homeserver.async_media"]
)
async def _update_photo(self, source: u.User, photo_id: str) -> bool:
if photo_id != self.photo_id or not self.avatar_set:

View File

@ -555,6 +555,20 @@ class User(DBUser, BaseUser):
return None
return await pu.Puppet.get_by_ktid(self.ktid)
async def get_portal_with(self, puppet: pu.Puppet, create: bool = True) -> po.Portal | None:
# TODO
return None
"""
if not self.ktid or not self.client:
return None
return await po.Portal.get_by_ktid(
await self.client.get_dm_channel_id_for(puppet.ktid),
kt_receiver=self.ktid,
create=create,
kt_type=KnownChannelType.DirectChat if puppet.ktid != self.ktid else KnownChannelType.MemoChat
)
"""
# region KakaoTalk event handling
async def on_connect(self) -> None:

View File

@ -1,7 +1,7 @@
aiohttp>=3,<4
asyncpg>=0.20,<0.26
commonmark>=0.8,<0.10
mautrix==0.15.0rc4
mautrix>=0.15.4,<0.16
pycryptodome>=3,<4
python-magic>=0.4,<0.5
ruamel.yaml>=0.15.94,<0.18

View File

@ -48,7 +48,7 @@ setuptools.setup(
install_requires=install_requires,
extras_require=extras_require,
python_requires="~=3.7",
python_requires="~=3.8",
classifiers=[
"Development Status :: 1 - Planning",