diff --git a/matrix_puppeteer_line/portal.py b/matrix_puppeteer_line/portal.py index 148ff00..aad7eff 100644 --- a/matrix_puppeteer_line/portal.py +++ b/matrix_puppeteer_line/portal.py @@ -18,6 +18,8 @@ import mimetypes import asyncio import magic +from random import randint +from os import remove from mautrix.appservice import AppService, IntentAPI from mautrix.bridge import BasePortal, NotificationDisabler @@ -122,20 +124,25 @@ class Portal(DBPortal, BasePortal): return # TODO deduplication of outgoing messages text = message.body - media_id = None - if message.msgtype == MessageType.EMOTE: - text = f"/me {text}" + if message.msgtype.is_text: + if message.msgtype == MessageType.EMOTE: + text = f"/me {text}" + message_id = await sender.client.send(self.chat_id, text) elif message.msgtype.is_media: - # if message.file and decrypt_attachment: - # data = await self.main_intent.download_media(message.file.url) - # data = decrypt_attachment(data, message.file.key.key, - # message.file.hashes.get("sha256"), message.file.iv) - # else: - # data = await self.main_intent.download_media(message.url) - # mime_type = message.info.mimetype or magic.from_buffer(data, mime=True) - # TODO media - return - message_id = await sender.client.send(self.chat_id, text) + if message.file and decrypt_attachment: + data = await self.main_intent.download_media(message.file.url) + data = decrypt_attachment(data, message.file.key.key, + message.file.hashes.get("sha256"), message.file.iv) + else: + data = await self.main_intent.download_media(message.url) + mime_type = message.info.mimetype or magic.from_buffer(data, mime=True) + + # TODO Set path from config + file_path = f"/dev/shm/file_{randint(0,1000)}{mimetypes.guess_extension(mime_type)}" + temp_file = open(file_path, 'wb') + temp_file.write(data) + message_id = await sender.client.send_file(self.chat_id, file_path) + remove(file_path) # TODO Handle message-send timeouts better if message_id != -1: msg = DBMessage(mxid=event_id, mx_room=self.mxid, mid=message_id, chat_id=self.chat_id) diff --git a/matrix_puppeteer_line/rpc/client.py b/matrix_puppeteer_line/rpc/client.py index aa58f0c..eab9833 100644 --- a/matrix_puppeteer_line/rpc/client.py +++ b/matrix_puppeteer_line/rpc/client.py @@ -73,6 +73,10 @@ class Client(RPCClient): resp = await self.request("send", chat_id=chat_id, text=text) return resp["id"] + async def send_file(self, chat_id: int, file_path: str) -> int: + resp = await self.request("send_file", chat_id=chat_id, file_path=file_path) + return resp["id"] + async def set_last_message_ids(self, msg_ids: Dict[int, int]) -> None: await self.request("set_last_message_ids", msg_ids=msg_ids) diff --git a/puppet/src/client.js b/puppet/src/client.js index b20223b..ba2f3e2 100644 --- a/puppet/src/client.js +++ b/puppet/src/client.js @@ -228,6 +228,7 @@ export default class Client { login: req => this.puppet.waitForLogin(req.login_type, req.login_data), cancel_login: () => this.puppet.cancelLogin(), send: req => this.puppet.sendMessage(req.chat_id, req.text), + send_file: req => this.puppet.sendFile(req.chat_id, req.file_path), set_last_message_ids: req => this.puppet.setLastMessageIDs(req.msg_ids), get_chats: () => this.puppet.getRecentChats(), get_chat: req => this.puppet.getChatInfo(req.chat_id), diff --git a/puppet/src/puppet.js b/puppet/src/puppet.js index 4fe83a1..9c53634 100644 --- a/puppet/src/puppet.js +++ b/puppet/src/puppet.js @@ -396,6 +396,36 @@ export default class MessagesPuppeteer { imageUrl)) } + async sendFile(chatID, filePath) { + return { id: await this.taskQueue.push(() => this._sendFileUnsafe(chatID, filePath)) } + } + + async _sendFileUnsafe(chatID, filePath) { + await this._switchChat(chatID) + const promise = this.page.evaluate( + () => window.__mautrixController.promiseOwnMessage()) + + // TODO Exit when the attach button is unclickable, + // like in chats with a bot + const [fileChooser] = await Promise.all([ + this.page.waitForFileChooser(), + this.page.click("#_chat_room_plus_btn") + ]) + await fileChooser.accept([filePath]) + + // TODO Commonize with text message sending + try { + this.log("Waiting for message to be sent") + const id = await promise + this.log(`Successfully sent message ${id} to ${chatID}`) + return id + } catch (e) { + // TODO Handle a timeout better than this + this.error(`Timed out waiting for message to ${chatID}`) + return -1 + } + } + async startObserving() { this.log("Adding chat list observer") await this.page.evaluate(