const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder } = require('discord.js'); const CommandOptions = require('../util/CommandOptionTypes').CommandOptionTypes; const bitfieldCalculator = require('discord-bitfield-calculator'); const generateAlarmMenus = (alarms, customId, placeholder, description) => { let alarmComponents = []; const max = 25; let id = 1; for (let i = 0; i < alarms.length; i += max) { let currentAlarmComponents = new StringSelectMenuBuilder() .setCustomId(`${customId}-${id}`) .setPlaceholder(placeholder); alarms.slice(i, i + max).forEach(alarm => { currentAlarmComponents.addOptions({ label: alarm.name, description: description, value: alarm.name, }); }); alarmComponents.push(new ActionRowBuilder().addComponents(currentAlarmComponents)); id++; } return alarmComponents; }; module.exports = { name: "alarm", debug: false, global: false, description: "Manage an Alarm", usage: "[command] [options]", permissions: { channel: ["VIEW_CHANNEL", "SEND_MESSAGES", "EMBED_LINKS"], member: ["MANAGE_GUILD"], }, options: [ { name: "create", description: "Create a new Zone Ping Alarm", value: "create", type: CommandOptions.SubCommand, options: [ { name: "x-coord", description: "X Coordinate of the origin", value: "x-coord", type: CommandOptions.Float, min_value: 0.01, required: true, }, { name: "y-coord", description: "Y Coordinate of the origin", value: "y-coord", type: CommandOptions.Float, min_value: 0.01, required: true, }, { name: "radius", description: "Radius of Alarm", value: "radius", type: CommandOptions.Float, min_value: 25.00, required: true, }, { name: "name", description: "Alarm Name", value: "name", type: CommandOptions.String, required: true, }, { name: "channel", description: "Alarm Channel", value: "channel", type: CommandOptions.Channel, channel_types: [0], // Restrict to text channel required: true, }, { name: "role", description: "Role to Ping on Alarm", value: "role", type: CommandOptions.Role, required: true, }, { name: "emp-exempt", description: "Is this Alarm Exempt to EMP Attacks?", value: false, type: CommandOptions.Boolean, required: false, }, { name: "show-player-coords", description: "Show a players coords when in the radius of the Alarm?", value: true, type: CommandOptions.Boolean, required: false, } ] }, { name: "delete", description: "Delete an Alarm", value: "delete", type: CommandOptions.SubCommand, }, { name: "add-player", description: "Add player to be ignored list of an Alarm", value: "add-player", type: CommandOptions.SubCommand, options: [{ name: "gamertag", description: "Gamertag of player to ignore", value: "gamertag", type: CommandOptions.String, required: true, }] }, { name: "remove-player", description: "Remove a player from the ignored list of an Alarm", value: "remove-player", type: CommandOptions.SubCommand, options: [{ name: "gamertag", description: "Gamertag of player to ignore", value: "gamertag", type: CommandOptions.String, required: true, }] }, { name: "disable", description: "Disable an Alarm", value: "disable", type: CommandOptions.SubCommand, }, { name: "enable", description: "Enable an Alarm", value: "enable", type: CommandOptions.SubCommand, }, { name: "mute", description: "Mute the role ping of an Alarm", value: "mute", type: CommandOptions.SubCommand, options: [{ name: "toggle", description: "Turn on/off role pings for this alarm", value: false, type: CommandOptions.Boolean, required: true, }] }, { name: "set-rule", description: "Add a Rule to an Alarm", value: "set-rule", type: CommandOptions.SubCommand, options: [{ name: "rule", description: "Select a rule to add to an Alarm", value: "rule", type: CommandOptions.String, required: true, choices: [ { name: 'Ban on Entry', value: 'ban_on_entry' }, { name: 'Ban on Kill', value: 'ban_on_kill' }, { name: 'Ban on Fireplace Placement', value: 'ban_on_fireplace_placement' }, ] }] }, { name: "remove-rule", description: "Remove a rule from an Alarm", value: "remove-rule", type: CommandOptions.SubCommand, }, { name: "rename", description: "Rename an Alarm", value: "rename", type: CommandOptions.SubCommand, options: [{ name: "name", description: "New Alarm Name", value: "name", type: CommandOptions.String, required: true, }] }, { name: "move-origin", description: "Move the origin of an Alarm", value: "move-origin", type: CommandOptions.SubCommand, options: [{ name: "x-coord", description: "X Coordinate of the new origin", value: "x-coord", type: CommandOptions.Float, min_value: 0.01, required: true, }, { name: "y-coord", description: "Y Coordinate of the new origin", value: "y-coord", type: CommandOptions.Float, min_value: 0.01, required: true, }] } ], SlashCommand: { /** * * @param {require("../structures/DayzRBot")} client * @param {import("discord.js").Message} message * @param {string[]} args * @param {*} param3 */ run: async (client, interaction, args, { GuildDB }) => { const permissions = bitfieldCalculator.permissions(interaction.member.permissions); let canUseCommand = false; if (permissions.includes("MANAGE_GUILD")) 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 (!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].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.')] }) let exists = GuildDB.alarms.find(alarm => alarm.name == args[0].options[3].value); if (exists) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Yellow).setDescription('**Invalid Name**\nAn alarm already exists with this name.')]}); let alarm = { origin: [args[0].options[0].value, args[0].options[1].value], radius: args[0].options[2].value, name: args[0].options[3].value, channel: args[0].options[4].value, role: args[0].options[5].value, ignoredPlayers: [], rules: [], empExempt: client.exists(args[0].options[6]) ? args[0].options[6].value : false, showPlayerCoord: client.exists(args[0].options[7]) ? args[0].options[7].value : true, disabled: false, empExpire: null, }; client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $push: { 'server.alarms': alarm, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:** Successfully set **${alarm.name}** in <#${alarm.channel}>`); return interaction.send({ embeds: [successEmbed] }); } else if (args[0].name == 'delete') { if (GuildDB.alarms.length == 0) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Default).setDescription('**Notice:** No Existing Alarms to Delete.')] }); const alarmComponents = generateAlarmMenus( GuildDB.alarms, `DeleteAlarmSelect`, `Select an Alarm to delete.`, `Delete this alarm` ); return interaction.send({ components: alarmComponents, flags: (1 << 6) }); } else if (args[0].name == 'add-player' || args[0].name == 'remove-player') { const add = args[0].name == 'add-player'; if (GuildDB.alarms.length == 0) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Default).setDescription(`**Notice:** No Existing Alarms to ${add?'Add':'Remove'} Player ${add?'to':'from'}.`)] }); const alarmComponents = generateAlarmMenus( GuildDB.alarms, `ManageAlarmIgnored-${add?'add':'remove'}-${args[0].options[0].value}`, `Select an Alarm to ${add?'add':'remove'} player ${add?'to':'from'}.`, `${add?'Add':'Remove'} player ${add?'to':'from'} this Alarm` ); return interaction.send({ components: alarmComponents, flags: (1 << 6) }); } else if (args[0].name == 'set-rule' || args[0].name == 'remove-rule') { if (GuildDB.alarms.length == 0) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Default).setDescription('**Notice:** No Existing Alarms to configure.')] }); const alarmComponents = generateAlarmMenus( GuildDB.alarms, `ManageRule-${args[0].name=='set-rule'?'add':'remove'}${args[0].name=='set-rule'?`-${args[0].options[0].value}`:''}`, `Select an Alarm to configure.`, `Configure this alarm` ); return interaction.send({ components: alarmComponents, flags: (1 << 6) }); } else if (args[0].name == 'enable' || args[0].name == 'disable') { const disable = args[0].name == 'disable'; const message = disable ? 'disable' : 'enable'; if (GuildDB.alarms.length == 0) return interaction.send({ embeds: [ new EmbedBuilder() .setColor(client.config.Colors.Default) .setDescription(`**Notice:**\n> No Existing Alarms to ${message}.`) ] }); if (!GuildDB.alarms.some(alarm => alarm.disabled != disable)) return interaction.send({ embeds: [ new EmbedBuilder() .setColor(client.config.Colors.Default) .setDescription(`**Notice:**\n> There are no alarms to ${message}.`) ] }); const alarmComponents = generateAlarmMenus( GuildDB.alarms, `EnableOrDisableAlarm-${message}`, `Select an Alarm to ${message}`, `Configure this alarm` ); return interaction.send({ components: alarmComponents, flags: (1 << 6) }); } else if (args[0].name == 'rename') { if (GuildDB.alarms.length == 0) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Default).setDescription('**Notice:**\n> No Existing Alarms to configure.')] }); const alarmComponents = generateAlarmMenus( GuildDB.alarms, `RenameAlarm-${args[0].options[0].value}`, `Select an Alarm to rename.`, `Rename this alarm` ); return interaction.send({ components: alarmComponents, flags: (1 << 6) }); } else if (args[0].name == 'move-origin') { if (GuildDB.alarms.length == 0) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Default).setDescription('**Notice:** No Existing Alarms to configure.')] }); const alarmComponents = generateAlarmMenus( GuildDB.alarms, `MoveOrigin-${args[0].options[0].value}-${args[0].options[1].value}`, `Select an Alarm to move.`, `Move this alarm` ); return interaction.send({ components: alarmComponents, flags: (1 << 6) }); } else if (args[0].name == 'mute') { if (GuildDB.alarms.length == 0) return interaction.send({ embeds: [new EmbedBuilder().setColor(client.config.Colors.Default).setDescription('**Notice:** No Existing Alarms to configure.')] }); const alarmComponents = generateAlarmMenus( GuildDB.alarms, `MuteAlarm-${args[0].options[0].value ? 1 : 0}`, `Select an Alarm to mute.`, `Mute this alarm` ); return interaction.send({ components: alarmComponents, flags: (1 << 6) }); } }, }, Interactions: { DeleteAlarmSelect: { run: async(client, interaction, GuildDB) => { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.values[0]); const prompt = new EmbedBuilder() .setTitle(`Are you sure you want to delete this Zone Alarm?`) .setColor(client.config.Colors.Default) const opt = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId(`DeleteAlarm-yes-${alarm.name}`) .setLabel("Yes") .setStyle(ButtonStyle.Danger), new ButtonBuilder() .setCustomId(`DeleteAlarm-no-${alarm.name}`) .setLabel("No") .setStyle(ButtonStyle.Success) ) return interaction.update({ embeds: [prompt], components: [opt], flags: (1 << 6) }); } }, DeleteAlarm: { run: async(client, interaction, GuildDB) => { if (interaction.customId.split('-')[1] == 'yes') { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.customId.split('-')[2]); client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $pull: { 'server.alarms': alarm, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:** Successfully Deleted **${interaction.customId.split('-')[2]}**`); return interaction.update({ embeds: [successEmbed], components: [] }); } else { let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`The Zone Alarm **${interaction.customId.split('-')[2]}** will not be deleted.`); return interaction.update({ embeds: [successEmbed], components: []}); } } }, ManageAlarmIgnored: { run: async(client, interaction, GuildDB) => { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.values[0]); let alarmIndex = GuildDB.alarms.indexOf(alarm); let playerStat = await client.dbo.collection("players").findOne({"gamertag": interaction.customId.split('-')[2]}); if (!client.exists(playerStat)) return interaction.update({ 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.')], components: [] }); let add = interaction.customId.split('-')[1] == 'add'; if (add) alarm.ignoredPlayers.push(playerStat.playerID); else alarm.ignoredPlayers = alarm.ignoredPlayers.filter((v) => { return v != playerStat.playerID; }); GuildDB.alarms[alarmIndex] = alarm; client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $set: { 'server.alarms': GuildDB.alarms, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:** Successfully ${add?'Added':'Removed'} **${interaction.customId.split('-')[2]}** ${add?'to':'from'} **${alarm.name}**`); return interaction.update({ embeds: [successEmbed], components: [] }); } }, ManageRule: { run: async(client, interaction, GuildDB) => { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.values[0]); let alarmIndex = GuildDB.alarms.indexOf(alarm); if (interaction.customId.split('-')[1] == 'add') { alarm.rules.push(interaction.customId.split('-')[2]); GuildDB.alarms[alarmIndex] = alarm; client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $set: { 'server.alarms': GuildDB.alarms, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:** Successfully Added Rule **${interaction.customId.split('-')[2]}** to **${alarm.name}**`); return interaction.update({ embeds: [successEmbed], components: [] }); } else if (interaction.customId.split('-')[1]=='remove') { let alarmRules = new StringSelectMenuBuilder() .setCustomId(`DeleteAlarmRule-${alarm.name}-${interaction.member.user.id}`) .setPlaceholder(`Select Rule to Remove from ${alarm.name}`); for (let i = 0; i < alarm.rules.length; i++) { alarmRules.addOptions({ label: alarm.rules[i], description: `Select this Rule to remove it.`, value: alarm.rules[i] }); } const opt = new ActionRowBuilder().addComponents(alarmRules); return interaction.update({ components: [opt], flags: (1 << 6) }); } } }, DeleteAlarmRule: { run: async(client, interaction, GuildDB) => { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.customId.split('-')[1]); let alarmIndex = GuildDB.alarms.indexOf(alarm); alarm.rules = alarm.rules.filter((v) => { return v != interaction.values[0]; }); GuildDB.alarms[alarmIndex] = alarm; client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $set: { 'server.alarms': GuildDB.alarms, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:** Successfully Removed Rule **${interaction.values[0]}** from **${interaction.customId.split('-')[1]}**`); return interaction.update({ embeds: [successEmbed], components: [] }); } }, EnableOrDisableAlarm: { run: async(client, interaction, GuildDB) => { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.values[0]); let alarmIndex = GuildDB.alarms.indexOf(alarm); let disable = interaction.customId.split('-')[1] == 'disable'; alarm.disabled = disable; GuildDB.alarms[alarmIndex] = alarm client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $set: { 'server.alarms': GuildDB.alarms, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:**\n> Successfully ${disable ? 'disabled' : 'enabled'} the Alarm **${interaction.values[0]}**`); return interaction.update({ embeds: [successEmbed], components: [] }); } }, MoveOrigin: { run: async(client, interaction, GuildDB) => { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.values[0]); let alarmIndex = GuildDB.alarms.indexOf(alarm); let origin = [parseFloat(interaction.customId.split('-')[1]), parseFloat(interaction.customId.split('-')[2])]; alarm.origin = origin; GuildDB.alarms[alarmIndex] = alarm client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $set: { 'server.alarms': GuildDB.alarms, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:** Successfully moved alarm to new **[origin](https://www.izurvive.com/chernarusplussatmap/#location=${origin[0]};${origin[1]})**`); return interaction.update({ embeds: [successEmbed], components: [] }); } }, RenameAlarm: { run: async(client, interaction, GuildDB) => { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.values[0]); let alarmIndex = GuildDB.alarms.indexOf(alarm); let oldName = alarm.name; alarm.name = interaction.customId.split('-')[1]; GuildDB.alarms[alarmIndex] = alarm client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $set: { 'server.alarms': GuildDB.alarms, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:** Successfully renamed the Alarm **${oldName}** to **${alarm.name}**`); return interaction.update({ embeds: [successEmbed], components: [] }); } }, MuteAlarm: { run: async(client, interaction, GuildDB) => { let alarm = GuildDB.alarms.find(alarm => alarm.name == interaction.values[0]); let alarmIndex = GuildDB.alarms.indexOf(alarm); let mute = parseInt(interaction.customId.split('-')[1]); alarm.mute = mute; GuildDB.alarms[alarmIndex] = alarm client.dbo.collection('guilds').updateOne({ 'server.serverID': GuildDB.serverID }, { $set: { 'server.alarms': GuildDB.alarms, } }, (err, res) => { if (err) return client.sendInternalError(interaction, err); }); let successEmbed = new EmbedBuilder() .setColor(client.config.Colors.Green) .setDescription(`**Success:** Successfully ${mute?'Muted':'Unmuted'} this alarm.`); return interaction.update({ embeds: [successEmbed], components: [] }); } } } }