Wait for outbound images to be sent
Also prevent possible race condition in waiting for inbound images, and generally tighten up file sending/receiving.
This commit is contained in:
parent
5a33500765
commit
afcc206a93
|
@ -150,6 +150,10 @@ class MautrixController {
|
||||||
* @property {string} [image] - The URL to the image in the message.
|
* @property {string} [image] - The URL to the image in the message.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
_isLoadedImageURL(src) {
|
||||||
|
return src?.startsWith("blob:")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a message element (mws-message-wrapper)
|
* Parse a message element (mws-message-wrapper)
|
||||||
*
|
*
|
||||||
|
@ -213,22 +217,27 @@ class MautrixController {
|
||||||
} else if (messageElement.classList.contains("mdRGT07Image")) {
|
} else if (messageElement.classList.contains("mdRGT07Image")) {
|
||||||
const img = messageElement.querySelector(".mdRGT07MsgImg > img")
|
const img = messageElement.querySelector(".mdRGT07MsgImg > img")
|
||||||
if (img) {
|
if (img) {
|
||||||
if (img.src.startsWith("blob:")) {
|
let resolve
|
||||||
messageData.image_url = img.src
|
// TODO Should reject on "#_chat_message_image_failure"
|
||||||
} else {
|
let observer = new MutationObserver((changes) => {
|
||||||
let resolve
|
for (const change of changes) {
|
||||||
// TODO Should reject on "#_chat_message_image_failure"
|
if (this._isLoadedImageURL(change.target.src) && observer) {
|
||||||
let observer = new MutationObserver((changes) => {
|
observer.disconnect()
|
||||||
for (const change of changes) {
|
observer = null
|
||||||
if (change.target.src.startsWith("blob:")) {
|
resolve(change.target.src)
|
||||||
observer.disconnect()
|
return
|
||||||
observer = null
|
|
||||||
resolve(change.target.src)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
observer.observe(img, { attributes: true, attributeFilter: ["src"] })
|
})
|
||||||
|
observer.observe(img, { attributes: true, attributeFilter: ["src"] })
|
||||||
|
|
||||||
|
if (this._isLoadedImageURL(img.src)) {
|
||||||
|
// Check for this AFTER attaching the observer, in case
|
||||||
|
// the image loaded after the img element was found but
|
||||||
|
// before the observer was attached.
|
||||||
|
messageData.image_url = img.src
|
||||||
|
observer.disconnect()
|
||||||
|
} else {
|
||||||
messageData.image_url = await new Promise((realResolve, reject) => {
|
messageData.image_url = await new Promise((realResolve, reject) => {
|
||||||
resolve = realResolve
|
resolve = realResolve
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -236,7 +245,7 @@ class MautrixController {
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
resolve(img.src)
|
resolve(img.src)
|
||||||
}
|
}
|
||||||
}, 5000)
|
}, 10000) // Longer timeout for image downloads
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,10 +254,11 @@ class MautrixController {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
promiseOwnMessage() {
|
promiseOwnMessage(timeoutLimitMillis, successSelector, failureSelector=null) {
|
||||||
let observer
|
let observer
|
||||||
let msgID = -1
|
let msgID = -1
|
||||||
let resolve
|
let resolve
|
||||||
|
let reject
|
||||||
|
|
||||||
const resolveMessage = () => {
|
const resolveMessage = () => {
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
|
@ -257,50 +267,81 @@ class MautrixController {
|
||||||
resolve(msgID)
|
resolve(msgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
const invisibleTimeCallback = (changes) => {
|
const rejectMessage = failureElement => {
|
||||||
|
observer.disconnect()
|
||||||
|
observer = null
|
||||||
|
reject(failureElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeCallback = (changes) => {
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
for (const addedNode of change.addedNodes) {
|
for (const addedNode of change.addedNodes) {
|
||||||
if (addedNode.classList.contains("mdRGT07Own")) {
|
if (addedNode.classList.contains("mdRGT07Own")) {
|
||||||
const timeElement = addedNode.querySelector("time")
|
const successElement = addedNode.querySelector(successSelector)
|
||||||
if (timeElement) {
|
if (successElement) {
|
||||||
|
console.log("Found success element")
|
||||||
|
console.log(successElement)
|
||||||
msgID = +addedNode.getAttribute("data-local-id")
|
msgID = +addedNode.getAttribute("data-local-id")
|
||||||
if (timeElement.classList.contains(".MdNonDisp")) {
|
if (successElement.classList.contains("MdNonDisp")) {
|
||||||
|
console.log("Invisible success, wait")
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
observer = new MutationObserver(visibleTimeCallback)
|
observer = new MutationObserver(getVisibleCallback(true))
|
||||||
observer.observe(timeElement, { attributes: true, attributeFilter: ["class"] })
|
observer.observe(successElement, { attributes: true, attributeFilter: ["class"] })
|
||||||
} else {
|
} else {
|
||||||
|
console.log("Already visible success")
|
||||||
resolveMessage()
|
resolveMessage()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
} else if (failureSelector) {
|
||||||
|
const failureElement = addedNode.querySelector(failureSelector)
|
||||||
|
if (failureElement) {
|
||||||
|
console.log("Found failure element")
|
||||||
|
console.log(failureElement)
|
||||||
|
if (failureElement.classList.contains("MdNonDisp")) {
|
||||||
|
console.log("Invisible failure, wait")
|
||||||
|
observer.disconnect()
|
||||||
|
observer = new MutationObserver(getVisibleCallback(false))
|
||||||
|
observer.observe(successElement, { attributes: true, attributeFilter: ["class"] })
|
||||||
|
} else {
|
||||||
|
console.log("Already visible failure")
|
||||||
|
rejectMessage(failureElement)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const visibleTimeCallback = (changes) => {
|
const getVisibleCallback = isSuccess => {
|
||||||
for (const change of changes) {
|
return changes => {
|
||||||
if (!change.target.classList.contains("MdNonDisp")) {
|
for (const change of changes) {
|
||||||
resolveMessage()
|
if (!change.target.classList.contains("MdNonDisp")) {
|
||||||
return
|
console.log(`Waited for visible ${isSuccess ? "success" : "failure"}`)
|
||||||
|
console.log(change.target)
|
||||||
|
isSuccess ? resolveMessage() : rejectMessage(change.target)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
observer = new MutationObserver(invisibleTimeCallback)
|
observer = new MutationObserver(changeCallback)
|
||||||
observer.observe(
|
observer.observe(
|
||||||
document.querySelector("#_chat_room_msg_list"),
|
document.querySelector("#_chat_room_msg_list"),
|
||||||
{ childList: true })
|
{ childList: true })
|
||||||
|
|
||||||
return new Promise((realResolve, reject) => {
|
return new Promise((realResolve, realReject) => {
|
||||||
resolve = realResolve
|
resolve = realResolve
|
||||||
// TODO Handle a timeout better than this
|
reject = realReject
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (observer) {
|
if (observer) {
|
||||||
|
console.log("Timeout!")
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
reject()
|
reject()
|
||||||
}
|
}
|
||||||
}, 5000)
|
}, timeoutLimitMillis)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -403,7 +403,10 @@ export default class MessagesPuppeteer {
|
||||||
async _sendFileUnsafe(chatID, filePath) {
|
async _sendFileUnsafe(chatID, filePath) {
|
||||||
await this._switchChat(chatID)
|
await this._switchChat(chatID)
|
||||||
const promise = this.page.evaluate(
|
const promise = this.page.evaluate(
|
||||||
() => window.__mautrixController.promiseOwnMessage())
|
() => window.__mautrixController.promiseOwnMessage(
|
||||||
|
10000, // Use longer timeout for file uploads
|
||||||
|
"#_chat_message_success_menu",
|
||||||
|
"#_chat_message_fail_menu"))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.log(`About to ask for file chooser in ${chatID}`)
|
this.log(`About to ask for file chooser in ${chatID}`)
|
||||||
|
@ -420,13 +423,14 @@ export default class MessagesPuppeteer {
|
||||||
|
|
||||||
// TODO Commonize with text message sending
|
// TODO Commonize with text message sending
|
||||||
try {
|
try {
|
||||||
this.log("Waiting for message to be sent")
|
this.log("Waiting for file to be sent")
|
||||||
const id = await promise
|
const id = await promise
|
||||||
this.log(`Successfully sent message ${id} to ${chatID}`)
|
this.log(`Successfully sent file in message ${id} to ${chatID}`)
|
||||||
return id
|
return id
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO Handle a timeout better than this
|
this.error(`Error sending file to ${chatID}`)
|
||||||
this.error(`Timed out waiting for message to ${chatID}`)
|
// TODO Figure out why e is undefined...
|
||||||
|
//this.error(e)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,7 +555,7 @@ export default class MessagesPuppeteer {
|
||||||
async _sendMessageUnsafe(chatID, text) {
|
async _sendMessageUnsafe(chatID, text) {
|
||||||
await this._switchChat(chatID)
|
await this._switchChat(chatID)
|
||||||
const promise = this.page.evaluate(
|
const promise = this.page.evaluate(
|
||||||
() => window.__mautrixController.promiseOwnMessage())
|
() => window.__mautrixController.promiseOwnMessage(5000, "time"))
|
||||||
|
|
||||||
const input = await this.page.$("#_chat_room_input")
|
const input = await this.page.$("#_chat_room_input")
|
||||||
await input.click()
|
await input.click()
|
||||||
|
@ -564,8 +568,8 @@ export default class MessagesPuppeteer {
|
||||||
this.log(`Successfully sent message ${id} to ${chatID}`)
|
this.log(`Successfully sent message ${id} to ${chatID}`)
|
||||||
return id
|
return id
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO Handle a timeout better than this
|
// TODO Catch if something other than a timeout
|
||||||
this.error(`Timed out waiting for message to ${chatID}`)
|
this.error(`Timed out sending message to ${chatID}`)
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue