Multivit4min icon

Untitled

Multivit4min | PRO | 03/03/20 02:13:34 PM UTC | 0 ⭐ | 445 👁️ | Never ⏰ | []
text |

12.32 KB

|

None

|

0 👍

/

0 👎

"use strict";
///<reference path="node_modules/sinusbot/typings/global.d.ts" />
registerPlugin({
    name: "Vote Reward",
    engine: ">= 1.0.0",
    version: "1.0.0",
    description: "Group Vote Rewards for TeamSpeakServers.org",
    author: "Multivitamin <[email protected]",
    requiredModules: ["http"],
    vars: [{
            name: "key",
            title: "TeamSpeak-Servers API Key",
            type: "string",
            default: ""
        }, {
            name: "sid",
            title: "TeamSpeak-Servers Server ID",
            type: "string",
            default: ""
        }, {
            name: "removeTime",
            title: "remove vote when older than x days (default: 30, -1 to disable removal)",
            type: "number",
            default: 30
        }, {
            name: "rewards",
            title: "Group per vote",
            type: "array",
            vars: [{
                    name: "amount",
                    title: "Amount of Votes in this month",
                    type: "number",
                    default: 0
                }, {
                    name: "group",
                    title: "Group Reward",
                    type: "number",
                    default: 0
                }]
        }]
}, (_, config) => {
    const { key, sid, removeTime, rewards } = config;
    let initialized = false;
    const rewardSorted = rewards.sort((g1, g2) => g1.amount - g2.amount).reverse();
    const availableGroups = rewards.map(g => g.group);
    const removeAfter = removeTime > 0 ? removeTime * 24 * 60 * 60 : -1;
    const engine = require("engine");
    const event = require("event");
    const store = require("store");
    const http = require("http");
    const backend = require("backend");
    const helpers = require("helpers");
    const CHECK_INTERVAL = 60 * 1000;
    class Vote {
        /** initializes store */
        init() {
            if (!Array.isArray(this.getVotes()))
                this.setVotes([]);
        }
        /** current store namespace */
        ns(name) {
            return `${this.namespace}${name}`;
        }
        /** retrieves the namespace for vote items */
        get nsVotes() {
            return this.ns("votes");
        }
        /** retrieves all votes from store */
        getVotes() {
            return store.getInstance(this.nsVotes);
        }
        /** saves all vote items back to store */
        setVotes(items) {
            store.setInstance(this.nsVotes, items);
        }
        /** retrieves all votes */
        saveItem(item) {
            this.setVotes(this.getVotes().map(i => i.hash === item.hash ? item : i));
            return this;
        }
        addItem(item) {
            this.setVotes([...this.getVotes(), item]);
            return this;
        }
        /** requests to add an item to the store */
        requestAdd(item) {
            const hash = this.getHash(item);
            if (this.findHash(hash))
                return false;
            if (this.isOld(item))
                return false;
            const newItem = this.createVoteItem(item);
            this.addItem(newItem);
            this.tryMakeClaim(newItem);
            return true;
        }
        /** checks if the item is too old to get added or still be hold in store */
        isOld(item) {
            if (removeAfter === -1)
                return false;
            return item.timestamp < Math.floor(Date.now() / 1000) - removeAfter;
        }
        /** retrieves the hash value of an item */
        getHash(item) {
            return helpers.MD5Sum(`${item.nickname}${item.timestamp}`);
        }
        /** finds an item with a specific hash */
        findHash(hash) {
            return this.getVotes().find((item) => item.hash === hash);
        }
        /** handles a full client check */
        checkClient(client) {
            this.getUnclaimedByNickname(client.nick()).forEach(item => this.tryMakeClaim(item, client));
            this.checkGroups(client);
        }
        /** tries to claim a possible not claimed item */
        tryMakeClaim(item, client) {
            if (!this.isUnclaimed(item))
                return false;
            client = client ? client : this.getClientByItem(item);
            if (!client)
                return false;
            engine.log(`Client ${client.nick()} (${client.uid()}) claims a vote (${item.hash})`);
            this.flagItemClaimed(item, client.uid());
            this.saveItem(item);
            this.checkGroups(client);
            return true;
        }
        /** checks wether an item is unclaimed or not */
        isUnclaimed(item) {
            return item.claimedBy === null;
        }
        /** tries to retrieve the client for which the vote is for */
        getClientByItem(item) {
            return backend.getClientByName(item.nickname);
        }
        /** validates the groups a client has */
        checkGroups(client) {
            const group = this.getGroupFromVoteCount(this.getVotesByClient(client).length);
            if (group === -1)
                return;
            return this.whiteListGroup(client, [group], availableGroups);
        }
        /**
         * adds a set of groups to a client and removes groups he should not be in
         * @param client the client to add/remove groups from
         * @param group the groups a client can have
         * @param whitelisted whitelisted groups
         */
        whiteListGroup(client, groups, whitelisted) {
            let assign = groups.map(g => String(g));
            const remove = whitelisted.map(w => String(w)).filter(w => !assign.includes(w));
            client.getServerGroups().forEach(group => {
                if (remove.includes(group.id())) {
                    client.removeFromServerGroup(group.id());
                }
                else if (assign.includes(group.id())) {
                    assign.splice(assign.indexOf(group.id()), 1);
                }
            });
            assign.forEach(g => client.addToServerGroup(g));
        }
        /**
         * retrieves the servergroup the amount of counts should get
         * @param votes the votecount to check
         */
        getGroupFromVoteCount(votes) {
            let g = rewardSorted.find(g => g.amount <= votes);
            if (!g) {
                if (rewardSorted.length === 0 || rewardSorted[0].amount > votes) {
                    g = { amount: -1, group: -1 };
                }
                else {
                    g = rewardSorted[rewardSorted.length - 1];
                }
            }
            return g.group;
        }
        /**
         * retrieves the vote items a client has been assigned
         * @param client the client to retrieve
         */
        getVotesByClient(client) {
            return this.getVotes().filter(item => item.claimedBy === client.uid());
        }
        /**
         * gets all unclaimed votes a client nickname can be assigned to
         * @param nick the nickname to check
         */
        getUnclaimedByNickname(nick) {
            return this.getVotes()
                .filter(item => this.isUnclaimed(item))
                .filter(item => item.nickname === nick);
        }
        /**
         * removes all items which are older than the days given in config
         */
        cleanOldItems() {
            const votes = this.getVotes();
            const cleaned = votes.filter(item => !this.isOld(item));
            if (votes.length === cleaned.length)
                return;
            this.setVotes(cleaned);
        }
        /**
         * interval checks
         */
        cron() {
            this.cleanOldItems();
            this.check();
        }
        /**
         * sets an items status to claimed
         * @param item the item which should get claimed
         * @param uid the uid which claimed the item
         */
        flagItemClaimed(item, uid) {
            item.claimedBy = uid;
            item.claimedAt = Date.now();
            return this;
        }
        /**
         * creates a fully valid VoteItem
         * @param item the item which should be upgraded
         */
        createVoteItem(item) {
            return {
                ...item,
                added: Date.now(),
                hash: this.getHash(item),
                claimedBy: null,
                claimedAt: 0
            };
        }
    }
    class TeamSpeakServers extends Vote {
        constructor({ key, sid, createCommand }) {
            super();
            this.namespace = "teamspeakServersDotOrg_";
            this.apikey = key;
            this.sid = sid;
            this.registerCommand(createCommand);
            this.init();
        }
        registerCommand(createCommand) {
            createCommand("vote")
                .help("retrieves the vote link from teamspeak-servers.org")
                .manual("retrieves the vote link for teamspeak-servers.org")
                .manual(`vote daily to get rewarded with servergroups!`)
                .exec((client, _, reply) => {
                reply(`[b][url=https://teamspeak-servers.org/server/${this.sid}/vote/?username=${encodeURI(client.nick())}]VOTE HERE[/url]`);
                reply(`It can take a few minutes until your vote gets counted!`);
                if (removeAfter === -1) {
                    reply(`You have have voted ${this.getVotesByClient(client).length} times!`);
                }
                else {
                    reply(`You have have voted ${this.getVotesByClient(client).length} times in the last ${removeTime} days!`);
                }
            });
        }
        async check() {
            const votes = await this.fetchVotes();
            votes.forEach(vote => this.requestAdd({
                nickname: vote.nickname,
                timestamp: vote.timestamp
            }));
        }
        fetchVotes() {
            return new Promise((fulfill, reject) => {
                http.simpleRequest({
                    method: "GET",
                    url: `https://teamspeak-servers.org/api/?object=servers&element=votes&key=${this.apikey}&format=json`
                }, (err, res) => {
                    if (err)
                        return reject(new Error(`Failed to retrieve data from teamspeak-servers.org api! (Error ${err})`));
                    if (res.statusCode !== 200)
                        return reject(new Error(`Failed to retrieve data from teamspeak-servers.org api! (Code ${res.statusCode})`));
                    try {
                        fulfill(JSON.parse(res.data.toString()).votes);
                    }
                    catch (e) {
                        engine.log(`got response from teamspeak-servers.org: ${res.data.toString()}`);
                        return reject(e);
                    }
                });
            });
        }
    }
    const votings = [];
    function doGlobalCheck() {
        votings.forEach(vote => vote.cron());
        backend.getClients()
            .filter(c => !c.isSelf())
            .forEach(c => votings.forEach(v => v.checkClient(c)));
    }
    event.on("connect", () => {
        if (initialized)
            return;
        initialized = true;
        doGlobalCheck();
    });
    event.on("disconnect", () => {
        initialized = false;
    });
    event.on("clientMove", ({ fromChannel, client }) => {
        if (fromChannel || client.isSelf())
            return;
        votings.forEach(v => v.checkClient(client));
    });
    event.on("clientNick", client => votings.forEach(v => v.checkClient(client)));
    event.on("serverGroupAdded", ev => votings.forEach(v => v.checkClient(ev.client)));
    event.on("serverGroupRemoved", ev => votings.forEach(v => v.checkClient(ev.client)));
    setInterval(() => {
        if (!backend.isConnected())
            return;
        votings.forEach(vote => vote.cron());
    }, CHECK_INTERVAL);
    event.on("load", () => {
        const command = require("command");
        const { createCommand } = command;
        votings.push(new TeamSpeakServers({ key, sid, createCommand }));
        if (backend.isConnected()) {
            initialized = true;
            doGlobalCheck();
        }
    });
});

Comments