From f30485dd711f5346629abd3fcbe0cc1d8062081b Mon Sep 17 00:00:00 2001 From: l1ttleO Date: Tue, 18 Jan 2022 22:38:15 +0500 Subject: [PATCH] ton of stuff - new command handler - multilanguage support - time out support - responses - a ton of stuff --- Boyfriend/Boyfriend.cs | 35 +- Boyfriend/Boyfriend.csproj | 18 +- Boyfriend/Boyfriend.csproj.DotSettings | 2 + Boyfriend/CommandHandler.cs | 89 +++-- Boyfriend/Commands/BanCommand.cs | 61 ++++ Boyfriend/Commands/BanModule.cs | 45 --- Boyfriend/Commands/ClearCommand.cs | 49 +++ Boyfriend/Commands/ClearModule.cs | 33 -- Boyfriend/Commands/Command.cs | 19 + Boyfriend/Commands/HelpCommand.cs | 30 ++ Boyfriend/Commands/HelpModule.cs | 25 -- Boyfriend/Commands/KickCommand.cs | 47 +++ Boyfriend/Commands/KickModule.cs | 34 -- Boyfriend/Commands/MuteCommand.cs | 92 +++++ Boyfriend/Commands/MuteModule.cs | 63 ---- Boyfriend/Commands/PingCommand.cs | 25 ++ Boyfriend/Commands/PingModule.cs | 14 - Boyfriend/Commands/SettingsCommand.cs | 125 +++++++ Boyfriend/Commands/SettingsModule.cs | 115 ------ Boyfriend/Commands/UnbanCommand.cs | 43 +++ Boyfriend/Commands/UnbanModule.cs | 29 -- Boyfriend/Commands/UnmuteCommand.cs | 61 ++++ Boyfriend/Commands/UnmuteModule.cs | 46 --- Boyfriend/EventHandler.cs | 68 ++-- Boyfriend/GuildConfig.cs | 9 +- Boyfriend/Messages.Designer.cs | 486 +++++++++++++++++++++++++ Boyfriend/Messages.resx | 246 +++++++++++++ Boyfriend/Messages.ru.resx | 237 ++++++++++++ Boyfriend/Utils.cs | 60 ++- 29 files changed, 1686 insertions(+), 520 deletions(-) create mode 100644 Boyfriend/Commands/BanCommand.cs delete mode 100644 Boyfriend/Commands/BanModule.cs create mode 100644 Boyfriend/Commands/ClearCommand.cs delete mode 100644 Boyfriend/Commands/ClearModule.cs create mode 100644 Boyfriend/Commands/Command.cs create mode 100644 Boyfriend/Commands/HelpCommand.cs delete mode 100644 Boyfriend/Commands/HelpModule.cs create mode 100644 Boyfriend/Commands/KickCommand.cs delete mode 100644 Boyfriend/Commands/KickModule.cs create mode 100644 Boyfriend/Commands/MuteCommand.cs delete mode 100644 Boyfriend/Commands/MuteModule.cs create mode 100644 Boyfriend/Commands/PingCommand.cs delete mode 100644 Boyfriend/Commands/PingModule.cs create mode 100644 Boyfriend/Commands/SettingsCommand.cs delete mode 100644 Boyfriend/Commands/SettingsModule.cs create mode 100644 Boyfriend/Commands/UnbanCommand.cs delete mode 100644 Boyfriend/Commands/UnbanModule.cs create mode 100644 Boyfriend/Commands/UnmuteCommand.cs delete mode 100644 Boyfriend/Commands/UnmuteModule.cs create mode 100644 Boyfriend/Messages.Designer.cs create mode 100644 Boyfriend/Messages.resx create mode 100644 Boyfriend/Messages.ru.resx 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 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(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 @@ https://github.com/l1ttleO/Boyfriend-CSharp https://github.com/l1ttleO/Boyfriend-CSharp git + 1.0.1 - + + + + + + ResXFileCodeGenerator + Messages.Designer.cs + + + + + + True + True + Messages.resx + 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 @@  No + + \ 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 GetAliases() { + return new List {"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 { - - [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 GetAliases() { + return new List {"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 { - - [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 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 GetAliases() { + return new List {"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 { - - [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 GetAliases() { + return new List {"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 { - - [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(); + 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 GetAliases() { + return new List {"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 { - - [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(); - 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 GetAliases() { + return new List {"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 { - - [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 GetAliases() { + return new List {"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 { - - [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 GetAliases() { + return new List {"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 { - - [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 GetAliases() { + return new List {"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 { - - [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 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 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> 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>(); } - 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 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +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 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + Couldn't find guild by message! + + + {0}I'm ready! (C#) + + + Deleted message in {0}, but I forgot what was there + + + Deleted message from {0} in channel + + + Too many mentions in 1 message + + + Message edited from {0} in channel {1}, but I forgot what was there before the edit + + + Message edited from {0} in channel {1}.{2}Before:{3}{4}{5}After:{6}{7} + + + {0}, welcome to {1} + + + Bah! + + + Bop! + + + Beep! + + + Invalid admin log channel for guild + + + You must set up a mute role in settings! + + + Command execution was unsuccessful: {0} + + + Repeated arguments detected! {0} + + + Command parsing failed: {0} + + + Unknown command! {0} + + + Invalid argument count! {0} + + + Arguments not present! {0} + + + I do not have permission to execute this command! + + + You do not have permission to execute this command! + + + Members are in different guilds! + + + You cannot interact with guild owner! + + + You cannot interact with yourself! + + + You cannot interact with me! + + + You cannot interact with this member! + + + I cannot interact with this member! + + + You were banned by {0} in guild {1} for {2} + + + {0} banned {1} for {2} + + + Punishment expired + + + Negative message amount specified! + + + Too many messages specified! + + + {0} deleted {1} messages in channel {2} + + + Command help:{0} + + + You were kicked by {0} in guild {1} for {2} + + + {0} kicked {1} for {2} + + + {0} muted {1} for {2} + + + ms + + + Member is already muted! + + + Someone removed the mute role manually! + + + Not specified + + + Not specified + + + Current settings:{0} + + + Language (`lang`): `{0}`{1} + + + Prefix (`prefix`): `{0}`{1} + + + Remove roles on mute (`removeRolesOnMute`): {0}{1} + + + Use system channel for notifications (`useSystemChannel`): {0}{1} + + + Send welcome messages (`sendWelcomeMessages`): {0}{1} + + + Default role (`defaultRole`): {0}{1} + + + Mute role (`muteRole`): {0}{1} + + + Admin log channel (`adminLogChannel`): {0}{1} + + + Bot log channel (`botLogChannel`): {0} + + + Language not supported! + + + Settings successfully updated + + + Invalid argument! 'true' or 'false' required! + + + Invalid role specified! + + + Invalid channel specified! + + + Yes + + + No + + + User not banned! + + + Member not muted! + + + Someone removed the mute role manually! I added back all roles that I removed during the mute + + + {0} unmuted {1} for {2} + + + {0} unbanned {1} for {2} + + + Welcome message: `{0}`{1} + + + Not enough arguments! Needed: {0}, provided: {1} + + + Invalid message amount specified! + + + :white_check_mark: Successfully banned {0} for {1} + + + :white_check_mark: Successfully kicked {0} for {1} + + + The specified user is not a member of this server! + + + :white_check_mark: Successfully muted {0} for {1} + + + :white_check_mark: Successfully unbanned {0} for {1} + + + :white_check_mark: Successfully unmuted {0} for {1} + + \ 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 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + Не удалось найти сервер по каналу! + + + {0}Я запустился! (C#) + + + Удалено сообщение в канале {0}, но я забыл что там было + + + Удалено сообщение от {0} в канале + + + Слишком много упоминаний в одном сообщении + + + Отредактировано сообщение от {0} в канале {1}, но я забыл что там было до редактирования + + + Отредактировано сообщение от {0} в канале {1}.{2}До:{3}{4}{5}После:{6}{7} + + + {0}, добро пожаловать на сервер {1} + + + Бап! + + + Боп! + + + Бип! + + + Требуется указать роль мута в настройках! + + + Неверный канал админ-логов для гильдии + + + Выполнение команды завершилось неудачей: {0} + + + Обнаружены повторяющиеся типы аргументов! {0} + + + Не удалось обработать команду: {0} + + + Неизвестная команда! {0} + + + Неверное количество аргументов! {0} + + + Нету нужных аргументов! {0} + + + У меня недостаточно прав для выполнения этой команды! + + + У тебя недостаточно прав для выполнения этой команды! + + + Участники находятся в разных гильдиях! + + + Ты не можешь взаимодействовать с владельцем сервера! + + + Ты не можешь взаимодействовать с самим собой! + + + Ты не можешь со мной взаимодействовать! + + + Ты не можешь взаимодействовать с этим участником! + + + Я не могу взаимодействовать с этим участником! + + + Тебя забанил {0} на сервере {1} за {2} + + + {0} банит {1} за {2} + + + Время наказания истекло + + + Указано отрицательное количество сообщений! + + + Указано слишком много сообщений! + + + {0} удаляет {1} сообщений в канале {2} + + + Справка по командам:{0} + + + Тебя кикнул {0} на сервере {1} за {2} + + + {0} выгоняет {1} за {2} + + + {0} глушит {1} за {2} + + + мс + + + Участник уже заглушен! + + + Кто-то убрал роль мута самостоятельно! + + + Не указан + + + Не указана + + + Текущие настройки:{0} + + + Язык (`lang`): `{0}`{1} + + + Префикс (`prefix`): `{0}`{1} + + + Удалять роли при муте (`removeRolesOnMute`): {0}{1} + + + Использовать канал системных сообщений для уведомлений (`useSystemChannel`): {0}{1} + + + Отправлять приветствия (`sendWelcomeMessages`): {0}{1} + + + Стандартная роль (`defaultRole`): {0}{1} + + + Роль мута (`muteRole`): {0}{1} + + + Канал админ-уведомлений (`adminLogChannel`): {0}{1} + + + Канал бот-уведомлений (`botLogChannel`): {0} + + + Язык не поддерживается! + + + Настройки успешно обновлены! + + + Неверный параметр! Требуется 'true' или 'false' + + + Указана недействительная роль! + + + Указан недействильный канал! + + + Да + + + Нет + + + Пользователь не забанен! + + + Участник не заглушен! + + + Кто-то убрал роль мута самостоятельно! Я вернул все роли, которые забрал при муте + + + {0} возвращает из мута {1} за {2} + + + {0} возвращает из бана {1} за {2} + + + Приветствие: `{0}`{1} + + + Недостаточно аргументов! Требуется: {0}, указано: {1} + + + Указано неверное количество сообщений! + + + :white_check_mark: Успешно забанен {0} за {1} + + + :white_check_mark: Успешно выгнан {0} за {1} + + + Указанный пользователь не является участником этого сервера! + + + :white_check_mark: Успешно заглушен {0} за {1} + + + :white_check_mark: Успешно возвращён из бана {0} за {1} + + + :white_check_mark: Успешно возвращён из мута {0} за {1} + + \ 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 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 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 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 ParseChannel(string mention) { + private static async Task ParseChannel(string mention) { return await Boyfriend.Client.GetChannelAsync(ParseMention(mention)); } - public static IRole ParseRole(IGuild guild, string mention) { + public static async Task 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