LinkedIn Connect

Add "Auto Connect" button that automatically sends connection requests

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         LinkedIn Connect
// @namespace    http://tampermonkey.net/
// @version      2025-02-27
// @description  Add "Auto Connect" button that automatically sends connection requests
// @author       Miguelx97
// @match        https://www.linkedin.com/mynetwork/grow/*
// @icon         data:image/jpeg;base64,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
// @grant        none
// ==/UserScript==

(function () {
  "use strict";

  /** Configuration constants and selectors */
  const CONFIG = {
    DEV_MODE: false,
    DELAY_SHORT: 200,
    DELAY_LONG: 2000,
    LIMIT_SCROLL: 8,
    LIMIT_CONNECTIONS: 16,
    MUTUAL_CONNECTION_THRESHOLD: 20,
    COLORS: {
      success: "#09ff00",
      danger: "#f2d8d8",
    },
    SELECTORS: {
      btnShowMore: '[data-view-name="cohort-section-see-all"]',
      scrollChild:
        '[data-sdui-screen="com.linkedin.sdui.flagshipnav.mynetwork.CohortSeeAll"]',
      listUsers:
        "div._1a8ay891.cnuthtbc.cnuthtj4._1a8ay893._1a8ay895._1a8ay89a.cnuthtew._1k2lxmew0._1k2lxmezs._1k2lxme13k._1k2lxme17c._139m7k23",
      userItem: "div.cnuthtaw",
      userContainer: '[role="listitem"]',
      mutualConnectionMsg:
        "p._12p2gmq9._12p2gmq2._12p2gmqi._29kmc32._29kmc33._29kmc38._29kmc3d._1lu65cq3._1lu65cq1._1xoe5hd3._1s9oaxgo._1ptbkx6c8._1s9oaxg5._1s9oaxgc._139m7k1fr._1s9oaxgn",
      btnConnect:
        "button.yyosfl1.h8e4ml0._1xoe5hd0._139m7k1gx._1s9oaxg7._1s9oaxgi.yyosfl4.yyosfl3.cnuthtc0.cnutht0.cnutht1i0._1k2lxmew._1ptbkx61go",
      topBar: "main ul.cnuthtb4.cnuthte8.cnuthth4.cnuththk",
    },
  };

  /** Utility function for delaying execution */
  const delay = (ms) => new Promise((res) => setTimeout(res, ms));

  /**
   * Waits for an element to appear in the DOM.
   * @param {string} selector - The CSS selector to wait for.
   * @param {HTMLElement} [container=document] - The container element.
   * @param {number} [timeout=10000] - Timeout in milliseconds.
   * @returns {Promise<HTMLElement>}
   */
  async function waitForElement(
    selector,
    container = document,
    timeout = 10000
  ) {
    const start = Date.now();
    return new Promise((resolve, reject) => {
      (function check() {
        const el = container.querySelector(selector);
        if (el) {
          return resolve(el);
        }
        if (Date.now() - start > timeout) {
          return reject(
            new Error(`Element ${selector} not found within timeout`)
          );
        }
        requestAnimationFrame(check);
      })();
    });
  }

  /** Opens the "See all" connections modal */
  async function openConnectionsModal() {
    const btnShowMore = await waitForElement(CONFIG.SELECTORS.btnShowMore);
    btnShowMore.click();
  }

  /** Scrolls the modal to load more user items and inserts a counter UI */
  async function scrollToLoadUsers() {
    const scrollChildEl = await waitForElement(CONFIG.SELECTORS.scrollChild);
    // Insert connection counter UI
    scrollChildEl.insertAdjacentHTML(
      "afterbegin",
      `<p style="text-align: center; font-size: 14px; margin: 4px;">
         Connection Requests: <span id="connectionRequests">0</span><span id="finishMsg"></span>
       </p>`
    );
    const scrollContainer = scrollChildEl.parentElement;
    const limitScroll = CONFIG.DEV_MODE ? 2 : CONFIG.LIMIT_SCROLL;
    for (let i = 0; i < limitScroll; i++) {
      if (i > 0) await delay(CONFIG.DELAY_LONG);
      scrollContainer.scrollTo({
        top: scrollContainer.scrollHeight,
        behavior: "smooth",
      });
    }
    // Scroll back to the top
    scrollContainer.scrollTo({ top: 0, behavior: "smooth" });
    return scrollChildEl;
  }

  /** Processes the list of users, marking those that meet the criteria and triggering connection */
  async function processConnections(scrollChildEl) {
    const listUsersElement = scrollChildEl.querySelector(
      CONFIG.SELECTORS.listUsers
    );
    if (!listUsersElement) {
      throw new Error("List users element not found");
    }
    const users = listUsersElement.querySelectorAll(CONFIG.SELECTORS.userItem);
    let connectionsCount = 0;
    for (const userEl of users) {
      const container = userEl.querySelector(CONFIG.SELECTORS.userContainer);
      if (!container) continue;

      const msgEl = container.querySelector(
        CONFIG.SELECTORS.mutualConnectionMsg
      );
      if (!msgEl) continue;

      const regex = /and\s+(\d+)\s+other mutual connection(?:s)?/;
      const match = msgEl.textContent.match(regex);
      const numMutualConnections = match ? parseInt(match[1], 10) : 0;

      // Determine the styling based on the number of mutual connections
      const { success, danger } = CONFIG.COLORS;
      let color = danger;
      let border = 2;
      const wannaConnect =
        numMutualConnections > CONFIG.MUTUAL_CONNECTION_THRESHOLD;
      if (wannaConnect) {
        color = success;
        border = Math.min(
          (numMutualConnections / CONFIG.MUTUAL_CONNECTION_THRESHOLD) * 2,
          6
        );
      }
      container.style.border = `solid ${border}px ${color}`;
      await delay(CONFIG.DELAY_SHORT);

      // If the criteria are met, click the Connect button
      if (wannaConnect) {
        const btnConnect = container.querySelector(CONFIG.SELECTORS.btnConnect);
        if (!btnConnect) continue;
        if (connectionsCount < CONFIG.LIMIT_CONNECTIONS && !CONFIG.DEV_MODE) {
          btnConnect.click();
          connectionsCount++;
          scrollChildEl.querySelector("#connectionRequests").textContent =
            connectionsCount;
        }
      }
    }
    scrollChildEl.querySelector("#finishMsg").textContent = " · Finished!";
  }

  /** Main auto-connect function that coordinates the process */
  async function startAutoConnect() {
    try {
      await openConnectionsModal();
      const scrollChildEl = await scrollToLoadUsers();
      await processConnections(scrollChildEl);
    } catch (error) {
      console.error("Error in auto connect:", error);
    }
  }

  /** Inserts the "Auto Connect" button into the page's top bar */
  async function addAutoConnectButton() {
    const topBar = await waitForElement(CONFIG.SELECTORS.topBar);
    if (!topBar) return;
    await delay(CONFIG.DELAY_SHORT);
    topBar.insertAdjacentHTML(
      "beforeend",
      `<li style="margin-left:auto;">
        <a id="auto-connect" class="minvu03 cnutht0 _139m7k7f h8e4ml0 _1xoe5hd0 _139m7k19r _139m7k1a1 _139m7k19w _1mamebb1 cnuthtb4 cnutht1i0 _1s9oaxgi _1pylls4i _1pylls4m _1ptbkx61fc minvu04 _1k2lxme13k _1k2lxme17c _1k2lxmevk _1k2lxmezc cnuthtig cnutht180">
          <span class="_12p2gmq9 _1s9oaxg7 _12p2gmqk _29kmc3a _29kmc3b _29kmc3g _29kmc3l _1s9oaxg6 _139m7k1gx _1s9oaxgn" style="color:#0a66c2;">Auto Connect</span>
          </span>
        </a>
    </li>`
    );
    const btnAutoConnect = await waitForElement("#auto-connect");
    btnAutoConnect.addEventListener("click", startAutoConnect);
  }

  addAutoConnectButton();
})();