|
|
|
@ -22,6 +22,9 @@ import chrono from "chrono-node"
|
|
|
|
|
|
|
|
|
|
import TaskQueue from "./taskqueue.js"
|
|
|
|
|
import { sleep } from "./util.js"
|
|
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
import utc from 'dayjs/plugin/utc.js';
|
|
|
|
|
import timezone from 'dayjs/plugin/timezone.js';
|
|
|
|
|
|
|
|
|
|
export default class MessagesPuppeteer {
|
|
|
|
|
static profileDir = "./profiles"
|
|
|
|
@ -34,7 +37,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
static viewport = { width: 960, height: 840 }
|
|
|
|
|
static url = undefined
|
|
|
|
|
static extensionDir = "extension_files"
|
|
|
|
|
|
|
|
|
|
static timeZone = "Asia/Taipei"
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {string} id
|
|
|
|
@ -79,7 +82,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
const args = [
|
|
|
|
|
`--disable-extensions-except=${MessagesPuppeteer.extensionDir}`,
|
|
|
|
|
`--load-extension=${MessagesPuppeteer.extensionDir}`,
|
|
|
|
|
`--window-size=${MessagesPuppeteer.viewport.width},${MessagesPuppeteer.viewport.height+120}`,
|
|
|
|
|
`--window-size=${MessagesPuppeteer.viewport.width},${MessagesPuppeteer.viewport.height + 120}`,
|
|
|
|
|
]
|
|
|
|
|
if (MessagesPuppeteer.noSandbox) {
|
|
|
|
|
args.push(`--no-sandbox`)
|
|
|
|
@ -142,7 +145,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
await this.page.exposeFunction("__mautrixLoggedOut",
|
|
|
|
|
this._onLoggedOut.bind(this))
|
|
|
|
|
await this.page.exposeFunction("__chronoParseDate", chrono.parseDate)
|
|
|
|
|
|
|
|
|
|
await this.page.exposeFunction("__tryParseDateByTimeZone", this._tryParseDateByTimeZone.bind(this))
|
|
|
|
|
// NOTE Must *always* re-login on a browser session, so no need to check if already logged in
|
|
|
|
|
this.loginRunning = false
|
|
|
|
|
this.loginCancelled = false
|
|
|
|
@ -150,6 +153,28 @@ export default class MessagesPuppeteer {
|
|
|
|
|
this.log("Startup complete")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _tryParseDateByTimeZone(text, ref, option) {
|
|
|
|
|
const localTz = MessagesPuppeteer.timeZone // This is the ISO string
|
|
|
|
|
dayjs.extend(utc);
|
|
|
|
|
dayjs.extend(timezone);
|
|
|
|
|
const localTime = dayjs.tz(new Date(), localTz)
|
|
|
|
|
const localOffset = localTime.utcOffset() // returns in minutes
|
|
|
|
|
|
|
|
|
|
const custom = chrono.casual.clone()
|
|
|
|
|
custom.refiners.push({
|
|
|
|
|
refine: (results) => {
|
|
|
|
|
Array.from(results).forEach((result) => {
|
|
|
|
|
// Returns the time with the offset in included (must use minutes)
|
|
|
|
|
result.start.imply('timezoneOffset', localOffset)
|
|
|
|
|
result.end && result.end.imply('timezoneOffset', localOffset)
|
|
|
|
|
})
|
|
|
|
|
return results
|
|
|
|
|
}})
|
|
|
|
|
const parsed = custom.parseDate(text, ref, option)
|
|
|
|
|
this.log(`parsed ${parsed}`)
|
|
|
|
|
return parsed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _preparePage(navigateTo) {
|
|
|
|
|
await this.page.bringToFront()
|
|
|
|
|
if (navigateTo) {
|
|
|
|
@ -196,7 +221,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
* @param {string} text - The text to input.
|
|
|
|
|
*/
|
|
|
|
|
async _enterText(inputElement, text) {
|
|
|
|
|
await inputElement.click({clickCount: 3})
|
|
|
|
|
await inputElement.click({ clickCount: 3 })
|
|
|
|
|
await inputElement.type(text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -213,45 +238,45 @@ export default class MessagesPuppeteer {
|
|
|
|
|
const loginContentArea = await this.page.waitForSelector("#login_content")
|
|
|
|
|
|
|
|
|
|
switch (login_type) {
|
|
|
|
|
case "qr": {
|
|
|
|
|
this.log("Running QR login")
|
|
|
|
|
const qrButton = await this.page.waitForSelector("#login_qr_btn")
|
|
|
|
|
await qrButton.click()
|
|
|
|
|
case "qr": {
|
|
|
|
|
this.log("Running QR login")
|
|
|
|
|
const qrButton = await this.page.waitForSelector("#login_qr_btn")
|
|
|
|
|
await qrButton.click()
|
|
|
|
|
|
|
|
|
|
const qrElement = await this.page.waitForSelector("#login_qrcode_area div[title]", {visible: true})
|
|
|
|
|
const currentQR = await this.page.evaluate(element => element.title, qrElement)
|
|
|
|
|
this._receiveQRChange(currentQR)
|
|
|
|
|
const qrElement = await this.page.waitForSelector("#login_qrcode_area div[title]", { visible: true })
|
|
|
|
|
const currentQR = await this.page.evaluate(element => element.title, qrElement)
|
|
|
|
|
this._receiveQRChange(currentQR)
|
|
|
|
|
|
|
|
|
|
await this.page.evaluate(
|
|
|
|
|
element => window.__mautrixController.addQRChangeObserver(element), qrElement)
|
|
|
|
|
await this.page.evaluate(
|
|
|
|
|
element => window.__mautrixController.addQRAppearObserver(element), loginContentArea)
|
|
|
|
|
await this.page.evaluate(
|
|
|
|
|
element => window.__mautrixController.addQRChangeObserver(element), qrElement)
|
|
|
|
|
await this.page.evaluate(
|
|
|
|
|
element => window.__mautrixController.addQRAppearObserver(element), loginContentArea)
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case "email": {
|
|
|
|
|
this.log("Running email login")
|
|
|
|
|
if (!login_data) {
|
|
|
|
|
this._sendLoginFailure("No login credentials provided for email login")
|
|
|
|
|
return
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
case "email": {
|
|
|
|
|
this.log("Running email login")
|
|
|
|
|
if (!login_data) {
|
|
|
|
|
this._sendLoginFailure("No login credentials provided for email login")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const emailButton = await this.page.waitForSelector("#login_email_btn")
|
|
|
|
|
await emailButton.click()
|
|
|
|
|
const emailButton = await this.page.waitForSelector("#login_email_btn")
|
|
|
|
|
await emailButton.click()
|
|
|
|
|
|
|
|
|
|
await this.page.waitForSelector("#login_email_area", {visible: true})
|
|
|
|
|
this.login_email = login_data["email"]
|
|
|
|
|
this.login_password = login_data["password"]
|
|
|
|
|
await this._sendEmailCredentials()
|
|
|
|
|
await this.page.waitForSelector("#login_email_area", { visible: true })
|
|
|
|
|
this.login_email = login_data["email"]
|
|
|
|
|
this.login_password = login_data["password"]
|
|
|
|
|
await this._sendEmailCredentials()
|
|
|
|
|
|
|
|
|
|
await this.page.evaluate(
|
|
|
|
|
element => window.__mautrixController.addEmailAppearObserver(element), loginContentArea)
|
|
|
|
|
await this.page.evaluate(
|
|
|
|
|
element => window.__mautrixController.addEmailAppearObserver(element), loginContentArea)
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
this._sendLoginFailure(`Invalid login type: ${login_type}`)
|
|
|
|
|
return
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
this._sendLoginFailure(`Invalid login type: ${login_type}`)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.page.evaluate(
|
|
|
|
@ -280,12 +305,12 @@ export default class MessagesPuppeteer {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await Promise.race([
|
|
|
|
|
() => this.page.waitForSelector("#mainApp:not(.MdNonDisp)", {timeout: 2000})
|
|
|
|
|
() => this.page.waitForSelector("#mainApp:not(.MdNonDisp)", { timeout: 2000 })
|
|
|
|
|
.then(value => {
|
|
|
|
|
loginSuccess = true
|
|
|
|
|
return value
|
|
|
|
|
}),
|
|
|
|
|
() => this.page.waitForSelector("#login_incorrect", {visible: true, timeout: 2000})
|
|
|
|
|
() => this.page.waitForSelector("#login_incorrect", { visible: true, timeout: 2000 })
|
|
|
|
|
.then(value => this.page.evaluate(element => element?.innerText, value)),
|
|
|
|
|
() => this._waitForLoginCancel(),
|
|
|
|
|
].map(promiseFn => cancelableResolve(promiseFn)))
|
|
|
|
@ -318,9 +343,9 @@ 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%...??
|
|
|
|
|
// TODO Sometimes it gets stuck at 99%...??
|
|
|
|
|
},
|
|
|
|
|
{timeout: 10000}, // Assume 10 seconds is long enough
|
|
|
|
|
{ timeout: 10000 }, // Assume 10 seconds is long enough
|
|
|
|
|
messageSyncElement)
|
|
|
|
|
} catch (err) {
|
|
|
|
|
//this._sendLoginFailure(`Failed to sync: ${err}`)
|
|
|
|
@ -582,7 +607,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
// - the most recently-sent own message is not fully read
|
|
|
|
|
let chatIDToSync
|
|
|
|
|
for (let i = 0, n = chatList.length; i < n; i++) {
|
|
|
|
|
const chatListItem = chatList[(i+offset) % n]
|
|
|
|
|
const chatListItem = chatList[(i + offset) % n]
|
|
|
|
|
|
|
|
|
|
if (chatListItem.notificationCount > 0) {
|
|
|
|
|
// Chat has unread notifications, so don't view it
|
|
|
|
@ -634,15 +659,15 @@ export default class MessagesPuppeteer {
|
|
|
|
|
const initialID = this.jiggleTimerID
|
|
|
|
|
|
|
|
|
|
exec(`xdotool mousemove --sync --window ${this.windowID} 0 0`, {},
|
|
|
|
|
(error, stdout, stderr) => {
|
|
|
|
|
if (error) {
|
|
|
|
|
this.log(`Error while jiggling mouse: ${error}`)
|
|
|
|
|
}
|
|
|
|
|
(error, stdout, stderr) => {
|
|
|
|
|
if (error) {
|
|
|
|
|
this.log(`Error while jiggling mouse: ${error}`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.jiggleTimerID == initialID) {
|
|
|
|
|
this._jiggleTimerStart()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
if (this.jiggleTimerID == initialID) {
|
|
|
|
|
this._jiggleTimerStart()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async startObserving() {
|
|
|
|
@ -693,8 +718,8 @@ export default class MessagesPuppeteer {
|
|
|
|
|
await this._interactWithPage(async () => {
|
|
|
|
|
this.log("Opening settings view")
|
|
|
|
|
await this.page.click("button.mdGHD01SettingBtn")
|
|
|
|
|
await this.page.waitForSelector("#context_menu li#settings", {visible: true}).then(e => e.click())
|
|
|
|
|
await this.page.waitForSelector("#settings_contents", {visible: true})
|
|
|
|
|
await this.page.waitForSelector("#context_menu li#settings", { visible: true }).then(e => e.click())
|
|
|
|
|
await this.page.waitForSelector("#settings_contents", { visible: true })
|
|
|
|
|
|
|
|
|
|
this.log("Getting own profile info")
|
|
|
|
|
ownProfile = {
|
|
|
|
@ -712,7 +737,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
|
|
|
|
|
const backSelector = "#label_setting button"
|
|
|
|
|
await this.page.click(backSelector)
|
|
|
|
|
await this.page.waitForSelector(backSelector, {visible: false})
|
|
|
|
|
await this.page.waitForSelector(backSelector, { visible: false })
|
|
|
|
|
})
|
|
|
|
|
return ownProfile
|
|
|
|
|
}
|
|
|
|
@ -729,6 +754,41 @@ export default class MessagesPuppeteer {
|
|
|
|
|
return `#joined_group_list_body > li[data-chatid="${id}"]`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_contactCountSelector() {
|
|
|
|
|
return `#contact_wrap_friends .MdLFT04Head._contactlist_header span[id=contact_friend_count]`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _getWholeContactResults(page, distance) {
|
|
|
|
|
await page.bringToFront()
|
|
|
|
|
await page.waitForSelector("#contact_wrap_friends > ul.MdCMN03List")
|
|
|
|
|
const contactTotalCount = 0
|
|
|
|
|
this.log(` distance is ${distance}`)
|
|
|
|
|
let incred = 0
|
|
|
|
|
const element = await page.$(this._contactCountSelector())
|
|
|
|
|
const foundContactCount = await element.evaluate(
|
|
|
|
|
element => {
|
|
|
|
|
return Number.parseInt(element?.innerText) || 0
|
|
|
|
|
})
|
|
|
|
|
this.log(` contact_friend_count is ${foundContactCount}`)
|
|
|
|
|
//infiniting contact list scrolling
|
|
|
|
|
while (incred <= distance) {
|
|
|
|
|
if (foundContactCount <= contactTotalCount) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
incred += 100
|
|
|
|
|
this.log(`scrollPosition at ${incred}/${distance}`)
|
|
|
|
|
await page.evaluate(d => {
|
|
|
|
|
const scrollableSection = document.querySelector("#contact_mode_contact_list");
|
|
|
|
|
scrollableSection.scrollTop = 300 + scrollableSection.offsetHeight + d;
|
|
|
|
|
}, incred);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
const $lis = await page.$$("#contact_wrap_friends > ul.MdCMN03List >li")
|
|
|
|
|
//lookup lazy loading count
|
|
|
|
|
const lis = $lis.slice(contactTotalCount, Math.Infinity)
|
|
|
|
|
contactTotalCount = $lis.length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _switchChat(chatID, forceView = false) {
|
|
|
|
|
// TODO Allow passing in an element directly
|
|
|
|
|
this.log(`Switching to chat ${chatID}`)
|
|
|
|
@ -777,7 +837,30 @@ export default class MessagesPuppeteer {
|
|
|
|
|
}
|
|
|
|
|
chatItem = await this.page.$(this._groupItemSelector(chatID))
|
|
|
|
|
} else {
|
|
|
|
|
needRealClick = true
|
|
|
|
|
const unselectedTabButton = await this.page.$(`#leftSide li[data-type=friends_list] > button:not(.ExSelected)`)
|
|
|
|
|
if (unselectedTabButton) {
|
|
|
|
|
switchedTabs = true
|
|
|
|
|
await unselectedTabButton.evaluate(e => e.click())
|
|
|
|
|
await this.page.waitForSelector("#wrap_contact_list > div.MdScroll")
|
|
|
|
|
|
|
|
|
|
let ulstyleheight = await this.page.evaluate(() => {
|
|
|
|
|
const ulList = document.querySelector('#contact_wrap_friends > ul.MdCMN03List')
|
|
|
|
|
return getComputedStyle(ulList).getPropertyValue("height")
|
|
|
|
|
})
|
|
|
|
|
const leg = parseInt(ulstyleheight, 10)
|
|
|
|
|
this.log(`found contact_wrap_friends height is ${leg}px`)
|
|
|
|
|
this.log(`starting to scroll to buttom`)
|
|
|
|
|
await this._getWholeContactResults(this.page, leg)
|
|
|
|
|
|
|
|
|
|
this.log(`finished to scroll to buttom`)
|
|
|
|
|
}
|
|
|
|
|
await this.page.waitForTimeout(300)
|
|
|
|
|
chatItem = await this.page.$(this._friendItemSelector(chatID))
|
|
|
|
|
this.log(`Chat ${chatID} not in recents list, so bot is creating a new chat`)
|
|
|
|
|
await this._interactWithPage(async () => {
|
|
|
|
|
await chatItem.click()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!chatItem) {
|
|
|
|
@ -788,8 +871,8 @@ export default class MessagesPuppeteer {
|
|
|
|
|
// HTML of friend/group item titles ever diverge
|
|
|
|
|
chatName = await chatItem.evaluate(
|
|
|
|
|
chatID.charAt(0) == "u"
|
|
|
|
|
? element => window.__mautrixController.getFriendsListItemName(element)
|
|
|
|
|
: element => window.__mautrixController.getGroupListItemName(element))
|
|
|
|
|
? element => window.__mautrixController.getFriendsListItemName(element)
|
|
|
|
|
: element => window.__mautrixController.getGroupListItemName(element))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this._retryUntilSuccess(3, "Clicking chat item didn't work...try again",
|
|
|
|
@ -805,7 +888,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
this.log(`Waiting for chat header title to be "${chatName}"`)
|
|
|
|
|
await this.page.waitForFunction(
|
|
|
|
|
isCorrectChatVisible,
|
|
|
|
|
{polling: "mutation", timeout: 1000},
|
|
|
|
|
{ polling: "mutation", timeout: 1000 },
|
|
|
|
|
chatName)
|
|
|
|
|
})
|
|
|
|
|
if (switchedTabs) {
|
|
|
|
@ -825,13 +908,13 @@ export default class MessagesPuppeteer {
|
|
|
|
|
this.log("Clicking chat header to show detail area")
|
|
|
|
|
await this.page.click("#_chat_header_area > .mdRGT04Link")
|
|
|
|
|
this.log("Waiting for detail area")
|
|
|
|
|
await this.page.waitForSelector("#_chat_detail_area > .mdRGT02Info", {timeout: 1000})
|
|
|
|
|
await this.page.waitForSelector("#_chat_detail_area > .mdRGT02Info", { timeout: 1000 })
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.log("Waiting for any item to appear in chat")
|
|
|
|
|
try {
|
|
|
|
|
await this.page.waitForSelector("#_chat_room_msg_list div", {timeout: 2000})
|
|
|
|
|
await this.page.waitForSelector("#_chat_room_msg_list div", { timeout: 2000 })
|
|
|
|
|
|
|
|
|
|
this.log("Waiting for chat to stabilize")
|
|
|
|
|
await this.page.evaluate(() => window.__mautrixController.waitForMessageListStability())
|
|
|
|
@ -852,17 +935,17 @@ export default class MessagesPuppeteer {
|
|
|
|
|
|
|
|
|
|
async _getChatInfoUnsafe(chatID, forceView) {
|
|
|
|
|
// TODO Commonize this
|
|
|
|
|
let [isDirect, isGroup, isRoom] = [false,false,false]
|
|
|
|
|
let [isDirect, isGroup, isRoom] = [false, false, false]
|
|
|
|
|
switch (chatID.charAt(0)) {
|
|
|
|
|
case "u":
|
|
|
|
|
isDirect = true
|
|
|
|
|
break
|
|
|
|
|
case "c":
|
|
|
|
|
isGroup = true
|
|
|
|
|
break
|
|
|
|
|
case "r":
|
|
|
|
|
isRoom = true
|
|
|
|
|
break
|
|
|
|
|
case "u":
|
|
|
|
|
isDirect = true
|
|
|
|
|
break
|
|
|
|
|
case "c":
|
|
|
|
|
isGroup = true
|
|
|
|
|
break
|
|
|
|
|
case "r":
|
|
|
|
|
isRoom = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const chatListItem = await this.page.$(this._chatItemSelector(chatID))
|
|
|
|
@ -924,7 +1007,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
for (const participant of participants) {
|
|
|
|
|
this.log(JSON.stringify(participant))
|
|
|
|
|
}
|
|
|
|
|
return {participants, ...chatListInfo}
|
|
|
|
|
return { participants, ...chatListInfo }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO Catch "An error has occurred" dialog
|
|
|
|
@ -951,7 +1034,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
await input.press("Enter")
|
|
|
|
|
await this.page.waitForFunction(
|
|
|
|
|
e => e.innerText == "",
|
|
|
|
|
{timeout: 500},
|
|
|
|
|
{ timeout: 500 },
|
|
|
|
|
input)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
@ -1043,14 +1126,14 @@ export default class MessagesPuppeteer {
|
|
|
|
|
// Sync receipts seen from newly-synced messages
|
|
|
|
|
// TODO When user leaves, clear the read-by count for the old number of other participants
|
|
|
|
|
let minCountToFind = 1
|
|
|
|
|
for (let i = messages.length-1; i >= 0; i--) {
|
|
|
|
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
|
|
|
const message = messages[i]
|
|
|
|
|
if (!message.is_outgoing) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
const count = message.receipt_count
|
|
|
|
|
if (count >= minCountToFind && message.id > (receiptMap.get(count) || 0)) {
|
|
|
|
|
minCountToFind = count+1
|
|
|
|
|
minCountToFind = count + 1
|
|
|
|
|
receiptMap.set(count, message.id)
|
|
|
|
|
}
|
|
|
|
|
// TODO Early exit when count == num other participants
|
|
|
|
@ -1150,12 +1233,12 @@ export default class MessagesPuppeteer {
|
|
|
|
|
// If >1, a notification was missed. Only way to get them is to view the chat.
|
|
|
|
|
// If == 0, might be own message...or just a shuffled chat, or something else.
|
|
|
|
|
// To play it safe, just sync them. Should be no harm, as they're viewed already.
|
|
|
|
|
diffNumNotifications != 1
|
|
|
|
|
diffNumNotifications != 1
|
|
|
|
|
// Without placeholders, some messages require visiting their chat to be synced.
|
|
|
|
|
|| !this.sendPlaceholders
|
|
|
|
|
&& (
|
|
|
|
|
// Can only use previews for DMs, because sender can't be found otherwise!
|
|
|
|
|
chatListInfo.id.charAt(0) != 'u'
|
|
|
|
|
chatListInfo.id.charAt(0) != 'u'
|
|
|
|
|
// Sync when lastMsg is a canned message for a non-previewable message type.
|
|
|
|
|
|| chatListInfo.lastMsg.endsWith(" sent a photo.")
|
|
|
|
|
|| chatListInfo.lastMsg.endsWith(" sent a sticker.")
|
|
|
|
@ -1182,7 +1265,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _syncChat(chatID) {
|
|
|
|
|
const {messages, receipts} = await this._getMessagesUnsafe(chatID)
|
|
|
|
|
const { messages, receipts } = await this._getMessagesUnsafe(chatID)
|
|
|
|
|
|
|
|
|
|
if (messages.length == 0) {
|
|
|
|
|
this.log("No new messages found in", chatID)
|
|
|
|
@ -1219,7 +1302,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
|
|
|
|
|
this.log(`Received read receipt ${receipt_id} (since ${prevReceiptID}) for chat ${chat_id}`)
|
|
|
|
|
if (this.client) {
|
|
|
|
|
this.client.sendReceipt({chat_id: chat_id, id: receipt_id})
|
|
|
|
|
this.client.sendReceipt({ chat_id: chat_id, id: receipt_id })
|
|
|
|
|
.catch(err => this.error("Error handling read receipt:", err))
|
|
|
|
|
} else {
|
|
|
|
|
this.log("No client connected, not sending receipts")
|
|
|
|
@ -1252,7 +1335,7 @@ export default class MessagesPuppeteer {
|
|
|
|
|
receipt.chat_id = chat_id
|
|
|
|
|
try {
|
|
|
|
|
await this.client.sendReceipt(receipt)
|
|
|
|
|
} catch(err) {
|
|
|
|
|
} catch (err) {
|
|
|
|
|
this.error("Error handling read receipt:", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|