diff --git a/data/config.txt b/data/config.txt index 59f1105..4ce8aaf 100644 --- a/data/config.txt +++ b/data/config.txt @@ -18,4 +18,9 @@ embed.colour.error: The HEX value of the error embeds (Default: "0xd52803") channels.logs.message: The channel message events will be logged to (Default: "message-logs") channels.logs.member: The channel member events will be logged to (Default: "member-logs") -channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs") \ No newline at end of file +channels.logs.mod: The channel mod events will be logged to (Default: "mod-logs") + +verification.enabled: Enables/Disables the verification feature (Default: "false") +verification.channel: The channel to listen to for entry codes (Default: "entry") +verification.role: The server access role (Default: "Entry") +verification.code: The entry code for the channel (Default: "") \ No newline at end of file diff --git a/src/commands/code.ts b/src/commands/code.ts new file mode 100644 index 0000000..d2438fb --- /dev/null +++ b/src/commands/code.ts @@ -0,0 +1,94 @@ +import { CommandResponse } from "../constants/CommandResponse"; +import { ICommandContext } from "../contracts/ICommandContext"; +import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; +import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import SettingsHelper from "../helpers/SettingsHelper"; +import StringTools from "../helpers/StringTools"; +import { Command } from "../type/command"; + +export default class Code extends Command { + constructor() { + super(); + + super._category = "Moderation"; + super._roles = [ + "moderator" + ]; + } + + public override async precheckAsync(context: ICommandContext): Promise { + if (!context.message.guild){ + return CommandResponse.NotInServer; + } + + const isEnabled = await SettingsHelper.GetSetting("verification.enabled", context.message.guild?.id); + + if (!isEnabled) { + return CommandResponse.FeatureDisabled; + } + + if (isEnabled.toLocaleLowerCase() != 'true') { + return CommandResponse.FeatureDisabled; + } + + return CommandResponse.Ok; + } + + public override async execute(context: ICommandContext) { + const action = context.args[0]; + + switch (action) { + case "randomise": + await this.Randomise(context); + break; + case "embed": + await this.SendEmbed(context); + break; + default: + await this.SendUsage(context); + } + } + + private async SendUsage(context: ICommandContext) { + const description = [ + "USAGE: ", + "", + "randomise: Sets the server's entry code to a random code", + "embed: Sends an embed with the server's entry code" + ].join("\n"); + + const embed = new PublicEmbed(context, "", description); + embed.SendToCurrentChannel(); + } + + private async Randomise(context: ICommandContext) { + if (!context.message.guild) { + return; + } + + const randomCode = StringTools.RandomString(5); + + await SettingsHelper.SetSetting("verification.code", context.message.guild.id, randomCode); + + const embed = new PublicEmbed(context, "Code", `Entry code has been set to \`${randomCode}\``); + embed.SendToCurrentChannel(); + } + + private async SendEmbed(context: ICommandContext) { + if (!context.message.guild) { + return; + } + + const code = await SettingsHelper.GetSetting("verification.code", context.message.guild.id); + + if (!code || code == "") { + const errorEmbed = new ErrorEmbed(context, "There is no code for this server setup."); + errorEmbed.SendToCurrentChannel(); + + return; + } + + const embed = new PublicEmbed(context, "Entry Code", code!); + embed.SendToCurrentChannel(); + } +} \ No newline at end of file diff --git a/src/constants/CommandResponse.ts b/src/constants/CommandResponse.ts index 968f352..b876ce8 100644 --- a/src/constants/CommandResponse.ts +++ b/src/constants/CommandResponse.ts @@ -2,4 +2,6 @@ export enum CommandResponse { Ok, Unauthorised, ServerNotSetup, + NotInServer, + FeatureDisabled, } \ No newline at end of file diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index c1348af..d091029 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -39,6 +39,12 @@ export default class DefaultValues { this.values.push({ Key: "channels.logs.message", Value: "message-logs" }); this.values.push({ Key: "channels.logs.member", Value: "member-logs" }); this.values.push({ Key: "channels.logs.mod", Value: "mod-logs" }); + + // Verification + this.values.push({ Key: "verification.enabled", Value: "false" }); + this.values.push({ Key: "verification.channel", Value: "entry" }); + this.values.push({ Key: "verification.role", Value: "Entry" }); + this.values.push({ Key: "verification.code", Value: "" }); } } } diff --git a/src/constants/ErrorMessages.ts b/src/constants/ErrorMessages.ts index f096a8a..588a143 100644 --- a/src/constants/ErrorMessages.ts +++ b/src/constants/ErrorMessages.ts @@ -7,6 +7,8 @@ export default class ErrorMessages { public static readonly UserUnauthorised = "You are not authorised to use this command"; public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command"; + public static readonly NotInServer = "This command requires to be ran inside of a server"; + public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator"; public static GetErrorMessage(response: CommandResponse): string { switch (response) { @@ -14,6 +16,10 @@ export default class ErrorMessages { return this.UserUnauthorised; case CommandResponse.ServerNotSetup: return this.ServerNotSetup; + case CommandResponse.NotInServer: + return this.NotInServer; + case CommandResponse.FeatureDisabled: + return this.FeatureDisabled; default: return ""; } diff --git a/src/events/MessageEvents.ts b/src/events/MessageEvents.ts index a664d5e..17797b4 100644 --- a/src/events/MessageEvents.ts +++ b/src/events/MessageEvents.ts @@ -2,6 +2,8 @@ import { Event } from "../type/event"; import { Message } from "discord.js"; import EventEmbed from "../helpers/embeds/EventEmbed"; import IEventReturnContext from "../contracts/IEventReturnContext"; +import SettingsHelper from "../helpers/SettingsHelper"; +import OnMessage from "./MessageEvents/OnMessage"; export default class MessageEvents extends Event { constructor() { @@ -68,4 +70,15 @@ export default class MessageEvents extends Event { embeds: [embed] }; } + + public override async message(message: Message) { + if (!message.guild) return; + if (message.author.bot) return; + + const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id); + + if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") { + await OnMessage.VerificationCheck(message); + } + } } \ No newline at end of file diff --git a/src/events/MessageEvents/OnMessage.ts b/src/events/MessageEvents/OnMessage.ts new file mode 100644 index 0000000..18c2a57 --- /dev/null +++ b/src/events/MessageEvents/OnMessage.ts @@ -0,0 +1,59 @@ +import { Message as Message } from "discord.js"; +import SettingsHelper from "../../helpers/SettingsHelper"; + +export default class OnMessage { + public static async VerificationCheck(message: Message) { + if (!message.guild) return; + + const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id); + + if (!verificationChannel) { + return; + } + + const channel = message.guild.channels.cache.find(x => x.name == verificationChannel); + + if (!channel) { + return; + } + + const currentChannel = message.guild.channels.cache.find(x => x == message.channel); + + if (!currentChannel || currentChannel.name != verificationChannel) { + return; + } + + const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id); + + if (!verificationCode || verificationCode == "") { + await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team."); + await message.delete(); + + return; + } + + const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id); + + if (!verificationRoleName) { + await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team."); + await message.delete(); + return; + } + + const role = message.guild.roles.cache.find(x => x.name == verificationRoleName); + + if (!role) { + await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team."); + await message.delete(); + return; + } + + if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) { + await message.delete(); + return; + } + + await message.member?.roles.add(role); + await message.delete(); + } +} \ No newline at end of file diff --git a/src/helpers/StringTools.ts b/src/helpers/StringTools.ts index b42eb90..dab3571 100644 --- a/src/helpers/StringTools.ts +++ b/src/helpers/StringTools.ts @@ -12,4 +12,17 @@ export default class StringTools { return result.join(" "); } + + public static RandomString(length: number) { + let result = ""; + + const characters = 'abcdefghkmnpqrstuvwxyz23456789'; + const charactersLength = characters.length; + + for ( var i = 0; i < length; i++ ) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + + return result; + } } \ No newline at end of file diff --git a/src/Register.ts b/src/registry.ts similarity index 93% rename from src/Register.ts rename to src/registry.ts index 452c6c4..81afe62 100644 --- a/src/Register.ts +++ b/src/registry.ts @@ -2,6 +2,7 @@ import { CoreClient } from "./client/client"; import About from "./commands/about"; import Ban from "./commands/ban"; import Clear from "./commands/clear"; +import Code from "./commands/code"; import Config from "./commands/config"; import Evaluate from "./commands/eval"; import Help from "./commands/help"; @@ -16,7 +17,7 @@ import Warn from "./commands/warn"; import MemberEvents from "./events/MemberEvents"; import MessageEvents from "./events/MessageEvents"; -export default class Register { +export default class Registry { public static RegisterCommands(client: CoreClient) { client.RegisterCommand("about", new About()); client.RegisterCommand("ban", new Ban()); @@ -32,6 +33,7 @@ export default class Register { client.RegisterCommand("warn", new Warn()); client.RegisterCommand("setup", new Setup()); client.RegisterCommand("config", new Config()); + client.RegisterCommand("code", new Code()); } public static RegisterEvents(client: CoreClient) { diff --git a/src/vylbot.ts b/src/vylbot.ts index 3437d89..d704cd4 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -1,6 +1,6 @@ import { CoreClient } from "./client/client"; import * as dotenv from "dotenv"; -import Register from "./Register"; +import registry from "./registry"; dotenv.config(); @@ -15,7 +15,7 @@ requiredConfigs.forEach(config => { const client = new CoreClient(); -Register.RegisterCommands(client); -Register.RegisterEvents(client); +registry.RegisterCommands(client); +registry.RegisterEvents(client); client.start(); \ No newline at end of file