const { Client, GatewayIntentBits, Partials, Routes, PermissionFlagsBits, PermissionsBitField, ChannelFlags, ChannelManager, ChannelFlagsBitField, ChannelType, Colors } = require('discord.js') require('dotenv').config() const { REST } = require('@discordjs/rest') const { SlashCommandBuilder } = require('@discordjs/builders') const { initReactionPerRole, messageReactionAdd, messageReactionRemove, } = require('../features/reactionPerRole') const { handleBlacklistAdd, handleBlacklistCheck, handleBlacklistShow, updateGlobalMessage, } = require('../features/blacklist') const { handleBirthdayAdd, handleBirthdayUpdate, handleBirthdayCheck, handleBirthdayDelete, } = require('../features/birthday') const { handleStaticAdd, handleStaticGet, handleStaticDelete, handleStaticUpdateName, handleStaticUpdateUser, handleStaticUpdateUsers, handleStaticUpdateSize, } = require('../features/static') const { startBirthdayCheckCron } = require('../tasks/checkBirthday') const { startEventCheckCron } = require('../tasks/eventReminder') const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN) const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMembers, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessageReactions, ], partials: [ Partials.Channel, ], }) client.on('interactionCreate', async interaction => { if (!interaction.isCommand()) return switch (interaction.commandName) { case 'blacklist': { const reportedUser = interaction.options.getString('player') const reason = interaction.options.getString('reason') const reportedByUser = interaction.user.username const res = await handleBlacklistAdd(reportedUser, reason, reportedByUser) if (res) { if (res.name === reportedUser) interaction.reply({ content: 'This user has already been reported', ephemeral: true }) else { interaction.reply({ content: `Player ** ${reportedUser}** had been successfully reported for ${reason}`, ephemeral: true }) updateGlobalMessage(client) } } else interaction.reply({ content: 'ERROR trying to add the player to the blacklist, please contact @Crylia', ephemeral: true }) break } case 'blacklist-check-player': { const player = interaction.options.getString('player') const reason2 = await handleBlacklistCheck(player) reason2 ? await interaction.reply({ content: `** ${reason2.name}** is blacklisted for: ** ${reason2.reason || 'No reason provided.'}**`, ephemeral: true }) : await interaction.reply({ content: `** ${player}** is not blacklisted.`, ephemeral: true }) break } case 'birthday': { const user = interaction.user.username const birthday = interaction.options.getString('birthday') // Matches format xx.xx.xxxx, later dd.mm.yyyy const match = birthday.match(/^(\d{2})\.(\d{2})\.(\d{4})$/) if (!match) { await interaction.reply({ content: 'Invalid date format. Please use dd.mm.yyyy.', ephemeral: true }) return } const day = parseInt(match[1], 10) const month = parseInt(match[2], 10) const year = parseInt(match[3], 10) // Validates dd.mm ae legit, year doesnt matter for the birthday const isValidDate = (day, month, year) => { if (month < 1 || month > 12) return false const daysInMonth = [31, ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] return day > 0 && day <= daysInMonth[month - 1] } if (!isValidDate(day, month, year)) { await interaction.reply({ content: 'Invalid date. Please enter a valid birthday as dd.mm.yyyy.', ephemeral: true }) return } await handleBirthdayCheck(user, birthday).length > 0 ? await handleBirthdayUpdate(user, birthday) : handleBirthdayAdd(user, birthday) ? await interaction.reply({ content: `Set ${birthday} as your birthday.Everyone will be notified once the day arrives!`, ephemeral: true }) : await interaction.reply({ content: 'Something went wrong when setting / updating your birthday, please contact @crylia', ephemeral: true }) break } case 'birthday-check': { const birthdayCheck = await handleBirthdayCheck(interaction.user.username) birthdayCheck ? await interaction.reply({ content: `Your birthday is currently set to ${new Date(birthdayCheck[0].date).toLocaleDateString('de-DE')}.`, ephemeral: true }) : await interaction.reply({ content: 'You don\'t have a birthday set. Use the`birthday` command to set one.', ephemeral: true }) break } case 'birthday-delete': { await handleBirthdayDelete(interaction.user.username) ? await interaction.reply({ content: 'Your birthday has been deleted.', ephemeral: true }) : await interaction.reply({ content: 'You don\'t have a birthday set.', ephemeral: true }) break } case 'static-create': { const static_name = interaction.options.getString('name') const static_size = interaction.options.getString('size') const member_string = interaction.options.getString('members') let static_members = [interaction.user] try { const static_role = await interaction.guild.roles.create({ name: static_name, color: Colors.Blue, }) interaction.member.roles.add(static_role) if (member_string) { for (const username of member_string.split(',').map(name => name.trim())) { const member = (await interaction.guild.members.fetch()).find(member => member.user.username === username.toLowerCase()) if (member) { static_members.push(member.user) member.roles.add(static_role) } else console.log(`WARNING: Creating static: ${static_name} member named ${username} not found`) } } let category = interaction.guild.channels.cache.find(channel => channel.name === 'Statics' && channel.type === ChannelType.GuildCategory) if (!category) { console.log(`ERROR: Creating static, couldn't find category Statics, ABBORTING`) interaction.guild.roles.remove(static_role) } const static_text_channel = await interaction.guild.channels.create({ name: static_name, type: ChannelType.GuildText, parent: category.id, permissionOverwrites: [ { id: interaction.guild.id, deny: [PermissionsBitField.Flags.ViewChannel] }, { id: static_role.id, allow: [PermissionsBitField.Flags.ViewChannel], } ] }) const static_voice_channel = await interaction.guild.channels.create({ name: static_name, type: ChannelType.GuildVoice, parent: category.id, permissionOverwrites: [ { id: interaction.guild.id, deny: [PermissionsBitField.Flags.ViewChannel] }, { id: static_role.id, allow: [PermissionsBitField.Flags.ViewChannel], } ], }) const res = handleStaticAdd(static_name, static_members[0].username, static_members, static_size, static_role.id, static_text_channel.id, static_voice_channel.id) if (res) { interaction.reply({ content: `Static ${static_name} created. Current members are ${static_members}.`, ephemeral: true }) } else interaction.reply({ content: `Error creating static, please contact @Crylia for help`, ephemeral: true }) } catch (error) { console.error('Error creating static or assigning roles:', error) interaction.guild.channels.delete(static_text_channel) interaction.guild.channels.delete(static_voice_channel) interaction.reply({ content: 'An error occurred while creating the static. Please try again or contact an admin.', ephemeral: true, }) } break } case 'static-delete': { break } case 'static-show': { break } default: { break } } }) /* client.on('messageReactionAdd', async (reaction, user) => { messageReactionAdd(user, reaction) }) client.on('messageReactionRemove', async (reaction, user) => { messageReactionRemove(user, reaction) }) */ client.once('ready', async () => { console.log(`Logged in as ${client.user.tag} `) const unauthorizedGuilds = client.guilds.cache.filter(guild => guild.id !== process.env.SERVER_ID) const leavePromises = unauthorizedGuilds.map(async guild => { console.log(`Leaving unauthorized server: ${guild.id}`); await guild.leave(); }) await Promise.all(leavePromises) startBirthdayCheckCron(client) startEventCheckCron(client) updateGlobalMessage(client) //initReactionPerRole(client) }) client.on('guildCreate', guild => { if (guild.id !== process.env.SERVER_ID) { console.log(`Unauthorized server joined: ${guild.name} (${guild.id})`) guild.leave() .then(() => console.log(`Left unauthorized server: ${guild.name}`)) .catch(console.error) } }) client.on('guildDelete', guild => { console.log(`Bot removed from server: ${guild.name} (${guild.id})`) }) const connectDiscord = async () => { try { console.log('Started refreshing application (/) commands.') const commands = [ new SlashCommandBuilder() .setName('birthday') .setDescription('Set yourself a birthday') .addStringOption(option => option.setName('birthday') .setDescription('Set (or overwrite) your birthday as dd.mm.yyyy (e.g. 01.12.1999)') .setRequired(true) ), new SlashCommandBuilder() .setName('birthday-check') .setDescription('Check your set birthday (only you will see the date)'), new SlashCommandBuilder() .setName('birthday-delete') .setDescription('Delete your birthday, nobody will know when your birthday arrives :('), new SlashCommandBuilder() .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) .setName('blacklist') .setDescription('Add a player to a blacklist with a reason') .addStringOption(option => option.setName('player') .setDescription('The in-game name of the player') .setRequired(true) ) .addStringOption(option => option.setName('reason') .setDescription('Explain what happened, why should this player be blacklisted') .setRequired(true) ), new SlashCommandBuilder() .setName('blacklist-check-player') .setDescription('Check if a player is blacklisted') .addStringOption(option => option.setName('player') .setDescription('The in-game name of the player') .setRequired(true) ), /* new SlashCommandBuilder() .setName('static-create') .setDescription('Create a new static with a voice and text channel just for your members.') .addStringOption(option => option.setName('name') .setDescription('Name of the static, the voice and test channel will be named after this.') .setRequired(true) ) .addStringOption(option => option.setName('size') .setDescription('Number of members in the static.') .setRequired(false) ) .addStringOption(option => option.setName('members') .setDescription('Optionally assign members here by a comma seperated list (user1,user2,user3...).') .setRequired(false) ) */ ].map(command => command.toJSON()) await rest.put( Routes.applicationCommands(process.env.CLIENT_ID), { body: commands } ) console.log('Successfully reloaded application (/) commands.') } catch (error) { console.error(error) } try { client.login(process.env.BOT_TOKEN) } catch (error) { console.error('Error logging in to Discord:', error) } } module.exports = { client, connectDiscord }