From 51b7a8d1c32a91e32a32302de4e3f71f42db4e23 Mon Sep 17 00:00:00 2001 From: Braeden Sowinski Date: Tue, 7 Mar 2023 17:18:37 -0800 Subject: [PATCH] 4.7.2.1 dev version 1 collect logs --- commands/debug.js | 29 +++++++ config/config.js | 5 ++ package-lock.json | 4 +- package.json | 8 +- structures/DayzArmbands.js | 19 ++++- util/collect.py | 163 +++++++++++++++++++++++++++++++++++++ 6 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 commands/debug.js create mode 100644 util/collect.py diff --git a/commands/debug.js b/commands/debug.js new file mode 100644 index 0000000..05bd8f6 --- /dev/null +++ b/commands/debug.js @@ -0,0 +1,29 @@ +const { EmbedBuilder } = require('discord.js'); + +module.exports = { + name: "debug", + debug: true, + global: true, + description: "debug", + usage: "", + permissions: { + channel: ["VIEW_CHANNEL", "SEND_MESSAGES", "EMBED_LINKS"], + member: [], + }, + options: [], + SlashCommand: { + /** + * + * @param {require("../structures/QuarksBot")} client + * @param {import("discord.js").Message} message + * @param {string[]} args + * @param {*} param3 + */ + run: async (client, interaction, args, { GuildDB }, start) => { + + client.updateLogs() + + return interaction.send({ content: 'debugging...' }) + }, + }, +} \ No newline at end of file diff --git a/config/config.js b/config/config.js index 3bf2751..3ccdda1 100644 --- a/config/config.js +++ b/config/config.js @@ -9,6 +9,11 @@ module.exports = { SupportServer: "https://discord.gg/KVFJCvvFtK", //Support Server Link Token: process.env.token || "", //Discord Bot Token Scopes: ["identify", "guilds", "applications.commands"], //Discord OAuth2 Scopes + Nitrado: { + ServerID: process.env.SERVER_ID, + UserID: process.env.USER_ID, + Auth: process.env.AUTH_KEY + }, IconURL: "", Colors: { Default: "#8a7c72", diff --git a/package-lock.json b/package-lock.json index 711eb43..0bcf20b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dayz-armbands", - "version": "0.0.1", + "version": "4.7.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dayz-armbands", - "version": "0.0.1", + "version": "4.7.2", "license": "ISC", "dependencies": { "@discordjs/rest": "^1.1.0", diff --git a/package.json b/package.json index 0aa3b8e..046c6cf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dayz-armbands", - "version": "4.7.2", - "description": "A Discord Bot to handle DayZ server flags and armbands for in game factions.", + "version": "4.7.2.1", + "description": "A General Purpose Discord Bot for DayZ Servers.", "main": "index.js", "nodemonConfig": { "ignore": [ @@ -11,8 +11,8 @@ }, "scripts": { "start": "node index.js", - "dev": "node dev-index.js", - "debug": "nodemon dev-index.js" + "dev": "node index.js", + "debug": "nodemon index.js" }, "author": "Braeden Sowinski", "license": "ISC", diff --git a/structures/DayzArmbands.js b/structures/DayzArmbands.js index 40e55e5..c0eebdd 100644 --- a/structures/DayzArmbands.js +++ b/structures/DayzArmbands.js @@ -3,8 +3,11 @@ const { Collection, Client, EmbedBuilder, Routes } = require('discord.js'); const MongoClient = require('mongodb').MongoClient; const { REST } = require('@discordjs/rest'); const Logger = require("../util/Logger"); + const path = require("path"); const fs = require('fs'); +const { Readable } = require('stream'); +const { finished } = require('stream/promises'); class DayzArmbands extends Client { @@ -15,7 +18,7 @@ class DayzArmbands extends Client { this.commands = new Collection(); this.interactionHandlers = new Collection(); this.logger = new Logger(path.join(__dirname, "..", "logs/Logs.log")); - + if (this.config.Token === "") throw new TypeError( "The config.js is not filled out. Please make sure nothing is blank, otherwise the bot will not work properly." @@ -87,6 +90,20 @@ class DayzArmbands extends Client { const client = this; } + async updateLogs() { + const res = await fetch(`https://api.nitrado.net/services/${this.config.Nitrado.ServerID}/gameservers/file_server/download?file=/games/${this.config.Nitrado.UserID}/noftp/dayzxb/config/DayZServer_X1_x64.ADM`, { + headers: { + "Authorization": this.config.Nitrado.Auth + } + }).then(response => + response.json().then(data => data) + ).then(res => res); + + const stream = fs.createWriteStream('./logs/server-logs.ADM'); + const { body } = await fetch(res.data.token.url); + await finished(Readable.fromWeb(body).pipe(stream)); + } + async connectMongo(mongoURI, dbo) { let failed = false; diff --git a/util/collect.py b/util/collect.py new file mode 100644 index 0000000..a8807ed --- /dev/null +++ b/util/collect.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 + +import requests +import urllib +import json +import os +import re +from enum import Enum +from dotenv import load_dotenv + +load_dotenv() +AUTH_KEY: str = os.getenv('AUTH_KEY') +SERVER_ID: str = os.getenv('SERVER_ID') +USER_ID: str = os.getenv('USER_ID') + +AUTH_KEY: str = 'nrBQHGnTMMzQZbInit-C-DLYBtywLc-15GHLw9Zjj_K87V8L589BqAIPW8d9vh7Zc-NtyyL798YO9V9o2GT7gpug0GiiKR8wrE-b' +SERVER_ID: str = '12215784' +USER_ID: str = 'ni8434545_1' + +class LogTemplates(Enum): + pos: str = '$time | Player "$gamertag" (id=$playerID pos=<$pos>)' + damage: str = '$time | Player "$gamertag" (DEAD) (id=$playerID pos=<3$pos>)[HP: 0] hit by Player "$killer" (id=$killerID pos=<$killerPOS>) into $areaHit for $damageVal damage ($bullet_type) with $weapon from $distance meters' + killed: str = '$time | Player "$gamertag" (DEAD) (id=$playerID pos=<3$pos>) killed by Player "$killer" (id=$killerID pos=<$killerPOS>) with $weapon from $distance meters' + connect: str = '$time | Player "$gamertag" is connected (id=$playerID)' + disconnect: str = '$time | Player "$gamertag" (id=$playerID pos=<$pos>) has been disconnected' + +logFlags = [ + "disconnected", + ") placed ", + "connected", + "hit by", + "regained consciousne", + "is unconscious", + "killed by", + ")Built ", + ") folded", + ")Player SurvivorBase", + ") died.", + ") committed suicide", + ")Dismantled", + ") bled" +] +players = { + 'players': [] +} + +# Download Raw Logs off Nitrado +def getRawLogs(): + data = requests.get( + f"https://api.nitrado.net/services/{SERVER_ID}/gameservers/file_server/download?file=/games/{USER_ID}/noftp/dayzxb/config/DayZServer_X1_x64.ADM", + headers={ + "Authorization": AUTH_KEY + }).json() + + print(data) + + # url = data['data']['token']['url'] + # if not os.path.exists('output'): os.mkdir('output') + # urllib.request.urlretrieve(url, "./output/logs.ADM") + + +# Convert Raw Logs into cleaned logs (only positional data logs) +def cleanLogs(): + with open("./output/logs.ADM", "r") as logs: + lines = logs.readlines() + # Isolate Player logs (Removes Connect, Disconnect, place, hit) + with open("./output/clean.txt", "w") as logs: + for line in lines: + if not any(flag in line for flag in logFlags) and "| Player" in line.strip("\n"): + logs.write(line) + + +# Generate List of player names, id's and positions +def collectPlayerData(): + with open('./output/clean.txt', 'r') as logs: + cleanLines = logs.readlines() + for line in cleanLines: + pattern = re.escape(LogTemplates.pos) + pattern = re.sub(r'\\\$(\w+)', r'(?P<\1>.*)', pattern) + data = re.match(pattern, line) + if data is None: break + + query = { + 'gamertag': data.groupdict()['gamertag'], + 'playerID': data.groupdict()['playerID'], + 'time': data.groupdict()['time']+' EST', + 'pos': data.groupdict()['pos'].split(", "), + 'posHistory': [] + } + + if len(players['players'])==0: players['players'].append(query) + else: + for i in range(len(players['players'])): + if players['players'][i]['gamertag']==data.groupdict()['gamertag']: + # Updates existing player data + for j in range(len(players['players'][i]['posHistory'])): + query['posHistory'].append({ + 'time': players['players'][i]['posHistory'][j]['time'], + 'pos': players['players'][i]['posHistory'][j]['pos'] + }) + + query['posHistory'].append({ + 'time': players['players'][i]['time'], + 'pos': players['players'][i]['pos'] + }) + + players['players'].remove(players['players'][i]) + break + + # Logs new player data + players['players'].append(query) + + +# Search Logs for Connected and Disconnected messages +def activeStatus(): + with open("./output/logs.ADM", "r") as logs: + lines = logs.readlines() + for line in lines: + status = "" + update = False + if "\" is connected" in line.strip("\n") and "| Player" in line.strip("\n"): + status = "Online" + update = True + elif ") has been disconnected" in line.strip("\n") and "| Player" in line.strip("\n"): + status = "Offline" + update = True + + if update: + beginPlayer = 19 # Player names always start here + if status=="Online": endPlayer = line.strip("\n").find("\" is") + if status=="Offline": endPlayer = line.strip("\n").find("\"(id=") + playerName = line.strip("\n")[beginPlayer:endPlayer] + + playerFoundAndUpdated = False + for i in range(len(players['players'])): + if players['players'][i]['gamertag']==playerName: + players['players'][i]['connectionStatus'] = status + playerFoundAndUpdated = True + + if not playerFoundAndUpdated: + # Get player ID + beginID = line.strip("\n").find('(id=')+4 + endID = line.strip("\n").find(")") + playerID = line.strip("\n")[beginID:endID] + query = { + "gamertag": playerName, + "playerID": playerID, + "time": None, + "pos": [], + "posHistory": [], + "connectionStatus": "Online" + } + # Logs new player data + players["players"].append(query) + +if __name__ == '__main__': + getRawLogs() + cleanLogs() + collectPlayerData() + activeStatus() + + with open("./output/players.json", "w") as playerJSON: + json.dump(players, playerJSON, ensure_ascii=False, indent=2)