Slack Allow HTTP OAuth urls

A simple script that lets you bypass the slack restriction of only allowing https oauth urls

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         Slack Allow HTTP OAuth urls
// @description  A simple script that lets you bypass the slack restriction of only allowing https oauth urls
// @license      MIT
// @namespace    https://tangled.sh/@dunkirk.sh/bunplayground/slack-http-allowed
// @version      0.1
// @description
// @author       Kieran Klukas
// @match        https://api.slack.com/apps/*/oauth*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(() => {
  // Track HTTP inputs to avoid redundant processing
  const httpInputs = new Set();

  // Add CSS to style the editor table layout
  function addCustomStyles() {
    const styleEl = document.createElement("style");
    styleEl.textContent = `
      .p-url_table_editor__actions {
        width: 176px !important;
      }
      .p-url_table_editor__fields {
        width: calc(100% - 176px) !important;
      }
    `;
    document.head.appendChild(styleEl);
  }

  function watchForHTTPEntries() {
    const urlInputs = document.querySelectorAll(
      "[data-url-table-editor-input]",
    );

    // Add event listeners to each input field that doesn't already have one
    for (const input of urlInputs) {
      if (input.hasHttpListener) continue;

      input.hasHttpListener = true;
      input.addEventListener("input", function () {
        const row = this.closest("[data-row]");
        if (!row) return;

        const saveButton = row.querySelector(
          '[data-js-url-editor="save-create"]',
        );
        if (!saveButton) return;

        // Check if input contains http:// (non-https URL)
        if (this.value.trim().startsWith("http://")) {
          // Make the button sassy and green
          saveButton.classList.remove("disabled");
          saveButton.removeAttribute("disabled");
          saveButton.textContent = "add 😈";

          // Add to tracking set
          httpInputs.add(this);
        } else {
          // Reset to default state if not http://
          saveButton.textContent = "Add";

          // Remove from tracking set
          httpInputs.delete(this);

          // Only re-disable if empty (assuming that's the original logic)
          if (!this.value.trim()) {
            saveButton.classList.add("disabled");
            saveButton.setAttribute("disabled", "");
          }
        }
      });
    }
  }

  // Function to specifically fix HTTP buttons that might have been disabled
  function fixHttpButtons() {
    // Only process inputs we know have HTTP URLs
    for (const input of httpInputs) {
      if (!input.value.trim().startsWith("http://")) {
        httpInputs.delete(input);
        continue;
      }

      const row = input.closest("[data-row]");
      if (!row) {
        httpInputs.delete(input);
        continue;
      }

      const saveButton = row.querySelector(
        '[data-js-url-editor="save-create"]',
      );
      if (!saveButton) {
        httpInputs.delete(input);
        continue;
      }

      // Force the button to stay enabled
      saveButton.classList.remove("disabled");
      saveButton.removeAttribute("disabled");
      saveButton.textContent = "add 😈";
    }
  }

  // Watch for attribute changes on buttons to detect when they're disabled again
  function watchButtonAttributes() {
    const buttonObserver = new window.MutationObserver((mutations) => {
      for (const mutation of mutations) {
        if (
          mutation.type === "attributes" &&
          (mutation.attributeName === "disabled" ||
            mutation.attributeName === "class")
        ) {
          const button = mutation.target;
          const row = button.closest("[data-row]");
          if (!row) continue;

          const input = row.querySelector("[data-url-table-editor-input]");
          if (!input || !input.value.trim().startsWith("http://")) continue;

          // This is an HTTP input with a button that was just disabled
          // Re-enable it immediately
          if (
            button.classList.contains("disabled") ||
            button.hasAttribute("disabled")
          ) {
            button.classList.remove("disabled");
            button.removeAttribute("disabled");
            button.textContent = "add 😈";
          }
        }
      }
    });

    // Observe all save buttons for attribute changes
    const saveButtons = document.querySelectorAll(
      '[data-js-url-editor="save-create"]',
    );
    for (const button of saveButtons) {
      buttonObserver.observe(button, { attributes: true });
    }

    return buttonObserver;
  }

  // Run the CSS additions immediately
  addCustomStyles();

  // Run the function immediately
  watchForHTTPEntries();
  fixHttpButtons();
  const buttonObserver = watchButtonAttributes();

  // Then run it again after a delay to ensure DOM is fully loaded
  setTimeout(() => {
    watchForHTTPEntries();
    fixHttpButtons();
  }, 1000);

  // Run the button fix less frequently to reduce browser lag
  setInterval(fixHttpButtons, 1000);

  // Observer for new inputs
  const inputObserver = new window.MutationObserver((mutations) => {
    // Throttle the observer callback
    if (inputObserver.isProcessing) return;
    inputObserver.isProcessing = true;

    setTimeout(() => {
      let hasNewInputs = false;
      let hasNewButtons = false;

      for (const mutation of mutations) {
        if (mutation.addedNodes.length) {
          hasNewInputs = true;

          // Check if any new save buttons were added
          for (const node of mutation.addedNodes) {
            if (node.nodeType === 1) {
              // Node.ELEMENT_NODE is 1
              const newButtons = node.querySelectorAll
                ? node.querySelectorAll('[data-js-url-editor="save-create"]')
                : [];
              if (newButtons.length > 0) hasNewButtons = true;
            }
          }
        }
      }

      if (hasNewInputs) {
        watchForHTTPEntries();
      }

      if (hasNewButtons) {
        // Observe any new buttons
        const newSaveButtons = document.querySelectorAll(
          '[data-js-url-editor="save-create"]',
        );
        for (const button of newSaveButtons) {
          if (!button.hasAttributeObserver) {
            buttonObserver.observe(button, { attributes: true });
            button.hasAttributeObserver = true;
          }
        }
      }

      fixHttpButtons();
      inputObserver.isProcessing = false;
    }, 200);
  });

  // Start observing the document with fewer things to watch
  inputObserver.observe(document.body, {
    childList: true,
    subtree: true,
  });
})();