【油猴脚本/Tampermonkey】DeepSeek 服务器繁忙无限重试(20250214优化)
目录
一、 引言
二、 逻辑
三、 源代码
四、 添加新脚本
五、 使用
六、 BUG
七、 优化日志
1.获取最后消息内容报错
2.对话框切换无法正常使用
一、 引言
deepseek演都不演了,每次第一次提问就正常,后面就开始繁忙了,有一点阴招全使我们身上。
greasyfork登不上,不知道是不是被墙了,所以直接在这里存档。
二、 逻辑
每隔5秒使用xpath表达式匹配当前对话框的最后一个消息,出现“服务器繁忙,请稍后再试。”即点击重试。
三、 源代码
// ==UserScript==
// @name Deepseek 服务器繁忙无限重试
// @namespace http://tampermonkey.net/
// @version 2025-02-11
// @description 每隔5秒检测一次最后一条数据是否出现服务器繁忙,出现点击重试,无限循环。
// @author Vdoi
// @match *chat.deepseek.com/*
// @icon https://cdn.deepseek.com/chat/icon.png
// @grant none
// ==/UserScript==
(function() {
'use strict';
console.log('start')
let intervalId = null;
//重试次数
let retryCount = 0;
// 每隔 5 秒检查一次
var times = 5000
// 用于存储定时器 ID 的数组
const intervalIds = [];
//记录重试按钮状态
let retryStatus = false;
// 创建通知元素并插入到页面中
function addNotification() {
let notification = document.getElementById('notification');
if (!notification) { // 检查是否已存在通知元素
notification = document.createElement('div');
notification.id = 'notification';
notification.style.position = 'fixed';
notification.style.left = '50%';
notification.style.top = '50%';
notification.style.transform = 'translate(-50%, -50%)';
notification.style.backgroundColor = '#4D6BFE';
notification.style.color = 'white';
notification.style.padding = '15px';
notification.style.borderRadius = '5px';
notification.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
notification.style.zIndex = '1000';
notification.style.textAlign = 'center';
notification.style.display = 'none'; // 默认隐藏
document.body.appendChild(notification);
}
return notification;
}
// 显示通知的方法
function showNotification(text) {
//console.log('显示提示')
const notification = document.getElementById('notification');
notification.textContent = text;
notification.style.display = 'block';
setTimeout(() => {
notification.style.display = 'none';
}, 3000); // 3秒后隐藏通知
}
//自动重试按钮点击事件
function retryButtonClick() {
var xpath = '//div[@id="retry"]/span';
var elementResult = document.evaluate(
xpath,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
var clickElement = elementResult.singleNodeValue;
clickElement.addEventListener('click', function() {
//console.log('点击')
const xpathExpression = '//div[@id="retry"]';
const result = document.evaluate(
xpathExpression,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const newButtonDiv = result.singleNodeValue;
var text = times + '毫秒后开启自动重试'
if (clickElement.textContent === '打开自动重试') {
console.log('已开启自动重试')
retryStatus = true;
showNotification(text); // 显示通知
// 设置背景颜色
newButtonDiv.style.backgroundColor = '#4D6BFE';
// 设置文字颜色为白色
newButtonDiv.style.color = 'white';
//开启之前清除自动重试的定时器
intervalIds.forEach((id) => {
clearInterval(id);
});
//开启定时器
clickElement.textContent = '关闭自动重试';
intervalId = setInterval(() => checkAndClick(times), times);
intervalIds.push(intervalId);
} else {
console.log('已关闭自动重试')
retryStatus = false;
text = '已关闭自动重试'
showNotification(text); // 显示通知
// 设置背景颜色
newButtonDiv.style.backgroundColor = 'white';
// 设置文字颜色为白色
newButtonDiv.style.color = '#4D6BFE';
// 如果定时器已启动,则清除它,并更新按钮文本
//clearInterval(intervalId);
//intervalId = null;
// 遍历数组,清除每个定时器
intervalIds.forEach((id) => {
clearInterval(id);
});
clickElement.textContent = '打开自动重试';
}
});
}
//增加自动重试按钮
function addButton() {
const xpathExpression = '//span[contains(text(),"深度思考")]/../../div[2]';
const result = document.evaluate(
xpathExpression,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const targetElement = result.singleNodeValue;
//console.log('增加', targetElement)
if (targetElement) {
// 创建要插入的 div 元素(按钮)
const newButtonDiv = document.createElement('div');
newButtonDiv.setAttribute('class', 'ds-button ds-button--primary d9f56c96');
newButtonDiv.setAttribute('id', 'retry');
//未开启白底蓝字,关闭蓝底白字
var backgroundColor = 'white';
var color = '#4D6BFE'
var textContent = '打开自动重试'
if (retryStatus){
backgroundColor = '#4D6BFE';
color = 'white'
textContent = '关闭自动重试'
}
// 设置背景颜色
newButtonDiv.style.backgroundColor = backgroundColor;
// 设置文字颜色为白色
newButtonDiv.style.color = color;
// 创建内部的 span 元素
const buttonTextSpan = document.createElement('span');
buttonTextSpan.setAttribute('class', 'ad0c98fd');
buttonTextSpan.textContent = textContent;
// 将 span 元素添加到按钮 div 中
newButtonDiv.appendChild(buttonTextSpan);
// 在目标元素后面插入新按钮元素
console.log('增加')
targetElement.parentNode.insertBefore(newButtonDiv, targetElement.nextSibling);
//targetElement.appendChild(newButtonDiv);
}
}
//当前页面最后消息
function lastNews(){
// 定义 XPath 表达式
const xpathExpression = '//*[@id="root"]/div/div[2]/div[2]/div/div[2]/div/div/div[1]/div[last()]/div[@class="ds-markdown ds-markdown--block"]//p';
let flag = false
// 使用 document.evaluate() 方法执行 XPath 查询
const result = document.evaluate(
xpathExpression,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const targetElement = result.singleNodeValue;
//增加空判断
if (targetElement){
console.log(targetElement.textContent)
if (targetElement.textContent === '服务器繁忙,请稍后再试。') {
flag = true;
}
}
return flag;
}
//点击重试
function clickRe(){
const exists = lastNews()
if (exists) {
console.log('检测到服务器繁忙提示,尝试点击按钮...');
// 定义用于定位按钮的 XPath 表达式
const buttonXpathExpression = '//*[@id="root"]/div/div[2]/div[2]/div/div[2]/div/div/div[1]/div[last()]//div[2]/div[@class="ds-icon"]';
// 执行 XPath 查询以获取按钮元素
const buttonResult = document.evaluate(
buttonXpathExpression,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const buttonToClick = buttonResult.singleNodeValue;
if (buttonToClick) {
buttonToClick.click();
console.log('按钮已点击');
var text = '次重试';
showNotification(text)
} else {
console.log('未找到要点击的按钮');
}
}
}
//重试次数
function getreCount(){
//重试次数
const xpathCount = '//*[@id="root"]/div/div[2]/div[2]/div/div[2]/div/div/div[1]/div[last()]/div[@class="ds-flex"]/div[@class="ds-flex"]/div[2]'
const countResult = document.evaluate(
xpathCount,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const countElement = countResult.singleNodeValue;
if (countElement)
{
const count = countElement.textContent;
console.log('重试文本:' + count)
const parts = count.split(' / ');
const numberBeforeSlash = parseInt(parts[0], 10);
console.log('重试次数:' + numberBeforeSlash);
}
}
//检查等待加载函数
function checkDsLoadingElementExists() {
const xpathExpression = '//*[@id="root"]//div[contains(@class,"ds-loading")]';
const result = document.evaluate(
xpathExpression,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const loadingElement = result.singleNodeValue;
return Boolean(loadingElement);
}
// 定时重试函数
function checkAndClick(times) {
console.log('每隔' + times + '毫秒检查一次')
//重试次数
getreCount()
//是否含有加载元素
const exists = checkDsLoadingElementExists();
const exists2 = lastNews()
if (exists) {
console.log('找到了包含 ds - loading 类的元素。');
} else
{
if (exists2){
console.log('未找到了包含 ds - loading 类的元素。');
//点击重试
clickRe();
//增加重试次数
retryCount++;
//显示提示
var text = '已重试' + retryCount + '次'
showNotification(text)
}
}
console.log('')
}
//检查按钮存在
function checkButton(times){
//console.log(times + '毫秒检查一次重试按钮是否存在')
const xpath = '//span[contains(text(),"自动重试")]';
const result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (result.singleNodeValue === null) {
//增加按钮
addButton()
//增加点击事件
retryButtonClick()
}
}
//新对话框点击事件
function clickNewButton(){
var xpath = '//div[text()="开启新对话"]';
var elementResult = document.evaluate(
xpath,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
var clickElement = elementResult.singleNodeValue;
if (clickElement){
clickElement.addEventListener('click', function() {
console.log('开启新对话')
setTimeout(() => {
checkButton(0)
}, 1000);
})
}
}
function clickNewButton2(){
var xpath = '//span[text()="开启新对话"]';
var elementResult = document.evaluate(
xpath,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
var clickElement = elementResult.singleNodeValue;
if (clickElement){
clickElement.addEventListener('click', function() {
console.log('开启新对话2')
setTimeout(() => {
checkButton(0)
}, 1000);
})
}
}
//加载完成
window.onload = function() {
// 页面所有资源加载完成后执行的代码
console.log('页面所有资源加载完成后执行的代码')
//增加按钮
addButton()
//为按钮增加点击事件
retryButtonClick()
//增加提示
addNotification()
//检测按钮变化
//clickNewButton()
//clickNewButton2()
//定时检查重试按钮是否存在
setInterval(() => checkButton(times), times);
}
})();
四、 添加新脚本
在任意网页中打开油猴脚本菜单
保存打开deepseek网页,出现自动重试按钮即可。
五、 使用
点击打开或关闭自动重试,会出现相应提示框
六、 BUG
已知BUG:
1. 在开启重试过程中切换多个对话框可能会无法结束,建议只在当前对话框中使用;
2.在新对话与历史对话框中切换自动重试功能无法正常关闭;
3.在新对话与历史对话框中切换自动重试按钮没有实时改变状态,一直是“打开自动重试”;
对话框切换导致的问题已解决,可能还有我没遇到的情况,后面再慢慢优化吧。
七、 优化日志
1.获取最后消息内容报错
编号:
ERROR-2025021301-LAST_MESSAGE_FETCH
时间:
2025-02-13 17:00:21
图例:
描述:
在尝试获取聊天记录或消息列表中的最后一条消息时,由于未事先判断目标元素是否存在,直接访问该元素导致程序抛出异常。
修改说明:
增加空判断,元素存在再输出。
原代码:
//当前页面最后消息
function lastNews(){
// 定义 XPath 表达式
const xpathExpression = '//*[@id="root"]/div/div[2]/div[2]/div/div[2]/div/div/div[1]/div[last()]/div[@class="ds-markdown ds-markdown--block"]//p';
let flag = false
// 使用 document.evaluate() 方法执行 XPath 查询
const result = document.evaluate(
xpathExpression,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const targetElement = result.singleNodeValue;
console.log(targetElement.textContent)
if (targetElement && targetElement.textContent === '服务器繁忙,请稍后再试。') {
flag = true;
}
return flag;
}
更改代码:
//当前页面最后消息
function lastNews(){
// 定义 XPath 表达式
const xpathExpression = '//*[@id="root"]/div/div[2]/div[2]/div/div[2]/div/div/div[1]/div[last()]/div[@class="ds-markdown ds-markdown--block"]//p';
let flag = false
// 使用 document.evaluate() 方法执行 XPath 查询
const result = document.evaluate(
xpathExpression,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
);
const targetElement = result.singleNodeValue;
//增加空判断
if (targetElement){
console.log(targetElement.textContent)
if (targetElement.textContent === '服务器繁忙,请稍后再试。') {
flag = true;
}
}
return flag;
}
对比:
2.对话框切换无法正常使用
编号:
ERROR - 2025021401 - DIALOG_SWITCH_FAILURE
时间:
2025-02-14 10:45:01
描述:
- 在开启重试后切换对话框,可能会导致重试逻辑对应的元素或状态在新对话框中丢失或者错乱,从而使得重试无法正常结束;
- 在新对话和历史对话框之间进行切换操作时,自动重试功能的关闭变得异常困难。点击关闭自动重试按钮后,系统毫无反应,重试任务会继续按照既定的时间间隔不断执行,不受用户关闭操作的控制;
- 在新对话与历史对话框之间切换的过程中,自动重试按钮的状态未能实时更新。无论实际的自动重试功能是处于开启还是关闭状态,按钮始终显示为 “打开自动重试”,这使得用户无法从按钮状态直观地了解当前重试功能的实际运行情况,容易导致误操作。
修改说明:
- 实时新增按钮后,绑定重试事件;
- 增加全局变量重试状态,首次新增按钮样式与该状态一致;
- 定时器存放至数组,每次开启之前先清除该数组。
代码对比: