Target Hunter Extended

A configurable target finder with level, battle stats, job, and company filtering

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Target Hunter Extended
// @version      3.1
// @namespace    http://tampermonkey.net/
// @description  A configurable target finder with level, battle stats, job, and company filtering
// @author       devilsfallguy [3317459]
// @license      MIT
// @match        https://www.torn.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // Torn job types for filtering
    const TORN_JOBS = {
        1: 'Unemployed',
        2: 'Grocery Store',
        3: 'Law Firm',
        4: 'Hair Salon',
        5: 'Army',
        6: 'Casino',
        7: 'Education',
        8: 'Oil Rig',
        9: 'Medical',
        10: 'Car Dealership',
        11: 'Gun Shop',
        12: 'Flower Shop',
        13: 'Zoo',
        14: 'Night Club',
        15: 'Candle Shop',
        16: 'Cyber Cafe'
    };

    // Default settings
    const DEFAULT_SETTINGS = {
        minID: 3000000,
        maxID: 3400000,
        minLevel: 1,
        maxLevel: 100,
        apiKey: '',
        maxRetries: 15,
        openInNewTab: false,
        useAttackLink: true,

        // Battle stats filtering
        useBattleStatsFilter: false,
        battleStatsFilterType: 'total', // 'total' or 'individual'

        // Total battle stats
        minTotalStats: 0,
        maxTotalStats: 1000000000,

        // Individual stats
        minStrength: 0,
        maxStrength: 1000000000,
        minDefense: 0,
        maxDefense: 1000000000,
        minSpeed: 0,
        maxSpeed: 1000000000,
        minDexterity: 0,
        maxDexterity: 1000000000,

        // Job filtering
        useJobFilter: false,
        jobFilterType: 'include', // 'include' or 'exclude'
        selectedJobs: [], // Array of job IDs to include/exclude

        // Last Action filtering
        useLastActionFilter: false,
        lastActionFilterType: 'include', // 'include' or 'exclude'
        selectedTimeRanges: [], // Array of time ranges

        // Company/Faction filtering
        useCompanyFilter: false,
        companyFilterType: 'exclude', // 'include' or 'exclude'
        companyFilterMethod: 'any', // 'any' (any faction) or 'specific' (specific factions)
        selectedCompanies: [], // Array of faction IDs to include/exclude
        companySearchText: '' // Text input for faction names
    };

    // Load settings from storage
    function loadSettings() {
        const settings = {};
        Object.keys(DEFAULT_SETTINGS).forEach(key => {
            const value = GM_getValue(key, DEFAULT_SETTINGS[key]);
            // Parse arrays from storage
            if (key === 'selectedJobs' || key === 'selectedCompanies' || key === 'selectedTimeRanges') {
                settings[key] = typeof value === 'string' ? JSON.parse(value || '[]') : value;
            } else {
                settings[key] = value;
            }
        });
        return settings;
    }

    // Save settings to storage
    function saveSettings(settings) {
        Object.keys(settings).forEach(key => {
            // Stringify arrays for storage
            if (key === 'selectedJobs' || key === 'selectedCompanies' || key === 'selectedTimeRanges') {
                GM_setValue(key, JSON.stringify(settings[key]));
            } else {
                GM_setValue(key, settings[key]);
            }
        });
    }

    let settings = loadSettings();

    // Utility function for random number generation
    function getRandomNumber(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    // Format numbers with commas
    function formatNumber(num) {
        return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }

    // Check user profile via API
    async function getUserProfile(userID) {
        if (!settings.apiKey) {
            console.warn('No API key provided - skipping profile check');
            return null;
        }

        try {
            let selections = ['profile'];

            if (settings.useBattleStatsFilter) {
                selections.push('battlestats');
            }

            if (settings.useJobFilter || settings.useJobTypeFilter || settings.usePrivateCompanyFilter || settings.useCompanyFilter || settings.useLastActionFilter) {
                selections.push('basic');
            }

            const selectionsStr = selections.join(',');
            const response = await fetch(`https://api.torn.com/user/${userID}?selections=${selectionsStr}&key=${settings.apiKey}`);
            const data = await response.json();

            if (data.error) {
                console.warn(`API Error for user ${userID}:`, data.error);
                return null;
            }

            return {
                level: data.level,
                job: data.job ? {
                    job_id: data.job.job_id,
                    company_id: data.job.company_id,
                    company_name: data.job.company_name
                } : null,
                faction: data.faction ? {
                    faction_id: data.faction.faction_id,
                    faction_name: data.faction.faction_name
                } : null,
                last_action: data.last_action ? {
                    timestamp: data.last_action.timestamp,
                    relative: data.last_action.relative,
                    status: data.last_action.status
                } : null,
                battlestats: data.strength ? {
                    strength: data.strength,
                    defense: data.defense,
                    speed: data.speed,
                    dexterity: data.dexterity,
                    total: data.strength + data.defense + data.speed + data.dexterity
                } : null
            };
        } catch (error) {
            console.warn(`Failed to fetch user ${userID}:`, error);
            return null;
        }
    }

    // Check if target meets battle stats criteria
    function meetsBattleStatsCriteria(battlestats) {
        if (!settings.useBattleStatsFilter || !battlestats) {
            return true;
        }

        if (settings.battleStatsFilterType === 'total') {
            return battlestats.total >= settings.minTotalStats && battlestats.total <= settings.maxTotalStats;
        } else {
            return battlestats.strength >= settings.minStrength && battlestats.strength <= settings.maxStrength &&
                   battlestats.defense >= settings.minDefense && battlestats.defense <= settings.maxDefense &&
                   battlestats.speed >= settings.minSpeed && battlestats.speed <= settings.maxSpeed &&
                   battlestats.dexterity >= settings.minDexterity && battlestats.dexterity <= settings.maxDexterity;
        }
    }

    // Check if target meets job criteria
    function meetsJobCriteria(job) {
        if (!settings.useJobFilter) {
            return true;
        }

        if (settings.selectedJobs.length === 0) {
            return true; // No jobs selected means include all
        }

        const userJobId = job ? job.job_id : 1; // Default to unemployed
        const isJobSelected = settings.selectedJobs.includes(userJobId);

        if (settings.jobFilterType === 'include') {
            return isJobSelected;
        } else { // exclude
            return !isJobSelected;
        }
    }

    // Check if target meets last action criteria
    function meetsLastActionCriteria(lastAction) {
        if (!settings.useLastActionFilter) {
            return true;
        }

        if (settings.selectedTimeRanges.length === 0) {
            return true; // No time ranges selected means include all
        }

        if (!lastAction || !lastAction.timestamp) {
            return false; // No last action data available
        }

        const now = Math.floor(Date.now() / 1000); // Current timestamp in seconds
        const lastActionTime = lastAction.timestamp;
        const timeDiff = now - lastActionTime;

        // Convert time differences to seconds
        const timeRanges = {
            '7days': 7 * 24 * 60 * 60,
            '1week': 7 * 24 * 60 * 60,
            '1month': 30 * 24 * 60 * 60,
            '3months': 90 * 24 * 60 * 60,
            '6months': 180 * 24 * 60 * 60,
            '1year': 365 * 24 * 60 * 60,
            'over1year': 365 * 24 * 60 * 60,
            'over2years': 730 * 24 * 60 * 60
        };

        let matchesTimeRange = false;

        for (const selectedRange of settings.selectedTimeRanges) {
            switch (selectedRange) {
                case '7days':
                case '1week':
                    if (timeDiff <= timeRanges[selectedRange]) {
                        matchesTimeRange = true;
                    }
                    break;
                case '1month':
                    if (timeDiff <= timeRanges['1month'] && timeDiff > timeRanges['7days']) {
                        matchesTimeRange = true;
                    }
                    break;
                case '3months':
                    if (timeDiff <= timeRanges['3months'] && timeDiff > timeRanges['1month']) {
                        matchesTimeRange = true;
                    }
                    break;
                case '6months':
                    if (timeDiff <= timeRanges['6months'] && timeDiff > timeRanges['3months']) {
                        matchesTimeRange = true;
                    }
                    break;
                case '1year':
                    if (timeDiff <= timeRanges['1year'] && timeDiff > timeRanges['6months']) {
                        matchesTimeRange = true;
                    }
                    break;
                case 'over1year':
                    if (timeDiff > timeRanges['over1year'] && timeDiff <= timeRanges['over2years']) {
                        matchesTimeRange = true;
                    }
                    break;
                case 'over2years':
                    if (timeDiff > timeRanges['over2years']) {
                        matchesTimeRange = true;
                    }
                    break;
            }

            if (matchesTimeRange) break; // Found a match, no need to check other ranges
        }

        if (settings.lastActionFilterType === 'include') {
            return matchesTimeRange;
        } else { // exclude
            return !matchesTimeRange;
        }
    }

    // Check if target meets company/faction criteria
    function meetsCompanyCriteria(faction) {
        if (!settings.useCompanyFilter) {
            return true;
        }

        // If filtering for "any faction" members
        if (settings.companyFilterMethod === 'any') {
            const hasCompany = faction && faction.faction_id && faction.faction_id > 0;

            if (settings.companyFilterType === 'include') {
                return hasCompany; // Must be in any faction
            } else { // exclude
                return !hasCompany; // Must not be in any faction
            }
        }

        // If filtering for specific factions
        if (settings.companyFilterMethod === 'specific') {
            if (settings.selectedCompanies.length === 0) {
                return true; // No specific companies selected means include all
            }

            const userFactionId = faction ? faction.faction_id : 0;
            const isFactionSelected = settings.selectedCompanies.includes(userFactionId);

            if (settings.companyFilterType === 'include') {
                return isFactionSelected;
            } else { // exclude
                return !isFactionSelected;
            }
        }

        return true;
    }

    // Find a random target within criteria
    async function findRandomTarget() {
        let attempts = 0;
        const maxAttempts = settings.maxRetries;

        console.log('Starting target search with criteria:', {
            useJobFilter: settings.useJobFilter,
            useLastActionFilter: settings.useLastActionFilter,
            useCompanyFilter: settings.useCompanyFilter,
            useBattleStatsFilter: settings.useBattleStatsFilter
        });

        while (attempts < maxAttempts) {
            const randID = getRandomNumber(settings.minID, settings.maxID);
            console.log(`Attempt ${attempts + 1}/${maxAttempts}: Checking user ${randID}`);

            // If no API key is set, just return the random ID
            if (!settings.apiKey) {
                console.log('No API key - returning random ID');
                return { id: randID, profile: null };
            }

            const profile = await getUserProfile(randID);

            if (profile) {
                console.log(`Profile found for ${randID}:`, {
                    level: profile.level,
                    jobId: profile.job?.job_id,
                    companyName: profile.job?.company_name,
                    factionName: profile.faction?.faction_name,
                    lastAction: profile.last_action?.relative,
                    totalStats: profile.battlestats?.total
                });

                const meetsLevel = profile.level >= settings.minLevel && profile.level <= settings.maxLevel;
                const meetsBattleStats = meetsBattleStatsCriteria(profile.battlestats);
                const meetsJob = meetsJobCriteria(profile.job);
                const meetsLastAction = meetsLastActionCriteria(profile.last_action);
                const meetsCompany = meetsCompanyCriteria(profile.faction);

                console.log(`Filter results for ${randID}:`, {
                    meetsLevel,
                    meetsBattleStats,
                    meetsJob,
                    meetsLastAction,
                    meetsCompany
                });

                if (meetsLevel && meetsBattleStats && meetsJob && meetsLastAction && meetsCompany) {
                    let logMessage = `Found target: ID ${randID}, Level ${profile.level}`;

                    if (profile.job && profile.job.job_id > 1) {
                        if (profile.job.job_id > 16) {
                            // Private company job
                            logMessage += `, Job: Private Company (${profile.job.company_name || 'Unknown'})`;
                        } else {
                            // NPC job
                            logMessage += `, Job: ${TORN_JOBS[profile.job.job_id] || 'Unknown'}`;
                        }
                    } else {
                        logMessage += `, Job: Unemployed`;
                    }

                    if (profile.faction) {
                        logMessage += `, Faction: ${profile.faction.faction_name}`;
                    }

                    if (profile.last_action) {
                        logMessage += `, Last Action: ${profile.last_action.relative}`;
                    }

                    if (profile.battlestats) {
                        logMessage += `, Total Stats: ${formatNumber(profile.battlestats.total)}`;
                    }

                    console.log(logMessage);
                    return { id: randID, profile: profile };
                } else {
                    console.log(`User ${randID} filtered out`);
                }
            } else {
                console.log(`No profile data for ${randID}`);
            }

            attempts++;

            // Add small delay to avoid rate limiting
            await new Promise(resolve => setTimeout(resolve, 150));
        }

        console.warn(`Could not find target within criteria after ${maxAttempts} attempts`);
        return null;
    }

    // Create settings panel
    function createSettingsPanel() {
        const panel = document.createElement('div');
        panel.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #2a2a2a;
            color: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            z-index: 100000;
            font-family: Arial, sans-serif;
            font-size: 14px;
            min-width: 500px;
            max-width: 600px;
            max-height: 80vh;
            overflow-y: auto;
            display: none;
        `;

        const jobCheckboxes = Object.entries(TORN_JOBS).map(([id, name]) =>
            `<label style="display: block; margin: 2px 0; font-size: 12px;">
                <input type="checkbox" value="${id}" ${settings.selectedJobs.includes(parseInt(id)) ? 'checked' : ''}> ${name}
            </label>`
        ).join('');

        panel.innerHTML = `
            <h3 style="margin-top: 0; color: #4CAF50;">Target Finder Settings</h3>

            <div style="border-bottom: 1px solid #444; padding-bottom: 15px; margin-bottom: 15px;">
                <h4 style="margin: 0 0 10px 0; color: #FFA500;">Basic Settings</h4>
                <div style="margin-bottom: 10px;">
                    <label>Min User ID:</label><br>
                    <input type="number" id="minID" value="${settings.minID}" style="width: 100%; padding: 5px; margin-top: 2px;">
                </div>
                <div style="margin-bottom: 10px;">
                    <label>Max User ID:</label><br>
                    <input type="number" id="maxID" value="${settings.maxID}" style="width: 100%; padding: 5px; margin-top: 2px;">
                </div>
                <div style="margin-bottom: 10px;">
                    <label>API Key (required for filtering):</label><br>
                    <input type="password" id="apiKey" value="${settings.apiKey}" style="width: 100%; padding: 5px; margin-top: 2px;" placeholder="Get from torn.com/preferences.php#tab=api">
                </div>
                <div style="margin-bottom: 10px;">
                    <label>Max Retries:</label><br>
                    <input type="number" id="maxRetries" value="${settings.maxRetries}" style="width: 100%; padding: 5px; margin-top: 2px;" min="1" max="50">
                </div>
                <div style="margin-bottom: 10px;">
                    <label>
                        <input type="checkbox" id="openInNewTab" ${settings.openInNewTab ? 'checked' : ''}> Open in new tab
                    </label>
                </div>
                <div style="margin-bottom: 10px;">
                    <label>
                        <input type="checkbox" id="useAttackLink" ${settings.useAttackLink ? 'checked' : ''}> Use attack link (unchecked = profile link)
                    </label>
                </div>
            </div>

            <div style="border-bottom: 1px solid #444; padding-bottom: 15px; margin-bottom: 15px;">
                <h4 style="margin: 0 0 10px 0; color: #FFA500;">Level Filter</h4>
                <div style="margin-bottom: 10px;">
                    <label>Min Level:</label><br>
                    <input type="number" id="minLevel" value="${settings.minLevel}" style="width: 100%; padding: 5px; margin-top: 2px;">
                </div>
                <div style="margin-bottom: 10px;">
                    <label>Max Level:</label><br>
                    <input type="number" id="maxLevel" value="${settings.maxLevel}" style="width: 100%; padding: 5px; margin-top: 2px;">
                </div>
            </div>

            <div style="border-bottom: 1px solid #444; padding-bottom: 15px; margin-bottom: 15px;">
                <h4 style="margin: 0 0 10px 0; color: #FFA500;">Job Filter</h4>
                <div style="margin-bottom: 15px;">
                    <label>
                        <input type="checkbox" id="useJobFilter" ${settings.useJobFilter ? 'checked' : ''}> Enable job filtering
                    </label>
                </div>

                <div id="jobFilterOptions" style="display: ${settings.useJobFilter ? 'block' : 'none'};">
                    <div style="margin-bottom: 15px;">
                        <label>Filter Type:</label><br>
                        <select id="jobFilterType" style="width: 100%; padding: 5px; margin-top: 2px;">
                            <option value="include" ${settings.jobFilterType === 'include' ? 'selected' : ''}>Include only selected jobs</option>
                            <option value="exclude" ${settings.jobFilterType === 'exclude' ? 'selected' : ''}>Exclude selected jobs</option>
                        </select>
                    </div>

                    <div style="margin-bottom: 10px;">
                        <label>Select Jobs:</label><br>
                        <div style="max-height: 150px; overflow-y: auto; border: 1px solid #555; padding: 10px; margin-top: 5px; background: #333;">
                            <div style="margin-bottom: 10px;">
                                <button type="button" id="selectAllJobs" style="background: #666; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; margin-right: 5px; font-size: 11px;">Select All</button>
                                <button type="button" id="clearAllJobs" style="background: #666; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 11px;">Clear All</button>
                            </div>
                            <div id="jobCheckboxes" style="columns: 2; column-gap: 15px;">
                                ${jobCheckboxes}
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <div style="border-bottom: 1px solid #444; padding-bottom: 15px; margin-bottom: 15px;">
                <h4 style="margin: 0 0 10px 0; color: #FFA500;">Last Action Filter</h4>
                <div style="margin-bottom: 15px;">
                    <label>
                        <input type="checkbox" id="useLastActionFilter" ${settings.useLastActionFilter ? 'checked' : ''}> Enable last action filtering
                    </label>
                </div>

                <div id="lastActionFilterOptions" style="display: ${settings.useLastActionFilter ? 'block' : 'none'};">
                    <div style="margin-bottom: 15px;">
                        <label>Filter Type:</label><br>
                        <select id="lastActionFilterType" style="width: 100%; padding: 5px; margin-top: 2px;">
                            <option value="include" ${settings.lastActionFilterType === 'include' ? 'selected' : ''}>Include only selected ranges</option>
                            <option value="exclude" ${settings.lastActionFilterType === 'exclude' ? 'selected' : ''}>Exclude selected ranges</option>
                        </select>
                    </div>

                    <div style="margin-bottom: 10px;">
                        <label>Select Time Ranges:</label><br>
                        <div style="border: 1px solid #555; padding: 10px; margin-top: 5px; background: #333;">
                            <div style="margin-bottom: 10px;">
                                <button type="button" id="selectAllTimeRanges" style="background: #666; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; margin-right: 5px; font-size: 11px;">Select All</button>
                                <button type="button" id="clearAllTimeRanges" style="background: #666; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 11px;">Clear All</button>
                            </div>
                            <div id="timeRangeCheckboxes" style="columns: 2; column-gap: 15px;">
                                <label style="display: block; margin: 2px 0; font-size: 12px;">
                                    <input type="checkbox" value="7days" ${settings.selectedTimeRanges.includes('7days') ? 'checked' : ''}> Last 7 days
                                </label>
                                <label style="display: block; margin: 2px 0; font-size: 12px;">
                                    <input type="checkbox" value="1week" ${settings.selectedTimeRanges.includes('1week') ? 'checked' : ''}> Last week
                                </label>
                                <label style="display: block; margin: 2px 0; font-size: 12px;">
                                    <input type="checkbox" value="1month" ${settings.selectedTimeRanges.includes('1month') ? 'checked' : ''}> Last month
                                </label>
                                <label style="display: block; margin: 2px 0; font-size: 12px;">
                                    <input type="checkbox" value="3months" ${settings.selectedTimeRanges.includes('3months') ? 'checked' : ''}> Last 3 months
                                </label>
                                <label style="display: block; margin: 2px 0; font-size: 12px;">
                                    <input type="checkbox" value="6months" ${settings.selectedTimeRanges.includes('6months') ? 'checked' : ''}> Last 6 months
                                </label>
                                <label style="display: block; margin: 2px 0; font-size: 12px;">
                                    <input type="checkbox" value="1year" ${settings.selectedTimeRanges.includes('1year') ? 'checked' : ''}> Last year
                                </label>
                                <label style="display: block; margin: 2px 0; font-size: 12px;">
                                    <input type="checkbox" value="over1year" ${settings.selectedTimeRanges.includes('over1year') ? 'checked' : ''}> Over 1 year ago
                                </label>
                                <label style="display: block; margin: 2px 0; font-size: 12px;">
                                    <input type="checkbox" value="over2years" ${settings.selectedTimeRanges.includes('over2years') ? 'checked' : ''}> Over 2 years ago
                                </label>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <div style="border-bottom: 1px solid #444; padding-bottom: 15px; margin-bottom: 15px;">
                <h4 style="margin: 0 0 10px 0; color: #FFA500;">Company/Faction Filter</h4>
                <div style="margin-bottom: 15px;">
                    <label>
                        <input type="checkbox" id="useCompanyFilter" ${settings.useCompanyFilter ? 'checked' : ''}> Enable company filtering
                    </label>
                </div>

                <div id="companyFilterOptions" style="display: ${settings.useCompanyFilter ? 'block' : 'none'};">
                    <div style="margin-bottom: 15px;">
                        <label>Filter Type:</label><br>
                        <select id="companyFilterType" style="width: 100%; padding: 5px; margin-top: 2px;">
                            <option value="include" ${settings.companyFilterType === 'include' ? 'selected' : ''}>Include selected</option>
                            <option value="exclude" ${settings.companyFilterType === 'exclude' ? 'selected' : ''}>Exclude selected</option>
                        </select>
                    </div>

                    <div style="margin-bottom: 15px;">
                        <label>Filter Method:</label><br>
                        <select id="companyFilterMethod" style="width: 100%; padding: 5px; margin-top: 2px;">
                            <option value="any" ${settings.companyFilterMethod === 'any' ? 'selected' : ''}>Any faction membership</option>
                            <option value="specific" ${settings.companyFilterMethod === 'specific' ? 'selected' : ''}>Specific factions</option>
                        </select>
                    </div>

                    <div id="specificCompanyOptions" style="display: ${settings.companyFilterMethod === 'specific' ? 'block' : 'none'};">
                        <div style="margin-bottom: 10px;">
                            <label>Faction IDs (comma-separated):</label><br>
                            <input type="text" id="selectedCompanies" value="${settings.selectedCompanies.join(',')}" style="width: 100%; padding: 5px; margin-top: 2px;" placeholder="e.g., 12345,67890">
                            <small style="color: #aaa;">Enter faction IDs you want to include/exclude</small>
                        </div>
                    </div>
                </div>
            </div>

            <div style="padding-bottom: 15px;">
                <h4 style="margin: 0 0 10px 0; color: #FFA500;">Battle Stats Filter</h4>
                <div style="margin-bottom: 15px;">
                    <label>
                        <input type="checkbox" id="useBattleStatsFilter" ${settings.useBattleStatsFilter ? 'checked' : ''}> Enable battle stats filtering
                    </label>
                </div>

                <div id="battleStatsOptions" style="display: ${settings.useBattleStatsFilter ? 'block' : 'none'};">
                    <div style="margin-bottom: 15px;">
                        <label>Filter Type:</label><br>
                        <select id="battleStatsFilterType" style="width: 100%; padding: 5px; margin-top: 2px;">
                            <option value="total" ${settings.battleStatsFilterType === 'total' ? 'selected' : ''}>Total Battle Stats</option>
                            <option value="individual" ${settings.battleStatsFilterType === 'individual' ? 'selected' : ''}>Individual Stats</option>
                        </select>
                    </div>

                    <div id="totalStatsFilter" style="display: ${settings.battleStatsFilterType === 'total' ? 'block' : 'none'};">
                        <div style="margin-bottom: 10px;">
                            <label>Min Total Stats:</label><br>
                            <input type="number" id="minTotalStats" value="${settings.minTotalStats}" style="width: 100%; padding: 5px; margin-top: 2px;">
                        </div>
                        <div style="margin-bottom: 10px;">
                            <label>Max Total Stats:</label><br>
                            <input type="number" id="maxTotalStats" value="${settings.maxTotalStats}" style="width: 100%; padding: 5px; margin-top: 2px;">
                        </div>
                    </div>

                    <div id="individualStatsFilter" style="display: ${settings.battleStatsFilterType === 'individual' ? 'block' : 'none'};">
                        <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
                            <div>
                                <label>Min Strength:</label><br>
                                <input type="number" id="minStrength" value="${settings.minStrength}" style="width: 100%; padding: 5px; margin-top: 2px;">
                            </div>
                            <div>
                                <label>Max Strength:</label><br>
                                <input type="number" id="maxStrength" value="${settings.maxStrength}" style="width: 100%; padding: 5px; margin-top: 2px;">
                            </div>
                            <div>
                                <label>Min Defense:</label><br>
                                <input type="number" id="minDefense" value="${settings.minDefense}" style="width: 100%; padding: 5px; margin-top: 2px;">
                            </div>
                            <div>
                                <label>Max Defense:</label><br>
                                <input type="number" id="maxDefense" value="${settings.maxDefense}" style="width: 100%; padding: 5px; margin-top: 2px;">
                            </div>
                            <div>
                                <label>Min Speed:</label><br>
                                <input type="number" id="minSpeed" value="${settings.minSpeed}" style="width: 100%; padding: 5px; margin-top: 2px;">
                            </div>
                            <div>
                                <label>Max Speed:</label><br>
                                <input type="number" id="maxSpeed" value="${settings.maxSpeed}" style="width: 100%; padding: 5px; margin-top: 2px;">
                            </div>
                            <div>
                                <label>Min Dexterity:</label><br>
                                <input type="number" id="minDexterity" value="${settings.minDexterity}" style="width: 100%; padding: 5px; margin-top: 2px;">
                            </div>
                            <div>
                                <label>Max Dexterity:</label><br>
                                <input type="number" id="maxDexterity" value="${settings.maxDexterity}" style="width: 100%; padding: 5px; margin-top: 2px;">
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <div>
                <button id="saveSettings" style="background: #4CAF50; color: white; border: none; padding: 8px 15px; border-radius: 5px; cursor: pointer; margin-right: 10px;">Save Settings</button>
                <button id="cancelSettings" style="background: #f44336; color: white; border: none; padding: 8px 15px; border-radius: 5px; cursor: pointer;">Cancel</button>
            </div>
        `;

        document.body.appendChild(panel);

        // Add event listeners for dynamic UI
        const useJobFilterCheckbox = panel.querySelector('#useJobFilter');
        const jobFilterOptions = panel.querySelector('#jobFilterOptions');
        const useLastActionFilterCheckbox = panel.querySelector('#useLastActionFilter');
        const lastActionFilterOptions = panel.querySelector('#lastActionFilterOptions');
        const useCompanyFilterCheckbox = panel.querySelector('#useCompanyFilter');
        const companyFilterOptions = panel.querySelector('#companyFilterOptions');
        const companyFilterMethod = panel.querySelector('#companyFilterMethod');
        const specificCompanyOptions = panel.querySelector('#specificCompanyOptions');
        const useBattleStatsCheckbox = panel.querySelector('#useBattleStatsFilter');
        const battleStatsOptions = panel.querySelector('#battleStatsOptions');
        const filterTypeSelect = panel.querySelector('#battleStatsFilterType');
        const totalStatsFilter = panel.querySelector('#totalStatsFilter');
        const individualStatsFilter = panel.querySelector('#individualStatsFilter');

        // Job filter toggle
        useJobFilterCheckbox.addEventListener('change', function() {
            jobFilterOptions.style.display = this.checked ? 'block' : 'none';
        });

        // Last action filter toggle
        useLastActionFilterCheckbox.addEventListener('change', function() {
            lastActionFilterOptions.style.display = this.checked ? 'block' : 'none';
        });

        // Company filter toggle
        useCompanyFilterCheckbox.addEventListener('change', function() {
            companyFilterOptions.style.display = this.checked ? 'block' : 'none';
        });

        // Company filter method toggle
        companyFilterMethod.addEventListener('change', function() {
            specificCompanyOptions.style.display = this.value === 'specific' ? 'block' : 'none';
        });

        // Job checkbox management
        panel.querySelector('#selectAllJobs').addEventListener('click', function() {
            const checkboxes = panel.querySelectorAll('#jobCheckboxes input[type="checkbox"]');
            checkboxes.forEach(cb => cb.checked = true);
        });

        panel.querySelector('#clearAllJobs').addEventListener('click', function() {
            const checkboxes = panel.querySelectorAll('#jobCheckboxes input[type="checkbox"]');
            checkboxes.forEach(cb => cb.checked = false);
        });

        // Time range checkbox management
        panel.querySelector('#selectAllTimeRanges').addEventListener('click', function() {
            const checkboxes = panel.querySelectorAll('#timeRangeCheckboxes input[type="checkbox"]');
            checkboxes.forEach(cb => cb.checked = true);
        });

        panel.querySelector('#clearAllTimeRanges').addEventListener('click', function() {
            const checkboxes = panel.querySelectorAll('#timeRangeCheckboxes input[type="checkbox"]');
            checkboxes.forEach(cb => cb.checked = false);
        });

        useBattleStatsCheckbox.addEventListener('change', function() {
            battleStatsOptions.style.display = this.checked ? 'block' : 'none';
        });

        filterTypeSelect.addEventListener('change', function() {
            totalStatsFilter.style.display = this.value === 'total' ? 'block' : 'none';
            individualStatsFilter.style.display = this.value === 'individual' ? 'block' : 'none';
        });

        // Save settings
        panel.querySelector('#saveSettings').addEventListener('click', () => {
            settings.minID = parseInt(panel.querySelector('#minID').value);
            settings.maxID = parseInt(panel.querySelector('#maxID').value);
            settings.minLevel = parseInt(panel.querySelector('#minLevel').value);
            settings.maxLevel = parseInt(panel.querySelector('#maxLevel').value);
            settings.apiKey = panel.querySelector('#apiKey').value;
            settings.maxRetries = parseInt(panel.querySelector('#maxRetries').value);
            settings.openInNewTab = panel.querySelector('#openInNewTab').checked;
            settings.useAttackLink = panel.querySelector('#useAttackLink').checked;

            // Job filtering settings
            settings.useJobFilter = panel.querySelector('#useJobFilter').checked;
            settings.jobFilterType = panel.querySelector('#jobFilterType').value;

            const selectedJobCheckboxes = panel.querySelectorAll('#jobCheckboxes input[type="checkbox"]:checked');
            settings.selectedJobs = Array.from(selectedJobCheckboxes).map(cb => parseInt(cb.value));

            // Last action filtering settings
            settings.useLastActionFilter = panel.querySelector('#useLastActionFilter').checked;
            settings.lastActionFilterType = panel.querySelector('#lastActionFilterType').value;

            const selectedTimeRangeCheckboxes = panel.querySelectorAll('#timeRangeCheckboxes input[type="checkbox"]:checked');
            settings.selectedTimeRanges = Array.from(selectedTimeRangeCheckboxes).map(cb => cb.value);

            // Company filtering settings
            settings.useCompanyFilter = panel.querySelector('#useCompanyFilter').checked;
            settings.companyFilterType = panel.querySelector('#companyFilterType').value;
            settings.companyFilterMethod = panel.querySelector('#companyFilterMethod').value;

            const companyIds = panel.querySelector('#selectedCompanies').value;
            settings.selectedCompanies = companyIds ? companyIds.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)) : [];

            settings.useBattleStatsFilter = panel.querySelector('#useBattleStatsFilter').checked;
            settings.battleStatsFilterType = panel.querySelector('#battleStatsFilterType').value;

            settings.minTotalStats = parseInt(panel.querySelector('#minTotalStats').value) || 0;
            settings.maxTotalStats = parseInt(panel.querySelector('#maxTotalStats').value) || 1000000000;

            settings.minStrength = parseInt(panel.querySelector('#minStrength').value) || 0;
            settings.maxStrength = parseInt(panel.querySelector('#maxStrength').value) || 1000000000;
            settings.minDefense = parseInt(panel.querySelector('#minDefense').value) || 0;
            settings.maxDefense = parseInt(panel.querySelector('#maxDefense').value) || 1000000000;
            settings.minSpeed = parseInt(panel.querySelector('#minSpeed').value) || 0;
            settings.maxSpeed = parseInt(panel.querySelector('#maxSpeed').value) || 1000000000;
            settings.minDexterity = parseInt(panel.querySelector('#minDexterity').value) || 0;
            settings.maxDexterity = parseInt(panel.querySelector('#maxDexterity').value) || 1000000000;

            saveSettings(settings);
            panel.style.display = 'none';

            showNotification('Settings saved successfully!', 'success');
        });

        panel.querySelector('#cancelSettings').addEventListener('click', () => {
            panel.style.display = 'none';
        });

        return panel;
    }

    // Show notification with target details
    function showNotification(message, type = 'info', details = null) {
        const notification = document.createElement('div');
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 12px 16px;
            border-radius: 8px;
            color: white;
            z-index: 10001;
            font-family: Arial, sans-serif;
            font-size: 14px;
            background: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : '#2196F3'};
            max-width: 350px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
        `;

        let content = message;
        if (details && details.profile) {
            content += `<br><strong>Level:</strong> ${details.profile.level}`;

            if (details.profile.job && details.profile.job.job_id > 1) {
                if (details.profile.job.job_id > 16) {
                    // Private company job
                    content += `<br><strong>Job:</strong> Private Company`;
                    if (details.profile.job.company_name) {
                        content += ` (${details.profile.job.company_name})`;
                    }
                } else {
                    // NPC job
                    content += `<br><strong>Job:</strong> ${TORN_JOBS[details.profile.job.job_id] || 'Unknown'}`;
                }
            } else {
                content += `<br><strong>Job:</strong> Unemployed`;
            }

            if (details.profile.faction) {
                content += `<br><strong>Faction:</strong> ${details.profile.faction.faction_name}`;
            }

            if (details.profile.last_action) {
                content += `<br><strong>Last Action:</strong> ${details.profile.last_action.relative}`;
            }

            if (details.profile.battlestats) {
                content += `<br><strong>Total Stats:</strong> ${formatNumber(details.profile.battlestats.total)}`;
                if (settings.battleStatsFilterType === 'individual') {
                    content += `<br><small>STR: ${formatNumber(details.profile.battlestats.strength)} | DEF: ${formatNumber(details.profile.battlestats.defense)}<br>SPD: ${formatNumber(details.profile.battlestats.speed)} | DEX: ${formatNumber(details.profile.battlestats.dexterity)}</small>`;
                }
            }
        }

        notification.innerHTML = content;
        document.body.appendChild(notification);

        setTimeout(() => {
            notification.remove();
        }, 6000);
    }

    // Create main UI
    function createUI() {
        const container = document.createElement('div');
        container.style.cssText = `
            position: fixed;
            top: 27%;
            right: 0;
            z-index: 9999;
            font-family: Arial, sans-serif;
        `;

        // Main button
        const mainButton = document.createElement('button');
        mainButton.innerHTML = '🎯 Find Target';
        mainButton.style.cssText = `
            background: linear-gradient(45deg, #4CAF50, #45a049);
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 5px 0 0 5px;
            cursor: pointer;
            font-size: 14px;
            font-weight: bold;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
        `;

        // Settings button
        const settingsButton = document.createElement('button');
        settingsButton.innerHTML = '⚙️';
        settingsButton.style.cssText = `
            background: #2196F3;
            color: white;
            border: none;
            padding: 10px 8px;
            border-radius: 0 5px 5px 0;
            cursor: pointer;
            font-size: 14px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            border-left: 1px solid rgba(255,255,255,0.3);
        `;

        container.appendChild(mainButton);
        container.appendChild(settingsButton);

        const settingsPanel = createSettingsPanel();

        // Event listeners
        mainButton.addEventListener('click', async function() {
            mainButton.disabled = true;
            mainButton.innerHTML = '🔍 Searching...';

            try {
                const result = await findRandomTarget();

                if (result) {
                    const linkType = settings.useAttackLink ? 'attack' : 'profile';
                    const profileLink = settings.useAttackLink
                        ? `https://www.torn.com/loader.php?sid=attack&user2ID=${result.id}`
                        : `https://www.torn.com/profiles.php?XID=${result.id}`;

                    if (settings.openInNewTab) {
                        window.open(profileLink, '_blank');
                    } else {
                        window.location.href = profileLink;
                    }

                    showNotification(`Target found: ${result.id}`, 'success', result);
                } else {
                    showNotification('No suitable target found. Try adjusting your filters or increasing max retries.', 'error');
                }
            } catch (error) {
                console.error('Error finding target:', error);
                showNotification('Error occurred while finding target', 'error');
            } finally {
                mainButton.disabled = false;
                mainButton.innerHTML = '🎯 Find Target';
            }
        });

        settingsButton.addEventListener('click', function() {
            settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'block' : 'none';
        });

        document.body.appendChild(container);
    }

    function makeDraggable(element, handle) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        handle.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            element.style.top = (element.offsetTop - pos2) + "px";
            element.style.left = (element.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }

    // Initialize when page loads
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createUI);
    } else {
        createUI();
    }
})();