From e1b822bd52151a8a623f54550ea7928a10ba4278 Mon Sep 17 00:00:00 2001 From: Andrew Ferrazzutti Date: Thu, 1 Jul 2021 01:49:43 -0400 Subject: [PATCH] Jiggle mouse with xdotool to keep LINE alive Even if Chrome is the only window in an X session, and even if the window is focused, the LINE extension stops getting new messages unless the mouse has moved recently. To work around that, use xdotool to move the mouse every so often. --- SETUP.md | 1 + puppet/README.md | 8 ++++++ puppet/example-config.json | 3 +++ puppet/src/main.js | 3 +++ puppet/src/puppet.js | 55 +++++++++++++++++++++++++++++++------- 5 files changed, 61 insertions(+), 9 deletions(-) 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() {