stuff for multi person chats

This commit is contained in:
Andrew Ferrazzutti 2021-02-25 22:21:11 -05:00
parent a43502b45e
commit 328167c5f2
5 changed files with 69 additions and 31 deletions

View File

@ -58,6 +58,7 @@ class Config(BaseBridgeConfig):
copy("bridge.displayname_max_length")
copy("bridge.initial_conversation_sync")
copy("bridge.invite_own_puppet_to_pm")
copy("bridge.login_shared_secret")
copy("bridge.federate_rooms")
copy("bridge.backfill.invite_own_puppet")

View File

@ -49,6 +49,7 @@ ReuploadedMediaInfo = NamedTuple('ReuploadedMediaInfo', mxc=Optional[ContentURI]
class Portal(DBPortal, BasePortal):
invite_own_puppet_to_pm: bool = False
by_mxid: Dict[RoomID, 'Portal'] = {}
by_chat_id: Dict[int, 'Portal'] = {}
config: Config
@ -77,7 +78,15 @@ class Portal(DBPortal, BasePortal):
@property
def is_direct(self) -> bool:
return self.other_user is not None
return self.chat_id[0] == "u"
@property
def is_group(self) -> bool:
return self.chat_id[0] == "c"
@property
def is_room(self) -> bool:
return self.chat_id[0] == "r"
@property
def main_intent(self) -> IntentAPI:
@ -92,6 +101,7 @@ class Portal(DBPortal, BasePortal):
cls.az = bridge.az
cls.loop = bridge.loop
cls.bridge = bridge
cls.invite_own_puppet_to_pm = cls.config["bridge.invite_own_puppet_to_pm"]
NotificationDisabler.puppet_cls = p.Puppet
NotificationDisabler.config_enabled = cls.config["bridge.backfill.disable_notifications"]
@ -145,36 +155,54 @@ class Portal(DBPortal, BasePortal):
self.log.debug(f"{user.mxid} left portal to {self.chat_id}")
# TODO cleanup if empty
async def handle_remote_message(self, source: 'u.User', evt: Message) -> None:
if evt.is_outgoing:
async def _bridge_own_message_pm(self, source: 'u.User', sender: 'p.Puppet', mid: str,
invite: bool = True) -> bool:
if self.is_direct and sender.fbid == source.fbid and not sender.is_real_user:
if self.invite_own_puppet_to_pm and invite:
await self.main_intent.invite_user(self.mxid, sender.mxid)
elif await self.az.state_store.get_membership(self.mxid,
sender.mxid) != Membership.JOIN:
self.log.warning(f"Ignoring own {mid} in private chat because own puppet is not in"
" room.")
return False
return True
async def handle_remote_message(self, source: 'u.User', message: Message) -> None:
# TODO
if message.is_outgoing:
if not source.intent:
self.log.warning(f"Ignoring message {evt.id}: double puppeting isn't enabled")
return
intent = source.intent
if not await self._bridge_own_message_pm(source, sender, mid, invite):
return
if not self.invite_own_puppet_to_pm:
self.log.warning(f"Ignoring message {message.id}: double puppeting isn't enabled")
return
intent = self.main_intent
else:
intent = source.intent
elif self.other_user:
intent = (await p.Puppet.get_by_mid(self.other_user)).intent
else:
# TODO group chats
self.log.warning(f"Ignoring message {evt.id}: group chats aren't supported yet")
self.log.warning(f"Ignoring message {message.id}: group chats aren't supported yet")
return
if await DBMessage.get_by_mid(evt.id):
self.log.debug(f"Ignoring duplicate message {evt.id}")
if await DBMessage.get_by_mid(message.id):
self.log.debug(f"Ignoring duplicate message {message.id}")
return
event_id = None
if evt.image:
content = await self._handle_remote_photo(source, intent, evt)
if message.image:
content = await self._handle_remote_photo(source, intent, message)
if content:
event_id = await self._send_message(intent, content, timestamp=evt.timestamp)
if evt.text and not evt.text.isspace():
content = TextMessageEventContent(msgtype=MessageType.TEXT, body=evt.text)
event_id = await self._send_message(intent, content, timestamp=evt.timestamp)
event_id = await self._send_message(intent, content, timestamp=message.timestamp)
if message.text and not message.text.isspace():
content = TextMessageEventContent(msgtype=MessageType.TEXT, body=message.text)
event_id = await self._send_message(intent, content, timestamp=message.timestamp)
if event_id:
msg = DBMessage(mxid=event_id, mx_room=self.mxid, mid=evt.id, chat_id=self.chat_id)
msg = DBMessage(mxid=event_id, mx_room=self.mxid, mid=message.id, chat_id=self.chat_id)
await msg.insert()
await self._send_delivery_receipt(event_id)
self.log.debug(f"Handled remote message {evt.id} -> {event_id}")
self.log.debug(f"Handled remote message {message.id} -> {event_id}")
async def _handle_remote_photo(self, source: 'u.User', intent: IntentAPI, message: Message
) -> Optional[MediaMessageEventContent]:
@ -200,15 +228,16 @@ class Portal(DBPortal, BasePortal):
return ReuploadedMediaInfo(mxc, decryption_info, mime_type, file_name, len(data))
async def update_info(self, conv: ChatInfo) -> None:
# TODO Not true: a single-participant chat could be a group!
if len(conv.participants) == 1:
if self.is_direct:
self.other_user = conv.participants[0].id
if self._main_intent is self.az.intent:
self._main_intent = (await p.Puppet.get_by_mid(self.other_user)).intent
for participant in conv.participants:
puppet = await p.Puppet.get_by_mid(participant.id)
await puppet.update_info(participant)
changed = await self._update_name(conv.name)
# 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)")
if changed:
await self.update_bridge_info()
await self.update()
@ -267,8 +296,10 @@ class Portal(DBPortal, BasePortal):
self.log.debug("Got %d messages from server", len(messages))
async with NotificationDisabler(self.mxid, source):
for evt in messages:
await self.handle_remote_message(source, evt)
for message in messages:
# TODO
#puppet = await p.Puppet.get_by_mid(message.)
await self.handle_remote_message(source, message)
self.log.info("Backfilled %d messages through %s", len(messages), source.mxid)
@property

View File

@ -48,6 +48,8 @@ class Message(SerializableAttrs['Message']):
id: int
chat_id: int
is_outgoing: bool
# TODO
sender: Optional[str]
timestamp: int = None
text: Optional[str] = None
image: Optional[str] = None

View File

@ -104,6 +104,7 @@ class MautrixController {
* @property {number} id - The ID of the message. Seems to be sequential.
* @property {number} timestamp - The unix timestamp of the message. Not very accurate.
* @property {boolean} is_outgoing - Whether or not this user sent the message.
* @property {null|string} sender - The ID of the user who sent the message, or null if outgoing.
* @property {string} [text] - The text in the message.
* @property {string} [image] - The URL to the image in the message.
*/
@ -117,10 +118,17 @@ class MautrixController {
* @private
*/
_tryParseMessage(date, element) {
const is_outgoing: element.classList.contains("mdRGT07Own")
const messageData = {
id: +element.getAttribute("data-local-id"),
timestamp: date ? date.getTime() : null,
is_outgoing: element.classList.contains("mdRGT07Own"),
is_outgoing: is_outgoing,
// TODO The sender's mid isn't available, so must validate their display name somehow...
// Get it from contact list? It's always available in "#contact_wrap_friends > ul"
// <li .mdMN02Li data-mid, title=name>
// But what about non-friends?
// TODO Also, sender is always there, but not actually needed for DMs
sender: !is_outgoing ? element.querySelector(".mdRGT07Body > .mdRGT07Ttl") : null
}
const messageElement = element.querySelector(".mdRGT07Body > .mdRGT07Msg")
if (messageElement.classList.contains("mdRGT07Text")) {
@ -226,10 +234,8 @@ class MautrixController {
* @return {[Participant]} - The list of participants.
*/
parseParticipantList(element) {
// TODO Slice to exclude first member, which is always yourself (right?)
// TODO Only slice if double-puppeting is enabled!
//return Array.from(element.children).slice(1).map(child => {
return Array.from(element.children).map(child => {
// TODO The first member is always yourself, right?
return Array.from(element.children).slice(1).map(child => {
return {
id: child.getAttribute("data-mid"),
// TODO avatar: child.querySelector("img").src,
@ -331,7 +337,6 @@ class MautrixController {
* @private
*/
_observeChatListMutations(mutations) {
/* TODO
const changedChatIDs = new Set()
for (const change of mutations) {
console.debug("Chat list mutation:", change)
@ -350,7 +355,6 @@ class MautrixController {
() => console.debug("Chat list mutations dispatched"),
err => console.error("Error dispatching chat list mutations:", err))
}
*/
}
/**

View File

@ -95,10 +95,8 @@ export default class MessagesPuppeteer {
await this.page.exposeFunction("__mautrixExpiry", this._receiveExpiry.bind(this))
await this.page.exposeFunction("__mautrixReceiveMessageID",
id => this.sentMessageIDs.add(id))
/* TODO
await this.page.exposeFunction("__mautrixReceiveChanges",
this._receiveChatListChanges.bind(this))
*/
await this.page.exposeFunction("__chronoParseDate", chrono.parseDate)
// NOTE Must *always* re-login on a browser session, so no need to check if already logged in
@ -509,6 +507,7 @@ export default class MessagesPuppeteer {
return messages.filter(msg => msg.id > minID && !this.sentMessageIDs.has(msg.id))
}
// TODO
async _processChatListChangeUnsafe(id) {
this.updatedChats.delete(id)
this.log("Processing change to", id)
@ -535,6 +534,7 @@ export default class MessagesPuppeteer {
}
}
// TODO
_receiveChatListChanges(changes) {
this.log("Received chat list changes:", changes)
for (const item of changes) {