DeepSeek繁忙自动点击重试

DeepSeek提示繁忙,自动点击重试,并显示操作状态通知

// ==UserScript==
// @name         DeepSeek繁忙自动点击重试
// @namespace    http://tampermonkey.net/
// @version      2025-02-15
// @license      MIT
// @description  DeepSeek提示繁忙,自动点击重试,并显示操作状态通知
// @author       Dingning
// @include      *://chat.deepseek.com/*
// @icon         https://registry.npmmirror.com/@lobehub/icons-static-png/latest/files/dark/deepseek-color.png
// @grant        none
// ==/UserScript==

(function () {
  "use strict";

  // 配置参数
  let CHECK_INTERVAL = 1;
  let retryDelay = 1;
  let maxRetryDelay = 60;
  let retryCount = 0;
  let threshold = 5;
  let normalDelayIncrement = 2;
  let fastFrequencyDelayIncrement = 30;
  let retryTimeoutId = null;
  let configPanel;
  let countdownTimer = null;
  let remainingTime = 0;
  let isMonitoring = true;
  let isPanelExpanded = true;

  // 添加全局样式
  const addGlobalStyles = () => {
      const style = document.createElement('style');
      style.textContent = `
          input::placeholder { color: #999!important; opacity: 1!important; }
          input { color: #333!important; }
        .toggle-btn.paused { background: #ff3b30!important; }
        .collapse-btn {
              padding: 6px 10px;
              border: none;
              border-radius: 4px;
              background: #007aff;
              color: white;
              cursor: pointer;
              font-size: 12px;
              transition: background 0.2s ease;
          }
        .collapse-btn:hover {
              background: #0063cc;
          }
      `;
      document.head.appendChild(style);
  };

  const checkAndRetry = () => {
      if (!isMonitoring) return;

      const loadingElements = document.querySelectorAll(".ds-loading");
      const hasLoading = loadingElements.length > 0;

      const contentList = document.querySelectorAll(".ds-markdown");
      let hasBusyMessage = false;
      const busyMessages = [
          "<p>服务器繁忙,请稍后再试。</p>",
          "<p>The server is busy. Please try again later.</p>",
      ];
      const lastContent = contentList.length > 0? contentList[contentList.length - 1].innerHTML : "";

      if (busyMessages.includes(lastContent)) hasBusyMessage = true;

      if (!hasLoading &&!hasBusyMessage) {
          if (retryCount > 0) createNotification("已重置重试次数和重试延迟", "success");
          retryCount = 0;
          retryDelay = 1;
          if (configPanel) configPanel.style.display = "none";
      } else if (hasBusyMessage) {
          if (configPanel) {
              configPanel.style.display = "block";
              updateStatusDisplay();
          }
      }

      if (hasBusyMessage &&!retryTimeoutId) {
          startCountdown(retryDelay);

          retryTimeoutId = setTimeout(() => {
              const retryBtn = Array.from(document.querySelectorAll(".ds-icon-button"))
                .reverse()
                .find(btn => btn.querySelector("svg #重新生成"));

              if (retryBtn) {
                  retryBtn.click();
                  retryCount++;
                  createNotification(`已点击重试,重试次数: ${retryCount}`, "success");

                  const toastContents = document.querySelectorAll(".ds-toast__content");
                  for (const toast of toastContents) {
                      if (toast.textContent === "你发送消息的频率过快,请稍后再发") {
                          retryDelay = Math.min(retryDelay + fastFrequencyDelayIncrement, maxRetryDelay);
                          createNotification(`检测到频率过快,延迟调整为 ${retryDelay} 秒`, "warning");
                          break;
                      }
                  }

                  if (retryCount >= threshold) {
                      retryDelay = Math.min(retryDelay + normalDelayIncrement, maxRetryDelay);
                      createNotification(`重试次数过多,延迟调整为 ${retryDelay} 秒`, "warning");
                  }
              }

              clearTimeout(retryTimeoutId);
              retryTimeoutId = null;
          }, retryDelay * 1000);
      }
  };

  // 通知系统
  const createNotificationContainer = () => {
      const container = document.createElement("div");
      container.id = "retry-notifications";
      container.style.cssText = `
          position: fixed;
          top: 20px;
          right: 20px;
          z-index: 10000;
          display: flex;
          flex-direction: column;
          gap: 10px;
          max-width: 300px;
      `;
      document.body.appendChild(container);
      return container;
  };

  const createNotification = (message, type = "info") => {
      const notification = document.createElement("div");
      notification.className = `retry-notification ${type}`;
      notification.style.cssText = `
          padding: 12px 16px;
          background: white;
          border-radius: 8px;
          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
          font-size: 14px;
          color: #333;
          opacity: 0;
          transform: translateX(100%);
          transition: all 0.3s ease;
          display: flex;
          align-items: center;
          gap: 8px;
          border-left: 4px solid ${{
              success: "#52c41a",
              warning: "#faad14",
              info: "#1890ff"
          }[type]};
      `;

      const icon = document.createElement("span");
      icon.textContent = { success: "✅", warning: "⚠️", info: "ℹ️" }[type];
      notification.appendChild(icon);

      const text = document.createElement("span");
      text.textContent = message;
      notification.appendChild(text);

      const container = document.getElementById("retry-notifications") || createNotificationContainer();
      container.appendChild(notification);

      setTimeout(() => {
          notification.style.opacity = "1";
          notification.style.transform = "translateX(0)";
      }, 50);

      setTimeout(() => {
          notification.style.opacity = "0";
          setTimeout(() => notification.remove(), 300);
      }, 3000);
  };

  // 配置面板
  const createConfigPanel = () => {
      const panel = document.createElement("div");
      panel.id = "config-panel";
      panel.style.cssText = `
          position: fixed;
          bottom: 20px;
          right: 20px;
          z-index: 10000;
          background: rgba(255, 255, 255, 0.95);
          color: #414158;
          border-radius: 12px;
          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
          padding: 20px;
          display: flex;
          flex-direction: column;
          gap: 16px;
          max-width: 300px;
          backdrop-filter: blur(20px);
          border: 1px solid rgba(255, 255, 255, 0.2);
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
          display: none;
      `;

      // 折叠按钮
      const collapseBtn = document.createElement("button");
      collapseBtn.textContent = isPanelExpanded? "折叠" : "展开";
      collapseBtn.className = "collapse-btn";
      collapseBtn.addEventListener("click", () => {
          isPanelExpanded =!isPanelExpanded;
          collapseBtn.textContent = isPanelExpanded? "折叠" : "展开";
          if (isPanelExpanded) {
              panel.style.height = "auto";
              panel.style.padding = "20px";
              buttonContainer.style.marginTop = "10px";
              saveButton.style.display = "block";
              panel.querySelectorAll('.config-item').forEach(item => item.style.display = "flex");
          } else {
              panel.style.height = "auto";
              panel.style.padding = "10px";
              buttonContainer.style.marginTop = "0px";
              saveButton.style.display = "none";
              panel.querySelectorAll('.config-item').forEach(item => item.style.display = "none");
          }
      });
      panel.appendChild(collapseBtn);

      // 状态显示
      const statusContainer = document.createElement("div");
      statusContainer.style.cssText = `
          background: rgba(245, 245, 247, 0.8);
          border-radius: 8px;
          padding: 12px;
          min-width: 100px;
      `;

      const createStatusRow = (title, value, id) => {
          const row = document.createElement("div");
          row.style.cssText = `
              display: flex;
              justify-content: space-between;
              align-items: center;
              margin-bottom: 6px;
              font-size: 13px;
          `;

          const titleSpan = document.createElement("span");
          titleSpan.textContent = title;
          titleSpan.style.color = "#666";

          const valueSpan = document.createElement("span");
          valueSpan.id = id;
          valueSpan.textContent = value;
          valueSpan.style.cssText = `
              font-weight: 500;
              color: #1a1a1a;
          `;

          row.append(titleSpan, valueSpan);
          return row;
      };

      statusContainer.append(
          createStatusRow("检测状态", isMonitoring? "运行中" : "已暂停", "monitor-status"),
          createStatusRow("当前重试次数", retryCount, "retry-count"),
          createStatusRow("下次尝试", "等待中", "countdown")
      );
      panel.appendChild(statusContainer);

      // 配置项
      const createInputRow = (label, value) => {
          const row = document.createElement("div");
          row.style.cssText = `
              display: flex;
              align-items: center;
              justify-content: space-between;
              gap: 10px;
          `;

          const labelEl = document.createElement("label");
          labelEl.textContent = label;
          labelEl.style.cssText = `
              font-size: 13px;
              color: #666;
              flex: 1;
          `;

          const input = document.createElement("input");
          input.type = "number";
          input.value = value;
          input.placeholder = "输入数值";
          input.style.cssText = `
              width: 80px;
              padding: 6px 8px;
              border: 1px solid #ddd;
              border-radius: 6px;
              font-size: 13px;
              background: rgba(255, 255, 255, 0.8);
          `;

          row.append(labelEl, input);
          return { row, input };
      };

      const configs = [
          { label: "检查间隔 (秒)", value: CHECK_INTERVAL, action: v => CHECK_INTERVAL = v },
          { label: "初始延迟 (秒)", value: retryDelay, action: v => retryDelay = v },
          { label: "最大延迟 (秒)", value: maxRetryDelay, action: v => maxRetryDelay = v },
          { label: "重试阈值", value: threshold, action: v => threshold = v },
          { label: "正常增量 (秒)", value: normalDelayIncrement, action: v => normalDelayIncrement = v },
          { label: "过快增量 (秒)", value: fastFrequencyDelayIncrement, action: v => fastFrequencyDelayIncrement = v }
      ];

      configs.forEach(({ label, value, action }, index) => {
          const { row, input } = createInputRow(label, value);
          row.classList.add('config-item');
          if (!isPanelExpanded) {
              row.style.display = "none";
          }
          input.addEventListener("input", () => {
              action(parseInt(input.value) || 1);
              clearInterval(checkIntervalId);
              checkIntervalId = setInterval(checkAndRetry, CHECK_INTERVAL * 1000);
          });
          panel.appendChild(row);
      });

      // 控制按钮和保存按钮容器
      const buttonContainer = document.createElement("div");
      buttonContainer.style.cssText = `
          margin-top: 10px;
          display: flex;
          gap: 10px;
      `;

      const toggleBtn = document.createElement("button");
      toggleBtn.textContent = "暂停检测";
      toggleBtn.className = "toggle-btn";
      toggleBtn.style.cssText = `
          flex: 1;
          padding: 10px;
          border: none;
          border-radius: 8px;
          background: #007aff;
          color: white;
          cursor: pointer;
          font-size: 14px;
          font-weight: 500;
          transition: all 0.2s ease;
      `;

      toggleBtn.addEventListener("click", () => {
          isMonitoring =!isMonitoring;
          toggleBtn.textContent = isMonitoring? "暂停检测" : "继续检测";
          toggleBtn.classList.toggle("paused",!isMonitoring);
          createNotification(isMonitoring? "已恢复自动检测" : "已暂停自动检测", isMonitoring? "success" : "warning");

          if (isMonitoring) {
              clearInterval(checkIntervalId);
              checkIntervalId = setInterval(checkAndRetry, CHECK_INTERVAL * 1000);
          } else {
              clearInterval(checkIntervalId);
              clearTimeout(retryTimeoutId);
              retryTimeoutId = null;
              remainingTime = 0;
          }
          updateStatusDisplay();
      });

      buttonContainer.appendChild(toggleBtn);

      // 打开配置按钮(在折叠状态下显示)
      const openConfigBtn = document.createElement("button");
      openConfigBtn.textContent = "打开配置";
      openConfigBtn.style.cssText = `
          padding: 10px 18px;
          border: none;
          border-radius: 8px;
          background: #007aff;
          color: white;
          cursor: pointer;
          font-size: 14px;
          font-weight: 500;
          transition: background 0.2s ease;
      `;
      openConfigBtn.addEventListener("click", () => {
          isPanelExpanded = true;
          collapseBtn.textContent = "折叠";
          panel.style.height = "auto";
          panel.style.padding = "20px";
          panel.querySelectorAll('.config-item').forEach(item => item.style.display = "block");
      });

      // 保存按钮
      const saveButton = document.createElement("button");
      saveButton.textContent = "保存配置";
      saveButton.style.cssText = `
          padding: 10px 18px;
          border: none;
          border-radius: 8px;
          background: #007aff;
          color: white;
          cursor: pointer;
          font-size: 14px;
          font-weight: 500;
          transition: background 0.2s ease;
      `;
      saveButton.addEventListener("mouseenter", () => saveButton.style.background = "#0063cc");
      saveButton.addEventListener("mouseleave", () => saveButton.style.background = "#007aff");
      saveButton.addEventListener("click", () => createNotification("配置已保存", "success"));

      if (isPanelExpanded) {
          buttonContainer.appendChild(saveButton);
      } else {
          buttonContainer.appendChild(openConfigBtn);
      }

      panel.appendChild(buttonContainer);

      document.body.appendChild(panel);
      return panel;
  };

  // 倒计时管理
  const startCountdown = (seconds) => {
      clearInterval(countdownTimer);
      remainingTime = seconds;
      countdownTimer = setInterval(() => {
          remainingTime = Math.max(0, remainingTime - 1);
          updateStatusDisplay();
          if (remainingTime <= 0) clearInterval(countdownTimer);
      }, 1000);
  };

  const updateStatusDisplay = () => {
      document.getElementById("countdown").textContent =
          remainingTime > 0? `${remainingTime}秒` : "等待中";
      document.getElementById("retry-count").textContent = retryCount;
      document.getElementById("monitor-status").textContent =
          isMonitoring? "运行中" : "已暂停";
  };

  // 初始化
  addGlobalStyles();
  let checkIntervalId = setInterval(checkAndRetry, CHECK_INTERVAL * 1000);
  configPanel = createConfigPanel();

  window.addEventListener("beforeunload", () => {
      clearInterval(checkIntervalId);
      clearInterval(countdownTimer);
  });
})();