Skip to content

Instantly share code, notes, and snippets.

@intellectronica
Created May 22, 2025 17:31
Show Gist options
  • Save intellectronica/83d15d6bc0d2b64b442f574eb7882428 to your computer and use it in GitHub Desktop.
Save intellectronica/83d15d6bc0d2b64b442f574eb7882428 to your computer and use it in GitHub Desktop.
Monkey script for automatically opening Slack in the browser (convenient if you don't use the app)
// ==UserScript==
// @name slack-auto-open-in-browser
// @version 0.4
// @description Automatically redirects to the browser version of Slack using teamUrl from props_node data on ssb/redirect pages.
// @author Eleanor Berger (Gemini 2.5 Pro)
// @match *://*.slack.com/ssb/redirect*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// Consistent console log prefix for easier filtering
const logPrefix = '[Slack Auto SSB Redirect]';
console.log(`${logPrefix} Script started.`);
const propsNode = document.getElementById('props_node');
if (propsNode) {
const propsDataString = propsNode.getAttribute('data-props');
if (propsDataString) {
try {
const props = JSON.parse(propsDataString);
// For detailed debugging, you can uncomment the next line to see all parsed props:
// console.log(`${logPrefix} Parsed props_node data:`, props);
const teamUrl = props.teamUrl;
if (teamUrl && typeof teamUrl === 'string' && teamUrl.startsWith('http')) {
console.log(`${logPrefix} teamUrl found: "${teamUrl}". Redirecting...`);
window.location.href = teamUrl;
return; // Exit script after successful redirect
} else {
console.error(`${logPrefix} Valid teamUrl not found or invalid in props_node data. Value was: "${teamUrl}". Proceeding to fallback.`);
attemptClickFallback();
}
} catch (e) {
console.error(`${logPrefix} Error parsing props_node data. Proceeding to fallback.`, e);
attemptClickFallback();
}
} else {
console.error(`${logPrefix} props_node found but data-props attribute is empty or missing. Proceeding to fallback.`);
attemptClickFallback();
}
} else {
console.warn(`${logPrefix} props_node not found. This might indicate a page structure change. Proceeding to fallback.`);
attemptClickFallback();
}
function attemptClickFallback() {
// This fallback tries to click the link, which might appear dynamically.
console.log(`${logPrefix} Fallback: Attempting to find and click the "use Slack in your browser" link.`);
const linkSelectorQA = 'a[data-qa="ssb_redirect_open_in_browser"]';
const linkTextContent = "use Slack in your browser";
let linkClicked = false;
let attempts = 0;
const maxAttempts = 20; // Try for 10 seconds (20 attempts * 500ms interval)
const interval = 500; // Milliseconds
function findAndClick() {
if (linkClicked) {
return; // Link has already been found and clicked
}
attempts++;
let targetLink = document.querySelector(linkSelectorQA);
if (!targetLink) {
// If the data-qa selector fails, try finding by text content.
// This is often more robust to minor markup changes.
const allLinks = document.querySelectorAll('a'); // Query all anchor tags
for (const link of allLinks) {
if (link.textContent && link.textContent.trim().toLowerCase() === linkTextContent.toLowerCase()) {
// To make this even more specific, you could check for a common class if one exists,
// e.g., if (link.classList.contains('c-link') && ...)
targetLink = link;
console.log(`${logPrefix} Fallback: Found link by text content:`, targetLink);
break;
}
}
}
if (targetLink) {
console.log(`${logPrefix} Fallback: Link found. Clicking...`, targetLink);
targetLink.click();
linkClicked = true;
} else if (attempts < maxAttempts) {
// Optional: More verbose logging for each attempt.
// console.log(`${logPrefix} Fallback: Link not found on attempt ${attempts}/${maxAttempts}. Retrying in ${interval}ms.`);
setTimeout(findAndClick, interval);
} else {
console.warn(`${logPrefix} Fallback: Link not found after ${maxAttempts} attempts. The element with text "${linkTextContent}" or selector "${linkSelectorQA}" might not be present, takes too long to appear, or the primary props-based method should have worked.`);
}
}
// Initial call to start polling for the fallback mechanism.
// A small initial delay can sometimes help if the element appears very shortly after 'document-idle'.
setTimeout(findAndClick, interval);
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment