BW WAR Tracker (REST API v2.0)

Track targets and assists for Torn faction wars using Direct REST API

当前为 2025-11-25 提交的版本,查看 最新版本

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         BW WAR Tracker (REST API v2.0)
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Track targets and assists for Torn faction wars using Direct REST API
// @author       Tu
// @match        https://www.torn.com/*
// @icon         https://www.torn.com/favicon.ico
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @connect      api.torn.com
// @connect      www.tornstats.com
// @connect      bw-war-tracker-default-rtdb.europe-west1.firebasedatabase.app
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    let currentUser = null;
    let factionMembers = [];
    let myTargets = GM_getValue('bw_my_targets', []);
    let assists = [];
    
    // Configurare Directă Bază de Date (Fără librării externe)
    const DB_URL = "https://bw-war-tracker-default-rtdb.europe-west1.firebasedatabase.app";

    function initScript() {
        if (document.body) {
            setupScript();
        } else {
            setTimeout(initScript, 100);
        }
    }

    function setupScript() {
        GM_addStyle(`
        #bw-war-button { position: fixed !important; bottom: 20px !important; left: 20px !important; background-color: #000 !important; color: #fff !important; border: 2px solid #fff !important; padding: 12px 24px !important; font-size: 14px !important; font-weight: bold; cursor: pointer; border-radius: 5px; z-index: 999999 !important; box-shadow: 0 2px 10px rgba(0,0,0,0.3) !important; transition: all 0.3s !important; }
        #bw-war-button:hover { background-color: #333 !important; transform: translateY(-2px) !important; }
        #bw-war-modal, #bw-settings-modal { display: none !important; position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; width: 85% !important; max-width: 1000px !important; height: 75% !important; background-color: rgba(26, 26, 26, 0.95) !important; border: 2px solid #333 !important; border-radius: 10px !important; z-index: 999999 !important; box-shadow: 0 5px 30px rgba(0,0,0,0.5) !important; }
        #bw-war-modal.active, #bw-settings-modal.active { display: flex !important; flex-direction: column !important; overflow: hidden !important; }
        #bw-war-overlay { display: none !important; position: fixed !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; background-color: rgba(0,0,0,0.7) !important; z-index: 999998 !important; }
        #bw-war-overlay.active { display: block !important; }
        .bw-modal-header { background: linear-gradient(135deg, #000 0%, #1a1a1a 100%) !important; color: #fff !important; padding: 20px !important; border-radius: 8px 8px 0 0 !important; display: flex !important; flex-direction: column !important; align-items: center !important; border-bottom: 3px solid #333 !important; position: relative !important; }
        .bw-modal-title { font-size: 24px !important; font-weight: 900 !important; }
        .bw-header-buttons { position: absolute !important; top: 20px !important; right: 20px !important; display: flex !important; gap: 10px !important; }
        .bw-settings-button, .bw-close-button { background: none !important; border: 2px solid #fff !important; color: #fff !important; font-size: 20px !important; cursor: pointer !important; padding: 8px 12px !important; border-radius: 5px !important; width: 40px !important; height: 40px !important; }
        .bw-tabs { display: flex !important; background-color: #2a2a2a !important; border-bottom: 2px solid #333 !important; }
        .bw-tab { flex: 1 !important; padding: 15px !important; background-color: #2a2a2a !important; color: #999 !important; border: none !important; cursor: pointer !important; font-weight: bold !important; }
        .bw-tab.active { background-color: #000 !important; color: #fff !important; border-bottom: 3px solid #4CAF50 !important; }
        .bw-tab-content { display: none !important; padding: 20px !important; overflow-y: auto !important; flex: 1 !important; color: #fff !important; }
        .bw-tab-content.active { display: block !important; }
        .bw-member-item, .bw-target-item, .bw-assist-item { background-color: #2a2a2a !important; padding: 12px 15px !important; border-radius: 5px !important; margin-bottom: 8px !important; display: flex !important; justify-content: space-between !important; align-items: center !important; }
        .bw-member-name, .bw-target-name { color: #fff !important; cursor: pointer !important; font-weight: bold !important; }
        .bw-member-name:hover, .bw-target-name:hover { color: #4CAF50 !important; }
        .bw-rank-badge { background-color: #4CAF50 !important; color: #000 !important; padding: 3px 10px !important; border-radius: 12px !important; font-size: 11px !important; font-weight: bold !important; }
        .bw-input-section { display: flex !important; gap: 10px !important; margin-bottom: 20px !important; }
        .bw-input-field { flex: 1 !important; padding: 10px !important; background-color: #2a2a2a !important; border: 1px solid #444 !important; color: #fff !important; border-radius: 5px !important; }
        .bw-add-button { background-color: #2196F3 !important; color: #fff !important; border: none !important; padding: 10px 20px !important; border-radius: 5px !important; cursor: pointer !important; font-weight: bold !important; }
        .bw-remove-button { background-color: #f44336 !important; color: #fff !important; border: none !important; padding: 5px 12px !important; border-radius: 3px !important; cursor: pointer !important; font-size: 11px !important; }
        .bw-slot-selector { display: flex !important; gap: 5px !important; margin: 0 10px !important; }
        .bw-slot-btn { background-color: #2a2a2a !important; color: #fff !important; border: 2px solid #444 !important; padding: 5px 10px !important; border-radius: 3px !important; cursor: pointer !important; font-size: 11px !important; }
        .bw-slot-btn.active { background-color: #4CAF50 !important; border-color: #4CAF50 !important; }
        .bw-im-in-btn { background-color: #FF9800 !important; color: #fff !important; border: none !important; padding: 6px 15px !important; border-radius: 15px !important; cursor: pointer !important; font-size: 12px !important; font-weight: bold !important; }
        .bw-participants { display: flex !important; flex-wrap: wrap !important; gap: 8px !important; margin-top: 10px !important; }
        .bw-participant-tag { background-color: #1a1a1a !important; color: #4CAF50 !important; padding: 5px 12px !important; border-radius: 15px !important; font-size: 11px !important; border: 1px solid #4CAF50 !important; }
        .bw-loading { text-align: center !important; padding: 20px !important; color: #999 !important; }
        `);

        const button = document.createElement('button');
        button.id = 'bw-war-button';
        button.textContent = 'BW WAR';
        document.body.appendChild(button);

        const overlay = document.createElement('div');
        overlay.id = 'bw-war-overlay';
        document.body.appendChild(overlay);

        const modal = document.createElement('div');
        modal.id = 'bw-war-modal';
        modal.innerHTML = `
            <div class="bw-modal-header">
                <div class="bw-header-buttons"><button class="bw-settings-button">⚙️</button><button class="bw-close-button">&times;</button></div>
                <img src="https://factionimages.torn.com/08099e78-0a69-4424-8ccd-5219338250ad-40039.png" class="bw-modal-logo" style="max-width: 200px; height: 50px; margin-bottom: 10px;">
                <div class="bw-modal-title">Black & White War Manager</div>
            </div>
            <div class="bw-tabs">
                <button class="bw-tab active" data-tab="info">Info</button>
                <button class="bw-tab" data-tab="team">Team</button>
                <button class="bw-tab" data-tab="targets">My Targets</button>
                <button class="bw-tab" data-tab="assists">Assists</button>
            </div>
            <div class="bw-tab-content active" id="info-content"><div class="bw-loading">Loading your information...</div></div>
            <div class="bw-tab-content" id="team-content"><div class="bw-loading">Loading faction members...</div></div>
            <div class="bw-tab-content" id="targets-content">
                <div class="bw-input-section"><input type="text" id="mytarget-input" class="bw-input-field" placeholder="Enter username or ID"><button id="add-mytarget-btn" class="bw-add-button">Add Target</button></div>
                <div id="mytargets-list"></div>
            </div>
            <div class="bw-tab-content" id="assists-content">
                <div class="bw-input-section">
                    <input type="text" id="assist-input" class="bw-input-field" placeholder="Enter User ID (Recommended) or Name" style="flex: 2;">
                    <div class="bw-slot-selector">
                        <button class="bw-slot-btn" data-slots="1">+1</button><button class="bw-slot-btn" data-slots="2">+2</button><button class="bw-slot-btn" data-slots="3">+3</button><button class="bw-slot-btn" data-slots="4">+4</button>
                    </div>
                    <button id="add-assist-btn" class="bw-add-button">Add</button>
                </div>
                <div id="assists-list"></div>
            </div>
        `;
        document.body.appendChild(modal);

        const settingsModal = document.createElement('div');
        settingsModal.id = 'bw-settings-modal';
        settingsModal.innerHTML = `
            <div class="bw-modal-header">
                <div class="bw-header-buttons"><button class="bw-close-button settings-close">&times;</button></div>
                <img src="https://factionimages.torn.com/08099e78-0a69-4424-8ccd-5219338250ad-40039.png" class="bw-modal-logo" style="max-width: 200px; height: 50px; margin-bottom: 10px;">
                <div class="bw-modal-title">Settings</div>
            </div>
            <div style="padding: 30px;">
                <div style="margin-bottom: 25px;">
                    <label style="display: block; margin-bottom: 8px; font-weight: bold; color: #fff;">Torn API Key:</label>
                    <input type="text" id="api-key-input" class="bw-input-field" placeholder="Enter your API key">
                    <button id="save-api-key" class="bw-add-button" style="margin-top: 10px;">Save</button>
                </div>
                <div style="margin-top: 20px; padding: 15px; background-color: #2a2a2a; border-radius: 5px; font-size: 13px; color: #aaa;">
                    <strong>Note:</strong> You need a Full Access API key. Get it from: <a href="https://www.torn.com/preferences.php#tab=api" target="_blank" style="color: #4CAF50;">Torn Preferences</a>
                </div>
            </div>
        `;
        document.body.appendChild(settingsModal);

        function getApiKey() { return GM_getValue('torn_api_key', ''); }

        function tornApiCall(endpoint, apiKey = null) {
            return new Promise((resolve, reject) => {
                const key = apiKey || getApiKey();
                if (!key) { reject('No API key set'); return; }
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://api.torn.com/${endpoint}&key=${key}`,
                    onload: function(response) {
                        try {
                            const data = JSON.parse(response.responseText);
                            if (data.error) { reject(data.error.error); } else { resolve(data); }
                        } catch (e) { reject('Failed to parse response'); }
                    },
                    onerror: function() { reject('Network error - Check connection or VPN'); }
                });
            });
        }

        function formatNumber(num) {
            if (!num) return '0';
            return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        }

        // --- DIRECT FIREBASE REST API FUNCTIONS (No Libraries needed) ---
        
        function fetchAssistsFromDB() {
            GM_xmlhttpRequest({
                method: "GET",
                url: `${DB_URL}/assists.json`,
                onload: function(response) {
                    try {
                        if(response.status === 200) {
                            const data = JSON.parse(response.responseText);
                            // Convert Object to Array and add the Firebase Key (ID)
                            assists = data ? Object.keys(data).map(key => ({ ...data[key], firebaseId: key })) : [];
                            renderAssists();
                        }
                    } catch(e) { console.error("Error parsing DB:", e); }
                }
            });
        }

        // Polling function (Refresh data every 5 seconds)
        function startDBPolling() {
            fetchAssistsFromDB();
            setInterval(fetchAssistsFromDB, 5000);
        }

        function addAssistToFirebase(assist) {
            return new Promise((resolve, reject) => {
                if (!currentUser) { alert("Error: User info not loaded!"); reject("No user"); return; }
                
                assist.initiatedBy = currentUser.name;
                assist.initiatedById = currentUser.player_id;
                assist.timestamp = Date.now();

                GM_xmlhttpRequest({
                    method: "POST",
                    url: `${DB_URL}/assists.json`,
                    data: JSON.stringify(assist),
                    headers: { "Content-Type": "application/json" },
                    onload: function(response) {
                        if(response.status === 200) {
                            fetchAssistsFromDB(); // Refresh immediately
                            resolve(true);
                        } else {
                            alert("Database Error: " + response.statusText);
                            reject(response.responseText);
                        }
                    },
                    onerror: function(err) {
                        alert("Network Error sending to DB");
                        reject(err);
                    }
                });
            });
        }

        function updateAssistInFirebase(firebaseId, updates) {
            GM_xmlhttpRequest({
                method: "PATCH",
                url: `${DB_URL}/assists/${firebaseId}.json`,
                data: JSON.stringify(updates),
                headers: { "Content-Type": "application/json" },
                onload: function() { fetchAssistsFromDB(); }
            });
        }

        function removeAssistFromFirebase(firebaseId) {
            GM_xmlhttpRequest({
                method: "DELETE",
                url: `${DB_URL}/assists/${firebaseId}.json`,
                onload: function() { fetchAssistsFromDB(); }
            });
        }

        // --- END DB FUNCTIONS ---

        async function loadUserInfo() {
            const container = document.querySelector('#info-content');
            try {
                const data = await tornApiCall('user/?selections=profile,battlestats,personalstats');
                currentUser = data;
                const battlestatsTotal = (data.strength || 0) + (data.defense || 0) + (data.speed || 0) + (data.dexterity || 0);
                container.innerHTML = `
                    <div style="max-width: 600px; margin: 0 auto;">
                        <div style="background-color: #2a2a2a; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
                            <a href="https://www.torn.com/profiles.php?XID=${data.player_id}" target="_blank" style="color: #4CAF50; font-size: 20px; font-weight: bold; text-decoration: none;">${data.name} [${data.player_id}]</a>
                            <span style="color: #999; margin-left: 10px;">Level ${data.level}</span>
                            <div style="margin-top: 15px; padding: 10px; background-color: #1a1a1a; border-radius: 5px;">
                                <div style="color: #4CAF50; font-weight: bold; margin-bottom: 8px;">Faction Info</div>
                                <div style="color: #fff; margin-bottom: 5px;"><span style="color: #999;">Faction:</span> ${data.faction?.faction_name || 'N/A'}</div>
                                <div style="color: #fff; margin-bottom: 5px;"><span style="color: #999;">Position:</span> <span style="background-color: #4CAF50; color: #000; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: bold; margin-left: 5px;">${data.faction?.position || 'N/A'}</span></div>
                                <div style="color: #fff;"><span style="color: #999;">Days in Faction:</span> ${data.faction?.days_in_faction || '0'}</div>
                            </div>
                            <div style="margin-top: 20px;">
                                <div style="color: #4CAF50; font-weight: bold; margin-bottom: 10px;">Battle Stats</div>
                                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
                                    <div style="background: #1a1a1a; padding: 10px; border-radius: 5px;"><div style="color: #999; font-size: 12px;">Strength</div><div style="color: #fff; font-weight: bold;">${formatNumber(data.strength)}</div></div>
                                    <div style="background: #1a1a1a; padding: 10px; border-radius: 5px;"><div style="color: #999; font-size: 12px;">Defense</div><div style="color: #fff; font-weight: bold;">${formatNumber(data.defense)}</div></div>
                                    <div style="background: #1a1a1a; padding: 10px; border-radius: 5px;"><div style="color: #999; font-size: 12px;">Speed</div><div style="color: #fff; font-weight: bold;">${formatNumber(data.speed)}</div></div>
                                    <div style="background: #1a1a1a; padding: 10px; border-radius: 5px;"><div style="color: #999; font-size: 12px;">Dexterity</div><div style="color: #fff; font-weight: bold;">${formatNumber(data.dexterity)}</div></div>
                                </div>
                                <div style="background: #1a1a1a; padding: 10px; border-radius: 5px; margin-top: 10px;"><div style="color: #999; font-size: 12px;">Total</div><div style="color: #fff; font-weight: bold; font-size: 18px;">${formatNumber(battlestatsTotal)}</div></div>
                            </div>
                        </div>
                    </div>`;
            } catch (error) {
                container.innerHTML = `<div style="text-align: center; padding: 40px; color: #f44336;">Error: ${error}<br><button onclick="document.querySelector('.bw-settings-button').click()" style="background: #4CAF50; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; margin-top: 20px;">Open Settings</button></div>`;
            }
        }

        async function loadFactionMembers() {
            const container = document.querySelector('#team-content');
            try {
                const data = await tornApiCall('faction/?selections=basic');
                if (!data.members) throw new Error('No members data received');
                factionMembers = Object.keys(data.members).map(id => {
                    const member = data.members[id];
                    return { id: id, name: member.name, position: member.position };
                });
                container.innerHTML = `<div style="color: #4CAF50; font-weight: bold; margin-bottom: 15px; font-size: 18px;">Faction Members (${factionMembers.length})</div>
                    ${factionMembers.map(member => `<div class="bw-member-item"><a href="https://www.torn.com/profiles.php?XID=${member.id}" target="_blank" class="bw-member-name">${member.name} [${member.id}]</a><span class="bw-rank-badge">${member.position}</span></div>`).join('')}`;
            } catch (error) {
                container.innerHTML = `<div style="text-align: center; padding: 40px; color: #f44336;">Error loading faction: ${error}<br><span style="font-size: 14px; color: #999;">Make sure you have a Full Access API key</span></div>`;
            }
        }

        function renderMyTargets() {
            const list = document.getElementById('mytargets-list');
            if (myTargets.length === 0) { list.innerHTML = '<div style="text-align: center; color: #666; padding: 20px;">No targets added yet</div>'; return; }
            list.innerHTML = myTargets.map((target, index) => `
                <div class="bw-target-item">
                    <a href="#" class="bw-target-name" data-id="${target.id}">${target.name} [${target.id}]${target.level ? ' - Level ' + target.level : ''}</a>
                    <div><button class="bw-add-button" data-id="${target.id}" style="margin-right: 5px; font-size: 11px; padding: 5px 10px;">Attack</button><button class="bw-remove-button" data-index="${index}" data-type="mytarget">Remove</button></div>
                </div>`).join('');
            document.querySelectorAll('#mytargets-list .bw-target-name').forEach(link => link.addEventListener('click', (e) => { e.preventDefault(); window.open(`https://www.tornstats.com/spy.php?id=${e.target.dataset.id}`, '_blank', 'width=1000,height=700'); }));
            document.querySelectorAll('#mytargets-list .bw-add-button').forEach(btn => btn.addEventListener('click', (e) => window.open(`https://www.torn.com/loader.php?sid=attack&user2ID=${e.target.dataset.id}`, '_blank')));
            document.querySelectorAll('#mytargets-list .bw-remove-button').forEach(btn => btn.addEventListener('click', (e) => { const index = parseInt(e.target.dataset.index); myTargets.splice(index, 1); GM_setValue('bw_my_targets', myTargets); renderMyTargets(); }));
        }

        document.getElementById('add-mytarget-btn').addEventListener('click', async () => {
            const input = document.getElementById('mytarget-input');
            const value = input.value.trim();
            if (!value) { alert('Please enter a username or ID'); return; }
            try {
                let data;
                if (/^\d+$/.test(value)) { data = await tornApiCall(`user/${value}?selections=profile`); }
                else { data = await tornApiCall(`user/${value}?selections=profile`); }
                if (!data || !data.player_id) { alert('User not found! Try using their ID (numbers) instead of username.'); return; }
                const newTarget = { id: data.player_id, name: data.name, level: data.level };
                if (myTargets.find(t => t.id === newTarget.id)) { alert('Target already added!'); return; }
                myTargets.push(newTarget);
                GM_setValue('bw_my_targets', myTargets);
                input.value = '';
                renderMyTargets();
                alert(`✓ Added: ${newTarget.name} [${newTarget.id}]`);
            } catch (error) { console.error('Add target error:', error); alert('Failed to add target. Try using the ID.'); }
        });

        renderMyTargets();
        let selectedSlots = 1;
        document.querySelectorAll('.bw-slot-btn').forEach(btn => btn.addEventListener('click', (e) => { document.querySelectorAll('.bw-slot-btn').forEach(b => b.classList.remove('active')); e.target.classList.add('active'); selectedSlots = parseInt(e.target.dataset.slots); }));
        document.querySelector('.bw-slot-btn[data-slots="1"]').classList.add('active');

        function renderAssists() {
            const list = document.getElementById('assists-list');
            if (assists.length === 0) { list.innerHTML = '<div style="text-align: center; color: #666; padding: 20px;">No group attacks scheduled</div>'; return; }
            list.innerHTML = assists.map((assist, index) => `
                <div class="bw-assist-item" style="flex-direction: column; align-items: flex-start;">
                    <div style="width: 100%; display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
                        <div><a href="#" class="bw-target-name" data-id="${assist.id}">${assist.name} [${assist.id}]${assist.level ? ' - Level ' + assist.level : ''}</a><span style="color: #999; margin-left: 10px; font-size: 12px;">(${assist.slots} slots)</span></div>
                        <div><button class="bw-im-in-btn" data-index="${index}">I'm in</button><button class="bw-add-button" data-id="${assist.id}" style="margin: 0 5px; font-size: 11px; padding: 5px 10px;">Attack</button><button class="bw-remove-button" data-index="${index}" data-type="assist">Remove</button></div>
                    </div>
                    ${assist.participants && assist.participants.length > 0 ? `<div class="bw-participants">${assist.participants.map(p => `<span class="bw-participant-tag">${p}</span>`).join('')}</div>` : '<div style="color: #666; font-size: 12px;">No participants yet</div>'}
                </div>`).join('');
            document.querySelectorAll('#assists-list .bw-target-name').forEach(link => link.addEventListener('click', (e) => { e.preventDefault(); window.open(`https://www.tornstats.com/spy.php?id=${e.target.dataset.id}`, '_blank', 'width=1000,height=700'); }));
            document.querySelectorAll('#assists-list .bw-add-button').forEach(btn => btn.addEventListener('click', (e) => window.open(`https://www.torn.com/loader.php?sid=attack&user2ID=${e.target.dataset.id}`, '_blank')));
            document.querySelectorAll('.bw-im-in-btn').forEach(btn => btn.addEventListener('click', (e) => {
                const index = parseInt(e.target.dataset.index);
                const assist = assists[index];
                if (!currentUser || !currentUser.name) { alert('Please load your info first from the Info tab'); return; }
                if (!assist.participants) assist.participants = [];
                if (assist.participants.includes(currentUser.name)) { alert('You are already in this assist!'); return; }
                if (assist.participants.length >= assist.slots) { alert('This assist is full!'); return; }
                assist.participants.push(currentUser.name);
                updateAssistInFirebase(assist.firebaseId, { participants: assist.participants });
            }));
            document.querySelectorAll('#assists-list .bw-remove-button').forEach(btn => btn.addEventListener('click', (e) => { const index = parseInt(e.target.dataset.index); const assist = assists[index]; removeAssistFromFirebase(assist.firebaseId); }));
        }
        
        document.getElementById('add-assist-btn').addEventListener('click', async () => {
            const input = document.getElementById('assist-input');
            const value = input.value.trim();
            if (!value) { alert('Please enter a username or ID'); return; }
            if(!getApiKey()) { alert("Missing API Key! Please go to Settings and add your Torn API Key."); return; }

            try {
                let data;
                if (/^\d+$/.test(value)) { data = await tornApiCall(`user/${value}?selections=profile`); }
                else { data = await tornApiCall(`user/${value}?selections=profile`); }
                
                if (!data || !data.player_id) { alert('User not found! Try using their ID (numbers) instead of username.'); return; }
                
                const newAssist = { id: data.player_id, name: data.name, level: data.level, slots: selectedSlots, participants: [] };
                if (assists.find(a => a.id === newAssist.id)) { alert('This target already has an assist scheduled!'); return; }

                const success = await addAssistToFirebase(newAssist);
                
                if(success) {
                    input.value = '';
                    alert(`✓ Added assist for: ${newAssist.name} [${newAssist.id}]`);
                }
            } catch (error) {
                console.error('Add assist error:', error);
                alert('Failed to add assist: ' + (error.message || error));
            }
        });

        function openModal() {
             modal.classList.add('active'); overlay.classList.add('active'); settingsModal.classList.remove('active');
             loadUserInfo(); loadFactionMembers();
             startDBPolling(); // Start checking for updates
        }
        function openSettingsModal() { settingsModal.classList.add('active'); overlay.classList.add('active'); modal.classList.remove('active'); document.getElementById('api-key-input').value = getApiKey(); }
        function closeModals() { modal.classList.remove('active'); settingsModal.classList.remove('active'); overlay.classList.remove('active'); }

        button.addEventListener('click', openModal);
        modal.querySelector('.bw-settings-button').addEventListener('click', openSettingsModal);
        overlay.addEventListener('click', closeModals);
        modal.querySelector('.bw-close-button').addEventListener('click', closeModals);
        settingsModal.querySelector('.settings-close').addEventListener('click', closeModals);
        document.getElementById('save-api-key').addEventListener('click', () => {
            const apiKey = document.getElementById('api-key-input').value.trim();
            if (apiKey) { GM_setValue('torn_api_key', apiKey); alert('API Key saved!'); closeModals(); }
        });

        const tabs = modal.querySelectorAll('.bw-tab');
        const tabContents = modal.querySelectorAll('.bw-tab-content');
        tabs.forEach(tab => tab.addEventListener('click', () => {
            tabs.forEach(t => t.classList.remove('active')); tabContents.forEach(tc => tc.classList.remove('active'));
            tab.classList.add('active'); document.getElementById(`${tab.dataset.tab}-content`).classList.add('active');
        }));
    }
    initScript();
})();