diff --git a/README.md b/README.md index c945501..cc047ad 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,18 @@ -# mautrix-amp -A very hacky Matrix-SMS bridge based on running Android Messages for Web in Puppeteer. -This project is temporary and will eventually be replaced by a separate Android app. +# mautrix-line +A very hacky Matrix-LINE bridge based on running LINE's Chrome Store Extension in Puppeteer. -Matrix room: [`#maunium:maunium.net`](https://matrix.to/#/#maunium:maunium.net) +Matrix room: [`#mautrix-line:miscworks.net`](https://matrix.to/#/#mautrix-line:miscworks.net) + +## Missing features +### Missing from LINE +- typing notifications +- message edits +- formatted messages + +### Missing from LINE on Chrome: +- message redaction (delete/unsend) +- replies +- voice messages + +### Missing from mautrix-line +- TODO diff --git a/mautrix_line/commands/auth.py b/mautrix_line/commands/auth.py index d34c397..2a1b07d 100644 --- a/mautrix_line/commands/auth.py +++ b/mautrix_line/commands/auth.py @@ -92,6 +92,7 @@ async def login_do(evt: CommandEvent, gen: AsyncGenerator[Tuple[str, str], None] if not failure and evt.sender.command_status: await evt.reply("Successfully logged in, now syncing") await evt.sender.sync() + await evt.reply("Syncing complete") # else command was cancelled or failed. Don't post message about it, "cancel" command or failure did already evt.sender.command_status = None @@ -107,12 +108,12 @@ async def login_qr(evt: CommandEvent) -> None: help_text="Log into LINE via email/password", help_args="<_email_> <_password_>") async def login_email(evt: CommandEvent) -> None: + await evt.az.intent.redact(evt.room_id, evt.event_id) if len(evt.args) != 2: await evt.reply("Usage: `$cmdprefix+sp login `") return if not await login_prep(evt, "email"): return - await evt.az.intent.redact(evt.room_id, evt.event_id) gen = evt.sender.client.login( evt.sender, login_data=dict(email=evt.args[0], password=evt.args[1])) diff --git a/mautrix_line/portal.py b/mautrix_line/portal.py index eeadbf5..ca4d174 100644 --- a/mautrix_line/portal.py +++ b/mautrix_line/portal.py @@ -318,7 +318,7 @@ class Portal(DBPortal, BasePortal): await self.main_intent.invite_user(self.mxid, source.mxid, check_cache=True) puppet = await p.Puppet.get_by_custom_mxid(source.mxid) if puppet: - await puppet.intent.ensure_joined(self.mxid) + await puppet.az.intent.ensure_joined(self.mxid) await self.update_info(info) @@ -395,7 +395,7 @@ class Portal(DBPortal, BasePortal): puppet = await p.Puppet.get_by_custom_mxid(source.mxid) if puppet: try: - await puppet.intent.join_room_by_id(self.mxid) + await puppet.az.intent.join_room_by_id(self.mxid) except MatrixError: self.log.debug("Failed to join custom puppet into newly created portal", exc_info=True) diff --git a/mautrix_line/user.py b/mautrix_line/user.py index 934dc90..477d0dc 100644 --- a/mautrix_line/user.py +++ b/mautrix_line/user.py @@ -55,7 +55,6 @@ class User(DBUser, BaseUser): self._metric_value = defaultdict(lambda: False) self._connection_check_task = None self.client = None - self.intent = None @classmethod def init_cls(cls, bridge: 'MessagesBridge') -> None: @@ -84,7 +83,7 @@ class User(DBUser, BaseUser): self.log.warning("Failed to log in with shared secret") return self.log.debug("Logged in with shared secret") - self.intent = self.az.intent.user(self.mxid, access_token) + #self.intent = self.az.intent.user(self.mxid, access_token) except Exception: self.log.exception("Error logging in with shared secret") diff --git a/puppet/src/puppet.js b/puppet/src/puppet.js index 62fd64f..21a2446 100644 --- a/puppet/src/puppet.js +++ b/puppet/src/puppet.js @@ -27,7 +27,7 @@ export default class MessagesPuppeteer { static executablePath = undefined static disableDebug = false static noSandbox = false - //static viewport = { width: 1920, height: 1080 } + static viewport = { width: 960, height: 880 } static url = undefined static extensionDir = 'extension_files' @@ -231,16 +231,18 @@ export default class MessagesPuppeteer { const text = messageSyncElement.innerText return text.startsWith("Syncing messages...") && (text.endsWith("100%") || text.endsWith("NaN%")) + // TODO Sometimes it gets stuck at 99%...?? }, - {}, + {timeout: 10000}, // Assume 10 seconds is long enough result) - - this.loginRunning = false - await this.startObserving() - this.log("Login complete") } catch (err) { - this._sendLoginFailure(`Failed to sync: ${err}`) + //this._sendLoginFailure(`Failed to sync: ${err}`) + this.log("LINE's sync took too long, assume it's fine and carry on...") } + + this.loginRunning = false + await this.startObserving() + this.log("Login complete") } /** @@ -452,7 +454,7 @@ export default class MessagesPuppeteer { //await chatDetailArea.$(".MdTxtDesc02") || // 1:1 chat with custom title - get participant's real name participants = [{ id: id, // the ID of a 1:1 chat is the other user's member ID - name: await participantElement.$eval( + name: await chatDetailArea.$eval( "#_chat_contact_detail_view > a", element => element.innerText), }] @@ -460,7 +462,11 @@ export default class MessagesPuppeteer { } this.log(`Found participants: ${participants}`) - return participants + return { + participants, + ...await this.page.$eval(this._listItemSelector(id), + elem => window.__mautrixController.parseChatListItem(elem)), + } } async _sendMessageUnsafe(chatID, text) { @@ -475,12 +481,14 @@ export default class MessagesPuppeteer { } async _getMessagesUnsafe(id, minID = 0) { + /* TODO Also handle "decrypting" state await this._switchChatUnsafe(id) this.log("Waiting for messages to load") await this.page.waitFor("mws-message-wrapper") const messages = await this.page.$eval("mws-messages-list .content", element => window.__mautrixController.parseMessageList(element)) return messages.filter(msg => msg.id > minID && !this.sentMessageIDs.has(msg.id)) + */ } async _processChatListChangeUnsafe(id) {