Discord Token Manager

Get Token, Login via Token, Batch Token Validator & Switcher (Editor & Line Numbers)

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Discord Token Manager
// @icon         https://www.google.com/s2/favicons?domain=discord.com&sz=256
// @namespace    http://tampermonkey.net/
// @version      4.7
// @description  Get Token, Login via Token, Batch Token Validator & Switcher (Editor & Line Numbers)
// @author       Chaython
// @homepageURL  https://github.com/Chaython/Discord-Token-Manager
// @supportURL   https://github.com/Chaython/Discord-Token-Manager/issues
// @match        https://discord.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // --- Global State ---
    let CAPTURED_TOKEN = null;
    let isMinimized = false;
    let hasDragged = false;

    // Position Memory
    let expandedPos = null;
    let minimizedPos = null;

    const scope = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;

    // --- Safe Storage Wrappers ---
    const safeGetValue = (key, def) => {
        try { return GM_getValue(key, def); } catch(e) { console.warn('[DTG] Storage Error:', e); return def; }
    };
    const safeSetValue = (key, val) => {
        try { GM_setValue(key, val); } catch(e) { console.warn('[DTG] Storage Error:', e); }
    };

    // --- Network Sniffer ---
    const originalOpen = unsafeWindow.XMLHttpRequest.prototype.open;
    const originalSetRequestHeader = unsafeWindow.XMLHttpRequest.prototype.setRequestHeader;
    unsafeWindow.XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
        if (header.toLowerCase() === 'authorization' && value) {
            CAPTURED_TOKEN = value;
        }
        return originalSetRequestHeader.apply(this, arguments);
    };

    // --- Styles ---
    const setupStyles = () => {
        const style = document.createElement('style');
        style.textContent = `
            .dtg-panel {
                position: fixed; bottom: 20px; right: 20px; z-index: 9999;
                background-color: #2f3136; color: #dcddde; border-radius: 8px;
                box-shadow: 0 4px 15px rgba(0,0,0,0.5); font-family: 'Whitney', sans-serif;
                border: 1px solid #202225;
                display: flex; flex-direction: column;
                overflow: hidden;
                min-width: 320px; min-height: 300px;
                max-width: 95vw; max-height: 95vh;
                width: 350px; height: 500px;
                transition: width 0.1s, height 0.1s, border-radius 0.2s;
            }

            /* --- STRICT MINIMIZED STATE --- */
            .dtg-panel.minimized {
                width: 40px !important;
                height: 40px !important;
                min-width: 0 !important;
                min-height: 0 !important;
                padding: 0 !important;
                border-radius: 50% !important;
                overflow: hidden !important;
                resize: none !important;
                background: #5865f2 !important;
                border: 2px solid #fff !important;
                cursor: grab !important;
                display: flex !important;
                align-items: center !important;
                justify-content: center !important;
                box-shadow: 0 4px 10px rgba(0,0,0,0.5) !important;
            }
            .dtg-panel.minimized:active {
                cursor: grabbing !important;
            }

            .dtg-panel.minimized > * { display: none !important; }

            .dtg-panel.minimized::after {
                content: '🔑'; font-size: 20px; line-height: 1; display: block;
            }

            /* Resize Handle */
            .dtg-resize-handle {
                position: absolute; top: 0; left: 0;
                width: 15px; height: 15px; cursor: nwse-resize; z-index: 10000;
                border-top: 3px solid rgba(255, 255, 255, 0.4);
                border-left: 3px solid rgba(255, 255, 255, 0.4);
                border-top-left-radius: 8px; transition: border-color 0.2s;
            }
            .dtg-resize-handle:hover { border-color: rgba(255, 255, 255, 0.9); }

            .dtg-header {
                background-color: #5865f2; color: white; padding: 8px 12px 8px 20px;
                font-weight: 600; font-size: 14px; display: flex; justify-content: space-between;
                align-items: center; flex-shrink: 0; cursor: grab;
            }
            .dtg-header:active { cursor: grabbing; }

            .dtg-controls { display: flex; gap: 8px; }
            .dtg-icon-btn { cursor: pointer; opacity: 0.8; font-weight: bold; }
            .dtg-icon-btn:hover { opacity: 1; }

            .dtg-tabs { display: flex; background: #202225; border-bottom: 1px solid #202225; flex-shrink: 0; }
            .dtg-tab { flex: 1; text-align: center; padding: 8px 0; cursor: pointer; font-size: 12px; color: #8e9297; transition: 0.2s; }
            .dtg-tab:hover { color: #dcddde; background: #292b2f; }
            .dtg-tab.active { color: #fff; background: #2f3136; border-bottom: 2px solid #5865f2; }

            .dtg-content { padding: 10px; display: flex; flex-direction: column; gap: 8px; flex: 1; overflow-y: auto; min-height: 0; }

            .dtg-btn {
                background-color: #4f545c; color: white; border: none; padding: 8px;
                border-radius: 4px; cursor: pointer; font-size: 13px; text-align: center;
                transition: background 0.2s; width: 100%; flex-shrink: 0;
            }
            .dtg-btn:hover { background-color: #686d73; }
            .dtg-btn.primary { background-color: #5865f2; }
            .dtg-btn.primary:hover { background-color: #4752c4; }
            .dtg-btn.danger { background-color: #ed4245; }
            .dtg-btn.warning { background-color: #faa61a; color: #000; }
            .dtg-btn.success { background-color: #3ba55c; }
            .dtg-btn-group { display: flex; gap: 5px; flex-shrink: 0; }

            .dtg-input {
                background-color: #202225; border: 1px solid #202225; color: white;
                padding: 8px; border-radius: 4px; font-size: 13px; outline: none; width: 100%; box-sizing: border-box; flex-shrink: 0;
            }

            .dtg-editor-wrapper {
                display: flex; flex: 1; background-color: #202225; border: 1px solid #202225; border-radius: 4px;
                overflow: hidden; position: relative; min-height: 100px;
            }
            .dtg-line-numbers {
                width: 28px; background-color: #2f3136; color: #72767d; text-align: right; 
                padding: 8px 4px 8px 0; font-family: monospace; font-size: 12px; line-height: 16px;
                overflow: hidden; border-right: 1px solid #18191c; user-select: none;
            }
            .dtg-textarea {
                flex: 1; background: transparent; border: none; color: white;
                padding: 8px 8px 8px 6px; padding-right: 24px; padding-bottom: 24px;
                font-family: monospace; font-size: 12px; line-height: 16px;
                outline: none; resize: none; white-space: pre; overflow: auto;
            }

            .dtg-separator { height: 1px; background-color: #40444b; margin: 4px 0; flex-shrink: 0; }

            .dtg-batch-item {
                display: flex; align-items: center; justify-content: space-between;
                background: #202225; padding: 8px; border-radius: 4px;
                font-size: 12px; margin-bottom: 8px; flex-shrink: 0;
            }
            .dtg-batch-status { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 6px; flex-shrink: 0; }
            .dtg-status-valid { background-color: #3ba55c; box-shadow: 0 0 5px #3ba55c; }
            .dtg-status-invalid { background-color: #ed4245; }

            .dtg-line-tag {
                color: #72767d; font-family: monospace; font-size: 11px; margin-right: 8px;
                background: #2f3136; padding: 1px 4px; border-radius: 3px;
            }

            .dtg-batch-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-right: 5px; }
            .dtg-action-group { display: flex; gap: 4px; }
            .dtg-btn-sm { padding: 4px 8px; font-size: 11px; width: auto; }
        `;
        document.head.appendChild(style);
    };

    // --- Core Logic ---
    const loginWithToken = (token) => {
        if (!token) return;
        token = token.replace(/^"|"$/g, '').trim();
        try {
            const iframe = document.createElement('iframe');
            document.body.appendChild(iframe);
            const localStorage = iframe.contentWindow.localStorage;
            localStorage.setItem('token', `"${token}"`);
            document.body.removeChild(iframe);
            setTimeout(() => window.location.reload(), 300);
        } catch (e) {
            console.error(e);
            alert('Login failed. Check console.');
        }
    };

    const getToken = () => {
        if (CAPTURED_TOKEN) return CAPTURED_TOKEN;
        try {
            const targetEval = (unsafeWindow || window).eval;
            return targetEval(`
                (webpackChunkdiscord_app.push([[''], {}, e => {
                    m = [];
                    for (let c in e.c) m.push(e.c[c]);
                }]), m).find(m => m?.exports?.default?.getToken !== void 0).exports.default.getToken()
            `);
        } catch (e) {}
        return null;
    };

    const copyTokenSafe = (t) => {
        if(!t) t = getToken();
        if(t) {
            navigator.clipboard.writeText(t).catch(err => prompt("Copy your token:", t));
        } else {
            alert('Token not found yet. Switch channels to generate traffic.');
        }
    };

    const updateLineNumbers = (panel) => {
        const textarea = panel.querySelector('#dtg-batch-input');
        const numbers = panel.querySelector('#dtg-line-numbers');
        if(!textarea || !numbers) return;
        const lineCount = textarea.value.split('\n').length;
        numbers.innerHTML = Array.from({length: lineCount}, (_, i) => i + 1).join('<br>');
    };

    const validateToken = async (token) => {
        try {
            const response = await fetch('https://discord.com/api/v9/users/@me', {
                headers: { 'Authorization': token }
            });
            if (response.status === 200) {
                const data = await response.json();
                return { valid: true, username: data.username, discriminator: data.discriminator };
            }
            return { valid: false };
        } catch (e) {
            return { valid: false, error: true };
        }
    };

    const runBatchCheck = async (panel) => {
        const textarea = panel.querySelector('#dtg-batch-input');
        if (!textarea) return;
        let raw = textarea.value;
        const allLines = raw.split(/\r?\n/);
        const seenTokens = new Set();
        const uniqueLines = [];
        for (const line of allLines) {
            const trimmed = line.trim();
            if (trimmed.length < 5) continue;
            if (!seenTokens.has(trimmed)) {
                seenTokens.add(trimmed);
                uniqueLines.push(trimmed);
            }
        }
        const finalText = uniqueLines.join('\n') + (uniqueLines.length > 0 ? '\n' : '');
        textarea.value = finalText;
        updateLineNumbers(panel);
        safeSetValue('dtg_saved_tokens', finalText);

        const resultsDiv = panel.querySelector('#dtg-batch-results');
        resultsDiv.innerHTML = '<div style="text-align:center; color:#888;">Checking...</div>';

        let html = '';
        const accountCounts = {};

        for (let i = 0; i < uniqueLines.length; i++) {
            const token = uniqueLines[i];
            const cleanToken = token.replace(/^"|"$/g, '');
            const lineNumber = i + 1;
            const res = await validateToken(cleanToken);
            const statusClass = res.valid ? 'dtg-status-valid' : 'dtg-status-invalid';
            let name = 'Invalid / Locked';
            let loginBtn = '';

            if (res.valid) {
                let baseName = `${res.username}`;
                if (res.discriminator && res.discriminator !== '0') {
                    baseName += `#${res.discriminator}`;
                }
                if (accountCounts[baseName] === undefined) {
                    accountCounts[baseName] = 0;
                    name = baseName;
                } else {
                    accountCounts[baseName]++;
                    name = `${baseName} (Copy ${accountCounts[baseName]})`;
                }
                loginBtn = `<button class="dtg-btn dtg-btn-sm primary dtg-action-login" data-token="${cleanToken}">Login</button>`;
            }
            const copyBtn = `<button class="dtg-btn dtg-btn-sm dtg-action-copy" data-token="${cleanToken}">Copy</button>`;
            html += `
                <div class="dtg-batch-item">
                    <div style="display:flex; align-items:center; flex:1; min-width:0;">
                        <span class="dtg-batch-status ${statusClass}"></span>
                        <span class="dtg-line-tag">#${lineNumber}</span>
                        <span class="dtg-batch-name" title="${name}">${name}</span>
                    </div>
                    <div class="dtg-action-group">${copyBtn}${loginBtn}</div>
                </div>
            `;
            resultsDiv.innerHTML = html;
            await new Promise(r => setTimeout(r, 200));
        }
        if (!html) resultsDiv.innerHTML = '<div style="text-align:center;">No valid tokens found</div>';

        resultsDiv.querySelectorAll('.dtg-action-login').forEach(btn => {
            btn.addEventListener('click', (e) => { e.stopPropagation(); loginWithToken(btn.dataset.token); });
        });
        resultsDiv.querySelectorAll('.dtg-action-copy').forEach(btn => {
            btn.addEventListener('click', (e) => {
                e.stopPropagation();
                const t = btn.dataset.token;
                navigator.clipboard.writeText(t).then(() => {
                    const originalText = btn.innerText;
                    btn.innerText = 'Copied!';
                    setTimeout(() => btn.innerText = originalText, 1000);
                });
            });
        });
    };

    const removeInvalidLines = async (panel) => {
        const textarea = panel.querySelector('#dtg-batch-input');
        if (!textarea) return;
        const resultsDiv = panel.querySelector('#dtg-batch-results');
        const raw = textarea.value;
        const allLines = raw.split(/\r?\n/);
        const validLines = [];
        resultsDiv.innerHTML = '<div style="text-align:center; color:#faa61a;">Validating & Removing Invalid...</div>';
        for (const line of allLines) {
            const token = line.trim();
            if (token.length < 5) continue;
            const cleanToken = token.replace(/^"|"$/g, '');
            const res = await validateToken(cleanToken);
            if (res.valid) validLines.push(token);
        }
        const newText = validLines.join('\n') + (validLines.length > 0 ? '\n' : '');
        textarea.value = newText;
        updateLineNumbers(panel);
        safeSetValue('dtg_saved_tokens', newText);
        runBatchCheck(panel);
    };

    // --- UI Creation ---
    const createPanel = () => {
        // Prevent Duplicates
        const existing = document.getElementById('dtg-panel');
        if (existing) existing.remove();

        const panel = document.createElement('div');
        panel.id = 'dtg-panel';
        panel.className = 'dtg-panel';

        panel.innerHTML = `
            <div id="dtg-resize-handle" class="dtg-resize-handle" title="Drag to Resize"></div>
            <div class="dtg-header" id="dtg-header">
                <span>Discord Tools</span>
                <div class="dtg-controls">
                    <span class="dtg-icon-btn" id="dtg-min">_</span>
                    <span class="dtg-icon-btn" id="dtg-close">✕</span>
                </div>
            </div>
            <div class="dtg-tabs">
                <div class="dtg-tab active" data-tab="single">Single</div>
                <div class="dtg-tab" data-tab="batch">Batch</div>
            </div>
            <div class="dtg-content" id="dtg-content-single"></div>
            <div class="dtg-content" id="dtg-content-batch" style="display:none;"></div>
        `;

        if(document.body) document.body.appendChild(panel);
        else return;

        const renderSingle = () => {
            const container = panel.querySelector('#dtg-content-single');
            const isLoginPage = window.location.href.includes('/login');
            if (!isLoginPage) {
                container.innerHTML = `
                    <button id="dtg-copy" class="dtg-btn primary">Copy Current Token</button>
                    <div class="dtg-separator"></div>
                    <input id="dtg-quick-input" class="dtg-input" type="password" placeholder="Paste token to switch">
                    <button id="dtg-quick-login" class="dtg-btn">Login</button>
                    <div class="dtg-separator"></div>
                    <button id="dtg-logout" class="dtg-btn danger">Logout</button>
                `;
                panel.querySelector('#dtg-copy').onclick = () => copyTokenSafe();
                panel.querySelector('#dtg-quick-login').onclick = () => loginWithToken(panel.querySelector('#dtg-quick-input').value);
                panel.querySelector('#dtg-logout').onclick = () => { window.location.href = '/login'; };
            } else {
                container.innerHTML = `
                    <input id="dtg-input-token" class="dtg-input" type="password" placeholder="Paste Token Here">
                    <button id="dtg-login-btn" class="dtg-btn primary">Login</button>
                `;
                panel.querySelector('#dtg-login-btn').onclick = () => loginWithToken(panel.querySelector('#dtg-input-token').value);
            }
        };

        const renderBatch = () => {
            const container = panel.querySelector('#dtg-content-batch');
            container.innerHTML = `
                <div class="dtg-editor-wrapper">
                    <div id="dtg-line-numbers" class="dtg-line-numbers">1</div>
                    <textarea id="dtg-batch-input" class="dtg-textarea" placeholder="Paste list of tokens" spellcheck="false"></textarea>
                </div>
                <div class="dtg-btn-group">
                    <button id="dtg-batch-check" class="dtg-btn success" style="flex:2;">Check & List</button>
                    <button id="dtg-batch-remove-invalid" class="dtg-btn warning" style="flex:2;">Remove Invalid</button>
                    <button id="dtg-batch-clear" class="dtg-btn danger" style="flex:1;">Clear</button>
                </div>
                <div class="dtg-separator"></div>
                <div id="dtg-batch-results"></div>
            `;
            const input = panel.querySelector('#dtg-batch-input');
            const lines = panel.querySelector('#dtg-line-numbers');
            input.addEventListener('scroll', () => { lines.scrollTop = input.scrollTop; });
            input.addEventListener('input', () => updateLineNumbers(panel));

            const savedTokens = safeGetValue('dtg_saved_tokens', '');
            if (savedTokens) { input.value = savedTokens; updateLineNumbers(panel); }

            panel.querySelector('#dtg-batch-check').onclick = () => runBatchCheck(panel);
            panel.querySelector('#dtg-batch-remove-invalid').onclick = () => removeInvalidLines(panel);
            panel.querySelector('#dtg-batch-clear').onclick = () => {
                input.value = '';
                panel.querySelector('#dtg-batch-results').innerHTML = '';
                safeSetValue('dtg_saved_tokens', '');
                updateLineNumbers(panel);
            };
        };

        renderSingle();
        renderBatch();

        // --- POSITION LOGIC ---
        const toggleMinimize = (shouldMinimize) => {
            const rect = panel.getBoundingClientRect();
            
            if (shouldMinimize) {
                expandedPos = { top: rect.top, left: rect.left };
                panel.classList.add('minimized');
                isMinimized = true;

                if (minimizedPos) {
                    panel.style.top = minimizedPos.top + 'px';
                    panel.style.left = minimizedPos.left + 'px';
                    panel.style.bottom = 'auto'; panel.style.right = 'auto';
                } else {
                    panel.style.top = rect.top + 'px';
                    panel.style.left = rect.left + 'px';
                    panel.style.bottom = 'auto'; panel.style.right = 'auto';
                }
            } else {
                minimizedPos = { top: rect.top, left: rect.left };
                panel.classList.remove('minimized');
                isMinimized = false;

                if (expandedPos) {
                    panel.style.top = expandedPos.top + 'px';
                    panel.style.left = expandedPos.left + 'px';
                    panel.style.bottom = 'auto'; panel.style.right = 'auto';
                }
            }
        };


        // --- DRAG LOGIC ---
        const initDrag = (e) => {
            const isHeader = e.target.closest('#dtg-header');
            const isMinimizedClick = panel.classList.contains('minimized');
            if (e.target.closest('.dtg-controls')) return;
            if (!isHeader && !isMinimizedClick) return;

            e.preventDefault();
            hasDragged = false;
            
            const rect = panel.getBoundingClientRect();
            panel.style.bottom = 'auto'; panel.style.right = 'auto';
            panel.style.left = rect.left + 'px'; panel.style.top = rect.top + 'px';

            const startX = e.clientX; const startY = e.clientY;
            const startLeft = rect.left; const startTop = rect.top;

            const onMove = (e) => {
                const dx = e.clientX - startX; const dy = e.clientY - startY;
                if (Math.abs(dx) > 3 || Math.abs(dy) > 3) hasDragged = true;
                panel.style.left = (startLeft + dx) + 'px';
                panel.style.top = (startTop + dy) + 'px';
            };

            const onUp = () => {
                document.removeEventListener('mousemove', onMove);
                document.removeEventListener('mouseup', onUp);
            };
            document.addEventListener('mousemove', onMove);
            document.addEventListener('mouseup', onUp);
        };

        panel.addEventListener('mousedown', initDrag);

        // --- RESIZE LOGIC ---
        const handle = panel.querySelector('#dtg-resize-handle');
        handle.addEventListener('mousedown', function(e) {
            e.preventDefault(); e.stopPropagation();
            const startX = e.clientX; const startY = e.clientY;
            const startWidth = parseInt(document.defaultView.getComputedStyle(panel).width, 10);
            const startHeight = parseInt(document.defaultView.getComputedStyle(panel).height, 10);

            function doDrag(e) {
                const width = startWidth + (startX - e.clientX);
                const height = startHeight + (startY - e.clientY);
                if (width > 320 && width < (window.innerWidth - 20)) panel.style.width = width + 'px';
                if (height > 300 && height < (window.innerHeight - 20)) panel.style.height = height + 'px';
            }
            function stopDrag() {
                document.removeEventListener('mousemove', doDrag);
                document.removeEventListener('mouseup', stopDrag);
            }
            document.addEventListener('mousemove', doDrag);
            document.addEventListener('mouseup', stopDrag);
        });

        // Tabs & Controls
        const switchTab = (tabName) => {
            panel.querySelectorAll('.dtg-tab').forEach(t => t.classList.remove('active'));
            const activeTab = panel.querySelector(`.dtg-tab[data-tab="${tabName}"]`);
            if(activeTab) activeTab.classList.add('active');
            if (tabName === 'single') {
                panel.querySelector('#dtg-content-single').style.display = 'flex';
                panel.querySelector('#dtg-content-batch').style.display = 'none';
            } else {
                panel.querySelector('#dtg-content-single').style.display = 'none';
                panel.querySelector('#dtg-content-batch').style.display = 'flex';
                updateLineNumbers(panel);
            }
            safeSetValue('dtg_last_tab', tabName);
        };

        panel.querySelectorAll('.dtg-tab').forEach(tab => {
            tab.addEventListener('click', () => switchTab(tab.dataset.tab));
        });

        panel.querySelector('#dtg-min').addEventListener('click', (e) => {
            e.stopPropagation();
            toggleMinimize(true);
        });

        panel.querySelector('#dtg-close').addEventListener('click', (e) => {
            e.stopPropagation();
            panel.remove();
        });

        panel.addEventListener('click', (e) => {
            if (hasDragged) { hasDragged = false; return; }
            if (panel.classList.contains('minimized')) {
                toggleMinimize(false);
            }
        });

        const stopProp = (e) => e.stopPropagation();
        const inputs = panel.querySelectorAll('input, textarea, button');
        inputs.forEach(el => el.addEventListener('click', stopProp));

        const lastTab = safeGetValue('dtg_last_tab', 'single');
        if (lastTab === 'batch') {
            switchTab('batch');
            const savedTokens = safeGetValue('dtg_saved_tokens', '');
            if (savedTokens) { setTimeout(() => runBatchCheck(panel), 500); }
        }
    };

    const init = () => {
        setupStyles();
        if (document.readyState === 'complete') { createPanel(); } 
        else { window.addEventListener('load', () => { setTimeout(createPanel, 1000); }); }
    };

    init();

})();