Fix getting ID after sending and missed message backfilling

This commit is contained in:
Tulir Asokan 2020-08-28 19:34:13 +03:00
parent a3441a40ae
commit a7ffe6761a
5 changed files with 68 additions and 22 deletions

View File

@ -125,11 +125,9 @@ class Portal(DBPortal, BasePortal):
# mime_type = message.info.mimetype or magic.from_buffer(data, mime=True) # mime_type = message.info.mimetype or magic.from_buffer(data, mime=True)
# TODO media # TODO media
return return
await sender.client.send(self.chat_id, text) message_id = await sender.client.send(self.chat_id, text)
message_id = 0 msg = DBMessage(mxid=event_id, mx_room=self.mxid, mid=message_id, chat_id=self.chat_id)
# TODO figure out the message ID and store it await msg.insert()
# msg = DBMessage(mxid=event_id, mx_room=self.mxid, mid=message_id)
# await msg.insert()
await self._send_delivery_receipt(event_id) await self._send_delivery_receipt(event_id)
self.log.debug(f"Handled Matrix message {event_id} -> {message_id}") self.log.debug(f"Handled Matrix message {event_id} -> {message_id}")
@ -155,7 +153,10 @@ class Portal(DBPortal, BasePortal):
self.log.warning(f"Ignoring message {evt.id}: group chats aren't supported yet") self.log.warning(f"Ignoring message {evt.id}: group chats aren't supported yet")
return return
# TODO deduplication of outgoing messages if await DBMessage.get_by_mid(evt.id):
self.log.debug(f"Ignoring duplicate message {evt.id}")
return
event_id = None event_id = None
if evt.image: if evt.image:
content = await self._handle_remote_photo(source, intent, evt) content = await self._handle_remote_photo(source, intent, evt)
@ -238,8 +239,8 @@ class Portal(DBPortal, BasePortal):
if user_id == self.az.bot_mxid: if user_id == self.az.bot_mxid:
continue continue
mid = p.Puppet.get_id_from_mxid(user_id) mid = p.Puppet.get_id_from_mxid(user_id)
print(mid)
if mid and mid not in current_members: if mid and mid not in current_members:
print(mid)
await self.main_intent.kick_user(self.mxid, user_id, await self.main_intent.kick_user(self.mxid, user_id,
reason="User had left this chat") reason="User had left this chat")
@ -298,12 +299,15 @@ class Portal(DBPortal, BasePortal):
except Exception: except Exception:
self.log.warning("Failed to update bridge info", exc_info=True) self.log.warning("Failed to update bridge info", exc_info=True)
async def update_matrix_room(self, source: 'u.User', info: ChatInfo) -> Optional[RoomID]:
try:
await self._update_matrix_room(source, info)
except Exception:
self.log.exception("Failed to update portal")
async def create_matrix_room(self, source: 'u.User', info: ChatInfo) -> Optional[RoomID]: async def create_matrix_room(self, source: 'u.User', info: ChatInfo) -> Optional[RoomID]:
if self.mxid: if self.mxid:
try: await self.update_matrix_room(source, info)
await self._update_matrix_room(source, info)
except Exception:
self.log.exception("Failed to update portal")
return self.mxid return self.mxid
async with self._create_room_lock: async with self._create_room_lock:
return await self._create_matrix_room(source, info) return await self._create_matrix_room(source, info)

View File

@ -49,16 +49,16 @@ class Client(RPCClient):
resp = await self.request("get_messages", chat_id=chat_id) resp = await self.request("get_messages", chat_id=chat_id)
return [Message.deserialize(data) for data in resp] return [Message.deserialize(data) for data in resp]
async def send(self, chat_id: int, text: str) -> None: async def send(self, chat_id: int, text: str) -> int:
await self.request("send", chat_id=chat_id, text=text) resp = await self.request("send", chat_id=chat_id, text=text)
return resp["id"]
async def set_last_message_ids(self, msg_ids: Dict[int, int]) -> None: async def set_last_message_ids(self, msg_ids: Dict[int, int]) -> None:
await self.request("set_last_message_ids", msg_ids=msg_ids) await self.request("set_last_message_ids", msg_ids=msg_ids)
async def on_message(self, func: Callable[[Message], Awaitable[None]]) -> None: async def on_message(self, func: Callable[[Message], Awaitable[None]]) -> None:
async def wrapper(data: Dict[str, Any]) -> None: async def wrapper(data: Dict[str, Any]) -> None:
print("Received", data) await func(Message.deserialize(data["message"]))
await func(Message.deserialize(data))
self.add_event_handler("message", wrapper) self.add_event_handler("message", wrapper)

View File

@ -105,7 +105,11 @@ class User(DBUser, BaseUser):
portal = await po.Portal.get_by_chat_id(chat.id, create=True) portal = await po.Portal.get_by_chat_id(chat.id, create=True)
if portal.mxid or index < limit: if portal.mxid or index < limit:
chat = await self.client.get_chat(chat.id) chat = await self.client.get_chat(chat.id)
await portal.create_matrix_room(self, chat) if portal.mxid:
await portal.update_matrix_room(self, chat)
await portal.backfill(self)
else:
await portal.create_matrix_room(self, chat)
async def stop(self) -> None: async def stop(self) -> None:
if self.client: if self.client:

View File

@ -32,6 +32,11 @@ window.__mautrixReceiveChanges = function (changes) {}
* @return {Promise<void>} * @return {Promise<void>}
*/ */
window.__mautrixReceiveQR = function (url) {} window.__mautrixReceiveQR = function (url) {}
/**
* @param {number} id - The ID of the message that was sent
* @return {Promise<void>}
*/
window.__mautrixReceiveMessageID = function(id) {}
class MautrixController { class MautrixController {
constructor() { constructor() {
@ -112,6 +117,32 @@ class MautrixController {
return messageData return messageData
} }
waitForMessage(elem) {
return new Promise(resolve => {
let msgID = null
const observer = new MutationObserver(changes => {
for (const change of changes) {
if (change.type === "attributes" && change.attributeName === "msg-id") {
msgID = +elem.getAttribute("msg-id")
window.__mautrixReceiveMessageID(msgID)
} else if (change.type === "childList"
&& change.target.nodeName.toLowerCase() === "mws-relative-timestamp"
&& change.addedNodes.length > 0
&& change.addedNodes[0] instanceof Text) {
resolve(msgID)
observer.disconnect()
return
}
}
})
observer.observe(elem, { attributes: true, attributeFilter: ["msg-id"] })
observer.observe(elem.querySelector("mws-message-status"), {
childList: true,
subtree: true,
})
})
}
/** /**
* Parse a message list in the given element. The element should probably be the .content div * Parse a message list in the given element. The element should probably be the .content div
* inside a mws-message-list element. * inside a mws-message-list element.
@ -125,7 +156,9 @@ class MautrixController {
for (const child of element.children) { for (const child of element.children) {
switch (child.tagName.toLowerCase()) { switch (child.tagName.toLowerCase()) {
case "mws-message-wrapper": case "mws-message-wrapper":
messages.push(this._parseMessage(messageDate, child)) if (!child.getAttribute("msg-id").startsWith("tmp_")) {
messages.push(this._parseMessage(messageDate, child))
}
break break
case "mws-tombstone-message-wrapper": case "mws-tombstone-message-wrapper":
messageDate = await this._parseDate( messageDate = await this._parseDate(

View File

@ -43,6 +43,7 @@ export default class MessagesPuppeteer {
this.id = id this.id = id
this.profilePath = profilePath this.profilePath = profilePath
this.updatedChats = new Set() this.updatedChats = new Set()
this.sentMessageIDs = new Set()
this.mostRecentMessages = new Map() this.mostRecentMessages = new Map()
this.taskQueue = new TaskQueue(this.id) this.taskQueue = new TaskQueue(this.id)
this.client = client this.client = client
@ -83,6 +84,8 @@ export default class MessagesPuppeteer {
await this.page.addScriptTag({ path: "./src/contentscript.js", type: "module" }) await this.page.addScriptTag({ path: "./src/contentscript.js", type: "module" })
this.log("Exposing functions") this.log("Exposing functions")
await this.page.exposeFunction("__mautrixReceiveQR", this._receiveQRChange.bind(this)) await this.page.exposeFunction("__mautrixReceiveQR", this._receiveQRChange.bind(this))
await this.page.exposeFunction("__mautrixReceiveMessageID",
id => this.sentMessageIDs.add(id))
await this.page.exposeFunction("__mautrixReceiveChanges", await this.page.exposeFunction("__mautrixReceiveChanges",
this._receiveChatListChanges.bind(this)) this._receiveChatListChanges.bind(this))
await this.page.exposeFunction("__chronoParseDate", chrono.parseDate) await this.page.exposeFunction("__chronoParseDate", chrono.parseDate)
@ -195,9 +198,10 @@ export default class MessagesPuppeteer {
* *
* @param {number} chatID - The ID of the chat to send a message to. * @param {number} chatID - The ID of the chat to send a message to.
* @param {string} text - The text to send. * @param {string} text - The text to send.
* @return {Promise<{id: number}>} - The ID of the sent message.
*/ */
async sendMessage(chatID, text) { async sendMessage(chatID, text) {
await this.taskQueue.push(() => this._sendMessageUnsafe(chatID, text)) return { id: await this.taskQueue.push(() => this._sendMessageUnsafe(chatID, text)) }
} }
/** /**
@ -273,6 +277,10 @@ export default class MessagesPuppeteer {
await this.page.focus("mws-message-compose .input-box textarea") await this.page.focus("mws-message-compose .input-box textarea")
await this.page.keyboard.type(text) await this.page.keyboard.type(text)
await this.page.click(".compose-container > mws-message-send-button > button") await this.page.click(".compose-container > mws-message-send-button > button")
const id = await this.page.$eval("mws-message-wrapper.outgoing[msg-id^='tmp_']",
elem => window.__mautrixController.waitForMessage(elem))
this.log("Successfully sent message", id, "to", chatID)
return id
} }
async _getMessagesUnsafe(id, minID = 0) { async _getMessagesUnsafe(id, minID = 0) {
@ -281,10 +289,7 @@ export default class MessagesPuppeteer {
await this.page.waitFor("mws-message-wrapper") await this.page.waitFor("mws-message-wrapper")
const messages = await this.page.$eval("mws-messages-list .content", const messages = await this.page.$eval("mws-messages-list .content",
element => window.__mautrixController.parseMessageList(element)) element => window.__mautrixController.parseMessageList(element))
if (minID) { return messages.filter(msg => msg.id > minID && !this.sentMessageIDs.has(msg.id))
return messages.filter(message => message.id > minID)
}
return messages
} }
async _processChatListChangeUnsafe(id) { async _processChatListChangeUnsafe(id) {