|
|
|
@ -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
|
|
|
|
@ -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) {
|
|
|
|
@ -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) {
|
|
|
|
|