diff --git a/SETUP.md b/SETUP.md index 584d73f..590252b 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,6 +1,7 @@ # Minimum Requirements * Python 3.8 * Node 10.18.1 +* xdotool # Initial setup ## Puppeteer module diff --git a/puppet/README.md b/puppet/README.md index d10d193..0fbf271 100644 --- a/puppet/README.md +++ b/puppet/README.md @@ -12,5 +12,13 @@ The `profile_dir` specifies which directory to put Chromium user data directorie ### Extension directory The `extension_dir` specifies which directory contains the files for the LINE extension, which you must download yourself. +### Cycle delay +`cycle_delay` specifies the period (in milliseconds) at which Puppeteer should view chats to check on their read receipts. Only chats with messages that haven't been fully read need to be checked. + +### `xdotool` +Set `use_xdotool` to `true` to allow the Node process to manipulate the mouse cursor of the X server it runs in. Requires the `xdotool` utility to be installed. Highly recommended, especially when running in a virtual X server. Its default value is `false` so that running in a non-virtual X server won't interfere with a real mouse cursor. + +`jiggle_delay` specifies the period (in milliseconds) between "jiggling" the mouse cursor (necessary to keep the LINE extension active). Only relevant when `use_xdotool` is `true`. + ### DevTools Set `devtools` to `true` to launch Chromium with DevTools enabled by default. diff --git a/puppet/example-config.json b/puppet/example-config.json index a0520b1..b28c152 100644 --- a/puppet/example-config.json +++ b/puppet/example-config.json @@ -6,5 +6,8 @@ "profile_dir": "./profiles", "url": "chrome-extension:///index.html", "extension_dir": "./extension_files", + "cycle_delay": 5000, + "use_xdotool": false, + "jiggle_delay": 20000, "devtools": false } diff --git a/puppet/src/main.js b/puppet/src/main.js index 81f8b9d..90598ed 100644 --- a/puppet/src/main.js +++ b/puppet/src/main.js @@ -39,6 +39,9 @@ MessagesPuppeteer.profileDir = config.profile_dir || MessagesPuppeteer.profileDi MessagesPuppeteer.devtools = config.devtools || false MessagesPuppeteer.url = config.url MessagesPuppeteer.extensionDir = config.extension_dir || MessagesPuppeteer.extensionDir +MessagesPuppeteer.cycleDelay = config.cycle_delay || MessagesPuppeteer.cycleDelay +MessagesPuppeteer.useXdotool = config.use_xdotool || MessagesPuppeteer.useXdotool +MessagesPuppeteer.jiggleDelay = config.jiggle_delay || MessagesPuppeteer.jiggleDelay const api = new PuppetAPI(config.listen) diff --git a/puppet/src/puppet.js b/puppet/src/puppet.js index e031b7a..71bca9d 100644 --- a/puppet/src/puppet.js +++ b/puppet/src/puppet.js @@ -15,6 +15,7 @@ // along with this program. If not, see . import process from "process" import path from "path" +import { exec, execSync } from "child_process" import puppeteer from "puppeteer" import chrono from "chrono-node" @@ -25,6 +26,9 @@ import { sleep } from "./util.js" export default class MessagesPuppeteer { static profileDir = "./profiles" static executablePath = undefined + static cycleDelay = 5000 + static useXdotool = false + static jiggleDelay = 30000 static devtools = false static noSandbox = false static viewport = { width: 960, height: 840 } @@ -43,6 +47,7 @@ export default class MessagesPuppeteer { } this.id = id this.ownID = ownID + this.windowID = null this.sendPlaceholders = sendPlaceholders this.profilePath = profilePath this.updatedChats = new Set() @@ -51,6 +56,7 @@ export default class MessagesPuppeteer { this.mostRecentReceipts = new Map() this.numChatNotifications = new Map() this.cycleTimerID = null + this.jiggleTimerID = null this.taskQueue = new TaskQueue(this.id) this.client = client } @@ -99,7 +105,14 @@ export default class MessagesPuppeteer { this.blankPage = await this.browser.newPage() await this.page.bringToFront() - this.log("Opening", MessagesPuppeteer.url) + if (MessagesPuppeteer.useXdotool) { + this.log("Finding window ID") + const buffer = execSync("xdotool search 'about:blank'") + this.windowID = Number.parseInt(buffer) + this.log(`Found window ID ${this.windowID}`) + } + + this.log(`Opening ${MessagesPuppeteer.url}`) await this.page.setBypassCSP(true) // Needed to load content scripts await this._preparePage(true) @@ -472,10 +485,9 @@ export default class MessagesPuppeteer { } _cycleTimerStart() { - // TODO Config for cycle delay this.cycleTimerID = setTimeout( () => this.taskQueue.push(() => this._cycleChatUnsafe()), - 5000) + MessagesPuppeteer.cycleDelay) } async _cycleChatUnsafe() { @@ -515,21 +527,39 @@ export default class MessagesPuppeteer { } chatIDToSync = chatListItem.id + this.log(`Viewing chat ${chatIDToSync} to check for new read receipts`) + await this._syncChat(chatIDToSync) break } if (!chatIDToSync) { - // TODO Confirm if this actually works...! - this.log(`Found no chats in need of read receipt updates, so force-viewing ${currentChatID} just to keep LINE alive`) - await this._switchChat(currentChatID, true) - } else { - this.log(`Viewing chat ${chatIDToSync} to check for new read receipts`) - await this._syncChat(chatIDToSync) + this.log("Found no chats in need of read receipt updates") } this._cycleTimerStart() } + /** + * Jiggle the mouse periodically. + * Have to do this to keep the LINE extension "awake". Ridiculous, but necessary... + */ + _jiggleTimerStart() { + this.jiggleTimerID = setTimeout(() => this._jiggleMouse(), MessagesPuppeteer.jiggleDelay) + } + + _jiggleMouse() { + this.log("Jiggling mouse") + exec(`xdotool mousemove --sync --window ${this.windowID} 0 0`, {}, + (error, stdout, stderr) => { + if (error) { + this.log(`Error while jiggling mouse: ${error}`) + } else { + this.log("Jiggled mouse") + } + this._jiggleTimerStart() + }) + } + async startObserving() { // TODO Highly consider syncing anything that was missed since stopObserving... const chatID = await this.page.evaluate(() => window.__mautrixController.getCurrentChatID()) @@ -545,6 +575,9 @@ export default class MessagesPuppeteer { if (this.cycleTimerID == null) { this._cycleTimerStart() } + if (MessagesPuppeteer.useXdotool && this.jiggleTimerID == null) { + this._jiggleTimerStart() + } } async stopObserving() { @@ -558,6 +591,10 @@ export default class MessagesPuppeteer { clearTimeout(this.cycleTimerID) this.cycleTimerID = null } + if (this.jiggleTimerID != null) { + clearTimeout(this.jiggleTimerID) + this.jiggleTimerID = null + } } async getOwnProfile() {