/*
 * Revolt Typing Spammer
 *     Spam annoying typing status on Revolt channels.
 *
 * How to use:
 *     1. Paste this script in the console.
 *     2. Call `TypingSpam.addSpammer(channelId, interval)` where `channelId` is a string of a channel ID and `interval` is a number in millisecond to add a spammer.
 *     3. Call `await TypingSpam.removeSpammer(channelId)` where `channelId` is a string of a channel ID to remove a spammer.
 *
 * Author
 *     Noxturnix (Noxturnix#6034)
 *     https://noxt.us/
 * 
 * License
 *     Copyright © 2023 Noxturnix
 *     This work is free. You can redistribute it and/or modify it under the
 *     terms of the Do What The Fuck You Want To Public License, Version 2,
 *     as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
*/
class TypingSpam {
    static Spammer = class {
        #logName = "TypingSpam.Spammer";
        #toStop = false;

        constructor(channel, interval) {
            this.channel = channel;
            this.interval = interval;
        }

        start() {
            if (this.channel === undefined) {
                this.#throwError("`channel` object is undefined.");
            }

            this.promise = this.#spam();
            return this.promise;
        }

        async stop() {
            this.#toStop = true;
            await this.promise;
        }

        async #spam() {
            while (true) {
                if (this.#toStop) return;
                this.channel.startTyping();
                await this.#createSleepPromise(this.interval);
                this.channel.stopTyping();
                if (this.#toStop) return;
                await this.#createSleepPromise(this.interval);
            }
        }

        #createSleepPromise(delay) {
            return new Promise((resolve) => setTimeout(resolve, delay));
        }

        #throwError(message) {
            throw new Error(`[${this.#logName}] ${message}`);
        }
    }

    static spammers = new Map();

    static #logName = "TypingSpam";
    static #client = controllers.client.getReadyClient();
    static #chromiumWarned = false;

    static addSpammer(channelId, interval = 3e3) {
        let existingSpammer = this.spammers.get(channelId);
        if (existingSpammer !== undefined) {
            this.#log(`Existing spammer found. Changing interval from ${existingSpammer.interval} to ${interval}.`);
            existingSpammer.interval = interval;
            return;
        }

        let channel = this.#client.channels.get(channelId);
        let spammer = new this.Spammer(channel, interval);

        this.spammers.set(channelId, spammer);
        spammer.start();
    
        this.#log(`Started spamming with interval ${interval}.`);

        if (!this.#chromiumWarned) {
            this.#log("Note for Chromium users: Your Revolt window must be active for the timer to be accurate. This is due to browser's limitation for background processes.\nhttps://stackoverflow.com/a/6032591");
            this.#chromiumWarned = true;
        }
    }

    static async removeSpammer(channelId) {
        let spammer = this.spammers.get(channelId);
        if (spammer === undefined) {
            this.#log("Not spamming the channel.");
            return;
        }

        await spammer.stop();
        this.spammers.delete(channelId);

        this.#log("Spammer stopped and removed.");
    }

    static #log(message) {
        console.log(`[${this.#logName}] ${message}`);
    }
}