Syncable chat lists

This commit is contained in:
Andrew Ferrazzutti 2021-02-18 01:03:33 -05:00
parent 76f2478c8c
commit fc05a8f832
5 changed files with 39 additions and 18 deletions

View File

@ -1,5 +1,18 @@
# mautrix-amp # mautrix-line
A very hacky Matrix-SMS bridge based on running Android Messages for Web in Puppeteer. A very hacky Matrix-LINE bridge based on running LINE's Chrome Store Extension in Puppeteer.
This project is temporary and will eventually be replaced by a separate Android app.
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

View File

@ -92,6 +92,7 @@ async def login_do(evt: CommandEvent, gen: AsyncGenerator[Tuple[str, str], None]
if not failure and evt.sender.command_status: if not failure and evt.sender.command_status:
await evt.reply("Successfully logged in, now syncing") await evt.reply("Successfully logged in, now syncing")
await evt.sender.sync() 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 # else command was cancelled or failed. Don't post message about it, "cancel" command or failure did already
evt.sender.command_status = None 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_text="Log into LINE via email/password",
help_args="<_email_> <_password_>") help_args="<_email_> <_password_>")
async def login_email(evt: CommandEvent) -> None: async def login_email(evt: CommandEvent) -> None:
await evt.az.intent.redact(evt.room_id, evt.event_id)
if len(evt.args) != 2: if len(evt.args) != 2:
await evt.reply("Usage: `$cmdprefix+sp login <email> <password>`") await evt.reply("Usage: `$cmdprefix+sp login <email> <password>`")
return return
if not await login_prep(evt, "email"): if not await login_prep(evt, "email"):
return return
await evt.az.intent.redact(evt.room_id, evt.event_id)
gen = evt.sender.client.login( gen = evt.sender.client.login(
evt.sender, evt.sender,
login_data=dict(email=evt.args[0], password=evt.args[1])) login_data=dict(email=evt.args[0], password=evt.args[1]))

View File

@ -318,7 +318,7 @@ class Portal(DBPortal, BasePortal):
await self.main_intent.invite_user(self.mxid, source.mxid, check_cache=True) await self.main_intent.invite_user(self.mxid, source.mxid, check_cache=True)
puppet = await p.Puppet.get_by_custom_mxid(source.mxid) puppet = await p.Puppet.get_by_custom_mxid(source.mxid)
if puppet: if puppet:
await puppet.intent.ensure_joined(self.mxid) await puppet.az.intent.ensure_joined(self.mxid)
await self.update_info(info) await self.update_info(info)
@ -395,7 +395,7 @@ class Portal(DBPortal, BasePortal):
puppet = await p.Puppet.get_by_custom_mxid(source.mxid) puppet = await p.Puppet.get_by_custom_mxid(source.mxid)
if puppet: if puppet:
try: try:
await puppet.intent.join_room_by_id(self.mxid) await puppet.az.intent.join_room_by_id(self.mxid)
except MatrixError: except MatrixError:
self.log.debug("Failed to join custom puppet into newly created portal", self.log.debug("Failed to join custom puppet into newly created portal",
exc_info=True) exc_info=True)

View File

@ -55,7 +55,6 @@ class User(DBUser, BaseUser):
self._metric_value = defaultdict(lambda: False) self._metric_value = defaultdict(lambda: False)
self._connection_check_task = None self._connection_check_task = None
self.client = None self.client = None
self.intent = None
@classmethod @classmethod
def init_cls(cls, bridge: 'MessagesBridge') -> None: 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") self.log.warning("Failed to log in with shared secret")
return return
self.log.debug("Logged in with shared secret") 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: except Exception:
self.log.exception("Error logging in with shared secret") self.log.exception("Error logging in with shared secret")

View File

@ -27,7 +27,7 @@ export default class MessagesPuppeteer {
static executablePath = undefined static executablePath = undefined
static disableDebug = false static disableDebug = false
static noSandbox = false static noSandbox = false
//static viewport = { width: 1920, height: 1080 } static viewport = { width: 960, height: 880 }
static url = undefined static url = undefined
static extensionDir = 'extension_files' static extensionDir = 'extension_files'
@ -231,16 +231,18 @@ export default class MessagesPuppeteer {
const text = messageSyncElement.innerText const text = messageSyncElement.innerText
return text.startsWith("Syncing messages...") return text.startsWith("Syncing messages...")
&& (text.endsWith("100%") || text.endsWith("NaN%")) && (text.endsWith("100%") || text.endsWith("NaN%"))
// TODO Sometimes it gets stuck at 99%...??
}, },
{}, {timeout: 10000}, // Assume 10 seconds is long enough
result) result)
this.loginRunning = false
await this.startObserving()
this.log("Login complete")
} catch (err) { } 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 //await chatDetailArea.$(".MdTxtDesc02") || // 1:1 chat with custom title - get participant's real name
participants = [{ participants = [{
id: id, // the ID of a 1:1 chat is the other user's member ID 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", "#_chat_contact_detail_view > a",
element => element.innerText), element => element.innerText),
}] }]
@ -460,7 +462,11 @@ export default class MessagesPuppeteer {
} }
this.log(`Found participants: ${participants}`) 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) { async _sendMessageUnsafe(chatID, text) {
@ -475,12 +481,14 @@ export default class MessagesPuppeteer {
} }
async _getMessagesUnsafe(id, minID = 0) { async _getMessagesUnsafe(id, minID = 0) {
/* TODO Also handle "decrypting" state
await this._switchChatUnsafe(id) await this._switchChatUnsafe(id)
this.log("Waiting for messages to load") this.log("Waiting for messages to load")
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))
return messages.filter(msg => msg.id > minID && !this.sentMessageIDs.has(msg.id)) return messages.filter(msg => msg.id > minID && !this.sentMessageIDs.has(msg.id))
*/
} }
async _processChatListChangeUnsafe(id) { async _processChatListChangeUnsafe(id) {