Discord - Custom Background Image

You can set custom background image of Discord

// ==UserScript==
// @name         Discord - Custom Background Image
// @namespace    http://tampermonkey.net/
// @version      0.9
// @description  You can set custom background image of Discord
// @author       You
// @match        https://discord.com/channels/*
// @match        https://discord.com/channels/*/*
// @icon         https://www.google.com/s2/favicons?domain=discord.com
// @grant        GM.xmlHttpRequest
// @grant        GM.registerMenuCommand
// @grant        GM.getValue
// @grant        GM.setValue
// @grant        GM.addElement
// @connect      i.imgur.com
// @license      MIT
// ==/UserScript==

(async window => {
    'use strict';

    let intervalTime = 30 * 1000;
    let urls = [
        'https://i.imgur.com/cHKbKQT.jpeg',
        'https://i.imgur.com/sqmqy20.png',
        'https://i.imgur.com/cCL6TPk.jpeg',
        'https://i.imgur.com/Z6GYpxh.jpeg',
        'https://i.imgur.com/FNJdY5h.jpeg',
        'https://i.imgur.com/uKVG1XM.jpeg'
    ];

    let g_elm = null;

    const del = () => {
        if (g_elm) {
            g_elm.remove();
            g_elm = null;
            return true;
        }
        return false;
    };

    const addInput = async (value) => {
        if (del()) return;

        g_elm = await GM.addElement(document.body, 'div', {});
        Object.assign(g_elm.style, {
            'position': 'fixed',
            'width': '100vw',
            'height': '100vh',
            'display': 'flex',
            'flex-wrap': 'wrap',
            'justify-content': 'center',
            'align-items': 'center',
            'z-index': Infinity
        });

        const input = await GM.addElement(g_elm, 'textarea', {
            textContent: value
        });

        Object.assign(input.style, {
            'width': '50vw',
            'height': '30vh',
            'font-size': '24px',
            'font-weight': 'bold'
        });

        const btnSave = await GM.addElement(g_elm, 'button', {
            textContent: 'save'
        });

        await GM.addElement(g_elm, 'div', {
            textContent: ' '
        });

        const btnCancel = await GM.addElement(g_elm, 'button', {
            textContent: 'cancel'
        });

        for (const v of [btnSave, btnCancel]) {
            Object.assign(v.style, {
                'color': 'white',
                'backgroundColor': 'red'
            });
        }

        return new Promise((resolve, reject) => {
            btnSave.addEventListener('click', () => del() && resolve(input.value));
            btnCancel.addEventListener('click', () => del() && reject());
        });
    };

    const key1 = 'intervalTime';
    GM.registerMenuCommand('config interval time', async () => {
        const res = await addInput(await GM.getValue(key1, intervalTime));
        if (!res) return;

        const m = res.match(/[0-9]+/);
        if (!m) return;

        const n = Number(m[0]);
        intervalTime = n;
        GM.setValue(key1, n);
    });

    intervalTime = await GM.getValue(key1, intervalTime);

    const key2 = 'URL';
    GM.registerMenuCommand('config URL', async () => {
        const res = await addInput(await GM.getValue(key2, urls.join('\n')));
        if (!res) return;

        const a = findURL(res);
        if (!a.length) return;

        urls = a;
        GM.setValue(key2, a.join('\n'));
    });

    urls = (await GM.getValue(key2, urls.join('\n'))).split('\n');

    const findURL = (str) => {
        const m = str.match(/(https?|ftp)(:\/\/[-_.!~*'()a-zA-Z0-9;\/?:@&=+$,%#]+)/g);
        return m || [];
    };

    const memo = new Map();
    const get = async (url) => {
        if (memo.has(url)) return memo.get(url);

        const res = await GM.xmlHttpRequest({
            method: 'GET',
            url: url,
            withCredentials: true,
            responseType: 'arraybuffer',
        });

        const blobUrl = URL.createObjectURL(new Blob([res.response], { type: 'application/octet-binary' }));
        memo.set(url, blobUrl);

        return blobUrl;
    };

    let g_url = await get(urls[0]);

    const wait = (resolve) => {
        if (document.querySelector('[class^="chatContent"]')) return resolve();
        setTimeout(() => wait(resolve), 500);
    };

    await new Promise(resolve => wait(resolve));

    const setURL = () => {
        Object.assign(document.body.children[0].style, {
            'background-image': 'url("' + g_url + '")',
            'background-attachment': 'fixed',
            'background-position': 'center center',
            'background-size': 'cover',
            'background-repeat': 'no-repeat',
            'transition-duration': '1.5s'
        });
    };

    setURL();

    const setOther = () => {
        for (const v of document.querySelectorAll('*')) {
            v.style.backgroundColor = 'rgba(0, 0, 0, 0)';
        }
        document.body.children[0].children[3].style.backgroundColor = 'rgba(0, 0, 0, 0.7)';

        const aplicarTransparencia = () => {
            const embeds = document.querySelectorAll('.embed');
            embeds.forEach(embed => {
                embed.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
                embed.style.backdropFilter = 'blur(10px)';
            });

            const embedContents = document.querySelectorAll('.embed .embed-content');
            embedContents.forEach(content => {
                content.style.backgroundColor = 'rgba(0, 0, 0, 0.3)';
            });

            const attachments = document.querySelectorAll('.attachment');
            attachments.forEach(attachment => {
                attachment.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
                attachment.style.backdropFilter = 'blur(10px)';
            });
        };

        const observeEmbeds = () => {
            const observer = new MutationObserver(mutations => {
                aplicarTransparencia();
            });

            observer.observe(document.body, { childList: true, subtree: true });

            setInterval(() => {
                aplicarTransparencia();
            }, 50); // Verificação a cada 50ms
        };

        observeEmbeds();

        aplicarTransparencia();
    };

    setOther();

    let _url = location.href;
    let _time = 0;
    let index = 0;

    const update = async () => {
        const time = performance.now();

        if (time - _time > intervalTime) {
            g_url = await get(urls[(++index) % urls.length]);
            _time = performance.now();
            setURL();
        } else {
            const url = location.href;
            if (url !== _url) {
                _url = url;
                setOther();
            }
        }

        requestAnimationFrame(update);
    };

    update();
})(window.unsafeWindow || window);