Avatar and icon URLs
This commit is contained in:
parent
8263023d2b
commit
b007751610
|
@ -31,23 +31,24 @@ class Portal:
|
|||
other_user: str
|
||||
mxid: Optional[RoomID]
|
||||
name: Optional[str]
|
||||
icon_url: Optional[str]
|
||||
encrypted: bool
|
||||
|
||||
async def insert(self) -> None:
|
||||
q = ("INSERT INTO portal (chat_id, other_user, mxid, name, encrypted) "
|
||||
"VALUES ($1, $2, $3, $4, $5)")
|
||||
q = ("INSERT INTO portal (chat_id, other_user, mxid, name, icon_url, encrypted) "
|
||||
"VALUES ($1, $2, $3, $4, $5, $6)")
|
||||
await self.db.execute(q, self.chat_id, self.other_user, self.mxid, self.name,
|
||||
self.encrypted)
|
||||
self.icon_url, self.encrypted)
|
||||
|
||||
async def update(self) -> None:
|
||||
q = ("UPDATE portal SET other_user=$2, mxid=$3, name=$4, encrypted=$5 "
|
||||
q = ("UPDATE portal SET other_user=$2, mxid=$3, name=$4, icon_url=$5, encrypted=$6 "
|
||||
"WHERE chat_id=$1")
|
||||
await self.db.execute(q, self.chat_id, self.other_user,
|
||||
self.mxid, self.name, self.encrypted)
|
||||
self.mxid, self.name, self.icon_url, self.encrypted)
|
||||
|
||||
@classmethod
|
||||
async def get_by_mxid(cls, mxid: RoomID) -> Optional['Portal']:
|
||||
q = ("SELECT chat_id, other_user, mxid, name, encrypted "
|
||||
q = ("SELECT chat_id, other_user, mxid, name, icon_url, encrypted "
|
||||
"FROM portal WHERE mxid=$1")
|
||||
row = await cls.db.fetchrow(q, mxid)
|
||||
if not row:
|
||||
|
@ -56,7 +57,7 @@ class Portal:
|
|||
|
||||
@classmethod
|
||||
async def get_by_chat_id(cls, chat_id: int) -> Optional['Portal']:
|
||||
q = ("SELECT chat_id, other_user, mxid, name, encrypted "
|
||||
q = ("SELECT chat_id, other_user, mxid, name, icon_url, encrypted "
|
||||
"FROM portal WHERE chat_id=$1")
|
||||
row = await cls.db.fetchrow(q, chat_id)
|
||||
if not row:
|
||||
|
@ -65,12 +66,12 @@ class Portal:
|
|||
|
||||
@classmethod
|
||||
async def find_private_chats(cls) -> List['Portal']:
|
||||
rows = await cls.db.fetch("SELECT chat_id, other_user, mxid, name, encrypted "
|
||||
rows = await cls.db.fetch("SELECT chat_id, other_user, mxid, name, icon_url, encrypted "
|
||||
"FROM portal WHERE other_user IS NOT NULL")
|
||||
return [cls(**row) for row in rows]
|
||||
|
||||
@classmethod
|
||||
async def all_with_room(cls) -> List['Portal']:
|
||||
rows = await cls.db.fetch("SELECT chat_id, other_user, mxid, name, encrypted "
|
||||
rows = await cls.db.fetch("SELECT chat_id, other_user, mxid, name, icon_url, encrypted "
|
||||
"FROM portal WHERE mxid IS NOT NULL")
|
||||
return [cls(**row) for row in rows]
|
||||
|
|
|
@ -28,20 +28,20 @@ class Puppet:
|
|||
|
||||
mid: str
|
||||
name: Optional[str]
|
||||
# TODO avatar: Optional[str]
|
||||
avatar_url: Optional[str]
|
||||
is_registered: bool
|
||||
|
||||
async def insert(self) -> None:
|
||||
q = "INSERT INTO puppet (mid, name, is_registered) VALUES ($1, $2, $3)"
|
||||
await self.db.execute(q, self.mid, self.name, self.is_registered)
|
||||
q = "INSERT INTO puppet (mid, name, avatar_url, is_registered) VALUES ($1, $2, $3, $4)"
|
||||
await self.db.execute(q, self.mid, self.name, self.avatar_url, self.is_registered)
|
||||
|
||||
async def update(self) -> None:
|
||||
q = "UPDATE puppet SET name=$2, is_registered=$3 WHERE mid=$1"
|
||||
await self.db.execute(q, self.mid, self.name, self.is_registered)
|
||||
q = "UPDATE puppet SET name=$2, avatar_url=$3, is_registered=$4 WHERE mid=$1"
|
||||
await self.db.execute(q, self.mid, self.name, self.avatar_url, self.is_registered)
|
||||
|
||||
@classmethod
|
||||
async def get_by_mid(cls, mid: str) -> Optional['Puppet']:
|
||||
row = await cls.db.fetchrow("SELECT mid, name, is_registered FROM puppet WHERE mid=$1",
|
||||
row = await cls.db.fetchrow("SELECT mid, name, avatar_url, is_registered FROM puppet WHERE mid=$1",
|
||||
mid)
|
||||
if not row:
|
||||
return None
|
||||
|
|
|
@ -46,3 +46,13 @@ async def upgrade_v1(conn: Connection) -> None:
|
|||
|
||||
UNIQUE (mxid, mx_room)
|
||||
)""")
|
||||
|
||||
|
||||
@upgrade_table.register(description="Avatars and icons")
|
||||
async def upgrade_avatars(conn: Connection) -> None:
|
||||
for (table, column) in [('puppet', 'avatar_url'), ('portal', 'icon_url')]:
|
||||
column_exists = await conn.fetchval(
|
||||
"SELECT EXISTS(SELECT FROM information_schema.columns "
|
||||
f"WHERE table_name='{table}' AND column_name='{column}')")
|
||||
if not column_exists:
|
||||
await conn.execute(f'ALTER TABLE "{table}" ADD COLUMN {column} TEXT')
|
|
@ -62,9 +62,9 @@ class Portal(DBPortal, BasePortal):
|
|||
_last_participant_update: Set[str]
|
||||
|
||||
def __init__(self, chat_id: int, other_user: Optional[str] = None,
|
||||
mxid: Optional[RoomID] = None, name: Optional[str] = None, encrypted: bool = False
|
||||
) -> None:
|
||||
super().__init__(chat_id, other_user, mxid, name, encrypted)
|
||||
mxid: Optional[RoomID] = None, name: Optional[str] = None, icon_url: Optional[str] = None,
|
||||
encrypted: bool = False) -> None:
|
||||
super().__init__(chat_id, other_user, mxid, name, icon_url, encrypted)
|
||||
self._create_room_lock = asyncio.Lock()
|
||||
self.log = self.log.getChild(str(chat_id))
|
||||
|
||||
|
@ -240,6 +240,7 @@ class Portal(DBPortal, BasePortal):
|
|||
# TODO Consider setting no room name for non-group chats.
|
||||
# But then the LINE bot itself may appear in the title...
|
||||
changed = await self._update_name(f"{conv.name} (LINE)")
|
||||
changed = await self._update_icon(conv.iconURL) or changed
|
||||
if changed:
|
||||
await self.update_bridge_info()
|
||||
await self.update()
|
||||
|
@ -255,6 +256,15 @@ class Portal(DBPortal, BasePortal):
|
|||
return True
|
||||
return False
|
||||
|
||||
async def _update_icon(self, icon_url: Optional[str]) -> bool:
|
||||
if self.icon_url != icon_url:
|
||||
self.icon_url = icon_url
|
||||
if icon_url:
|
||||
# TODO set icon from bytes
|
||||
pass
|
||||
return True
|
||||
return False
|
||||
|
||||
async def _update_participants(self, participants: List[Participant]) -> None:
|
||||
if not self.mxid:
|
||||
return
|
||||
|
@ -472,6 +482,8 @@ class Portal(DBPortal, BasePortal):
|
|||
await DBMessage.delete_all(self.mxid)
|
||||
self.by_mxid.pop(self.mxid, None)
|
||||
self.mxid = None
|
||||
self.name = None
|
||||
self.icon_url = None
|
||||
self.encrypted = False
|
||||
await self.update()
|
||||
|
||||
|
|
|
@ -38,8 +38,9 @@ class Puppet(DBPuppet, BasePuppet):
|
|||
|
||||
default_mxid: UserID
|
||||
|
||||
def __init__(self, mid: str, name: Optional[str] = None, is_registered: bool = False) -> None:
|
||||
super().__init__(mid=mid, name=name, is_registered=is_registered)
|
||||
def __init__(self, mid: str, name: Optional[str] = None, avatar_url: Optional[str] = None,
|
||||
is_registered: bool = False) -> None:
|
||||
super().__init__(mid=mid, name=name, avatar_url=avatar_url, is_registered=is_registered)
|
||||
self.log = self.log.getChild(mid)
|
||||
|
||||
self.default_mxid = self.get_mxid_from_id(mid)
|
||||
|
@ -63,7 +64,7 @@ class Puppet(DBPuppet, BasePuppet):
|
|||
async def update_info(self, info: Participant) -> None:
|
||||
update = False
|
||||
update = await self._update_name(info.name) or update
|
||||
# TODO Update avatar
|
||||
update = await self._update_avatar(info.avatarURL) or update
|
||||
if update:
|
||||
await self.update()
|
||||
|
||||
|
@ -75,6 +76,15 @@ class Puppet(DBPuppet, BasePuppet):
|
|||
return True
|
||||
return False
|
||||
|
||||
async def _update_avatar(self, avatar_url: Optional[str]) -> bool:
|
||||
if avatar_url != self.avatar_url:
|
||||
self.avatar_url = avatar_url
|
||||
if avatar_url:
|
||||
# TODO set the avatar from bytes
|
||||
pass
|
||||
return True
|
||||
return False
|
||||
|
||||
def _add_to_cache(self) -> None:
|
||||
self.by_mid[self.mid] = self
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class RPCError(Exception):
|
|||
class ChatListInfo(SerializableAttrs['ChatListInfo']):
|
||||
id: int
|
||||
name: str
|
||||
iconURL: Optional[str]
|
||||
lastMsg: str
|
||||
lastMsgDate: str
|
||||
|
||||
|
@ -35,7 +36,7 @@ class ChatListInfo(SerializableAttrs['ChatListInfo']):
|
|||
@dataclass
|
||||
class Participant(SerializableAttrs['Participant']):
|
||||
id: str
|
||||
# TODO avatar: str
|
||||
avatarURL: Optional[str]
|
||||
name: str
|
||||
|
||||
|
||||
|
|
|
@ -174,9 +174,6 @@ class MautrixController {
|
|||
// Room members are always friends (right?),
|
||||
// so search the friend list for the sender's name
|
||||
// and get their ID from there.
|
||||
// TODO For rooms, allow creating Matrix puppets in case
|
||||
// a message is sent by someone who since left the
|
||||
// room and never had a puppet made for them yet.
|
||||
sender.id = this.getUserIdFromFriendsList(sender.name)
|
||||
// Group members aren't necessarily friends,
|
||||
// but the participant list includes their ID.
|
||||
|
@ -185,7 +182,7 @@ class MautrixController {
|
|||
const participantsList = document.querySelector(participantsListSelector)
|
||||
sender.id = participantsList.querySelector(`img[alt='${senderName}'`).parentElement.parentElement.getAttribute("data-mid")
|
||||
}
|
||||
// TODO Avatar
|
||||
sender.avatarURL = this.getParticipantListItemAvatarURL(element)
|
||||
} else {
|
||||
// TODO Get own ID and store it somewhere appropriate.
|
||||
// Unable to get own ID from a room chat...
|
||||
|
@ -199,7 +196,7 @@ class MautrixController {
|
|||
await window.__mautrixShowParticipantsList()
|
||||
const participantsList = document.querySelector(participantsListSelector)
|
||||
sender.name = this.getParticipantListItemName(participantsList.children[0])
|
||||
// TODO avatar
|
||||
sender.avatarURL = this.getParticipantListItemAvatarURL(participantsList.children[0])
|
||||
sender.id = this.ownID
|
||||
}
|
||||
|
||||
|
@ -306,7 +303,7 @@ class MautrixController {
|
|||
* @typedef Participant
|
||||
* @type object
|
||||
* @property {string} id - The member ID for the participant
|
||||
* TODO @property {string} avatar - The URL of the participant's avatar
|
||||
* @property {string} avatarURL - The URL of the participant's avatar
|
||||
* @property {string} name - The contact list name of the participant
|
||||
*/
|
||||
|
||||
|
@ -314,6 +311,15 @@ class MautrixController {
|
|||
return element.querySelector(".mdRGT13Ttl").innerText
|
||||
}
|
||||
|
||||
getParticipantListItemAvatarURL(element) {
|
||||
const img = element.querySelector(".mdRGT13Img img[src]")
|
||||
if (img && img.getAttribute("data-picture-path") != "" && img.src.startsWith("blob:")) {
|
||||
return img.src
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
getParticipantListItemId(element) {
|
||||
// TODO Cache own ID
|
||||
return element.getAttribute("data-mid")
|
||||
|
@ -334,7 +340,7 @@ class MautrixController {
|
|||
// One idea is to add real ID as suffix if we're in a group, and
|
||||
// put in the puppet DB table somehow.
|
||||
id: this.ownID,
|
||||
// TODO avatar: child.querySelector("img").src,
|
||||
avatarURL: this.getParticipantListItemAvatarURL(element.children[0]),
|
||||
name: this.getParticipantListItemName(element.children[0]),
|
||||
}
|
||||
|
||||
|
@ -343,7 +349,7 @@ class MautrixController {
|
|||
const id = this.getParticipantListItemId(child) || this.getUserIdFromFriendsList(name)
|
||||
return {
|
||||
id: id, // NOTE Don't want non-own user's ID to ever be null.
|
||||
// TODO avatar: child.querySelector("img").src,
|
||||
avatarURL: this.getParticipantListItemAvatarURL(child),
|
||||
name: name,
|
||||
}
|
||||
}))
|
||||
|
@ -354,7 +360,7 @@ class MautrixController {
|
|||
* @type object
|
||||
* @property {number} id - The ID of the chat.
|
||||
* @property {string} name - The name of the chat.
|
||||
* TODO @property {string} icon - The icon of the chat.
|
||||
* @property {string} iconURL - The URL of the chat icon.
|
||||
* @property {string} lastMsg - The most recent message in the chat.
|
||||
* May be prefixed by sender name.
|
||||
* @property {string} lastMsgDate - An imprecise date for the most recent message
|
||||
|
@ -369,6 +375,15 @@ class MautrixController {
|
|||
return element.querySelector(".mdCMN04Ttl").innerText
|
||||
}
|
||||
|
||||
getChatListItemIconURL(element) {
|
||||
const img = element.querySelector(".mdCMN04Img > :not(.mdCMN04ImgInner) > img[src]")
|
||||
if (img && img.getAttribute("data-picture-path") != "" && img.src.startsWith("blob:")) {
|
||||
return img.src
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
getChatListItemLastMsg(element) {
|
||||
return element.querySelector(".mdCMN04Desc").innerText
|
||||
}
|
||||
|
@ -388,7 +403,7 @@ class MautrixController {
|
|||
return !element.classList.contains("chatList") ? null : {
|
||||
id: knownId || this.getChatListItemId(element),
|
||||
name: this.getChatListItemName(element),
|
||||
// TODO icon, but only for groups
|
||||
iconURL: this.getChatListItemIconURL(element),
|
||||
lastMsg: this.getChatListItemLastMsg(element),
|
||||
lastMsgDate: this.getChatListItemLastMsgDate(element),
|
||||
}
|
||||
|
|
|
@ -496,7 +496,7 @@ export default class MessagesPuppeteer {
|
|||
//await chatDetailArea.$(".MdTxtDesc02") || // 1:1 chat with custom title - get participant's real name
|
||||
participants = [{
|
||||
id: id,
|
||||
// TODO avatar, or leave null since this is a 1:1 chat
|
||||
avatarURL: chatListInfo.iconURL,
|
||||
name: chatListInfo.name,
|
||||
}]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue