115 助力助手 - 全站通用版(支持幸运5分钟抽奖)

在 115 全站自动完成邀请助力任务,支持许愿树、抽奖等页面

当前为 2025-09-23 提交的版本,查看 最新版本

// ==UserScript==
// @name         115 助力助手 - 全站通用版(支持幸运5分钟抽奖)
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  在 115 全站自动完成邀请助力任务,支持许愿树、抽奖等页面
// @author       You
// @match        https://*.115.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  let isRunning = false;
  let controller = new AbortController();
  let startTime;
  let completedRequests = 0;

  // 防止重复加载
  if (document.getElementById('boost-panel')) return;

  // 检查是否在有效页面显示面板(避免在 iframe 或无操作区域显示)
  function shouldShowPanel() {
    const validPaths = [
      '/home/',
      '/social/games/lucky5',
      '/act/'
    ];
    const currentPath = window.location.pathname;
    return validPaths.some(path => currentPath.includes(path)) || window.location.host.includes('f.115.com');
  }

  if (!shouldShowPanel()) return;

  // 创建侧边栏控制按钮
  const createToggleButton = () => {
    const btn = document.createElement('button');
    btn.id = 'boost-toggle-btn';
    btn.textContent = '助力工具';
    Object.assign(btn.style, {
      position: 'fixed',
      top: '200px',
      right: '0',
      width: '80px',
      height: '40px',
      backgroundColor: '#007bff',
      color: 'white',
      border: 'none',
      borderRadius: '4px 0 0 4px',
      cursor: 'pointer',
      zIndex: '9999',
      fontSize: '14px',
      boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
    });
    btn.addEventListener('click', togglePanel);
    return btn;
  };

  // 创建主面板
  const createPanel = () => {
    const panel = document.createElement('div');
    panel.id = 'boost-panel';
    Object.assign(panel.style, {
      position: 'fixed',
      top: '120px',
      right: '-320px',
      width: '300px',
      height: '600px',
      backgroundColor: 'white',
      border: '1px solid #ddd',
      borderRadius: '8px 0 0 8px',
      boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
      zIndex: '9999',
      transition: 'right 0.3s ease',
      overflow: 'hidden',
      fontFamily: 'Arial, sans-serif',
    });

    panel.innerHTML = `
      <div style="padding: 16px; background: #007bff; color: white; font-weight: bold;">
        115 助力助手
      </div>
      <div style="padding: 16px;">
        <label style="display:block;margin-bottom:8px;font-size:14px;">邀请码列表(每行一个)</label>
        <textarea id="boost-codes" rows="6"
          style="width:100%;font-family:monospace;font-size:12px;padding:8px;
                 border:1px solid #ccc;border-radius:4px;resize:none;"
          placeholder="ABC123&#10;XYZ789"></textarea>

        <div id="action-buttons" style="margin-top:12px;display:flex;gap:8px;">
          <button id="start-boost"
            style="flex:1;background:#28a745;color:white;
                   border:none;padding:10px 0;border-radius:4px;font-size:14px;
                   cursor:pointer;">开始助力</button>
        </div>

        <div id="stats" style="margin-top:12px;font-size:12px;">
          <div>总数: <span id="total">0</span></div>
          <div style="color:green;">成功: <span id="success">0</span></div>
          <div style="color:orange;">重复: <span id="duplicate">0</span></div>
          <div style="color:#666;">速率: <span id="rate">0</span> req/s</div>
        </div>

        <div style="margin-top:16px;font-size:14px;font-weight:bold;">执行日志</div>
        <div id="log-area"
          style="height:200px;overflow-y:auto;border:1px solid #eee;
                 padding:8px;background:#f9f9f9;font-size:12px;">
          <div class="log-item" style="color:#666;">等待启动...</div>
        </div>

        <!-- 加载动画 -->
        <div id="loading" style="display:none;text-align:center;margin-top:8px;">
          <div style="display:inline-block;width:16px;height:16px;border:2px solid #ddd;border-top-color:#007bff;border-radius:50%;animation:spin 1s linear infinite;"></div>
          <span style="margin-left:8px;font-size:12px;color:#666;">处理中...</span>
        </div>
      </div>

      <style>
        @keyframes spin {
          to { transform: rotate(360deg); }
        }
      </style>
    `;
    return panel;
  };

  // 添加日志(带自动滚动到底部)
  function addLog(message, color = 'black') {
    const logArea = document.getElementById('log-area');
    const item = document.createElement('div');
    item.className = 'log-item';
    item.style.color = color;
    item.style.margin = '4px 0';
    item.style.whiteSpace = 'nowrap';
    item.style.overflow = 'hidden';
    item.style.textOverflow = 'ellipsis';
    const time = new Date().toLocaleTimeString();
    item.textContent = `[${time}] ${message}`;
    logArea.appendChild(item);

    // 自动滚动到底部
    requestAnimationFrame(() => {
      logArea.scrollTop = logArea.scrollHeight;
    });
  }

  // 更新统计
  function updateStats(key) {
    const el = document.getElementById(key);
    const val = parseInt(el.textContent || '0');
    el.textContent = val + 1;
  }

  // 重置统计
  function resetStats() {
    document.getElementById('success').textContent = '0';
    document.getElementById('duplicate').textContent = '0';
    document.getElementById('rate').textContent = '0';
  }

  // 更新速率
  function updateRate() {
    if (!startTime) return;
    const elapsed = (Date.now() - startTime) / 1000;
    const rate = elapsed > 0 ? (completedRequests / elapsed).toFixed(1) : '0';
    document.getElementById('rate').textContent = rate;
  }

  // 发送助力请求(带重试机制)
  async function sendBoost(code, retryCount = 3) {
    for (let i = 0; i < retryCount; i++) {
      try {
        const formData = new FormData();
        formData.append('boost_code', code);
        formData.append('source', 'link');

        const response = await fetch('https://act.115.com/api/1.0/web/1.0/invite_boost/accept_invite', {
          method: 'POST',
          body: formData,
          credentials: 'include',
          signal: controller.signal
        });

        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const data = await response.json();
        return data;
      } catch (err) {
        if (err.name === 'AbortError') return { state: 0, message: '请求被取消' };
        if (i === retryCount - 1) {
          return { state: 0, message: `网络错误(已重试${retryCount}次)` };
        }
        // 指数退避
        await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
      }
    }
  }

  // 主要逻辑
  async function startBoost() {
    if (isRunning) return;

    const textarea = document.getElementById('boost-codes');
    const codes = textarea.value
      .split('\n')
      .map(line => line.trim().toUpperCase())
      .filter(line => /^[A-Z0-9]{6}$/.test(line));

    if (codes.length === 0) {
      alert('请输入有效的6位邀请码(A-Z, 0-9),每行一个');
      return;
    }

    isRunning = true;
    controller = new AbortController();
    startTime = Date.now();
    completedRequests = 0;

    // 冻结输入框和原按钮
    textarea.disabled = true;
    const startBtn = document.getElementById('start-boost');
    if (startBtn) startBtn.style.display = 'none';

    // 显示加载动画
    document.getElementById('loading').style.display = 'block';

    // 清除旧的按钮
    const actionButtons = document.getElementById('action-buttons');
    const existingStop = document.getElementById('stop-boost');
    if (existingStop) existingStop.remove();

    // 添加“停止”按钮
    const stopBtn = document.createElement('button');
    stopBtn.id = 'stop-boost';
    stopBtn.textContent = '停止助力';
    stopBtn.style = 'flex:1;background:#dc3545;color:white;border:none;padding:10px 0;border-radius:4px;font-size:14px;cursor:pointer;';
    stopBtn.onclick = () => {
      isRunning = false;
      controller.abort();
      addLog('🛑 用户手动停止助力', 'red');
      finishProcess();
    };
    actionButtons.appendChild(stopBtn);

    // 重置并显示总数
    resetStats();
    document.getElementById('total').textContent = codes.length;

    // 清空日志
    document.getElementById('log-area').innerHTML = '';
    addLog(`共发现 ${codes.length} 个有效邀请码,开始处理...`, 'blue');

    // 逐个处理
    for (const code of codes) {
      if (!isRunning) break;

      addLog(`正在助力: ${code}`, '#007bff');
      const result = await sendBoost(code);

      if (result.state === 1) {
        addLog(`✅ 成功助力: ${result.data.inviter_name || '未知用户'}`, 'green');
        updateStats('success');
      } else if (result.code === 40203004 || result.message.includes('已经')) {
        addLog(`🟡 已助力过: ${code}`, 'orange');
        updateStats('duplicate');
      } else {
        addLog(`❌ 助力失败: ${result.message || '未知错误'}`, 'red');
      }

      completedRequests++;
      updateRate(); // 更新速率

      await new Promise(resolve => {
        if (!isRunning) return resolve();
        setTimeout(resolve, 800);
      });
    }

    finishProcess();
  }

  function finishProcess() {
    isRunning = false;
    const stopBtn = document.getElementById('stop-boost');
    if (stopBtn) stopBtn.remove();

    // 隐藏加载动画
    document.getElementById('loading').style.display = 'none';

    const actionButtons = document.getElementById('action-buttons');
    actionButtons.innerHTML = '';

    const clearBtn = document.createElement('button');
    clearBtn.textContent = '清空';
    clearBtn.style = 'flex:1;background:#6c757d;color:white;border:none;padding:10px 0;border-radius:4px;font-size:14px;cursor:pointer;';
    clearBtn.onclick = clearAll;

    const saveBtn = document.createElement('button');
    saveBtn.textContent = '保存日志';
    saveBtn.style = 'flex:1;background:#17a2b8;color:white;border:none;padding:10px 0;border-radius:4px;font-size:14px;cursor:pointer;';
    saveBtn.onclick = saveLog;

    actionButtons.appendChild(clearBtn);
    actionButtons.appendChild(saveBtn);
  }

  function clearAll() {
    const textarea = document.getElementById('boost-codes');
    textarea.value = '';
    textarea.disabled = false;

    const logArea = document.getElementById('log-area');
    logArea.innerHTML = '<div class="log-item" style="color:#666;">等待启动...</div>';

    document.getElementById('total').textContent = '0';
    resetStats();

    const actionButtons = document.getElementById('action-buttons');
    actionButtons.innerHTML = `
      <button id="start-boost"
        style="flex:1;background:#28a745;color:white;
               border:none;padding:10px 0;border-radius:4px;font-size:14px;
               cursor:pointer;">开始助力</button>
    `;

    document.getElementById('start-boost').addEventListener('click', startBoost, { once: false });
  }

  function saveLog() {
    const logArea = document.getElementById('log-area');
    const logs = Array.from(logArea.children)
      .map(el => el.textContent)
      .join('\n');

    const now = new Date();
    const filename = `115助力助手-${now.getFullYear()}${String(now.getMonth()+1).padStart(2,'0')}${String(now.getDate()).padStart(2,'0')}${String(now.getHours()).padStart(2,'0')}${String(now.getMinutes()).padStart(2,'0')}${String(now.getSeconds()).padStart(2,'0')}.txt`;

    const blob = new Blob([logs], { type: 'text/plain;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
  }

  function togglePanel() {
    const panel = document.getElementById('boost-panel');
    if (!panel) return;
    const currentRight = getComputedStyle(panel).right;
    panel.style.right = currentRight === '0px' ? '-320px' : '0';
  }

  // 初始化函数
  function init() {
    if (document.getElementById('boost-panel')) return;

    const toggleBtn = createToggleButton();
    const panel = createPanel();

    document.body.appendChild(toggleBtn);
    document.body.appendChild(panel);

    document.getElementById('start-boost').addEventListener('click', startBoost, { once: false });
  }

  // 页面加载完成后初始化
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();

QingJ © 2025

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