Friday, September 20, 2024 12:15:14 AM
> settings

Customize


Authenticate

> database.js
const CooldownService = require("../services/cooldown_service");

const DEFAULTS = {
    CLIENT_PROFILE: {
        id: "",
        steam_uid: "",
        preferences: {}
    },
    SERVER: {
        id: "",
        name: "",
        ip: "",
        port: "",
        price_per_object: "",
        territory_lifetime: "",
        restart_hour: 0,
        restart_min: 0,
        start_time: "",
        pay_tax: 0,
        upgrade_tax: 0,
        is_premium: true,
        version: "0.8.0"
    }
};

module.exports = class Database {
    constructor(r, bot) {
        this.r = r;
        this.ESMBot = bot;
        this.util = this.ESMBot.util;
    }

    async registerClient(discord_id, steam_uid) {
        try {
            await this.r.table("users").insert({
                id: discord_id,
                steam_uid: steam_uid,
                cooldowns: {},
                preferences: {}
            }, {
                conflict: 'update'
            }).run();
            return this.r.table("users").get(discord_id).run();
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async isRegistered(discordID) {
        try {
            let isRegistered = await this.r.table("users").get(discordID).run();
            return (isRegistered == null) ? false : !(isRegistered.steam_uid == null || isRegistered.steam_uid === "");
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async authenticateServer(token) {
        try {
            if (this.util.isEmpty(token)) return null;
            let ret = await this.r.table("servers").filter({
                key: token
            }).run();
            if (this.util.isEmpty(ret)) return null;
            return ret[0];
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async getSteamUIDFromID(discordID) {
        try {
            let ret = await this.r.table("users").get(discordID).run();
            return (ret == null) ? null : ret.steam_uid;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async getSteamUIDFromTag(discordTag) {
        try {
            if (this.util.regex.steamUID.only.test(discordTag)) return {
                uid: discordTag
            };
            if (!(this.util.regex.discordTag.only.test(discordTag))) return {
                message: "Malformed discord tag"
            };
            let discordUser = await this.ESMBot.getUser(discordTag);
            if (this.util.isEmpty(discordUser)) return {
                message: "I've never seen this user in my life"
            };
            let isRegistered = await this.isRegistered(discordUser.id);
            if (!isRegistered) return {
                message: `This user has not registered yet. Let them know they can register by running \`!register\``
            };
            let steamUID = await this.getSteamUIDFromID(discordUser.id);
            if (this.util.isEmpty(steamUID)) return {
                message: "I was unable to retrieve this user's steam UID"
            };
            return {
                uid: steamUID
            };
        } catch (err) {
            this.ESMBot.logger.error(err);
            return {
                message: this.ESMBot.errors.GENERIC_ERROR
            };
        }
    }

    //////////////////////////////////////////////////////
    async serverInitialization(serverID, information) {
        try {
            await this.r.table("servers").insert({
                id: serverID,
                community_id: this.util.getCommunityID(serverID),
                name: information.server_name,
                price_per_object: information.price_per_object,
                territory_lifetime: information.territory_lifetime,
                restart_hour: information.server_restart[0],
                restart_min: information.server_restart[1],
                start_time: information.server_start_time,
                pay_tax: information.pay_tax_percentage || 0,
                upgrade_tax: information.upgrade_tax_percentage || 0,
                last_updated: this.ESMBot.moment.utc().format(),
                version: information.server_version || "0.8.0"
            }, {
                conflict: 'update'
            }).run();

            // Delete all existing territories for this server
            await this.r.table("territories").filter({
                server_id: serverID
            }).delete().run();

            for (let attribute in information) {
                if (/^territory_level/i.test(attribute)) {
                    let territory = information[attribute];
                    this.r.table("territories").insert({
                        server_id: serverID,
                        level: territory.level,
                        radius: territory.radius,
                        purchase_price: territory.purchase_price,
                        object_count: territory.object_count,
                        last_updated: this.ESMBot.moment.utc().format()
                    }).run();
                }
            }

            return await this.r.table("servers").get(serverID).run();
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async getCommunity(id) {
        try {
            return await this.r.table("communities").get(this.util.getCommunityID(id)).run();
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async getCommunityFromGuildID(id) {
        try {
            let community = await this.r.table("communities").filter({
                guild_id: id
            }).run();
            return community[0];
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async getServerUptime(serverID) {
        try {
            let ret = await this.r.table("servers").get(serverID).run();
            if (ret == null) return 0;
            let now = this.ESMBot.moment.utc(),
                then = this.ESMBot.moment.utc(ret.start_time);
            return this.util.parseTime(now.diff(then, "s"));
        } catch (err) {
            this.ESMBot.logger.error(err);
            return 0;
        }
    }

    //////////////////////////////////////////////////////
    async getLoggingChannel(id) {
        try {
            let ret = await this.r.table("communities").get(this.util.getCommunityID(id)).run();
            if (ret == null) return "";
            return ret.logging_channel;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return "";
        }
    }

    //////////////////////////////////////////////////////
    async getDiscordIDsFromSteamUID(steamUID) {
        try {
            let ret = await this.r.table("users").filter({
                steam_uid: steamUID
            }).pluck("id").run();
            if (this.util.isEmpty(ret)) return [];
            let output = [];
            for (let row of ret) {
                output.push(row.id);
            }
            return output;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return [];
        }
    }

    //////////////////////////////////////////////////////
    async getGuildID(communityID) {
        try {
            let ret = await this.r.table("communities").get(communityID).default({
                guild_id: ""
            }).pluck("guild_id").run();
            if (this.util.isEmpty(ret)) return "";
            return ret.guild_id;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return "";
        }
    }

    //////////////////////////////////////////////////////
    async getTerritoryInfo(serverID, level) {
        try {
            let server = await this.r.table("servers").get(serverID).run();
            if (server == null) return null;

            let territories = await this.r.table("territories").filter({
                server_id: serverID,
                level: level + 1
            }).run();

            let info = {
                price_per_object: server.price_per_object,
                territory_lifetime: server.territory_lifetime,
                pay_tax: server.settings.taxes.territory_payment,
                upgrade_tax: server.settings.taxes.territory_upgrade
            };

            if (!this.util.isEmpty(territories)) {
                info.upgrade_radius = territories[0].radius;
                info.upgrade_price = territories[0].purchase_price;
                info.upgrade_object_count = territories[0].object_count;
            }

            return info;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async getServer(serverID) {
        try {
            let ret = await this.r.table("servers").get(serverID).run();
            if (this.util.isEmpty(ret)) return {};
            return ret;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return {};
        }
    }

    //////////////////////////////////////////////////////
    async getServers(communityID) {
        try {
            let ret = await this.r.table("servers").filter({
                community_id: communityID
            }).run();
            if (ret == null) return {};
            return ret;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async getServerIDs(communityID) {
        try {
            if (!this.isValidCommunity(communityID)) return [];
            let ret = await this.r.table("servers").filter({
                community_id: communityID
            }).run();
            if (this.util.isEmpty(ret)) return [];
            return ret.map(server => server.id);
        } catch (err) {
            this.ESMBot.logger.error(err);
            return [];
        }
    }

    //////////////////////////////////////////////////////
    async getServerIDsAndInfo(communityID) {
        try {
            if (!this.isValidCommunity(communityID)) return [];
            let ret = await this.r.table("servers").filter({
                community_id: communityID
            }).run();
            if (this.util.isEmpty(ret)) return [];
            return ret.map(server => {
                return {
                    id: server.id,
                    name: server.name || "N/A",
                    ip: server.ip,
                    port: server.port
                };
            });
        } catch (err) {
            this.ESMBot.logger.error(err);
            return [];
        }
    }

    //////////////////////////////////////////////////////
    async getAllLoggingChannels() {
        try {
            let ret = await this.r.table("communities").run();
            if (ret == null) return null;
            let channels = [];
            await ret.each((err, community) => {
                if (err) return;
                if (community.broadcast_notif && community.logging_channel !== "") {
                    channels.push(channel);
                }
            }).catch(err => {});
            return channels;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async isCommandAllowedHere(command) {
        try {
            let user = command.message.author;
            let community = command.community;
            let guildMember = await command.message.guild.members.fetch(user.id, true, false);
            let isDifferentCommunity = false;

            if (this.util.isEmpty(guildMember)) {
                return this.ESMBot.errors.format(user, "NULL_USER");
            }

            let configurations = community.command_configuration;
            if (command.params.serverID != null || command.params.communityID != null) {
                let id = this.util.getCommunityID(command.params.serverID || command.params.communityID);
                if (id !== community.id) {
                    // Check if we are in player mode
                    if (!community.player_mode_enabled) {
                        return this.ESMBot.errors.format(user, "COMMAND_NOT_ALLOWED");
                    }

                    community = await this.r.table("communities").get(id).run();
                    configurations = community.command_configuration;
                    isDifferentCommunity = true;
                }
            }

            // First check if we have any configuration for this command on the community.
            if (!this.util.isEmpty(configurations) && configurations.hasOwnProperty(command.name)) {
                let config = configurations[command.name];

                if (!_.isUndefined(guildMember) && !isDifferentCommunity) {
                    if (guildMember.permissions.has("ADMINISTRATOR")) return true;
                }

                if (config.hasOwnProperty("enabled")) {
                    if (!config.enabled) return this.ESMBot.errors.format(user, "COMMAND_DISABLED");
                }

                if (config.hasOwnProperty("permissions")) {

                    if (!_.isUndefined(guildMember)) {
                        for (let perm of config.permissions) {
                            if (guildMember.roles.cache.has(perm)) return true;
                        }
                    }

                    return this.ESMBot.errors.format(user, "NO_PERMS");
                }

                if (config.hasOwnProperty("allowed_in_text_channels")) {
                    return config.allowed_in_text_channels ? true : this.ESMBot.errors.format(user, "DIRECT_MESSAGE_ONLY");
                }
            }

            // if we are down here, it means that we didn't hit anything above, check against the defaults
            if (!_.isUndefined(guildMember) && !isDifferentCommunity) {
                if (guildMember.permissions.has("ADMINISTRATOR")) return true;
            }

            if (this.util.isEmpty(command.configuration)) return true;

            if (command.configuration.hasOwnProperty("enabled")) {
                if (!command.configuration.enabled.default) return this.ESMBot.errors.format(user, "COMMAND_DISABLED");
            }

            if (command.configuration.hasOwnProperty("permissions")) {
                if (!_.isUndefined(guildMember)) {
                    for (let perm in command.configuration.permissions.default) {
                        if (guildMember.roles.cache.has(perm)) return true;
                    }
                }

                return this.ESMBot.errors.format(user, "NO_PERMS");
            }

            if (command.configuration.hasOwnProperty("allowedInTextChannels")) {
                return command.configuration.allowedInTextChannels.default ? true : this.ESMBot.errors.format(user, "DIRECT_MESSAGE_ONLY");
            }

            return this.ESMBot.errors.format(command.message.author, "FAILED_IS_COMMAND_ALLOWED_HERE");
        } catch (err) {
            this.ESMBot.logger.error(err);
            return this.ESMBot.errors.format(command.message.author, "ERROR_IS_COMMAND_ALLOWED_HERE");
        }
    }

    //////////////////////////////////////////////////////
    async isCommandAllowedHerePM(command) {
        try {
            let user = command.message.author;
            let community = await this.r.table("communities").get(command.communityID).run();
            let configurations = community.command_configuration;

            if (!this.util.isEmpty(configurations) && configurations.hasOwnProperty(command.name)) {
                let config = configurations[command.name];

                if (config.hasOwnProperty("enabled")) {
                    return config.enabled ? true : this.ESMBot.errors.format(user, "COMMAND_DISABLED");
                }

                if (config.hasOwnProperty("permissions")) {
                    return this.ESMBot.errors.format(user, "NO_PERMS");
                }
            } else {
                if (this.util.isEmpty(command.configuration)) return true;

                if (command.configuration.hasOwnProperty("enabled")) {
                    return command.configuration.enabled.default ? true : this.ESMBot.errors.format(user, "COMMAND_DISABLED");
                }

                if (command.configuration.hasOwnProperty("permissions")) {
                    return this.ESMBot.errors.format(user, "NO_PERMS");
                }
            }

            return true;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return this.ESMBot.errors.format(command.message.author, "ERROR_IS_COMMAND_ALLOWED_HERE");
        }
    }

    //////////////////////////////////////////////////////
    async initializeServers() {
        try {
            let ret = await this.r.table("servers").pluck("id").run();
            if (ret == null) return {};
            let servers = {};
            _.each(ret, (row) => {
                servers[row.id] = null;
            });
            return servers;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return {};
        }
    }

    //////////////////////////////////////////////////////
    async addCooldown(command) {
        try {
            let cooldownService = new CooldownService(this, command);
            await cooldownService.addCooldown();
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async getCooldown(command) {
        try {
            let cooldownService = new CooldownService(this, command);
            return await cooldownService.getCooldown();
        } catch (err) {
            this.ESMBot.logger.error(err);
            return this.ESMBot.moment.utc();
        }
    }

    //////////////////////////////////////////////////////
    async resetCooldown(communityID, commandName, userID) {
        try {
            let cooldownService = new CooldownService(this, {
                message: null
            });

            return await cooldownService.resetCooldown(communityID, commandName, userID);
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async getCommunityID(guildID) {
        try {
            let ret = await this.r.table("communities").filter({
                guild_id: guildID
            }).run();

            if (this.util.isEmpty(ret)) {
                return null;
            }

            return ret[0].id;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async addGuild(guild) {
        try {
            let count = 0,
                id = "";
            while (count < 10000) {
                id = this.util.generateKey(4, true, false, false, false, false);
                let ret = await this.r.table("communities").get(id).run();
                if (ret == null) break;
                count++;
            }

            if (count === 10000) {
                // this.ESMBot.client.users.cache.get(guild.ownerID).send(this.ESMBot.errors.COMMUNITY_IDS_RAN_OUT);
                return;
            }

            await this.r.table("communities").insert({
                id: id,
                guild_id: guild.id,
                name: guild.name,
                premium_state: "-1",
                command_configuration: {},
                territory_admins: {},
                player_mode_enabled: true
            }, {
                conflict: "update"
            }).run();

            let notifications = [
                //////////////////////////
                // XM8 Notifications
                //////////////////////////
                {
                    category: "xm8",
                    type: "base-raid",
                    title: "Base Raid `({{ territoryID }})`",
                    message: "**{{ territoryName }}** is being raided!",
                    color: "#DE7839",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "flag-stolen",
                    title: "Flag Stolen `({{ territoryID }})`",
                    message: "**{{ territoryName }}'s** flag has been stolen! Go get it back!",
                    color: "#C62551",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "flag-restored",
                    title: "Flag Restored `({{ territoryID }})`",
                    message: "**{{ territoryName }}'s** flag has been restored! Good job getting it back!",
                    color: "#9FDE3A",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "protection-money-due",
                    title: "Protection Money Due `({{ territoryID }})`",
                    message: "**{{ territoryName }}'s** protection money is due today!\nUse `!pay {{ serverID }} {{ territoryID }}` to make a payment!",
                    color: "#DECA39",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "protection-money-paid",
                    title: "Protection Money Paid `({{ territoryID }})`",
                    message: "**{{ territoryName }}'s** protection money has been paid",
                    color: "#3ED3FB",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "charge-plant-started",
                    title: "Charge Planted `({{ territoryID }})`",
                    message: "Someone set us up the bomb. **{{ territoryName }}** is about to go BOOM!",
                    color: "#DE7839",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "grind-started",
                    title: "Grinding Started `({{ territoryID }})`",
                    message: "Some scalliwag is tryna grind yer locks! **{{ territoryName }}** is being raided!",
                    color: "#DE7839",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "hack-started",
                    title: "Hacking Started `({{ territoryID }})`",
                    message: "H4x0rs are trying to get into your stuff! **{{ territoryName }}** is being robbed! ",
                    color: "#DE7839",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "flag-steal-started",
                    title: "Flag Steal Started `({{ territoryID }})`",
                    message: "Someone is trying to steal **{{ territoryName }}'s** flag!",
                    color: "#DE7839",
                    user_id: "137709767954137088"
                }, {
                    category: "xm8",
                    type: "marxet-item-sold",
                    title: "Item sold on MarXet",
                    message: "You just sold **{{ item }}** for **{{ amount }}** poptabs",
                    color: "#9FDE3A",
                    user_id: "137709767954137088"
                },

                //////////////////////////
                // Gambling Notifications
                //////////////////////////
                {
                    category: "gambling",
                    type: "win",
                    title: "Damn, you won **{{ userName }}**",
                    message: "You won **{{ amountChanged }}** poptabs. You now have **{{ lockerAfter }}** poptabs",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "win",
                    title: "Slow down **{{ userName }}**",
                    message: "You just won **{{ amountChanged }}** poptabs. You have **{{ lockerAfter }}** poptabs in your locker",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "win",
                    title: "Looks like drinks are on **{{ userName }}**",
                    message: "They just won **{{ amountChanged }}** poptabs. **{{ userTag }}**, you have **{{ lockerAfter }}** poptabs now.",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "win",
                    title: "Hey **{{ userName }}**",
                    message: "Yes, you just won **{{ amountChanged }}** poptabs. Yes, your locker balance is now **{{ lockerAfter }}** poptabs. But did you know gambling is a serious addiction? We are concerned about you, so we found [this](http://www.gamblersanonymous.org/ga/) for you.",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "win",
                    title: "Guess what **{{ userName }}**?",
                    message: "You're one step closer to a new attack helicopter! You won **{{ amountChanged }}** poptabs, and have a total of **{{ lockerAfter }}** poptabs in your locker",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "win",
                    title: "**{{ userName }}**",
                    message: "Congratulations, you won **{{ amountChanged }}** poptabs, this is just enough to afford one month of FarmersOnly.com, **PREMIUM**",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "loss",
                    title: "RIPs, you are not a winner **{{ userName }}**",
                    message: "You lost **{{ amountChanged }}** poptabs. You have **{{ lockerAfter }}** poptabs remaining.",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "loss",
                    title: "I'm sorry **{{ userName }}**",
                    message: "You just lost **{{ amountChanged }}** poptabs, but don't worry! Try gambling again and maybe you'll win it back. :wink:\nYou have **{{ lockerAfter }}** poptabs left.",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "loss",
                    title: "Maybe next time **{{ userName }}**!",
                    message: "You lost **{{ amountChanged }}** poptabs. You have **{{ lockerAfter }}** poptabs remaining.",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "loss",
                    title: "Don't quit your day job **{{ userName }}**",
                    message: "I'm serious, don't. You can't afford to. You lost **{{ amountChanged }}** poptabs and you have **{{ lockerAfter }}** poptabs left.",
                    color: "random",
                    user_id: "137709767954137088"
                }, {
                    category: "gambling",
                    type: "loss",
                    title: "Look **{{ userName }}**, there is still hope.",
                    message: "You just lost **{{ amountChanged }}** poptabs, but that doesn't have to happen anymore. This [website](http://www.gamblersanonymous.org/ga/) has some awesome resources to help you get over your addiction. If you won't do it for yourself, please, do it for your community. You have **{{ lockerAfter }}** poptabs left.",
                    color: "random",
                    user_id: "137709767954137088"
                }
            ];

            _.each(notifications, (notification) => {
                this.r.table("notifications").insert({
                    community_id: id,
                    category: notification.category,
                    type: notification.type,
                    title: notification.title,
                    message: notification.message,
                    color: notification.color,
                    user_id: notification.user_id
                }).run();
            });

            return await this.r.table("communities").get(id).run();
        } catch (err) {
            this.ESMBot.logger.error(err);
            return "";
        }
    }

    //////////////////////////////////////////////////////
    async removeGuild(guild) {
        try {
            let ret = await this.r.table("communities").filter({
                guild_id: guild.id
            }).run();
            await ret.each((err, row) => {
                if (err) return;
                // Remove the community
                this.r.table("communities").get(row.id).delete().run();

                // Delete server info
                this.r.table("servers").filter({
                    community_id: row.id
                }).delete().run();

                // Delete territory info
                this.r.table("territories").filter(
                    this.r.row("server_id").match(`^${row.id}_`)
                ).delete().run();

                // Delete notifications
                this.r.table("notifications").filter({
                    community_id: row.id
                }).delete().run();
            }).catch(err => {});
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async deactivateGuild(guild) {
        try {
            let communityID = await this.getCommunityID(guild.id);
            if (this.util.isEmpty(communityID)) return false;
            let ret = await this.r.table("communities").get(communityID).update({
                deactivated_at: this.ESMBot.moment.utc().format()
            }).run();
            return ret.errors === 0;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async reactivateGuild(communityID) {
        try {
            let ret = await this.r.table("communities").get(communityID).update({
                deactivated_at: null
            }).run();
            return ret.errors === 0;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    async isGuildDeactivated(communityID) {
        try {
            let ret = await this.r.table("communities").get(communityID).run();
            if (this.util.isEmpty(ret)) return true;
            return !this.util.isEmpty(ret.deactivated_at);
        } catch (err) {
            this.ESMBot.logger.error(err);
            return true;
        }
    }

    //////////////////////////////////////////////////////
    async isValidCommunity(id) {
        try {
            if (this.util.isEmpty(id)) return false;
            id = this.util.getCommunityID(id);
            let ret = await this.r.table("communities").get(id).run();
            return ret ? true : false;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async isValidServer(serverID) {
        try {
            let ret = await this.r.table("servers").get(serverID).run();
            return ret ? true : false;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async getAllServerIDs() {
        try {
            let ret = await this.r.table("servers").run();
            if (this.util.isEmpty(ret)) return [];
            return ret.map(server => server.server_id);
        } catch (err) {
            this.ESMBot.logger.error(err);
            return [];
        }
    }

    //////////////////////////////////////////////////////
    async setPremiumStatus(communityID, state) {
        try {
            this.r.table("communities").get(communityID).update({
                premium_state: state
            }).run();

            switch (state) {
                case "0":
                    this.r.table("servers").filter({
                        community_id: communityID
                    }).update({
                        is_premium: false
                    }).run();
                    break;
                case "-1":
                    this.r.table("servers").filter({
                        community_id: communityID
                    }).update({
                        is_premium: true
                    }).run();
                    break;
            }

            return true;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async changeCommunityID(oldCommunityID, newCommunityID) {
        try {
            let communityProfile = await this.r.table("communities").get(oldCommunityID).run();
            if (communityProfile == null) return false;
            communityProfile.id = newCommunityID;

            // Update server info
            this.r.table("servers").filter({
                community_id: oldCommunityID
            }).update({
                community_id: newCommunityID
            }).run();

            // Delete territory info (it will regenerate next restart)
            this.r.table("territories").filter(
                this.r.row("server_id").match(`^${oldCommunityID}_`)
            ).delete().run();

            let deleted = await this.r.table("communities").get(oldCommunityID).delete().run();
            if (deleted.errors !== 0) return false;
            let inserted = await this.r.table("communities").insert(communityProfile).run();
            return inserted.errors === 0;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async allowReconnectMessages(id) {
        try {
            id = this.util.getCommunityID(id);
            let ret = await this.r.table("communities").get(id).run();
            if (ret == null) return false;
            return ret.logging_channel !== "" && ret.reconnect_notif;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async isPremium(_serverID) {
        return true;
    }

    //////////////////////////////////////////////////////
    async getPremiumState(_communityID) {
        return "-1";
    }

    //////////////////////////////////////////////////////
    async deregisterUser(id) {
        try {
            let ret = await this.r.table("users").get(id).delete().run();
            if (ret == null) return false;
            return ret.errors === 0;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async getNotificationPreference(id, serverID, type) {
        try {
            let profile = await this.r.table("users").get(id).run();
            if (this.util.isEmpty(profile)) return false;
            let preferences = profile.preferences;
            if (this.util.isEmpty(preferences)) return true;
            if (preferences[serverID] == null) return true;
            let preference = preferences[serverID];
            return preference[type] ? preference[type] : false;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async updateNotificationPreference(id, serverID, type, state) {
        try {
            let profile = await this.r.table("users").get(id).run();
            if (this.util.isEmpty(profile)) return false;
            let preferences = profile.preferences;
            if (preferences == null) {
                preferences = {};
            }
            if (preferences[serverID] == null) {
                preferences[serverID] = {};
            }
            preferences[serverID][type] = state;
            let ret = await this.r.table("users").get(id).update({
                preferences: preferences
            }).run();
            return ret.errors === 0;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async getNotificationPreferences(id) {
        try {
            let profile = await this.r.table("users").get(id).run();
            if (this.util.isEmpty(profile)) return null;
            let preferences = profile.preferences;
            return this.util.isEmpty(preferences) ? null : preferences;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async cleanOldServers() {
        try {
            let ret = await this.r.table("servers").pluck("community_id", "last_updated", "server_id").run();
            if (this.util.isEmpty(ret)) return;

            for (let i = 0; i < ret.length; i++) {
                let server = ret[i],
                    lastUpdated = this.ESMBot.moment.utc(server.last_updated),
                    timeDiff = this.ESMBot.moment.utc().diff(lastUpdated, "d");
                if (timeDiff >= 7) {
                    let ret = await this.r.table("communities").get(server.community_id).run();
                    if (this.util.isEmpty(ret)) continue;
                    if (timeDiff >= 14) {
                        this.ESMBot.send(ret.logging_channel, `Hey there!\nWe've noticed you haven't started server '**${server.server_id}**' in last 14 days. We have unlinked your server from Exile Server Manager meaning serverID \`${server.server_id}\` and it's esm.key is no longer valid.\nFear not! If you would like to link your server back up, just head over to our web portal (https://www.esmbot.com/portal) and add it back. :)`);

                        // Delete server info
                        this.r.table("servers").filter({
                            server_id: server.server_id
                        }).delete().run();

                        // Delete server auth
                        this.r.table("server_auth").filter({
                            server_id: server.server_id
                        }).delete().run();

                        // Delete territory info
                        this.r.table("territories").filter({
                            server_id: server.server_id
                        }).delete().run();

                        // Log
                        this.ESMBot.send(this.ESMBot.config.LOGGING.CLEANUP, {
                            title: "[CLEANUP] Removed stale server",
                            color: this.ESMBot.colors.WHITE,
                            fields: [{
                                    name: "Community ID",
                                    value: server.community_id,
                                    inline: true
                                },
                                {
                                    name: "Server ID",
                                    value: server.server_id,
                                    inline: true
                                }
                            ]
                        });
                    } else if (timeDiff === 7) {
                        // Send at 7 days
                        this.ESMBot.send(ret.logging_channel, `Hey there!\nWe've noticed you haven't started server '**${server.server_id}**' in the last 7 days. We are giving you a heads-up that your server will be unlinked from Exile Server Manager in 7 days if it is not connected to the bot within that time.`);

                        // Log
                        this.ESMBot.send(this.ESMBot.config.LOGGING.CLEANUP, {
                            title: "[AUTOMATIC] Stale server warning sent",
                            color: this.ESMBot.colors.WHITE,
                            fields: [{
                                    name: "Community ID",
                                    value: server.community_id,
                                    inline: true
                                },
                                {
                                    name: "Server ID",
                                    value: server.server_id,
                                    inline: true
                                },
                                {
                                    name: "Deletion Date",
                                    value: this.ESMBot.moment.utc().add(7, "d").format("MMMM Do YYYY, h:mm:ss a"),
                                    inline: true
                                }
                            ]
                        });
                    }
                }
            }
        } catch (err) {
            this.ESMBot.logger.error(err);
            return;
        }
    }

    //////////////////////////////////////////////////////
    async cleanOldGuilds() {
        try {
            let ret = await this.r.table("communities").run();
            if (this.util.isEmpty(ret)) return;
            for (let i = 0; i < ret.length; i++) {
                let guild = ret[i];
                if (guild.deactivated_at == null) continue;
                let lastUpdated = this.ESMBot.moment.utc(guild.deactivated_at),
                    timeDiff = this.ESMBot.moment.utc().diff(lastUpdated, "d"),
                    communityID = guild.id;
                if (timeDiff >= 3) {
                    // Delete server info
                    this.r.table("servers").filter({
                        community_id: communityID
                    }).delete().run();

                    // Delete server auth
                    this.r.table("server_auth").filter(
                        this.r.row("server_id").match(`^${communityID}_`)
                    ).delete().run();

                    // Delete territory info
                    this.r.table("territories").filter(
                        this.r.row("server_id").match(`^${communityID}_`)
                    ).delete().run();

                    // Remove the guild
                    this.r.table("communities").get(communityID).delete().run();

                    // Log
                    this.ESMBot.send(this.ESMBot.config.LOGGING.CLEANUP, {
                        title: "[CLEANUP] Removed stale guild",
                        description: `Community ID: ${communityID}`,
                        color: this.ESMBot.colors.WHITE
                    });
                }
            }
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async cleanOldLinks() {
        try {
            let ret = await this.r.table("website_data").run();
            if (this.util.isEmpty(ret)) return;
            for (let i = 0; i < ret.length; i++) {
                let entry = ret[i];
                let expiresAt = this.ESMBot.moment.utc(entry.expires_at),
                    timeDiff = this.ESMBot.moment.utc().diff(expiresAt, "s");
                if (timeDiff >= 0) {
                    this.r.table("website_data").get(entry.token).delete().run();
                }
            }
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async cleanOldRequests() {
        try {
            let ret = await this.r.table("request_data").run();
            if (this.util.isEmpty(ret)) return;
            for (let entry of ret) {
                let expiresAt = this.ESMBot.moment.utc(entry.expires_at),
                    timeDiff = this.ESMBot.moment.utc().diff(expiresAt, "s");
                if (timeDiff >= 0) {
                    this.r.table("request_data").get(entry.id).delete().run();
                }
            }
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async cleanOldTerritories() {
        try {
            let ret = await this.r.table("territories").run();
            if (this.util.isEmpty(ret)) return;
            for (let entry of ret) {
                let lastUpdated = this.ESMBot.moment.utc(entry.last_updated),
                    timeDiff = this.ESMBot.moment.utc().diff(lastUpdated, "d");
                if (timeDiff >= 3) {
                    this.r.table("territories").get(entry.id).delete().run();
                }
            }
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async generateLinkHash(data, expireAt) {
        try {
            let ret = await this.r.table("parsed_logs").insert({
                data: data,
                expires_at: expireAt
            }, {
                returnChanges: true
            }).run();
            return ret.generated_keys[0];
        } catch (err) {
            this.ESMBot.logger.error(err);
            return "";
        }
    }

    //////////////////////////////////////////////////////
    async addRequest(targetUID, data) {
        try {
            let ret = await this.r.table("request_data").insert({
                target_uid: targetUID,
                data: data,
                expires_at: this.ESMBot.moment.utc().add(1, "d").format()
            }, {
                conflict: "update"
            }).run();
            return ret.errors === 0;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async getRequests(targetUID, filters) {
        try {
            let ret = await this.r.table("request_data").filter({
                target_uid: targetUID
            }).run();
            if (this.util.isEmpty(ret)) return [];
            if (this.util.isEmpty(filters)) return ret;
            let rows = [];
            for (let i = 0; i < ret.length; i++) {
                let req = ret[i].data;
                let match = false;
                for (let name in filters) {
                    if (req.hasOwnProperty(name)) {
                        match = req[name] === filters[name];
                    }
                }
                if (match) {
                    req.id = ret[i].id;
                    rows.push(req);
                }
            }

            if (_.isEmpty(rows)) {
                return [];
            } else {
                return rows[0];
            }

        } catch (err) {
            this.ESMBot.logger.error(err);
            return [];
        }
    }

    //////////////////////////////////////////////////////
    async removeRequest(id) {
        try {
            let ret = await this.r.table("request_data").get(id).delete().run();
            return ret.errors === 0;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async getStats(type) {
        try {
            let count = 0;
            switch (type) {
                case "community": {
                    count = await this.r.table("communities").count().run();
                    break;
                }
                case "servers": {
                    count = await this.r.table("servers").count().run();
                    break;
                }
                case "users": {
                    count = await this.r.table("users").count().run();
                    break;
                }
            }
            return count;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return 0;
        }
    }

    //////////////////////////////////////////////////////
    async addGambleWin(discordID, serverID, amountWon) {
        try {
            let ret = await this.r.table("users").get(discordID).default({
                gambling: {}
            }).pluck("gambling").run();
            let info = {};
            if (ret.hasOwnProperty("gambling") && ret.gambling.hasOwnProperty(serverID)) {
                let temp = ret.gambling[serverID];
                temp.wins++;
                temp.won_poptabs += amountWon;
                if (temp.last_action === "win") {
                    temp.current_streak++;
                } else {
                    temp.current_streak = 1;
                }
                if (temp.current_streak > temp.win_streak) {
                    temp.win_streak = temp.current_streak;
                }
                temp.last_action = "win";
                info = ret.gambling;
            } else {
                info[serverID] = {
                    wins: 1,
                    loss: 0,
                    won_poptabs: amountWon,
                    loss_poptabs: 0,
                    current_streak: 1,
                    win_streak: 1,
                    loss_streak: 0,
                    last_action: "win"
                };
            }

            this.r.table("users").get(discordID).update({
                gambling: info
            }).run();
            return info[serverID];
        } catch (err) {
            this.ESMBot.logger.error(err);
            return {};
        }
    }

    //////////////////////////////////////////////////////
    async addGambleLoss(discordID, serverID, amountLoss) {
        try {
            let ret = await this.r.table("users").get(discordID).run();
            let info = {};
            if (ret.hasOwnProperty("gambling") && ret.gambling.hasOwnProperty(serverID)) {
                let temp = ret.gambling[serverID];
                temp.loss++;
                temp.loss_poptabs += amountLoss;
                if (temp.last_action === "loss") {
                    temp.current_streak++;
                } else {
                    temp.current_streak = 1;
                }
                if (temp.current_streak > temp.loss_streak) {
                    temp.loss_streak = temp.current_streak;
                }
                temp.last_action = "loss";
                info = ret.gambling;
            } else {
                info[serverID] = {
                    wins: 0,
                    loss: 1,
                    won_poptabs: 0,
                    loss_poptabs: amountLoss,
                    current_streak: 1,
                    win_streak: 0,
                    loss_streak: 1,
                    last_action: "loss"
                };
            }

            this.r.table("users").get(discordID).update({
                gambling: info
            }).run();
            return info[serverID];
        } catch (err) {
            this.ESMBot.logger.error(err);
            return {};
        }
    }

    //////////////////////////////////////////////////////
    async getGambleInfo(discordID, serverID) {
        try {
            let ret = await this.r.table("users").get(discordID).run();
            if (this.util.isEmpty(ret)) throw `Could not find user: ${discordID}`;
            if (this.util.isNull(ret.gambling)) {
                return {
                    wins: 0,
                    loss: 0,
                    won_poptabs: 0,
                    loss_poptabs: 0,
                    current_streak: 0,
                    win_streak: 0,
                    loss_streak: 0,
                    last_action: ""
                };
            }
            return ret.gambling.hasOwnProperty(serverID) ?
                ret.gambling[serverID] : {
                    wins: 0,
                    loss: 0,
                    won_poptabs: 0,
                    loss_poptabs: 0,
                    current_streak: 0,
                    win_streak: 0,
                    loss_streak: 0,
                    last_action: ""
                };
        } catch (err) {
            this.ESMBot.logger.error(err);
            return {
                wins: 0,
                loss: 0,
                won_poptabs: 0,
                loss_poptabs: 0,
                current_streak: 0,
                win_streak: 0,
                loss_streak: 0,
                last_action: ""
            };
        }
    }

    //////////////////////////////////////////////////////
    async addLog(log, type = "info", file = "", method = "", category = "info") {
        this.r.table("logs").insert({
            log: log,
            type: type,
            file: file,
            method: method,
            category: category,
            created_at: this.ESMBot.moment().format()
        }).run();
    }

    //////////////////////////////////////////////////////
    async getBroadcastUsers(serverID) {
        try {
            let ret = await this.r.table("users").filter((row) => {
                return row("preferences")(serverID)("custom").eq(true);
            }).pluck("id").run();
            return ret;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return [];
        }
    }

    //////////////////////////////////////////////////////
    async getServerVersion(serverID) {
        try {
            let ret = await this.r.table("servers").get(serverID).default(DEFAULTS.SERVER).pluck("version").run();
            return this.util.isNull(ret.version) ? "0.8.0" : ret.version;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return "0.8.0";
        }
    }

    //////////////////////////////////////////////////////
    async getCommand(commandID) {
        try {
            let ret = await this.r.table("commands").get(commandID).run();
            return ret;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async startCommand(command) {
        try {
            let ret;
            if (this.util.isEmpty(command.id)) {
                ret = await this.r.table("commands").insert({
                    created_at: this.ESMBot.moment.utc().format()
                }, {
                    returnChanges: true
                }).run();
                ret = ret.changes[0].new_val;
            } else {
                ret = await this.r.table("commands").get(command.id).run();
            }
            return ret;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return null;
        }
    }

    //////////////////////////////////////////////////////
    async updateCommand(commandID, info) {
        try {
            let ret = await this.r.table("commands").get(commandID).update(info).run();
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async finishCommand(commandID, info) {
        try {
            let ret = await this.r.table("commands").get(commandID).update({
                response_package: info,
                received_at: this.ESMBot.moment.utc().format()
            }).run();
            return ret.errors === 0;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return false;
        }
    }

    //////////////////////////////////////////////////////
    async getBotInformation() {
        try {
            let ret = await this.r.table("bots").get("esm_bot").run();
            return ret;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return {
                type: "PLAYING",
                message: "!register"
            };
        }
    }

    //////////////////////////////////////////////////////
    async getServerRewards(serverID) {
        try {
            let ret = await this.r.table("servers").get(serverID).run();
            if (this.util.isEmpty(ret)) return {};
            return ret.rewards;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return {};
        }
    }

    //////////////////////////////////////////////////////
    async setPlayerMode(communityID, state) {
        try {
            this.r.table("communities").get(communityID).update({
                player_mode_enabled: state
            }).run();
        } catch (err) {
            this.ESMBot.logger.error(err);
        }
    }

    //////////////////////////////////////////////////////
    async getNotifications(communityID, type) {
        try {
            let notifications = await this.r.table("notifications").filter({
                community_id: communityID,
                type: type
            }).run();
            return notifications;
        } catch (err) {
            this.ESMBot.logger.error(err);
            return [];
        }
    }
}
All opinions represented herein are my own
- © 2024 itsthedevman
- build 340fbb8