Captcha Utility Module

Complete utility functions for captcha system

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/530644/1558606/Captcha%20Utility%20Module.js

// ==UserScript==
// @name         Captcha Utility Module
// @namespace    http://your-namespace.com
// @version      1.0
// @description  Complete utility functions for captcha system
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
// ==/UserScript==

const CaptchaUtils = {
  // █████ 图像处理工具 █████
  Image: {
    /**
     * 将页面元素转换为Base64图像
     * 支持:img、canvas、div背景图
     */
    async elementToBase64(element) {
      try {
        const tagName = element.tagName.toLowerCase();
        let base64 = '';

        if (tagName === 'img') {
          base64 = await this.processImgElement(element);
        } else if (tagName === 'canvas') {
          base64 = this.processCanvasElement(element);
        } else if (tagName === 'div') {
          base64 = await this.processDivElement(element);
        }

        return this.validateImage(base64);
      } catch (error) {
        console.error('图像转换失败:', error);
        return null;
      }
    },

    async processImgElement(img) {
      // 处理跨域图像
      if (this.isCrossOrigin(img)) {
        return await this.fetchCrossOriginImage(img.src);
      }
      return this.canvasToBase64(img);
    },

    processCanvasElement(canvas) {
      return canvas.toDataURL('image/png').split(',')[1];
    },

    async processDivElement(div) {
      const style = getComputedStyle(div);
      const bgImage = style.backgroundImage.match(/url\(["']?(.*?)["']?\)/)[1];
      return bgImage.startsWith('data:') ? 
        bgImage.split(',')[1] : 
        await this.fetchCrossOriginImage(bgImage);
    },

    async fetchCrossOriginImage(url) {
      return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
          url,
          method: 'GET',
          responseType: 'blob',
          onload: res => {
            const reader = new FileReader();
            reader.onload = () => resolve(reader.result.split(',')[1]);
            reader.readAsDataURL(res.response);
          },
          onerror: reject
        });
      });
    },

    canvasToBase64(img) {
      const canvas = document.createElement('canvas');
      canvas.width = img.naturalWidth;
      canvas.height = img.naturalHeight;
      canvas.getContext('2d').drawImage(img, 0, 0);
      return canvas.toDataURL('image/jpeg', 0.8).split(',')[1];
    }
  },

  // █████ 加密安全工具 █████
  Security: {
    generateRequestSignature(params, secret) {
      const sorted = Object.keys(params).sort();
      const signStr = sorted.map(k => `${k}=${params[k]}`).join('&');
      return CryptoJS.HmacSHA256(signStr, secret).toString();
    },

    validateResponse(response, secret) {
      const signature = response.sign;
      delete response.sign;
      const calcSig = this.generateRequestSignature(response, secret);
      return signature === calcSig;
    },

    encryptData(data, key) {
      return CryptoJS.AES.encrypt(JSON.stringify(data), key).toString();
    },

    decryptData(ciphertext, key) {
      const bytes = CryptoJS.AES.decrypt(ciphertext, key);
      return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
    }
  },

  // █████ DOM元素工具 █████
  Element: {
    findAssociatedInput(imageElement) {
      // 在3层父级内查找最近的输入框
      let parent = imageElement.parentElement;
      for (let i = 0; i < 3; i++) {
        const inputs = parent.querySelectorAll('input[type="text"], input:not([type])');
        if (inputs.length > 0) return inputs[inputs.length - 1];
        parent = parent.parentElement;
      }
      return null;
    },

    generateSelector(element) {
      const path = [];
      while (element.parentElement) {
        let selector = element.tagName.toLowerCase();
        if (element.id) {
          selector += `#${element.id}`;
        } else {
          const siblings = element.parentElement.children;
          if (siblings.length > 1) {
            const index = Array.from(siblings).indexOf(element);
            selector += `:nth-child(${index + 1})`;
          }
        }
        path.unshift(selector);
        element = element.parentElement;
      }
      return path.join(' > ');
    },

    triggerValidationEvents(input) {
      ['input', 'change', 'keydown', 'keyup'].forEach(event => {
        input.dispatchEvent(new Event(event, { bubbles: true }));
      });
    }
  },

  // █████ 数学计算工具 █████
  Math: {
    calculateSlideDistance(rawValue, calibration) {
      // 应用校准公式:基础比例 + 偏移量
      const base = rawValue * (calibration.baseRatio || 1);
      const calibrated = base + (calibration.offset || 0);
      return Math.max(calibrated, calibration.minDistance || 0);
    },

    getColorComplexity(imageData) {
      const colorMap = new Map();
      const data = new Uint32Array(imageData.data.buffer);
      
      data.forEach(pixel => {
        colorMap.set(pixel, (colorMap.get(pixel) || 0) + 1);
      });

      // 计算颜色熵值
      let entropy = 0;
      const total = data.length;
      colorMap.forEach(count => {
        const p = count / total;
        entropy -= p * Math.log2(p);
      });

      return entropy;
    },

    calculateTextDensity(element) {
      const rect = element.getBoundingClientRect();
      const area = rect.width * rect.height;
      const textLength = element.textContent.trim().length;
      return textLength / (area || 1);
    }
  },

  // █████ 缓存管理工具 █████
  Cache: {
    getWithCache(key, fetcher, ttl = 300) {
      const cached = GM_getValue(key);
      if (cached && Date.now() < cached.expire) {
        return cached.data;
      }
      
      return fetcher().then(data => {
        GM_setValue(key, {
          data,
          expire: Date.now() + ttl * 1000
        });
        return data;
      });
    },

    memoize(func, resolver = JSON.stringify) {
      const cache = new Map();
      return (...args) => {
        const key = resolver(args);
        if (cache.has(key)) return cache.get(key);
        const result = func(...args);
        cache.set(key, result);
        return result;
      };
    }
  },

  // █████ 错误处理工具 █████
  Error: {
    createErrorLog(error, context) {
      return {
        timestamp: Date.now(),
        message: error.message,
        stack: error.stack,
        context: this.sanitizeContext(context),
        systemInfo: this.getSystemInfo()
      };
    },

    sanitizeContext(context) {
      const sanitized = { ...context };
      // 过滤敏感信息
      ['image', 'token'].forEach(k => {
        if (sanitized[k]) sanitized[k] = '****';
      });
      return sanitized;
    },

    getSystemInfo() {
      return {
        userAgent: navigator.userAgent,
        screen: `${screen.width}x${screen.height}`,
        timestamp: Date.now()
      };
    }
  }
};

/* 使用示例:
// 图像处理
const base64 = await CaptchaUtils.Image.elementToBase64(imgElement);

// 安全签名
const signature = CaptchaUtils.Security.generateRequestSignature(params, secret);

// 查找输入框
const input = CaptchaUtils.Element.findAssociatedInput(captchaImage);

// 计算滑块距离
const distance = CaptchaUtils.Math.calculateSlideDistance(raw, calibrationConfig);
*/

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址