Airpl4ne Discord Spammer

Automate sending messages to Discord DMs or server channels with customizable delay and count.

// ==UserScript==
// @name         Airpl4ne Discord Spammer
// @name:vi Airpl4ne Discord Spammer
// @namespace    https://twisk.fun
// @version      1.0.1
// @description  Automate sending messages to Discord DMs or server channels with customizable delay and count.
// @description:vi Tự động gửi tin nhắn đến Discord DMs hoặc kênh với có thể chỉnh delay và số lượng.
// @author       airpl4ne
// @match        https://discord.com/*
// @grant        GM_addStyle
// @icon https://images-eds-ssl.xboxlive.com/image?url=4rt9.lXDC4H_93laV1_eHHFT949fUipzkiFOBH3fAiZZUCdYojwUyX2aTonS1aIwMrx6NUIsHfUHSLzjGJFxxsG72wAo9EWJR4yQWyJJaDaK1XdUso6cUMpI9hAdPUU_FNs11cY1X284vsHrnWtRw7oqRpN1m9YAg21d_aNKnIo-&format=source
// @license MIT
// ==/UserScript==

// never use it for illegal purposes.
// We do not take any responsibility.
// if you need help , join our discord : https://discord.gg/m3EV55SpYw
// please rate good if this tool is helpful to u! big thanks!<3

(function() {
    'use strict';

    // css
    GM_addStyle(`
        #message-sender-panel {
            position: fixed;
            top: 50px;
            right: 20px;
            background: #2f3136;
            color: #dcddde;
            padding: 15px;
            border-radius: 8px;
            z-index: 10000;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
            font-family: 'Whitney', sans-serif;
            width: 320px;
            user-select: none;
            transition: all 0.3s ease;
        }
        #message-sender-panel:hover {
            box-shadow: 0 4px 15px rgba(0,0,0,0.4);
        }
        #panel-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
            margin-bottom: 10px;
        }
        #panel-header h3 {
            margin: 0;
            font-size: 16px;
            font-weight: 600;
        }
        #close-btn {
            background: none;
            border: none;
            color: #b9bbbe;
            cursor: pointer;
            font-size: 18px;
        }
        #close-btn:hover {
            color: #dcddde;
        }
        #message-sender-panel label {
            display: block;
            margin: 5px 0 2px;
            font-size: 12px;
            color: #b9bbbe;
        }
        #message-sender-panel input, #message-sender-panel select, #message-sender-panel textarea {
            background: #40444b;
            border: none;
            border-radius: 3px;
            color: #dcddde;
            padding: 8px;
            width: 100%;
            box-sizing: border-box;
            font-size: 14px;
            margin-bottom: 10px;
        }
        #message-sender-panel textarea {
            resize: vertical;
            min-height: 50px;
        }
        #message-sender-panel button {
            background: #5865f2;
            color: white;
            border: none;
            border-radius: 3px;
            padding: 8px;
            cursor: pointer;
            font-size: 14px;
            width: 100%;
            margin: 5px 0;
            transition: background 0.2s;
        }
        #message-sender-panel button:hover {
            background: #4752c4;
        }
        #message-sender-panel button:disabled {
            background: #72767d;
            cursor: not-allowed;
        }
        #stop-btn {
            background: #ed4245;
        }
        #stop-btn:hover {
            background: #da373c;
        }
        #status {
            margin-top: 10px;
            font-size: 12px;
            color: #43b581;
        }
        .error {
            color: #f04747 !important;
        }
        .hidden {
            display: none;
        }
    `);

    // create css
    function createPanel() {
        const panel = document.createElement('div');
        panel.id = 'message-sender-panel';
        panel.innerHTML = `
            <div id="panel-header">
                <h3>Airpl4ne Spammer</h3>
                <button id="close-btn">×</button>
            </div>
            <label for="target-type">Target Type:</label>
            <select id="target-type">
                <option value="dm">DM (User ID)</option>
                <option value="channel">Server Channel (Channel ID)</option>
            </select>
            <div id="dm-input">
                <label for="user-id">User ID:</label>
                <input type="text" id="user-id" placeholder="Enter User ID (e.g., 123456789012345678)">
            </div>
            <div id="channel-input" class="hidden">
                <label for="channel-id">Channel ID:</label>
                <input type="text" id="channel-id" placeholder="Enter Channel ID (e.g., 987654321098765432)">
            </div>
            <label for="message-content">Message:</label>
            <textarea id="message-content" placeholder="Enter message (supports multi-line)"></textarea>
            <div style="display: flex; justify-content: space-between;">
                <div style="width: 48%;">
                    <label for="delay">Delay (ms):</label>
                    <input type="number" id="delay" value="1000" min="500">
                </div>
                <div style="width: 48%;">
                    <label for="count">Count:</label>
                    <input type="number" id="count" value="1" min="1">
                </div>
            </div>
            <button id="start-btn">Start Sending</button>
            <button id="stop-btn" class="hidden">Stop Sending</button>
            <div id="status"></div>
        `;
        document.body.appendChild(panel);

        // event
        document.getElementById('start-btn').addEventListener('click', startSending);
        document.getElementById('stop-btn').addEventListener('click', stopSending);
        document.getElementById('close-btn').addEventListener('click', () => panel.remove());
        document.getElementById('target-type').addEventListener('change', toggleInputs);

        // drag
        makeDraggable(panel);

        toggleInputs(); // Initial setup
    }

    // inputs
    function toggleInputs() {
        const type = document.getElementById('target-type').value;
        document.getElementById('dm-input').classList.toggle('hidden', type !== 'dm');
        document.getElementById('channel-input').classList.toggle('hidden', type !== 'channel');
    }

    // element drag
    function makeDraggable(element) {
        const header = document.getElementById('panel-header');
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        header.onmousedown = dragMouseDown;

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

        function elementDrag(e) {
            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;
        }
    }

    // discord auth
    function getAuthToken() {
        let token = localStorage.getItem('token');
        if (!token) {
            updateStatus('Error: No authorization token found. Please log in to Discord.', true);
            return null;
        }
        return token.replace(/"/g, '');
    }

    // status
    function updateStatus(message, isError = false) {
        const statusDiv = document.getElementById('status');
        statusDiv.textContent = message;
        statusDiv.className = isError ? 'error' : '';
    }

    // vali id
    function validateId(id) {
        return /^\d{17,19}$/.test(id);
    }

    // api
    async function sendMessage(channelId, message, authToken) {
        const url = `https://discord.com/api/v10/channels/${channelId}/messages`;
        const payload = {
            content: message,
            tts: false,
            nonce: Date.now().toString() + Math.floor(Math.random() * 1000).toString()
        };

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Authorization': authToken,
                    'Content-Type': 'application/json',
                    'User-Agent': navigator.userAgent
                },
                body: JSON.stringify(payload)
            });

            if (response.ok) {
                return true;
            } else {
                const error = await response.json();
                updateStatus(`Error: ${error.message || 'Failed to send message'}`, true);
                return false;
            }
        } catch (error) {
            updateStatus(`Error: ${error.message}`, true);
            return false;
        }
    }

    // sendinh
    let isSending = false;
    let sentCount = 0;
    async function startSending() {
        if (isSending) return;

        const type = document.getElementById('target-type').value;
        let targetId = type === 'dm' ? document.getElementById('user-id').value.trim() : document.getElementById('channel-id').value.trim();
        const message = document.getElementById('message-content').value.trim();
        const delay = parseInt(document.getElementById('delay').value);
        const count = parseInt(document.getElementById('count').value);
        const authToken = getAuthToken();

        if (!authToken) return;
        if (!message) {
            updateStatus('Error: Please enter a message.', true);
            return;
        }
        if (!targetId) {
            updateStatus(`Error: Please enter a ${type === 'dm' ? 'User ID' : 'Channel ID'}.`, true);
            return;
        }
        if (!validateId(targetId)) {
            updateStatus('Error: Invalid ID format (must be 17-19 digits).', true);
            return;
        }
        if (delay < 500) {
            updateStatus('Warning: Delay set to minimum 500ms to avoid rate limits.', true);
            document.getElementById('delay').value = 500;
        }

        let channelId = targetId;
        if (type === 'dm') {
            channelId = await getDMChannelId(targetId, authToken);
            if (!channelId) return;
        }

        isSending = true;
        sentCount = 0;
        document.getElementById('start-btn').disabled = true;
        document.getElementById('stop-btn').classList.remove('hidden');
        updateStatus(`Sending messages... (0/${count})`);

        for (let i = 0; i < count && isSending; i++) {
            const success = await sendMessage(channelId, message, authToken);
            if (!success) break;
            sentCount++;
            updateStatus(`Sending messages... (${sentCount}/${count})`);
            if (i < count - 1) {
                await new Promise(resolve => setTimeout(resolve, delay));
            }
        }

        isSending = false;
        document.getElementById('start-btn').disabled = false;
        document.getElementById('stop-btn').classList.add('hidden');
        updateStatus(`Finished sending ${sentCount} message(s).`);
    }

    // stop
    function stopSending() {
        isSending = false;
        updateStatus(`Stopped after sending ${sentCount} message(s).`);
        document.getElementById('start-btn').disabled = false;
        document.getElementById('stop-btn').classList.add('hidden');
    }

    //get id
    async function getDMChannelId(userId, authToken) {
        const url = 'https://discord.com/api/v10/users/@me/channels';
        const payload = { recipient_id: userId };

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Authorization': authToken,
                    'Content-Type': 'application/json',
                    'User-Agent': navigator.userAgent
                },
                body: JSON.stringify(payload)
            });

            if (response.ok) {
                const data = await response.json();
                return data.id;
            } else {
                const error = await response.json();
                updateStatus(`Error creating DM channel: ${error.message || error.code}`, true);
                return null;
            }
        } catch (error) {
            updateStatus(`Error: ${error.message}`, true);
            return null;
        }
    }

    // bye bye!
    if (window.location.href.includes('discord.com')) {
        createPanel();
    }
})();