Greasy Fork

Greasy Fork is available in English.

ChatGPT降级检测

由于 ChatGPT 会对某些 ip 进行无提示的服务降级,此脚本用于检测你的 ip 在 ChatGPT 数据库中的风险等级。

当前为 2024-11-06 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         ChatGPT降级检测
// @namespace    https://github.com/KoriIku/chatgpt-degrade-checker
// @homepage     https://github.com/KoriIku/chatgpt-degrade-checker
// @author       KoriIku
// @version      1.8
// @description  由于 ChatGPT 会对某些 ip 进行无提示的服务降级,此脚本用于检测你的 ip 在 ChatGPT 数据库中的风险等级。
// @match        *://chatgpt.com/*
// @grant        none
// @license AGPLv3
// ==/UserScript==

(function() {
    'use strict';

    // 创建显示框
    const displayBox = document.createElement('div');
    displayBox.style.position = 'fixed';
    displayBox.style.top = '50%';
    displayBox.style.right = '20px';
    displayBox.style.transform = 'translateY(-50%)';
    displayBox.style.width = '220px';
    displayBox.style.padding = '10px';
    displayBox.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
    displayBox.style.color = '#fff';
    displayBox.style.fontSize = '14px';
    displayBox.style.borderRadius = '8px';
    displayBox.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)';
    displayBox.style.zIndex = '10000';
    displayBox.style.transition = 'all 0.3s ease';
    displayBox.style.display = 'none';

    displayBox.innerHTML = `
        <div style="margin-bottom: 10px;">
            <strong>PoW 信息</strong>
        </div>
        <div id="content">
            PoW难度: <span id="difficulty">N/A</span><span id="difficulty-level" style="margin-left: 3px"></span>
            <span id="difficulty-tooltip" style="
                cursor: pointer;
                color: #fff;
                font-size: 12px;
                display: inline-block;
                width: 14px;
                height: 14px;
                line-height: 14px;
                text-align: center;
                border-radius: 50%;
                border: 1px solid #fff;
                margin-left: 3px;
            ">?</span><br>
            IP质量: <span id="ip-quality">N/A</span><br>
            用户类型: <span id="persona">N/A</span>
        </div>`;
    document.body.appendChild(displayBox);

    // 创建收缩状态的指示器
    const collapsedIndicator = document.createElement('div');
    collapsedIndicator.style.position = 'fixed';
    collapsedIndicator.style.top = '50%';
    collapsedIndicator.style.right = '20px';
    collapsedIndicator.style.transform = 'translateY(-50%)';
    collapsedIndicator.style.width = '32px';
    collapsedIndicator.style.height = '32px';
    collapsedIndicator.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
    collapsedIndicator.style.borderRadius = '50%';
    collapsedIndicator.style.cursor = 'pointer';
    collapsedIndicator.style.zIndex = '10000';
    collapsedIndicator.style.padding = '4px';
    collapsedIndicator.style.display = 'flex';
    collapsedIndicator.style.alignItems = 'center';
    collapsedIndicator.style.justifyContent = 'center';
    collapsedIndicator.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
    collapsedIndicator.style.transition = 'all 0.3s ease';

    // 创建内部的difficulty指示球
    collapsedIndicator.innerHTML = `
        <div id="difficulty-indicator" style="
            width: 24px;
            height: 24px;
            border-radius: 50%;
            background-color: #888;
            transition: all 0.3s ease;
            box-shadow: 0 0 8px rgba(255, 255, 255, 0.3) inset;
        "></div>
    `;
    document.body.appendChild(collapsedIndicator);

    // 鼠标悬停事件
    collapsedIndicator.addEventListener('mouseenter', function() {
        displayBox.style.display = 'block';
        collapsedIndicator.style.opacity = '0';
    });

    displayBox.addEventListener('mouseleave', function() {
        displayBox.style.display = 'none';
        collapsedIndicator.style.opacity = '1';
    });

    // 创建提示框
    const tooltip = document.createElement('div');
    tooltip.id = 'tooltip';
    tooltip.innerText = '这个值越小,代表PoW难度越高,ChatGPT认为你的IP风险越高。';
    tooltip.style.position = 'fixed';
    tooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
    tooltip.style.color = '#fff';
    tooltip.style.padding = '8px 12px';
    tooltip.style.borderRadius = '5px';
    tooltip.style.fontSize = '12px';
    tooltip.style.visibility = 'hidden';
    tooltip.style.zIndex = '10001';
    tooltip.style.width = '240px';
    tooltip.style.lineHeight = '1.4';
    tooltip.style.pointerEvents = 'none';
    document.body.appendChild(tooltip);

    // 显示提示
    document.getElementById('difficulty-tooltip').addEventListener('mouseenter', function(event) {
        tooltip.style.visibility = 'visible';

        // 计算提示框位置,避免超出屏幕和被鼠标遮挡
        const tooltipWidth = 240; // 提示框宽度
        const windowWidth = window.innerWidth;
        const mouseX = event.clientX;
        const mouseY = event.clientY;

        // 计算左侧位置,确保不会超出屏幕
        let leftPosition = mouseX - tooltipWidth - 10; // 默认显示在鼠标左侧
        if (leftPosition < 10) { // 如果太靠左,就显示在鼠标右侧
            leftPosition = mouseX + 20;
        }

        // 计算顶部位置,显示在鼠标上方
        let topPosition = mouseY - 40;

        tooltip.style.left = `${leftPosition}px`;
        tooltip.style.top = `${topPosition}px`;
    });

    // 隐藏提示
    document.getElementById('difficulty-tooltip').addEventListener('mouseleave', function() {
        tooltip.style.visibility = 'hidden';
    });

    // 更新difficulty指示器
    function updateDifficultyIndicator(difficulty) {
        const indicator = document.getElementById('difficulty-indicator');
        const difficultyLevel = document.getElementById('difficulty-level');
        const ipQuality = document.getElementById('ip-quality');

        if (difficulty === 'N/A') {
            indicator.style.backgroundColor = '#888';
            indicator.style.transform = 'scale(0.8)';
            difficultyLevel.innerText = '';
            ipQuality.innerHTML = 'N/A';
            return;
        }

        // 去掉前导的0x和前导0,只保留有效的十六进制位数
        const cleanDifficulty = difficulty.replace('0x', '').replace(/^0+/, '');

        // 获取实际的十六进制字符串的长度
        const hexLength = cleanDifficulty.length;

        let color, scale, level, textColor, qualityText;

        if (hexLength <= 2) { // 2位及以下为高风险 - 红色
            color = '#F44336';
            textColor = '#ff6b6b';
            scale = 1.2;
            level = '(困难)';
            qualityText = '高风险';
        } else if (hexLength === 3) { // 3位为中等 - 黄色
            color = '#FFC107';
            textColor = '#ffd700';
            scale = 1;
            level = '(中等)';
            qualityText = '中等';
        } else if (hexLength === 4) { // 4位为良好 - 浅绿色
            color = '#8BC34A';
            textColor = '#9acd32';
            scale = 0.9;
            level = '(简单)';
            qualityText = '良好';
        } else { // 5位及以上为优秀 - 深绿色
            color = '#4CAF50';
            textColor = '#98fb98';
            scale = 0.8;
            level = '(极易)';
            qualityText = '优秀';
        }

        indicator.style.backgroundColor = color;
        indicator.style.transform = `scale(${scale})`;
        indicator.style.boxShadow = `0 0 12px ${color}`;

        // 更新难度等级文本
        difficultyLevel.innerHTML = `<span style="color: ${textColor}">${level}</span>`;
        
        // 更新 IP 质量文本
        ipQuality.innerHTML = `<span style="color: ${textColor}">${qualityText}</span>`;
    }

    // 拦截 fetch 请求
    const originalFetch = window.fetch;
    window.fetch = async function(resource, options) {
        const response = await originalFetch(resource, options);

        if (resource.includes('/backend-api/sentinel/chat-requirements') && options.method === 'POST') {
            const clonedResponse = response.clone();
            clonedResponse.json().then(data => {
                const difficulty = data.proofofwork ? data.proofofwork.difficulty : 'N/A';
                const persona = data.persona || 'N/A';
                document.getElementById('difficulty').innerText = difficulty;
                document.getElementById('persona').innerText = persona;
                updateDifficultyIndicator(difficulty);
            }).catch(e => console.error('解析响应时出错:', e));
        }
        return response;
    };
})();