From 04facc3de25563c06d1a9fcca38c60f64c449096 Mon Sep 17 00:00:00 2001 From: l1ttleO Date: Wed, 2 Feb 2022 18:14:26 +0500 Subject: [PATCH] general code refactor and bug fixes --- Boyfriend/Boyfriend.cs | 7 ++- Boyfriend/Boyfriend.csproj | 2 +- Boyfriend/Boyfriend.csproj.DotSettings | 5 -- Boyfriend/CommandHandler.cs | 34 ++++++++----- Boyfriend/Commands/BanCommand.cs | 39 ++++++++------ Boyfriend/Commands/ClearCommand.cs | 17 ++++--- Boyfriend/Commands/HelpCommand.cs | 6 +-- Boyfriend/Commands/KickCommand.cs | 21 ++++---- Boyfriend/Commands/MuteCommand.cs | 70 +++++++++++++++----------- Boyfriend/Commands/SettingsCommand.cs | 7 ++- Boyfriend/Commands/UnbanCommand.cs | 21 +++++--- Boyfriend/Commands/UnmuteCommand.cs | 50 +++++++++--------- Boyfriend/EventHandler.cs | 23 ++++++--- Boyfriend/GuildConfig.cs | 6 +++ Boyfriend/Utils.cs | 21 +++++--- 15 files changed, 197 insertions(+), 132 deletions(-) delete mode 100644 Boyfriend/Boyfriend.csproj.DotSettings diff --git a/Boyfriend/Boyfriend.cs b/Boyfriend/Boyfriend.cs index 7d3d94d..1793c3e 100644 --- a/Boyfriend/Boyfriend.cs +++ b/Boyfriend/Boyfriend.cs @@ -21,9 +21,10 @@ public static class Boyfriend { } private static async Task Init() { - Client.Log += Log; var token = (await File.ReadAllTextAsync("token.txt")).Trim(); + Client.Log += Log; + await Client.LoginAsync(TokenType.Bot, token); await Client.StartAsync(); await Client.SetActivityAsync(new Game("Retrospecter - Expurgation", ActivityType.Listening)); @@ -35,6 +36,7 @@ public static class Boyfriend { private static Task Log(LogMessage msg) { Console.WriteLine(msg.ToString()); + return Task.CompletedTask; } @@ -56,13 +58,16 @@ public static class Boyfriend { public static void ResetGuildConfig(IGuild guild) { GuildConfigDictionary.Remove(guild.Id); + var config = new GuildConfig(guild.Id); config.Validate(); + GuildConfigDictionary.Add(guild.Id, config); } public static GuildConfig GetGuildConfig(IGuild guild) { Messages.Culture = new CultureInfo("ru"); + var config = GuildConfigDictionary.ContainsKey(guild.Id) ? GuildConfigDictionary[guild.Id] : new GuildConfig(guild.Id); config.Validate(); diff --git a/Boyfriend/Boyfriend.csproj b/Boyfriend/Boyfriend.csproj index ada7a52..eaa313b 100644 --- a/Boyfriend/Boyfriend.csproj +++ b/Boyfriend/Boyfriend.csproj @@ -15,7 +15,7 @@ - + diff --git a/Boyfriend/Boyfriend.csproj.DotSettings b/Boyfriend/Boyfriend.csproj.DotSettings deleted file mode 100644 index b5da49f..0000000 --- a/Boyfriend/Boyfriend.csproj.DotSettings +++ /dev/null @@ -1,5 +0,0 @@ - - No - - - \ No newline at end of file diff --git a/Boyfriend/CommandHandler.cs b/Boyfriend/CommandHandler.cs index b9a455c..610b69a 100644 --- a/Boyfriend/CommandHandler.cs +++ b/Boyfriend/CommandHandler.cs @@ -18,7 +18,8 @@ public static class CommandHandler { 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; + if (!command.GetAliases().Contains(regex.Replace(message.Content, "", 1).Split()[0])) + continue; var args = message.Content.Split().Skip(1).ToArray(); try { @@ -26,16 +27,17 @@ public static class CommandHandler { throw new ApplicationException(string.Format(Messages.NotEnoughArguments, command.GetArgumentsAmountRequired(), args.Length)); await command.Run(context, args); - } - catch (Exception e) { + } 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)); + var stacktrace = e.StackTrace; + var toSend = $"{signature} `{e.Message}`"; + if (stacktrace != null && e is not ApplicationException && e is not UnauthorizedAccessException) + toSend += $"{Environment.NewLine}{Utils.Wrap(stacktrace)}"; + await context.Channel.SendMessageAsync(toSend); throw; } @@ -45,18 +47,26 @@ public static class CommandHandler { public static async Task CheckPermissions(IGuildUser user, GuildPermission toCheck, GuildPermission forBot = GuildPermission.StartEmbeddedActivities) { + var me = await user.Guild.GetCurrentUserAsync(); if (forBot == GuildPermission.StartEmbeddedActivities) forBot = toCheck; - if (!(await user.Guild.GetCurrentUserAsync()).GuildPermissions.Has(forBot)) - throw new UnauthorizedAccessException(Messages.CommandNoPermissionBot); - if (!user.GuildPermissions.Has(toCheck)) - throw new UnauthorizedAccessException(Messages.CommandNoPermissionUser); + + if (user.Id != user.Guild.OwnerId + && (!me.GuildPermissions.Has(GuildPermission.Administrator) + || !user.GuildPermissions.Has(GuildPermission.Administrator))) { + if (!me.GuildPermissions.Has(forBot)) + throw new UnauthorizedAccessException(Messages.CommandNoPermissionBot); + if (!user.GuildPermissions.Has(toCheck)) + throw new UnauthorizedAccessException(Messages.CommandNoPermissionUser); + } } public static async Task CheckInteractions(IGuildUser actor, IGuildUser target) { - if (actor.Guild != target.Guild) - throw new UnauthorizedAccessException(Messages.InteractionsDifferentGuilds); var me = await target.Guild.GetCurrentUserAsync(); + + if (actor.Guild != target.Guild) + throw new Exception(Messages.InteractionsDifferentGuilds); if (actor.Id == actor.Guild.OwnerId) return; + if (target.Id == target.Guild.OwnerId) throw new UnauthorizedAccessException(Messages.InteractionsOwner); if (actor == target) diff --git a/Boyfriend/Commands/BanCommand.cs b/Boyfriend/Commands/BanCommand.cs index dcd5a7b..145316a 100644 --- a/Boyfriend/Commands/BanCommand.cs +++ b/Boyfriend/Commands/BanCommand.cs @@ -9,42 +9,49 @@ 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) { + } 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); + await Utils.ParseUser(args[0]), duration, reason); } public static async Task BanUser(IGuild guild, ITextChannel? channel, IGuildUser author, IUser toBan, TimeSpan duration, string reason) { var authorMention = author.Mention; + var guildBanMessage = $"({Utils.GetNameAndDiscrim(author)}) {reason}"; + var memberToBan = await guild.GetUserAsync(toBan.Id); + var notification = string.Format(Messages.UserBanned, authorMention, toBan.Mention, Utils.WrapInline(reason)); + + await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers); + if (memberToBan != null) + await CommandHandler.CheckInteractions(author, memberToBan); + 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); + + async void UnbanWhenExpires() { + try { + await UnbanCommand.UnbanUser(guild, null, await guild.GetCurrentUserAsync(), toBan, + Messages.PunishmentExpired); + } catch (ApplicationException) {} + } + + await Utils.StartDelayed(new Task(UnbanWhenExpires), duration); } public override List GetAliases() { @@ -58,4 +65,4 @@ public class BanCommand : Command { public override string GetSummary() { return "Банит пользователя"; } -} \ No newline at end of file +} diff --git a/Boyfriend/Commands/ClearCommand.cs b/Boyfriend/Commands/ClearCommand.cs index fee031e..473d8d6 100644 --- a/Boyfriend/Commands/ClearCommand.cs +++ b/Boyfriend/Commands/ClearCommand.cs @@ -9,16 +9,19 @@ namespace Boyfriend.Commands; public class ClearCommand : Command { public override async Task Run(SocketCommandContext context, string[] args) { + var user = context.User; + int toDelete; try { toDelete = Convert.ToInt32(args[0]); - } - catch (Exception e) when (e is FormatException or OverflowException) { + } 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); + + await CommandHandler.CheckPermissions(context.Guild.GetUser(user.Id), GuildPermission.ManageMessages); + switch (toDelete) { case < 1: throw new ApplicationException(Messages.ClearNegativeAmount); @@ -26,9 +29,11 @@ public class ClearCommand : Command { throw new ApplicationException(Messages.ClearAmountTooLarge); default: { var messages = await channel.GetMessagesAsync(toDelete + 1).FlattenAsync(); - await channel.DeleteMessagesAsync(messages); + + await channel.DeleteMessagesAsync(messages, Utils.GetRequestOptions(Utils.GetNameAndDiscrim(user))); + await Utils.SilentSendAsync(await Utils.GetAdminLogChannel(context.Guild), - string.Format(Messages.MessagesDeleted, context.User.Mention, toDelete + 1, + string.Format(Messages.MessagesDeleted, user.Mention, toDelete + 1, Utils.MentionChannel(context.Channel.Id))); break; } @@ -46,4 +51,4 @@ public class ClearCommand : Command { public override string GetSummary() { return "Очищает сообщения"; } -} \ No newline at end of file +} diff --git a/Boyfriend/Commands/HelpCommand.cs b/Boyfriend/Commands/HelpCommand.cs index b3eeed1..50b9818 100644 --- a/Boyfriend/Commands/HelpCommand.cs +++ b/Boyfriend/Commands/HelpCommand.cs @@ -8,11 +8,11 @@ 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; + var toSend = string.Format(Messages.CommandHelp, nl); + toSend = CommandHandler.Commands.Aggregate(toSend, (current, command) => current + $"`{prefix}{command.GetAliases()[0]}`: {command.GetSummary()}{nl}"); - await context.Channel.SendMessageAsync(toSend); } @@ -27,4 +27,4 @@ public class HelpCommand : Command { public override string GetSummary() { return "Показывает эту справку"; } -} \ No newline at end of file +} diff --git a/Boyfriend/Commands/KickCommand.cs b/Boyfriend/Commands/KickCommand.cs index ac7038b..0145ebc 100644 --- a/Boyfriend/Commands/KickCommand.cs +++ b/Boyfriend/Commands/KickCommand.cs @@ -9,24 +9,27 @@ 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); + + await KickMember(context.Guild, context.Channel as ITextChannel, author, toKick, Utils.JoinString(args, 1)); } - private static async void KickMember(IGuild guild, ITextChannel? channel, IUser author, IGuildUser toKick, + private static async Task 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 guildKickMessage = $"({Utils.GetNameAndDiscrim(author)}) {reason}"; var notification = string.Format(Messages.MemberKicked, authorMention, toKick.Mention, Utils.WrapInline(reason)); + + await Utils.SendDirectMessage(toKick, string.Format(Messages.YouWereKicked, authorMention, guild.Name, + Utils.WrapInline(reason))); + + await toKick.KickAsync(guildKickMessage); + await Utils.SilentSendAsync(channel, string.Format(Messages.KickResponse, toKick.Mention, Utils.WrapInline(reason))); await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification); @@ -44,4 +47,4 @@ public class KickCommand : Command { public override string GetSummary() { return "Выгоняет участника"; } -} \ No newline at end of file +} diff --git a/Boyfriend/Commands/MuteCommand.cs b/Boyfriend/Commands/MuteCommand.cs index 2c3e0a9..aece45d 100644 --- a/Boyfriend/Commands/MuteCommand.cs +++ b/Boyfriend/Commands/MuteCommand.cs @@ -1,5 +1,6 @@ using Discord; using Discord.Commands; +using Discord.Net; // ReSharper disable UnusedType.Global // ReSharper disable UnusedMember.Global @@ -9,27 +10,29 @@ namespace Boyfriend.Commands; public class MuteCommand : Command { public override async Task Run(SocketCommandContext context, string[] args) { - TimeSpan duration; + var author = context.Guild.GetUser(context.User.Id); + var config = Boyfriend.GetGuildConfig(context.Guild); var reason = Utils.JoinString(args, 1); + var role = Utils.GetMuteRole(context.Guild); + var rolesRemoved = config.RolesRemovedOnMute!; + var toMute = await Utils.ParseMember(context.Guild, args[0]); + + TimeSpan duration; try { duration = Utils.GetTimeSpan(args[1]); reason = Utils.JoinString(args, 2); - } - catch (Exception e) when (e is ArgumentNullException or FormatException or OverflowException) { + } 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 config = Boyfriend.GetGuildConfig(context.Guild); - var rolesRemoved = config.RolesRemovedOnMute!; + if (rolesRemoved.ContainsKey(toMute.Id)) { foreach (var roleId in rolesRemoved[toMute.Id]) await toMute.AddRoleAsync(roleId); rolesRemoved.Remove(toMute.Id); @@ -40,44 +43,53 @@ public class MuteCommand : Command { await CommandHandler.CheckPermissions(author, GuildPermission.ModerateMembers, GuildPermission.ManageRoles); await CommandHandler.CheckInteractions(author, toMute); - MuteMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toMute, + + await 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, + private static async Task 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.GetValueOrDefault(false) && role != null) { - var rolesRemoved = new List(); - try { + var requestOptions = Utils.GetRequestOptions($"({Utils.GetNameAndDiscrim(author)}) {reason}"); + var role = Utils.GetMuteRole(guild); + + if (role != null) { + if (config.RemoveRolesOnMute.GetValueOrDefault(false)) { + var rolesRemoved = new List(); foreach (var roleId in toMute.RoleIds) { - if (roleId == guild.Id) continue; - await toMute.RemoveRoleAsync(roleId); - rolesRemoved.Add(roleId); + try { + if (roleId == guild.Id) continue; + if (roleId == role.Id) continue; + await toMute.RemoveRoleAsync(roleId); + rolesRemoved.Add(roleId); + } catch (HttpException) {} } + + config.RolesRemovedOnMute!.Add(toMute.Id, rolesRemoved); + await config.Save(); } - catch (NullReferenceException) { } - config.RolesRemovedOnMute!.Add(toMute.Id, rolesRemoved); - await config.Save(); - } - - if (role != null) - await toMute.AddRoleAsync(role); - else - await toMute.SetTimeOutAsync(duration); + await toMute.AddRoleAsync(role, requestOptions); + } else + await toMute.SetTimeOutAsync(duration, requestOptions); 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)); + + async void UnmuteWhenExpires() { + try { + await UnmuteCommand.UnmuteMember(guild, null, await guild.GetCurrentUserAsync(), toMute, + Messages.PunishmentExpired); + } catch (ApplicationException) {} + } + if (role != null) - await Utils.StartDelayed(task, duration, () => toMute.RoleIds.Any(x => x == role.Id)); + await Utils.StartDelayed(new Task(UnmuteWhenExpires), duration); } public override List GetAliases() { diff --git a/Boyfriend/Commands/SettingsCommand.cs b/Boyfriend/Commands/SettingsCommand.cs index 9cdfe6e..fd5da44 100644 --- a/Boyfriend/Commands/SettingsCommand.cs +++ b/Boyfriend/Commands/SettingsCommand.cs @@ -9,9 +9,11 @@ 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; + + await CommandHandler.CheckPermissions(context.Guild.GetUser(context.User.Id), GuildPermission.ManageGuild); + if (args.Length == 0) { var nl = Environment.NewLine; var adminLogChannel = guild.GetTextChannel(config.AdminLogChannel.GetValueOrDefault(0)); @@ -43,8 +45,9 @@ public class SettingsCommand : Command { } var setting = args[0].ToLower(); - var value = ""; var shouldDefault = false; + var value = ""; + if (args.Length >= 2) value = args[1].ToLower(); else diff --git a/Boyfriend/Commands/UnbanCommand.cs b/Boyfriend/Commands/UnbanCommand.cs index d2d223f..69874de 100644 --- a/Boyfriend/Commands/UnbanCommand.cs +++ b/Boyfriend/Commands/UnbanCommand.cs @@ -9,20 +9,25 @@ 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 ApplicationException(Messages.UserNotBanned); - UnbanUser(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), toUnban, - Utils.JoinString(args, 1)); + await UnbanUser(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), + await Utils.ParseUser(args[0]), Utils.JoinString(args, 1)); } - public static async void UnbanUser(IGuild guild, ITextChannel? channel, IGuildUser author, IUser toUnban, + public static async Task 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); + var requestOptions = Utils.GetRequestOptions($"({Utils.GetNameAndDiscrim(author)}) {reason}"); + + await CommandHandler.CheckPermissions(author, GuildPermission.BanMembers); + + if (guild.GetBanAsync(toUnban.Id) == null) + throw new ApplicationException(Messages.UserNotBanned); + + await guild.RemoveBanAsync(toUnban, requestOptions); + await Utils.SilentSendAsync(channel, string.Format(Messages.UnbanResponse, toUnban.Mention, Utils.WrapInline(reason))); await Utils.SilentSendAsync(await guild.GetSystemChannelAsync(), notification); diff --git a/Boyfriend/Commands/UnmuteCommand.cs b/Boyfriend/Commands/UnmuteCommand.cs index 75eec78..9710d5a 100644 --- a/Boyfriend/Commands/UnmuteCommand.cs +++ b/Boyfriend/Commands/UnmuteCommand.cs @@ -9,49 +9,45 @@ 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 UnmuteMember(context.Guild, context.Channel as ITextChannel, context.Guild.GetUser(context.User.Id), + await Utils.ParseMember(context.Guild, args[0]), Utils.JoinString(args, 1)); + } + + public static async Task UnmuteMember(IGuild guild, ITextChannel? channel, IGuildUser author, IGuildUser toUnmute, + string reason) { + await CommandHandler.CheckPermissions(author, GuildPermission.ModerateMembers, GuildPermission.ManageRoles); await CommandHandler.CheckInteractions(author, toUnmute); - var role = Utils.GetMuteRole(context.Guild); + var authorMention = author.Mention; + var config = Boyfriend.GetGuildConfig(guild); + var notification = string.Format(Messages.MemberUnmuted, authorMention, toUnmute.Mention, + Utils.WrapInline(reason)); + var requestOptions = Utils.GetRequestOptions($"({Utils.GetNameAndDiscrim(author)}) {reason}"); + var role = Utils.GetMuteRole(guild); + if (role != null) { if (toUnmute.RoleIds.All(x => x != role.Id)) { - var config = Boyfriend.GetGuildConfig(context.Guild); var rolesRemoved = config.RolesRemovedOnMute; - foreach (var roleId in rolesRemoved![toUnmute.Id]) await toUnmute.AddRoleAsync(roleId); + await toUnmute.AddRolesAsync(rolesRemoved![toUnmute.Id]); rolesRemoved.Remove(toUnmute.Id); await config.Save(); throw new ApplicationException(Messages.RolesReturned); } - } - if (role != null && toUnmute.RoleIds.All(x => x != role.Id) || - toUnmute.TimedOutUntil == null || toUnmute.TimedOutUntil.Value.ToUnixTimeMilliseconds() - < DateTimeOffset.Now.ToUnixTimeMilliseconds()) - throw new ApplicationException(Messages.MemberNotMuted); - 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)); - var role = Utils.GetMuteRole(guild); - - if (role != null) { - await toUnmute.RemoveRoleAsync(role); - var config = Boyfriend.GetGuildConfig(guild); + if (toUnmute.RoleIds.All(x => x != role.Id)) + throw new ApplicationException(Messages.MemberNotMuted); + await toUnmute.RemoveRoleAsync(role, requestOptions); if (config.RolesRemovedOnMute!.ContainsKey(toUnmute.Id)) { - foreach (var roleId in config.RolesRemovedOnMute[toUnmute.Id]) await toUnmute.AddRoleAsync(roleId); + await toUnmute.AddRolesAsync(config.RolesRemovedOnMute[toUnmute.Id]); config.RolesRemovedOnMute.Remove(toUnmute.Id); await config.Save(); } } else { + if (toUnmute.TimedOutUntil == null || toUnmute.TimedOutUntil.Value.ToUnixTimeMilliseconds() + < DateTimeOffset.Now.ToUnixTimeMilliseconds()) + throw new ApplicationException(Messages.MemberNotMuted); + await toUnmute.RemoveTimeOutAsync(); } diff --git a/Boyfriend/EventHandler.cs b/Boyfriend/EventHandler.cs index 3ca9897..0435da4 100644 --- a/Boyfriend/EventHandler.cs +++ b/Boyfriend/EventHandler.cs @@ -21,10 +21,12 @@ public class EventHandler { await Boyfriend.SetupGuildConfigs(); var i = new Random().Next(3); + foreach (var guild in Boyfriend.Client.Guilds) { var config = Boyfriend.GetGuildConfig(guild); - Messages.Culture = new CultureInfo(config.Lang!); var channel = guild.GetTextChannel(config.BotLogChannel.GetValueOrDefault(0)); + Messages.Culture = new CultureInfo(config.Lang!); + if (!config.ReceiveStartupMessages.GetValueOrDefault(true) || channel == null) continue; await channel.SendMessageAsync(string.Format(Messages.Ready, Utils.GetBeep(config.Lang!, i))); } @@ -33,6 +35,7 @@ public class EventHandler { private static async Task MessageDeletedEvent(Cacheable message, Cacheable channel) { var msg = message.Value; + var toSend = msg == null ? string.Format(Messages.UncachedMessageDeleted, Utils.MentionChannel(channel.Id)) : string.Format(Messages.CachedMessageDeleted, msg.Author.Mention) + @@ -43,21 +46,23 @@ public class EventHandler { private static async Task MessageReceivedEvent(SocketMessage messageParam) { if (messageParam is not SocketUserMessage message) return; + + var argPos = 0; var user = (IGuildUser) message.Author; var guild = user.Guild; - var argPos = 0; - var guildConfig = Boyfriend.GetGuildConfig(guild); + var prev = ""; + var prevFailsafe = ""; + var prevs = await message.Channel.GetMessagesAsync(3).FlattenAsync(); + var prevsArray = prevs as IMessage[] ?? prevs.ToArray(); + Messages.Culture = new CultureInfo(guildConfig.Lang!); + if ((message.MentionedUsers.Count > 3 || message.MentionedRoles.Count > 2) && !user.GuildPermissions.MentionEveryone) 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 = ""; - var prevFailsafe = ""; try { prev = prevsArray[1].Content; prevFailsafe = prevsArray[2].Content; @@ -77,7 +82,9 @@ public class EventHandler { ISocketMessageChannel channel) { var msg = messageCached.Value; var nl = Environment.NewLine; + if (msg != null && msg.Content == messageSocket.Content) return; + var toSend = msg == null ? string.Format(Messages.UncachedMessageEdited, messageSocket.Author.Mention, Utils.MentionChannel(channel.Id)) + @@ -91,9 +98,11 @@ public class EventHandler { private static async Task UserJoinedEvent(SocketGuildUser user) { var guild = user.Guild; var config = Boyfriend.GetGuildConfig(guild); + if (config.SendWelcomeMessages.GetValueOrDefault(true)) 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()!)); } diff --git a/Boyfriend/GuildConfig.cs b/Boyfriend/GuildConfig.cs index 6b45aa7..57654c4 100644 --- a/Boyfriend/GuildConfig.cs +++ b/Boyfriend/GuildConfig.cs @@ -7,15 +7,19 @@ public class GuildConfig { public ulong? Id { get; } public string? Lang { get; set; } public string? Prefix { get; set; } + public bool? RemoveRolesOnMute { get; set; } public bool? UseSystemChannel { get; set; } public bool? SendWelcomeMessages { get; set; } public bool? ReceiveStartupMessages { 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; private set; } public GuildConfig(ulong id) { @@ -25,6 +29,7 @@ public class GuildConfig { public void Validate() { if (Id == null) throw new Exception("Something went horribly, horribly wrong"); + Lang ??= "ru"; Messages.Culture = new CultureInfo(Lang); Prefix ??= "!"; @@ -43,6 +48,7 @@ public class GuildConfig { public async Task Save() { Validate(); RolesRemovedOnMute!.TrimExcess(); + await File.WriteAllTextAsync("config_" + Id + ".json", JsonConvert.SerializeObject(this)); } } diff --git a/Boyfriend/Utils.cs b/Boyfriend/Utils.cs index eedeaf7..e653b86 100644 --- a/Boyfriend/Utils.cs +++ b/Boyfriend/Utils.cs @@ -16,6 +16,7 @@ public static class Utils { 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]; } @@ -38,11 +39,9 @@ public static class Utils { return $"<#{id}>"; } - public static async Task StartDelayed(Task toRun, TimeSpan delay, Func? condition = null) { + public static async Task StartDelayed(Task toRun, TimeSpan delay) { await Task.Delay(delay); - var conditionResult = condition?.Invoke() ?? true; - if (conditionResult) - toRun.Start(); + toRun.Start(); } private static ulong ParseMention(string mention) { @@ -52,8 +51,7 @@ public static class Utils { private static ulong? ParseMentionNullable(string mention) { try { return ParseMention(mention) == 0 ? throw new FormatException() : ParseMention(mention); - } - catch (FormatException) { + } catch (FormatException) { return null; } } @@ -99,6 +97,7 @@ public static class Utils { 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) {} @@ -111,4 +110,14 @@ public static class Utils { public static string JoinString(string[] args, int startIndex) { return string.Join(" ", args, startIndex, args.Length - startIndex); } + + public static string GetNameAndDiscrim(IUser user) { + return $"{user.Username}#{user.Discriminator}"; + } + + public static RequestOptions GetRequestOptions(string reason) { + var options = RequestOptions.Default; + options.AuditLogReason = reason; + return options; + } }