Created
December 4, 2025 19:58
-
-
Save TheGreatRambler/107fb0b991c6222c56bd3417808e8c96 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name OpenTripPlanner Randomize Routes | |
| // @namespace http://tampermonkey.net/ | |
| // @version 0.1 | |
| // @description Periodically query a random route in the center of the OpenTripPlanner map | |
| // @author TheGreatRambler | |
| // @match http://localhost/* | |
| // @match http://127.0.0.1/* | |
| // @match http://0.0.0.0/* | |
| // @grant none | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| // === CONFIGURABLE SETTINGS === | |
| // How often to run the sequence (ms) | |
| const CYCLE_INTERVAL_MS = 3000; | |
| // Bounding box (in viewport coordinates, relative to top-left of the visible window) | |
| // Adjust these for your target area | |
| const BOX_MARGIN_LEFT = 660; | |
| const BOX_MARGIN_RIGHT = 167; | |
| const BOX_MARGIN_TOP = 201; | |
| const BOX_MARGIN_BOTTOM = 27; | |
| // Click offsets | |
| const FIRST_LEFT_CLICK_OFFSET_Y = -90; // 20px up | |
| const SECOND_LEFT_CLICK_OFFSET_Y = -50; // 10px up | |
| // Delays between actions (ms) | |
| const DELAY_AFTER_FIRST_RIGHT = 300; | |
| const DELAY_AFTER_FIRST_LEFT = 300; | |
| const DELAY_AFTER_SECOND_RIGHT = 300; | |
| const DELAY_AFTER_SECOND_LEFT = 0; // end of sequence, can be 0 | |
| // === INTERNAL STATE === | |
| let isRunning = false; | |
| let intervalId = null; | |
| // === UTILS === | |
| function sleep(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| // Random point inside the bounding box | |
| function getRandomPointInBox() { | |
| const left = BOX_MARGIN_LEFT; | |
| const right = window.innerWidth - BOX_MARGIN_RIGHT; | |
| const top = BOX_MARGIN_TOP; | |
| const bottom = window.innerHeight - BOX_MARGIN_BOTTOM; | |
| // Guard against weird cases | |
| if (right <= left || bottom <= top) { | |
| // fallback to center-ish | |
| return { | |
| x: Math.round(window.innerWidth / 2), | |
| y: Math.round(window.innerHeight / 2), | |
| }; | |
| } | |
| const x = Math.round(left + Math.random() * (right - left)); | |
| const y = Math.round(top + Math.random() * (bottom - top)); | |
| return { x, y }; | |
| } | |
| function dispatchMouse(element, type, button, x, y) { | |
| if (!element) return; | |
| const evt = new MouseEvent(type, { | |
| bubbles: true, | |
| cancelable: true, | |
| view: window, | |
| button: button, // 0 = left, 2 = right | |
| buttons: 1 << button, // bitmask | |
| clientX: x, | |
| clientY: y, | |
| }); | |
| element.dispatchEvent(evt); | |
| } | |
| function rightClickAt(x, y) { | |
| const el = document.elementFromPoint(x, y) || document.body; | |
| // Simulate a right click: mousedown, mouseup, contextmenu | |
| dispatchMouse(el, 'mousedown', 2, x, y); | |
| dispatchMouse(el, 'mouseup', 2, x, y); | |
| dispatchMouse(el, 'contextmenu', 2, x, y); | |
| } | |
| function leftClickAt(x, y) { | |
| const el = document.elementFromPoint(x, y) || document.body; | |
| // Simulate a left click: mousedown, mouseup, click | |
| dispatchMouse(el, 'mousedown', 0, x, y); | |
| dispatchMouse(el, 'mouseup', 0, x, y); | |
| dispatchMouse(el, 'click', 0, x, y); | |
| } | |
| // === MAIN SEQUENCE === | |
| async function runCycle() { | |
| if (!isRunning) return; | |
| // 1) Right click at random point in bounding box | |
| const p1 = getRandomPointInBox(); | |
| rightClickAt(p1.x, p1.y); | |
| await sleep(DELAY_AFTER_FIRST_RIGHT); | |
| // 2) Left click ~20px up from that point | |
| let y1Up = p1.y + FIRST_LEFT_CLICK_OFFSET_Y; | |
| if (y1Up < 0) y1Up = 0; | |
| leftClickAt(p1.x, y1Up); | |
| await sleep(DELAY_AFTER_FIRST_LEFT); | |
| // 3) Right click at another random point | |
| const p2 = getRandomPointInBox(); | |
| rightClickAt(p2.x, p2.y); | |
| await sleep(DELAY_AFTER_SECOND_RIGHT); | |
| // 4) Left click ~10px up from second point | |
| let y2Up = p2.y + SECOND_LEFT_CLICK_OFFSET_Y; | |
| if (y2Up < 0) y2Up = 0; | |
| leftClickAt(p2.x, y2Up); | |
| await sleep(DELAY_AFTER_SECOND_LEFT); | |
| } | |
| function startAutoClicker() { | |
| if (isRunning) return; | |
| isRunning = true; | |
| updateToggleButton(); | |
| intervalId = setInterval(runCycle, CYCLE_INTERVAL_MS); | |
| } | |
| function stopAutoClicker() { | |
| isRunning = false; | |
| updateToggleButton(); | |
| if (intervalId !== null) { | |
| clearInterval(intervalId); | |
| intervalId = null; | |
| } | |
| } | |
| // === TOGGLE UI === | |
| let toggleBtn = null; | |
| function createToggleButton() { | |
| toggleBtn = document.createElement('button'); | |
| toggleBtn.id = 'bbox-auto-clicker-toggle'; | |
| toggleBtn.textContent = 'AutoClick: OFF'; | |
| toggleBtn.style.position = 'fixed'; | |
| toggleBtn.style.top = '10px'; | |
| toggleBtn.style.right = '10px'; | |
| toggleBtn.style.zIndex = '999999'; | |
| toggleBtn.style.padding = '6px 10px'; | |
| toggleBtn.style.fontSize = '12px'; | |
| toggleBtn.style.background = 'rgba(0,0,0,0.7)'; | |
| toggleBtn.style.color = '#fff'; | |
| toggleBtn.style.border = '1px solid #aaa'; | |
| toggleBtn.style.borderRadius = '4px'; | |
| toggleBtn.style.cursor = 'pointer'; | |
| toggleBtn.addEventListener('click', () => { | |
| if (isRunning) { | |
| stopAutoClicker(); | |
| } else { | |
| startAutoClicker(); | |
| } | |
| }); | |
| document.body.appendChild(toggleBtn); | |
| } | |
| function updateToggleButton() { | |
| if (!toggleBtn) return; | |
| if (isRunning) { | |
| toggleBtn.textContent = 'AutoClick: ON'; | |
| toggleBtn.style.background = 'rgba(0,128,0,0.8)'; | |
| } else { | |
| toggleBtn.textContent = 'AutoClick: OFF'; | |
| toggleBtn.style.background = 'rgba(128,0,0,0.8)'; | |
| } | |
| } | |
| // === INIT === | |
| function init() { | |
| createToggleButton(); | |
| // default is OFF; user can click to enable | |
| } | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', init); | |
| } else { | |
| init(); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment