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