const ESMCommand = require("../esm_command"); const Steam = require("../../code/steam"); module.exports = class ESMCommand_Logs extends ESMCommand { constructor(bot) { super(bot); this.permissions = { disabled_in_player_mode: true, requires_registration: true, requires_server: true, text_only: true }; this.commandParams = { serverID: { regex: this.util.regex.serverID.base }, search: { regex: /.+/ } }; this.information = { category: "server", params: "<server_id> <target_uid | @user | search_string (premium only)>", help: "Parses Exile's logs for the search parameter within a 5 day window. If the server has premium, the length is set to 14 days and you can parse for anything. This includes using regex." }; this.configuration = { permissions: { modifiable: true, default: {} }, allowedInTextChannels: { modifiable: false, default: true }, cooldown: { modifiable: true, default: [20, "seconds"] }, enabled: { modifiable: true, default: true } }; } async fromDiscord() { let days = 5; let isPremium = true; switch (true) { case this.util.regex.discordTag.base.test(this.params.search): let discordUser = await this.ESMBot.getUser(this.params.search); if (discordUser == null) { return this.ESMBot.send(this.message.channel, this.ESMBot.errors.format(this.message.author, "USER_NOT_FOUND")); } let isRegistered = await this.db.isRegistered(discordUser.id); if (!isRegistered) { return this.ESMBot.send(this.message.channel, this.ESMBot.errors.format(this.message.author, "USER_NOT_REGISTERED")); } this.params.search = await this.db.getSteamUIDFromID(discordUser.id); if (this.params.search == null) return; break; case this.util.regex.steamUID.base.test(this.params.search): break; default: if (!isPremium) { return this.ESMBot.send(this.message.channel, "Free plans can only parse for SteamUIDs (directly or via a Discord tag).\nIf you would like to search for anything or use Regex **plus** a 14 day search period, consider upgrading to Premium."); } days = 14; let validRegex = true, errorMessage = ""; try { new RegExp(this.params.search); } catch (err) { errorMessage = err.message.replace(/invalid regular expression: .*\/: /i, ""); validRegex = false; } if (!validRegex) { return this.ESMBot.send(this.message.channel, `\`${this.params.search}\` is not valid regex. Reason: \`${errorMessage}\``); } break; } this.send({ serverID: this.params.serverID, command: "logs", parameters: { search: this.params.search, length: days } }); } async fromServer() { if (this.params[1] == null || this.params[1].length === 0) { return this.ESMBot.send(this.info.channel, `We didn't find any log entries for \`${this.params.search}\``); } let data = {}; for (let y = 1; y < this.params.length; y++) { data[this.params[y].date] = {}; for (let log in this.params[y]) { let entries = []; let logName = ""; switch (log) { case "Exile_DeathLog.log": logName = "Exile Death Log"; break; case "Exile_TerritoryLog.log": logName = "Exile Territory Log"; break; case "Exile_TradingLog.log": logName = "Exile Trading Log"; break; default: continue; } for (let i = 0; i < this.params[y][log].length; i++) { let entry = this.params[y][log][i]; let newEntry = { line: entry.line }; let match = null; match = /\[(\d{2}:\d{2}:\d{2}):.+ (-?\d{2}:\d{2})\] \[thread \d+\] /i.exec(entry.entry); if (match != null) { newEntry.timestamp = this.ESMBot.moment.utc(`${entry.date}T${match[1]}${match[2]}`).format("MM/d/YYYY, h:mm:ss a"); entry.entry = entry.entry.replace(/\[(\d{2}:\d{2}:\d{2}):.+ (-?\d{2}:\d{2})\] \[thread \d+\] /i, ""); } let regex = [ // Waste Dump [ /player: \( \d{17} \) (.+ \(.+\))(?:\sremote)? sold item: (.+) with Cargo (\[.+\]) for (\d+) poptabs and (.+) respect \| player total money: (\d+)/i, "$1 sold vehicle: <code>$2</code> containing <code>$3</code> for $4</code> poptabs and $5</code> respect. Total Player Money: $6</code> poptabs" ], // Sold item [ /player: \( \d{17} \) (.+ \(.+\))(?:\sremote)? sold item (.+) for (\d+) poptabs and (.+) respect \| player total money: (\d+)/i, "$1 sold item <code>$2</code> for <code>$3</code> poptabs and <code>$4</code> respect. Total player money: <code>$5</code> poptabs" ], // Vehicle skin [ /player: \( \d{17} \) (.+ \(.+\))(?:\sremote)? purchased vehicle skin (.+) for (\d+) poptabs \| player total money: (\d+)/i, "$1 purchased vehicle skin <code>$2</code> for <code>$3</code> poptabs. Total player money: <code>$4</code> poptabs" ], // Vehicle [ /player: \( \d{17} \) (.+ \(.+\))(?:\sremote)? purchased vehicle (.+) for (\d+) poptabs \| player total money: (\d+)/i, "$1 purchased vehicle <code>$2</code> for <code>$3</code> poptabs. Total player money: <code>$4</code> poptabs" ], // Purchase item [ /player: \( \d{17} \) (.+ \(.+\))(?:\sremote)? purchased item (.+) for (\d+) poptabs \| player total money: (\d+)/i, "$1 purchased item <code>$2</code> for <code>$3</code> poptabs. Total player money: <code>$4</code> poptabs" ], // Upgrade Territory [ /player \( \d{17} \) (.+ \(.+\))(?:\sremote)? paid (\d+) pop tabs to upgrade territory #(\d+) to level (\d+) \| player total pop tabs: (\d+)/i, "$1 paid <code>$2</code> poptabs to upgrade their territory (ID #$3) to level $4. Total player poptabs: <code>$5</code>" ], // Restored territory [ /player \( \d{17} \) (.+ \(.+\))(?:\sremote)? restored the flag of territory #(\d+)/i, "$1 restored the flag of territory ID #$2" ], // Purchased Territory [ /player \( \d{17} \) (.+ \(.+\))(?:\sremote)? paid (\d+) pop tabs to purchase a territory flag \| player total pop tabs: (\d+)/i, "$1 purchased a territory flag. Cost: <code>$2</code> poptabs. Total player poptabs: <code>$3</code>" ], // Base Protection Money [ /player \( \d{17} \) (.+ \(.+\))(?:\sremote)? paid (\d+) pop tabs to protect territory #(\d+) \| player total pop tabs: (\d+)/i, "$1 paid base protection for territory #$3. Cost: <code>$2</code> poptabs. Player total poptabs: <code>$4</code>" ], // Ransom payment [ /player \( \d{17} \) (.+ \(.+\))(?:\sremote)? paid (\d+) pop tabs for the ransom of territory #(\d+) \| player total pop tabs: (\d+)/i, "$1 paid ransom for territory #$3. Cost <code>$2</code> poptabs. Total player poptabs: <code>$4</code>" ], // Stole flag [ /player \( \d{17} \) (.+ \(.+\))(?:\sremote)? stole a level (\d+) flag from territory #(\d+)/i, "$1 stole a level $2 flag from territory #$3" ] ]; for (let x = 0; x < regex.length; x++) { match = regex[x][0].exec(entry.entry); if (match != null) { newEntry.entry = entry.entry.replace(regex[x][0], regex[x][1]); break; } } if (newEntry.entry == null) { newEntry.entry = entry.entry; } entries.push(newEntry); } data[this.params[y].date][logName] = entries; } } let targetInfo = {}; if (this.util.regex.steamUID.base.test(this.params[0].search)) { targetInfo.uid = this.params[0].search; let discordIDs = await this.db.getDiscordIDsFromSteamUID(targetInfo.uid); if (!this.util.isEmpty(discordIDs)) { targetInfo.discord_users = []; for (let id of discordIDs) { let discordUser = await this.ESMBot.getUser(id); if (discordUser != null) { targetInfo.discord_users.push([discordUser.username, discordUser.discriminator]); } } } let profileData = await Steam.getSteamUserData(targetInfo.uid); if (profileData.response) { targetInfo.steam_username = profileData.data.username; targetInfo.steam_profile_url = profileData.data.profileurl; } } let requestingUser = await this.ESMBot.getUser(this.info.author.id); let expires = this.ESMBot.moment.utc().add("1", "d"); let hash = await this.db.generateLinkHash({ requested_by: { username: requestingUser.username, discriminator: requestingUser.discriminator, avatar: requestingUser.avatarURL() }, server_id: this.serverID, requested_at: this.ESMBot.moment.utc().format(), entries: data, target_info: targetInfo, search: this.params[0].search }, expires.format() ); this.ESMBot.send(this.info.channel, { title: `Log Parsing on ${this.serverID} completed`, description: `Search results for \`${this.params[0].search}\`: [Results](${this.ESMBot.config.DEBUG ? `http://localhost:3000/logs/${hash}` : `https://www.esmbot.com/logs/${hash}`})`, footer: { text: `Link expires on ${expires.format("MMMM Do YYYY, h:mm:ss a")}` }, color: this.ESMBot.colors.GREEN }); } }