Instantly share code, notes, and snippets.
Last active
August 25, 2025 02:08
-
Star
2
(2)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save guider23/e9ed427bf8cc116f2407ff49117f2556 to your computer and use it in GitHub Desktop.
Automate Reddit Sharing Button
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
| (function () { | |
| async function redditShareClickerDeep_resilient() { | |
| if (window.redditShareClickerDeepRunning) { | |
| console.log("redditShareClickerDeep already running. Stop with: window.redditShareClickerStop = true"); | |
| return; | |
| } | |
| window.redditShareClickerDeepRunning = true; | |
| window.redditShareClickerStop = false; | |
| const sleep = ms => new Promise(res => setTimeout(res, ms)); | |
| const rand = (a,b) => a + Math.random()*(b-a); | |
| function querySelectorAllDeep(selector, root = document) { | |
| const results = new Set(); | |
| const nodes = [root]; | |
| while (nodes.length) { | |
| const node = nodes.shift(); | |
| try { | |
| const found = node.querySelectorAll ? node.querySelectorAll(selector) : []; | |
| for (const f of found) results.add(f); | |
| } catch (e) { } | |
| let childElems = []; | |
| try { childElems = node.querySelectorAll ? node.querySelectorAll('*') : []; } catch(e){ childElems = []; } | |
| for (const ch of childElems) { | |
| if (ch.shadowRoot) nodes.push(ch.shadowRoot); | |
| } | |
| } | |
| return Array.from(results); | |
| } | |
| function isSinglePostView() { | |
| try { | |
| const p = location.pathname || ''; | |
| if (p.includes('/comments/')) return true; | |
| if (document.querySelector('[data-test-id="post-content"], .Comment, #comments')) return true; | |
| } catch (e) {} | |
| return false; | |
| } | |
| function getPostKey(post) { | |
| try { | |
| const custom = (querySelectorAllDeep('shreddit-post-share-button', post) || [])[0]; | |
| if (custom) { | |
| const sid = custom.getAttribute && custom.getAttribute('source-id'); | |
| if (sid) return sid; | |
| } | |
| const cA = post.querySelector && post.querySelector('a[data-click-id="comments"], a[href*="/comments/"]'); | |
| if (cA) { | |
| const href = cA.href || cA.getAttribute('href') || ''; | |
| const m = href.match(/\/comments\/([^\/?#]+)/); | |
| if (m && m[1]) return 'comments:' + m[1]; | |
| return 'commentsHref:' + href; | |
| } | |
| const vid = post.getAttribute && (post.getAttribute('id') || post.dataset?.postId || post.dataset?.id); | |
| if (vid) return 'id:' + vid; | |
| const html = (post.outerHTML || '').slice(0,400); | |
| let h = 0; | |
| for (let i=0;i<html.length;i++) h = ((h<<5)-h) + html.charCodeAt(i); | |
| return 'hash:' + (h >>> 0); | |
| } catch (e) { return 'unknown:' + Math.random(); } | |
| } | |
| function findCopyMenuItemsDeep() { | |
| const candidates = querySelectorAllDeep('button, a, [role="menuitem"], [role="button"], li, span, div'); | |
| const re = /(copy link|copy permalink|copy post link|copy post permalink|copy url|copy link to post|copy)/i; | |
| return candidates.filter(n => { | |
| try { | |
| const txt = (n.innerText || n.textContent || '').trim(); | |
| if (re.test(txt)) return true; | |
| const aria = (n.getAttribute && (n.getAttribute('aria-label') || n.getAttribute('title') || '')) || ''; | |
| if (re.test(aria)) return true; | |
| } catch (e) {} | |
| return false; | |
| }); | |
| } | |
| function isVisible(el) { | |
| if (!el || !el.getBoundingClientRect) return false; | |
| const r = el.getBoundingClientRect(); | |
| if (r.width === 0 || r.height === 0) return false; | |
| if (r.bottom < 0 || r.top > (window.innerHeight || document.documentElement.clientHeight)) return false; | |
| const style = window.getComputedStyle(el); | |
| if (style && (style.visibility === 'hidden' || style.display === 'none' || style.opacity === '0')) return false; | |
| return true; | |
| } | |
| function distanceBetweenRects(a, b) { | |
| const ax = a.left + a.width/2, ay = a.top + a.height/2; | |
| const bx = b.left + b.width/2, by = b.top + b.height/2; | |
| return Math.hypot(ax - bx, ay - by); | |
| } | |
| function robustClickWithShadowFallback(el) { | |
| if (!el) return false; | |
| try { | |
| el.click(); | |
| return true; | |
| } catch (e) {} | |
| try { | |
| const evOpts = { bubbles: true, cancelable: true, view: window }; | |
| el.dispatchEvent(new MouseEvent('mousedown', evOpts)); | |
| el.dispatchEvent(new MouseEvent('mouseup', evOpts)); | |
| el.dispatchEvent(new MouseEvent('click', evOpts)); | |
| return true; | |
| } catch (e) { | |
| try { | |
| if (el.shadowRoot) { | |
| const btn = el.shadowRoot.querySelector('button, [role="button"], .icon-button'); | |
| if (btn) { | |
| btn.click(); | |
| return true; | |
| } | |
| } | |
| } catch (err) {} | |
| console.warn('robustClickWithShadowFallback failed:', e); | |
| } | |
| return false; | |
| } | |
| async function attemptReturnToListing(listingUrl) { | |
| try { | |
| history.back(); | |
| await sleep(900); | |
| if (!isSinglePostView()) return true; | |
| } catch (e) {} | |
| try { | |
| if (listingUrl && listingUrl !== location.href) { | |
| location.href = listingUrl; | |
| await sleep(1200); | |
| if (!isSinglePostView()) return true; | |
| } | |
| } catch (e) {} | |
| try { | |
| location.href = 'https://www.reddit.com/'; | |
| await sleep(1200); | |
| if (!isSinglePostView()) return true; | |
| } catch (e) {} | |
| return !isSinglePostView(); | |
| } | |
| console.log("redditShareClickerDeep (resilient) started — Stop with: window.redditShareClickerStop = true"); | |
| const processedKeys = new Set(); | |
| let touchedCount = 0; | |
| let iteration = 0; | |
| let savedListingUrl = location.href; | |
| try { | |
| while (!window.redditShareClickerStop) { | |
| iteration++; | |
| if (isSinglePostView()) { | |
| console.log('Detected single-post view. Trying to return to listing.'); | |
| const ok = await attemptReturnToListing(savedListingUrl); | |
| if (!ok) { | |
| console.warn('Could not return to listing automatically. Stopping script to avoid lock-in.'); | |
| break; | |
| } else { | |
| console.log('Returned to listing.'); | |
| } | |
| } | |
| if (!isSinglePostView()) savedListingUrl = location.href; | |
| let postCandidates = querySelectorAllDeep('article, [data-testid="post-container"], .Post'); | |
| postCandidates = postCandidates.filter(p => p && isVisible(p)); | |
| if (postCandidates.length === 0) { | |
| const hosts = querySelectorAllDeep('shreddit-post-share-button'); | |
| for (const host of hosts) { | |
| let ancestor = host; | |
| while (ancestor && ancestor.nodeType === 1 && !ancestor.matches?.('article, [data-testid="post-container"], .Post')) { | |
| ancestor = ancestor.parentElement || ancestor.getRootNode()?.host; | |
| } | |
| if (ancestor && !postCandidates.includes(ancestor)) postCandidates.push(ancestor); | |
| } | |
| } | |
| if (postCandidates.length === 0) { | |
| window.scrollBy({ top: window.innerHeight * 0.75, behavior: 'smooth' }); | |
| await sleep(rand(800, 1500)); | |
| continue; | |
| } | |
| let progress = false; | |
| for (const post of postCandidates) { | |
| if (window.redditShareClickerStop) break; | |
| if (!post) continue; | |
| const key = getPostKey(post); | |
| if (processedKeys.has(key)) continue; | |
| processedKeys.add(key); | |
| progress = true; | |
| if (isSinglePostView()) { | |
| const ok = await attemptReturnToListing(savedListingUrl); | |
| if (!ok) break; | |
| } | |
| try { if (post.scrollIntoView) post.scrollIntoView({ behavior: 'smooth', block: 'center' }); } catch(e){} | |
| await sleep(rand(450, 900)); | |
| const shareEl = (function findShareButtonDeepWithin(postRoot) { | |
| try { | |
| const custom = querySelectorAllDeep('shreddit-post-share-button', postRoot).find(x => postRoot.contains(x) || (x.getRootNode && x.getRootNode() === postRoot)); | |
| if (custom) return custom; | |
| } catch(e){} | |
| const selectors = [ | |
| 'button[aria-label="Share"]', | |
| 'button[aria-label="share"]', | |
| 'a[data-click-id="share"]', | |
| 'button[title="Share"]', | |
| 'button[aria-label*="share"]', | |
| 'div[role="button"][data-click-id="share"]' | |
| ]; | |
| for (const sel of selectors) { | |
| const found = querySelectorAllDeep(sel, postRoot); | |
| if (found && found.length) { | |
| for (const f of found) if (isVisible(f)) return f; | |
| return found[0]; | |
| } | |
| } | |
| const textCandidates = querySelectorAllDeep('button, a, div[role="button"], span', postRoot) | |
| .filter(el => { | |
| try { | |
| const t = (el.innerText || el.textContent || '').trim().toLowerCase(); | |
| return t && t.includes('share'); | |
| } catch(e){ return false; } | |
| }); | |
| if (textCandidates.length) return textCandidates[0]; | |
| return null; | |
| })(post); | |
| if (!shareEl) { | |
| console.log('No share button (deep) found for a post — skipping.'); | |
| await sleep(rand(200, 500)); | |
| continue; | |
| } | |
| const beforeHref = location.href; | |
| robustClickWithShadowFallback(shareEl); | |
| await sleep(rand(350, 900)); | |
| if (location.href !== beforeHref || isSinglePostView()) { | |
| console.warn('Click caused navigation or we entered a post — attempting to recover.'); | |
| const ok = await attemptReturnToListing(savedListingUrl || beforeHref); | |
| if (!ok) { | |
| console.error('Unable to recover from accidental post navigation. Stopping to avoid lock-in.'); | |
| window.redditShareClickerStop = true; | |
| break; | |
| } else { | |
| console.log('Recovered to listing page — marking this post as processed and continuing.'); | |
| touchedCount++; | |
| await sleep(rand(700, 1200)); | |
| continue; | |
| } | |
| } | |
| const candidates = findCopyMenuItemsDeep(); | |
| let chosen = null; | |
| if (candidates.length) { | |
| let shareRect = { left: window.innerWidth/2, top: window.innerHeight/2, width: 0, height: 0 }; | |
| try { shareRect = shareEl.getBoundingClientRect(); } catch(e){} | |
| let scored = candidates.map(c => { | |
| try { return { el: c, rect: c.getBoundingClientRect(), txt: (c.innerText||c.textContent||'').trim() }; } catch(e){ return {el:c,rect:shareRect,txt:''}; } | |
| }).map(o => ({ ...o, dist: distanceBetweenRects(shareRect, o.rect) })); | |
| scored.sort((a,b) => a.dist - b.dist); | |
| chosen = scored[0]?.el; | |
| } | |
| if (chosen && isVisible(chosen)) { | |
| try { | |
| chosen.click(); | |
| touchedCount++; | |
| console.log(`Clicked Copy item for a post (#${touchedCount}).`); | |
| } catch (e) { | |
| try { | |
| chosen.dispatchEvent(new MouseEvent('mousedown', { bubbles:true })); | |
| chosen.dispatchEvent(new MouseEvent('mouseup', { bubbles:true })); | |
| chosen.dispatchEvent(new MouseEvent('click', { bubbles:true })); | |
| touchedCount++; | |
| console.log(`Clicked Copy item (via dispatched events) for a post (#${touchedCount}).`); | |
| } catch (err) { | |
| console.warn('Failed to click copy item, but share was opened (counted).', err); | |
| touchedCount++; | |
| } | |
| } | |
| } else { | |
| console.log('Copy item not found after opening share, but share clicked (counted as touched).'); | |
| touchedCount++; | |
| } | |
| await sleep(rand(400, 1200)); | |
| try { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles:true })); } catch(e){} | |
| await sleep(120); | |
| } | |
| if (!progress) { | |
| window.scrollBy({ top: window.innerHeight * 0.9, behavior: 'smooth' }); | |
| await sleep(rand(900, 1500)); | |
| } else { | |
| window.scrollBy({ top: window.innerHeight * 0.45, behavior: 'smooth' }); | |
| await sleep(rand(700, 1300)); | |
| } | |
| if (iteration % 5 === 0) console.log(`Iteration ${iteration} — touchedCount ${touchedCount}`); | |
| } | |
| } catch (err) { | |
| console.error('redditShareClickerDeep encountered an error:', err); | |
| } finally { | |
| console.log('redditShareClickerDeep stopped. total touched:', touchedCount); | |
| window.redditShareClickerDeepRunning = false; | |
| } | |
| } | |
| function _escapeHtml(s) { | |
| return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); | |
| } | |
| const runnableSource = "(" + redditShareClickerDeep_resilient.toString() + ")();"; | |
| const host = location.hostname || ""; | |
| const isRedditHost = /(^|\.)reddit\.com$/.test(host) || host.endsWith('.reddit.com'); | |
| if (isRedditHost) { | |
| try { | |
| (0, eval)(runnableSource); | |
| } catch (e) { | |
| console.error("Failed to eval the redditShareClickerDeep script in this context:", e); | |
| } | |
| return; | |
| } | |
| try { | |
| const newTab = window.open('https://www.reddit.com', '_blank'); | |
| if (navigator.clipboard && navigator.clipboard.writeText) { | |
| navigator.clipboard.writeText(runnableSource).then(() => { | |
| alert('Opened reddit in a new tab and copied the runnable script to your clipboard.\n\nSwitch to the reddit tab, open the console (F12) and paste (Ctrl+V) + Enter to run.'); | |
| }).catch(() => { | |
| const blobHtml = '<!doctype html><meta charset="utf-8"><title>Reddit script — copy</title>' + | |
| '<p>Copy the script below, switch to Reddit tab, open console, paste & Enter:</p>' + | |
| '<textarea style="width:100%;height:70vh;">' + _escapeHtml(runnableSource) + '</textarea>'; | |
| const blob = new Blob([blobHtml], { type: 'text/html' }); | |
| window.open(URL.createObjectURL(blob), '_blank'); | |
| alert('Opened reddit in a new tab. Clipboard copy blocked — a new page with the script has been opened for manual copy.'); | |
| }); | |
| } else { | |
| const blobHtml = '<!doctype html><meta charset="utf-8"><title>Reddit script — copy</title>' + | |
| '<p>Copy the script below, switch to Reddit tab, open console, paste & Enter:</p>' + | |
| '<textarea style="width:100%;height:70vh;">' + _escapeHtml(runnableSource) + '</textarea>'; | |
| const blob = new Blob([blobHtml], { type: 'text/html' }); | |
| window.open(URL.createObjectURL(blob), '_blank'); | |
| alert('Opened reddit in a new tab. Clipboard unavailable — a new page with the script has been opened for manual copy.'); | |
| } | |
| } catch (err) { | |
| console.error('Failed to open reddit or copy script automatically:', err); | |
| alert('Could not open reddit or copy script automatically. The runnable script is available in the variable `runnableSource` in this console. Copy it and paste into reddit console manually.'); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment