您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
UI for selecting and joining raids by difficulty with a draggable button
// ==UserScript== // @name DOTV Bulk Join Raids // @version 1.3 // @license MIT // @namespace https://greasyfork.org/users/1159361 // @description UI for selecting and joining raids by difficulty with a draggable button // @author Zaregoto_Gaming // @match https://play.dragonsofthevoid.com/* // @exclude https://play.dragonsofthevoid.com/#/login // @grant none // ==/UserScript== (function() { 'use strict'; const raidMap = { "r.lesser-ent": "Lesser Tree Ent", "r.superior-watcher": "Superior Watcher", "r.elven-rangers": "Elven Rangers", "r.greater-ent": "Greater Ent", "r.sand-wyrm": "Sand Wyrm", "r.corrupted-golem": "Corrupted Golem", "r.naga-risaldar": "Naga Risaldar", "r.galeohog": "Galeohog", "r.naga-karamati": "Naga Karamati", "r.jagar-the-red": "Jagar the Red", "r.rotting-fen-lure": "Rotting Fen Lure", "r.sentry-ghoul": "Sentry Ghoul", "r.fallen-naga-subedar": "Fallen Naga Subedar", "r.bone-dragon": "Bone Dragon" }; const difficulties = ["easy", "hard", "legendary"]; const JOIN_LIMIT_STORAGE_KEY = 'autojoinRaidMaxJoinLimit'; let publicRaidCounts = {}; function loadSettings() { return JSON.parse(localStorage.getItem('autojoinRaidSettings')) || {}; } function saveSettings(settings) { localStorage.setItem('autojoinRaidSettings', JSON.stringify(settings)); } function loadButtonPosition() { return JSON.parse(localStorage.getItem('autojoinButtonPosition')) || { top: '40px', left: '10px' }; } function saveButtonPosition(position) { localStorage.setItem('autojoinButtonPosition', JSON.stringify(position)); } function saveMaxJoinLimit(limit) { localStorage.setItem(JOIN_LIMIT_STORAGE_KEY, limit); } function loadMaxJoinLimit() { return parseInt(localStorage.getItem(JOIN_LIMIT_STORAGE_KEY), 10) || 20; } function makeDraggable(element) { let offsetX, offsetY, isDragging = false; element.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - element.getBoundingClientRect().left; offsetY = e.clientY - element.getBoundingClientRect().top; element.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const newX = e.clientX - offsetX; const newY = e.clientY - offsetY; element.style.top = `${Math.max(0, newY)}px`; element.style.left = `${Math.max(0, newX)}px`; }); document.addEventListener('mouseup', () => { if (!isDragging) return; isDragging = false; element.style.cursor = 'grab'; saveButtonPosition({ top: element.style.top, left: element.style.left }); }); } async function fetchPublicRaidCounts() { const token = localStorage.token; publicRaidCounts = {}; const [publicRes, activeRes] = await Promise.all([ fetch("https://api.dragonsofthevoid.com/api/raid/public", { headers: { authorization: token } }), fetch("https://api.dragonsofthevoid.com/api/raid/active", { headers: { authorization: token } }) ]); const publicRaids = await publicRes.json(); const activeRaids = await activeRes.json(); const activeIds = new Set(activeRaids.map(r => r.id)); for (const raid of publicRaids) { if (activeIds.has(raid.id)) continue; const xml = raid.raidXmlId; const diff = raid.difficulty; if (!publicRaidCounts[xml]) publicRaidCounts[xml] = {}; if (!publicRaidCounts[xml][diff]) publicRaidCounts[xml][diff] = 0; publicRaidCounts[xml][diff]++; } } async function createUI(button) { await fetchPublicRaidCounts(); const settings = loadSettings(); const ui = document.createElement("div"); ui.id = "autojoin-ui"; ui.style.position = "fixed"; ui.style.background = "#fff"; ui.style.padding = "10px"; ui.style.border = "1px solid black"; ui.style.borderRadius = "8px"; ui.style.boxShadow = "0 2px 6px rgba(0,0,0,0.2)"; ui.style.zIndex = "1000"; ui.style.maxHeight = "400px"; ui.style.overflowY = "auto"; const buttonRect = button.getBoundingClientRect(); ui.style.top = `${Math.min(window.innerHeight - 410, buttonRect.bottom + window.scrollY)}px`; ui.style.left = `${Math.min(window.innerWidth - 300, buttonRect.left + window.scrollX)}px`; const title = document.createElement("div"); title.innerHTML = `<strong>Bulk Join Settings</strong>`; ui.appendChild(title); // Run button + join limit input const runRow = document.createElement("div"); runRow.style.display = "flex"; runRow.style.alignItems = "center"; runRow.style.margin = "8px 0 12px 0"; const runButton = document.createElement("button"); runButton.textContent = "Join Selected Raids"; runButton.style.marginRight = "10px"; const input = document.createElement("input"); input.type = "number"; input.min = "1"; input.value = loadMaxJoinLimit(); input.style.width = "60px"; input.addEventListener('change', () => { let val = parseInt(input.value, 10); if (isNaN(val) || val < 1) val = 1; input.value = val; saveMaxJoinLimit(val); }); runButton.onclick = () => { joinSelectedRaids(parseInt(input.value, 10) || 20); }; runRow.appendChild(runButton); runRow.appendChild(document.createTextNode("Max: ")); runRow.appendChild(input); ui.appendChild(runRow); Object.entries(raidMap).forEach(([id, name]) => { const nameLabel = document.createElement("div"); nameLabel.textContent = name; nameLabel.style.fontWeight = "bold"; ui.appendChild(nameLabel); difficulties.forEach(diff => { const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.checked = settings[id]?.includes(diff) || false; checkbox.onchange = () => { if (!settings[id]) settings[id] = []; if (checkbox.checked) { if (!settings[id].includes(diff)) settings[id].push(diff); } else { settings[id] = settings[id].filter(d => d !== diff); } saveSettings(settings); }; const count = publicRaidCounts[id]?.[diff] || 0; ui.appendChild(checkbox); ui.appendChild(document.createTextNode(` ${diff.charAt(0).toUpperCase() + diff.slice(1)} (${count})`)); ui.appendChild(document.createElement("br")); }); ui.appendChild(document.createElement("hr")); }); // Paste input const label = document.createElement("div"); label.innerHTML = `<strong>Join by Paste:</strong>`; ui.appendChild(label); const textArea = document.createElement("textarea"); textArea.rows = 10; textArea.cols = 40; textArea.style.width = "100%"; textArea.placeholder = "Paste lines like:\njoinraid,6aca59f3..., Corrupted Golem hard \njoinraid,6aca59f3..., Corrupted Golem hard"; ui.appendChild(textArea); const pasteButton = document.createElement("button"); pasteButton.textContent = "Join from Paste"; pasteButton.onclick = async () => { const token = localStorage.token; const lines = textArea.value.trim().split("\n"); const joinKeys = []; for (let line of lines) { const match = line.match(/^joinraid,([a-f0-9\-]+)/i); if (match) joinKeys.push(match[1]); } let success = 0; for (let key of joinKeys) { try { await joinraid(key, token); success++; } catch (e) { console.error("Failed to join:", key, e); } } alert(`Attempted to join ${joinKeys.length} raids. Successful: ${success}`); }; ui.appendChild(pasteButton); document.body.appendChild(ui); } function addToggleButton() { const button = document.createElement("button"); button.textContent = "⚔️ Bulk Join"; button.style.position = "fixed"; button.style.zIndex = "1001"; button.style.cursor = "grab"; const pos = loadButtonPosition(); button.style.top = pos.top; button.style.left = pos.left; button.onclick = () => { const existing = document.getElementById("autojoin-ui"); if (existing) existing.remove(); else createUI(button); }; document.body.appendChild(button); makeDraggable(button); } async function joinSelectedRaids(limit = 20) { const token = localStorage.token; const settings = loadSettings(); const [publicRaids, activeRaids] = await Promise.all([ getpublicraids(token), getactiveraids(token) ]); const activeIds = new Set(activeRaids.map(r => r.id)); const toJoin = publicRaids.filter(raid => { if (raid.health < 10000) return false; if (activeIds.has(raid.id)) return false; return settings[raid.raidXmlId]?.includes(raid.difficulty); }).reverse(); let count = 0; for (const raid of toJoin) { if (count >= limit) break; await joinraid(raid.joinkey, token); count++; } alert(`Joined ${count} raids`); } async function getpublicraids(token) { const res = await fetch("https://api.dragonsofthevoid.com/api/raid/public", { headers: { authorization: token } }); return res.json(); } async function getactiveraids(token) { const res = await fetch("https://api.dragonsofthevoid.com/api/raid/active", { headers: { authorization: token } }); return res.json(); } async function joinraid(joinkey, token) { const res = await fetch("https://api.dragonsofthevoid.com/api/raid/join/" + joinkey, { headers: { authorization: token } }); return res.json(); } window.addEventListener("load", addToggleButton); })();