Smol - Veyra Farm Extension

Automated energy farming with draggable HUD, Farming Mode, Auto Login and Auto Resume farm - integrates with Smol Veyra Script Extension Hub for sidebar control and image blocking optimization.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Smol - Veyra Farm Extension
// @namespace    http://violentmonkey.github.io/smol-veyra-farm-extension
// @version      1.3
// @author       Smol
// @description  Automated energy farming with draggable HUD, Farming Mode, Auto Login and Auto Resume farm - integrates with Smol Veyra Script Extension Hub for sidebar control and image blocking optimization.
// @match        https://demonicscans.org/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=demonicscans.org
// @grant        none
// @license      MIT License
// ==/UserScript==

(function() {
  'use strict';

  // edit this email and password value and set autologin to true if you use this script as standalone
  var YOUR_EMAIL = 'WRITE_YOUR_EMAIL_HERE'; // change into your actual email
  var YOUR_PASSWORD = 'WRITE_YOUR_PASSWORD_HERE'; // change into your actual password
  var AUTO_LOGIN = false; // change to true if you wish to enable autologin feature

  // Initialize farming localStorage if not exists
  if (localStorage.getItem('veyra-farming-automation') === null) {
    localStorage.setItem('veyra-farming-automation', 'false');
  }
  if (localStorage.getItem('minus-energy-cap') === null) {
    localStorage.setItem('minus-energy-cap', '30');
  }
  if (localStorage.getItem('target-farming-energy') === null) {
    localStorage.setItem('target-farming-energy', '150');
  }
  if (localStorage.getItem('farming-mode') === null) {
    localStorage.setItem('farming-mode', 'energy-cap');
  }
  if (localStorage.getItem('smol-script-expanded') === null) {
    localStorage.setItem('smol-script-expanded', 'false');
  }
  if (localStorage.getItem('smol-script-farm-enabled') === null) {
    localStorage.setItem('smol-script-farm-enabled', 'true');
  }
  if (localStorage.getItem('smol-script-autologin-enabled') === null) {
    localStorage.setItem('smol-script-autologin-enabled', AUTO_LOGIN.toString());
  }

  // Check if image blocking is available
  const hasImageBlocking = typeof window.smolImageBlockEnabled !== 'undefined';

  // Initialize extension
  if (window.location.hostname === 'demonicscans.org') {
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', () => setTimeout(initAsExtension, 250));
    } else {
      setTimeout(initAsExtension, 250);
    }
  }

  function initAsExtension() {
    const smolScript = window.smolScript ? window.smolScript : undefined
    const gameSidebar = document.querySelector('#game-sidebar');
    const scriptExtension = document.getElementById('script-extension');
        
    if (smolScript && gameSidebar && scriptExtension) {
      const scriptExpanded = document.getElementById('script-expanded');
      if (scriptExpanded && !document.getElementById('farm-script-toggle')) {
        const farmDiv = document.createElement('div');
        farmDiv.innerHTML = `
          <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
            <div style="font-size: 12px; color: #888;"><span>🌾 Farm Script</span></div>
            <input type="checkbox" id="farm-script-toggle" ${localStorage.getItem('smol-script-farm-enabled') === 'true' ? 'checked' : ''}></input>
          </div>
        `;
        scriptExpanded.appendChild(farmDiv);
        
        document.getElementById('farm-script-toggle').addEventListener('change', (e) => {
          localStorage.setItem('smol-script-farm-enabled', e.target.checked.toString());
          window.location.reload();
        });
      }
    }
    
    if (localStorage.getItem('smol-script-farm-enabled') === 'true' && (!window.location.pathname.includes('.php') || window.location.pathname === '/index.php')) {
      initFarmingExtension();
    } else if (localStorage.getItem('smol-script-farm-enabled') === 'true' && window.location.pathname === '/signin.php') {
      autoLogin()
    }
  }

  function autoLogin() {
    if (!window.location.href.includes("signin.php")) return false;

    const isEnabled = localStorage.getItem('smol-script-autologin-enabled') === 'true';
    if (!isEnabled) return false;

    const email = YOUR_EMAIL;
    const password = YOUR_PASSWORD;

    if (!email || !password) return false;

    const emailInput = document.evaluate('//*[@id="login-container"]/form/input[1]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    const passwordInput = document.evaluate('//*[@id="login-container"]/form/input[2]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    const loginBtn = document.evaluate('//*[@id="login-container"]/form/input[3]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

    if (emailInput && passwordInput && loginBtn) {
      emailInput.value = email;
      passwordInput.value = password;
      loginBtn.click();
      return true;
    }
    return false;
  }

  function initFarmingExtension() {
    createFarmingHUD();

    if (getFarmingAutomation()) {
      setTimeout(runFarming, 1000);
    }
  }

  function createFarmingHUD() {
    if (document.getElementById('farming-hud-main')) return;

    const hud = document.createElement('div');
    hud.id = 'farming-hud-main';
    hud.className = 'veyra-glass';
    
    // Load saved position or use default
    const savedPosition = sessionStorage.getItem('farm-hud-position');
    if (savedPosition) {
      const pos = JSON.parse(savedPosition);
      hud.style.cssText = `
        position: fixed;
        left: ${pos.left}px;
        top: ${pos.top}px;
        z-index: 2147483647;
      `;
    } else {
      hud.style.cssText = `
        position: fixed;
        bottom: 16px;
        right: 16px;
        z-index: 2147483647;
      `;
    }

    document.body.appendChild(hud);
    makeDraggable(hud);
    updateFarmingHUD();
  }

  function makeDraggable(element) {
    let isDragging = false;
    let startX, startY, startLeft, startTop;

    element.addEventListener('mousedown', (e) => {
      if (e.target.classList.contains('drag-handle')) {
        isDragging = true;
        startX = e.clientX;
        startY = e.clientY;
        const rect = element.getBoundingClientRect();
        startLeft = rect.left;
        startTop = rect.top;
        element.style.cursor = 'grabbing';
        e.preventDefault();
      }
    });

    document.addEventListener('mousemove', (e) => {
      if (isDragging) {
        const deltaX = e.clientX - startX;
        const deltaY = e.clientY - startY;
        const newLeft = startLeft + deltaX;
        const newTop = startTop + deltaY;
        element.style.left = newLeft + 'px';
        element.style.top = newTop + 'px';
        element.style.right = 'auto';
        element.style.bottom = 'auto';
      }
    });

    document.addEventListener('mouseup', () => {
      if (isDragging) {
        isDragging = false;
        element.style.cursor = 'default';
        // Save position to sessionStorage
        const rect = element.getBoundingClientRect();
        sessionStorage.setItem('farm-hud-position', JSON.stringify({
          left: rect.left,
          top: rect.top
        }));
      }
    });
  }

  function updateFarmingHUD() {
    const hud = document.getElementById('farming-hud-main');
    if (!hud) return;

    const isRunning = getFarmingAutomation();
    const farmingMode = localStorage.getItem('farming-mode') || 'energy-cap';
    const energyCap = localStorage.getItem('minus-energy-cap') || '30';
    const energyTarget = localStorage.getItem('target-farming-energy') || '150';

    // Get current stamina and farm values
    const stamina = getStamina();
    const farm = getFarm();
    const staminaText = stamina ? `${stamina.current}/${stamina.max}` : '0/0';
    const farmText = farm ? `${farm.current}/${farm.max}` : '0/0';

    let statusText = 'Idle';
    if (isRunning) {
      statusText = 'Farming Energy';
    }

    const content = `
      <div class="veyra-header">
        <div class="veyra-title">Veyra — Farm</div>
        <div style="display: flex; align-items: center; gap: 8px;">
          <span class="veyra-pill ${isRunning ? 'pill-live' : 'pill-stop'}">${isRunning ? 'RUNNING' : 'PAUSED'}</span>
          <div class="drag-handle" style="width: 16px; height: 16px; background: rgba(255,255,255,0.3); border-radius: 3px; cursor: grab; display: flex; align-items: center; justify-content: center; font-size: 10px;">⋮⋮</div>
        </div>
      </div>
      <div class="veyra-body">
        <div class="veyra-row">
          <div>Mode</div>
          <div><select id="farm-mode-select" class="veyra-select" ${isRunning ? 'disabled' : ''}>
            <option value="energy-cap" ${farmingMode === 'energy-cap' ? 'selected' : ''}>Energy Cap</option>
            <option value="energy-target" ${farmingMode === 'energy-target' ? 'selected' : ''}>Energy Target</option>
          </select></div>
        </div>
        <div class="veyra-row" id="energy-cap-row" style="display: ${farmingMode === 'energy-cap' ? 'flex' : 'none'}">
          <div>Energy Cap</div>
          <div><input id="farm-energy-cap" type="number" class="veyra-input" min="0" value="${energyCap}" ${isRunning ? 'disabled' : ''}></div>
        </div>
        <div class="veyra-row" id="energy-target-row" style="display: ${farmingMode === 'energy-target' ? 'flex' : 'none'}">
          <div>Energy Target</div>
          <div><input id="farm-energy-target" type="number" class="veyra-input" min="0" value="${energyTarget}" ${isRunning ? 'disabled' : ''}></div>
        </div>
        <div class="veyra-row">
          <div>Auto Farm</div>
          <div><button id="farm-hud-toggle" class="veyra-btn">${isRunning ? 'Stop' : 'Start'}</button></div>
        </div>
        ${hasImageBlocking ? `<div class="veyra-row">
          <div>Block Images</div>
          <div><input id="farm-image-block" type="checkbox" ${localStorage.getItem('smol-script-noimage') === 'true' ? 'checked' : ''}></div>
        </div>` : ''}
        <div class="veyra-divider"></div>
        <div class="veyra-row">
          <div>Stamina</div>
          <div style="color:var(--glass-muted)">${staminaText}</div>
        </div>
        <div class="veyra-row">
          <div>Farm</div>
          <div style="color:var(--glass-muted)">${farmText}</div>
        </div>
        <div class="veyra-divider"></div>
        <div class="veyra-row">
          <div>Status</div>
          <div>${statusText}</div>
        </div>
      </div>
    `;

    hud.innerHTML = content;

    // Setup event listeners
    setTimeout(() => {
      const toggleBtn = document.getElementById('farm-hud-toggle');
      const modeSelect = document.getElementById('farm-mode-select');
      const energyCapInput = document.getElementById('farm-energy-cap');
      const energyTargetInput = document.getElementById('farm-energy-target');
      const energyCapRow = document.getElementById('energy-cap-row');
      const energyTargetRow = document.getElementById('energy-target-row');

      if (toggleBtn && !toggleBtn.hasAttribute('data-listener-added')) {
        toggleBtn.addEventListener('click', toggleFarmingFromHUD);
        toggleBtn.setAttribute('data-listener-added', 'true');
      }

      if (modeSelect && !modeSelect.hasAttribute('data-listener-added')) {
        modeSelect.addEventListener('change', (e) => {
          localStorage.setItem('farming-mode', e.target.value);
          energyCapRow.style.display = e.target.value === 'energy-cap' ? 'flex' : 'none';
          energyTargetRow.style.display = e.target.value === 'energy-target' ? 'flex' : 'none';
        });
        modeSelect.setAttribute('data-listener-added', 'true');
      }

      if (energyCapInput && !energyCapInput.hasAttribute('data-listener-added')) {
        energyCapInput.addEventListener('change', (e) => {
          const value = Math.max(0, parseInt(e.target.value) || 30);
          localStorage.setItem('minus-energy-cap', value.toString());
          e.target.value = value;
        });
        energyCapInput.setAttribute('data-listener-added', 'true');
      }

      if (energyTargetInput && !energyTargetInput.hasAttribute('data-listener-added')) {
        energyTargetInput.addEventListener('change', (e) => {
          const value = Math.max(0, parseInt(e.target.value) || 150);
          localStorage.setItem('target-farming-energy', value.toString());
          e.target.value = value;
        });
        energyTargetInput.setAttribute('data-listener-added', 'true');
      }

      const imageBlockCheckbox = document.getElementById('farm-image-block');
      if (imageBlockCheckbox && !imageBlockCheckbox.hasAttribute('data-listener-added')) {
        imageBlockCheckbox.addEventListener('change', (e) => {
          localStorage.setItem('smol-script-noimage', e.target.checked.toString());
          window.dispatchEvent(new StorageEvent('storage', {
            key: 'smol-script-noimage',
            newValue: e.target.checked.toString()
          }));
        });
        imageBlockCheckbox.setAttribute('data-listener-added', 'true');
      }
    }, 100);
  }

  function toggleFarmingFromHUD() {
    const newRunningState = !getFarmingAutomation();
    setFarmingAutomation(newRunningState);
    
    // Control image blocking during farming
    if (hasImageBlocking) {
      if (newRunningState) {
        localStorage.setItem('smol-script-noimage', 'true');
      } else {
        // Restore previous state when stopping
        const checkbox = document.getElementById('farm-image-block');
        if (checkbox) {
          localStorage.setItem('smol-script-noimage', checkbox.checked.toString());
        }
      }
      window.dispatchEvent(new StorageEvent('storage', {
        key: 'smol-script-noimage',
        newValue: localStorage.getItem('smol-script-noimage')
      }));
    }
    
    updateFarmingHUD();

    if (newRunningState) {
      showNotification('Starting farming automation...', 'success');
      runFarming();
    } else {
      showNotification('Farming automation stopped', 'info');
    }
  }

  // Farming automation functions
  function getFarmingAutomation() {
    return localStorage.getItem('veyra-farming-automation') === 'true';
  }

  function setFarmingAutomation(value) {
    localStorage.setItem('veyra-farming-automation', value.toString());
  }

  function getStamina() {
    const staminaEl = document.evaluate(
      '//*[@id="discuscontainer"]/div[1]/div[1]/div[2]/span[1]/span',
      document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
    ).singleNodeValue;
    if (!staminaEl) return null;
    const [current, max] = staminaEl.innerText.split('/').map(s => parseInt(s.trim()));
    return { current, max };
  }

  function getFarm() {
    const farmEl = document.evaluate(
      '//*[@id="discuscontainer"]/div[1]/div[1]/div[2]/span[2]/span',
      document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
    ).singleNodeValue;
    if (!farmEl) return null;
    const [current, max] = farmEl.innerText.split('/').map(s => parseInt(s.replace(/,/g, '').trim(), 10));
    return { current, max };
  }

  function checkUserLogin(bypass = false) {
    let userInfo, loginContainer;
    
    if (bypass) {
      userInfo = false;
      loginContainer = true;
    } else {
      userInfo = document.querySelector('.comments-section .user-info');
      loginContainer = document.querySelector('#login-container');
    }

    if ((!userInfo || loginContainer) && !window.location.href.includes("signin.php")) {
      console.log('User not logged in, clearing cookies and redirecting to login');
      document.cookie.split(";").forEach(function(c) {
        document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/");
      });
      sessionStorage.setItem("veyra_resume_page", window.location.href);
      window.location.href = "https://demonicscans.org/signin.php";
      return false;
    }
    return true;
  }

  function checkFarmingLimits() {
    const stamina = getStamina();
    const farm = getFarm();
    if (!stamina || !farm) return false;

    if (!getFarmingAutomation()) return false;

    if (!checkUserLogin()) return false;

    const farmingMode = localStorage.getItem('farming-mode') || 'energy-cap';

    if (farmingMode === 'energy-cap') {
      const minusEnergyCap = parseInt(localStorage.getItem('minus-energy-cap')) || 30;
      if (stamina.max - stamina.current <= minusEnergyCap) {
        setFarmingAutomation(false);
        updateFarmingHUD();
        return false;
      }
    } else {
      const targetEnergy = parseInt(localStorage.getItem('target-farming-energy')) || 150;
      if (stamina.current >= targetEnergy) {
        setFarmingAutomation(false);
        updateFarmingHUD();
        return false;
      }
    }

    if (farm.current >= farm.max) {
      setFarmingAutomation(false);
      startFarming();
      return false;
    }

    return true;
  }

  function clickReaction() {
    const reaction = document.evaluate(
      '/html/body/div[5]/center/div/div[1]/div[3]/div[1]',
      document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
    ).singleNodeValue;

    if (reaction) {
      reaction.scrollIntoView();
      reaction.click();
      console.log('✅ Clicked reaction on', window.location.href);
      return true;
    } else {
      console.log('⚠️ Reaction not found on', window.location.href);
      console.log('User not logged in, clearing cookies and redirecting to login');
      document.cookie.split(";").forEach(function(c) {
        document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/");
      });
      sessionStorage.setItem("veyra_resume_page", window.location.href);
      window.location.href = "https://demonicscans.org/signin.php";
      return false;
    }
  }

  function goNextPage() {
    const nextBtn = document.querySelector('body > div.chapter-info > div > a.nextchap');

    if (nextBtn) {
      console.log('➡️ Navigating to next chapter:', nextBtn.href);
      window.location.href = nextBtn.href;
    } else {
      console.log('❌ Next button not found, picking new manga');
      startFarming();
    }
  }

  function startFarming() {
    if (!getFarmingAutomation()) return;
    window.location.href = 'https://demonicscans.org';
  }

  function pickRandomManga() {
    const owlItems = document.querySelectorAll('.owl-item .owl-element a');
    if (owlItems.length === 0) {
      setTimeout(pickRandomManga, 1000);
      return;
    }

    const randomIndex = Math.floor(Math.random() * owlItems.length);
    const randomManga = owlItems[randomIndex];
    console.log('Picked random manga:', randomManga.href);
    window.location.href = randomManga.href;
  }

  function startFromLastChapter() {
    const chapters = document.querySelectorAll('#chapters-list > li > a');
    if (chapters.length === 0) {
      setTimeout(startFromLastChapter, 1000);
      return;
    }

    const lastChapter = chapters[chapters.length - 1];
    console.log('Starting from last chapter:', lastChapter.href);
    window.location.href = lastChapter.href;
  }

  function runFarming() {
    updateFarmingHUD();

    if (!getFarmingAutomation()) return;

    const currentPath = window.location.pathname;
    const currentUrl = window.location.href;

    // If automation starts from homepage or index.php - pick random manga
    if (currentPath === '/' || currentPath === '/index.php') {
      pickRandomManga();
      return;
    }

    // If automation starts from manga page - go to last chapter
    if (currentPath.includes('/manga/')) {
      startFromLastChapter();
      return;
    }

    // If automation starts from chapter page - check limits and continue
    if (currentPath.includes('/chapter/')) {
      if (!checkFarmingLimits()) {
        if (!checkUserLogin()) return false
        setTimeout(runFarming, 5000);
        return;
      }

      const reactSuccess = clickReaction();
      if (reactSuccess) {
        setTimeout(() => {
          goNextPage();
        }, 1500);
      } else {
        if (!checkUserLogin()) return false
      }
    }
  }

  function showNotification(msg, type = 'success') {
    let note = document.getElementById('farm-notification');
    if (!note) {
      note = document.createElement('div');
      note.id = 'farm-notification';
      note.style.cssText = `position: fixed; top: 90px; right: 20px; background: #2ecc71; color: white; padding: 12px 20px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); font-size: 15px; display: none; z-index: 9999;`;
      document.body.appendChild(note);
    }

    let emoji = '';
    if (type === 'success') emoji = '✅ ';
    else if (type === 'error') emoji = '❌ ';
    else if (type === 'warning') emoji = '⚠️ ';
    else if (type === 'info') emoji = 'ℹ️ ';

    note.innerHTML = emoji + msg;

    if (type === 'error') {
      note.style.background = 'linear-gradient(135deg, #e74c3c, #c0392b)';
    } else if (type === 'warning') {
      note.style.background = 'linear-gradient(135deg, #f39c12, #e67e22)';
    } else if (type === 'info') {
      note.style.background = 'linear-gradient(135deg, #3498db, #2980b9)';
    } else {
      note.style.background = 'linear-gradient(135deg, #2ecc71, #27ae60)';
    }

    note.style.display = 'block';
    setTimeout(() => {
      note.style.display = 'none';
    }, 4000);
  }

  // Add CSS styles
  const style = document.createElement('style');
  style.textContent = `
    :root{
      --glass-bg: rgba(255,255,255,0.06);
      --glass-border: rgba(255,255,255,0.08);
      --glass-text: #e6eef8;
      --glass-muted: #b6c2d3;
      --accent: #3aa3ff;
      --danger: #ff6b6b;
    }

    .veyra-glass {
      background: var(--glass-bg);
      color: var(--glass-text);
      backdrop-filter: blur(12px) saturate(120%);
      -webkit-backdrop-filter: blur(12px) saturate(120%);
      border: 1px solid var(--glass-border);
      border-radius: 14px;
      padding: 12px;
      min-width: 260px;
      box-shadow: 0 8px 30px rgba(2,6,23,0.6);
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      font-size: 13px;
      user-select: none;
      box-sizing: border-box;
    }

    .veyra-header { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:8px; }
    .veyra-title { font-weight:700; font-size:14px; }
    .veyra-body { display:block; }
    .veyra-row { display:flex; align-items:center; justify-content:space-between; gap:8px; margin:6px 0; }

    .veyra-btn {
      padding:6px 8px;
      border-radius:8px;
      border:none;
      cursor:pointer;
      background:linear-gradient(180deg,#3aa3ff,#1b7ed6);
      color:white;
    }

    .veyra-divider { height:1px; background:rgba(255,255,255,0.04); margin:8px 0; border-radius:2px; }

    .veyra-pill { font-size:11px; padding:4px 8px; border-radius:999px; }
    .pill-live { background:rgba(34,197,94,0.18); color:#c7ffd3; border:1px solid rgba(34,197,94,0.22); }
    .pill-stop { background:rgba(239,68,68,0.12); color:#ffd6d6; border:1px solid rgba(239,68,68,0.18); }

    .veyra-select, .veyra-input {
      padding: 4px 6px;
      border-radius: 6px;
      border: 1px solid var(--glass-border);
      background: rgba(255,255,255,0.04);
      color: var(--glass-text);
      font-size: 12px;
      width: 80px;
    }

    .veyra-select:disabled, .veyra-input:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }

    .drag-handle:hover {
      background: rgba(255,255,255,0.5) !important;
    }
  `;
  document.head.appendChild(style);

})();