// ==UserScript==
// @name ChatGPT 降智监控(实时)
// @namespace http://tampermonkey.net/
// @version 2.1
// @description 页面右下角显示ChatGPT PoW算力值,判断降智程度
// @match https://chatgpt.com/*
// @match https://openai.com/*
// @match https://chat.chatgpt.com/*
// @match https://*.openai.com/*
// @grant none
// @license GPT Plus升级 & 降智解决+V:ChatGPT4V
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
let powDifficulty = "N/A"; // 记录最新的算力值
let containerInitialized = false; // 用于避免重复创建浮动面板
// 1. 拦截 fetch,用于获取 PoW difficulty
const originalFetch = window.fetch;
window.fetch = async function(resource, options) {
const response = await originalFetch(resource, options);
const url = typeof resource === "string" ? resource : resource?.url;
// 如果是 /sentinel/chat-requirements 接口,则更新算力值
if (url && url.includes("/sentinel/chat-requirements") && options?.method === "POST") {
try {
const clonedResponse = response.clone();
clonedResponse.json().then(data => {
powDifficulty = data?.proofofwork?.difficulty || "N/A";
updatePowText(); // 更新显示的算力值
});
} catch (error) {
console.error("获取 PoW 失败:", error);
}
}
return response;
};
// 2. 主动请求一次 /sentinel/chat-requirements
setTimeout(() => {
fetchPowValue();
}, 2000);
function fetchPowValue() {
fetch("https://chatgpt.com/sentinel/chat-requirements", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({})
})
.then(res => res.json())
.then(data => {
powDifficulty = data?.proofofwork?.difficulty || "N/A";
updatePowText();
})
.catch(err => {
console.error("主动请求 PoW 失败:", err);
});
}
// 3. 初始化 UI 面板
function initPowUI() {
if (containerInitialized) return;
containerInitialized = true;
// 3.1 毛玻璃外框
const floatContainer = document.createElement('div');
floatContainer.id = "pow-float-container";
Object.assign(floatContainer.style, {
position: 'fixed',
bottom: '-2.5px',
right: '25px',
zIndex: 0,
padding: '12px 16px',
borderRadius: '12px',
backdropFilter: 'none',
backgroundColor: 'transparent',
border: 'none',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '8px',
fontFamily: 'Roboto, Arial, Helvetica, sans-serif',
color: '#333',
// 让外层容器不拦截点击事件
pointerEvents: 'none'
});
// 3.2 让真正需要交互的区域使用 pointer-events: auto
const powWrapper = document.createElement('div');
powWrapper.id = 'pow-wrapper';
powWrapper.style.position = 'relative';
powWrapper.style.pointerEvents = 'auto'; // 允许点击/悬浮
// 3.3 显示“波动图标 + 文本”的容器
const powText = document.createElement('div');
powText.id = 'pow-text';
powText.style.fontWeight = 'bold';
powText.style.display = 'inline-flex';
powText.style.alignItems = 'center';
powText.style.gap = '6px';
powText.style.whiteSpace = 'nowrap';
// 3.4 自定义 Tooltip
const tooltip = document.createElement('div');
tooltip.id = 'custom-tooltip';
// 初始隐藏 + 定位
tooltip.style.display = 'none';
tooltip.style.position = 'absolute';
tooltip.style.bottom = '100%';
tooltip.style.left = '50%';
tooltip.style.transform = 'translate(-50%, -8px)';
// 外观样式
tooltip.style.background = '#333';
tooltip.style.color = '#fff';
tooltip.style.padding = '8px 12px';
tooltip.style.borderRadius = '6px';
tooltip.style.fontSize = '12px';
tooltip.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)';
// 保持单行不自动换行,但用多个 <div> 实现“不同颜色之间换行”
tooltip.style.whiteSpace = 'nowrap';
// 多行提示(每条颜色独占一行)
tooltip.innerHTML = `
<div id="line-green" style="color: #00FF00;">绿色:全模型智商在线,且4o可答对推理问题</div>
<div id="line-yellow" style="color: #FFFF00;">黄色:降智概率高,且未降智也不如绿色(4o会答错推理问题)</div>
<div id="line-red" style="color: #FF0000;">红色:全模型智商下线,4o无法画图且会答错推理问题↓<br>带小数点的数字比大小都会答错,o系列全模型短耗时、伪推理</div>
`;
// 3.5 拼装
powWrapper.appendChild(powText);
powWrapper.appendChild(tooltip);
floatContainer.appendChild(powWrapper);
document.body.appendChild(floatContainer);
// 3.6 悬浮事件:显示 / 隐藏 tooltip,并自动贴右边
powWrapper.addEventListener('mouseenter', () => {
// 先显示,才能测量真实宽度
tooltip.style.display = 'block';
// 下一帧再测量
requestAnimationFrame(() => {
const rect = tooltip.getBoundingClientRect();
const margin = 8; // 与右边保持一点间距
// 如果 tooltip 右边超出窗口,就往左挪
if (rect.right > window.innerWidth - margin) {
const overflowRight = rect.right - (window.innerWidth - margin);
tooltip.style.transform = `translate(calc(-50% - ${overflowRight}px), -8px)`;
}
});
});
powWrapper.addEventListener('mouseleave', () => {
tooltip.style.display = 'none';
// 还原初始 transform
tooltip.style.transform = 'translate(-50%, -8px)';
});
}
// 4. 更新算力值文本(脉冲扩展图标 + 同色文字)
function updatePowText() {
const powText = document.getElementById('pow-text');
if (!powText) return;
const { color, label, trimmedHex } = parsePowDifficulty(powDifficulty);
// 生成脉冲扩展图标
const iconHTML = getPulseExpandIcon(color);
// 同色文字
powText.innerHTML = `
${iconHTML}
<span style="color:${color};">
${trimmedHex} ${label}
</span>
`;
}
// 5. 监听 DOM 变化, 防止面板丢失
initPowUI();
const observer = new MutationObserver(() => {
const container = document.getElementById("pow-float-container");
if (!container) {
containerInitialized = false;
initPowUI();
}
});
observer.observe(document.documentElement, { childList: true, subtree: true });
// ========== 辅助函数:解析 difficulty 并返回三档信息 ==========
function parsePowDifficulty(difficulty) {
if (difficulty === "N/A") {
return {
color: "#999",
label: "N/A",
trimmedHex: "N/A"
};
}
let hex = difficulty.replace(/^0x/i, "");
let trimmed = hex.replace(/^0+/, "");
if (!trimmed) trimmed = "0";
const length = trimmed.length;
let color, label;
if (length >= 5) {
color = "#00FF00"; // 绿色
label = "智商Top";
} else if (length === 4) {
color = "#FFFF00"; // 黄色
label = "高风险";
} else {
color = "#FF0000"; // 红色
label = "强降智";
}
return { color, label, trimmedHex: trimmed };
}
// ========== 辅助函数:生成脉冲扩展图标 ==========
function getPulseExpandIcon(color) {
return `
<svg width="16" height="16" viewBox="0 0 64 64" style="vertical-align:middle;">
<circle cx="32" cy="32" r="4" fill="${color}">
<animate attributeName="r" dur="1.5s" values="4;10;4" repeatCount="indefinite"/>
</circle>
<circle cx="32" cy="32" r="10" fill="none" stroke="${color}" stroke-width="2">
<animate attributeName="r" dur="2s" values="10;20;10" repeatCount="indefinite"/>
</circle>
<circle cx="32" cy="32" r="20" fill="none" stroke="${color}" stroke-width="2">
<animate attributeName="r" dur="3s" values="20;30;20" repeatCount="indefinite"/>
</circle>
</svg>
`;
}
})();