Skip to content

Instantly share code, notes, and snippets.

@kmorrill
Created September 16, 2025 23:49
Show Gist options
  • Select an option

  • Save kmorrill/0b9c8472e1f88eacbfa53169c000c012 to your computer and use it in GitHub Desktop.

Select an option

Save kmorrill/0b9c8472e1f88eacbfa53169c000c012 to your computer and use it in GitHub Desktop.
(function(){
if (window.__sbRemoteInitialized) {
if (typeof window.__sbRemoteStart === 'function') window.__sbRemoteStart();
return;
}
window.__sbRemoteInitialized = true;
const log = (...args) => console.log('[SB][remote]', ...args);
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function waitFor(selector, attempts, delay) {
attempts = attempts || 40;
delay = delay || 250;
for (let i = 0; i < attempts; i += 1) {
const node = document.querySelector(selector);
if (node) {
log('Found', selector);
return node;
}
await sleep(delay);
}
log('Failed to find', selector);
return null;
}
function findReviewsLink() {
const link = Array.from(document.querySelectorAll('a')).find(anchor => {
const text = (anchor.textContent || '').trim().toLowerCase();
return text.includes('google reviews');
});
log('Anchor for Google reviews', link);
return link;
}
async function ensureDialog() {
const link = findReviewsLink();
if (link) {
try { link.scrollIntoView({ block: 'center' }); log('Scrolling link into view'); } catch (err) { log('scrollIntoView failed', err); }
log('Clicking reviews link');
link.click();
await sleep(2000);
}
const dialog = await waitFor('div[role="dialog"]', 40, 300);
log('Dialog detected?', !!dialog);
return dialog || document;
}
function clickWithEvents(node) {
if (!node) return false;
log('Attempting clickWithEvents on', node);
try { node.focus({ preventScroll: true }); } catch (err) { log('focus failed', err); }
const events = [
new PointerEvent('pointerdown', { bubbles: true, pointerType: 'mouse', button: 0 }),
new MouseEvent('mousedown', { bubbles: true, button: 0 }),
new PointerEvent('pointerup', { bubbles: true, pointerType: 'mouse', button: 0 }),
new MouseEvent('mouseup', { bubbles: true, button: 0 }),
];
events.forEach(evt => { try { node.dispatchEvent(evt); } catch (err) { log('dispatch failed', evt.type, err); } });
try {
node.click();
log('click() invoked');
} catch (err) {
log('click() failed', err);
return false;
}
return true;
}
function findCandidates(selector) {
const nodes = Array.from(document.querySelectorAll(selector));
log('Found', nodes.length, 'candidates for', selector);
return nodes;
}
async function selectNewestFromGroup() {
log('Selecting newest from radiogroup');
const group = await waitFor('div[role="radiogroup"][aria-label="Sort reviews"]', 40, 300);
if (!group) return false;
let candidates = group.querySelectorAll('[data-sort="2"]');
if (!candidates.length) candidates = group.querySelectorAll('[role="radio"]');
candidates = Array.from(candidates);
log('Radiogroup candidates', candidates.length);
for (const node of candidates) {
const label = ((node.textContent || '') + ' ' + (node.getAttribute('aria-label') || '')).toLowerCase();
const matches = label.includes('newest') || node.getAttribute('data-sort') === '2';
log('Checking candidate', node, 'matches?', matches, 'aria-checked', node.getAttribute('aria-checked'));
if (!matches) continue;
if (node.getAttribute('aria-checked') === 'true') {
log('Already newest');
return true;
}
if (clickWithEvents(node)) {
await sleep(500);
const selected = node.getAttribute('aria-checked') === 'true';
log('After click aria-checked', node.getAttribute('aria-checked'));
if (selected) return true;
}
}
log('Radiogroup selection failed');
return false;
}
async function selectNewestFromTabs() {
log('Selecting newest from tabs');
let candidates = document.querySelectorAll('[data-sort="2"]');
if (!candidates.length) candidates = document.querySelectorAll('[role="tab"]');
candidates = Array.from(candidates);
log('Tab candidates', candidates.length);
for (const tab of candidates) {
const label = ((tab.textContent || '') + ' ' + (tab.getAttribute('aria-label') || '')).toLowerCase();
const matches = label.includes('newest') || tab.getAttribute('data-sort') === '2';
log('Tab candidate', tab, 'matches?', matches, 'aria-selected', tab.getAttribute('aria-selected'));
if (!matches) continue;
if (tab.getAttribute('aria-selected') === 'true') {
log('Tab already selected');
return true;
}
if (clickWithEvents(tab)) {
await sleep(500);
const selected = tab.getAttribute('aria-selected') === 'true';
log('After click aria-selected', tab.getAttribute('aria-selected'));
if (selected) return true;
}
}
log('Tab selection failed');
return false;
}
async function ensureNewest() {
log('Ensuring newest sort');
let selected = await selectNewestFromGroup();
if (!selected) selected = await selectNewestFromTabs();
log('Newest selected?', selected);
return selected;
}
async function findScrollContainer() {
let container = await waitFor('div.RVCQse', 60, 300);
if (!container) container = await waitFor('[jsname="fk8dgd"]', 20, 300);
if (!container) container = await waitFor('[role="main"]', 20, 300);
log('Scroll container found?', !!container);
return container;
}
async function scrollContainer(container) {
for (let i = 0; i < 40; i += 1) {
if (!document.body.contains(container)) {
log('Container removed, stopping scroll');
return;
}
const step = Math.max(400, container.clientHeight || 800);
container.scrollBy(0, step);
log('Scrolled step', i + 1, 'scrollTop now', container.scrollTop);
await sleep(800);
}
}
async function run() {
try {
await ensureDialog();
await ensureNewest();
const container = await findScrollContainer();
if (!container) return;
container.scrollTop = 0;
await scrollContainer(container);
} catch (error) {
console.error('[SB][remote] error', error);
}
}
async function start() {
if (window.__sbRemoteRunning) return false;
window.__sbRemoteRunning = true;
try {
await run();
} finally {
window.__sbRemoteRunning = false;
}
return true;
}
window.__sbRemoteStart = start;
window.__sbRemoteLoaded = true;
start();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment