refactor/support multiple discord guilds with 1 nitrado server each + better handling for NitradoAPI

This commit is contained in:
SowinskiBraeden committed 2024-04-04 20:11:48 -07:00
1 parent 71d03ffa72
commit 55c8a9ee7f
33 files changed
+881 -1021

No files matched your search

+12 -4
View File
@@ -1,8 +1,16 @@
# Discord Bot Token
token='Your Discord Bot token' token='Your Discord Bot token'
# MongoDB Information
mongoURI='Your mongodb URI' mongoURI='Your mongodb URI'
dbo='Your mongodb database' dbo='Your mongodb database'
SERVER_ID='Your Nitrado server ID'
USER_ID='Your Nitrado user ID' # Encryption
AUTH_KEY='Your Nitrado Auth Token' key='Secret Encryption Key',
GuildID='Your Discord Guild ID' iv='Secret Initialization Vector',
# Other Bot Configuration
Dev=PROD. # or DEV. Dev=PROD. # or DEV.
# Note: "PROD." downloads Nitrado logs every 5 minutes, "DEV." checks every 15 seconds.
# Any other value for Dev will not allow the bot to run.
+24
View File
@@ -134,6 +134,14 @@ module.exports = {
if (args[0].name == 'gamertag-link') { if (args[0].name == 'gamertag-link') {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let playerStat = await client.dbo.collection("players").findOne({"gamertag": args[0].options[1].value}); let playerStat = await client.dbo.collection("players").findOne({"gamertag": args[0].options[1].value});
if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**Not Found** This gamertag \` ${args[0].options[1].value} \` cannot be found, the gamertag may be incorrect or this player has not logged onto the server before for at least \` 5 minutes \`.`)] }); if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**Not Found** This gamertag \` ${args[0].options[1].value} \` cannot be found, the gamertag may be incorrect or this player has not logged onto the server before for at least \` 5 minutes \`.`)] });
@@ -180,6 +188,14 @@ module.exports = {
} else if (args[0].name == 'gamertag-unlink') { } else if (args[0].name == 'gamertag-unlink') {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let playerStat = await client.dbo.collection("players").findOne({"discordID": args[0].options[0].value}); let playerStat = await client.dbo.collection("players").findOne({"discordID": args[0].options[0].value});
if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**Not Found** <@${args[0].options[0].value}> has no gamertag linked.`)] }); if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**Not Found** <@${args[0].options[0].value}> has no gamertag linked.`)] });
@@ -265,6 +281,14 @@ module.exports = {
} else if (args[0].name == 'bounty-clear') { } else if (args[0].name == 'bounty-clear') {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let playerStat = await client.dbo.collection("players").findOne({"gamertag": args[0].options[0].value}); let playerStat = await client.dbo.collection("players").findOne({"gamertag": args[0].options[0].value});
if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('**Not Found** This player cannot be found, the gamertag may be incorrect or this player has not logged onto the server before for at least ` 5 minutes `.')] }); if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('**Not Found** This player cannot be found, the gamertag may be incorrect or this player has not logged onto the server before for at least ` 5 minutes `.')] });
+8
View File
@@ -214,6 +214,14 @@ module.exports = {
if (GuildDB.hasBotAdmin && interaction.member.roles.filter(e => GuildDB.botAdminRoles.indexOf(e) !== -1).length > 0) canUseCommand = true; if (GuildDB.hasBotAdmin && interaction.member.roles.filter(e => GuildDB.botAdminRoles.indexOf(e) !== -1).length > 0) canUseCommand = true;
if (!canUseCommand) return interaction.send({ content: 'You don\'t have the permissions to use this command.' }); if (!canUseCommand) return interaction.send({ content: 'You don\'t have the permissions to use this command.' });
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
if (args[0].name == 'create') { if (args[0].name == 'create') {
if (args[0].options[3].value.includes('-') || args[0].options[3].value.includes(' ')) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Red).setDescription('**Invalid Name:** Alarm Names cannot include hyphens or spaces.')] }) if (args[0].options[3].value.includes('-') || args[0].options[3].value.includes(' ')) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Red).setDescription('**Invalid Name:** Alarm Names cannot include hyphens or spaces.')] })
+8
View File
@@ -59,6 +59,14 @@ module.exports = {
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let banking; let banking;
if (args[0].name == 'set' || args[0].name == 'pay') { if (args[0].name == 'set' || args[0].name == 'pay') {
banking = await client.dbo.collection("users").findOne({"user.userID": interaction.member.user.id}).then(banking => banking); banking = await client.dbo.collection("users").findOne({"user.userID": interaction.member.user.id}).then(banking => banking);
+9
View File
@@ -33,6 +33,15 @@ module.exports = {
* @param {*} param3 * @param {*} param3
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let discord = args[0] && args[0].name == 'discord' ? args[0].value : undefined; let discord = args[0] && args[0].name == 'discord' ? args[0].value : undefined;
let gamertag = args[0] && args[0].name == 'gamertag' ? args[0].value : undefined; let gamertag = args[0] && args[0].name == 'gamertag' ? args[0].value : undefined;
+2 -11
View File
@@ -11,12 +11,7 @@ module.exports = {
channel: ["VIEW_CHANNEL", "SEND_MESSAGES", "EMBED_LINKS"], channel: ["VIEW_CHANNEL", "SEND_MESSAGES", "EMBED_LINKS"],
member: [], member: [],
}, },
options: [{ options: [],
name: 'sync-logs',
description: 'download the Nitrado logs and run checks now',
value: 'sync-logs',
type: CommandOptions.SubCommand,
}],
SlashCommand: { SlashCommand: {
/** /**
* *
@@ -28,11 +23,7 @@ module.exports = {
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.config.Admins.includes(interaction.member.user.id)) return interaction.send({ content: 'Only developers can access this command.', flags: (1 << 6) }) if (!client.config.Admins.includes(interaction.member.user.id)) return interaction.send({ content: 'Only developers can access this command.', flags: (1 << 6) })
if (args[0].name == 'sync-logs') { return interaction.send({ content: `Hello ${interaction.member.user.id}, you are my creator!!` })
if (client.processingLogs) return interaction.send({ content: 'The Nitrado logs are already being processed at the moment...', flags: (1 << 6) });
interaction.send({ content: 'Downloading and processing Nitrado logs now...', flags: (1 << 6) });
return client.logsUpdateTimer(client);
}
}, },
}, },
Interactions: {} Interactions: {}
+8
View File
@@ -72,6 +72,14 @@ module.exports = {
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
const permissions = bitfieldCalculator.permissions(interaction.member.permissions); const permissions = bitfieldCalculator.permissions(interaction.member.permissions);
let canUseCommand = false; let canUseCommand = false;
+8
View File
@@ -29,6 +29,14 @@ module.exports = {
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let playerStat = await client.dbo.collection("players").findOne({"gamertag": args[0].value}); let playerStat = await client.dbo.collection("players").findOne({"gamertag": args[0].value});
if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**Not Found** This gamertag \` ${args[0].value} \` cannot be found, the gamertag may be incorrect or this player has not logged onto the server before for at least \` 5 minutes \`.`)] }); if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**Not Found** This gamertag \` ${args[0].value} \` cannot be found, the gamertag may be incorrect or this player has not logged onto the server before for at least \` 5 minutes \`.`)] });
+8
View File
@@ -21,6 +21,14 @@ module.exports = {
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let playerStat = await client.dbo.collection("players").findOne({"discordID": interaction.member.user.id}); let playerStat = await client.dbo.collection("players").findOne({"discordID": interaction.member.user.id});
if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**No Gamertag Linked** It Appears your don't have a gamertag linked to your account.`)] }); if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**No Gamertag Linked** It Appears your don't have a gamertag linked to your account.`)] });
+9
View File
@@ -52,6 +52,15 @@ module.exports = {
* @param {*} param3 * @param {*} param3
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
const category = args[0].value; const category = args[0].value;
const limit = args[1].value; const limit = args[1].value;
+11 -17
View File
@@ -1,6 +1,5 @@
const { EmbedBuilder } = require('discord.js'); const { EmbedBuilder } = require('discord.js');
const { destinations } = require('../database/destinations'); const { nearest } = require('../database/destinations');
const { calculateVector } = require('../util/Vector');
module.exports = { module.exports = {
name: "location", name: "location",
@@ -21,27 +20,22 @@ module.exports = {
* @param {*} param3 * @param {*} param3
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let playerStat = await client.dbo.collection("players").findOne({"discordID": interaction.member.user.id}); let playerStat = await client.dbo.collection("players").findOne({"discordID": interaction.member.user.id});
if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**Not Found** You haven't linked your gamertag and are unable to use this command.`)], flags: (1 << 6) }); if (!client.exists(playerStat)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription(`**Not Found** You haven't linked your gamertag and are unable to use this command.`)], flags: (1 << 6) });
let newDt = await client.getDateEST(playerStat.time); let newDt = await client.getDateEST(playerStat.time);
let unixTime = Math.floor(newDt.getTime()/1000); let unixTime = Math.floor(newDt.getTime()/1000);
let tempDest; const destination = nearest(playerStat.pos, GuildDB.Nitrado.Mission);
let lastDist = 1000000;
let destination_dir;
if (showCoords) { // Only calculate if showing coords, very tiny minor optimization... probably amounts to nothing.
for (let i = 0; i < destinations.length; i++) {
let { distance, theta, dir } = calculateVector(info.victimPOS, destinations[i].coord);
if (distance < lastDist) {
tempDest = destinations[i].name;
lastDist = distance;
destination_dir = dir;
}
}
}
const destination = lastDist > 500 ? `${destination_dir} of ${tempDest}` : `Near ${tempDest}`;
let lastLocation = new EmbedBuilder() let lastLocation = new EmbedBuilder()
.setColor(client.config.Colors.Default) .setColor(client.config.Colors.Default)
+8
View File
@@ -46,6 +46,14 @@ module.exports = {
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
if (args[0].name == 'discord') { if (args[0].name == 'discord') {
let playerStat = await client.dbo.collection("players").findOne({"gamertag": args[0].options[0].value}); let playerStat = await client.dbo.collection("players").findOne({"gamertag": args[0].options[0].value});
+9
View File
@@ -21,6 +21,14 @@ module.exports = {
*/ */
run: async (client, interaction, args, { GuildDB }, start) => { run: async (client, interaction, args, { GuildDB }, start) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let activePlayers = await client.dbo.collection("players").find({"connected": true}); let activePlayers = await client.dbo.collection("players").find({"connected": true});
let des = activePlayers.length > 0 ? `` : `**No Players Online**`; let des = activePlayers.length > 0 ? `` : `**No Players Online**`;
@@ -28,6 +36,7 @@ module.exports = {
des += `**- ${activePlayers[i].gamertag}**\n`; des += `**- ${activePlayers[i].gamertag}**\n`;
} }
// TODO: handle activePlayers.length being undefined
const activePlayersEmbed = new EmbedBuilder() const activePlayersEmbed = new EmbedBuilder()
.setColor(client.config.Colors.Default) .setColor(client.config.Colors.Default)
.setTitle(`Online List - ${activePlayers.length} Player${(activePlayers.length>1||activePlayers.length==0)?'s':''} Online`) .setTitle(`Online List - ${activePlayers.length} Player${(activePlayers.length>1||activePlayers.length==0)?'s':''} Online`)
+9
View File
@@ -56,6 +56,15 @@ module.exports = {
* @param {*} param3 * @param {*} param3
*/ */
run: async (client, interaction, args, { GuildDB }, start) => { run: async (client, interaction, args, { GuildDB }, start) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let category = args[0].value; let category = args[0].value;
let discord = args[1] && args[1].name == 'discord' ? args[1].value : undefined; let discord = args[1] && args[1].name == 'discord' ? args[1].value : undefined;
let gamertag = args[1] && args[1].name == 'gamertag' ? args[1].value : undefined; let gamertag = args[1] && args[1].name == 'gamertag' ? args[1].value : undefined;
+9
View File
@@ -32,6 +32,15 @@ module.exports = {
* @param {*} param3 * @param {*} param3
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
if (client.exists(GuildDB.purchaseEMP) && !GuildDB.purchaseEMP) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('**Notice:** The admins have disabled this feature')] }); if (client.exists(GuildDB.purchaseEMP) && !GuildDB.purchaseEMP) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('**Notice:** The admins have disabled this feature')] });
const duration = args[0].value; const duration = args[0].value;
+9
View File
@@ -39,6 +39,15 @@ module.exports = {
* @param {*} param3 * @param {*} param3
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
if (client.exists(GuildDB.purchaseUAV) && !GuildDB.purchaseUAV) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('**Notice:** The admins have disabled this feature')] }); if (client.exists(GuildDB.purchaseUAV) && !GuildDB.purchaseUAV) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('**Notice:** The admins have disabled this feature')] });
let banking = await client.dbo.collection("users").findOne({"user.userID": interaction.member.user.id}).then(banking => banking); let banking = await client.dbo.collection("users").findOne({"user.userID": interaction.member.user.id}).then(banking => banking);
+1
View File
@@ -3,6 +3,7 @@ const CommandOptions = require('../util/CommandOptionTypes').CommandOptionTypes;
const { addUser } = require('../database/user'); const { addUser } = require('../database/user');
const bitfieldCalculator = require('discord-bitfield-calculator'); const bitfieldCalculator = require('discord-bitfield-calculator');
// TODO: Reset User Banking, Reset User Game Stats, Reset All Users Banking, Reset All User Game stats
module.exports = { module.exports = {
name: "reset", name: "reset",
debug: false, debug: false,
+167 -23
View File
@@ -1,8 +1,10 @@
const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder } = require('discord.js'); const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ModalBuilder, TextInputBuilder, TextInputStyle } = require('discord.js');
const CommandOptions = require('../util/CommandOptionTypes').CommandOptionTypes; const CommandOptions = require('../util/CommandOptionTypes').CommandOptionTypes;
const bitfieldCalculator = require('discord-bitfield-calculator'); const bitfieldCalculator = require('discord-bitfield-calculator');
const { BanPlayer, UnbanPlayer, RestartServer, CheckServerStatus, DisableBaseDamage, DisableContainerDamage } = require('../util/NitradoAPI'); const { BanPlayer, UnbanPlayer, RestartServer, CheckServerStatus, DisableBaseDamage, DisableContainerDamage } = require('../util/NitradoAPI');
const { encrypt, decrypt } = require('../util/Cryptic');
// TODO: deactivate nitrado server from guild
module.exports = { module.exports = {
name: "server", name: "server",
debug: false, debug: false,
@@ -13,8 +15,13 @@ module.exports = {
channel: ["VIEW_CHANNEL", "SEND_MESSAGES", "EMBED_LINKS"], channel: ["VIEW_CHANNEL", "SEND_MESSAGES", "EMBED_LINKS"],
member: [], member: [],
}, },
options: [ options: [{
{ name: "initialize",
description: "Connect your Nitrado server to the bot",
value: "initialize",
type: CommandOptions.SubCommand,
},
{
name: "ban-player", name: "ban-player",
description: "Ban a player from the DayZ server", description: "Ban a player from the DayZ server",
value: "ban-player", value: "ban-player",
@@ -91,16 +98,88 @@ module.exports = {
if (GuildDB.hasBotAdmin && interaction.member.roles.filter(e => GuildDB.botAdminRoles.indexOf(e) !== -1).length > 0) canUseCommand = true; if (GuildDB.hasBotAdmin && interaction.member.roles.filter(e => GuildDB.botAdminRoles.indexOf(e) !== -1).length > 0) canUseCommand = true;
if (!canUseCommand) return interaction.send({ content: 'You don\'t have the permissions to use this command.' }); if (!canUseCommand) return interaction.send({ content: 'You don\'t have the permissions to use this command.' });
const NitradoCred = client.exists(GuildDB.Nitrado) ? {
ServerID: GuildDB.Nitrado.ServerID,
UserID: GuildDB.Nitrado.UserID,
Auth: decrypt(
GuildDB.Nitrado.Auth,
client.config.EncryptionMethod,
client.key,
client.encryptionIV
)
} : {
ServerID: null,
UserID: null,
Auth: null
}
if (args[0].name == 'initialize') {
if (client.exists(GuildDB.Nitrado)) {
const prompt = new EmbedBuilder()
.setTitle(`Nitrado Server Information Already Configured!`)
.setDescription('**Notice:** This will overwrite your previously configured Nitrado Server Information')
.setColor(client.config.Colors.Yellow)
const opt = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`OverwriteNitrado-yes-${interaction.member.user.id}`)
.setLabel("Yes")
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId(`OverwriteNitrado-no-${interaction.member.user.id}`)
.setLabel("No")
.setStyle(ButtonStyle.Success)
)
return interaction.send({ embeds: [prompt], components: [opt], flags: (1 << 6) });
}
const NitradoCredentials = new ModalBuilder()
.setTitle('Connect your Nitrado Server')
.setCustomId(`NitradoCredentials-${interaction.member.user.id}`);
const ServerID = new ActionRowBuilder().addComponents(new TextInputBuilder()
.setCustomId('ServerIDInput')
.setLabel('Your Nitrado Server ID')
.setStyle(TextInputStyle.Short)
.setRequired(true)
);
const UserID = new ActionRowBuilder().addComponents(new TextInputBuilder()
.setCustomId('UserIDInput')
.setLabel('Your Nitrado User ID')
.setStyle(TextInputStyle.Short)
.setRequired(true)
);
const Auth = new ActionRowBuilder().addComponents(new TextInputBuilder()
.setCustomId('AuthInput')
.setLabel('Your Nitrado Authentication Token')
.setPlaceholder("This will be encrypted to protect your server!")
.setStyle(TextInputStyle.Short)
.setRequired(true)
);
NitradoCredentials.addComponents(ServerID, UserID, Auth);
return interaction.showModal(NitradoCredentials);
}
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID)) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Red).setDescription(`**Notice:**\nThis Discord guild has not been configured with a Nitrado DayZ server. To configure your guild, use </server initialize:1166877457559851011>`)] });
if (args[0].name == 'ban-player') { if (args[0].name == 'ban-player') {
let data = await BanPlayer(client, args[0].options[0].value); let data = await BanPlayer(NitradoCred, client, args[0].options[0].value);
if (data == 1) { if (data == 1) {
let failed = new EmbedBuilder() let failed = new EmbedBuilder()
.setColor(client.config.Colors.Red) .setColor(client.config.Colors.Red)
.setDescription(`Failed to ban **${args[0].options[0].value}**. Check internal logs for an error.`); .setDescription(`Failed to ban **${args[0].options[0].value}**. This can result from a variety of reasons:\nNitrado servers may be experiencing issues\nThe DayZ.R Bot may be experiencing issues\nYour Nitrado credentials were entered incorrectly`);
return interaciton.send({ embeds: [failed] }); return interaction.send({ embeds: [failed], flags: (1 << 6) });
} }
let banned = new EmbedBuilder() let banned = new EmbedBuilder()
@@ -111,12 +190,12 @@ module.exports = {
} else if (args[0].name == 'unban-player') { } else if (args[0].name == 'unban-player') {
let data = UnbanPlayer(client, args[0].options[0].value); let data = UnbanPlayer(NitradoCred, client, args[0].options[0].value);
if (data == 1) { if (data == 1) {
let failed = new EmbedBuilder() let failed = new EmbedBuilder()
.setColor(client.config.Colors.Red) .setColor(client.config.Colors.Red)
.setDescription(`Failed to ban **${args[0].options[0].value}**. Check internal logs for an error.`); .setDescription(`Failed to unban **${args[0].options[0].value}**. This can result from a variety of reasons:\nNitrado servers may be experiencing issues\nThe DayZ.R Bot may be experiencing issues\nYour Nitrado credentials were entered incorrectly`);
return interaction.send({ embeds: [failed] }); return interaction.send({ embeds: [failed] });
} }
@@ -132,29 +211,26 @@ module.exports = {
restart_message = 'Server being restarted by an admin.'; restart_message = 'Server being restarted by an admin.';
message = 'The server was restarted by an admin!'; message = 'The server was restarted by an admin!';
RestartServer(client, restart_message, message); RestartServer(NitradoCred, client, restart_message, message);
return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('The server will restart shortly.')], flags: (1 << 6) }); return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('The server will restart shortly.')], flags: (1 << 6) });
} else if (args[0].name == "auto-restart") { } else if (args[0].name == "auto-restart") {
msg = 'Auto server restart periodic check enabled.'; let msg = 'Auto server restart periodic check enabled.';
pref = 0; let pref = 0;
// Enable/Disable a 10min periodic server status check. // Enable/Disable a 10min periodic server status check.
if (!client.arIntervalId) { if (!client.arIntervalIds.has(GuildDB.serverID)) {
client.arIntervalId = setInterval(CheckServerStatus, client.arInterval, client); client.arIntervalIds.set(GuildDB.serverID, setInterval(CheckServerStatus, client.arInterval, NitradoCred, client));
client.log('Enabled and starting periodic Nitrado server status check.');
pref = 1; pref = 1;
} else { } else {
msg = 'Auto server restart periodic check disabled.' msg = 'Auto server restart periodic check disabled.'
clearInterval(client.arIntervalId); clearInterval(client.arIntervalIds.get(GuildDB.serverID));
client.arIntervalId = 0;
client.log('Disabled periodic Nitrado server status check.');
} }
// Update DB preference // Update DB preference
client.dbo.collection("guilds").updateOne({ "server.serverID": GuildDB.serverID }, { client.dbo.collection("guilds").updateOne({ "server.serverID": GuildDB.serverID }, {
$set: { $set: {
"server.autoRestart": pref "server.autoRestart": pref,
} }
}, function (err, res) { }, function (err, res) {
if (err) return client.sendInternalError(interaction, err); if (err) return client.sendInternalError(interaction, err);
@@ -166,23 +242,91 @@ module.exports = {
const preference = args[0].options[0].value; const preference = args[0].options[0].value;
await interaction.deferReply({ flags: (1 << 6) }); await interaction.deferReply({ flags: (1 << 6) });
const disableBaseDamageFailed = await DisableBaseDamage(client, preference); const disableBaseDamageFailed = await DisableBaseDamage(NitradoCred, client, preference);
if (disableBaseDamageFailed) return interaction.editReply({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Red).setDescription('Failed to set **disableBaseDamage**, try again later.')], flags: (1 << 6) }); if (disableBaseDamageFailed) return interaction.editReply({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Red).setDescription('Failed to set **disableBaseDamage**. This can result from a variety of reasons:\nNitrado servers may be experiencing issues\nThe DayZ.R Bot may be experiencing issues\nYour Nitrado credentials were entered incorrectly')], flags: (1 << 6) });
return interaction.editReply({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Green).setDescription(`Successfully set **disableBaseDamage** to ${preference}.\nRestart the DayZ server to apply these changes.`)], flags: (1 << 6) }); return interaction.editReply({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Green).setDescription(`Successfully set **disableBaseDamage** to ${preference}.\nRestart the DayZ server to apply these changes.`)], flags: (1 << 6) });
} else if (args[0].name == 'disable-container-damage') { } else if (args[0].name == 'disable-container-damage') {
const preference = args[0].options[0].value; const preference = args[0].options[0].value;
await interaction.deferReply({ flags: (1 << 6) }); await interaction.deferReply({ flags: (1 << 6) });
const disableContainerDamageFailed = await DisableContainerDamage(client, preference); const disableContainerDamageFailed = await DisableContainerDamage(NitradoCred, client, preference);
if (disableContainerDamageFailed) return interaction.editReply({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Red).setDescription('Failed to set **disableContainerDamage**, try again later.')], flags: (1 << 6) }); if (disableContainerDamageFailed) return interaction.editReply({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Red).setDescription('Failed to set **disableContainerDamage**. This can result from a variety of reasons:\nNitrado servers may be experiencing issues\nThe DayZ.R Bot may be experiencing issues\nYour Nitrado credentials were entered incorrectly')], flags: (1 << 6) });
return interaction.editReply({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Green).setDescription(`Successfully set **disableContainerDamage** to ${preference}.\nRestart the DayZ server to apply these changes.`)], flags: (1 << 6) }); return interaction.editReply({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Green).setDescription(`Successfully set **disableContainerDamage** to ${preference}.\nRestart the DayZ server to apply these changes.`)], flags: (1 << 6) });
} }
} }
}, },
Interactions: {} Interactions: {
NitradoCredentials: {
run: async(client, interaction, GuildDB) => {
if (!interaction.customId.endsWith(interaction.member.user.id))
return interaction.reply({ content: 'This interaction is not for you', flags: (1 << 6) });
const Nitrado = {
ServerID: interaction.fields.fields.get('ServerIDInput').value,
UserID: interaction.fields.fields.get('UserIDInput').value,
Auth: encrypt(
interaction.fields.fields.get('AuthInput').value,
client.config.EncryptionMethod,
client.key,
client.encryptionIV
), // Encrypt the Authentication Token
};
await client.dbo.collection('guilds').updateOne({ "server.serverID": GuildDB.serverID }, { $set: { "Nitrado": Nitrado } }, (err, res) => {
if (err) client.sendInternalError(interaction, err);
});
client.initNewNitradoServer(GuildDB.serverID, Nitrado);
return interaction.reply({ content: 'Successfully configured your Nitrado Server Information', flags: (1 << 6) });
}
},
OverwriteNitrado: {
run: async(client, interaction, GuildDB) => {
if (!interaction.customId.endsWith(interaction.member.user.id))
return interaction.reply({ content: 'This interaction is not for you', flags: (1 << 6) });
if (interaction.customId.split('-')[1] == 'yes') {
const NitradoCredentials = new ModalBuilder()
.setTitle('Connect your Nitrado Server')
.setCustomId(`NitradoCredentials-${interaction.member.user.id}`);
const ServerID = new ActionRowBuilder().addComponents(new TextInputBuilder()
.setCustomId('ServerIDInput')
.setLabel('Your Nitrado Server ID')
.setStyle(TextInputStyle.Short)
.setRequired(true)
);
const UserID = new ActionRowBuilder().addComponents(new TextInputBuilder()
.setCustomId('UserIDInput')
.setLabel('Your Nitrado User ID')
.setStyle(TextInputStyle.Short)
.setRequired(true)
);
const Auth = new ActionRowBuilder().addComponents(new TextInputBuilder()
.setCustomId('AuthInput')
.setLabel('Your Nitrado Authentication Token')
.setStyle(TextInputStyle.Short)
.setRequired(true)
);
NitradoCredentials.addComponents(ServerID, UserID, Auth);
return interaction.update(NitradoCredentials);
} else {
return interaction.reply({ content: 'Cancelled Overwriting Nitrado Server Information' });
}
}
}
}
} }
+9
View File
@@ -53,6 +53,15 @@ module.exports = {
* @param {*} param3 * @param {*} param3
*/ */
run: async (client, interaction, args, { GuildDB }) => { run: async (client, interaction, args, { GuildDB }) => {
if (!client.exists(GuildDB.Nitrado) || !client.exists(GuildDB.Nitrado.ServerID) || !client.exists(GuildDB.Nitrado.UserID) || !client.exists(GuildDB.Nitrado.Auth)) {
const warnNitradoNotInitialized = new EmbedBuilder()
.setColor(client.config.Colors.Yellow)
.setDescription("**WARNING:** The DayZ Nitrado Server has not been configured for this guild yet. This command or feature is currently unavailable.");
return interaction.send({ embeds: [warnNitradoNotInitialized], flags: (1 << 6) });
}
let discord = args[1] && args[1].name == 'discord' ? args[1].value : undefined; let discord = args[1] && args[1].name == 'discord' ? args[1].value : undefined;
let gamertag = args[1] && args[1].name == 'gamertag' ? args[1].value : undefined; let gamertag = args[1] && args[1].name == 'gamertag' ? args[1].value : undefined;
let self = !discord && !gamertag; // searching for self if both discord and gamertag are undefined let self = !discord && !gamertag; // searching for self if both discord and gamertag are undefined
+4 -8
View File
@@ -21,16 +21,12 @@ module.exports = {
Dev: process.env.Dev || "DEV.", Dev: process.env.Dev || "DEV.",
Version: package.version, // (major).(minor).(patch) Version: package.version, // (major).(minor).(patch)
Admins: ["362791661274660874", "329371697570381824"], // Admins of the bot Admins: ["362791661274660874", "329371697570381824"], // Admins of the bot
ServerID: "1050215624053374976", SupportServer: "https://discord.gg/KVFJCvvFtK", // Support Server Link
GuildID: process.env.GuildID || "",
SupportServer: "https://discord.gg/KVFJCvvFtK", // Support Server Link
Token: process.env.token || "", //Discord Bot Token Token: process.env.token || "", //Discord Bot Token
SecretKey: process.env.key || "01234567891",
SecretIv: process.env.iv || "9876543210",
EncryptionMethod: process.env.encryptionMethod || "aes-256-cbc",
Scopes: ["identify", "guilds", "applications.commands"], //Discord OAuth2 Scopes 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: "", IconURL: "",
Colors: { Colors: {
Default: "#8a7c72", Default: "#8a7c72",
+151 -1
View File
@@ -1,5 +1,26 @@
const { calculateVector } = require('../util/Vector');
module.exports = { module.exports = {
destinations: [ // Calculates the nearest location to a given coordinate
nearest: (pos, mission) => {
let tempDest;
let lastDist = 1000000;
let destination_dir;
for (let i = 0; i < destinations[mission].length; i++) {
let { distance, theta, dir } = calculateVector(data.pos, destinations[mission][i].coord);
if (distance < lastDist) {
tempDest = destinations[mission][i].name;
lastDist = distance;
destination_dir = dir;
}
}
return lastDist > 500 ? `${destination_dir} of ${tempDest}` : `Near ${tempDest}`;
}
}
// A curated list of destinations across DayZ Chernarus and Livonia
const destinations = {
Chernarus: [
{ {
name: 'Sinystok', name: 'Sinystok',
coord: [1481.47, 11933.38], coord: [1481.47, 11933.38],
@@ -310,5 +331,134 @@ module.exports = {
name: 'Ski Resort Peak', name: 'Ski Resort Peak',
coord: [250.80, 11867.28], coord: [250.80, 11867.28],
}, },
],
Livonia: [
{
name: 'Lukow',
coord: [3575.00 / 11925.00],
}, {
name: 'Brena',
coord: [6518.75 / 11228.13],
}, {
name: 'Kolembrody',
coord: [8406.25 / 11968.75],
}, {
name: 'Grabin',
coord: [10756.25 / 11062.50],
}, {
name: 'Sitnik',
coord: [11440.63 / 9543.75],
}, {
name: 'Tarnow',
coord: [9275.00 / 10921.88],
}, {
name: 'Sobatka',
coord: [6250.00 / 10193.75],
}, {
name: 'Gliniska',
coord: [5012.50 / 9881.25],
}, {
name: 'Gliniska Airfield',
coord: [3968.75 / 10278.13]
}, {
name: 'Kopa',
coord: [5545.31 / 8748.44],
}, {
name: 'Olszanka',
coord: [4856.25 / 7571.88],
}, {
name: 'Radacz',
coord: [4006.25 / 7972.66],
}, {
name: 'Topolin',
coord: [1665.62 / 7378.13],
}, {
name: 'Bielawa',
coord: [1525.00 / 9700.00],
}, {
name: 'Adamow',
coord: [3081.25 / 6793.75],
}, {
name: 'Muratyn',
coord: [4587.50 / 6387.50],
}, {
name: 'Lipina',
coord: [5943.75 / 6787.50],
}, {
name: 'Nidek',
coord: [6118.75 / 8056.25],
}, {
name: 'Zapadlisko',
coord: [8093.75 / 8710.94],
}, {
name: 'Krsnik Military',
coord: [7841.02 / 10075.39],
}, {
name: 'Zalesie',
coord: [878.12 / 5512.50],
}, {
name: 'Borek Military',
coord: [9807.81 / 8500.00],
}, {
name: 'Polkrabiec',
coord: [11878.13 / 6571.09],
}, {
name: 'Lembork',
coord: [8825.00 / 6628.13],
}, {
name: 'Karlin',
coord: [10064.39 / 6924.93],
}, {
name: 'Radunin',
coord: [7301.89 / 6418.68],
}, {
name: 'Roztoka',
coord: [7650.00 / 5246.88],
}, {
name: 'Sarnowek',
coord: [3287.50 / 5009.38],
}, {
name: 'Huta',
coord: [5154.69 / 5520.31],
}, {
name: 'Drewniki',
coord: [5834.38 / 5084.38],
}, {
name: 'Nadbor',
coord: [6056.25 / 4103.13],
}, {
name: 'Nadbor Military',
coord: [5625.00 / 3787.50],
}, {
name: 'Max',
coord: [6448.44 / 4732.81],
}, {
name: 'Wrzeszcz',
coord: [9042.19 / 4385.94],
}, {
name: 'Gieraltow',
coord: [11243.75 / 4332.81],
}, {
name: 'Konopki',
coord: [11460.16 / 2889.84],
}, {
name: 'Swarog Military',
coord: [5017.19 / 2146.88],
}, {
name: 'Hedrykow',
coord: [4487.50 / 4825.00],
}, {
name: 'Polana',
coord: [3296.87 / 2043.75],
}, {
name: 'Dambog',
coord: [597.27 / 1138.67],
}, {
name: 'Dolnik',
coord: [11410.94 / 578.12],
}, {
name: 'Widok',
coord: [10234.38 / 2165.63],
},
] ]
} }
+4
View File
@@ -7,6 +7,7 @@ module.exports = {
if (!guild) { if (!guild) {
guild = {} guild = {}
guild.server = module.exports.getDefaultSettings(GuildId); guild.server = module.exports.getDefaultSettings(GuildId);
guild.Nitrado = undefined;
if (client.databaseConnected) { if (client.databaseConnected) {
client.dbo.collection("guilds").insertOne(guild, (err, res) => { client.dbo.collection("guilds").insertOne(guild, (err, res) => {
if (err) throw err; if (err) throw err;
@@ -16,6 +17,8 @@ module.exports = {
return { return {
serverID: GuildId, serverID: GuildId,
Nitrado: guild.Nitrado,
lastLog: guild.server.lastLog,
serverName: guild.server.serverName, serverName: guild.server.serverName,
autoRestart: guild.server.autoRestart, autoRestart: guild.server.autoRestart,
showKillfeedCoords: guild.server.showKillfeedCoords, showKillfeedCoords: guild.server.showKillfeedCoords,
@@ -58,6 +61,7 @@ module.exports = {
getDefaultSettings(GuildId) { getDefaultSettings(GuildId) {
return { return {
serverID: GuildId, serverID: GuildId,
lastLog: null,
serverName: "our server!", serverName: "our server!",
autoRestart: 0, autoRestart: 0,
showKillfeedCoords: 0, showKillfeedCoords: 0,
+13 -10
View File
@@ -24,17 +24,20 @@ const createWeaponsObject = (value) => {
module.exports = { module.exports = {
UpdatePlayer: async (client, player, interaction=null) => { UpdatePlayer: async (client, player, interaction=null) => {
return await client.dbo.collection("players").updateOne( /* Wrapping this function in a promise solves some bugs */
{ "playerID": player.playerID }, return new Promise(resolve => {
{ $set: player }, client.dbo.collection("players").updateOne(
{ upsert: true }, // Create player stat document if it does not exist { "playerID": player.playerID },
(err, _) => { { $set: player },
if (err) { { upsert: true }, // Create player stat document if it does not exist
if (interaction == null) return client.error(err); (err, _) => {
else return client.sendInternalError(interaction, err); if (err) {
if (interaction == null) return client.error(err);
else return client.sendInternalError(interaction, err);
} else resolve();
} }
} );
); });
}, },
getDefaultPlayer(gt, pID, NSID) { getDefaultPlayer(gt, pID, NSID) {
+4 -2
View File
@@ -1,9 +1,11 @@
const { InteractionType } = require('discord.js');
const { GetGuild } = require('../database/guild'); const { GetGuild } = require('../database/guild');
module.exports = async (client, interaction) => { module.exports = async (client, interaction) => {
if (interaction.isCommand()) return; if (interaction.type == InteractionType.ApplicationCommand) return;
/* /*
This file handles all menu and button interactions This file routes any menu, modal & button interactions
from any command from any command
*/ */
+78 -695
View File
File diff suppressed because it is too large. Load diff
+2 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "dayzr-bot", "name": "dayzr-bot",
"version": "12.7.1", "version": "13.0.0",
"description": "A General Purpose Discord Bot for DayZ Nitrado Servers.", "description": "A General Purpose Discord Bot for DayZ Nitrado Servers.",
"main": "index.js", "main": "index.js",
"nodemonConfig": { "nodemonConfig": {
@@ -20,14 +20,12 @@
"@discordjs/rest": "^1.1.0", "@discordjs/rest": "^1.1.0",
"colors": "^1.4.0", "colors": "^1.4.0",
"concat-stream": "^2.0.0", "concat-stream": "^2.0.0",
"crypto": "^1.0.1",
"discord-bitfield-calculator": "^1.0.0", "discord-bitfield-calculator": "^1.0.0",
"discord.js": "^14.8.0", "discord.js": "^14.8.0",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"mongodb": "^4.12.1", "mongodb": "^4.12.1",
"winston": "^3.8.1" "winston": "^3.8.1"
},
"devDependencies": {
"nodemon": "^2.0.20"
} }
} }
+162 -101
View File
@@ -1,14 +1,16 @@
const { RegisterGlobalCommands, RegisterGuildCommands } = require("../util/RegisterSlashCommands"); const { RegisterGlobalCommands, RegisterGuildCommands } = require("../util/RegisterSlashCommands");
const { Collection, Client, EmbedBuilder, Routes } = require('discord.js'); const { Collection, Client, EmbedBuilder, Routes, InteractionResponseType, InteractionType, GatewayDispatchEvents } = require('discord.js');
const MongoClient = require('mongodb').MongoClient; const MongoClient = require('mongodb').MongoClient;
const { REST } = require('@discordjs/rest'); const { REST } = require('@discordjs/rest');
const Logger = require("../util/Logger"); const Logger = require("../util/Logger");
const crypto = require('crypto');
// custom util imports // custom util imports
const { DownloadNitradoFile, CheckServerStatus, FetchServerSettings, PostServerSettings } = require('../util/NitradoAPI'); const { DownloadNitradoFile, CheckServerStatus, FetchServerSettings, PostServerSettings } = require('../util/NitradoAPI');
const { HandlePlayerLogs, HandleActivePlayersList } = require('../util/LogsHandler'); const { HandlePlayerLogs, HandleActivePlayersList } = require('../util/LogsHandler');
const { HandleKillfeed, UpdateLastDeathDate } = require('../util/KillfeedHandler'); const { HandleKillfeed, UpdateLastDeathDate } = require('../util/KillfeedHandler');
const { HandleExpiredUAVs, HandleEvents, PlaceFireplaceInAlarm } = require('../util/AlarmsHandler'); const { HandleExpiredUAVs, HandleEvents, PlaceFireplaceInAlarm } = require('../util/AlarmsHandler');
const { decrypt } = require('../util/Cryptic');
// Data structures imports // Data structures imports
const { getDefaultPlayer, UpdatePlayer } = require('../database/player'); const { getDefaultPlayer, UpdatePlayer } = require('../database/player');
@@ -21,6 +23,12 @@ const readline = require('readline');
const minute = 60000; // 1 minute in milliseconds const minute = 60000; // 1 minute in milliseconds
const arInterval = 600000; // Set auto-restart interval 10 minutes (600,000ms) const arInterval = 600000; // Set auto-restart interval 10 minutes (600,000ms)
const Missions = {
"dayzOffline.chernarusplus": "Chernarus",
"dayzOffline.enoch": "Livonia",
};
// TODO: probably just rewrite this thing in a non garbage language
class DayzRBot extends Client { class DayzRBot extends Client {
constructor(options, config) { constructor(options, config) {
@@ -32,30 +40,53 @@ class DayzRBot extends Client {
this.logger = new Logger(path.join(__dirname, "..", "logs/Logs.log")); this.logger = new Logger(path.join(__dirname, "..", "logs/Logs.log"));
this.timer = this.config.Dev == 'PROD.' ? minute * 5 : minute / 4; this.timer = this.config.Dev == 'PROD.' ? minute * 5 : minute / 4;
if (this.config.Token === "" || this.config.GuildID === "") { if (
this.config.Token === "" ||
this.config.SecretKey === "" ||
this.config.SecretIv === ""
) {
throw new TypeError( throw new TypeError(
"The config.js is not filled out. Please make sure nothing is blank, otherwise the bot will not work properly." "The config.js is not filled out. Please make sure nothing is blank, otherwise the bot will not work properly."
); );
} }
if (!["DEV.", "PROD."].includes(this.config.Dev)) {
throw new TypeError(
"The Dev version in the config.js does not match the allowed cases of 'DEV.' or 'PROD.'"
);
}
// Generate secret hash with crypto to use for encryption
this.key = crypto
.createHash('sha512')
.update(this.config.SecretKey)
.digest('hex')
.substring(0, 32);
this.encryptionIV = crypto
.createHash('sha512')
.update(this.config.SecretIv)
.digest('hex')
.substring(0, 16);
this.db; this.db;
this.dbo; this.dbo;
this.databaseConnected = false; this.databaseConnected = false;
this.arInterval = arInterval; this.arInterval = arInterval;
this.arIntervalId; // Interval for auto-restart functions this.arIntervalIds = new Map();
this.playerSessions = new Map(); this.playerSessions = new Map();
this.alarmPingQueue = {}; this.logHistory = new Map();
this.processingLogs = false; this.alarmPingQueue = new Map();
this.autoRestartInit(); this.initialize();
this.LoadCommandsAndInteractionHandlers(); this.LoadCommandsAndInteractionHandlers();
this.LoadEvents(); this.LoadEvents();
this.Ready = false; this.Ready = false;
this.activePlayersTick = 11; this.activePlayersTick = 11;
this.ws.on("INTERACTION_CREATE", async (interaction) => { this.ws.on(GatewayDispatchEvents.InteractionCreate, async (interaction) => {
const start = new Date().getTime(); const start = new Date().getTime();
if (interaction.type != 3) { if (interaction.type == InteractionType.ApplicationCommand) {
let GuildDB = await GetGuild(this, interaction.guild_id); let GuildDB = await GetGuild(this, interaction.guild_id);
for (const [factionID, data] of Object.entries(GuildDB.factionArmbands)) { for (const [factionID, data] of Object.entries(GuildDB.factionArmbands)) {
@@ -66,7 +97,7 @@ class DayzRBot extends Client {
$pull: { 'server.usedArmbands': data.armband }, $pull: { 'server.usedArmbands': data.armband },
$unset: { [`server.factionArmbands.${factionID}`]: "" }, $unset: { [`server.factionArmbands.${factionID}`]: "" },
}; };
await this.dbo.collection("guilds").updateOne({ 'server.serverID': GuildDB.serverID }, query, (err, res) => { this.dbo.collection("guilds").updateOne({ 'server.serverID': GuildDB.serverID }, query, (err, res) => {
if (err) return this.sendInternalError(interaction, err); if (err) return this.sendInternalError(interaction, err);
}); });
} }
@@ -75,32 +106,28 @@ class DayzRBot extends Client {
const command = interaction.data.name.toLowerCase(); const command = interaction.data.name.toLowerCase();
const args = interaction.data.options; const args = interaction.data.options;
this.log(`Interaction - ${command}`); this.log(`Interaction [${interaction.guild_id}] - ${command}`);
this.rest = new REST({ version: '10' }).setToken(this.config.Token); const rest = new REST({ version: '10' }).setToken(this.config.Token);
// Easy to send response so ;) // Easy to send response so ;)
interaction.guild = await this.guilds.fetch(interaction.guild_id); interaction.guild = await this.guilds.fetch(interaction.guild_id);
interaction.send = async (message) => { const handleCallback = async (interactionType, message) => {
return await this.rest.post(Routes.interactionCallback(interaction.id, interaction.token), { return await rest.post(Routes.interactionCallback(interaction.id, interaction.token), {
body: { body: {
type: 4, type: interactionType,
data: message, data: message,
} }
}); });
}; }
interaction.deferReply = async (message) => { // Nicely name our custom callback functions and pass correct type because discord is picky with numbers...
return await this.rest.post(Routes.interactionCallback(interaction.id, interaction.token), { interaction.send = async (message) => handleCallback(InteractionResponseType.ChannelMessageWithSource, message);
body: { interaction.deferReply = async (message) => handleCallback(InteractionResponseType.DeferredChannelMessageWithSource, message);
type: 5, interaction.showModal = async (message) => handleCallback(InteractionResponseType.Modal, message);
data: message,
}
});
};
interaction.editReply = async (message) => { interaction.editReply = async (message) => {
return await this.rest.patch(Routes.webhookMessage(this.application.id, interaction.token), { return await rest.patch(Routes.webhookMessage(this.application.id, interaction.token), {
body: message, body: message,
}); });
}; };
@@ -135,17 +162,7 @@ class DayzRBot extends Client {
} }
async readLogs(guild) { async readLogs(guild) {
const fileStream = fs.createReadStream('./logs/server-logs.ADM'); const fileStream = fs.createReadStream(`./logs/${guild.Nitrado.ServerID}-logs.ADM`);
let logHistoryDir = path.join(__dirname, '..', 'logs', 'history-logs.ADM.json');
let history;
try {
history = JSON.parse(fs.readFileSync(logHistoryDir));
} catch (err) {
history = {
lastLog: null
};
}
const rl = readline.createInterface({ const rl = readline.createInterface({
input: fileStream, input: fileStream,
@@ -154,13 +171,14 @@ class DayzRBot extends Client {
let lines = []; let lines = [];
for await (const line of rl) { lines.push(line); } for await (const line of rl) { lines.push(line); }
let logIndex = lines.indexOf(history.lastLog); let logIndex = lines.indexOf(this.logHistory.get(guild.Nitrado.ServerID));
if (this.playerSessions.get(guild.Nitrado.ServerID).size === 0) {
let players = await this.dbo.collection('players').find({"nitradoServerID": guild.Nitrado.ServerID}) // Get all players of this server
.toArray().then(all => all.filter(p => p.connected).map(p => p.connected = false)); // assume all players who were previously connected are not connected on init only.
if (this.playerSessions.size === 0) {
let players = await this.dbo.collection('players').find({"connected": true}).toArray();
players.map(p => p.connected = false); // assume all players not connected on init only.
for (let i = 0; i < players.length; i++) { for (let i = 0; i < players.length; i++) {
UpdatePlayer(this, players[i]) await UpdatePlayer(this, players[i])
} }
} }
@@ -171,38 +189,44 @@ class DayzRBot extends Client {
if ((i - 1) >= 0 && lines[i] == lines[i - 1]) continue; // continue if this line is a duplicate of the last line if ((i - 1) >= 0 && lines[i] == lines[i - 1]) continue; // continue if this line is a duplicate of the last line
// Handle general logs // Handle general logs
if (lines[i].includes('connected') || lines[i].includes('pos=<')) await HandlePlayerLogs(this, guild, lines[i], guild.combatLogTimer); if (lines[i].includes('connected') || lines[i].includes('pos=<')) await HandlePlayerLogs(guild.Nitrado.ServerID, this, guild, lines[i], guild.combatLogTimer);
if (lines[i].includes('killed by Zmb') || lines[i].includes('>) died.')) await UpdateLastDeathDate(this, lines[i]); // Updates users last death date for non PVP deaths. if (lines[i].includes('killed by Zmb') || lines[i].includes('>) died.')) await UpdateLastDeathDate(guild.Nitrado.ServerID, this, lines[i]); // Updates users last death date for non PVP deaths.
if (lines[i].includes(') placed Fireplace')) await PlaceFireplaceInAlarm(this, guild, lines[i]); if (lines[i].includes(') placed Fireplace')) await PlaceFireplaceInAlarm(this, guild, lines[i]);
// Handle killfeed logs // Handle killfeed logs
if (lines[i].includes('killed by with') || lines[i].includes('killed by LandMineTrap')) await HandleKillfeed(this, guild, lines[i]); // Handles explosive deaths if (
if (!(i + 1 >= lines.length) && lines[i + 1].includes('killed by') && lines[i].includes('TransportHit')) await HandleKillfeed(this, guild, lines[i]); // Handles vehicle deaths (lines[i].includes('killed by with') || lines[i].includes('killed by LandMineTrap')) || // Handle explosive deaths
if (!(i + 1 >= lines.length) && lines[i + 1].includes('killed by Player') && lines[i].includes('hit by Player')) await HandleKillfeed(this, guild, lines[i]); // Handles regular deaths (!(i + 1 >= lines.length) && lines[i + 1].includes('killed by') && lines[i].includes('TransportHit')) || // Handle vehicle deaths
if (lines[i].includes('killed by Player') && !lines[i - 1].includes('hit by Player')) await HandleKillfeed(this, guild, lines[i]); // Handles deaths missing hit by log (!(i + 1 >= lines.length) && lines[i + 1].includes('killed by Player') && lines[i].includes('hit by Player')) || // Handle PVP deaths
(lines[i].includes('killed by Player') && !lines[i - 1].includes('hit by Player')) // Handle deaths missing hit by log
) await HandleKillfeed(guild.Nitrado.ServerID, this, guild, lines[i]);
} }
// Handle alarm pings // Handle alarm pings
const maxEmbed = 10; const maxEmbed = 10;
for (const [channel_id, data] of Object.entries(this.alarmPingQueue)) {
const channel = this.GetChannel(channel_id);
if (!channel) continue;
for (const [role, embeds] of Object.entries(data)) {
let embedArrays = [];
while (embeds.length > 0)
embedArrays.push(embeds.splice(0, maxEmbed))
for (let i = 0; i < embedArrays.length; i++) { this.alarmPingQueue.forEach(queue => {
if (role == '-no-role-ping-') channel.send({ embeds: embedArrays[i] }); queue.forEach((data, channel_id) => {
else channel.send({ content: `<@&${role}>`, embeds: embedArrays[i] }); const channel = this.GetChannel(channel_id);
} if (!channel) return;
} data.forEach((embeds, role) => {
} let embedArrays = [];
while (embeds.length > 0);
embedArrays.push(embeds.splice(0, maxEmbed));
this.alarmPingQueue = {}; for (let i = 0; i < embedArrays.length; i++) {
if (role == '-no-role-ping-') channel.send({ embeds: embedArrays[i] });
else channel.send({ content: `<@&${role}>`, embeds: embedArrays[i] });
}
});
});
});
this.alarmPingQueue.set(guild.serverID, new Map()); // Clear alarm queue for this guild
const playerTemplate = /(.*) \| Player \"(.*)\" \(id=(.*) pos=<(.*)>\)/g; const playerTemplate = /(.*) \| Player \"(.*)\" \(id=(.*) pos=<(.*)>\)/g;
let previouslyConnected = await this.dbo.collection('players').find({"connected": true}).toArray(); // All players with connection log captured above and no disconnect log let previouslyConnected = await this.dbo.collection('players').find({"nitradoServerID": guild.Nitrado.ServerID})
.toArray().then(players => players.filter(p => p.connected)); // All players with connection log captured above and no disconnect log
let lastDetectedTime; let lastDetectedTime;
for (let i = lines.length - 1; i > 0; i--) { for (let i = lines.length - 1; i > 0; i--) {
@@ -225,14 +249,14 @@ class DayzRBot extends Client {
lastDetectedTime = await this.getDateEST(info.time); lastDetectedTime = await this.getDateEST(info.time);
let playerStat = await this.dbo.collection("players").findOne({"playerID": info.playerID}); let playerStat = await this.dbo.collection("players").findOne({"playerID": info.playerID});
if (!this.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, this.config.Nitrado.ServerID); if (!this.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, guild.Nitrado.ServerID);
if (!previouslyConnected.includes(playerStat) && this.exists(playerStat.lastDisconnectionDate) && playerStat.lastDisconnectionDate !== null && playerStat.lastDisconnectionDate.getTime() > lastDetectedTime.getTime()) continue; // Skip this player if the lastDisconnectionDate time is later than the player log entry. if (!previouslyConnected.includes(playerStat) && this.exists(playerStat.lastDisconnectionDate) && playerStat.lastDisconnectionDate !== null && playerStat.lastDisconnectionDate.getTime() > lastDetectedTime.getTime()) continue; // Skip this player if the lastDisconnectionDate time is later than the player log entry.
// Track adjusted sessions this instance has handled (e.g. no bot crashes or restarts). // Track adjusted sessions this instance has handled (e.g. no bot crashes or restarts).
if (this.playerSessions.has(info.playerID)) { if (this.playerSessions.get(guild.Nitrado.ServerID).has(info.playerID)) {
// Player is already in a session, update the session's end time. // Player is already in a session, update the session's end time.
const session = this.playerSessions.get(info.playerID); const session = this.playerSessions.get(guild.Nitrado.ServerID).get(info.playerID);
session.endTime = lastDetectedTime; // Update end time. session.endTime = lastDetectedTime; // Update end time.
} else { } else {
// Player is not in a session, create a new session. // Player is not in a session, create a new session.
@@ -240,7 +264,7 @@ class DayzRBot extends Client {
startTime: lastDetectedTime, startTime: lastDetectedTime,
endTime: null, // Initialize end time as null. endTime: null, // Initialize end time as null.
}; };
this.playerSessions.set(info.playerID, newSession); this.playerSessions.get(guild.Nitrado.ServerID).set(info.playerID, newSession);
// Check if the player has been marked as connected before, but only if a session doesn't exist // Check if the player has been marked as connected before, but only if a session doesn't exist
// in the map, indicating the connection was discovered in the logs during this session. // in the map, indicating the connection was discovered in the logs during this session.
@@ -256,46 +280,59 @@ class DayzRBot extends Client {
} }
} }
history.lastLog = lines[lines.length - 1]; const lastLine = lines[lines.length - 1]
this.logHistory.set(guild.Nitrado.ServerID, lastLine);
// write JSON string to a file this.dbo.collection("guilds").updateOne({ "server.serverID": guild.serverID }, {$set: { "server.lastLog": lastLine }}, (err, res) => {
fs.writeFileSync(logHistoryDir, JSON.stringify(history)); if (err) this.error(`Failed to save last log to guild config [${guild.serverID}] for nitrado server [${guild.Nitrado.ServerID}]`);
});
} }
async logsUpdateTimer(c) { async logsUpdateTimer(c) {
if (c.processingLogs) return; // Process is already running, wait till next scheduled time.
c.processingLogs = true;
let t = new Date();
// c.log(`...Logs Tick - ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}...`);
c.activePlayersTick++; c.activePlayersTick++;
const settings = await FetchServerSettings(c, "logsUpdateTimer").then(res => res.data.gameserver); c.guilds.cache.forEach(async (guild) => {
let GuildDB = await GetGuild(c, guild.id);
if (settings == 1) { /*
c.error('...Failed to Fetch Server Settings to download Nitrado Logs...'); Note to self:
return; return statements do not prematurely exit out of a forEach loop like it does in a for loop.
} */
const filename = settings.game_specific.log_files.sort((a, b) => a.length - b.length)[0]; if (!c.exists(GuildDB.Nitrado)) return; // Continue if no nitrado credentials
const path = `${settings.game_specific.path.slice(0, -1)}${filename.split(settings.game)[1]}`;
// Ensure Player List is logged for next update const NitradoCred = {
const playerListEnabled = parseInt(settings.settings.config.adminLogPlayerList) ServerID: GuildDB.Nitrado.ServerID,
if (!playerListEnabled) PostServerSettings(this, "config", "adminLogPlayerList", '1') UserID: GuildDB.Nitrado.UserID,
Auth: decrypt(
GuildDB.Nitrado.Auth,
c.config.EncryptionMethod,
c.key,
c.encryptionIV
)
};
let guild = await GetGuild(c, c.config.GuildID); const response = await FetchServerSettings(NitradoCred, c, "logsUpdateTimer").then(res => res);
if (response == 1) return;
const settings = response.data.gameserver;
await DownloadNitradoFile(c, path, './logs/server-logs.ADM').then(async (status) => { GuildDB.Nitrado.Mission = Missions[settings.settings.config.mission];
if (status == 1) return c.error('...Failed to Download logs...');
// c.log('...Downloaded logs...'); const filename = settings.game_specific.log_files.sort((a, b) => a.length - b.length)[0];
await c.readLogs(guild).then(async () => { const path = `${settings.game_specific.path.slice(0, -1)}${filename.split(settings.game)[1]}`;
// c.log('...Analyzed logs...');
HandleExpiredUAVs(c, guild); // Ensure Player List is logged for next update
HandleEvents(c, guild) const playerListEnabled = parseInt(settings.settings.config.adminLogPlayerList)
if (c.activePlayersTick == 12) await HandleActivePlayersList(c, guild); if (!playerListEnabled) PostServerSettings(NitradoCred, c, "config", "adminLogPlayerList", '1')
})
await DownloadNitradoFile(NitradoCred, c, path, `./logs/${NitradoCred.ServerID}-logs.ADM`).then(async (status) => {
if (status == 1) return c.error(`Failed to Download Nitrado Log Files - [${NitradoCred.ServerID}]`);
await c.readLogs(GuildDB).then(async () => {
HandleExpiredUAVs(c, GuildDB);
HandleEvents(c, GuildDB)
if (c.activePlayersTick == 12) await HandleActivePlayersList(NitradoCred, c, GuildDB);
})
});
}); });
c.processingLogs = false;
} }
async connectMongo(mongoURI, dbo) { async connectMongo(mongoURI, dbo) {
@@ -338,19 +375,35 @@ class DayzRBot extends Client {
if (failed) process.exit(-1); if (failed) process.exit(-1);
} }
async autoRestartInit() { async initialize() {
// Wait for MongoDB to connect // Wait for MongoDB to connect
await this.connectMongo(this.config.mongoURI, this.config.dbo); await this.connectMongo(this.config.mongoURI, this.config.dbo);
let is_enabled = undefined; let guilds = await this.dbo.collection("guilds").find({}).toArray();
if (this.databaseConnected) is_enabled = await this.dbo.collection("guilds").findOne({ "server.autoRestart": 1 }).then(is_enabled => is_enabled);
if (is_enabled) { /*
this.log('Starting periodic Nitrado server status check.'); Initialize auto restart for enabled servers
this.arIntervalId = setInterval(CheckServerStatus, this.arInterval, this); Initialize last logs
Initialize Player Sessions
*/
for (let i = 0; i < guilds.length; i++) {
if (!this.exists(guilds[i].Nitrado)) continue;
if (guilds[i].server.autoRestart) this.arIntervalIds.set(guilds[i].server.serverID, setInterval(CheckServerStatus, this.arInterval, guilds[i].nitrado, this))
this.logHistory.set(guilds[i].Nitrado.ServerID, guilds[i].server.lastLog); // Using Nitrado Server ID over guild ID in case of future support for multiple nitrado servers in a single guild
this.playerSessions.set(guilds[i].Nitrado.ServerID, new Map()); // Same reason here as named above.
this.log(`[${guilds[i].server.serverID}] Initialized existing Nitrado`);
} }
} }
async initNewNitradoServer(guildId, Nitrado) {
let guild = await GetGuild(this, guildId)
if (guild.autoRestart) this.arIntervalIds.set(guildId, setInterval(CheckServerStatus, this.arInterval, Nitrado, this))
this.logHistory.set(Nitrado.ServerID, guild.lastLog);
this.playerSessions.set(Nitrado.ServerID, new Map());
this.log(`[${guild.serverID}] Initialized new Nitrado`);
}
exists(n) { return typeof(n) == 'number' ? !isNaN(n) : null != n && undefined != n && "" != n } exists(n) { return typeof(n) == 'number' ? !isNaN(n) : null != n && undefined != n && "" != n }
secondsToDhms(seconds) { secondsToDhms(seconds) {
@@ -434,7 +487,15 @@ class DayzRBot extends Client {
// Calls register for guild and global commands // Calls register for guild and global commands
RegisterSlashCommands() { RegisterSlashCommands() {
RegisterGlobalCommands(this); RegisterGlobalCommands(this);
this.guilds.cache.forEach((guild) => RegisterGuildCommands(this, guild.id)); let p = Promise.resolve()
this.guilds.cache.forEach((guild) => {
p = p.then(() => {
RegisterGuildCommands(this, guild.id);
return new Promise((resolve) => {
setTimeout(resolve, 500);
})
})
});
} }
build() { build() {
+3 -16
View File
@@ -1,6 +1,5 @@
const { EmbedBuilder } = require('discord.js'); const { EmbedBuilder } = require('discord.js');
const { destinations } = require('../database/destinations'); const { nearest } = require('../database/destinations');
const { calculateVector } = require('./Vector');
module.exports = { module.exports = {
@@ -16,7 +15,7 @@ module.exports = {
.setDescription(`**${data.connected ? 'Connect' : 'Disconnect'} Event - <t:${unixTime}>\n${data.player} ${data.connected ? 'Connected' : 'Disconnected'}**`); .setDescription(`**${data.connected ? 'Connect' : 'Disconnect'} Event - <t:${unixTime}>\n${data.player} ${data.connected ? 'Connected' : 'Disconnected'}**`);
if (!data.connected) { if (!data.connected) {
if (!(data.lastConnectionDate == null)) { if (data.lastConnectionDate != null) {
let oldUnixTime = Math.floor(data.lastConnectionDate.getTime() / 1000); let oldUnixTime = Math.floor(data.lastConnectionDate.getTime() / 1000);
let sessionTime = client.secondsToDhms(unixTime - oldUnixTime); let sessionTime = client.secondsToDhms(unixTime - oldUnixTime);
connectionLog.addFields({ name: '**Session Time**', value: `**${sessionTime}**`, inline: false }); connectionLog.addFields({ name: '**Session Time**', value: `**${sessionTime}**`, inline: false });
@@ -47,19 +46,7 @@ module.exports = {
if (!channel) return; if (!channel) return;
let unixTime = Math.floor(newDt.getTime() / 1000); let unixTime = Math.floor(newDt.getTime() / 1000);
const destination = nearest(data.pos, guild.Nitrado.Mission);
let tempDest;
let lastDist = 1000000;
let destination_dir;
for (let i = 0; i < destinations.length; i++) {
let { distance, theta, dir } = calculateVector(data.pos, destinations[i].coord);
if (distance < lastDist) {
tempDest = destinations[i].name;
lastDist = distance;
destination_dir = dir;
}
}
const destination = lastDist > 500 ? `${destination_dir} of ${tempDest}` : `Near ${tempDest}`;
let combatLog = new EmbedBuilder() let combatLog = new EmbedBuilder()
.setColor(client.config.Colors.Red) .setColor(client.config.Colors.Red)
+8 -31
View File
@@ -1,7 +1,6 @@
const { BanPlayer, UnbanPlayer } = require('./NitradoAPI'); const { BanPlayer, UnbanPlayer } = require('./NitradoAPI');
const { EmbedBuilder } = require('discord.js'); const { EmbedBuilder } = require('discord.js');
const { calculateVector } = require('./Vector'); const { nearest } = require('../database/destinations');
const { destinations } = require('../database/destinations');
const { GetGuild } = require('../database/guild'); const { GetGuild } = require('../database/guild');
// Private functions (only called locally) // Private functions (only called locally)
@@ -26,18 +25,7 @@ const HandlePlayerTrackEvent = async (client, guild, e) => {
let newDt = await client.getDateEST(player.time); let newDt = await client.getDateEST(player.time);
let unixTime = Math.floor(newDt.getTime()/1000); let unixTime = Math.floor(newDt.getTime()/1000);
let tempDest; const destination = nearest(player.pos, guild.Nitrado.Mission);
let lastDist = 1000000;
let destination_dir;
for (let i = 0; i < destinations.length; i++) {
let { distance, theta, dir } = calculateVector(player.pos, destinations[i].coord);
if (distance < lastDist) {
tempDest = destinations[i].name;
lastDist = distance;
destination_dir = dir;
}
}
const destination = lastDist > 500 ? `${destination_dir} of ${tempDest}` : `Near ${tempDest}`;
const trackEvent = new EmbedBuilder() const trackEvent = new EmbedBuilder()
.setColor(client.config.Colors.Default) .setColor(client.config.Colors.Default)
@@ -76,12 +64,12 @@ module.exports = {
let newDt = await client.getDateEST(data.time); let newDt = await client.getDateEST(data.time);
let unixTime = Math.floor(newDt.getTime()/1000); let unixTime = Math.floor(newDt.getTime()/1000);
if (!client.alarmPingQueue[alarm.channel]) client.alarmPingQueue[alarm.channel] = {}; if (!client.alarmPingQueue.get(guild.serverID).has(alarm.channel)) client.alarmPingQueue.get(guild.serverID).set(alarm.channel, new Map());
let route = alarm.mute ? '-no-role-ping-' : alarm.role; let route = alarm.mute ? null : alarm.role;
if (!client.alarmPingQueue[alarm.channel][route]) client.alarmPingQueue[alarm.channel][route] = []; if (!client.alarmPingQueue.get(guild.serverID).get(alarm.channel).has(route)) client.alarmPingQueue.get(guild.serverID).get(alarm.channel).set(route, []);
if (alarm.rules.includes['ban_on_entry']) { if (alarm.rules.includes['ban_on_entry']) {
client.alarmPingQueue[alarm.channel][route].push( client.alarmPingQueue.get(guild.serverID).get(alarm.channel).get(route).push(
new EmbedBuilder() new EmbedBuilder()
.setColor(client.config.Colors.Default) .setColor(client.config.Colors.Default)
.setDescription(`**Zone Ping - <t:${unixTime}>**\n**${data.player}** was located within **${distance} meters** of the Zone **${alarm.name}** __and has been banned.__`) .setDescription(`**Zone Ping - <t:${unixTime}>**\n**${data.player}** was located within **${distance} meters** of the Zone **${alarm.name}** __and has been banned.__`)
@@ -92,7 +80,7 @@ module.exports = {
return; return;
} }
client.alarmPingQueue[alarm.channel][route].push( client.alarmPingQueue.get(guild.serverID).get(alarm.channel).get(route).push(
new EmbedBuilder() new EmbedBuilder()
.setColor(client.config.Colors.Default) .setColor(client.config.Colors.Default)
.setDescription(`**Zone Ping - <t:${unixTime}>**\n**${data.player}** was located within **${distance} meters** of the Zone **${alarm.name}**`) .setDescription(`**Zone Ping - <t:${unixTime}>**\n**${data.player}** was located within **${distance} meters** of the Zone **${alarm.name}**`)
@@ -113,18 +101,7 @@ module.exports = {
let newDt = await client.getDateEST(data.time); let newDt = await client.getDateEST(data.time);
let unixTime = Math.floor(newDt.getTime()/1000); let unixTime = Math.floor(newDt.getTime()/1000);
let tempDest; const destination = nearest(data.pos, guild.Nitrado.Mission);
let lastDist = 1000000;
let destination_dir;
for (let i = 0; i < destinations.length; i++) {
let { distance, theta, dir } = calculateVector(data.pos, destinations[i].coord);
if (distance < lastDist) {
tempDest = destinations[i].name;
lastDist = distance;
destination_dir = dir;
}
}
const destination = lastDist > 500 ? `${destination_dir} of ${tempDest}` : `Near ${tempDest}`;
let uavEmbed = new EmbedBuilder() let uavEmbed = new EmbedBuilder()
.setColor(client.config.Colors.Default) .setColor(client.config.Colors.Default)
+19
View File
@@ -0,0 +1,19 @@
const crypto = require('crypto');
module.exports = {
encrypt: (data, EncryptionMethod, Key, EncryptionIV) => {
const cipher = crypto.createCipheriv(EncryptionMethod, Key, EncryptionIV)
return Buffer.from(
cipher.update(data, 'utf8', 'hex') + cipher.final('hex')
).toString('base64') // Encrypts data and converts to hex and base64
},
decrypt: (data, EncryptionMethod, Key, EncryptionIV) => {
const buff = Buffer.from(data, 'base64')
const decipher = crypto.createDecipheriv(EncryptionMethod, Key, EncryptionIV)
return (
decipher.update(buff.toString('utf8'), 'hex', 'utf8') +
decipher.final('utf8')
) // Decrypts data and converts to utf8
}
}
+8 -22
View File
@@ -1,8 +1,7 @@
const { EmbedBuilder } = require('discord.js'); const { EmbedBuilder } = require('discord.js');
const { createUser, addUser } = require('../database/user'); const { createUser, addUser } = require('../database/user');
const { KillInAlarm } = require('./AlarmsHandler'); const { KillInAlarm } = require('./AlarmsHandler');
const { destinations } = require('../database/destinations'); const { nearest } = require('../database/destinations');
const { calculateVector } = require('./Vector');
const { getDefaultPlayer, UpdatePlayer } = require('../database/player'); const { getDefaultPlayer, UpdatePlayer } = require('../database/player');
const { calculateNewCombatRating } = require('./CombatRatingHandler'); const { calculateNewCombatRating } = require('./CombatRatingHandler');
const { weapons, weaponClassOf } = require('../database/weapons'); const { weapons, weaponClassOf } = require('../database/weapons');
@@ -54,7 +53,7 @@ const Vehicles = {
module.exports = { module.exports = {
// Update last death date for non PVP deaths // Update last death date for non PVP deaths
UpdateLastDeathDate: async (client, line) => { UpdateLastDeathDate: async (NitradoServerID, client, line) => {
let killedByZmb = /(.*) \| Player \"(.*)\" \(DEAD\) \(id=(.*) pos=<(.*)>\) killed by (.*)/g; let killedByZmb = /(.*) \| Player \"(.*)\" \(DEAD\) \(id=(.*) pos=<(.*)>\) killed by (.*)/g;
let diedTemplate = /(.*) \| Player \"(.*)\" \(DEAD\) \(id=(.*) pos=<(.*)>\) died\. Stats> Water: (.*) Energy: (.*) Bleed sources: (.*)/g; let diedTemplate = /(.*) \| Player \"(.*)\" \(DEAD\) \(id=(.*) pos=<(.*)>\) died\. Stats> Water: (.*) Energy: (.*) Bleed sources: (.*)/g;
@@ -71,7 +70,7 @@ module.exports = {
const newDt = await client.getDateEST(info.time); const newDt = await client.getDateEST(info.time);
let victimStat = await client.dbo.collection("players").findOne({"playerID": info.playerID}); let victimStat = await client.dbo.collection("players").findOne({"playerID": info.playerID});
if (!client.exists(victimStat)) victimStat = getDefaultPlayer(info.player, info.playerID, client.config.Nitrado.ServerID); if (!client.exists(victimStat)) victimStat = getDefaultPlayer(info.player, info.playerID, NitradoServerID);
victimStat.lastDeathDate = newDt; victimStat.lastDeathDate = newDt;
@@ -79,7 +78,7 @@ module.exports = {
return return
}, },
HandleKillfeed: async (client, guild, line) => { HandleKillfeed: async (NitradoServerID, client, guild, line) => {
const channel = client.GetChannel(guild.killfeedChannel); const channel = client.GetChannel(guild.killfeedChannel);
@@ -127,26 +126,13 @@ module.exports = {
const showCoords = client.exists(guild.showKillfeedCoords) ? guild.showKillfeedCoords : false; // default to false if no record of configuration. const showCoords = client.exists(guild.showKillfeedCoords) ? guild.showKillfeedCoords : false; // default to false if no record of configuration.
const showWeapon = client.exists(guild.showKillfeedWeapon) ? guild.showKillfeedWeapon : false; // default to false if no record of configuration. const showWeapon = client.exists(guild.showKillfeedWeapon) ? guild.showKillfeedWeapon : false; // default to false if no record of configuration.
let tempDest;
let lastDist = 1000000;
let destination_dir;
if (showCoords) { // Only calculate if showing coords, very tiny minor optimization... probably amounts to nothing.
for (let i = 0; i < destinations.length; i++) {
let { distance, theta, dir } = calculateVector(info.victimPOS, destinations[i].coord);
if (distance < lastDist) {
tempDest = destinations[i].name;
lastDist = distance;
destination_dir = dir;
}
}
}
const destination = lastDist > 500 ? `${destination_dir} of ${tempDest}` : `Near ${tempDest}`; const destination = nearest(info.victimPOS, guild.Nitrado.Mission);
if ([Templates.LandMine, Templates.Explosion, Templates.Vehicle].includes(killedBy)) if ([Templates.LandMine, Templates.Explosion, Templates.Vehicle].includes(killedBy))
if (killedBy == Templates.LandMine || killedBy == Templates.Explosion || killedBy == Templates.Vehicle) { if (killedBy == Templates.LandMine || killedBy == Templates.Explosion || killedBy == Templates.Vehicle) {
let victimStat = await client.dbo.collection("players").findOne({"playerID": info.victimID}); let victimStat = await client.dbo.collection("players").findOne({"playerID": info.victimID});
if (!client.exists(victimStat)) victimStat = getDefaultPlayer(info.victim, info.victimID, client.config.Nitrado.ServerID); if (!client.exists(victimStat)) victimStat = getDefaultPlayer(info.victim, info.victimID, NitradoServerID);
victimStat.lastDeathDate = newDt; victimStat.lastDeathDate = newDt;
const cod = killedBy == Templates.LandMine ? `Land Mine Trap` : const cod = killedBy == Templates.LandMine ? `Land Mine Trap` :
@@ -169,8 +155,8 @@ module.exports = {
let victimStat = await client.dbo.collection("players").findOne({"playerID": info.victimID}); let victimStat = await client.dbo.collection("players").findOne({"playerID": info.victimID});
let killerStat = await client.dbo.collection("players").findOne({"playerID": info.killerID}); let killerStat = await client.dbo.collection("players").findOne({"playerID": info.killerID});
if (!client.exists(victimStat)) victimStat = getDefaultPlayer(info.victim, info.victimID, client.config.Nitrado.ServerID); if (!client.exists(victimStat)) victimStat = getDefaultPlayer(info.victim, info.victimID, NitradoServerID);
if (!client.exists(killerStat)) killerStat = getDefaultPlayer(info.killer, info.killerID, client.config.Nitrado.ServerID); if (!client.exists(killerStat)) killerStat = getDefaultPlayer(info.killer, info.killerID, NitradoServerID);
let weapon = info.weapon.includes("Engraved") ? info.weapon.split("Engraved ")[1] : let weapon = info.weapon.includes("Engraved") ? info.weapon.split("Engraved ")[1] :
info.weapon.includes("Sawed-off") ? info.weapon.split("Sawed-off ")[1] : info.weapon.includes("Sawed-off") ? info.weapon.split("Sawed-off ")[1] :
+12 -11
View File
@@ -5,11 +5,12 @@ const { getDefaultPlayer } = require('../database/player');
const { FetchServerSettings } = require('../util/NitradoAPI'); const { FetchServerSettings } = require('../util/NitradoAPI');
const { UpdatePlayer, insertPVPstats, createWeaponStats } = require('../database/player') const { UpdatePlayer, insertPVPstats, createWeaponStats } = require('../database/player')
// TODO: Remove lastSendMessage
let lastSendMessage; let lastSendMessage;
module.exports = { module.exports = {
HandlePlayerLogs: async (client, GuildDB, line, combatLogTimer = 5) => { HandlePlayerLogs: async (NitradoServerID, client, GuildDB, line, combatLogTimer = 5) => {
const connectTemplate = /(.*) \| Player \"(.*)\" is connected \(id=(.*)\)/g; const connectTemplate = /(.*) \| Player \"(.*)\" is connected \(id=(.*)\)/g;
const disconnectTemplate = /(.*) \| Player \"(.*)\"\(id=(.*)\) has been disconnected/g; const disconnectTemplate = /(.*) \| Player \"(.*)\"\(id=(.*)\) has been disconnected/g;
@@ -30,7 +31,7 @@ module.exports = {
if (!client.exists(info.player) || !client.exists(info.playerID)) return; if (!client.exists(info.player) || !client.exists(info.playerID)) return;
let playerStat = await client.dbo.collection("players").findOne({"playerID": info.playerID}); let playerStat = await client.dbo.collection("players").findOne({"playerID": info.playerID});
if (!client.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, client.config.Nitrado.ServerID); if (!client.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, NitradoServerID);
const newDt = await client.getDateEST(info.time); const newDt = await client.getDateEST(info.time);
playerStat.lastConnectionDate = newDt; playerStat.lastConnectionDate = newDt;
@@ -39,9 +40,9 @@ module.exports = {
playerStat.connections++; playerStat.connections++;
// Track adjusted sessions this instance has handled (e.g. no bot crashes or restarts). // Track adjusted sessions this instance has handled (e.g. no bot crashes or restarts).
if (client.playerSessions.has(info.playerID)) { if (client.playerSessions.get(NitradoServerID).has(info.playerID)) {
// Player is already in a session, update the session's end time. // Player is already in a session, update the session's end time.
const session = client.playerSessions.get(info.playerID); const session = client.playerSessions.get(NitradoServerID).get(info.playerID);
session.endTime = newDt; // Update end time. session.endTime = newDt; // Update end time.
} else { } else {
// Player is not in a session, create a new session. // Player is not in a session, create a new session.
@@ -49,7 +50,7 @@ module.exports = {
startTime: newDt, startTime: newDt,
endTime: null, // Initialize end time as null. endTime: null, // Initialize end time as null.
}; };
client.playerSessions.set(info.playerID, newSession); client.playerSessions.get(NitradoServerID).set(info.playerID, newSession);
} }
await SendConnectionLogs(client, GuildDB, { await SendConnectionLogs(client, GuildDB, {
@@ -75,7 +76,7 @@ module.exports = {
if (!client.exists(info.player) || !client.exists(info.playerID)) return; if (!client.exists(info.player) || !client.exists(info.playerID)) return;
let playerStat = await client.dbo.collection("players").findOne({"playerID": info.playerID}); let playerStat = await client.dbo.collection("players").findOne({"playerID": info.playerID});
if (!client.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, client.config.Nitrado.ServerID); if (!client.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, NitradoServerID);
let oldUnixTime; let oldUnixTime;
let sessionTimeSeconds; let sessionTimeSeconds;
@@ -129,7 +130,7 @@ module.exports = {
if (!client.exists(info.player) || !client.exists(info.playerID)) return; if (!client.exists(info.player) || !client.exists(info.playerID)) return;
let playerStat = await client.dbo.collection("players").findOne({"playerID": info.playerID}); let playerStat = await client.dbo.collection("players").findOne({"playerID": info.playerID});
if (!client.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, client.config.Nitrado.ServerID); if (!client.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, NitradoServerID);
if (!client.exists(playerStat.lastConnectionDate)) playerStat.lastConnectionDate = await client.getDateEST(info.time); if (!client.exists(playerStat.lastConnectionDate)) playerStat.lastConnectionDate = await client.getDateEST(info.time);
playerStat.lastPos = playerStat.pos; playerStat.lastPos = playerStat.pos;
@@ -169,8 +170,8 @@ module.exports = {
let playerStat = await client.dbo.collection("players").findOne({"playerID": info.playerID}); let playerStat = await client.dbo.collection("players").findOne({"playerID": info.playerID});
let attackerStat = await client.dbo.collection("players").findOne({"playerID": info.attackerID}); let attackerStat = await client.dbo.collection("players").findOne({"playerID": info.attackerID});
if (!client.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, client.config.Nitrado.ServerID); if (!client.exists(playerStat)) playerStat = getDefaultPlayer(info.player, info.playerID, NitradoServerID);
if (!client.exists(attackerStat)) attackerStat = getDefaultPlayer(info.attacker, info.attackerID, client.config.Nitrado.ServerID); if (!client.exists(attackerStat)) attackerStat = getDefaultPlayer(info.attacker, info.attackerID, NitradoServerID);
playerStat.lastDamageDate = await client.getDateEST(info.time); playerStat.lastDamageDate = await client.getDateEST(info.time);
playerStat.lastHitBy = info.attacker; playerStat.lastHitBy = info.attacker;
@@ -202,10 +203,10 @@ module.exports = {
return; return;
}, },
HandleActivePlayersList: async (client, guild) => { HandleActivePlayersList: async (nitrado_cred, client, guild) => {
client.activePlayersTick = 0; // reset hour tick client.activePlayersTick = 0; // reset hour tick
const data = await FetchServerSettings(client, 'HandleActivePlayersList'); // Fetch server status const data = await FetchServerSettings(nitrado_cred, client, 'HandleActivePlayersList'); // Fetch server status
if (data && data !== 1) { if (data && data !== 1) {
let hostname = data.data.gameserver.settings.config.hostname; let hostname = data.data.gameserver.settings.config.hostname;
+85 -65
View File
@@ -8,20 +8,18 @@ const retryDelay = 5000; // 5 seconds
// Private functions (only called locally) // Private functions (only called locally)
const UploadNitradoFile = async (client, remoteDir, remoteFilename, localFileDir) => { const UploadNitradoFile = async (nitrado_cred, client, remoteDir, remoteFilename, localFileDir) => {
for (let retries = 0; retries <= maxRetries; retries++) { for (let retries = 0; retries <= maxRetries; retries++) {
try { try {
const res = await fetch(`https://api.nitrado.net/services/${client.config.Nitrado.ServerID}/gameservers/file_server/upload?` + new URLSearchParams({ const res = await fetch(`https://api.nitrado.net/services/${nitrado_cred.ServerID}/gameservers/file_server/upload?` + new URLSearchParams({
path: remoteDir, path: remoteDir,
file: remoteFilename file: remoteFilename
}), { }), {
method: "POST", method: "POST",
headers: { headers: {
"Authorization": client.config.Nitrado.Auth "Authorization": nitrado_cred.Auth
}, },
}).then(response => }).then(response => response.json());
response.json().then(data => data)
).then(res => res);
let contents = fs.readFileSync(localFileDir, 'utf8'); let contents = fs.readFileSync(localFileDir, 'utf8');
@@ -34,22 +32,24 @@ const UploadNitradoFile = async (client, remoteDir, remoteFilename, localFileDir
body: contents, body: contents,
}) })
if (!uploadRes.ok) { if (!uploadRes.ok) {
const errorText = await uploadRes.text(); client.error(`Failed to upload file to Nitrado (${nitrado_cred.ServerID}): status: ${uploadRes.status}, message: ${res.statusText}: UploadNitradoFile`);
client.error(`Failed to upload file to Nitrado (${client.config.Nitrado.ServerID}): status: ${uploadRes.status}, message: ${errorText}: UploadNitradoFile`);
if (retries === 2) return 1; // Return error status on the second failed status code. if (retries === 2) return 1; // Return error status on the second failed status code.
} else { } else {
return uploadRes; return uploadRes;
} }
} catch (error) { } catch (error) {
client.error(`UploadNitradoFile: Error connecting to server (${client.config.Nitrado.ServerID}): ${error.message}`); client.error(`UploadNitradoFile: Error connecting to server (${nitrado_cred.ServerID}): ${error.message}`);
if (retries === maxRetries) throw new Error(`UploadNitradoFile: Error connecting to server (${client.config.Nitrado.ServerID}) after ${maxRetries} retries`); if (retries === maxRetries) {
client.error(`UploadNitradoFile: Error connecting to server (${nitrado_cred.ServerID}) after ${maxRetries} retries`);
return 1;
}
} }
await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying
} }
} }
const HandlePlayerBan = async (client, gamertag, ban) => { const HandlePlayerBan = async (nitrado_cred, client, gamertag, ban) => {
const data = await module.exports.FetchServerSettings(client, 'HandlePlayerBan'); // Fetch server status const data = await module.exports.FetchServerSettings(nitrado_cred, client, 'HandlePlayerBan'); // Fetch server status
if (data && data != 1) { if (data && data != 1) {
let bans = data.data.gameserver.settings.general.bans; let bans = data.data.gameserver.settings.general.bans;
@@ -59,26 +59,31 @@ const HandlePlayerBan = async (client, gamertag, ban) => {
let category = 'general'; let category = 'general';
let key = 'bans'; let key = 'bans';
return await module.exports.PostServerSettings(client, category, key, bans); // returns 1 (failed) or 0 (not failed) return await module.exports.PostServerSettings(nitrado_cred, client, category, key, bans); // returns 1 (failed) or 0 (not failed)
} }
} }
const GetRemoteDir = async (client, dir="") => { const GetRemoteDir = async (nitrado_cred, client, dir="") => {
const dirParam = client.exists(dir) ? `?dir=${dir}` : ""; const dirParam = client.exists(dir) ? `?dir=${dir}` : "";
for (let retries = 0; retries <= maxRetries; retries++) { for (let retries = 0; retries <= maxRetries; retries++) {
try { try {
const res = await fetch(`https://api.nitrado.net/services/${client.config.Nitrado.ServerID}/gameservers/file_server/list${dirParam}`, { const res = await fetch(`https://api.nitrado.net/services/${nitrado_cred.ServerID}/gameservers/file_server/list${dirParam}`, {
headers: { headers: {
"Authorization": client.config.Nitrado.Auth "Authorization": nitrado_cred.Auth
} }
}).then(response => }).then(response =>
response.json().then(data => data) response.json().then(data => data)
).then(res => res); ).then(res => res);
if (res.status === "error") return 1;
return res.data.entries; return res.data.entries;
} catch (error) { } catch (error) {
client.error(`GetRemoteDir: Error connecting to server (${client.config.Nitrado.ServerID}): ${error.message}`); client.error(`GetRemoteDir: Error connecting to server (${nitrado_cred.ServerID}): ${error}`);
if (retries == maxRetries) throw new Error(`GetRemoteDir: Error connecting to server (${client.config.Nitrado.ServerID}) after ${maxRetries} retries`) if (retries == maxRetries) {
client.error(`GetRemoteDir: Error connecting to server (${nitrado_cred.ServerID}) after ${maxRetries} retries`);
return 1;
}
} }
await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying
} }
@@ -88,12 +93,12 @@ const GetRemoteDir = async (client, dir="") => {
module.exports = { module.exports = {
DownloadNitradoFile: async(client, filename, outputDir) => { DownloadNitradoFile: async(nitrado_cred, client, filename, outputDir) => {
for (let retries = 0; retries <= maxRetries; retries++) { for (let retries = 0; retries <= maxRetries; retries++) {
try { try {
const res = await fetch(`https://api.nitrado.net/services/${client.config.Nitrado.ServerID}/gameservers/file_server/download?file=${filename}`, { const res = await fetch(`https://api.nitrado.net/services/${nitrado_cred.ServerID}/gameservers/file_server/download?file=${filename}`, {
headers: { headers: {
"Authorization": client.config.Nitrado.Auth "Authorization": nitrado_cred.Auth
} }
}).then(response => }).then(response =>
response.json().then(data => data) response.json().then(data => data)
@@ -101,16 +106,18 @@ module.exports = {
const stream = fs.createWriteStream(outputDir); const stream = fs.createWriteStream(outputDir);
if (!res.data || !res.data.token) { if (!res.data || !res.data.token) {
const errorText = await res.text(); client.error(`Error downloading File "${filename}": message: ${res.message}: DownloadNitradoFile`);
client.error(`Error downloading File "${filename}": message: ${errorText}: DownloadNitradoFile`);
return 1; return 1;
} }
const { body } = await fetch(res.data.token.url); const { body } = await fetch(res.data.token.url);
await finished(Readable.fromWeb(body).pipe(stream)); await finished(Readable.fromWeb(body).pipe(stream));
return 0; return 0;
} catch (error) { } catch (error) {
client.error(`DownloadNitradoFile: Error connecting to server (${client.config.Nitrado.ServerID}): ${error.message}`); client.error(`DownloadNitradoFile: Error connecting to server (${nitrado_cred.ServerID}): ${error.message}`);
if (retries === maxRetries) throw new Error(`DownloadNitradoFile: Error connecting to server (${client.config.Nitrado.ServerID}) after ${maxRetries} retries`); if (retries === maxRetries) {
client.error(`DownloadNitradoFile: Error connecting to server (${nitrado_cred.ServerID}) after ${maxRetries} retries`);
return 1;
}
} }
await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying
} }
@@ -122,67 +129,70 @@ module.exports = {
rather than write two whole different functions for each. rather than write two whole different functions for each.
*/ */
BanPlayer: async (client, gamertag) => await HandlePlayerBan(client, gamertag, true), BanPlayer: async (nitrado_cred, client, gamertag) => await HandlePlayerBan(nitrado_cred, client, gamertag, true),
UnbanPlayer: async (client, gamertag) => await HandlePlayerBan(client, gamertag, false), UnbanPlayer: async (nitrado_cred, client, gamertag) => await HandlePlayerBan(nitrado_cred, client, gamertag, false),
RestartServer: async (client, restart_message, message) => { RestartServer: async (nitrado_cred, client, restart_message, message) => {
const params = { const params = {
restart_message: restart_message, restart_message: restart_message,
message: message message: message
}; };
// client.log('Restarting server...');
for (let retries = 0; retries < maxRetries; retries++) { for (let retries = 0; retries < maxRetries; retries++) {
try { try {
const res = await fetch(`https://api.nitrado.net/services/${client.config.Nitrado.ServerID}/gameservers/restart`, { const res = await fetch(`https://api.nitrado.net/services/${nitrado_cred.ServerID}/gameservers/restart`, {
method: "POST", method: "POST",
headers: { headers: {
"Authorization": client.config.Nitrado.Auth, "Authorization": nitrado_cred.Auth,
}, },
body: JSON.stringify(params) body: JSON.stringify(params)
}); });
if (!res.ok) { if (!res.ok) {
const errorText = await res.text(); client.error(`Failed to restart Nitrado server (${nitrado_cred.ServerID}): status: ${res.status}, message: ${res.statusText}: RestartServer`);
client.error(`Failed to restart Nitrado server (${client.config.Nitrado.ServerID}): status: ${res.status}, message: ${errorText}: RestartServer`);
return 1; // Return error status on failed status code. return 1; // Return error status on failed status code.
} else { } else {
return 0; return 0;
} }
} catch (error) { } catch (error) {
client.error(`RestartServer: Error connecting to server (${client.config.Nitrado.ServerID}): ${error.message}`); client.error(`RestartServer: Error connecting to server (${nitrado_cred.ServerID}): ${error.message}`);
if (retries === maxRetries) throw new Error(`RestartServer: Error connecting to server (${client.config.Nitrado.ServerID}) after ${maxRetries} retries`); if (retries === maxRetries) {
client.error(`RestartServer: Error connecting to server (${nitrado_cred.ServerID}) after ${maxRetries} retries`);
return 1;
}
} }
await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying
} }
}, },
FetchServerSettings: async (client, fetcher) => { FetchServerSettings: async (nitrado_cred, client, fetcher) => {
for (let retries = 0; retries <= maxRetries; retries++) { for (let retries = 0; retries <= maxRetries; retries++) {
try { try {
// get current status // get current status
const res = await fetch(`https://api.nitrado.net/services/${client.config.Nitrado.ServerID}/gameservers`, { const res = await fetch(`https://api.nitrado.net/services/${nitrado_cred.ServerID}/gameservers`, {
headers: { headers: {
"Authorization": client.config.Nitrado.Auth "Authorization": nitrado_cred.Auth
} }
}); });
if (!res.ok) { if (!res.ok) {
const errorText = await res.text(); client.error(`Failed to get Nitrado server stats (${nitrado_cred.ServerID}): status: ${res.status}, message: ${res.statusText}: ${fetcher} via FetchServerSettings`);
client.error(`Failed to get Nitrado server stats (${client.config.Nitrado.ServerID}): status: ${res.status}, message: ${errorText}: ${fetcher} via FetchServerSettings`);
if (retries === 2) return 1; // Return error status on the second failed status code. if (retries === 2) return 1; // Return error status on the second failed status code.
} else { } else {
const data = await res.json(); const data = await res.json();
return data; return data;
} }
} catch (error) { } catch (error) {
client.error(`${fetcher} via FetchServerSettings: Error connecting to server (${client.config.Nitrado.ServerID}): ${error.message}`); client.error(`${fetcher} via FetchServerSettings: Error connecting to server (${nitrado_cred.ServerID}): ${error.message}`);
if (retries === maxRetries) throw new Error(`${fetcher} via FetchServerSettings: Error connecting to server (${client.config.Nitrado.ServerID}) after ${maxRetries} retries`); if (retries === maxRetries) {
client.error(`${fetcher} via FetchServerSettings: Error connecting to server (${nitrado_cred.ServerID}) after ${maxRetries} retries`);
return 1;
}
} }
await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying
} }
}, },
PostServerSettings: async (client, category, key, value) => { PostServerSettings: async (nitrado_cred, client, category, key, value) => {
for (let retries = 0; retries <= maxRetries; retries++) { for (let retries = 0; retries <= maxRetries; retries++) {
try { try {
const formData = new FormData(); const formData = new FormData();
@@ -191,18 +201,17 @@ module.exports = {
formData.append("value", value); formData.append("value", value);
formData.pipe(concat(data => { formData.pipe(concat(data => {
async function postData() { async function postData() {
const res = await fetch(`https://api.nitrado.net/services/${client.config.Nitrado.ServerID}/gameservers/settings`, { const res = await fetch(`https://api.nitrado.net/services/${nitrado_cred.ServerID}/gameservers/settings`, {
method: "POST", method: "POST",
credentials: 'include', credentials: 'include',
headers: { headers: {
...formData.getHeaders(), ...formData.getHeaders(),
"Authorization": client.config.Nitrado.Auth "Authorization": nitrado_cred.Auth
}, },
body: data, body: data,
}); });
if (!res.ok) { if (!res.ok) {
const errorText = await res.text(); client.error(`Failed to get post Nitrado server settings (${nitrado_cred.ServerID}): status: ${res.status}, message: ${res.statusText}: PostServerSettings`);
client.error(`Failed to get post Nitrado server settings (${client.config.Nitrado.ServerID}): status: ${res.status}, message: ${errorText}: PostServerSettings`);
if (retries === 2) return 1; // Return error status on the second failed status code. if (retries === 2) return 1; // Return error status on the second failed status code.
} else { } else {
const data = await res.json(); const data = await res.json();
@@ -213,39 +222,46 @@ module.exports = {
})); }));
return 0; return 0;
} catch (error) { } catch (error) {
client.error(`PostServerSettings: Error connecting to server (${client.config.Nitrado.ServerID}): ${error.message}`); client.error(`PostServerSettings: Error connecting to server (${nitrado_cred.ServerID}): ${error.message}`);
if (retries === maxRetries) throw new Error(`PostServerSettings: Error connecting to server (${client.config.Nitrado.ServerID}) after ${maxRetries} retries`); if (retries === maxRetries) {
client.error(`PostServerSettings: Error connecting to server (${nitrado_cred.ServerID}) after ${maxRetries} retries`);
return 1;
}
} }
await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying await new Promise(resolve => setTimeout(resolve, retryDelay)); // Delay before retrying
} }
}, },
CheckServerStatus: async (client) => { CheckServerStatus: async (nitrado_cred, client) => {
const data = await module.exports.FetchServerSettings(client, 'CheckServerStatus'); // Fetch server status const data = await module.exports.FetchServerSettings(nitrado_cred, client, 'CheckServerStatus'); // Fetch server status
if (data && data != 1) { if (data && data != 1) {
if (data && data.data.gameserver.status === 'stopped') { if (data && data.data.gameserver.status === 'stopped') {
client.log(`Restart of Nitrado server ${client.config.Nitrado.ServerID} has been invoked by the bot, the periodic check showed status of "${data.data.gameserver.status}".`); client.log(`Restart of Nitrado server ${nitrado_cred.ServerID} has been invoked by the bot, the periodic check showed status of "${data.data.gameserver.status}".`);
// Write optional "restart_message" to set in the Nitrado server logs and send a notice "message" to your server community. // Write optional "restart_message" to set in the Nitrado server logs and send a notice "message" to your server community.
restart_message = 'Server being restarted by periodic bot check.'; restart_message = 'Server being restarted by periodic bot check.';
message = 'The server was restarted by periodic bot check!'; message = 'The server was restarted by periodic bot check!';
module.exports.RestartServer(client, restart_message, message); module.exports.RestartServer(nitrado_cred, client, restart_message, message);
} }
} }
}, },
DisableBaseDamage: async (client, preference) => { DisableBaseDamage: async (nitrado_cred, client, preference) => {
const pref = preference ? '1' : '0'; const pref = preference ? '1' : '0';
const posted = await module.exports.PostServerSettings(client, "config", "disableBaseDamage", pref); const posted = await module.exports.PostServerSettings(nitrado_cred, client, "config", "disableBaseDamage", pref);
if (posted == 1) return 1; if (posted == 1) return 1;
const basePath = await GetRemoteDir(client).then(dirs => dirs.filter(dir => dir.type == 'dir')[0].path) const remoteDirs = await GetRemoteDir(nitrado_cred, client);
const missionPath = await GetRemoteDir(client, basePath).then(dirs => dirs[0].path) if (remoteDirs == 1) return 1;
const basePath = remoteDirs.filter(dir => dir.type == 'dir')[0].path
const remoteDirsFromBase = await GetRemoteDir(nitrado_cred, client, basePath);
if (remoteDirsFromBase == 1) return 1;
const missionPath = remoteDirsFromBase[0].path;
const cfggameplayPath = `${missionPath}/cfggameplay.json`; const cfggameplayPath = `${missionPath}/cfggameplay.json`;
const jsonDir = `./logs/cfggameplay.json`; const jsonDir = `./logs/cfggameplay.json`;
await module.exports.DownloadNitradoFile(client, cfggameplayPath, jsonDir); await module.exports.DownloadNitradoFile(nitrado_cred, client, cfggameplayPath, jsonDir);
let gameplay = JSON.parse(fs.readFileSync(jsonDir)); let gameplay = JSON.parse(fs.readFileSync(jsonDir));
gameplay.GeneralData.disableBaseDamage = preference; gameplay.GeneralData.disableBaseDamage = preference;
@@ -253,23 +269,27 @@ module.exports = {
// write JSON to file // write JSON to file
fs.writeFileSync(jsonDir, JSON.stringify(gameplay, null, 2)); fs.writeFileSync(jsonDir, JSON.stringify(gameplay, null, 2));
const uploaded = await UploadNitradoFile(client, missionPath, 'cfggameplay.json', jsonDir); const uploaded = await UploadNitradoFile(nitrado_cred, client, missionPath, 'cfggameplay.json', jsonDir);
if (uploaded == 1) return 1; if (uploaded == 1) return 1;
return 0; return 0;
}, },
DisableContainerDamage: async (client, preference) => { DisableContainerDamage: async (nitrado_cred, client, preference) => {
const pref = preference ? '1' : '0'; const pref = preference ? '1' : '0';
const posted = await module.exports.PostServerSettings(client, "config", "disableContainerDamage", pref); const posted = await module.exports.PostServerSettings(nitrado_cred, client, "config", "disableContainerDamage", pref);
if (posted == 1) return 1; if (posted == 1) return 1;
const basePath = await GetRemoteDir(client).then(dirs => dirs.filter(dir => dir.type == 'dir')[0].path) const remoteDirs = await GetRemoteDir(nitrado_cred, client);
const missionPath = await GetRemoteDir(client, basePath).then(dirs => dirs[0].path) if (remoteDirs == 1) return 1;
const basePath = remoteDirs.filter(dir => dir.type == 'dir')[0].path
const remoteDirsFromBase = await GetRemoteDir(nitrado_cred, client, basePath);
if (remoteDirsFromBase == 1) return 1;
const missionPath = remoteDirsFromBase[0].path;
const cfggameplayPath = `${missionPath}/cfggameplay.json`; const cfggameplayPath = `${missionPath}/cfggameplay.json`;
const jsonDir = `./logs/cfggameplay.json`; const jsonDir = `./logs/cfggameplay.json`;
await module.exports.DownloadNitradoFile(client, cfggameplayPath, jsonDir); await module.exports.DownloadNitradoFile(nitrado_cred, client, cfggameplayPath, jsonDir);
let gameplay = JSON.parse(fs.readFileSync(jsonDir)); let gameplay = JSON.parse(fs.readFileSync(jsonDir));
gameplay.GeneralData.disableContainerDamage = preference; gameplay.GeneralData.disableContainerDamage = preference;
@@ -277,7 +297,7 @@ module.exports = {
// write JSON to file // write JSON to file
fs.writeFileSync(jsonDir, JSON.stringify(gameplay, null, 2)); fs.writeFileSync(jsonDir, JSON.stringify(gameplay, null, 2));
const uploaded = await UploadNitradoFile(client, missionPath, 'cfggameplay.json', jsonDir); const uploaded = await UploadNitradoFile(nitrado_cred, client, missionPath, 'cfggameplay.json', jsonDir);
if (uploaded == 1) return 1; if (uploaded == 1) return 1;
return 0; return 0;