From 8e90be50a05ec265391036b83dd427be40c1285b Mon Sep 17 00:00:00 2001 From: Patric Plattner <patric@patricplattner.de> Date: Tue, 31 Aug 2021 15:57:01 +0200 Subject: [PATCH] Adding per server locale support and adding German locale. --- commands/AddRoleMenu.js | 51 ++++++----- commands/AddRoleToMenu.js | 37 ++++---- commands/AssignPermission.js | 32 ++++--- commands/ChangeLocale.js | 24 +++++ commands/DeleteRoleFromMenu.js | 37 ++++---- commands/DeleteRoleMenu.js | 29 +++--- commands/EditRoleMenu.js | 53 ++++++----- commands/ListPermissions.js | 13 ++- commands/RevokePermission.js | 29 +++--- components/locale.js | 55 ++++++++++++ components/permConfig.js | 50 +++++++++-- components/roles.js | 160 +++++++++++++++++++-------------- index.js | 28 +++--- locales/de.json | 116 ++++++++++++++++++++++++ locales/en.json | 116 ++++++++++++++++++++++++ models/RoleMenu.js | 18 +--- models/ServerConfig.js | 15 +++- registerCommands.js | 33 +++---- util/locale.js | 21 +++++ util/perm.js | 5 +- util/registerCommands.js | 30 +++++++ 21 files changed, 707 insertions(+), 245 deletions(-) create mode 100644 commands/ChangeLocale.js create mode 100644 components/locale.js create mode 100644 locales/de.json create mode 100644 locales/en.json create mode 100644 util/locale.js create mode 100644 util/registerCommands.js diff --git a/commands/AddRoleMenu.js b/commands/AddRoleMenu.js index 3459176..a0e6acb 100644 --- a/commands/AddRoleMenu.js +++ b/commands/AddRoleMenu.js @@ -1,24 +1,29 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const {SlashCommandBuilder} = require('@discordjs/builders'); -module.exports = new SlashCommandBuilder() - .setName('add_role_menu') - .setDescription('Create a new role menu') - .addChannelOption( option => - option.setName('channel') - .setDescription('Channel the menu will be put inside.') - .setRequired(true)) - .addStringOption( option => - option.setName('message') - .setDescription('Message content.') - .setRequired(true)) - .addStringOption( option => - option.setName('type') - .setDescription('NOT YET IMPLEMENTED Sets type of role menu.') - .setRequired(true) - .addChoice('One of N', 'one') - .addChoice('M of N', 'm') - .addChoice('Any of N', 'any')) - .addBooleanOption( option => - option.setName('verification') - .setDescription('NOT YET IMPLEMENTED Sets verification state of role menu.') - .setRequired(true)) \ No newline at end of file +const locale = require('../util/locale'); + +module.exports = (loc) => { + const commandLocale = locale.get(loc).slashCommands.add_role_menu; + return new SlashCommandBuilder() + .setName('add_role_menu') + .setDescription(commandLocale.description) + .addChannelOption(option => + option.setName('channel') + .setDescription(commandLocale.channelDescription) + .setRequired(true)) + .addStringOption(option => + option.setName('message') + .setDescription(commandLocale.messageDescription) + .setRequired(true)) + .addStringOption(option => + option.setName('type') + .setDescription(commandLocale.typeDescription) + .setRequired(true) + .addChoice(commandLocale.typeChoices.oneOfN, 'one') + .addChoice(commandLocale.typeChoices.mOfN, 'm') + .addChoice(commandLocale.typeChoices.anyOfN, 'any')) + .addBooleanOption(option => + option.setName('verification') + .setDescription(commandLocale.verificationDescription) + .setRequired(true)); +} \ No newline at end of file diff --git a/commands/AddRoleToMenu.js b/commands/AddRoleToMenu.js index f43dd27..238b5ca 100644 --- a/commands/AddRoleToMenu.js +++ b/commands/AddRoleToMenu.js @@ -1,17 +1,22 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const {SlashCommandBuilder} = require('@discordjs/builders'); -module.exports = new SlashCommandBuilder() - .setName('add_role_to_menu') - .setDescription('Add role to a menu.') - .addChannelOption( option => - option.setName('channel') - .setDescription('Channel of the menu.') - .setRequired(true)) - .addStringOption( option => - option.setName('message_id') - .setDescription('Message ID of the menu.') - .setRequired(true)) - .addRoleOption(option => - option.setName('role') - .setDescription('Role to add to the menu.') - .setRequired(true)) \ No newline at end of file +const locale = require('../util/locale'); + +module.exports = (loc) => { + const commandLocale = locale.get(loc).slashCommands.add_role_to_menu; + return new SlashCommandBuilder() + .setName('add_role_to_menu') + .setDescription(commandLocale.description) + .addChannelOption(option => + option.setName('channel') + .setDescription(commandLocale.channelDescription) + .setRequired(true)) + .addStringOption(option => + option.setName('message_id') + .setDescription(commandLocale.messageIDDescription) + .setRequired(true)) + .addRoleOption(option => + option.setName('role') + .setDescription(commandLocale.roleDescription) + .setRequired(true)); +} \ No newline at end of file diff --git a/commands/AssignPermission.js b/commands/AssignPermission.js index b2009c4..e591f9a 100644 --- a/commands/AssignPermission.js +++ b/commands/AssignPermission.js @@ -1,14 +1,20 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const {SlashCommandBuilder} = require('@discordjs/builders'); -module.exports = new SlashCommandBuilder() - .setName('assign_permission') - .setDescription('Assign a permission to a role') - .addStringOption(option => - option.setName('permission') - .setDescription('Permission to assign.') - .setRequired(true) - .addChoice("Role Menus", 'role_menu')) - .addRoleOption(option => - option.setName('role') - .setDescription('Role to assign permission to.') - .setRequired(true)) \ No newline at end of file +const locale = require('../util/locale'); + +module.exports = (loc) => { + const commandLocale = locale.get(loc).slashCommands.assign_permission; + return new SlashCommandBuilder() + .setName('assign_permission') + .setDescription(commandLocale.description) + .addStringOption(option => + option.setName('permission') + .setDescription(commandLocale.permissionDescription) + .setRequired(true) + .addChoice(commandLocale.permissionChoices.roleMenus, 'role_menu') + .addChoice(commandLocale.permissionChoices.locale, 'locale')) + .addRoleOption(option => + option.setName('role') + .setDescription(commandLocale.roleDescription) + .setRequired(true)); +} \ No newline at end of file diff --git a/commands/ChangeLocale.js b/commands/ChangeLocale.js new file mode 100644 index 0000000..c93669a --- /dev/null +++ b/commands/ChangeLocale.js @@ -0,0 +1,24 @@ +const {SlashCommandBuilder} = require('@discordjs/builders'); +const fs = require('fs'); +locale = require('../util/locale'); + +module.exports = (loc, pathToLocales) => { + const commandLocale = locale.get(loc).slashCommands.change_locale; + return new SlashCommandBuilder() + .setName('change_locale') + .setDescription(commandLocale.description) + .addStringOption((option) => { + option.setName('locale') + .setDescription(commandLocale.localeDescription) + .setRequired(true) + const localeFiles = fs.readdirSync('./locales').filter(file => file.endsWith('.json')); + for (const localeFile of localeFiles) { + const locale1 = JSON.parse(fs.readFileSync(`./locales/${localeFile}`)); + option.addChoice(locale1.commonName, locale1.locale); + } + return option + }); + +} + + diff --git a/commands/DeleteRoleFromMenu.js b/commands/DeleteRoleFromMenu.js index b0c6004..d927919 100644 --- a/commands/DeleteRoleFromMenu.js +++ b/commands/DeleteRoleFromMenu.js @@ -1,17 +1,22 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const {SlashCommandBuilder} = require('@discordjs/builders'); -module.exports = new SlashCommandBuilder() - .setName('delete_role_from_menu') - .setDescription('Delete role from menu.') - .addChannelOption( option => - option.setName('channel') - .setDescription('Channel of the menu.') - .setRequired(true)) - .addStringOption( option => - option.setName('message_id') - .setDescription('Message ID of the menu.') - .setRequired(true)) - .addRoleOption(option => - option.setName('role') - .setDescription('Role to remove from the menu.') - .setRequired(true)) \ No newline at end of file +const locale = require('../util/locale'); + +module.exports = (loc) => { + const commandLocale = locale.get(loc).slashCommands.delete_role_from_menu; + return new SlashCommandBuilder() + .setName('delete_role_from_menu') + .setDescription(commandLocale.description) + .addChannelOption(option => + option.setName('channel') + .setDescription(commandLocale.channelDescription) + .setRequired(true)) + .addStringOption(option => + option.setName('message_id') + .setDescription(commandLocale.messageIDDescription) + .setRequired(true)) + .addRoleOption(option => + option.setName('role') + .setDescription(commandLocale.roleDescription) + .setRequired(true)); +} \ No newline at end of file diff --git a/commands/DeleteRoleMenu.js b/commands/DeleteRoleMenu.js index 779a25f..a09c786 100644 --- a/commands/DeleteRoleMenu.js +++ b/commands/DeleteRoleMenu.js @@ -1,13 +1,18 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const {SlashCommandBuilder} = require('@discordjs/builders'); -module.exports = new SlashCommandBuilder() - .setName('delete_role_menu') - .setDescription('Delete a role menu') - .addChannelOption( option => - option.setName('channel') - .setDescription('Select channel of to be deleted menu') - .setRequired(true)) - .addStringOption( option => - option.setName('message_id') - .setDescription('Message ID of to be deleted menu') - .setRequired(true)) \ No newline at end of file +const locale = require('../util/locale'); + +module.exports = (loc) => { + const commandLocale = locale.get(loc).slashCommands.delete_role_menu; + return new SlashCommandBuilder() + .setName('delete_role_menu') + .setDescription(commandLocale.description) + .addChannelOption(option => + option.setName('channel') + .setDescription(commandLocale.channelDescription) + .setRequired(true)) + .addStringOption(option => + option.setName('message_id') + .setDescription(commandLocale.messageIDDescription) + .setRequired(true)); +} \ No newline at end of file diff --git a/commands/EditRoleMenu.js b/commands/EditRoleMenu.js index 4bc2093..19f766d 100644 --- a/commands/EditRoleMenu.js +++ b/commands/EditRoleMenu.js @@ -1,25 +1,30 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const {SlashCommandBuilder} = require('@discordjs/builders'); -module.exports = new SlashCommandBuilder() - .setName('edit_role_menu') - .setDescription('Edit an existing role menu.') - .addChannelOption( option => - option.setName('channel') - .setDescription('Channel of the menu.') - .setRequired(true)) - .addStringOption( option => - option.setName('message_id') - .setDescription('Message ID of the menu.') - .setRequired(true)) - .addStringOption( option => - option.setName('message') - .setDescription('Message content.')) - .addStringOption( option => - option.setName('type') - .setDescription('NOT YET IMPLEMENTED') - .addChoice('One of N', 'one') - .addChoice('M of N', 'm') - .addChoice('Any of N', 'any')) - .addBooleanOption( option => - option.setName('verification') - .setDescription('NOT YET IMPLEMENTED Sets verification state of role menu.')) \ No newline at end of file +const locale = require('../util/locale'); + +module.exports = (loc) => { + const commandLocale = locale.get(loc).slashCommands.edit_role_menu; + return new SlashCommandBuilder() + .setName('edit_role_menu') + .setDescription(commandLocale.description) + .addChannelOption(option => + option.setName('channel') + .setDescription(commandLocale.channelDescription) + .setRequired(true)) + .addStringOption(option => + option.setName('message_id') + .setDescription(commandLocale.messageIDDescription) + .setRequired(true)) + .addStringOption(option => + option.setName('message') + .setDescription(commandLocale.messageDescription)) + .addStringOption(option => + option.setName('type') + .setDescription(commandLocale.typeDescription) + .addChoice(commandLocale.typeChoices.oneOfN, 'one') + .addChoice(commandLocale.typeChoices.mOfN, 'm') + .addChoice(commandLocale.typeChoices.anyOfN, 'any')) + .addBooleanOption(option => + option.setName('verification') + .setDescription(commandLocale.verificationDescription)); +} \ No newline at end of file diff --git a/commands/ListPermissions.js b/commands/ListPermissions.js index b6d35be..c8f7af8 100644 --- a/commands/ListPermissions.js +++ b/commands/ListPermissions.js @@ -1,5 +1,10 @@ -const { SlashCommandBuilder } = require('@discordjs/builders'); +const {SlashCommandBuilder} = require('@discordjs/builders'); -module.exports = new SlashCommandBuilder() - .setName('list_permissions') - .setDescription('List all Permissions for this Server.'); \ No newline at end of file +const locale = require('../util/locale'); + +module.exports = (loc) => { + const commandLocale = locale.get(loc).slashCommands.list_permissions; + return new SlashCommandBuilder() + .setName('list_permissions') + .setDescription(commandLocale.description); +} \ No newline at end of file diff --git a/commands/RevokePermission.js b/commands/RevokePermission.js index 45b18c4..855bcd9 100644 --- a/commands/RevokePermission.js +++ b/commands/RevokePermission.js @@ -1,14 +1,19 @@ const { SlashCommandBuilder } = require('@discordjs/builders'); -module.exports = new SlashCommandBuilder() - .setName('revoke_permission') - .setDescription('Revoke a permission from a role') - .addStringOption(option => - option.setName('permission') - .setDescription('Permission to revoke.') - .setRequired(true) - .addChoice("Role Menus", 'role_menu')) - .addRoleOption(option => - option.setName('role') - .setDescription('Role to revoke permission from.') - .setRequired(true)) \ No newline at end of file +const locale = require('../util/locale'); + +module.exports = (loc) => { + const commandLocale = locale.get(loc).slashCommands.revoke_permission; + return new SlashCommandBuilder() + .setName('revoke_permission') + .setDescription(commandLocale.description) + .addStringOption(option => + option.setName('permission') + .setDescription(commandLocale.permissionDescription) + .setRequired(true) + .addChoice(commandLocale.permissionChoices.roleMenus, 'role_menu')) + .addRoleOption(option => + option.setName('role') + .setDescription(commandLocale.roleDescription) + .setRequired(true)); +} \ No newline at end of file diff --git a/components/locale.js b/components/locale.js new file mode 100644 index 0000000..b4d8424 --- /dev/null +++ b/components/locale.js @@ -0,0 +1,55 @@ +const ServerConfig = require('../models/ServerConfig'); +const RoleMenu = require('../models/RoleMenu'); +const checkPerm = require("../util/perm"); +const locales = require('../util/locale'); +const registerCommands = require('../util/registerCommands'); + +async function changeLocale(interaction) { + let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.locale; + if (!(await checkPerm(interaction.member, 'locale'))) { + await interaction.reply({content: functionLocale.authDenied, ephemeral: true}) + return; + } + const newLocale = interaction.options.getString('locale'); + if (newLocale !== config.locale) { + if (config == null) { + config = new ServerConfig() + config.guildID = interaction.guild.id.toString(); + config.permissions = { + roleMenu: [], locale: [] + } + } + config.locale = newLocale; + const messageLocale = locales.get(newLocale).misc.messageTemplates.roleDropdown + const menus = await RoleMenu.find({guildID: interaction.guild.id.toString()}); + await Promise.all(menus.map(async (menu) => { + console.log(menu); + const message = await (await interaction.guild.channels.fetch(menu.channelID)).messages.fetch(menu.messageID); + let components = message.components; + components[0].components[0].options[components[0].components[0].options.map((option) => option.value).indexOf('select_reset')].label = messageLocale.selectResetText; + components[0].components[0].placeholder = messageLocale.placeholder; + await message.edit({content: message.content, components}); + return true; + })); + await config.save(); + await interaction.reply({content: locales.get(newLocale).components.locale.localeApplied, ephemeral: true}); + await registerCommands(interaction.guild.id.toString(), './commands'); + } else { + await interaction.reply({content: functionLocale.sameLocale, ephemeral: true}); + } +} + +const slashCommandHandler = async (interaction) => { + if (interaction.commandName === 'change_locale') { + await changeLocale(interaction); + } +} + +module.exports = { + slashCommandHandler +} diff --git a/components/permConfig.js b/components/permConfig.js index 4235839..85c1524 100644 --- a/components/permConfig.js +++ b/components/permConfig.js @@ -1,11 +1,17 @@ const ServerConfig = require('../models/ServerConfig'); const {Permissions} = require('discord.js'); +const locales = require("../util/locale"); async function assignPermission(interaction) { + let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.permConfig; if (interaction.member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) { const role = interaction.options.getRole('role'); const perm = interaction.options.getString('permission'); - let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); if (config == null) { config = new ServerConfig(); config.guildID = interaction.guild.id.toString(); @@ -15,15 +21,30 @@ async function assignPermission(interaction) { if (!config.permissions.roleMenu.includes(role.id.toString())) { config.permissions.roleMenu.push(role.id.toString()); } + } else if (perm === 'locale') { + if (!config.permissions.locale.includes(role.id.toString())) { + config.permissions.locale.push(role.id.toString()); + } } await config.save(); - await interaction.reply({content: `Role ${role.name} has been added to ${perm} permission.`, ephemeral: true}); + await interaction.reply({ + content: functionLocale.replyAssign + .replaceAll("$$ROLE$$", `${role.name}`) + .replaceAll('$$PERM$$', `${perm}`), + ephemeral: true + }); } else { - await interaction.reply({content: 'Only administrators can access this command.', ephemeral: true}); + await interaction.reply({content: functionLocale.accessDenied, ephemeral: true}); } } async function revokePermission(interaction) { + let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.permConfig; if (interaction.member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) { const role = interaction.options.getRole('role'); const perm = interaction.options.getString('permission'); @@ -37,18 +58,30 @@ async function revokePermission(interaction) { if (config.permissions.roleMenu.includes(role.id.toString())) { config.permissions.roleMenu.splice(config.permissions.roleMenu.indexOf(role.id.toString()), 1); } + } else if (perm === 'locale') { + if (config.permissions.locale.includes(role.id.toString())) { + config.permissions.locale.splice(config.permissions.locale.indexOf(role.id.toString()), 1); + } } await config.save(); await interaction.reply({ - content: `Role ${role.name} has been removed from ${perm} permission.`, + content: functionLocale.replyRevoke + .replaceAll("$$ROLE$$", `${role.name}`) + .replaceAll('$$PERM$$', `${perm}`), ephemeral: true }); } else { - await interaction.reply({content: 'Only administrators can access this command.', ephemeral: true}); + await interaction.reply({content: functionLocale.accessDenied, ephemeral: true}); } } async function listPermissions(interaction) { + let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.permConfig; if (interaction.member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) { let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); if (config == null) { @@ -58,7 +91,12 @@ async function listPermissions(interaction) { } const roles = await Promise.all(config.permissions.roleMenu.map(async (roleID) => (await interaction.guild.roles.fetch(roleID)).name)); - await interaction.reply({content: `List of Permissions:\n\nRole Menu: ${roles.join(',')}`, ephemeral: true}); + await interaction.reply({ + content: functionLocale.replyList.replaceAll('$$ROLEMENU$$', `${roles.join(',')}`), + ephemeral: true + }); + } else { + await interaction.reply({content: functionLocale.accessDenied, ephemeral: true}); } } diff --git a/components/roles.js b/components/roles.js index 43985bd..ce1b8fb 100644 --- a/components/roles.js +++ b/components/roles.js @@ -1,11 +1,20 @@ const RoleMenu = require('../models/RoleMenu'); +const ServerConfig = require('../models/ServerConfig'); const {MessageActionRow, MessageSelectMenu} = require("discord.js"); const checkPerm = require('../util/perm'); +const locales = require("../util/locale"); // slashcommand handler async function addRoleMenu(interaction) { + const config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.roleMenu; + const messageLocale = locales.get(locale).misc.messageTemplates.roleDropdown; if (!(await checkPerm(interaction.member, 'role_menu'))) { - await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true}) + await interaction.reply({content: functionLocale.accessDenied, ephemeral: true}); return; } const channel = interaction.options.getChannel('channel'); @@ -16,10 +25,10 @@ async function addRoleMenu(interaction) { .addComponents( new MessageSelectMenu() .setCustomId('selectRole') - .setPlaceholder("Select a role.") + .setPlaceholder(messageLocale.placeholder) .addOptions([ { - label: 'Reset Selection', + label: messageLocale.selectResetText, value: 'select_reset' } ]) @@ -33,42 +42,64 @@ async function addRoleMenu(interaction) { menu.type = type; menu.roles = []; await menu.save(); - await interaction.reply({content: 'Role Menu has been created.', ephemeral: true}); + await interaction.reply({content: functionLocale.roleMenuCreated, ephemeral: true}); } async function deleteRoleMenu(interaction) { + const config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.roleMenu; if (!(await checkPerm(interaction.member, 'role_menu'))) { - await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true}) + await interaction.reply({content: functionLocale.accessDenied, ephemeral: true}) return; } const channel = interaction.options.getChannel('channel'); const messageID = interaction.options.getString('message_id'); let menu = await RoleMenu.findOne({guildID: channel.guild.id, channelID: channel.id, messageID}); if (channel.isText()) { - const message = await channel.messages.fetch(messageID); - if (message != null) { - if (menu != null) { - try { - await RoleMenu.deleteOne({_id: menu._id}); - await message.delete(); - await interaction.reply({content: 'Menu has been deleted.', ephemeral: true}); - } catch (e) { - await interaction.reply({content: 'Deleting the Menu has failed.', ephemeral: true}); + var message = null; + var messageSuccess = true; + try { + message = await channel.messages.fetch(messageID); + } catch (e) { + messageSuccess = false; + } + if (messageSuccess) { + if (message != null) { + if (menu != null) { + try { + await RoleMenu.deleteOne({_id: menu._id}); + await message.delete(); + await interaction.reply({content: functionLocale.roleMenuDeleted, ephemeral: true}); + } catch (e) { + await interaction.reply({content: functionLocale.roleMenuDeletedFailed, ephemeral: true}); + } + } else { + await interaction.reply({content: functionLocale.notMenuMessage, ephemeral: true}); } } else { - await interaction.reply({content: 'Please select a menu message.', ephemeral: true}); + await interaction.reply({content: functionLocale.messageIdNotFound, ephemeral: true}); } } else { - await interaction.reply({content: 'The message ID does not exist in this channel.', ephemeral: true}); + await interaction.reply({content: functionLocale.invalidMessageId, ephemeral: true}) } } else { - await interaction.reply({content: 'Please select a text channel.', ephemeral: true}); + await interaction.reply({content: functionLocale.channelNotText, ephemeral: true}); } } async function addRoleToMenu(interaction) { + const config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.roleMenu; if (!(await checkPerm(interaction.member, 'role_menu'))) { - await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true}) + await interaction.reply({content: functionLocale.accessDenied, ephemeral: true}) return; } const channel = interaction.options.getChannel('channel'); @@ -104,37 +135,38 @@ async function addRoleToMenu(interaction) { await menu.save(); await message.edit({content: message.content, components}); await interaction.reply({ - content: `Role ${role.name} has been added to the message.`, + content: functionLocale.roleAdded.replaceAll("$$ROLE$$", `${role.name}`), ephemeral: true }); } else { await interaction.reply({ - content: `Role ${role.name} is already added to the message.`, + content: functionLocale.roleAlreadyAdded.replaceAll('$$ROLE$$', `${role.name}`), ephemeral: true }); } } else { - await interaction.reply({content: 'Please select a menu message.', ephemeral: true}); + await interaction.reply({content: functionLocale.notMenuMessage, ephemeral: true}); } } else { - await interaction.reply({ - content: 'The message ID does not exist in this channel.', - ephemeral: true - }); + await interaction.reply({content: functionLocale.messageIdNotFound, ephemeral: true}); } } else { - await interaction.reply({ - content: 'The message ID was invalid.', ephemeral: true - }) + await interaction.reply({content: functionLocale.invalidMessageId, ephemeral: true}) } } else { - await interaction.reply({content: 'Please select a text channel.', ephemeral: true}); + await interaction.reply({content: functionLocale.channelNotText, ephemeral: true}); } } async function deleteRoleFromMenu(interaction) { + const config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.roleMenu; if (!(await checkPerm(interaction.member, 'role_menu'))) { - await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true}) + await interaction.reply({content: functionLocale.accessDenied, ephemeral: true}) return; } const channel = interaction.options.getChannel('channel'); @@ -166,33 +198,39 @@ async function deleteRoleFromMenu(interaction) { .splice(index2, 1); await message.edit({content: message.content, components: message.components}); await menu.save(); - await interaction.reply({content: 'Role has been removed from menu.', ephemeral: true}); + await interaction.reply({ + content: functionLocale.roleRemoved.replaceAll('$$ROLE$$', `${role.name}`), + ephemeral: true + }); } else { await interaction.reply({ - content: 'This role has not been added to the menu.', + content: functionLocale.roleNotRemoved.replaceAll('$$ROLE$$', `${role.name}`), ephemeral: true }); } } else { - await interaction.reply({content: 'Please select a menu message.', ephemeral: true}); + await interaction.reply({content: functionLocale.notMenuMessage, ephemeral: true}); } } else { - await interaction.reply({ - content: 'The message ID does not exist in this channel.', - ephemeral: true - }); + await interaction.reply({content: functionLocale.messageIdNotFound, ephemeral: true}); } } else { - await interaction.reply({ - content: 'The message ID was invalid.', ephemeral: true - }) + await interaction.reply({content: functionLocale.invalidMessageId, ephemeral: true}) } + } else { + await interaction.reply({content: functionLocale.channelNotText, ephemeral: true}); } } async function editRoleMenu(interaction) { + const config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.roleMenu; if (!(await checkPerm(interaction.member, 'role_menu'))) { - await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true}) + await interaction.reply({content: functionLocale.accessDenied, ephemeral: true}) return; } const channel = interaction.options.getChannel('channel'); @@ -226,21 +264,19 @@ async function editRoleMenu(interaction) { menu.verification = verification; } await menu.save(); - await interaction.reply({content: 'Menu has been updated.', ephemeral: true}); + await interaction.reply({content: functionLocale.menuUpdated, ephemeral: true}); + } else { - await interaction.reply({content: 'Please select a menu message.', ephemeral: true}); + await interaction.reply({content: functionLocale.notMenuMessage, ephemeral: true}); } } else { - await interaction.reply({ - content: 'The message ID does not exist in this channel.', - ephemeral: true - }); + await interaction.reply({content: functionLocale.messageIdNotFound, ephemeral: true}); } } else { - await interaction.reply({ - content: 'The message ID was invalid.', ephemeral: true - }) + await interaction.reply({content: functionLocale.invalidMessageId, ephemeral: true}) } + } else { + await interaction.reply({content: functionLocale.channelNotText, ephemeral: true}); } } @@ -264,6 +300,12 @@ const slashCommandHandler = async (interaction) => { const selectMenuHandler = async (interaction) => { if (interaction.customId === 'selectRole') { + const config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()}); + let locale = 'en'; + if (config != null) { + locale = config.locale; + } + const functionLocale = locales.get(locale).components.roleMenu; const guild = interaction.guild; const channel = interaction.channel; const message = interaction.message; @@ -272,32 +314,20 @@ const selectMenuHandler = async (interaction) => { channelID: channel.id, messageID: message.id }); - console.log("1"); - if (interaction.values[0] != 'placeholder') { - console.log("2"); if (menu != null) { - console.log("3"); if (interaction.values[0] === 'select_reset') { - console.log("4"); const roleIDs = menu.roles.map(role => role.roleID); roleIDs.forEach(roleID => interaction.member.roles.remove(roleID)); - await interaction.reply({content: 'Your role has been removed.', ephemeral: true}); + await interaction.reply({content: functionLocale.interactionMessage.roleRemoved, ephemeral: true}); } else { - console.log("5"); const roleIDs = menu.roles.map(role => role.roleID); roleIDs.forEach(roleID => interaction.member.roles.remove(roleID)); interaction.member.roles.add(roleIDs[menu.roles.map(role => role.value).indexOf(interaction.values[0])]); - await interaction.reply({content: 'You have received your role.', ephemeral: true}); + await interaction.reply({content: functionLocale.interactionMessage.roleAdded, ephemeral: true}); } } else { - await interaction.reply({content: 'Internal Error. Please contact admins.', ephemeral: true}); + await interaction.reply({content: functionLocale.internalError, ephemeral: true}); } - } else { - await interaction.reply({ - content: 'This message does not have any roles yet. Please contact your administrator.', - ephemeral: true - }); - } } } module.exports = { diff --git a/index.js b/index.js index 65968dd..61c457e 100644 --- a/index.js +++ b/index.js @@ -5,10 +5,16 @@ const botConfig = { token: process.env.DISCORD_API_TOKEN, }; +const locale = require('./util/locale') + // Connecting Database. connectDB(); +// Initializing localizations + +locale.init() + // Collecting all bot components. const components = []; @@ -19,6 +25,8 @@ for (const file of componentFiles) { components.push(component); } +// Creating the discord client. + const client = new Client({intents: [Intents.FLAGS.GUILDS]}); client.on('ready', () => { @@ -27,26 +35,20 @@ client.on('ready', () => { client.on('interactionCreate', async (interaction) => { if (interaction.isCommand()) { - components.forEach( component => { - if (component.slashCommandHandler != null) { - component.slashCommandHandler(interaction); - } - }); - } else if (interaction.isButton()) { - components.forEach( component => { - if (component.buttonHandler != null) { - component.buttonHandler(interaction); + components.forEach(component => { + if (component.slashCommandHandler !== undefined) { + component.slashCommandHandler(interaction); } }); } else if (interaction.isButton()) { - components.forEach( component => { - if (component.buttonHandler != null) { + components.forEach(component => { + if (component.buttonHandler !== undefined) { component.buttonHandler(interaction); } }); } else if (interaction.isSelectMenu()) { - components.forEach( component => { - if (component.selectMenuHandler != null) { + components.forEach(component => { + if (component.selectMenuHandler !== undefined) { component.selectMenuHandler(interaction); } }); diff --git a/locales/de.json b/locales/de.json new file mode 100644 index 0000000..09f7abc --- /dev/null +++ b/locales/de.json @@ -0,0 +1,116 @@ +{ + "locale": "de", + "commonName": "Deutsch", + "translations": { + "slashCommands": { + "assign_permission": { + "description": "Eine Berechtigung einer Rolle hinzufügen.", + "permissionDescription": "Berechtigung, die hinzugefügt werden soll.", + "permissionChoices": { + "roleMenus": "Rollenmenüs", + "locale": "Sprache wechseln" + }, + "roleDescription": "Rolle, der die Berechtigung hinzugefügt werden soll." + }, + "revoke_permission": { + "description": "Einer Rolle eine Berechtigung entziehen.", + "permissionDescription": "Berechtigung, die entzogen werden soll.", + "permissionChoices": { + "roleMenus": "Rollenmenüs", + "locale": "Sprache wechseln" + }, + "roleDescription": "Rolle, der die Berechtigung entzogen werden soll." + }, + "add_role_menu": { + "description": "Ein neues Rollenmenü erstellen.", + "channelDescription": "Kanal, in den das Rollenmenü gelegt werden soll", + "messageDescription": "Nachricht des Rollenmenüs.", + "typeDescription": "NOT YET IMPLEMENTED Typ des Rollenmenüs.", + "typeChoices": { + "oneOfN": "Bis zu eine Rolle aus N", + "mOfN": "Bis zu M Rollen aus N", + "anyOfN": "Bis zu N Rollen aus N" + }, + "verificationDescription": "NOT YET IMPLEMENTED Ist Fllaami Verifikation für dieses Rollenmenü notwendig?" + }, + "delete_role_menu": { + "description": "Ein Rollenmenü löschen.", + "channelDescription": "Kanal des zu löschenden Menüs.", + "messageIDDescription": "Nachrichten ID des zu löschenden Menüs." + }, + "edit_role_menu": { + "description": "Ein Rollenmenü bearbeiten.", + "channelDescription": "Kanal des zu bearbeitenden Menüs.", + "messageIDDescription": "Nachrichten ID des zu bearbeitenden Menüs.", + "messageDescription": "Message content.", + "typeDescription": "NOT YET IMPLEMENTED Typ des Rollenmenüs.", + "typeChoices": { + "oneOfN": "Bis zu eine Rolle aus N", + "mOfN": "Bis zu M Rollen aus N", + "anyOfN": "Bis zu N Rollen aus N" + }, + "verificationDescription": "NOT YET IMPLEMENTED Ist Fllaami Verifikation für dieses Rollenmenü notwendig?." + }, + "delete_role_from_menu": { + "description": "Rolle aus Rollenmenü löschen.", + "channelDescription": "Kanal des zu bearbeitenden Menüs.", + "messageIDDescription": "Nachrichten ID des zu bearbeitenden Menüs.", + "roleDescription": "Zu entfernende Rolle." + }, + "add_role_to_menu": { + "description": "Einem Rollenmenü eine Rolle hinzufügen.", + "channelDescription": "Kanal des zu bearbeitenden Menüs.", + "messageIDDescription": "Nachrichten ID des zu bearbeitenden Menüs.", + "roleDescription": "Hinzuzufügende Rolle." + }, + "list_permissions": { + "description": "Alle Berechtigungen des Servers auflisten." + }, + "change_locale": { + "description": "Die Sprache des Servers wechseln.", + "localeDescription": "Sprache, zu der gewechselt werden soll." + } + }, + "components": { + "locale": { + "authDenied": "Du darfst die Sprache des Bots auf diesem Server nicht ändern.", + "localeApplied": "Du hast die Sprache des Bots auf Deutsch gestellt.", + "sameLocale": "Die Sprache des Servers ist bereits Deutsch." + }, + "permConfig": { + "replyAssign": "Der Rolle $$ROLE$$ wurde die $$PERM$$ Berechtigung hinzugefügt.", + "replyRevoke": "Der Rolle $ROLE$$ wurde die $$PERM$$ Berechtigung entzogen.", + "replyList": "Liste der Berechtigungen:\n\nRollenmenüs: $$ROLEMENU$$\nSprache ändern $$LOCALE$$", + "accessDenied": "Nur administratoren dürfen diesen Command nutzen." + }, + "roleMenu": { + "accessDenied": "Du darfst diesen Command nicht nutzen.", + "channelNotText": "Bitte wähle einen Textkanal aus.", + "invalidMessageId": "Die Nachrichten ID war nicht gültig (falsches Format).'", + "messageIdNotFound": "Die Nachrichten ID gehört nicht zu einer Nachricht in diesem Kanal.", + "notMenuMessage": "Bitte wähle eine Nachricht, die zu einem Rollenmenü gehört, aus.", + "roleMenuCreated": "Das Rollenmenü wurde erstellt.", + "roleMenuDeleted": "Das Rollenmenü wurde gelöscht.", + "roleMenuDeletedFailed": "Das Rollenmenü konnte nicht gelöscht werden.", + "roleAdded": "Die Rolle $$ROLE$$ wurde dem Menü hinzugefügt.", + "roleAlreadyAdded": "Die Rolle $$ROLE$$ ist bereits in der Nachricht.", + "roleRemoved": "Die Rolle $$ROLE$$ Wurde aus dem Menü entfernt.", + "roleNotRemoved": "Die Rolle $$ROLE$$ wurde dem Menü noch nicht hinzugefügt.", + "menuUpdated": "Das Menü wurde aktualisiert.", + "interactionMessage": { + "roleAdded": "Du hast deine Rolle erhalten.", + "roleRemoved": "Deine Rolle wurde entfernt." + }, + "internalError": "Interner Fehler. Bitte kontaktier einen Admin." + } + }, + "misc": { + "messageTemplates": { + "roleDropdown": { + "placeholder": "Wähle eine Rolle aus.", + "selectResetText": "Auswahl zurücksetzen." + } + } + } + } +} \ No newline at end of file diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 0000000..56b4f76 --- /dev/null +++ b/locales/en.json @@ -0,0 +1,116 @@ +{ + "locale": "en", + "commonName": "English", + "translations": { + "slashCommands": { + "assign_permission": { + "description": "Assign a permission to a role.", + "permissionDescription": "Permission to assign.", + "permissionChoices": { + "roleMenus": "Role Menus", + "locale": "Change Locales" + }, + "roleDescription": "Role to assign permission to." + }, + "revoke_permission": { + "description": "Revoke a permission from a role.", + "permissionDescription": "Permission to revoke.", + "permissionChoices": { + "roleMenus": "Role Menus", + "locale": "Change Locales" + }, + "roleDescription": "Role to revoke permission from." + }, + "add_role_menu": { + "description": "Create a new role menu.", + "channelDescription": "Channel the menu will be put inside.", + "messageDescription": "Message content.", + "typeDescription": "NOT YET IMPLEMENTED Sets type of role menu.", + "typeChoices": { + "oneOfN": "One of N", + "mOfN": "M of N", + "anyOfN": "Any of N" + }, + "verificationDescription": "NOT YET IMPLEMENTED Sets verification state of role menu." + }, + "delete_role_menu": { + "description": "Delete a role menu.", + "channelDescription": "Select channel of to be deleted menu.", + "messageIDDescription": "Message ID of to be deleted menu." + }, + "edit_role_menu": { + "description": "Edit an existing role menu.", + "channelDescription": "Channel of the menu.", + "messageIDDescription": "Message ID of the menu.", + "messageDescription": "Message content.", + "typeDescription": "NOT YET IMPLEMENTED Sets type of role menu.", + "typeChoices": { + "oneOfN": "One of N", + "mOfN": "M of N", + "anyOfN": "Any of N" + }, + "verificationDescription": "NOT YET IMPLEMENTED Sets verification state of role menu." + }, + "delete_role_from_menu": { + "description": "Delete role from menu.", + "channelDescription": "Channel of the menu.", + "messageIDDescription": "Message ID of the menu.", + "roleDescription": "Role to remove from the menu." + }, + "add_role_to_menu": { + "description": "Add role to a menu.", + "channelDescription": "Channel of the menu.", + "messageIDDescription": "Message ID of the menu.", + "roleDescription": "Role to add to the menu." + }, + "list_permissions": { + "description": "List all permissions for this Server." + }, + "change_locale": { + "description": "Change locale of the bot on this server.", + "localeDescription": "Locale you want to change to bot to." + } + }, + "components": { + "locale": { + "authDenied": "You are not authorized to change the locale of the server.", + "localeApplied": "You changed the locale of the bot to English.", + "sameLocale": "The locale of the server is already english." + }, + "permConfig": { + "replyAssign": "Role $$ROLE$$ has been aded to $$PERM$$ permission.", + "replyRevoke": "Role $$ROLE$$ has been removed from $$PERM$$ permission.", + "replyList": "List of Permissions:\n\nRole Menu: $$ROLEMENU$$\nLocale: $$LOCALE$$", + "accessDenied": "Only administrators can access this command." + }, + "roleMenu": { + "accessDenied": "You are not authorized to do this.", + "channelNotText": "Please select a text channel.", + "invalidMessageId": "The message ID was invalid.'", + "messageIdNotFound": "The message ID does not exist in this channel.", + "notMenuMessage": "Please select a menu message.", + "roleMenuCreated": "Role Menu has been created.", + "roleMenuDeleted": "Role Menu has been deleted.", + "roleMenuDeletedFailed": "Deleting the Menu has failed.", + "roleAdded": "Role $$ROLE$$ has been added to the message.", + "roleAlreadyAdded": "Role $$ROLE$$ is already added to the message.", + "roleRemoved": "Role $$ROLE$$ has been removed from menu.", + "roleNotRemoved": "Role $$ROLE$$ has not yet been added to the menu.", + "menuUpdated": "Menu has been updated", + "interactionMessage": { + "roleAdded": "You have received your role.", + "roleRemoved": "Your role has been removed." + }, + "internalError": "Internal Error. Please contact admins." + } + }, + "misc": { + "messageTemplates": { + "roleDropdown": { + "placeholder": "Select a role.", + "selectResetText": "Reset selection." + } + } + } + } +} \ No newline at end of file diff --git a/models/RoleMenu.js b/models/RoleMenu.js index 380fac5..d0932c4 100644 --- a/models/RoleMenu.js +++ b/models/RoleMenu.js @@ -2,28 +2,16 @@ const {Schema, model} = require('mongoose'); const RoleMenuSchema = new Schema({ guildID: { - type: Number, + type: String, required: true, - validate: { - validator: Number.isInteger, - message: '{VALUE} is not an integer value' - } }, channelID: { - type: Number, + type: String, required: true, - validate: { - validator: Number.isInteger, - message: '{VALUE} is not an integer value' - } }, messageID: { - type: Number, + type: String, required: true, - validate: { - validator: Number.isInteger, - message: '{VALUE} is not an integer value' - } }, verification: { type: Boolean, diff --git a/models/ServerConfig.js b/models/ServerConfig.js index 1428e8a..39aad0d 100644 --- a/models/ServerConfig.js +++ b/models/ServerConfig.js @@ -6,10 +6,19 @@ const ServerConfigSchema = new Schema({ unique: true, required: true }, + locale: { + type: String, + required: true, + default: 'en' + }, permissions: { - roleMenu: [{ - type: String - }]} + roleMenu: [{ + type: String + }], + locale: [{ + type: String + }] + } }); diff --git a/registerCommands.js b/registerCommands.js index 4ab8b75..49ed341 100644 --- a/registerCommands.js +++ b/registerCommands.js @@ -1,26 +1,24 @@ -const {REST} = require('@discordjs/rest'); -const {Routes} = require('discord-api-types/v9'); const {Client, Intents} = require('discord.js'); -const fs = require('fs'); +const connectDB = require('./util/db'); const botConfig = { token: process.env.DISCORD_API_TOKEN, applicationID: process.env.DISCORD_APP_ID }; +const locale = require('./util/locale'); +const registerCommands = require('./util/registerCommands'); + +// Initializing Database + +connectDB() + +// Initializing locales + +locale.init(); // Loading discord client to get all guild IDs const client = new Client({intents: [Intents.FLAGS.GUILDS]}); client.on('ready', () => { - // Getting all command Javascript files from the commands folder - commands = [] - const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); - - for (const file of commandFiles) { - const command = require(`./commands/${file}`); - commands.push(command); - } - - const rest = new REST({version: '9'}).setToken(botConfig.token); (async () => { try { @@ -29,15 +27,10 @@ client.on('ready', () => { const guildIDs = await client.guilds.cache.map(guild => guild.id); console.log(guildIDs) //Going through all guildIDs and registering the commands with each one. - await Promise.all(guildIDs.map(async (guildID) => { - console.log(guildID); - await rest.put( - Routes.applicationGuildCommands(botConfig.applicationID, guildID), - {body: commands}, - ); - })); + await Promise.all(guildIDs.map(async (guildID) => registerCommands(guildID, './commands'))); console.log('Successfully reloaded application (/) commands.'); + process.exit(0); } catch (error) { console.error(error); } diff --git a/util/locale.js b/util/locale.js new file mode 100644 index 0000000..bff1db0 --- /dev/null +++ b/util/locale.js @@ -0,0 +1,21 @@ +const fs = require('fs'); + +let locales = null; + +module.exports = { + get: (locale) => { + if (locales[locale] !== undefined) { + return locales[locale]; + } else { + return locales["en"]; + } + }, + init: () => { + locales = new Object(); + const localeFiles = fs.readdirSync('./locales').filter(file => file.endsWith('.json')); + localeFiles.forEach((localeFile) => { + const locale = JSON.parse(fs.readFileSync(`./locales/${localeFile}`)); + locales[locale.locale] = locale.translations; + }); + } +} \ No newline at end of file diff --git a/util/perm.js b/util/perm.js index 4fe3a40..1c27730 100644 --- a/util/perm.js +++ b/util/perm.js @@ -8,10 +8,9 @@ module.exports = async (member, permission) => { return false; } if (permission === 'role_menu') { - //console.log(member.roles.cache); - //console.log(config.permissions.roleMenu); - console.log("ASD"); return member.roles.cache.some((role) => config.permissions.roleMenu.some((prole) => prole == role.id.toString())) || member.permissions.has(Permissions.FLAGS.ADMINISTRATOR); + } else if (permission === 'locale') { + return member.roles.cache.some((role) => config.permissions.locale.some((prole) => prole == role.id.toString())) || member.permissions.has(Permissions.FLAGS.ADMINISTRATOR); } return false; } \ No newline at end of file diff --git a/util/registerCommands.js b/util/registerCommands.js new file mode 100644 index 0000000..dadc378 --- /dev/null +++ b/util/registerCommands.js @@ -0,0 +1,30 @@ +const ServerConfig = require("../models/ServerConfig"); +const fs = require("fs"); +const {Routes} = require("discord-api-types/v9"); +const {REST} = require("@discordjs/rest"); +const botConfig = { + token: process.env.DISCORD_API_TOKEN, + applicationID: process.env.DISCORD_APP_ID +}; + +module.exports = async (guildID, pathToCommands) => { + + + const rest = new REST({version: '9'}).setToken(botConfig.token); + const config = await ServerConfig.findOne({guildID}); + var serverLocale = 'en'; + if (config != null) { + serverLocale = config.locale; + } + // Getting all command Javascript files from the commands folder + let commands = [] + const commandFiles = fs.readdirSync(pathToCommands).filter(file => file.endsWith('.js')); + for (const file of commandFiles) { + const command = require(`../commands/${file}`); + commands.push(command(serverLocale)); + } + await rest.put( + Routes.applicationGuildCommands(botConfig.applicationID, guildID), + {body: commands}, + ); +} \ No newline at end of file -- GitLab