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 []; } } }