diff --git a/Boyfriend/Boyfriend.cs b/Boyfriend/Boyfriend.cs
index ef1c5ab..05d473b 100644
--- a/Boyfriend/Boyfriend.cs
+++ b/Boyfriend/Boyfriend.cs
@@ -1,4 +1,5 @@
-using System.Text.Json;
+using System.Globalization;
+using System.Text.Json;
 using Discord;
 using Discord.WebSocket;
 
@@ -6,9 +7,6 @@ namespace Boyfriend;
 
 public static class Boyfriend {
 
-    public static void Main()
-        => Init().GetAwaiter().GetResult();
-
     private static readonly DiscordSocketConfig Config = new() {
         MessageCacheSize = 250,
         GatewayIntents = GatewayIntents.All
@@ -18,15 +16,19 @@ public static class Boyfriend {
 
     private static readonly Dictionary<ulong, GuildConfig> GuildConfigDictionary = new();
 
+    public static void Main() {
+        Init().GetAwaiter().GetResult();
+    }
+
     private static async Task Init() {
         Client.Log += Log;
         var token = (await File.ReadAllTextAsync("token.txt")).Trim();
 
         await Client.LoginAsync(TokenType.Bot, token);
         await Client.StartAsync();
-        await Client.SetActivityAsync(new Game("Retrospecter - Electrospasm", ActivityType.Listening));
+        await Client.SetActivityAsync(new Game("Retrospecter - Chiller", ActivityType.Listening));
 
-        await new EventHandler().InitEvents();
+        new EventHandler().InitEvents();
 
         await Task.Delay(-1);
     }
@@ -45,29 +47,28 @@ public static class Boyfriend {
             try {
                 config = await JsonSerializer.DeserializeAsync<GuildConfig>(openStream) ?? throw new Exception();
             } catch (JsonException) {
-                config = new GuildConfig(guild.Id, "ru", "!", false, true, true, 0, 0, 0);
+                Messages.Culture = new CultureInfo("ru");
+                config = new GuildConfig(guild.Id, "ru", "!", false, true,
+                    true, Messages.DefaultWelcomeMessage, 0, 0, 0, 0);
             }
             GuildConfigDictionary.Add(guild.Id, config);
         }
     }
 
     public static GuildConfig GetGuildConfig(IGuild guild) {
+        Messages.Culture = new CultureInfo("ru");
         var toReturn = GuildConfigDictionary.ContainsKey(guild.Id) ? GuildConfigDictionary[guild.Id]
-            : new GuildConfig(guild.Id, "ru", "!", false, true, true, 0, 0, 0);
+            : new GuildConfig(guild.Id, "ru", "!", false, true, true, Messages.DefaultWelcomeMessage, 0, 0, 0, 0);
 
         if (toReturn.Id != guild.Id) throw new Exception();
         return toReturn;
     }
 
-    public static IGuild FindGuild(ITextChannel channel) {
-        foreach (var guild in Client.Guilds) {
-            if (guild.Channels.Any(x => x == channel)) return guild;
-        }
+    public static IGuild FindGuild(IMessageChannel channel) {
+        foreach (var guild in Client.Guilds)
+            if (guild.Channels.Any(x => x == channel))
+                return guild;
 
-        throw new Exception("Не удалось найти сервер по каналу!");
-    }
-
-    public static void ThrowFatal(Exception e) {
-        throw e;
+        throw new Exception(Messages.CouldntFindGuildByChannel);
     }
 }
\ No newline at end of file
diff --git a/Boyfriend/Boyfriend.csproj b/Boyfriend/Boyfriend.csproj
index 9b9566e..1563891 100644
--- a/Boyfriend/Boyfriend.csproj
+++ b/Boyfriend/Boyfriend.csproj
@@ -11,10 +11,26 @@
         <PackageProjectUrl>https://github.com/l1ttleO/Boyfriend-CSharp</PackageProjectUrl>
         <RepositoryUrl>https://github.com/l1ttleO/Boyfriend-CSharp</RepositoryUrl>
         <RepositoryType>git</RepositoryType>
+        <PackageVersion>1.0.1</PackageVersion>
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="Discord.Net" Version="3.0.0-dev-20211206.5" />
+        <PackageReference Include="Discord.Net" Version="3.1.0"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <EmbeddedResource Update="Messages.resx">
+            <Generator>ResXFileCodeGenerator</Generator>
+            <LastGenOutput>Messages.Designer.cs</LastGenOutput>
+        </EmbeddedResource>
+    </ItemGroup>
+
+    <ItemGroup>
+        <Compile Update="Messages.Designer.cs">
+            <DesignTime>True</DesignTime>
+            <AutoGen>True</AutoGen>
+            <DependentUpon>Messages.resx</DependentUpon>
+        </Compile>
     </ItemGroup>
 
 </Project>
diff --git a/Boyfriend/Boyfriend.csproj.DotSettings b/Boyfriend/Boyfriend.csproj.DotSettings
index 47a5106..b5da49f 100644
--- a/Boyfriend/Boyfriend.csproj.DotSettings
+++ b/Boyfriend/Boyfriend.csproj.DotSettings
@@ -1,3 +1,5 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
 	<s:String x:Key="/Default/CodeEditing/Localization/Localizable/@EntryValue">No</s:String>
+	
+	
 	</wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/Boyfriend/CommandHandler.cs b/Boyfriend/CommandHandler.cs
index 90026cc..0d79886 100644
--- a/Boyfriend/CommandHandler.cs
+++ b/Boyfriend/CommandHandler.cs
@@ -1,48 +1,43 @@
-using Discord;
+using System.Text.RegularExpressions;
+using Boyfriend.Commands;
+using Discord;
 using Discord.Commands;
 using Discord.WebSocket;
 
 namespace Boyfriend;
 
 public static class CommandHandler {
-    public static async Task HandleCommand(SocketUserMessage message, int argPos) {
-        var context = new SocketCommandContext(Boyfriend.Client, message);
-        var result = await EventHandler.Commands.ExecuteAsync(context, argPos, null);
+    public static readonly Command[] Commands = {
+        new BanCommand(), new ClearCommand(), new HelpCommand(),
+        new KickCommand(), new MuteCommand()
+    };
 
-        await HandleErrors(context, result);
-    }
-    private static async Task HandleErrors(SocketCommandContext context, IResult result) {
-        var channel = context.Channel;
-        var reason = Utils.WrapInline(result.ErrorReason);
-        switch (result.Error) {
-            case CommandError.Exception:
-                await channel.SendMessageAsync(reason);
-                break;
-            case CommandError.Unsuccessful:
-                await channel.SendMessageAsync($"Выполнение команды завершилось неудачей: {reason}");
-                break;
-            case CommandError.MultipleMatches:
-                await channel.SendMessageAsync($"Обнаружены повторяющиеся типы аргументов! {reason}");
-                break;
-            case CommandError.ParseFailed:
-                await channel.SendMessageAsync($"Не удалось обработать команду: {reason}");
-                break;
-            case CommandError.UnknownCommand:
-                await channel.SendMessageAsync($"Неизвестная команда! {reason}");
-                break;
-            case CommandError.UnmetPrecondition:
-                await channel.SendMessageAsync($"У тебя недостаточно прав для выполнения этой к: {reason}");
-                break;
-            case CommandError.BadArgCount:
-                await channel.SendMessageAsync($"Неверное количество аргументов! {reason}");
-                break;
-            case CommandError.ObjectNotFound:
-                await channel.SendMessageAsync($"Нету нужных аргументов! {reason}");
-                break;
-            case null:
-                break;
-            default:
-                throw new Exception("CommandError");
+    public static async Task HandleCommand(SocketUserMessage message) {
+        var context = new SocketCommandContext(Boyfriend.Client, message);
+
+        foreach (var command in Commands) {
+            var regex = new Regex(Regex.Escape(Boyfriend.GetGuildConfig(context.Guild).Prefix));
+            if (!command.GetAliases().Contains(regex.Replace(message.Content, "", 1).Split()[0])) continue;
+
+            var args = message.Content.Split().Skip(1).ToArray();
+            try {
+                if (command.GetArgumentsAmountRequired() > args.Length)
+                    throw new ApplicationException(string.Format(Messages.NotEnoughArguments,
+                        command.GetArgumentsAmountRequired(), args.Length));
+                await command.Run(context, args);
+            }
+            catch (Exception e) {
+                var signature = e switch {
+                    ApplicationException => ":x:",
+                    UnauthorizedAccessException => ":no_entry_sign:",
+                    _ => ":stop_sign:"
+                };
+                await context.Channel.SendMessageAsync($"{signature} `{e.Message}`");
+                if (e.StackTrace != null && e is not ApplicationException or UnauthorizedAccessException)
+                    await context.Channel.SendMessageAsync(Utils.Wrap(e.StackTrace));
+            }
+
+            break;
         }
     }
 
@@ -50,25 +45,25 @@ public static class CommandHandler {
         GuildPermission forBot = GuildPermission.StartEmbeddedActivities) {
         if (forBot == GuildPermission.StartEmbeddedActivities) forBot = toCheck;
         if (!(await user.Guild.GetCurrentUserAsync()).GuildPermissions.Has(forBot))
-            throw new Exception("У меня недостаточно прав для выполнения этой команды!");
+            throw new UnauthorizedAccessException(Messages.CommandNoPermissionBot);
         if (!user.GuildPermissions.Has(toCheck))
-            throw new Exception("У тебя недостаточно прав для выполнения этой команды!");
+            throw new UnauthorizedAccessException(Messages.CommandNoPermissionUser);
     }
 
     public static async Task CheckInteractions(IGuildUser actor, IGuildUser target) {
         if (actor.Guild != target.Guild)
-            throw new Exception("Участники находятся в разных гильдиях!");
+            throw new UnauthorizedAccessException(Messages.InteractionsDifferentGuilds);
         var me = await target.Guild.GetCurrentUserAsync();
         if (actor.Id == actor.Guild.OwnerId) return;
         if (target.Id == target.Guild.OwnerId)
-            throw new Exception("Ты не можешь взаимодействовать с владельцем сервера!");
+            throw new UnauthorizedAccessException(Messages.InteractionsOwner);
         if (actor == target)
-            throw new Exception("Ты не можешь взаимодействовать с самим собой!");
+            throw new UnauthorizedAccessException(Messages.InteractionsYourself);
         if (target == me)
-            throw new Exception("Ты не можешь со мной взаимодействовать!");
-        if (actor.Hierarchy <= target.Hierarchy)
-            throw new Exception("Ты не можешь взаимодействовать с этим участником!");
+            throw new UnauthorizedAccessException(Messages.InteractionsMe);
         if (me.Hierarchy <= target.Hierarchy)
-            throw new Exception("Я не могу взаимодействовать с этим участником!");
+            throw new UnauthorizedAccessException(Messages.InteractionsFailedBot);
+        if (actor.Hierarchy <= target.Hierarchy)
+            throw new UnauthorizedAccessException(Messages.InteractionsFailedUser);
     }
 }
\ No newline at end of file
diff --git a/Boyfriend/Commands/BanCommand.cs b/Boyfriend/Commands/BanCommand.cs
new file mode 100644
index 0000000..dcd5a7b
--- /dev/null
+++ b/Boyfriend/Commands/BanCommand.cs
@@ -0,0 +1,61 @@
+using Discord;
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable ClassNeverInstantiated.Global
+
+namespace Boyfriend.Commands;
+
+public class BanCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        var toBan = await Utils.ParseUser(args[0]);
+        var reason = Utils.JoinString(args, 1);
+        TimeSpan duration;
+        try {
+            duration = Utils.GetTimeSpan(args[1]);
+            reason = Utils.JoinString(args, 2);
+        }
+        catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
+            duration = TimeSpan.FromMilliseconds(-1);
+        }
+
+        var author = context.Guild.GetUser(context.User.Id);
+
+        await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
+        var memberToBan = context.Guild.GetUser(toBan.Id);
+        if (memberToBan != null)
+            await CommandHandler.CheckInteractions(author, memberToBan);
+        await BanUser(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id),
+            toBan, duration, reason);
+    }
+
+    public static async Task BanUser(IGuild guild, ITextChannel? channel, IGuildUser author, IUser toBan,
+        TimeSpan duration, string reason) {
+        var authorMention = author.Mention;
+        await Utils.SendDirectMessage(toBan, string.Format(Messages.YouWereBanned, author.Mention, guild.Name,
+            Utils.WrapInline(reason)));
+        var guildBanMessage = $"({author.Username}#{author.Discriminator}) {reason}";
+        await guild.AddBanAsync(toBan, 0, guildBanMessage);
+        var notification = string.Format(Messages.UserBanned, authorMention, toBan.Mention, Utils.WrapInline(reason));
+        await Utils.SilentSendAsync(channel, string.Format(Messages.BanResponse, toBan.Mention,
+            Utils.WrapInline(reason)));
+        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
+        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
+        var task = new Task(() => UnbanCommand.UnbanUser(guild, null, guild.GetCurrentUserAsync().Result, toBan,
+            Messages.PunishmentExpired));
+        await Utils.StartDelayed(task, duration, () => guild.GetBanAsync(toBan).Result != null);
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"ban", "бан"};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 2;
+    }
+
+    public override string GetSummary() {
+        return "Банит пользователя";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/BanModule.cs b/Boyfriend/Commands/BanModule.cs
deleted file mode 100644
index cd1295b..0000000
--- a/Boyfriend/Commands/BanModule.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using Discord;
-using Discord.Commands;
-
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-// ReSharper disable ClassNeverInstantiated.Global
-
-namespace Boyfriend.Commands;
-
-public class BanModule : ModuleBase<SocketCommandContext> {
-
-    [Command("ban")]
-    [Summary("Банит пользователя")]
-    [Alias("бан")]
-    public async Task Run(string user, [Remainder]string reason) {
-        TimeSpan duration;
-        try {
-            var reasonArray = reason.Split();
-            duration = Utils.GetTimeSpan(reasonArray[0]);
-            reason = string.Join(" ", reasonArray.Skip(1));
-        } catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
-            duration = TimeSpan.FromMilliseconds(-1);
-        }
-        var author = Context.Guild.GetUser(Context.User.Id);
-        var toBan = await Utils.ParseUser(user);
-        await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
-        var memberToBan = Context.Guild.GetUser(toBan.Id);
-        if (memberToBan != null)
-            await CommandHandler.CheckInteractions(author, memberToBan);
-        BanUser(Context.Guild, Context.Guild.GetUser(Context.User.Id), toBan, duration, reason);
-    }
-
-    public static async void BanUser(IGuild guild, IGuildUser author, IUser toBan, TimeSpan duration, string reason) {
-        var authorMention = author.Mention;
-        await Utils.SendDirectMessage(toBan, $"Тебя забанил {author.Mention} на сервере {guild.Name} за `{reason}`");
-        var guildBanMessage = $"({author.Username}#{author.Discriminator}) {reason}";
-        await guild.AddBanAsync(toBan, 0, guildBanMessage);
-        var notification = $"{authorMention} банит {toBan.Mention} за {Utils.WrapInline(reason)}";
-        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
-        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
-        var task = new Task(() => UnbanModule.UnbanUser(guild, guild.GetCurrentUserAsync().Result, toBan,
-            "Время наказания истекло"));
-        await Utils.StartDelayed(task, duration, () => guild.GetBanAsync(toBan).Result != null);
-    }
-}
\ No newline at end of file
diff --git a/Boyfriend/Commands/ClearCommand.cs b/Boyfriend/Commands/ClearCommand.cs
new file mode 100644
index 0000000..fee031e
--- /dev/null
+++ b/Boyfriend/Commands/ClearCommand.cs
@@ -0,0 +1,49 @@
+using Discord;
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable ClassNeverInstantiated.Global
+
+namespace Boyfriend.Commands;
+
+public class ClearCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        int toDelete;
+        try {
+            toDelete = Convert.ToInt32(args[0]);
+        }
+        catch (Exception e) when (e is FormatException or OverflowException) {
+            throw new ApplicationException(Messages.ClearInvalidAmountSpecified);
+        }
+
+        if (context.Channel is not ITextChannel channel) return;
+        await CommandHandler.CheckPermissions(context.Guild.GetUser(context.User.Id), GuildPermission.ManageMessages);
+        switch (toDelete) {
+            case < 1:
+                throw new ApplicationException(Messages.ClearNegativeAmount);
+            case > 200:
+                throw new ApplicationException(Messages.ClearAmountTooLarge);
+            default: {
+                var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync();
+                await channel.DeleteMessagesAsync(messages);
+                await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(context.Guild),
+                    string.Format(Messages.MessagesDeleted, context.User.Mention, toDelete + 1,
+                        Utils.MentionChannel(context.Channel.Id)));
+                break;
+            }
+        }
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"clear", "purge", "очистить", "стереть"};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 1;
+    }
+
+    public override string GetSummary() {
+        return "Очищает сообщения";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/ClearModule.cs b/Boyfriend/Commands/ClearModule.cs
deleted file mode 100644
index 94eade6..0000000
--- a/Boyfriend/Commands/ClearModule.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Discord;
-using Discord.Commands;
-
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-// ReSharper disable ClassNeverInstantiated.Global
-
-namespace Boyfriend.Commands;
-
-public class ClearModule : ModuleBase<SocketCommandContext> {
-
-    [Command("clear")]
-    [Summary("Удаляет указанное количество сообщений")]
-    [Alias("очистить")]
-    public async Task Run(int toDelete) {
-        if (Context.Channel is not ITextChannel channel) return;
-        await CommandHandler.CheckPermissions(Context.Guild.GetUser(Context.User.Id), GuildPermission.ManageMessages);
-        switch (toDelete) {
-            case < 1:
-                throw new Exception( "Указано отрицательное количество сообщений!");
-            case > 200:
-                throw new Exception("Указано слишком много сообщений!");
-            default: {
-                var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync();
-                await channel.DeleteMessagesAsync(messages);
-                await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Context.Guild),
-                    $"{Context.User.Mention} удаляет {toDelete + 1} сообщений в канале " +
-                    $"{Utils.MentionChannel(Context.Channel.Id)}");
-                break;
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Boyfriend/Commands/Command.cs b/Boyfriend/Commands/Command.cs
new file mode 100644
index 0000000..b90d6b0
--- /dev/null
+++ b/Boyfriend/Commands/Command.cs
@@ -0,0 +1,19 @@
+using Discord;
+using Discord.Commands;
+using Discord.WebSocket;
+
+namespace Boyfriend.Commands;
+
+public abstract class Command {
+    public abstract Task Run(SocketCommandContext context, string[] args);
+
+    public abstract List<string> GetAliases();
+
+    public abstract int GetArgumentsAmountRequired();
+
+    public abstract string GetSummary();
+
+    protected static async Task Warn(ISocketMessageChannel channel, string warning) {
+        await Utils.SilentSendAsync(channel as ITextChannel, ":warning: " + warning);
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/HelpCommand.cs b/Boyfriend/Commands/HelpCommand.cs
new file mode 100644
index 0000000..b3eeed1
--- /dev/null
+++ b/Boyfriend/Commands/HelpCommand.cs
@@ -0,0 +1,30 @@
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+
+namespace Boyfriend.Commands;
+
+public class HelpCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        var nl = Environment.NewLine;
+        var toSend = string.Format(Messages.CommandHelp, nl);
+        var prefix = Boyfriend.GetGuildConfig(context.Guild).Prefix;
+        toSend = CommandHandler.Commands.Aggregate(toSend,
+            (current, command) => current + $"`{prefix}{command.GetAliases()[0]}`: {command.GetSummary()}{nl}");
+
+        await context.Channel.SendMessageAsync(toSend);
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"help", "помощь", "справка"};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 0;
+    }
+
+    public override string GetSummary() {
+        return "Показывает эту справку";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/HelpModule.cs b/Boyfriend/Commands/HelpModule.cs
deleted file mode 100644
index cafea6d..0000000
--- a/Boyfriend/Commands/HelpModule.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Discord.Commands;
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-
-namespace Boyfriend.Commands;
-
-public class HelpModule : ModuleBase<SocketCommandContext> {
-
-    [Command("help")]
-    [Summary("Показывает эту справку")]
-    [Alias("помощь", "справка")]
-    public Task Run() {
-        var nl = Environment.NewLine;
-        var toSend = $"Справка по командам:{nl}";
-        var prefix = Boyfriend.GetGuildConfig(Context.Guild).Prefix;
-        foreach (var command in EventHandler.Commands.Commands) {
-            var aliases = command.Aliases.Aggregate("", (current, alias) =>
-                current + (current == "" ? "" : $", {prefix}") + alias);
-            toSend += $"`{prefix}{aliases}`: {command.Summary}{nl}";
-        }
-
-        Context.Channel.SendMessageAsync(toSend);
-        return Task.CompletedTask;
-    }
-}
\ No newline at end of file
diff --git a/Boyfriend/Commands/KickCommand.cs b/Boyfriend/Commands/KickCommand.cs
new file mode 100644
index 0000000..ac7038b
--- /dev/null
+++ b/Boyfriend/Commands/KickCommand.cs
@@ -0,0 +1,47 @@
+using Discord;
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable ClassNeverInstantiated.Global
+
+namespace Boyfriend.Commands;
+
+public class KickCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        var reason = Utils.JoinString(args, 1);
+        var author = context.Guild.GetUser(context.User.Id);
+        var toKick = await Utils.ParseMember(context.Guild, args[0]);
+        await CommandHandler.CheckPermissions(author, GuildPermission.KickMembers);
+        await CommandHandler.CheckInteractions(author, toKick);
+        KickMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toKick,
+            reason);
+    }
+
+    private static async void KickMember(IGuild guild, ITextChannel? channel, IUser author, IGuildUser toKick,
+        string reason) {
+        var authorMention = author.Mention;
+        await Utils.SendDirectMessage(toKick, string.Format(Messages.YouWereKicked, authorMention, guild.Name,
+            Utils.WrapInline(reason)));
+        var guildKickMessage = $"({author.Username}#{author.Discriminator}) {reason}";
+        await toKick.KickAsync(guildKickMessage);
+        var notification = string.Format(Messages.MemberKicked, authorMention, toKick.Mention,
+            Utils.WrapInline(reason));
+        await Utils.SilentSendAsync(channel, string.Format(Messages.KickResponse, toKick.Mention,
+            Utils.WrapInline(reason)));
+        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
+        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"kick", "кик"};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 2;
+    }
+
+    public override string GetSummary() {
+        return "Выгоняет участника";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/KickModule.cs b/Boyfriend/Commands/KickModule.cs
deleted file mode 100644
index 67fb9fd..0000000
--- a/Boyfriend/Commands/KickModule.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Discord;
-using Discord.Commands;
-
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-// ReSharper disable ClassNeverInstantiated.Global
-
-namespace Boyfriend.Commands;
-
-public class KickModule : ModuleBase<SocketCommandContext> {
-
-    [Command("kick")]
-    [Summary("Выгоняет пользователя")]
-    [Alias("кик")]
-    public async Task Run(string user, [Remainder]string reason) {
-        var author = Context.Guild.GetUser(Context.User.Id);
-        var toKick = await Utils.ParseMember(Context.Guild, user);
-        await CommandHandler.CheckPermissions(author, GuildPermission.KickMembers);
-        await CommandHandler.CheckInteractions(author, toKick);
-        KickMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toKick, reason);
-    }
-
-    private static async void KickMember(IGuild guild, IUser author, IGuildUser toKick, string reason) {
-        var authorMention = author.Mention;
-        await Utils.SendDirectMessage(toKick, $"Тебя кикнул {authorMention} на сервере {guild.Name} за " +
-                                              $"{Utils.WrapInline(reason)}");
-
-        var guildKickMessage = $"({author.Username}#{author.Discriminator}) {reason}";
-        await toKick.KickAsync(guildKickMessage);
-        var notification = $"{authorMention} выгоняет {toKick.Mention} за {Utils.WrapInline(reason)}";
-        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
-        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
-    }
-}
\ No newline at end of file
diff --git a/Boyfriend/Commands/MuteCommand.cs b/Boyfriend/Commands/MuteCommand.cs
new file mode 100644
index 0000000..d9832fc
--- /dev/null
+++ b/Boyfriend/Commands/MuteCommand.cs
@@ -0,0 +1,92 @@
+using Discord;
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable ClassNeverInstantiated.Global
+
+namespace Boyfriend.Commands;
+
+public class MuteCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        TimeSpan duration;
+        var reason = Utils.JoinString(args, 1);
+        try {
+            duration = Utils.GetTimeSpan(args[1]);
+            reason = Utils.JoinString(args, 2);
+        }
+        catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
+            duration = TimeSpan.FromMilliseconds(-1);
+        }
+
+        var author = context.Guild.GetUser(context.User.Id);
+        var toMute = await Utils.ParseMember(context.Guild, args[0]);
+        if (toMute == null)
+            throw new ApplicationException(Messages.UserNotInGuild);
+        var role = Utils.GetMuteRole(context.Guild);
+        if (role != null && toMute.RoleIds.Any(x => x == role.Id) ||
+            toMute.TimedOutUntil != null && toMute.TimedOutUntil.Value.ToUnixTimeMilliseconds()
+            > DateTimeOffset.Now.ToUnixTimeMilliseconds())
+            throw new ApplicationException(Messages.MemberAlreadyMuted);
+        var rolesRemoved = Boyfriend.GetGuildConfig(context.Guild).RolesRemovedOnMute;
+        if (rolesRemoved.ContainsKey(toMute.Id)) {
+            foreach (var roleId in rolesRemoved[toMute.Id]) await toMute.AddRoleAsync(roleId);
+            rolesRemoved.Remove(toMute.Id);
+            await Warn(context.Channel, Messages.RolesReturned);
+            return;
+        }
+
+        await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
+        await CommandHandler.CheckInteractions(author, toMute);
+        MuteMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toMute,
+            duration, reason);
+    }
+
+    private static async void MuteMember(IGuild guild, ITextChannel? channel, IGuildUser author, IGuildUser toMute,
+        TimeSpan duration, string reason) {
+        await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
+        var authorMention = author.Mention;
+        var role = Utils.GetMuteRole(guild);
+        var config = Boyfriend.GetGuildConfig(guild);
+        if (config.RemoveRolesOnMute && role != null) {
+            var rolesRemoved = new List<ulong>();
+            try {
+                foreach (var roleId in toMute.RoleIds) {
+                    if (roleId == guild.Id) continue;
+                    await toMute.RemoveRoleAsync(roleId);
+                    rolesRemoved.Add(roleId);
+                }
+            }
+            catch (NullReferenceException) { }
+
+            config.RolesRemovedOnMute.Add(toMute.Id, rolesRemoved);
+            await config.Save();
+        }
+
+        if (role != null)
+            await toMute.AddRoleAsync(role);
+        else
+            await toMute.SetTimeOutAsync(duration);
+        var notification = string.Format(Messages.MemberMuted, authorMention, toMute.Mention, Utils.WrapInline(reason));
+        await Utils.SilentSendAsync(channel, string.Format(Messages.MuteResponse, toMute.Mention,
+            Utils.WrapInline(reason)));
+        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
+        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
+        var task = new Task(() => UnmuteCommand.UnmuteMember(guild, null, guild.GetCurrentUserAsync().Result, toMute,
+            Messages.PunishmentExpired));
+        if (role != null)
+            await Utils.StartDelayed(task, duration, () => toMute.RoleIds.Any(x => x == role.Id));
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"mute", "мут", "мьют"};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 2;
+    }
+
+    public override string GetSummary() {
+        return "Глушит участника";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/MuteModule.cs b/Boyfriend/Commands/MuteModule.cs
deleted file mode 100644
index 24a67d5..0000000
--- a/Boyfriend/Commands/MuteModule.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using Discord;
-using Discord.Commands;
-
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-// ReSharper disable ClassNeverInstantiated.Global
-
-namespace Boyfriend.Commands;
-
-public class MuteModule : ModuleBase<SocketCommandContext> {
-
-    [Command("mute")]
-    [Summary("Глушит пользователя")]
-    [Alias("мут")]
-    public async Task Run(string user, [Remainder]string reason) {
-        TimeSpan duration;
-        try {
-            var reasonArray = reason.Split();
-            duration = Utils.GetTimeSpan(reasonArray[0]);
-            reason = string.Join(" ", reasonArray.Skip(1));
-        } catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) {
-            duration = TimeSpan.FromMilliseconds(-1);
-        }
-        var author = Context.Guild.GetUser(Context.User.Id);
-        var toMute = await Utils.ParseMember(Context.Guild, user);
-        if (toMute.RoleIds.Any(x => x == Utils.GetMuteRole(Context.Guild).Id))
-            throw new Exception("Участник уже заглушен!");
-        if (Boyfriend.GetGuildConfig(Context.Guild).RolesRemovedOnMute.ContainsKey(toMute.Id))
-            throw new Exception("Кто-то убрал роль мута самостоятельно!");
-        await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
-        await CommandHandler.CheckInteractions(author, toMute);
-        MuteMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toMute, duration, reason);
-    }
-
-    private static async void MuteMember(IGuild guild, IGuildUser author, IGuildUser toMute, TimeSpan duration,
-        string reason) {
-        await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
-        var authorMention = author.Mention;
-        var role = Utils.GetMuteRole(guild);
-        var config = Boyfriend.GetGuildConfig(guild);
-
-        if (config.RemoveRolesOnMute) {
-            var rolesRemoved = new List<ulong>();
-            try {
-                foreach (var roleId in toMute.RoleIds) {
-                    if (roleId == guild.Id) continue;
-                    await toMute.RemoveRoleAsync(roleId);
-                    rolesRemoved.Add(roleId);
-                }
-            } catch (NullReferenceException) {}
-            config.RolesRemovedOnMute.Add(toMute.Id, rolesRemoved);
-            config.Save();
-        }
-
-        await toMute.AddRoleAsync(role);
-        var notification = $"{authorMention} глушит {toMute.Mention} за {Utils.WrapInline(reason)}";
-        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
-        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
-        var task = new Task(() => UnmuteModule.UnmuteMember(guild, guild.GetCurrentUserAsync().Result, toMute,
-            "Время наказания истекло"));
-        await Utils.StartDelayed(task, duration, () => toMute.RoleIds.Any(x => x == role.Id));
-    }
-}
\ No newline at end of file
diff --git a/Boyfriend/Commands/PingCommand.cs b/Boyfriend/Commands/PingCommand.cs
new file mode 100644
index 0000000..ce8100a
--- /dev/null
+++ b/Boyfriend/Commands/PingCommand.cs
@@ -0,0 +1,25 @@
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+
+namespace Boyfriend.Commands;
+
+public class PingCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        await context.Channel.SendMessageAsync($"{Utils.GetBeep(Boyfriend.GetGuildConfig(context.Guild).Lang)}" +
+                                               $"{Boyfriend.Client.Latency}{Messages.Milliseconds}");
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"ping", "пинг", "задержка"};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 0;
+    }
+
+    public override string GetSummary() {
+        return "Измеряет время обработки REST-запроса";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/PingModule.cs b/Boyfriend/Commands/PingModule.cs
deleted file mode 100644
index 6c86073..0000000
--- a/Boyfriend/Commands/PingModule.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Discord.Commands;
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-
-namespace Boyfriend.Commands;
-
-public class PingModule : ModuleBase<SocketCommandContext> {
-
-    [Command("ping")]
-    [Summary("Измеряет время обработки REST-запроса")]
-    [Alias("пинг")]
-    public async Task Run()
-        => await ReplyAsync($"{Utils.GetBeep()}{Boyfriend.Client.Latency}мс");
-}
\ No newline at end of file
diff --git a/Boyfriend/Commands/SettingsCommand.cs b/Boyfriend/Commands/SettingsCommand.cs
new file mode 100644
index 0000000..3902077
--- /dev/null
+++ b/Boyfriend/Commands/SettingsCommand.cs
@@ -0,0 +1,125 @@
+using System.Globalization;
+using Discord;
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+
+namespace Boyfriend.Commands;
+
+public class SettingsCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        await CommandHandler.CheckPermissions(context.Guild.GetUser(context.User.Id), GuildPermission.ManageGuild);
+        var config = Boyfriend.GetGuildConfig(context.Guild);
+        var guild = context.Guild;
+        if (args.Length == 0) {
+            var nl = Environment.NewLine;
+            var adminLogChannel = guild.GetTextChannel(config.AdminLogChannel);
+            var admin = adminLogChannel == null ? Messages.ChannelNotSpecified : adminLogChannel.Mention;
+            var botLogChannel = guild.GetTextChannel(config.BotLogChannel);
+            var bot = botLogChannel == null ? Messages.ChannelNotSpecified : botLogChannel.Mention;
+            var muteRole = guild.GetRole(config.MuteRole);
+            var mute = muteRole == null ? Messages.RoleNotSpecified : muteRole.Mention;
+            var defaultRole = guild.GetRole(config.DefaultRole);
+            var defaultr = muteRole == null ? Messages.RoleNotSpecified : defaultRole.Mention;
+            var toSend = string.Format(Messages.CurrentSettings, nl) +
+                         string.Format(Messages.CurrentSettingsLang, config.Lang, nl) +
+                         string.Format(Messages.CurrentSettingsPrefix, config.Prefix, nl) +
+                         string.Format(Messages.CurrentSettingsRemoveRoles, YesOrNo(config.RemoveRolesOnMute), nl) +
+                         string.Format(Messages.CurrentSettingsUseSystemChannel, YesOrNo(config.UseSystemChannel), nl) +
+                         string.Format(Messages.CurrentSettingsSendWelcomeMessages, YesOrNo(config.UseSystemChannel),
+                             nl) +
+                         string.Format(Messages.CurrentSettingsWelcomeMessage, config.WelcomeMessage, nl) +
+                         string.Format(Messages.CurrentSettingsDefaultRole, defaultr, nl) +
+                         string.Format(Messages.CurrentSettingsMuteRole, mute, nl) +
+                         string.Format(Messages.CurrentSettingsAdminLogChannel, admin, nl) +
+                         string.Format(Messages.CurrentSettingsBotLogChannel, bot);
+            await Utils.SilentSendAsync(context.Channel as ITextChannel ?? throw new Exception(), toSend);
+            return;
+        }
+
+        var setting = args[0].ToLower();
+        var value = args[1].ToLower();
+
+        var boolValue = ParseBool(args[1]);
+        var channel = await Utils.ParseChannelNullable(value) as IGuildChannel;
+        var role = Utils.ParseRoleNullable(guild, value);
+
+        switch (setting) {
+            case "lang" when value is not ("ru" or "en"):
+                throw new Exception(Messages.LanguageNotSupported);
+            case "lang":
+                config.Lang = value;
+                Messages.Culture = new CultureInfo(value);
+                break;
+            case "prefix":
+                config.Prefix = value;
+                break;
+            case "removerolesonmute":
+                config.RemoveRolesOnMute = GetBoolValue(boolValue);
+                break;
+            case "usesystemchannel":
+                config.UseSystemChannel = GetBoolValue(boolValue);
+                break;
+            case "sendwelcomemessages":
+                config.SendWelcomeMessages = GetBoolValue(boolValue);
+                break;
+            case "welcomemessage":
+                config.WelcomeMessage = value;
+                break;
+            case "defaultrole":
+                config.DefaultRole = GetRoleId(role);
+                break;
+            case "muterole":
+                config.MuteRole = GetRoleId(role);
+                break;
+            case "adminlogchannel":
+                config.AdminLogChannel = GetChannelId(channel);
+                break;
+            case "botlogchannel":
+                config.BotLogChannel = GetChannelId(channel);
+                break;
+        }
+
+        await config.Save();
+
+        await context.Channel.SendMessageAsync(Messages.SettingsUpdated);
+    }
+
+    private static bool? ParseBool(string toParse) {
+        try {
+            return bool.Parse(toParse.ToLower());
+        }
+        catch (FormatException) {
+            return null;
+        }
+    }
+
+    private static bool GetBoolValue(bool? from) {
+        return from ?? throw new Exception(Messages.InvalidBoolean);
+    }
+
+    private static ulong GetRoleId(IRole? role) {
+        return (role ?? throw new Exception(Messages.InvalidRoleSpecified)).Id;
+    }
+
+    private static ulong GetChannelId(IGuildChannel? channel) {
+        return (channel ?? throw new Exception(Messages.InvalidChannelSpecified)).Id;
+    }
+
+    private static string YesOrNo(bool isYes) {
+        return isYes ? Messages.Yes : Messages.No;
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"settings", "настройки", "config", "конфиг "};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 0;
+    }
+
+    public override string GetSummary() {
+        return "Настраивает бота отдельно для этого сервера";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/SettingsModule.cs b/Boyfriend/Commands/SettingsModule.cs
deleted file mode 100644
index dd25c56..0000000
--- a/Boyfriend/Commands/SettingsModule.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using Discord;
-using Discord.Commands;
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-
-namespace Boyfriend.Commands;
-
-public class SettingsModule : ModuleBase<SocketCommandContext> {
-
-    [Command("settings")]
-    [Summary("Настраивает бота")]
-    [Alias("config", "настройки", "конфиг")]
-    public async Task Run([Remainder] string s = "") {
-        await CommandHandler.CheckPermissions(Context.Guild.GetUser(Context.User.Id), GuildPermission.ManageGuild);
-        var config = Boyfriend.GetGuildConfig(Context.Guild);
-        var sArray = s.Split(" ");
-        var guild = Context.Guild;
-        if (s == "") {
-            var nl = Environment.NewLine;
-            var adminLogChannel = guild.GetTextChannel(config.AdminLogChannel);
-            var admin = adminLogChannel == null ? "Не указан" : adminLogChannel.Mention;
-            var botLogChannel = guild.GetTextChannel(config.BotLogChannel);
-            var bot = botLogChannel == null ? "Не указан" : botLogChannel.Mention;
-            var muteRole = guild.GetRole(config.MuteRole);
-            var mute = muteRole == null ? "Не указана" : muteRole.Mention;
-            var toSend = $"Текущие настройки:{nl}" +
-                         $"Язык (`lang`): `{config.Lang}`{nl}" +
-                         $"Префикс (`prefix`): `{config.Prefix}`{nl}" +
-                         $"Удалять роли при муте (`removeRolesOnMute`): {YesOrNo(config.RemoveRolesOnMute)}{nl}" +
-                         "Использовать канал системных сообщений для уведомлений (`useSystemChannel`): " +
-                         $"{YesOrNo(config.UseSystemChannel)}{nl}" +
-                         $"Отправлять приветствия (`sendWelcomeMessages`): {YesOrNo(config.UseSystemChannel)}{nl}" +
-                         $"Роль мута (`muteRole`): {mute}{nl}" +
-                         $"Канал админ-уведомлений (`adminLogChannel`): " +
-                         $"{admin}{nl}" +
-                         $"Канал бот-уведомлений (`botLogChannel`): " +
-                         $"{bot}";
-            await Utils.SilentSendAsync(Context.Channel as ITextChannel ?? throw new Exception(), toSend);
-            return;
-        }
-
-        var setting = sArray[0].ToLower();
-        var value = sArray[1].ToLower();
-
-        ITextChannel? channel;
-        try {
-            channel = await Utils.ParseChannel(value) as ITextChannel;
-        } catch (FormatException) {
-            channel = null;
-        }
-
-        IRole? role;
-        try {
-            role = Utils.ParseRole(guild, value);
-        }
-        catch (FormatException) {
-            role = null;
-        }
-
-        var boolValue = ParseBool(sArray[1]);
-
-        switch (setting) {
-            case "lang" when sArray[1].ToLower() != "ru":
-                throw new Exception("Язык не поддерживается!");
-            case "lang":
-                config.Lang = value;
-                break;
-            case "prefix":
-                config.Prefix = value;
-                break;
-            case "removerolesonmute":
-                config.RemoveRolesOnMute = boolValue ??
-                                           throw new Exception("Неверный параметр! Требуется `true` или `false");
-                break;
-            case "usesystemchannel":
-                config.UseSystemChannel = boolValue ??
-                                          throw new Exception("Неверный параметр! Требуется `true` или `false");
-                break;
-            case "sendwelcomemessages":
-                config.SendWelcomeMessages = boolValue ??
-                                             throw new Exception("Неверный параметр! Требуется `true` или `false");
-                break;
-            case "adminlogchannel":
-                config.AdminLogChannel = Convert.ToUInt64((channel ??
-                                                           throw new Exception("Указан недействительный канал!"))
-                    .Id);
-                break;
-            case "botlogchannel":
-                config.BotLogChannel = Convert.ToUInt64((channel ??
-                                                           throw new Exception("Указан недействительный канал!"))
-                    .Id);
-                break;
-            case "muterole":
-                config.MuteRole = Convert.ToUInt64((role ?? throw new Exception("Указана недействительная роль!"))
-                    .Id);
-                break;
-        }
-
-        config.Save();
-
-        await Context.Channel.SendMessageAsync("Настройки успешно обновлены!");
-    }
-
-    private static bool? ParseBool(string toParse) {
-        try {
-            return bool.Parse(toParse.ToLower());
-        } catch (FormatException) {
-            return null;
-        }
-    }
-
-    private static string YesOrNo(bool isYes) {
-        return isYes ? "Да" : "Нет";
-    }
-}
\ No newline at end of file
diff --git a/Boyfriend/Commands/UnbanCommand.cs b/Boyfriend/Commands/UnbanCommand.cs
new file mode 100644
index 0000000..c8b9fef
--- /dev/null
+++ b/Boyfriend/Commands/UnbanCommand.cs
@@ -0,0 +1,43 @@
+using Discord;
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable ClassNeverInstantiated.Global
+
+namespace Boyfriend.Commands;
+
+public class UnbanCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        var toUnban = await Utils.ParseUser(args[0]);
+        if (context.Guild.GetBanAsync(toUnban.Id) == null)
+            throw new Exception(Messages.UserNotBanned);
+        UnbanUser(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toUnban,
+            Utils.JoinString(args, 1));
+    }
+
+    public static async void UnbanUser(IGuild guild, ITextChannel? channel, IGuildUser author, IUser toUnban,
+        string reason) {
+        await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
+        var authorMention = author.Mention;
+        var notification = string.Format(Messages.UserUnbanned, authorMention, toUnban.Mention,
+            Utils.WrapInline(reason));
+        await guild.RemoveBanAsync(toUnban);
+        await Utils.SilentSendAsync(channel, string.Format(Messages.UnbanResponse, toUnban.Mention,
+            Utils.WrapInline(reason)));
+        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
+        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"unban", "разбан"};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 2;
+    }
+
+    public override string GetSummary() {
+        return "Возвращает пользователя из бана";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/UnbanModule.cs b/Boyfriend/Commands/UnbanModule.cs
deleted file mode 100644
index 33b5902..0000000
--- a/Boyfriend/Commands/UnbanModule.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Discord;
-using Discord.Commands;
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-// ReSharper disable ClassNeverInstantiated.Global
-
-namespace Boyfriend.Commands;
-
-public class UnbanModule : ModuleBase<SocketCommandContext> {
-
-    [Command("unban")]
-    [Summary("Возвращает пользователя из бана")]
-    [Alias("разбан")]
-    public async Task Run(string user, [Remainder] string reason) {
-        var toUnban = await Utils.ParseUser(user);
-        if (Context.Guild.GetBanAsync(toUnban.Id) == null)
-            throw new Exception("Пользователь не забанен!");
-        UnbanUser(Context.Guild, Context.Guild.GetUser(Context.User.Id), toUnban, reason);
-    }
-
-    public static async void UnbanUser(IGuild guild, IGuildUser author, IUser toUnban, string reason) {
-        await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers);
-        var authorMention = author.Mention;
-        var notification = $"{authorMention} возвращает из бана {toUnban.Mention} за {Utils.WrapInline(reason)}";
-        await guild.RemoveBanAsync(toUnban);
-        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
-        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
-    }
-}
\ No newline at end of file
diff --git a/Boyfriend/Commands/UnmuteCommand.cs b/Boyfriend/Commands/UnmuteCommand.cs
new file mode 100644
index 0000000..c416807
--- /dev/null
+++ b/Boyfriend/Commands/UnmuteCommand.cs
@@ -0,0 +1,61 @@
+using Discord;
+using Discord.Commands;
+
+// ReSharper disable UnusedType.Global
+// ReSharper disable UnusedMember.Global
+// ReSharper disable ClassNeverInstantiated.Global
+
+namespace Boyfriend.Commands;
+
+public class UnmuteCommand : Command {
+    public override async Task Run(SocketCommandContext context, string[] args) {
+        var toUnmute = await Utils.ParseMember(context.Guild, args[0]);
+        var author = context.Guild.GetUser(context.User.Id);
+        await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
+        await CommandHandler.CheckInteractions(author, toUnmute);
+        var role = Utils.GetMuteRole(context.Guild);
+        if (role != null)
+            if (toUnmute.RoleIds.All(x => x != role.Id)) {
+                var rolesRemoved = Boyfriend.GetGuildConfig(context.Guild).RolesRemovedOnMute;
+
+                foreach (var roleId in rolesRemoved[toUnmute.Id]) await toUnmute.AddRoleAsync(roleId);
+                rolesRemoved.Remove(toUnmute.Id);
+                throw new ApplicationException(Messages.RolesReturned);
+            }
+
+        UnmuteMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id),
+            toUnmute, Utils.JoinString(args, 1));
+    }
+
+    public static async void UnmuteMember(IGuild guild, ITextChannel? channel, IGuildUser author, IGuildUser toUnmute,
+        string reason) {
+        await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
+        var authorMention = author.Mention;
+        var notification = string.Format(Messages.MemberUnmuted, authorMention, toUnmute.Mention,
+            Utils.WrapInline(reason));
+        await toUnmute.RemoveRoleAsync(Utils.GetMuteRole(guild));
+        var config = Boyfriend.GetGuildConfig(guild);
+
+        if (config.RolesRemovedOnMute.ContainsKey(toUnmute.Id)) {
+            foreach (var roleId in config.RolesRemovedOnMute[toUnmute.Id]) await toUnmute.AddRoleAsync(roleId);
+            config.RolesRemovedOnMute.Remove(toUnmute.Id);
+        }
+
+        await Utils.SilentSendAsync(channel, string.Format(Messages.UnmuteResponse, toUnmute.Mention,
+            Utils.WrapInline(reason)));
+        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
+        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
+    }
+
+    public override List<string> GetAliases() {
+        return new List<string> {"unmute", "размут"};
+    }
+
+    public override int GetArgumentsAmountRequired() {
+        return 2;
+    }
+
+    public override string GetSummary() {
+        return "Снимает мут с участника";
+    }
+}
\ No newline at end of file
diff --git a/Boyfriend/Commands/UnmuteModule.cs b/Boyfriend/Commands/UnmuteModule.cs
deleted file mode 100644
index 9baee78..0000000
--- a/Boyfriend/Commands/UnmuteModule.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using Discord;
-using Discord.Commands;
-// ReSharper disable UnusedType.Global
-// ReSharper disable UnusedMember.Global
-// ReSharper disable ClassNeverInstantiated.Global
-
-namespace Boyfriend.Commands;
-
-public class UnmuteModule : ModuleBase<SocketCommandContext> {
-
-    [Command("unmute")]
-    [Summary("Возвращает пользователя из мута")]
-    [Alias("размут")]
-    public async Task Run(string user, [Remainder] string reason) {
-        var toUnmute = await Utils.ParseMember(Context.Guild, user);
-        var author = Context.Guild.GetUser(Context.User.Id);
-        await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
-        await CommandHandler.CheckInteractions(author, toUnmute);
-        if (toUnmute.RoleIds.All(x => x != Utils.GetMuteRole(Context.Guild).Id)) {
-            var rolesRemoved = Boyfriend.GetGuildConfig(Context.Guild).RolesRemovedOnMute;
-            if (!rolesRemoved.ContainsKey(toUnmute.Id)) throw new Exception("Пользователь не в муте!");
-            rolesRemoved.Remove(toUnmute.Id);
-            throw new Exception("Пользователь не в муте, но я нашёл и удалил запись о его удалённых ролях!");
-        }
-
-        UnmuteMember(Context.Guild, Context.Guild.GetUser(Context.User.Id), toUnmute, reason);
-    }
-
-    public static async void UnmuteMember(IGuild guild, IGuildUser author, IGuildUser toUnmute, string reason) {
-        await CommandHandler.CheckPermissions(author, GuildPermission.ManageMessages, GuildPermission.ManageRoles);
-        var authorMention = author.Mention;
-        var notification = $"{authorMention} возвращает из мута {toUnmute.Mention} за {Utils.WrapInline(reason)}";
-        await toUnmute.RemoveRoleAsync(Utils.GetMuteRole(guild));
-        var config = Boyfriend.GetGuildConfig(guild);
-
-        if (config.RolesRemovedOnMute.ContainsKey(toUnmute.Id)) {
-            foreach (var roleId in config.RolesRemovedOnMute[toUnmute.Id]) {
-                await toUnmute.AddRoleAsync(roleId);
-            }
-            config.RolesRemovedOnMute.Remove(toUnmute.Id);
-        }
-
-        await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification);
-        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(guild), notification);
-    }
-}
\ No newline at end of file
diff --git a/Boyfriend/EventHandler.cs b/Boyfriend/EventHandler.cs
index 05d6fb2..fdae9d2 100644
--- a/Boyfriend/EventHandler.cs
+++ b/Boyfriend/EventHandler.cs
@@ -1,4 +1,4 @@
-using System.Reflection;
+using System.Globalization;
 using Boyfriend.Commands;
 using Discord;
 using Discord.Commands;
@@ -8,24 +8,25 @@ namespace Boyfriend;
 
 public class EventHandler {
     private readonly DiscordSocketClient _client = Boyfriend.Client;
-    public static readonly CommandService Commands = new();
 
-    public async Task InitEvents() {
+    public void InitEvents() {
         _client.Ready += ReadyEvent;
         _client.MessageDeleted += MessageDeletedEvent;
         _client.MessageReceived += MessageReceivedEvent;
         _client.MessageUpdated += MessageUpdatedEvent;
         _client.UserJoined += UserJoinedEvent;
-        await Commands.AddModulesAsync(Assembly.GetEntryAssembly(), null);
     }
 
     private static async Task ReadyEvent() {
         await Boyfriend.SetupGuildConfigs();
 
+        var i = new Random().Next(3);
         foreach (var guild in Boyfriend.Client.Guilds) {
-            var channel = guild.GetTextChannel(Boyfriend.GetGuildConfig(guild).BotLogChannel);
+            var config = Boyfriend.GetGuildConfig(guild);
+            Messages.Culture = new CultureInfo(config.Lang);
+            var channel = guild.GetTextChannel(config.BotLogChannel);
             if (channel == null) continue;
-            await channel.SendMessageAsync($"{Utils.GetBeep()}Я запустился! (C#)");
+            await channel.SendMessageAsync(string.Format(Messages.Ready, Utils.GetBeep(config.Lang, i)));
         }
     }
 
@@ -33,13 +34,11 @@ public class EventHandler {
         Cacheable<IMessageChannel, ulong> channel) {
         var msg = message.Value;
         var toSend = msg == null
-            ? $"Удалено сообщение в канале {Utils.MentionChannel(channel.Id)}, но я забыл что там было"
-            : $"Удалено сообщение от {msg.Author.Mention} в канале " +
+            ? string.Format(Messages.UncachedMessageDeleted, Utils.MentionChannel(channel.Id))
+            : string.Format(Messages.CachedMessageDeleted, msg.Author.Mention) +
               $"{Utils.MentionChannel(channel.Id)}: {Environment.NewLine}{Utils.Wrap(msg.Content)}";
-        try {
-            await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(
-                    Boyfriend.FindGuild(channel.Value as ITextChannel)), toSend);
-        } catch (ArgumentException) {}
+        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(
+            Boyfriend.FindGuild(channel.Value)), toSend);
     }
 
     private static async Task MessageReceivedEvent(SocketMessage messageParam) {
@@ -48,45 +47,54 @@ public class EventHandler {
         var guild = user.Guild;
         var argPos = 0;
 
+        var guildConfig = Boyfriend.GetGuildConfig(guild);
+        Messages.Culture = new CultureInfo(guildConfig.Lang);
         if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2)
             && !user.GuildPermissions.MentionEveryone)
-            BanModule.BanUser(guild, await guild.GetCurrentUserAsync(), user, TimeSpan.FromMilliseconds(-1),
-                "Более 3-ёх упоминаний в одном сообщении");
+            await BanCommand.BanUser(guild, null, await guild.GetCurrentUserAsync(), user,
+                TimeSpan.FromMilliseconds(-1), Messages.AutobanReason);
 
         var prevs = await message.Channel.GetMessagesAsync(3).FlattenAsync();
         var prevsArray = prevs as IMessage[] ?? prevs.ToArray();
-        var prev = prevsArray[1].Content;
-        var prevFailsafe = prevsArray[2].Content;
-        if (message.Channel is not ITextChannel channel) throw new Exception();
-        if (!(message.HasStringPrefix(Boyfriend.GetGuildConfig(guild).Prefix, ref argPos)
+        var prev = "";
+        var prevFailsafe = "";
+        try {
+            prev = prevsArray[1].Content;
+            prevFailsafe = prevsArray[2].Content;
+        }
+        catch (IndexOutOfRangeException) { }
+
+        if (!(message.HasStringPrefix(guildConfig.Prefix, ref argPos)
               || message.HasMentionPrefix(Boyfriend.Client.CurrentUser, ref argPos))
-            || user == await Boyfriend.FindGuild(channel).GetCurrentUserAsync()
+            || user == await guild.GetCurrentUserAsync()
             || user.IsBot && message.Content.Contains(prev) || message.Content.Contains(prevFailsafe))
             return;
 
-        await CommandHandler.HandleCommand(message, argPos);
+        await CommandHandler.HandleCommand(message);
     }
 
     private static async Task MessageUpdatedEvent(Cacheable<IMessage, ulong> messageCached, SocketMessage messageSocket,
         ISocketMessageChannel channel) {
         var msg = messageCached.Value;
         var nl = Environment.NewLine;
-        if (msg.Content == messageSocket.Content) return;
+        if (msg != null && msg.Content == messageSocket.Content) return;
         var toSend = msg == null
-            ? $"Отредактировано сообщение от {messageSocket.Author.Mention} в канале" +
-              $" {Utils.MentionChannel(channel.Id)}," + " но я забыл что там было до редактирования: " +
+            ? string.Format(Messages.UncachedMessageEdited, messageSocket.Author.Mention,
+                  Utils.MentionChannel(channel.Id)) +
               Utils.Wrap(messageSocket.Content)
-            : $"Отредактировано сообщение от {msg.Author.Mention} " +
-              $"в канале {Utils.MentionChannel(channel.Id)}." +
-              $"{nl}До:{nl}{Utils.Wrap(msg.Content)}{nl}После:{nl}{Utils.Wrap(messageSocket.Content)}";
-        try {
-            await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel as ITextChannel)),
+            : string.Format(Messages.CachedMessageEdited, msg.Author.Mention, Utils.MentionChannel(channel.Id), nl, nl,
+                Utils.Wrap(msg.Content), nl, nl, Utils.Wrap(messageSocket.Content));
+        await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(Boyfriend.FindGuild(channel)),
                 toSend);
-        } catch (ArgumentException) {}
     }
 
     private static async Task UserJoinedEvent(SocketGuildUser user) {
         var guild = user.Guild;
-        await guild.SystemChannel.SendMessageAsync($"{user.Mention}, добро пожаловать на сервер {guild.Name}");
+        var config = Boyfriend.GetGuildConfig(guild);
+        if (config.SendWelcomeMessages)
+            await Utils.SilentSendAsync(guild.SystemChannel, string.Format(config.WelcomeMessage, user.Mention,
+                guild.Name));
+        if (config.DefaultRole != 0)
+            await user.AddRoleAsync(Utils.ParseRole(guild, config.DefaultRole.ToString()));
     }
 }
\ No newline at end of file
diff --git a/Boyfriend/GuildConfig.cs b/Boyfriend/GuildConfig.cs
index 80490de..1238579 100644
--- a/Boyfriend/GuildConfig.cs
+++ b/Boyfriend/GuildConfig.cs
@@ -9,26 +9,31 @@ public class GuildConfig {
     public bool RemoveRolesOnMute { get; set; }
     public bool UseSystemChannel { get; set; }
     public bool SendWelcomeMessages { get; set; }
+    public string WelcomeMessage { get; set; }
+    public ulong DefaultRole { get; set; }
     public ulong MuteRole { get; set; }
     public ulong AdminLogChannel { get; set; }
     public ulong BotLogChannel { get; set; }
     public Dictionary<ulong, List<ulong>> RolesRemovedOnMute { get; set; }
 
     public GuildConfig(ulong id, string lang, string prefix, bool removeRolesOnMute, bool useSystemChannel,
-        bool sendWelcomeMessages, ulong muteRole, ulong adminLogChannel, ulong botLogChannel) {
+        bool sendWelcomeMessages, string welcomeMessage, ulong defaultRole, ulong muteRole, ulong adminLogChannel,
+        ulong botLogChannel) {
         Id = id;
         Lang = lang;
         Prefix = prefix;
         RemoveRolesOnMute = removeRolesOnMute;
         UseSystemChannel = useSystemChannel;
         SendWelcomeMessages = sendWelcomeMessages;
+        WelcomeMessage = welcomeMessage;
+        DefaultRole = defaultRole;
         MuteRole = muteRole;
         AdminLogChannel = adminLogChannel;
         BotLogChannel = botLogChannel;
         RolesRemovedOnMute = new Dictionary<ulong, List<ulong>>();
     }
 
-    public async void Save() {
+    public async Task Save() {
         await using var stream = File.OpenWrite("config_" + Id + ".json");
         await JsonSerializer.SerializeAsync(stream, this);
     }
diff --git a/Boyfriend/Messages.Designer.cs b/Boyfriend/Messages.Designer.cs
new file mode 100644
index 0000000..6538678
--- /dev/null
+++ b/Boyfriend/Messages.Designer.cs
@@ -0,0 +1,486 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Boyfriend {
+    using System;
+    
+    
+    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Messages {
+        
+        private static System.Resources.ResourceManager resourceMan;
+        
+        private static System.Globalization.CultureInfo resourceCulture;
+        
+        [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Messages() {
+        }
+        
+        [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.Equals(null, resourceMan)) {
+                    System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Boyfriend.Messages", typeof(Messages).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        internal static string CouldntFindGuildByChannel {
+            get {
+                return ResourceManager.GetString("CouldntFindGuildByChannel", resourceCulture);
+            }
+        }
+        
+        internal static string Ready {
+            get {
+                return ResourceManager.GetString("Ready", resourceCulture);
+            }
+        }
+        
+        internal static string UncachedMessageDeleted {
+            get {
+                return ResourceManager.GetString("UncachedMessageDeleted", resourceCulture);
+            }
+        }
+        
+        internal static string CachedMessageDeleted {
+            get {
+                return ResourceManager.GetString("CachedMessageDeleted", resourceCulture);
+            }
+        }
+        
+        internal static string AutobanReason {
+            get {
+                return ResourceManager.GetString("AutobanReason", resourceCulture);
+            }
+        }
+        
+        internal static string UncachedMessageEdited {
+            get {
+                return ResourceManager.GetString("UncachedMessageEdited", resourceCulture);
+            }
+        }
+        
+        internal static string CachedMessageEdited {
+            get {
+                return ResourceManager.GetString("CachedMessageEdited", resourceCulture);
+            }
+        }
+        
+        internal static string DefaultWelcomeMessage {
+            get {
+                return ResourceManager.GetString("DefaultWelcomeMessage", resourceCulture);
+            }
+        }
+        
+        internal static string Beep1 {
+            get {
+                return ResourceManager.GetString("Beep1", resourceCulture);
+            }
+        }
+        
+        internal static string Beep2 {
+            get {
+                return ResourceManager.GetString("Beep2", resourceCulture);
+            }
+        }
+        
+        internal static string Beep3 {
+            get {
+                return ResourceManager.GetString("Beep3", resourceCulture);
+            }
+        }
+        
+        internal static string InvalidAdminLogChannel {
+            get {
+                return ResourceManager.GetString("InvalidAdminLogChannel", resourceCulture);
+            }
+        }
+        
+        internal static string MuteRoleRequired {
+            get {
+                return ResourceManager.GetString("MuteRoleRequired", resourceCulture);
+            }
+        }
+        
+        internal static string CommandExecutionUnsuccessful {
+            get {
+                return ResourceManager.GetString("CommandExecutionUnsuccessful", resourceCulture);
+            }
+        }
+        
+        internal static string RepeatedArgumentsDetected {
+            get {
+                return ResourceManager.GetString("RepeatedArgumentsDetected", resourceCulture);
+            }
+        }
+        
+        internal static string CommandParseFailed {
+            get {
+                return ResourceManager.GetString("CommandParseFailed", resourceCulture);
+            }
+        }
+        
+        internal static string UnknownCommand {
+            get {
+                return ResourceManager.GetString("UnknownCommand", resourceCulture);
+            }
+        }
+        
+        internal static string BadArgumentCount {
+            get {
+                return ResourceManager.GetString("BadArgumentCount", resourceCulture);
+            }
+        }
+        
+        internal static string ArgumentNotPresent {
+            get {
+                return ResourceManager.GetString("ArgumentNotPresent", resourceCulture);
+            }
+        }
+        
+        internal static string CommandNoPermissionBot {
+            get {
+                return ResourceManager.GetString("CommandNoPermissionBot", resourceCulture);
+            }
+        }
+        
+        internal static string CommandNoPermissionUser {
+            get {
+                return ResourceManager.GetString("CommandNoPermissionUser", resourceCulture);
+            }
+        }
+        
+        internal static string InteractionsDifferentGuilds {
+            get {
+                return ResourceManager.GetString("InteractionsDifferentGuilds", resourceCulture);
+            }
+        }
+        
+        internal static string InteractionsOwner {
+            get {
+                return ResourceManager.GetString("InteractionsOwner", resourceCulture);
+            }
+        }
+        
+        internal static string InteractionsYourself {
+            get {
+                return ResourceManager.GetString("InteractionsYourself", resourceCulture);
+            }
+        }
+        
+        internal static string InteractionsMe {
+            get {
+                return ResourceManager.GetString("InteractionsMe", resourceCulture);
+            }
+        }
+        
+        internal static string InteractionsFailedUser {
+            get {
+                return ResourceManager.GetString("InteractionsFailedUser", resourceCulture);
+            }
+        }
+        
+        internal static string InteractionsFailedBot {
+            get {
+                return ResourceManager.GetString("InteractionsFailedBot", resourceCulture);
+            }
+        }
+        
+        internal static string YouWereBanned {
+            get {
+                return ResourceManager.GetString("YouWereBanned", resourceCulture);
+            }
+        }
+        
+        internal static string UserBanned {
+            get {
+                return ResourceManager.GetString("UserBanned", resourceCulture);
+            }
+        }
+        
+        internal static string PunishmentExpired {
+            get {
+                return ResourceManager.GetString("PunishmentExpired", resourceCulture);
+            }
+        }
+        
+        internal static string ClearNegativeAmount {
+            get {
+                return ResourceManager.GetString("ClearNegativeAmount", resourceCulture);
+            }
+        }
+        
+        internal static string ClearAmountTooLarge {
+            get {
+                return ResourceManager.GetString("ClearAmountTooLarge", resourceCulture);
+            }
+        }
+        
+        internal static string MessagesDeleted {
+            get {
+                return ResourceManager.GetString("MessagesDeleted", resourceCulture);
+            }
+        }
+        
+        internal static string CommandHelp {
+            get {
+                return ResourceManager.GetString("CommandHelp", resourceCulture);
+            }
+        }
+        
+        internal static string YouWereKicked {
+            get {
+                return ResourceManager.GetString("YouWereKicked", resourceCulture);
+            }
+        }
+        
+        internal static string MemberKicked {
+            get {
+                return ResourceManager.GetString("MemberKicked", resourceCulture);
+            }
+        }
+        
+        internal static string MemberMuted {
+            get {
+                return ResourceManager.GetString("MemberMuted", resourceCulture);
+            }
+        }
+        
+        internal static string Milliseconds {
+            get {
+                return ResourceManager.GetString("Milliseconds", resourceCulture);
+            }
+        }
+        
+        internal static string MemberAlreadyMuted {
+            get {
+                return ResourceManager.GetString("MemberAlreadyMuted", resourceCulture);
+            }
+        }
+        
+        internal static string MuteRoleManuallyRemoved {
+            get {
+                return ResourceManager.GetString("MuteRoleManuallyRemoved", resourceCulture);
+            }
+        }
+        
+        internal static string ChannelNotSpecified {
+            get {
+                return ResourceManager.GetString("ChannelNotSpecified", resourceCulture);
+            }
+        }
+        
+        internal static string RoleNotSpecified {
+            get {
+                return ResourceManager.GetString("RoleNotSpecified", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettings {
+            get {
+                return ResourceManager.GetString("CurrentSettings", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsLang {
+            get {
+                return ResourceManager.GetString("CurrentSettingsLang", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsPrefix {
+            get {
+                return ResourceManager.GetString("CurrentSettingsPrefix", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsRemoveRoles {
+            get {
+                return ResourceManager.GetString("CurrentSettingsRemoveRoles", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsUseSystemChannel {
+            get {
+                return ResourceManager.GetString("CurrentSettingsUseSystemChannel", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsSendWelcomeMessages {
+            get {
+                return ResourceManager.GetString("CurrentSettingsSendWelcomeMessages", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsDefaultRole {
+            get {
+                return ResourceManager.GetString("CurrentSettingsDefaultRole", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsMuteRole {
+            get {
+                return ResourceManager.GetString("CurrentSettingsMuteRole", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsAdminLogChannel {
+            get {
+                return ResourceManager.GetString("CurrentSettingsAdminLogChannel", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsBotLogChannel {
+            get {
+                return ResourceManager.GetString("CurrentSettingsBotLogChannel", resourceCulture);
+            }
+        }
+        
+        internal static string LanguageNotSupported {
+            get {
+                return ResourceManager.GetString("LanguageNotSupported", resourceCulture);
+            }
+        }
+        
+        internal static string SettingsUpdated {
+            get {
+                return ResourceManager.GetString("SettingsUpdated", resourceCulture);
+            }
+        }
+        
+        internal static string InvalidBoolean {
+            get {
+                return ResourceManager.GetString("InvalidBoolean", resourceCulture);
+            }
+        }
+        
+        internal static string InvalidRoleSpecified {
+            get {
+                return ResourceManager.GetString("InvalidRoleSpecified", resourceCulture);
+            }
+        }
+        
+        internal static string InvalidChannelSpecified {
+            get {
+                return ResourceManager.GetString("InvalidChannelSpecified", resourceCulture);
+            }
+        }
+        
+        internal static string Yes {
+            get {
+                return ResourceManager.GetString("Yes", resourceCulture);
+            }
+        }
+        
+        internal static string No {
+            get {
+                return ResourceManager.GetString("No", resourceCulture);
+            }
+        }
+        
+        internal static string UserNotBanned {
+            get {
+                return ResourceManager.GetString("UserNotBanned", resourceCulture);
+            }
+        }
+        
+        internal static string MemberNotMuted {
+            get {
+                return ResourceManager.GetString("MemberNotMuted", resourceCulture);
+            }
+        }
+        
+        internal static string RolesReturned {
+            get {
+                return ResourceManager.GetString("RolesReturned", resourceCulture);
+            }
+        }
+        
+        internal static string MemberUnmuted {
+            get {
+                return ResourceManager.GetString("MemberUnmuted", resourceCulture);
+            }
+        }
+        
+        internal static string UserUnbanned {
+            get {
+                return ResourceManager.GetString("UserUnbanned", resourceCulture);
+            }
+        }
+        
+        internal static string CurrentSettingsWelcomeMessage {
+            get {
+                return ResourceManager.GetString("CurrentSettingsWelcomeMessage", resourceCulture);
+            }
+        }
+        
+        internal static string NotEnoughArguments {
+            get {
+                return ResourceManager.GetString("NotEnoughArguments", resourceCulture);
+            }
+        }
+        
+        internal static string ClearInvalidAmountSpecified {
+            get {
+                return ResourceManager.GetString("ClearInvalidAmountSpecified", resourceCulture);
+            }
+        }
+        
+        internal static string BanResponse {
+            get {
+                return ResourceManager.GetString("BanResponse", resourceCulture);
+            }
+        }
+        
+        internal static string KickResponse {
+            get {
+                return ResourceManager.GetString("KickResponse", resourceCulture);
+            }
+        }
+        
+        internal static string UserNotInGuild {
+            get {
+                return ResourceManager.GetString("UserNotInGuild", resourceCulture);
+            }
+        }
+        
+        internal static string MuteResponse {
+            get {
+                return ResourceManager.GetString("MuteResponse", resourceCulture);
+            }
+        }
+        
+        internal static string UnbanResponse {
+            get {
+                return ResourceManager.GetString("UnbanResponse", resourceCulture);
+            }
+        }
+        
+        internal static string UnmuteResponse {
+            get {
+                return ResourceManager.GetString("UnmuteResponse", resourceCulture);
+            }
+        }
+    }
+}
diff --git a/Boyfriend/Messages.resx b/Boyfriend/Messages.resx
new file mode 100644
index 0000000..bc46f3c
--- /dev/null
+++ b/Boyfriend/Messages.resx
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<root>
+    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
+                id="root"
+                xmlns="">
+        <xsd:element name="root" msdata:IsDataSet="true">
+
+        </xsd:element>
+    </xsd:schema>
+    <resheader name="resmimetype">
+        <value>text/microsoft-resx</value>
+    </resheader>
+    <resheader name="version">
+        <value>1.3</value>
+    </resheader>
+    <resheader name="reader">
+        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
+            PublicKeyToken=b77a5c561934e089
+        </value>
+    </resheader>
+    <resheader name="writer">
+        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
+            PublicKeyToken=b77a5c561934e089
+        </value>
+    </resheader>
+    <data name="CouldntFindGuildByChannel" xml:space="preserve">
+        <value>Couldn't find guild by message!</value>
+    </data>
+    <data name="Ready" xml:space="preserve">
+        <value>{0}I'm ready! (C#)</value>
+    </data>
+    <data name="UncachedMessageDeleted" xml:space="preserve">
+        <value>Deleted message in {0}, but I forgot what was there</value>
+    </data>
+    <data name="CachedMessageDeleted" xml:space="preserve">
+        <value>Deleted message from {0} in channel </value>
+    </data>
+    <data name="AutobanReason" xml:space="preserve">
+        <value>Too many mentions in 1 message</value>
+    </data>
+    <data name="UncachedMessageEdited" xml:space="preserve">
+        <value>Message edited from {0} in channel {1}, but I forgot what was there before the edit</value>
+    </data>
+    <data name="CachedMessageEdited" xml:space="preserve">
+        <value>Message edited from {0} in channel {1}.{2}Before:{3}{4}{5}After:{6}{7}</value>
+    </data>
+    <data name="DefaultWelcomeMessage" xml:space="preserve">
+        <value>{0}, welcome to {1}</value>
+    </data>
+    <data name="Beep1" xml:space="preserve">
+        <value>Bah! </value>
+    </data>
+    <data name="Beep2" xml:space="preserve">
+        <value>Bop! </value>
+    </data>
+    <data name="Beep3" xml:space="preserve">
+        <value>Beep! </value>
+    </data>
+    <data name="InvalidAdminLogChannel" xml:space="preserve">
+        <value>Invalid admin log channel for guild</value>
+    </data>
+    <data name="MuteRoleRequired" xml:space="preserve">
+        <value>You must set up a mute role in settings!</value>
+    </data>
+    <data name="CommandExecutionUnsuccessful" xml:space="preserve">
+        <value>Command execution was unsuccessful: {0}</value>
+    </data>
+    <data name="RepeatedArgumentsDetected" xml:space="preserve">
+        <value>Repeated arguments detected! {0}</value>
+    </data>
+    <data name="CommandParseFailed" xml:space="preserve">
+        <value>Command parsing failed: {0}</value>
+    </data>
+    <data name="UnknownCommand" xml:space="preserve">
+        <value>Unknown command! {0}</value>
+    </data>
+    <data name="BadArgumentCount" xml:space="preserve">
+        <value>Invalid argument count! {0}</value>
+    </data>
+    <data name="ArgumentNotPresent" xml:space="preserve">
+        <value>Arguments not present! {0}</value>
+    </data>
+    <data name="CommandNoPermissionBot" xml:space="preserve">
+        <value>I do not have permission to execute this command!</value>
+    </data>
+    <data name="CommandNoPermissionUser" xml:space="preserve">
+        <value>You do not have permission to execute this command!</value>
+    </data>
+    <data name="InteractionsDifferentGuilds" xml:space="preserve">
+        <value>Members are in different guilds!</value>
+    </data>
+    <data name="InteractionsOwner" xml:space="preserve">
+        <value>You cannot interact with guild owner!</value>
+    </data>
+    <data name="InteractionsYourself" xml:space="preserve">
+        <value>You cannot interact with yourself!</value>
+    </data>
+    <data name="InteractionsMe" xml:space="preserve">
+        <value>You cannot interact with me!</value>
+    </data>
+    <data name="InteractionsFailedUser" xml:space="preserve">
+        <value>You cannot interact with this member!</value>
+    </data>
+    <data name="InteractionsFailedBot" xml:space="preserve">
+        <value>I cannot interact with this member!</value>
+    </data>
+    <data name="YouWereBanned" xml:space="preserve">
+        <value>You were banned by {0} in guild {1} for {2}</value>
+    </data>
+    <data name="UserBanned" xml:space="preserve">
+        <value>{0} banned {1} for {2}</value>
+    </data>
+    <data name="PunishmentExpired" xml:space="preserve">
+        <value>Punishment expired</value>
+    </data>
+    <data name="ClearNegativeAmount" xml:space="preserve">
+        <value>Negative message amount specified!</value>
+    </data>
+    <data name="ClearAmountTooLarge" xml:space="preserve">
+        <value>Too many messages specified!</value>
+    </data>
+    <data name="MessagesDeleted" xml:space="preserve">
+        <value>{0} deleted {1} messages in channel {2}</value>
+    </data>
+    <data name="CommandHelp" xml:space="preserve">
+        <value>Command help:{0}</value>
+    </data>
+    <data name="YouWereKicked" xml:space="preserve">
+        <value>You were kicked by {0} in guild {1} for {2}</value>
+    </data>
+    <data name="MemberKicked" xml:space="preserve">
+        <value>{0} kicked {1} for {2}</value>
+    </data>
+    <data name="MemberMuted" xml:space="preserve">
+        <value>{0} muted {1} for {2}</value>
+    </data>
+    <data name="Milliseconds" xml:space="preserve">
+        <value>ms</value>
+    </data>
+    <data name="MemberAlreadyMuted" xml:space="preserve">
+        <value>Member is already muted!</value>
+    </data>
+    <data name="MuteRoleManuallyRemoved" xml:space="preserve">
+        <value>Someone removed the mute role manually!</value>
+    </data>
+    <data name="ChannelNotSpecified" xml:space="preserve">
+        <value>Not specified</value>
+    </data>
+    <data name="RoleNotSpecified" xml:space="preserve">
+        <value>Not specified</value>
+    </data>
+    <data name="CurrentSettings" xml:space="preserve">
+        <value>Current settings:{0}</value>
+    </data>
+    <data name="CurrentSettingsLang" xml:space="preserve">
+        <value>Language (`lang`): `{0}`{1}</value>
+    </data>
+    <data name="CurrentSettingsPrefix" xml:space="preserve">
+        <value>Prefix (`prefix`): `{0}`{1}</value>
+    </data>
+    <data name="CurrentSettingsRemoveRoles" xml:space="preserve">
+        <value>Remove roles on mute (`removeRolesOnMute`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsUseSystemChannel" xml:space="preserve">
+        <value>Use system channel for notifications (`useSystemChannel`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsSendWelcomeMessages" xml:space="preserve">
+        <value>Send welcome messages (`sendWelcomeMessages`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsDefaultRole" xml:space="preserve">
+        <value>Default role (`defaultRole`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsMuteRole" xml:space="preserve">
+        <value>Mute role (`muteRole`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsAdminLogChannel" xml:space="preserve">
+        <value>Admin log channel (`adminLogChannel`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsBotLogChannel" xml:space="preserve">
+        <value>Bot log channel (`botLogChannel`): {0}</value>
+    </data>
+    <data name="LanguageNotSupported" xml:space="preserve">
+        <value>Language not supported!</value>
+    </data>
+    <data name="SettingsUpdated" xml:space="preserve">
+        <value>Settings successfully updated</value>
+    </data>
+    <data name="InvalidBoolean" xml:space="preserve">
+        <value>Invalid argument! 'true' or 'false' required!</value>
+    </data>
+    <data name="InvalidRoleSpecified" xml:space="preserve">
+        <value>Invalid role specified!</value>
+    </data>
+    <data name="InvalidChannelSpecified" xml:space="preserve">
+        <value>Invalid channel specified!</value>
+    </data>
+    <data name="Yes" xml:space="preserve">
+        <value>Yes</value>
+    </data>
+    <data name="No" xml:space="preserve">
+        <value>No</value>
+    </data>
+    <data name="UserNotBanned" xml:space="preserve">
+        <value>User not banned!</value>
+    </data>
+    <data name="MemberNotMuted" xml:space="preserve">
+        <value>Member not muted!</value>
+    </data>
+    <data name="RolesReturned" xml:space="preserve">
+        <value>Someone removed the mute role manually! I added back all roles that I removed during the mute</value>
+    </data>
+    <data name="MemberUnmuted" xml:space="preserve">
+        <value>{0} unmuted {1} for {2}</value>
+    </data>
+    <data name="UserUnbanned" xml:space="preserve">
+        <value>{0} unbanned {1} for {2}</value>
+    </data>
+    <data name="CurrentSettingsWelcomeMessage" xml:space="preserve">
+        <value>Welcome message: `{0}`{1}</value>
+    </data>
+    <data name="NotEnoughArguments" xml:space="preserve">
+        <value>Not enough arguments! Needed: {0}, provided: {1}</value>
+    </data>
+    <data name="ClearInvalidAmountSpecified" xml:space="preserve">
+        <value>Invalid message amount specified!</value>
+    </data>
+    <data name="BanResponse" xml:space="preserve">
+        <value>:white_check_mark: Successfully banned {0} for {1}</value>
+    </data>
+    <data name="KickResponse" xml:space="preserve">
+        <value>:white_check_mark: Successfully kicked {0} for {1}</value>
+    </data>
+    <data name="UserNotInGuild" xml:space="preserve">
+        <value>The specified user is not a member of this server!</value>
+    </data>
+    <data name="MuteResponse" xml:space="preserve">
+        <value>:white_check_mark: Successfully muted {0} for {1}</value>
+    </data>
+    <data name="UnbanResponse" xml:space="preserve">
+        <value>:white_check_mark: Successfully unbanned {0} for {1}</value>
+    </data>
+    <data name="UnmuteResponse" xml:space="preserve">
+        <value>:white_check_mark: Successfully unmuted {0} for {1}</value>
+    </data>
+</root>
\ No newline at end of file
diff --git a/Boyfriend/Messages.ru.resx b/Boyfriend/Messages.ru.resx
new file mode 100644
index 0000000..534cca1
--- /dev/null
+++ b/Boyfriend/Messages.ru.resx
@@ -0,0 +1,237 @@
+<root>
+    <resheader name="resmimetype">
+        <value>text/microsoft-resx</value>
+    </resheader>
+    <resheader name="version">
+        <value>1.3</value>
+    </resheader>
+    <resheader name="reader">
+        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
+            PublicKeyToken=b77a5c561934e089
+        </value>
+    </resheader>
+    <resheader name="writer">
+        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
+            PublicKeyToken=b77a5c561934e089
+        </value>
+    </resheader>
+    <data name="CouldntFindGuildByChannel" xml:space="preserve">
+        <value>Не удалось найти сервер по каналу!</value>
+    </data>
+    <data name="Ready" xml:space="preserve">
+        <value>{0}Я запустился! (C#)</value>
+    </data>
+    <data name="UncachedMessageDeleted" xml:space="preserve">
+        <value>Удалено сообщение в канале {0}, но я забыл что там было</value>
+    </data>
+    <data name="CachedMessageDeleted" xml:space="preserve">
+        <value>Удалено сообщение от {0} в канале </value>
+    </data>
+    <data name="AutobanReason" xml:space="preserve">
+        <value>Слишком много упоминаний в одном сообщении</value>
+    </data>
+    <data name="UncachedMessageEdited" xml:space="preserve">
+        <value>Отредактировано сообщение от {0} в канале {1}, но я забыл что там было до редактирования</value>
+    </data>
+    <data name="CachedMessageEdited" xml:space="preserve">
+        <value>Отредактировано сообщение от {0} в канале {1}.{2}До:{3}{4}{5}После:{6}{7}</value>
+    </data>
+    <data name="DefaultWelcomeMessage" xml:space="preserve">
+        <value>{0}, добро пожаловать на сервер {1}</value>
+    </data>
+    <data name="Beep1" xml:space="preserve">
+        <value>Бап! </value>
+    </data>
+    <data name="Beep2" xml:space="preserve">
+        <value>Боп! </value>
+    </data>
+    <data name="Beep3" xml:space="preserve">
+        <value>Бип! </value>
+    </data>
+    <data name="MuteRoleRequired" xml:space="preserve">
+        <value>Требуется указать роль мута в настройках!</value>
+    </data>
+    <data name="InvalidAdminLogChannel" xml:space="preserve">
+        <value>Неверный канал админ-логов для гильдии </value>
+    </data>
+    <data name="CommandExecutionUnsuccessful" xml:space="preserve">
+        <value>Выполнение команды завершилось неудачей: {0}</value>
+    </data>
+    <data name="RepeatedArgumentsDetected" xml:space="preserve">
+        <value>Обнаружены повторяющиеся типы аргументов! {0}</value>
+    </data>
+    <data name="CommandParseFailed" xml:space="preserve">
+        <value>Не удалось обработать команду: {0}</value>
+    </data>
+    <data name="UnknownCommand" xml:space="preserve">
+        <value>Неизвестная команда! {0}</value>
+    </data>
+    <data name="BadArgumentCount" xml:space="preserve">
+        <value>Неверное количество аргументов! {0}</value>
+    </data>
+    <data name="ArgumentNotPresent" xml:space="preserve">
+        <value>Нету нужных аргументов! {0}</value>
+    </data>
+    <data name="CommandNoPermissionBot" xml:space="preserve">
+        <value>У меня недостаточно прав для выполнения этой команды!</value>
+    </data>
+    <data name="CommandNoPermissionUser" xml:space="preserve">
+        <value>У тебя недостаточно прав для выполнения этой команды!</value>
+    </data>
+    <data name="InteractionsDifferentGuilds" xml:space="preserve">
+        <value>Участники находятся в разных гильдиях!</value>
+    </data>
+    <data name="InteractionsOwner" xml:space="preserve">
+        <value>Ты не можешь взаимодействовать с владельцем сервера!</value>
+    </data>
+    <data name="InteractionsYourself" xml:space="preserve">
+        <value>Ты не можешь взаимодействовать с самим собой!</value>
+    </data>
+    <data name="InteractionsMe" xml:space="preserve">
+        <value>Ты не можешь со мной взаимодействовать!</value>
+    </data>
+    <data name="InteractionsFailedUser" xml:space="preserve">
+        <value>Ты не можешь взаимодействовать с этим участником!</value>
+    </data>
+    <data name="InteractionsFailedBot" xml:space="preserve">
+        <value>Я не могу взаимодействовать с этим участником!</value>
+    </data>
+    <data name="YouWereBanned" xml:space="preserve">
+        <value>Тебя забанил {0} на сервере {1} за {2}</value>
+    </data>
+    <data name="UserBanned" xml:space="preserve">
+        <value>{0} банит {1} за {2}</value>
+    </data>
+    <data name="PunishmentExpired" xml:space="preserve">
+        <value>Время наказания истекло</value>
+    </data>
+    <data name="ClearNegativeAmount" xml:space="preserve">
+        <value>Указано отрицательное количество сообщений!</value>
+    </data>
+    <data name="ClearAmountTooLarge" xml:space="preserve">
+        <value>Указано слишком много сообщений!</value>
+    </data>
+    <data name="MessagesDeleted" xml:space="preserve">
+        <value>{0} удаляет {1} сообщений в канале {2}</value>
+    </data>
+    <data name="CommandHelp" xml:space="preserve">
+        <value>Справка по командам:{0}</value>
+    </data>
+    <data name="YouWereKicked" xml:space="preserve">
+        <value>Тебя кикнул {0} на сервере {1} за {2}</value>
+    </data>
+    <data name="MemberKicked" xml:space="preserve">
+        <value>{0} выгоняет {1} за {2}</value>
+    </data>
+    <data name="MemberMuted" xml:space="preserve">
+        <value>{0} глушит {1} за {2}</value>
+    </data>
+    <data name="Milliseconds" xml:space="preserve">
+        <value>мс</value>
+    </data>
+    <data name="MemberAlreadyMuted" xml:space="preserve">
+        <value>Участник уже заглушен!</value>
+    </data>
+    <data name="MuteRoleManuallyRemoved" xml:space="preserve">
+        <value>Кто-то убрал роль мута самостоятельно!</value>
+    </data>
+    <data name="ChannelNotSpecified" xml:space="preserve">
+        <value>Не указан</value>
+    </data>
+    <data name="RoleNotSpecified" xml:space="preserve">
+        <value>Не указана</value>
+    </data>
+    <data name="CurrentSettings" xml:space="preserve">
+        <value>Текущие настройки:{0}</value>
+    </data>
+    <data name="CurrentSettingsLang" xml:space="preserve">
+        <value>Язык (`lang`): `{0}`{1}</value>
+    </data>
+    <data name="CurrentSettingsPrefix" xml:space="preserve">
+        <value>Префикс (`prefix`): `{0}`{1}</value>
+    </data>
+    <data name="CurrentSettingsRemoveRoles" xml:space="preserve">
+        <value>Удалять роли при муте (`removeRolesOnMute`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsUseSystemChannel" xml:space="preserve">
+        <value>Использовать канал системных сообщений для уведомлений (`useSystemChannel`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsSendWelcomeMessages" xml:space="preserve">
+        <value>Отправлять приветствия (`sendWelcomeMessages`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsDefaultRole" xml:space="preserve">
+        <value>Стандартная роль (`defaultRole`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsMuteRole" xml:space="preserve">
+        <value>Роль мута (`muteRole`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsAdminLogChannel" xml:space="preserve">
+        <value>Канал админ-уведомлений (`adminLogChannel`): {0}{1}</value>
+    </data>
+    <data name="CurrentSettingsBotLogChannel" xml:space="preserve">
+        <value>Канал бот-уведомлений (`botLogChannel`): {0}</value>
+    </data>
+    <data name="LanguageNotSupported" xml:space="preserve">
+        <value>Язык не поддерживается!</value>
+    </data>
+    <data name="SettingsUpdated" xml:space="preserve">
+        <value>Настройки успешно обновлены!</value>
+    </data>
+    <data name="InvalidBoolean" xml:space="preserve">
+        <value>Неверный параметр! Требуется 'true' или 'false'</value>
+    </data>
+    <data name="InvalidRoleSpecified" xml:space="preserve">
+        <value>Указана недействительная роль!</value>
+    </data>
+    <data name="InvalidChannelSpecified" xml:space="preserve">
+        <value>Указан недействильный канал!</value>
+    </data>
+    <data name="Yes" xml:space="preserve">
+        <value>Да</value>
+    </data>
+    <data name="No" xml:space="preserve">
+        <value>Нет</value>
+    </data>
+    <data name="UserNotBanned" xml:space="preserve">
+        <value>Пользователь не забанен!</value>
+    </data>
+    <data name="MemberNotMuted" xml:space="preserve">
+        <value>Участник не заглушен!</value>
+    </data>
+    <data name="RolesReturned" xml:space="preserve">
+        <value>Кто-то убрал роль мута самостоятельно! Я вернул все роли, которые забрал при муте</value>
+    </data>
+    <data name="MemberUnmuted" xml:space="preserve">
+        <value>{0} возвращает из мута {1} за {2}</value>
+    </data>
+    <data name="UserUnbanned" xml:space="preserve">
+        <value>{0} возвращает из бана {1} за {2}</value>
+    </data>
+    <data name="CurrentSettingsWelcomeMessage" xml:space="preserve">
+        <value>Приветствие: `{0}`{1}</value>
+    </data>
+    <data name="NotEnoughArguments" xml:space="preserve">
+        <value>Недостаточно аргументов! Требуется: {0}, указано: {1}</value>
+    </data>
+    <data name="ClearInvalidAmountSpecified" xml:space="preserve">
+        <value>Указано неверное количество сообщений!</value>
+    </data>
+    <data name="BanResponse" xml:space="preserve">
+        <value>:white_check_mark: Успешно забанен {0} за {1}</value>
+    </data>
+    <data name="KickResponse" xml:space="preserve">
+        <value>:white_check_mark: Успешно выгнан {0} за {1}</value>
+    </data>
+    <data name="UserNotInGuild" xml:space="preserve">
+        <value>Указанный пользователь не является участником этого сервера!</value>
+    </data>
+    <data name="MuteResponse" xml:space="preserve">
+        <value>:white_check_mark: Успешно заглушен {0} за {1}</value>
+    </data>
+    <data name="UnbanResponse" xml:space="preserve">
+        <value>:white_check_mark: Успешно возвращён из бана {0} за {1}</value>
+    </data>
+    <data name="UnmuteResponse" xml:space="preserve">
+        <value>:white_check_mark: Успешно возвращён из мута {0} за {1}</value>
+    </data>
+</root>
\ No newline at end of file
diff --git a/Boyfriend/Utils.cs b/Boyfriend/Utils.cs
index 2c9ad4b..b6b981f 100644
--- a/Boyfriend/Utils.cs
+++ b/Boyfriend/Utils.cs
@@ -6,17 +6,23 @@ using Discord.Net;
 namespace Boyfriend;
 
 public static class Utils {
-    public static string GetBeep() {
-        var letters = new[] {"а", "о", "и"};
-        return $"Б{letters[new Random().Next(3)]}п! ";
+    private static readonly string[] Formats = {
+        "%d'd'%h'h'%m'm'%s's'", "%d'd'%h'h'%m'm'", "%d'd'%h'h'%s's'", "%d'd'%h'h'", "%d'd'%m'm'%s's'", "%d'd'%m'm'",
+        "%d'd'%s's'", "%d'd'", "%h'h'%m'm'%s's'", "%h'h'%m'm'", "%h'h'%s's'", "%h'h'", "%m'm'%s's'", "%m'm'", "%s's'",
+
+        "%d'д'%h'ч'%m'м'%s'с'", "%d'д'%h'ч'%m'м'", "%d'д'%h'ч'%s'с'", "%d'д'%h'ч'", "%d'д'%m'м'%s'с'", "%d'д'%m'м'",
+        "%d'д'%s'с'", "%d'д'", "%h'ч'%m'м'%s'с'", "%h'ч'%m'м'", "%h'ч'%s'с'", "%h'ч'", "%m'м'%s'с'", "%m'м'", "%s'с'"
+    };
+
+    public static string GetBeep(string cultureInfo, int i = -1) {
+        Messages.Culture = new CultureInfo(cultureInfo);
+        var beeps = new[] {Messages.Beep1, Messages.Beep2, Messages.Beep3};
+        return beeps[i < 0 ? new Random().Next(3) : i];
     }
 
-    public static async Task<ITextChannel> GetAdminLogChannel(IGuild guild) {
-        var adminLogChannel = await ParseChannel(Boyfriend.GetGuildConfig(guild).AdminLogChannel.ToString());
-        if (adminLogChannel is ITextChannel channel)
-            return channel;
-
-        throw new Exception("Неверный канал админ-логов для гильдии " + guild.Id);
+    public static async Task<ITextChannel?> GetAdminLogChannel(IGuild guild) {
+        var adminLogChannel = await ParseChannelNullable(Boyfriend.GetGuildConfig(guild).AdminLogChannel.ToString());
+        return adminLogChannel as ITextChannel;
     }
 
     public static string Wrap(string original) {
@@ -43,6 +49,15 @@ public static class Utils {
         return Convert.ToUInt64(Regex.Replace(mention, "[^0-9]", ""));
     }
 
+    private static ulong? ParseMentionNullable(string mention) {
+        try {
+            return ParseMention(mention) == 0 ? throw new FormatException() : ParseMention(mention);
+        }
+        catch (FormatException) {
+            return null;
+        }
+    }
+
     public static async Task<IUser> ParseUser(string mention) {
         var user = Boyfriend.Client.GetUserAsync(ParseMention(mention));
         return await user;
@@ -52,14 +67,22 @@ public static class Utils {
         return await guild.GetUserAsync(ParseMention(mention));
     }
 
-    public static async Task<IChannel> ParseChannel(string mention) {
+    private static async Task<IChannel> ParseChannel(string mention) {
         return await Boyfriend.Client.GetChannelAsync(ParseMention(mention));
     }
 
-    public static IRole ParseRole(IGuild guild, string mention) {
+    public static async Task<IChannel?> ParseChannelNullable(string mention) {
+        return ParseMentionNullable(mention) == null ? null : await ParseChannel(mention);
+    }
+
+    public static IRole? ParseRole(IGuild guild, string mention) {
         return guild.GetRole(ParseMention(mention));
     }
 
+    public static IRole? ParseRoleNullable(IGuild guild, string mention) {
+        return ParseMentionNullable(mention) == null ? null : ParseRole(guild, mention);
+    }
+
     public static async Task SendDirectMessage(IUser user, string toSend) {
         try {
             await user.SendMessageAsync(toSend);
@@ -69,24 +92,23 @@ public static class Utils {
         }
     }
 
-    public static IRole GetMuteRole(IGuild guild) {
+    public static IRole? GetMuteRole(IGuild guild) {
         var role = guild.Roles.FirstOrDefault(x => x.Id == Boyfriend.GetGuildConfig(guild).MuteRole);
-        if (role == null) throw new Exception("Требуется указать роль мута в настройках!");
         return role;
     }
 
-    public static async Task SilentSendAsync(ITextChannel channel, string text) {
+    public static async Task SilentSendAsync(ITextChannel? channel, string text) {
+        if (channel == null) return;
         try {
             await channel.SendMessageAsync(text, false, null, null, AllowedMentions.None);
         } catch (ArgumentException) {}
     }
-
-    private static readonly string[] Formats = {
-        "%d'd'%h'h'%m'm'%s's'", "%d'd'%h'h'%m'm'", "%d'd'%h'h'%s's'", "%d'd'%h'h'", "%d'd'%m'm'%s's'", "%d'd'%m'm'",
-        "%d'd'%s's'", "%d'd'", "%h'h'%m'm'%s's'", "%h'h'%m'm'", "%h'h'%s's'", "%h'h'", "%m'm'%s's'", "%m'm'", "%s's'"
-    };
     public static TimeSpan GetTimeSpan(string from) {
         return TimeSpan.ParseExact(from.ToLowerInvariant(), Formats,
             CultureInfo.InvariantCulture);
     }
+
+    public static string JoinString(string[] args, int startIndex) {
+        return string.Join(" ", args, startIndex, args.Length - startIndex);
+    }
 }
\ No newline at end of file