Inbound channel photo & description
This commit is contained in:
parent
d843fcf5d2
commit
a12efc92c4
|
@ -38,10 +38,12 @@ class Portal:
|
|||
kt_type: ChannelType
|
||||
mxid: RoomID | None
|
||||
name: str | None
|
||||
description: str | None
|
||||
photo_id: str | None
|
||||
avatar_url: ContentURI | None
|
||||
encrypted: bool
|
||||
name_set: bool
|
||||
topic_set: bool
|
||||
avatar_set: bool
|
||||
relay_user_id: UserID | None
|
||||
|
||||
|
@ -56,7 +58,7 @@ class Portal:
|
|||
@classmethod
|
||||
async def get_by_ktid(cls, ktid: int, kt_receiver: int) -> Portal | None:
|
||||
q = """
|
||||
SELECT ktid, kt_receiver, kt_type, mxid, name, photo_id, avatar_url, encrypted,
|
||||
SELECT ktid, kt_receiver, kt_type, mxid, name, description, photo_id, avatar_url, encrypted,
|
||||
name_set, avatar_set, relay_user_id
|
||||
FROM portal WHERE ktid=$1 AND kt_receiver=$2
|
||||
"""
|
||||
|
@ -66,7 +68,7 @@ class Portal:
|
|||
@classmethod
|
||||
async def get_by_mxid(cls, mxid: RoomID) -> Portal | None:
|
||||
q = """
|
||||
SELECT ktid, kt_receiver, kt_type, mxid, name, photo_id, avatar_url, encrypted,
|
||||
SELECT ktid, kt_receiver, kt_type, mxid, name, description, photo_id, avatar_url, encrypted,
|
||||
name_set, avatar_set, relay_user_id
|
||||
FROM portal WHERE mxid=$1
|
||||
"""
|
||||
|
@ -76,7 +78,7 @@ class Portal:
|
|||
@classmethod
|
||||
async def get_all_by_receiver(cls, kt_receiver: int) -> list[Portal]:
|
||||
q = """
|
||||
SELECT ktid, kt_receiver, kt_type, mxid, name, photo_id, avatar_url, encrypted,
|
||||
SELECT ktid, kt_receiver, kt_type, mxid, name, description, photo_id, avatar_url, encrypted,
|
||||
name_set, avatar_set, relay_user_id
|
||||
FROM portal WHERE kt_receiver=$1
|
||||
"""
|
||||
|
@ -86,7 +88,7 @@ class Portal:
|
|||
@classmethod
|
||||
async def all(cls) -> list[Portal]:
|
||||
q = """
|
||||
SELECT ktid, kt_receiver, kt_type, mxid, name, photo_id, avatar_url, encrypted,
|
||||
SELECT ktid, kt_receiver, kt_type, mxid, name, description, photo_id, avatar_url, encrypted,
|
||||
name_set, avatar_set, relay_user_id
|
||||
FROM portal
|
||||
"""
|
||||
|
@ -101,6 +103,7 @@ class Portal:
|
|||
self.kt_type,
|
||||
self.mxid,
|
||||
self.name,
|
||||
self.description,
|
||||
self.photo_id,
|
||||
self.avatar_url,
|
||||
self.encrypted,
|
||||
|
@ -111,9 +114,9 @@ class Portal:
|
|||
|
||||
async def insert(self) -> None:
|
||||
q = """
|
||||
INSERT INTO portal (ktid, kt_receiver, kt_type, mxid, name, photo_id, avatar_url,
|
||||
INSERT INTO portal (ktid, kt_receiver, kt_type, mxid, name, description, photo_id, avatar_url,
|
||||
encrypted, name_set, avatar_set, relay_user_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
"""
|
||||
await self.db.execute(q, *self._values)
|
||||
|
||||
|
@ -123,8 +126,8 @@ class Portal:
|
|||
|
||||
async def save(self) -> None:
|
||||
q = """
|
||||
UPDATE portal SET kt_type=$3, mxid=$4, name=$5, photo_id=$6, avatar_url=$7,
|
||||
encrypted=$8, name_set=$9, avatar_set=$10, relay_user_id=$11
|
||||
UPDATE portal SET kt_type=$3, mxid=$4, name=$5, description=$6, photo_id=$7, avatar_url=$8,
|
||||
encrypted=$9, name_set=$10, avatar_set=$11, relay_user_id=$12
|
||||
WHERE ktid=$1 AND kt_receiver=$2
|
||||
"""
|
||||
await self.db.execute(q, *self._values)
|
||||
|
|
|
@ -3,3 +3,4 @@ from mautrix.util.async_db import UpgradeTable
|
|||
upgrade_table = UpgradeTable()
|
||||
|
||||
from . import v01_initial_revision
|
||||
from . import v02_channel_meta
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# matrix-appservice-kakaotalk - A Matrix-KakaoTalk puppeting bridge.
|
||||
# Copyright (C) 2022 Tulir Asokan, Andrew Ferrazzutti
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from mautrix.util.async_db import Connection
|
||||
|
||||
from . import upgrade_table
|
||||
|
||||
|
||||
@upgrade_table.register(description="Support channel descriptions")
|
||||
async def upgrade_v2(conn: Connection) -> None:
|
||||
await conn.execute("ALTER TABLE portal ADD COLUMN description TEXT")
|
||||
await conn.execute("ALTER TABLE portal ADD COLUMN topic_set BOOLEAN NOT NULL DEFAULT false")
|
|
@ -500,6 +500,13 @@ class Client:
|
|||
str(data["channelType"]),
|
||||
)
|
||||
|
||||
def _on_channel_meta_change(self, data: dict[str, JSON]) -> Awaitable[None]:
|
||||
return self.user.on_channel_meta_change(
|
||||
PortalChannelInfo.deserialize(data["info"]),
|
||||
Long.deserialize(data["channelId"]),
|
||||
str(data["channelType"]),
|
||||
)
|
||||
|
||||
|
||||
def _on_listen_disconnect(self, data: dict[str, JSON]) -> Awaitable[None]:
|
||||
try:
|
||||
|
@ -532,6 +539,7 @@ class Client:
|
|||
self._add_event_handler("channel_kicked", self._on_channel_kicked)
|
||||
self._add_event_handler("user_join", self._on_user_join)
|
||||
self._add_event_handler("user_left", self._on_user_left)
|
||||
self._add_event_handler("channel_meta_change", self._on_channel_meta_change)
|
||||
self._add_event_handler("disconnected", self._on_listen_disconnect)
|
||||
self._add_event_handler("switch_server", self._on_switch_server)
|
||||
self._add_event_handler("error", self._on_error)
|
||||
|
|
|
@ -69,8 +69,9 @@ setattr(UserInfoUnion, "deserialize", deserialize_user_info_union)
|
|||
@dataclass
|
||||
class PortalChannelInfo(SerializableAttrs):
|
||||
name: str
|
||||
participants: list[UserInfoUnion]
|
||||
# TODO Image
|
||||
description: Optional[str] = None
|
||||
photoURL: Optional[str] = None
|
||||
participants: Optional[list[UserInfoUnion]] = None # May set to None to skip participant update
|
||||
channel_info: Optional[ChannelInfoUnion] = None # Should be set manually by caller
|
||||
|
||||
|
||||
|
|
|
@ -146,10 +146,12 @@ class Portal(DBPortal, BasePortal):
|
|||
kt_type: ChannelType,
|
||||
mxid: RoomID | None = None,
|
||||
name: str | None = None,
|
||||
description: str | None = None,
|
||||
photo_id: str | None = None,
|
||||
avatar_url: ContentURI | None = None,
|
||||
encrypted: bool = False,
|
||||
name_set: bool = False,
|
||||
topic_set: bool = False,
|
||||
avatar_set: bool = False,
|
||||
relay_user_id: UserID | None = None,
|
||||
) -> None:
|
||||
|
@ -159,10 +161,12 @@ class Portal(DBPortal, BasePortal):
|
|||
kt_type,
|
||||
mxid,
|
||||
name,
|
||||
description,
|
||||
photo_id,
|
||||
avatar_url,
|
||||
encrypted,
|
||||
name_set,
|
||||
topic_set,
|
||||
avatar_set,
|
||||
relay_user_id,
|
||||
)
|
||||
|
@ -317,17 +321,18 @@ class Portal(DBPortal, BasePortal):
|
|||
changed = any(
|
||||
await asyncio.gather(
|
||||
self._update_name(info.name),
|
||||
# TODO
|
||||
#self._update_photo(source, info.image),
|
||||
self._update_description(info.description),
|
||||
self._update_photo(source, info.photoURL),
|
||||
)
|
||||
)
|
||||
changed = await self._update_participants(source, info.participants) or changed
|
||||
if info.participants is not None:
|
||||
changed = await self._update_participants(source, info.participants) or changed
|
||||
if self.mxid and self.is_open:
|
||||
user_power_levels = await self._get_mapped_participant_power_levels(info.participants, skip_default=False)
|
||||
asyncio.create_task(self.set_power_levels(user_power_levels))
|
||||
if changed or force_save:
|
||||
await self.update_bridge_info()
|
||||
await self.save()
|
||||
if self.mxid and self.is_open:
|
||||
user_power_levels = await self._get_mapped_participant_power_levels(info.participants, skip_default=False)
|
||||
await self.set_power_levels(user_power_levels)
|
||||
return info
|
||||
|
||||
async def _get_mapped_participant_power_levels(self, participants: list[UserInfoUnion], skip_default: bool) -> dict[UserID, int]:
|
||||
|
@ -434,23 +439,37 @@ class Portal(DBPortal, BasePortal):
|
|||
return True
|
||||
return False
|
||||
|
||||
"""
|
||||
async def _update_photo(self, source: u.User, photo: graphql.Picture) -> bool:
|
||||
async def _update_description(self, description: str | None) -> bool:
|
||||
if self.description != description or not self.topic_set:
|
||||
self.log.trace("Updating description %s -> %s", self.description, description)
|
||||
self.description = description
|
||||
if self.mxid and (self.encrypted or not self.is_direct):
|
||||
try:
|
||||
await self.main_intent.set_room_topic(self.mxid, self.description)
|
||||
self.topic_set = True
|
||||
except Exception:
|
||||
self.log.exception("Failed to set room description")
|
||||
self.topic_set = False
|
||||
return True
|
||||
return False
|
||||
|
||||
async def _update_photo(self, source: u.User, photo_id: str | None) -> bool:
|
||||
if self.is_direct and not self.encrypted:
|
||||
return False
|
||||
photo_id = self.get_photo_id(photo)
|
||||
if self.photo_id is not None and photo_id is None:
|
||||
self.log.warning("Portal previously had a photo_id, but new photo_id is None. Leaving it as it is")
|
||||
return False
|
||||
if self.photo_id != photo_id or not self.avatar_set:
|
||||
self.photo_id = photo_id
|
||||
if photo:
|
||||
if photo_id:
|
||||
if self.photo_id != photo_id or not self.avatar_url:
|
||||
# Reset avatar_url first in case the upload fails
|
||||
self.avatar_url = None
|
||||
self.avatar_url = await p.Puppet.reupload_avatar(
|
||||
source,
|
||||
self.main_intent,
|
||||
photo.uri,
|
||||
photo_id,
|
||||
self.ktid,
|
||||
use_graph=self.is_direct and (photo.height or 0) < 500,
|
||||
)
|
||||
else:
|
||||
self.avatar_url = ContentURI("")
|
||||
|
@ -463,7 +482,6 @@ class Portal(DBPortal, BasePortal):
|
|||
self.avatar_set = False
|
||||
return True
|
||||
return False
|
||||
"""
|
||||
|
||||
async def _update_photo_from_puppet(self, puppet: p.Puppet) -> bool:
|
||||
if self.photo_id == puppet.photo_id and self.avatar_set:
|
||||
|
|
|
@ -39,7 +39,7 @@ from .db import User as DBUser
|
|||
|
||||
from .kt.client import Client
|
||||
from .kt.client.errors import AuthenticationRequired, ResponseError
|
||||
from .kt.client.types import SettingsStruct, FROM_PERM_MAP
|
||||
from .kt.client.types import PortalChannelInfo, SettingsStruct, FROM_PERM_MAP
|
||||
from .kt.types.bson import Long
|
||||
from .kt.types.channel.channel_info import ChannelInfo, NormalChannelInfo, NormalChannelData
|
||||
from .kt.types.channel.channel_type import ChannelType, KnownChannelType
|
||||
|
@ -55,6 +55,7 @@ METRIC_CONNECT_AND_SYNC = Summary("bridge_connect_and_sync", "calls to connect_a
|
|||
METRIC_CHAT = Summary("bridge_on_chat", "calls to on_chat")
|
||||
METRIC_CHAT_DELETED = Summary("bridge_on_chat_deleted", "calls to on_chat_deleted")
|
||||
METRIC_CHAT_READ = Summary("bridge_on_chat_read", "calls to on_chat_read")
|
||||
METRIC_CHANNEL_META_CHANGE = Summary("bridge_on_channel_meta_change", "calls to on_channel_meta_change")
|
||||
METRIC_PROFILE_CHANGE = Summary("bridge_on_profile_changed", "calls to on_profile_changed")
|
||||
METRIC_PERM_CHANGE = Summary("bridge_on_perm_changed", "calls to on_perm_changed")
|
||||
METRIC_CHANNEL_JOIN = Summary("bridge_on_channel_join", "calls to on_channel_join")
|
||||
|
@ -741,6 +742,21 @@ class User(DBUser, BaseUser):
|
|||
await portal.backfill_lock.wait(f"read receipt from {sender_id}")
|
||||
await portal.handle_kakaotalk_chat_read(self, puppet, chat_id)
|
||||
|
||||
@async_time(METRIC_CHANNEL_META_CHANGE)
|
||||
async def on_channel_meta_change(
|
||||
self,
|
||||
info: PortalChannelInfo,
|
||||
channel_id: Long,
|
||||
channel_type: ChannelType,
|
||||
) -> None:
|
||||
portal = await po.Portal.get_by_ktid(
|
||||
channel_id,
|
||||
kt_receiver=self.ktid,
|
||||
kt_type=channel_type,
|
||||
)
|
||||
if portal:
|
||||
await portal.update_info(self, info)
|
||||
|
||||
@async_time(METRIC_PROFILE_CHANGE)
|
||||
async def on_profile_changed(self, info: OpenLinkChannelUserInfo) -> None:
|
||||
puppet = await pu.Puppet.get_by_ktid(info.userId)
|
||||
|
|
|
@ -191,6 +191,33 @@ class UserClient {
|
|||
})
|
||||
})
|
||||
|
||||
this.#talkClient.on("meta_change", (channel, type, newMeta) => {
|
||||
this.log(`Channel ${channel.channelId} metadata changed`)
|
||||
})
|
||||
|
||||
this.#talkClient.on("push_packet", (method, data) => {
|
||||
// TODO Find a better way to do this...but data doesn't have much.
|
||||
if (method == "SYNCLINKUP") {
|
||||
if (!data?.ol) return
|
||||
const linkURL = data.ol?.lu
|
||||
if (!linkURL) return
|
||||
for (const channel of this.#talkClient.channelList.open.all()) {
|
||||
if (channel.info.openLink?.linkURL == linkURL) {
|
||||
this.write("channel_meta_change", {
|
||||
info: {
|
||||
name: data.ol?.ln,
|
||||
description: data.ol?.desc || null,
|
||||
photoURL: data.ol?.liu || null,
|
||||
},
|
||||
channelId: channel.channelId,
|
||||
channelType: channel.info.type,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.#talkClient.on("disconnected", (reason) => {
|
||||
this.log(`Disconnected (reason=${reason})`)
|
||||
this.disconnect()
|
||||
|
@ -568,8 +595,10 @@ export default class PeerClient {
|
|||
|
||||
return makeCommandResult({
|
||||
name: talkChannel.getDisplayName(),
|
||||
description: talkChannel.info.openLink?.description,
|
||||
// TODO Find out why linkCoverURL is blank, despite having updated the channel!
|
||||
photoURL: talkChannel.info.openLink?.linkCoverURL || null,
|
||||
participants: Array.from(talkChannel.getAllUserInfo()),
|
||||
// TODO Image
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue