DeepSeek Auto-Retry with Text & SVG Detection

Auto retry when server busy detected by text or SVG icon, with toggle UI and cooldown.

// ==UserScript==
// @name         DeepSeek Auto-Retry with Text & SVG Detection
// @version      1.5
// @description  Auto retry when server busy detected by text or SVG icon, with toggle UI and cooldown.
// @author       balkken
// @match        https://chat.deepseek.com/*
// @grant        none
// @license      Blakken 
// @namespace https://greasyfork.org/users/1477546
// ==/UserScript==

(function () {
  "use strict";

  const config = {
    autoRetryEnabled: true,
    cooldown: 15000,  // 15 secondes entre retries
    interval: 1000,   // vérifie toutes les 1 seconde
    errorTexts: [
      "Server busy, please try again later.",
      "The server is busy. Please try again later.",
      "Thought for 0 seconds",
    ],
  };

  // Retourne vrai si un texte contient un message d'erreur
  function isErrorText(text) {
    return config.errorTexts.some(msg => text.includes(msg));
  }

  // Vérifie la présence d'un SVG orange spécifique dans un block
  function blockHasBusySVG(block) {
    const path = block.querySelector("svg path");
    if (!path) return false;
    const d = path.getAttribute("d") || "";
    const fill = path.getAttribute("fill") || "";
    const pathStart = "M12 .5C18.351.5 23.5 5.649 23.5 12S18.351 23.5";
    return d.startsWith(pathStart) && fill.toLowerCase() === "#f59e0b";
  }

  // Trouve le bouton retry par détection SVG orange dans les boutons
  function findRetryButton() {
    const buttons = document.querySelectorAll("div.ds-icon-button");
    const pathStart = "M12 .5C18.351.5 23.5 5.649 23.5 12S18.351 23.5";
    for (let i = buttons.length - 1; i >= 0; i--) {
      const btn = buttons[i];
      const path = btn.querySelector("svg path");
      if (!path) continue;
      const d = path.getAttribute("d") || "";
      const fill = path.getAttribute("fill") || "";
      if (d.startsWith(pathStart) && fill.toLowerCase() === "#f59e0b") {
        return btn;
      }
    }
    return null;
  }

  // Cherche dans la page un message d'erreur textuel ou un SVG de busy
  function detectErrors() {
    // 1) Cherche les blocs avec texte d'erreur
    const textBlocks = [...document.querySelectorAll("div.ac2694a7 span, div.ds-markdown.ds-markdown--block")];
    for (const block of textBlocks) {
      const text = block.innerText || "";
      if (isErrorText(text)) return true;
    }
    // 2) Cherche les SVG d'icône busy dans les mêmes blocks (au cas où)
    for (const block of textBlocks) {
      if (blockHasBusySVG(block)) return true;
    }
    return false;
  }

  // Affiche un message visuel temporaire sur la page
  function showStatus(msg) {
    let el = document.getElementById("autoRetryStatus");
    if (!el) {
      el = document.createElement("div");
      el.id = "autoRetryStatus";
      el.style.position = "fixed";
      el.style.bottom = "100px";
      el.style.right = "20px";
      el.style.backgroundColor = "rgba(0,0,0,0.7)";
      el.style.color = "white";
      el.style.padding = "6px 12px";
      el.style.borderRadius = "6px";
      el.style.zIndex = "9999";
      document.body.appendChild(el);
    }
    el.textContent = msg;
    setTimeout(() => {
      if (el.textContent === msg) el.textContent = "";
    }, 3000);
  }

  // Vérifie si erreur détectée et clique sur retry si cooldown passé
  function checkAndRetry() {
    if (!config.autoRetryEnabled) return;
    if (!detectErrors()) return;

    const now = Date.now();
    if (window.lastRetryTime && now - window.lastRetryTime < config.cooldown) {
      showStatus(`AutoRetry: cooldown (${Math.round((config.cooldown - (now - window.lastRetryTime))/1000)}s left)`);
      return;
    }

    const retryBtn = findRetryButton();
    if (retryBtn) {
      retryBtn.click();
      window.lastRetryTime = now;
      showStatus("AutoRetry: Retry clicked");
      console.log("AutoRetry: retry button clicked");
    } else {
      showStatus("AutoRetry: Retry button NOT found");
      console.log("AutoRetry: retry button NOT found");
    }
  }

  // Créé le toggle UI ON/OFF pour l'auto-retry
  function createToggleUI() {
    const container = document.createElement("div");
    container.id = "autoRetryToggleUI";
    container.style.position = "fixed";
    container.style.bottom = "70px";
    container.style.right = "20px";
    container.style.zIndex = "9999";
    container.style.backgroundColor = "rgba(0,0,0,0.6)";
    container.style.padding = "8px 12px";
    container.style.borderRadius = "8px";
    container.style.color = "white";
    container.style.fontFamily = "Arial, sans-serif";
    container.style.fontSize = "14px";
    container.style.userSelect = "none";

    container.innerHTML = `
      <label style="cursor: pointer;">
        <input type="checkbox" id="autoRetryCheckbox" checked style="margin-right:6px;">
        Auto‑Retry
      </label>
    `;
    document.body.appendChild(container);

    document.getElementById("autoRetryCheckbox").addEventListener("change", e => {
      config.autoRetryEnabled = e.target.checked;
      showStatus(`Auto‑Retry turned ${config.autoRetryEnabled ? "ON" : "OFF"}`);
      console.log(`Auto‑Retry turned ${config.autoRetryEnabled ? "ON" : "OFF"}`);
    });
  }

  // Initialisation
  function init() {
    createToggleUI();
    setInterval(checkAndRetry, config.interval);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }
})();