From 973170e6f69154031aff3ae3773d12efb277ee22 Mon Sep 17 00:00:00 2001 From: CPULL Date: Mon, 16 Aug 2021 18:03:14 +0200 Subject: [PATCH 01/54] =?UTF-8?q?=F0=9F=A9=B9=20improved=20the=20logging?= =?UTF-8?q?=20for=20users=20joining=20and=20leaving?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UPBot Code/Actions/MembersTracking.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/UPBot Code/Actions/MembersTracking.cs b/UPBot Code/Actions/MembersTracking.cs index 63299af..57b4cd5 100644 --- a/UPBot Code/Actions/MembersTracking.cs +++ b/UPBot Code/Actions/MembersTracking.cs @@ -31,17 +31,19 @@ public static async Task DiscordMemberRemoved(DiscordClient client, DSharpPlus.E public static async Task DiscordMemberAdded(DiscordClient client, DSharpPlus.EventArgs.GuildMemberAddEventArgs args) { if (tracking == null) tracking = new Dictionary(); if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); - tracking[args.Member.Id] = DateTime.Now; - await Task.Delay(15000); - if (tracking.ContainsKey(args.Member.Id)) { - string msgC = UtilityFunctions.GetEmojiSnowflakeID(EmojiEnum.OK) + " User " + args.Member.Mention + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; - string msgL = "+ User " + args.Member.DisplayName + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; + _ = SomethingAsync(args.Member.Id, args.Member.DisplayName, args.Member.Mention, args.Guild.MemberCount); + await Task.Delay(10); + } + + static async Task SomethingAsync(ulong id, string name, string mention, int numMembers) { + await Task.Delay(25000); + if (tracking.ContainsKey(id)) { + string msgC = UtilityFunctions.GetEmojiSnowflakeID(EmojiEnum.OK) + " User " + mention + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; + string msgL = "+ User " + name + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; await trackChannel.SendMessageAsync(msgC); UtilityFunctions.Log(msgL); - tracking.Remove(args.Member.Id); + tracking.Remove(id); } - await Task.Delay(10); } - } From 66a4a2665be0ebedcba3306250e7b45d7d132881 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 19:41:51 +0200 Subject: [PATCH 02/54] Edited WhoIs.cs - Added description to 'whois' command - Changed the code in 'WhoIs.cs' to use the 'BuildEmbed' utility function, instead of doing everything manually. Code has been cleaned up. --- UPBot Code/Commands/WhoIs.cs | 45 +++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/UPBot Code/Commands/WhoIs.cs b/UPBot Code/Commands/WhoIs.cs index deecde2..938466c 100644 --- a/UPBot Code/Commands/WhoIs.cs +++ b/UPBot Code/Commands/WhoIs.cs @@ -11,11 +11,14 @@ public class WhoIs : BaseCommandModule { [Command("whois")] + [Aliases("userinfo")] + [Description("Get information about a specific user.")] public async Task WhoIsCommand(CommandContext ctx) { // Basic version without parameters await GenerateWhoIs(ctx, null); } [Command("whoami")] + [Description("Get information about your own Discord account.")] public async Task WhoAmICommand(CommandContext ctx) { // Alternate version without parameters await GenerateWhoIs(ctx, null); } @@ -31,12 +34,7 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { m = ctx.Member; } bool you = m == ctx.Member; - - DiscordEmbedBuilder b = new DiscordEmbedBuilder(); - b.Title = "Who Is user " + m.DisplayName + "#" + m.Discriminator; - b.WithThumbnail(m.AvatarUrl, 64, 64); - - b.WithColor(m.Color); + DateTimeOffset jdate = m.JoinedAt.UtcDateTime; string joined = jdate.Year + "/" + jdate.Month + "/" + jdate.Day; DateTimeOffset cdate = m.CreationTimestamp.UtcDateTime; @@ -45,24 +43,29 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { int daysJ = (int)(DateTime.Now - m.JoinedAt.DateTime).TotalDays; int daysA = (int)(DateTime.Now - m.CreationTimestamp.DateTime).TotalDays; double years = daysA / 365.25; - b.WithDescription(m.Username + " joined on " + joined + " (" + daysJ + " days)\n Account created on " + creation + " (" + daysA + " days, " + years.ToString("N1") + " years)"); - b.AddField("Is you", you ? "✓" : "❌", true); - b.AddField("Is a bot", m.IsBot ? "🤖" : "❌", true); - b.AddField("Is the boss", m.IsOwner ? "👑" : "❌", true); - b.AddField("Is Muted", m.IsMuted ? "✓" : "❌", true); - b.AddField("Is Deafened", m.IsDeafened ? "✓" : "❌", true); + string title = "Who is the user " + m.DisplayName + "#" + m.Discriminator; + string description = m.Username + " joined on " + joined + " (" + daysJ + " days)\n Account created on " + + creation + " (" + daysA + " days, " + years.ToString("N1") + " years)"; + var embed = UtilityFunctions.BuildEmbed(title, description, m.Color); + embed.WithThumbnail(m.AvatarUrl, 64, 64); + + embed.AddField("Is you", you ? "✓" : "❌", true); + embed.AddField("Is a bot", m.IsBot ? "🤖" : "❌", true); + embed.AddField("Is the boss", m.IsOwner ? "👑" : "❌", true); + embed.AddField("Is Muted", m.IsMuted ? "✓" : "❌", true); + embed.AddField("Is Deafened", m.IsDeafened ? "✓" : "❌", true); - if (m.Locale != null) b.AddField("Speaks", m.Locale, true); - if (m.Nickname != null) b.AddField("Is called", m.Nickname, true); - b.AddField("Avatar Hex Color", m.Color.ToString(), true); + if (m.Locale != null) embed.AddField("Speaks", m.Locale, true); + if (m.Nickname != null) embed.AddField("Is called", m.Nickname, true); + embed.AddField("Avatar Hex Color", m.Color.ToString(), true); if (m.PremiumSince != null) { DateTimeOffset bdate = ((DateTimeOffset)m.PremiumSince).UtcDateTime; string booster = bdate.Year + "/" + bdate.Month + "/" + bdate.Day; - b.AddField("Booster", "Form " + booster, true); + embed.AddField("Booster", "Form " + booster, true); } - if (m.Flags != null) b.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags + if (m.Flags != null) embed.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags string roles = ""; int num = 0; @@ -71,9 +74,9 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { num++; } if (num == 1) - b.AddField("Role", roles, false); + embed.AddField("Role", roles, false); else - b.AddField(num + " Roles", roles, false); + embed.AddField(num + " Roles", roles, false); string perms = ""; // Not all permissions are shown if (m.Permissions.HasFlag(DSharpPlus.Permissions.CreateInstantInvite)) perms += ", Invite"; @@ -93,8 +96,8 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageEmojis)) perms += ", Manage Emojis"; if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseSlashCommands)) perms += ", Use Bot"; if (m.Permissions.HasFlag(DSharpPlus.Permissions.UsePublicThreads)) perms += ", Use Threads"; - if (perms.Length > 0) b.AddField("Permissions", perms.Substring(2), false); + if (perms.Length > 0) embed.AddField("Permissions", perms.Substring(2), false); - return ctx.RespondAsync(b.Build()); + return ctx.RespondAsync(embed.Build()); } } \ No newline at end of file From d9f8c9c2dabb8b49c43f961ac6beb6661d31381b Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 19:58:44 +0200 Subject: [PATCH 03/54] Changed redundant character in 'HelpLanguages.cs' - Please take care of what you commit and avoid typos :) --- UPBot Code/Commands/HelpLanguages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 0812692..0c84bb9 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -70,7 +70,7 @@ public async Task ErrorMessage(CommandContext ctx) { public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string TypeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { UtilityFunctions.LogUserCommand(ctx); - lang = NormalizeLanguage(lang);s + lang = NormalizeLanguage(lang); if (lang == null) await ErrorMessage(ctx); From 36b1cb53f4ec7e32f9d9ec0ba623b2f42c1551e8 Mon Sep 17 00:00:00 2001 From: CPULL Date: Mon, 16 Aug 2021 22:07:05 +0200 Subject: [PATCH 04/54] Fixed the null problem with BannedWords --- UPBot Code/Commands/BannedWords.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index 02bea3e..257e87e 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -16,15 +16,15 @@ public class BannedWords : BaseCommandModule { private static List bannedWords = null; - private static Regex valid = new Regex(@"^[a-zA-Z0-9]+$"); - private static Regex letters = new Regex(@"[a-zA-Z0-9]"); + readonly static Regex valid = new Regex(@"^[a-zA-Z0-9]+$"); + readonly static Regex letters = new Regex(@"[a-zA-Z0-9]"); private const string directoryName = "Restrictions"; public static void Init() { + bannedWords = new List(); string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); if (!File.Exists(path)) return; string[] all = File.ReadAllLines(path); - bannedWords = new List(); foreach (string line in all) { BannedWord word = new BannedWord(line); if (word.word == null) continue; @@ -127,6 +127,7 @@ void SaveWord(BannedWord w) { try { using (StreamWriter sw = File.AppendText(path)) { sw.Write(w.ToString()); + sw.FlushAsync(); } } catch (Exception e) { UtilityFunctions.Log(e.Message); @@ -147,6 +148,7 @@ void SaveList() { using (StreamWriter sw = File.CreateText(path)) { foreach (BannedWord w in bannedWords) { sw.Write(w.ToString()); + sw.FlushAsync(); } } } catch (Exception e) { @@ -208,7 +210,7 @@ internal static async Task CheckMessage(DiscordClient client, MessageCreateEvent return; } foreach (DiscordRole role in member.Roles) { - // if (role.Id == 831050318171078718ul /* Helper */ || role.Id == 830901743624650783ul /* Mod */ || role.Id == 830901562960117780ul /* Owner */) return; + if (role.Id == 831050318171078718ul /* Helper */ || role.Id == 830901743624650783ul /* Mod */ || role.Id == 830901562960117780ul /* Owner */) return; } string msg = args.Message.Content.ToLowerInvariant(); From 984572fe52e0d03e3de16b3ba6cef7a3550c9be3 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 22:22:50 +0200 Subject: [PATCH 05/54] Bugfix 'delete @user x' and fixed typo - Executing the command, which is supposed to delete the last x messages of a specified user deleted the last x - 1 messages, unlike the normal 'delete' command without specifying a user. This has been fixed! - Fixed typo in 'whois' command (booster "Form" to "From") --- UPBot Code/Commands/Delete.cs | 2 +- UPBot Code/Commands/WhoIs.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UPBot Code/Commands/Delete.cs b/UPBot Code/Commands/Delete.cs index 8196295..92618d6 100644 --- a/UPBot Code/Commands/Delete.cs +++ b/UPBot Code/Commands/Delete.cs @@ -61,7 +61,7 @@ public async Task DeleteCommand(CommandContext ctx, DiscordMember targetUser, in bool limitExceeded = CheckLimit(count); var allMessages = ctx.Channel.GetMessagesAsync().Result; // Get last 100 messages - var userMessages = allMessages.Where(x => x.Author == targetUser).Take(count); + var userMessages = allMessages.Where(x => x.Author == targetUser).Take(count + 1); await DeleteMessages(ctx, userMessages); await Success(ctx, limitExceeded, count, targetUser); diff --git a/UPBot Code/Commands/WhoIs.cs b/UPBot Code/Commands/WhoIs.cs index 938466c..4d14048 100644 --- a/UPBot Code/Commands/WhoIs.cs +++ b/UPBot Code/Commands/WhoIs.cs @@ -63,7 +63,7 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { if (m.PremiumSince != null) { DateTimeOffset bdate = ((DateTimeOffset)m.PremiumSince).UtcDateTime; string booster = bdate.Year + "/" + bdate.Month + "/" + bdate.Day; - embed.AddField("Booster", "Form " + booster, true); + embed.AddField("Booster", "From " + booster, true); } if (m.Flags != null) embed.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags From 782b6fbbc91b8cbcea3ef4f881759f0290194804 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 22:41:40 +0200 Subject: [PATCH 06/54] Listing CC's - Users can now get a list of all Custom Commands (CC's) with the name and the aliases in parenthesis - New error added ('UtilityFunctions.cs') = "Unknown error" and this error is currently being handled in the ErrorCallback of 'CustomCommandsService.cs' - Added new color to common colors ("Yellow") --- UPBot Code/Commands/CustomCommandsService.cs | 24 ++++++++++++++++++++ UPBot Code/UtilityFunctions.cs | 2 ++ 2 files changed, 26 insertions(+) diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 5c17e61..26cde8b 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -112,6 +112,27 @@ public async Task EditCommand(CommandContext ctx, string name) } } + [Command("cclist")] + [Aliases("listcc")] + [Description("Get a list of all Custom Commands (CC's).")] + public async Task ListCC(CommandContext ctx) + { + if (Commands.Count <= 0) + { + await ErrorCallback(CommandErrors.UnknownError, ctx); + return; + } + + string allCommands = string.Empty; + foreach (var cmd in Commands) + { + if(cmd.Names.Length > 0) + allCommands += $"- {cmd.Names[0]} ({(cmd.Names.Length > 1 ? string.Join(", ", cmd.Names.Skip(1)) : string.Empty)}){System.Environment.NewLine}"; + } + + await UtilityFunctions.BuildEmbedAndExecute("CC List", allCommands, UtilityFunctions.Yellow, ctx, true); + } + internal static async Task LoadCustomCommands() { string path = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "CustomCommands"); @@ -172,6 +193,9 @@ private async Task ErrorCallback(CommandErrors error, CommandContext ctx, params else throw new System.ArgumentException("This error type 'CommandErrors.CommandExists' requires a string"); break; + case CommandErrors.UnknownError: + await UtilityFunctions.BuildEmbedAndExecute("Error", "Unknown error!", UtilityFunctions.Red, ctx, false); + break; } } diff --git a/UPBot Code/UtilityFunctions.cs b/UPBot Code/UtilityFunctions.cs index 3b2d76b..305629b 100644 --- a/UPBot Code/UtilityFunctions.cs +++ b/UPBot Code/UtilityFunctions.cs @@ -18,6 +18,7 @@ public static class UtilityFunctions public static readonly DiscordColor Red = new DiscordColor("#f50f48"); public static readonly DiscordColor Green = new DiscordColor("#32a852"); public static readonly DiscordColor LightBlue = new DiscordColor("#34cceb"); + public static readonly DiscordColor Yellow = new DiscordColor("#f5bc42"); // Fields relevant for InitClient() private static DiscordClient client; @@ -216,4 +217,5 @@ public enum CommandErrors InvalidParams, InvalidParamsDelete, CommandExists, + UnknownError } From f6a9be900257c34285fb8f71165ca68e256f413d Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 00:02:57 +0200 Subject: [PATCH 07/54] Edit CC's - You can now, not only edit the content of a CC, but also edit the name and aliases of a CC. Use the `cceditname` (or an alias) command for that: `cceditname oldMainName newMainName aliasesIfDesired` - Changed main command names for CC's to start with CC (instead of 'deletecc', the main name is 'ccdelete' now for example), but the aliases are still there. I also added a few more aliases to one or another command. - The 'ErrorCallback' function is now global for every error and command (in 'UtilityFunctions'). The error feedbacks are defined only once, in this static function. - Improved the 'ErrorCallback' function to increase readability - Added description to each parameter (for \help command) - Added two new errors ("MissingCommand" and "NoCustomCommands") --- UPBot Code/Commands/CustomCommandsService.cs | 87 +++++++++++++------- UPBot Code/Commands/Delete.cs | 27 ++---- UPBot Code/UtilityFunctions.cs | 45 +++++++++- 3 files changed, 105 insertions(+), 54 deletions(-) diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 26cde8b..31fe3de 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -27,13 +27,13 @@ public class CustomCommandsService : BaseCommandModule "be careful what you type!\n\n**Usage:**\n\n- `newcc name` (without alias)\n- `newcc name alias1 alias2`" + " (with 2 aliases)\n\nThis command can only be invoked by a Mod.")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task CreateCommand(CommandContext ctx, params string[] names) + public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of all aliases. The first term is the **main name**, the other ones, separated by a space, are aliases")] params string[] names) { foreach (var name in names) { if (DiscordClient.GetCommandsNext().RegisteredCommands.ContainsKey(name)) // Check if there is a command with one of the names already { - await ErrorCallback(CommandErrors.CommandExists, ctx, name); + await UtilityFunctions.ErrorCallback(CommandErrors.CommandExists, ctx, name); return; } @@ -41,7 +41,7 @@ public async Task CreateCommand(CommandContext ctx, params string[] names) { if (cmd.Names.Contains(name)) // Check if there is already a CC with one of the names { - await ErrorCallback(CommandErrors.CommandExists, ctx, name); + await UtilityFunctions.ErrorCallback(CommandErrors.CommandExists, ctx, name); return; } } @@ -56,13 +56,13 @@ public async Task CreateCommand(CommandContext ctx, params string[] names) await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, false); } - [Command("delcc")] - [Aliases("deletecc", "removecc")] + [Command("ccdel")] + [Aliases("ccdelete", "ccremove", "delcc", "deletecc", "removecc")] [Description("**Delete** a Custom Command (so-called 'CC').\n**Attention!** Use the main name of the CC " + "you entered first when you created it, **not an alias!**\nThe CC will be irrevocably deleted." + "\n\nThis command can only be invoked by a Mod.")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task DeleteCommand(CommandContext ctx, string name) + public async Task DeleteCommand(CommandContext ctx, [Description("Main name of the CC you want to delete")] string name) { string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, name, ".txt"); if (File.Exists(filePath)) @@ -76,13 +76,13 @@ public async Task DeleteCommand(CommandContext ctx, string name) } } - [Command("editcc")] - [Aliases("ccedit")] + [Command("ccedit")] + [Aliases("editcc")] [Description("**Edit** the **content** of a Custom Command (so-called 'CC')." + "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**" + "\n\nThis command can only be invoked by a Mod.")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task EditCommand(CommandContext ctx, string name) + public async Task EditCommand(CommandContext ctx, [Description("Main name of the CC you want to edit")] string name) { string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, name, ".txt"); if (File.Exists(filePath)) @@ -106,10 +106,54 @@ public async Task EditCommand(CommandContext ctx, string name) await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, false); } else + await UtilityFunctions.ErrorCallback(CommandErrors.MissingCommand, ctx); + } + + [Command("cceditname")] + [Aliases("ccnameedit", "editnamecc")] + [Description("**Edit** the **names** (including aliases) of an existing CC." + + "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**" + + "\n\nThis command can only be invoked by a Mod.")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EditCommandName(CommandContext ctx, [Description("A list of new names and aliases, " + + "__**BUT**__ the **FIRST** term is the current **main name** " + + "of the CC whose name you want to edit, the **SECOND** term " + + "is the new **main name** and all the other terms are new aliases")] params string[] names) + { + if (names.Length < 2) { - string embedMessage = "There is no Custom Command with this name! Please don't use an alias, use the original name!"; - await UtilityFunctions.BuildEmbedAndExecute("Error", embedMessage, UtilityFunctions.Red, ctx, true); + await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParams, ctx); + return; } + + string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, names[0], ".txt"); + if (File.Exists(filePath)) + { + if (TryGetCommand(names[0], out CustomCommand command)) + command.EditCommand(names.Skip(1).ToArray()); + + string content = string.Empty; + using (StreamReader sr = File.OpenText(filePath)) + { + string c; + await sr.ReadLineAsync(); + while ((c = await sr.ReadLineAsync()) != null) + content += c + System.Environment.NewLine; + } + + string newPath = UtilityFunctions.ConstructPath(DirectoryNameCC, names[1], ".txt"); + File.Move(filePath, newPath); + using (StreamWriter sw = File.CreateText(newPath)) + { + await sw.WriteLineAsync(string.Join(',', names.Skip(1))); + await sw.WriteLineAsync(content); + } + + string embedDescription = "The CC names have been successfully edited."; + await UtilityFunctions.BuildEmbedAndExecute("Success", embedDescription, UtilityFunctions.Green, ctx, false); + } + else + await UtilityFunctions.ErrorCallback(CommandErrors.MissingCommand, ctx); } [Command("cclist")] @@ -119,7 +163,7 @@ public async Task ListCC(CommandContext ctx) { if (Commands.Count <= 0) { - await ErrorCallback(CommandErrors.UnknownError, ctx); + await UtilityFunctions.ErrorCallback(CommandErrors.NoCustomCommands, ctx); return; } @@ -180,25 +224,6 @@ private async Task WriteToFile(CustomCommand command) } } - private async Task ErrorCallback(CommandErrors error, CommandContext ctx, params object[] additionalParams) - { - switch (error) - { - case CommandErrors.CommandExists: - if (additionalParams[0] is string name) - { - string message = $"There is already a command containing the alias {additionalParams[0]}"; - await UtilityFunctions.BuildEmbedAndExecute("Error", message, UtilityFunctions.Red, ctx, true); - } - else - throw new System.ArgumentException("This error type 'CommandErrors.CommandExists' requires a string"); - break; - case CommandErrors.UnknownError: - await UtilityFunctions.BuildEmbedAndExecute("Error", "Unknown error!", UtilityFunctions.Red, ctx, false); - break; - } - } - private async Task WaitForContent(CommandContext ctx, string name) { string embedMessage = $"Please input the content of the CC **{name}** in one single message. Your next message will count as the content."; diff --git a/UPBot Code/Commands/Delete.cs b/UPBot Code/Commands/Delete.cs index 92618d6..084c6e0 100644 --- a/UPBot Code/Commands/Delete.cs +++ b/UPBot Code/Commands/Delete.cs @@ -26,12 +26,12 @@ public class Delete : BaseCommandModule "\nThis command can only be invoked by a Helper or Mod.")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task DeleteCommand(CommandContext ctx, int count) + public async Task DeleteCommand(CommandContext ctx, [Description("How many messages should be deleted?")] int count) { UtilityFunctions.LogUserCommand(ctx); if (count <= 0) { - await ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); + await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); return; } @@ -49,12 +49,13 @@ public async Task DeleteCommand(CommandContext ctx, int count) [Command("delete")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task DeleteCommand(CommandContext ctx, DiscordMember targetUser, int count) + public async Task DeleteCommand(CommandContext ctx, [Description("Whose last x messages should get deleted?")]DiscordMember targetUser, + [Description("How many messages should get deleted?")] int count) { UtilityFunctions.LogUserCommand(ctx); if (count <= 0) { - await ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); + await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); return; } @@ -98,24 +99,6 @@ private async Task Success(CommandContext ctx, bool limitExceeded, int count, Di await message.DeleteAsync(); } - private async Task ErrorCallback(CommandErrors error, CommandContext ctx, params object[] additionalParams) - { - string message = string.Empty; - switch (error) - { - case CommandErrors.InvalidParams: - message = $"Invalid params for the command {ctx?.Command.Name}."; - break; - case CommandErrors.InvalidParamsDelete: - if (additionalParams[0] is int count) - message = $"You can't delete {count} messages. Try to eat {count} apples, does that make sense?"; - else - goto case CommandErrors.InvalidParams; - break; - } - await UtilityFunctions.BuildEmbedAndExecute("Error", message, UtilityFunctions.Red, ctx, true); - } - private bool CheckLimit(int count) { return count > MessageLimit; diff --git a/UPBot Code/UtilityFunctions.cs b/UPBot Code/UtilityFunctions.cs index 305629b..c62cc5b 100644 --- a/UPBot Code/UtilityFunctions.cs +++ b/UPBot Code/UtilityFunctions.cs @@ -193,6 +193,47 @@ internal static void Log(string msg) { string m = e.Message; } } + + internal static async Task ErrorCallback(CommandErrors error, CommandContext ctx, params object[] additionalParams) + { + DiscordColor red = UtilityFunctions.Red; + string message = string.Empty; + bool respond = false; + switch (error) + { + case CommandErrors.CommandExists: + respond = true; + if (additionalParams[0] is string name) + message = $"There is already a command containing the alias {additionalParams[0]}"; + else + throw new System.ArgumentException("This error type 'CommandErrors.CommandExists' requires a string"); + break; + case CommandErrors.UnknownError: + message = "Unknown error!"; + respond = false; + break; + case CommandErrors.InvalidParams: + message = "The given parameters are invalid. Enter \\help [commandName] to get help with the usage of the command."; + respond = true; + break; + case CommandErrors.InvalidParamsDelete: + if (additionalParams[0] is int count) + message = $"You can't delete {count} messages. Try to eat {count} apples, does that make sense?"; + else + goto case CommandErrors.InvalidParams; + break; + case CommandErrors.MissingCommand: + message = "There is no command with this name! If it's a CC, please don't use an alias, use the original name!"; + respond = true; + break; + case CommandErrors.NoCustomCommands: + message = "There are no CC's currently."; + respond = false; + break; + } + + await UtilityFunctions.BuildEmbedAndExecute("Error", message, red, ctx, respond); + } } public enum EmojiEnum { @@ -217,5 +258,7 @@ public enum CommandErrors InvalidParams, InvalidParamsDelete, CommandExists, - UnknownError + UnknownError, + MissingCommand, + NoCustomCommands } From 9b1e5109ee908b79a66f7096cecb26e37c30ca95 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 17:49:21 +0200 Subject: [PATCH 08/54] Added flexible directory name to 'ConstructPath' function and changed all references - Added the ability to specify the final directory name, you want to use for the file, when calling the 'ConstructPath' function in 'UtilityFunctions'. For further information, please refer to the summary and read this commit carefully. - Changed all references to this function to include this directoryName. For that, I added two constant strings for 'BannedWords.cs' and 'CustomCommandService.cs' ('CustomCommand.cs' shares string with the mentioned class). The occurrence in 'UtilityFunctions.cs' uses a hard-coded literal. --- UPBot Code/Commands/BannedWords.cs | 7 ++++--- UPBot Code/Commands/CustomCommand.cs | 2 +- UPBot Code/Commands/CustomCommandsService.cs | 5 +++-- UPBot Code/UtilityFunctions.cs | 9 ++++++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index f1a8efd..43371f0 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -18,9 +18,10 @@ public class BannedWords : BaseCommandModule { private static List bannedWords = null; private static Regex valid = new Regex(@"^[a-zA-Z0-9]+$"); private static Regex letters = new Regex(@"[a-zA-Z0-9]"); + private const string directoryName = "Restrictions"; public static void Init() { - string path = UtilityFunctions.ConstructPath("BannedWords", ".txt"); + string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); if (!File.Exists(path)) return; string[] all = File.ReadAllLines(path); bannedWords = new List(); @@ -121,7 +122,7 @@ private async Task> HandleAddRemoveOfBannedWords(CommandCon } void SaveWord(BannedWord w) { - string path = UtilityFunctions.ConstructPath("BannedWords", ".txt"); + string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); if (!File.Exists(path)) File.CreateText(path); try { using (StreamWriter sw = File.AppendText(path)) { @@ -133,7 +134,7 @@ void SaveWord(BannedWord w) { } void SaveList() { - string path = UtilityFunctions.ConstructPath("BannedWords", ".txt"); + string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); if (File.Exists(path)) { try { File.Delete(path); diff --git a/UPBot Code/Commands/CustomCommand.cs b/UPBot Code/Commands/CustomCommand.cs index d01dc50..85c6055 100644 --- a/UPBot Code/Commands/CustomCommand.cs +++ b/UPBot Code/Commands/CustomCommand.cs @@ -10,7 +10,7 @@ public class CustomCommand public CustomCommand(string[] names, string content) { this.Names = names; - this.FilePath = UtilityFunctions.ConstructPath(names[0], ".txt"); + this.FilePath = UtilityFunctions.ConstructPath(CustomCommandsService.DirectoryNameCC, names[0], ".txt"); this.Content = content; } diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 57ab9ea..1f560cd 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -17,6 +17,7 @@ public class CustomCommandsService : BaseCommandModule { private static readonly List Commands = new List(); internal static DiscordClient DiscordClient { get; set; } + internal const string DirectoryNameCC = "CustomCommands"; [Command("newcc")] [Aliases("createcc", "addcc", "ccadd", "cccreate")] @@ -55,7 +56,7 @@ public async Task CreateCommand(CommandContext ctx, params string[] names) [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task DeleteCommand(CommandContext ctx, string name) { - string filePath = UtilityFunctions.ConstructPath(name, ".txt"); + string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, name, ".txt"); if (File.Exists(filePath)) { File.Delete(filePath); @@ -72,7 +73,7 @@ public async Task DeleteCommand(CommandContext ctx, string name) [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EditCommand(CommandContext ctx, string name) { - string filePath = UtilityFunctions.ConstructPath(name, ".txt"); + string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, name, ".txt"); if (File.Exists(filePath)) { string content = await WaitForContent(ctx, name); diff --git a/UPBot Code/UtilityFunctions.cs b/UPBot Code/UtilityFunctions.cs index 9beeedd..2dae765 100644 --- a/UPBot Code/UtilityFunctions.cs +++ b/UPBot Code/UtilityFunctions.cs @@ -43,7 +43,7 @@ public static void InitClient(DiscordClient c) { 876180793213464606ul // AutoRefactored = 12, }; sortableDateTimeFormat = CultureInfo.GetCultureInfo("en-US").DateTimeFormat; - string logPath = ConstructPath("BotLogs " + DateTime.Now.ToString("yyyyMMdd"), ".logs"); + string logPath = ConstructPath("Logs", "BotLogs " + DateTime.Now.ToString("yyyyMMdd"), ".logs"); if (File.Exists(logPath)) logs = new StreamWriter(logPath, append: true); else logs = File.CreateText(logPath); } @@ -64,9 +64,12 @@ public static string PluralFormatter(int count, string singular, string plural) /// with a given raw file name and the fileSuffix (file type) /// NOTE: The file suffix must contain a period (e.g. ".txt" or ".png") /// - public static string ConstructPath(string fileNameRaw, string fileSuffix) + /// The name of the final folder, in which the file will be saved + /// The name of the file (without file type) + /// The file-suffix (file-type, e.g. ".txt" or ".png") + public static string ConstructPath(string directoryName, string fileNameRaw, string fileSuffix) { - return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CustomCommands", fileNameRaw.Trim().ToLowerInvariant() + fileSuffix); + return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, directoryName, fileNameRaw.Trim().ToLowerInvariant() + fileSuffix); } /// From 24d659fe4ae58107a0e2da8283e4d38c8be6abed Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 18:08:09 +0200 Subject: [PATCH 09/54] Added description to 'delete' and all CC commands - Added descriptions to the 'delete' and all CC commands ('newcc', 'delcc', 'editcc'), which will be used when someone invokes the \help command for one of these. --- UPBot Code/Commands/CustomCommandsService.cs | 9 +++++++++ UPBot Code/Commands/Delete.cs | 3 +++ 2 files changed, 12 insertions(+) diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 1f560cd..8457cb4 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -21,6 +21,11 @@ public class CustomCommandsService : BaseCommandModule [Command("newcc")] [Aliases("createcc", "addcc", "ccadd", "cccreate")] + [Description("**Create** a new Custom Command (so-called 'CC') with a specified name and all aliases if desired " + + "(no duplicate alias allowed).\nAfter doing this, the bot will ask you to input the content, which will " + + "be displayed once someone invokes this CC. Your entire next message will be used for the content, so " + + "be careful what you type!\n\n**Usage:**\n\n- `newcc name` (without alias)\n-`newcc name alias1 alias2`" + + " (with 2 aliases)")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task CreateCommand(CommandContext ctx, params string[] names) { @@ -53,6 +58,8 @@ public async Task CreateCommand(CommandContext ctx, params string[] names) [Command("delcc")] [Aliases("deletecc", "removecc")] + [Description("**Delete** a Custom Command (so-called 'CC').\n**Attention!** Use the main name of the CC " + + "you entered first when you created it, **not an alias!**\nThe CC will be irrevocably deleted.")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task DeleteCommand(CommandContext ctx, string name) { @@ -70,6 +77,8 @@ public async Task DeleteCommand(CommandContext ctx, string name) [Command("editcc")] [Aliases("ccedit")] + [Description("**Edit** the **content** of a Custom Command (so-called 'CC')." + + "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EditCommand(CommandContext ctx, string name) { diff --git a/UPBot Code/Commands/Delete.cs b/UPBot Code/Commands/Delete.cs index 7581409..8196295 100644 --- a/UPBot Code/Commands/Delete.cs +++ b/UPBot Code/Commands/Delete.cs @@ -21,6 +21,9 @@ public class Delete : BaseCommandModule /// [Command("delete")] [Aliases("clear", "purge")] + [Description("Deletes the last x messages in the channel, the command was invoked in (e.g. `\\delete 10`)." + + "\nIt contains an overload to delete the last x messages of a specified user (e.g. `\\delete @User 10`)." + + "\nThis command can only be invoked by a Helper or Mod.")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task DeleteCommand(CommandContext ctx, int count) From 73111bd8501b4fa61d5345d2bc32d4eaddbc95e2 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 18:10:55 +0200 Subject: [PATCH 10/54] Addendum to command descriptions - Added some information about the accessibility of the CC commands (only Mod) --- UPBot Code/Commands/CustomCommandsService.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 8457cb4..9616250 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -25,7 +25,7 @@ public class CustomCommandsService : BaseCommandModule "(no duplicate alias allowed).\nAfter doing this, the bot will ask you to input the content, which will " + "be displayed once someone invokes this CC. Your entire next message will be used for the content, so " + "be careful what you type!\n\n**Usage:**\n\n- `newcc name` (without alias)\n-`newcc name alias1 alias2`" + - " (with 2 aliases)")] + " (with 2 aliases)\n\nThis command can only be invoked by a Mod.")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task CreateCommand(CommandContext ctx, params string[] names) { @@ -59,7 +59,8 @@ public async Task CreateCommand(CommandContext ctx, params string[] names) [Command("delcc")] [Aliases("deletecc", "removecc")] [Description("**Delete** a Custom Command (so-called 'CC').\n**Attention!** Use the main name of the CC " + - "you entered first when you created it, **not an alias!**\nThe CC will be irrevocably deleted.")] + "you entered first when you created it, **not an alias!**\nThe CC will be irrevocably deleted." + + "\n\nThis command can only be invoked by a Mod.")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task DeleteCommand(CommandContext ctx, string name) { @@ -78,7 +79,8 @@ public async Task DeleteCommand(CommandContext ctx, string name) [Command("editcc")] [Aliases("ccedit")] [Description("**Edit** the **content** of a Custom Command (so-called 'CC')." + - "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**")] + "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**" + + "\n\nThis command can only be invoked by a Mod.")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EditCommand(CommandContext ctx, string name) { From 469e59241f7e883b044aef1ebadade8f558eecc8 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 18:17:05 +0200 Subject: [PATCH 11/54] Bugfix: Accessed directory, that didn't exist - In one of the previous commits, I added the ability to define the final directory/ies (folder/s), the file should be found/created in, when calling the 'ConstructPath' function in 'UtilityFunctions.cs'. However, there was no check if the directory exists. Now, I added this into the function itself, so it creates the directory if it didn't exist already. - Removed redundant space in 'CustomCommandsService.cs' --- UPBot Code/Commands/CustomCommandsService.cs | 2 +- UPBot Code/UtilityFunctions.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 9616250..e449c34 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -103,7 +103,7 @@ public async Task EditCommand(CommandContext ctx, string name) command.EditCommand(content); string embedMessage = $"CC **{name}** successfully edited!"; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, false); + await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, false); } else { diff --git a/UPBot Code/UtilityFunctions.cs b/UPBot Code/UtilityFunctions.cs index 2dae765..3b2d76b 100644 --- a/UPBot Code/UtilityFunctions.cs +++ b/UPBot Code/UtilityFunctions.cs @@ -69,7 +69,11 @@ public static string PluralFormatter(int count, string singular, string plural) /// The file-suffix (file-type, e.g. ".txt" or ".png") public static string ConstructPath(string directoryName, string fileNameRaw, string fileSuffix) { - return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, directoryName, fileNameRaw.Trim().ToLowerInvariant() + fileSuffix); + string directoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, directoryName); + if (!Directory.Exists(Path.Combine(directoryPath))) + Directory.CreateDirectory(directoryPath); + + return Path.Combine(directoryPath, fileNameRaw.Trim().ToLowerInvariant() + fileSuffix); } /// From a192e213ee5502b1309078bbde695143091557a4 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 18:26:25 +0200 Subject: [PATCH 12/54] Small changes to avoid warnings and possible optimization for CC - Previously, the 'LoadCustomCommands()' function loaded all files in the base directory. Now, it only considers the 'CustomCommands' folder inside. - Avoid warning by changing `async void` to `async Task` and `awaiting` the function call in 'Program.cs' (the function is located in 'BannedWords.cs') --- UPBot Code/Commands/BannedWords.cs | 2 +- UPBot Code/Commands/CustomCommandsService.cs | 2 +- UPBot Code/Program.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index 43371f0..fe9d50d 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -196,7 +196,7 @@ async Task GetUserNameFromDiscord(ulong userId, CommandContext ctx) return await ctx.Client.GetUserAsync(userId); } - internal static async void CheckMessage(DiscordClient client, MessageCreateEventArgs args) { + internal static async Task CheckMessage(DiscordClient client, MessageCreateEventArgs args) { // Who is the author? If the bot or a mod then ignore if (args.Author.Equals(client.CurrentUser)) return; DiscordUser user = args.Author; diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index e449c34..16ce94f 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -118,7 +118,7 @@ internal static async Task LoadCustomCommands() if (!Directory.Exists(path)) Directory.CreateDirectory(path); - foreach (string fileName in Directory.GetFiles(System.AppDomain.CurrentDomain.BaseDirectory)) + foreach (string fileName in Directory.GetFiles(path)) { using (StreamReader sr = File.OpenText(fileName)) { diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index 17bec1e..52fdafa 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -31,7 +31,7 @@ static async Task MainAsync(string token, string prefix) { commands.RegisterCommands(Assembly.GetExecutingAssembly()); // Registers all defined commands BannedWords.Init(); - discord.MessageCreated += async (s, e) => { BannedWords.CheckMessage(s, e); }; + discord.MessageCreated += async (s, e) => { await BannedWords.CheckMessage(s, e); }; discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; discord.GuildMemberRemoved += MembersTracking.DiscordMemberRemoved; From 7aeaf3b76d451c231957353cb792c8ce4c6b2c00 Mon Sep 17 00:00:00 2001 From: CPULL Date: Mon, 16 Aug 2021 18:42:37 +0200 Subject: [PATCH 13/54] Improved Jonathan code for helplanguage --- UPBot Code/Commands/HelpLanguages.cs | 279 ++++++++++++++++++--------- 1 file changed, 185 insertions(+), 94 deletions(-) diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 9afe1b4..0c84bb9 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using DSharpPlus.CommandsNext; @@ -9,113 +9,204 @@ /// This command shows for the new users useful links to how to code on specific language. /// Command author: J0nathan550, Source Code: CPU /// -public class HelpLanguagesModel : BaseCommandModule -{ - private List lastRequests = new List(); - private Dictionary languageLinks = new Dictionary() - { - { "c#", "<:csharp:831465428214743060>!\nLink: https://youtu.be/GhQdlIFylQ8" }, - { "c++", "<:cpp:831465408874676273>!\nLink: https://youtu.be/vLnPwxZdW4Y" }, - { "python", "<:python:831465381016895500>!\nLink: https://youtu.be/rfscVS0vtbw" }, - { "java", "<:java:875852276017815634>!\nLink: https://youtu.be/grEKMHGYyns" } +public class HelpLanguagesModel : BaseCommandModule { + private List lastRequests = new List(); + + private Dictionary languageLinks = new Dictionary() // video course + { + { "C#", "<:csharp:831465428214743060>!\nLink: https://youtu.be/GhQdlIFylQ8" }, + { "C++", "<:cpp:831465408874676273>!\nLink: https://youtu.be/vLnPwxZdW4Y" }, + { "Python", "<:python:831465381016895500>!\nLink: https://youtu.be/rfscVS0vtbw" }, + { "JavaScript", "<:Javascript:876103767068647435>!\nLink: https://youtu.be/PkZNo7MFNFg" }, + { "Java", "<:java:875852276017815634>!\nLink: https://youtu.be/grEKMHGYyns" } + }; + private Dictionary languageCourseLink = new Dictionary() // site course + { + { "C#", "<:csharp:831465428214743060>!\nLink: https://www.w3schools.com/cs/"}, + { "C++", "<:cpp:831465408874676273>!\nLink: https://www.w3schools.com/cpp/"}, + { "Python", "<:python:831465381016895500>!\nLink: https://www.w3schools.com/python/"}, + { "JavaScript", "<:Javascript:876103767068647435>!\nLink: https://www.w3schools.com/js/" }, + { "Java", "<:java:875852276017815634>!\nLink: https://www.w3schools.com/java/" } }; - private Dictionary colors = new Dictionary() - { - { "c#", "812f84" }, - { "c++", "3f72db" }, - { "python", "d1e13b" }, - { "java", "e92c2c" } + + private Dictionary colors = new Dictionary() // colors for embed + { + { "C#", "812f84" }, + { "C++", "3f72db" }, + { "Python", "d1e13b" }, + { "JavaScript", "f8ff00"}, + { "Java", "e92c2c" } }; - - private string[] helpfulAnswers = - { - "Hello! @@@, here is a good tutorial about ///", - "Hey! hey! @@@, here is a sick tutorial about ///" + + private string[] helpfulAnswers = // words for video course + { + "Hello! @@@, here is a good video tutorial about ///", + "Hey! hey! @@@, here is a sick video tutorial about ///", + "Hello @@@, here is your video tutorial ///" + }; + private string[] helpFulCourseAnswers = { // words for site course + "Hello! @@@, here is a good course about ///", + "Hey! hey! @@@, here is a sick course about ///", + "Hello @@@, here is your course ///" }; - [Command("helplanguage")] - public async Task ErrorMessage(CommandContext ctx) - { - DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); - deb.Title = "Help Language - How To Use"; - deb.WithColor(new DiscordColor("ff0025")); - - DiscordMember member = ctx.Member; - deb.Description = member.Mention + " , Available commands: c#, c++, python, java. \n Write command like this: `/helplanguage c#`"; - await ctx.RespondAsync(deb.Build()); - } - - [Command("helplanguage")] - public async Task HelpCommand(CommandContext ctx, string lang) // C# - { + [Description("Gives good tutorials on specific language.")] // for \help helplanguage info + [Command("helplanguage")] + public async Task ErrorMessage(CommandContext ctx) { + DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); + deb.Title = "Help Language - How To Use"; + deb.WithColor(new DiscordColor("ff0025")); + + DiscordMember member = ctx.Member; + deb.Description = member.Mention + " , if you want to get video course about specific language type: `\\helplanguage video C#`" + + " \nIf you want to get full online course about specific language type: \n`\\helplanguage course C#`" + + " \nAvailable languages: `ะก#, C++, Python, JavaScript, Java`"; + await ctx.RespondAsync(deb.Build()); // \helplanguage + } + + [Description("For use this command you should type `\\helplanguage C#`")] + [Command("helplanguage")] + public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string TypeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# + { UtilityFunctions.LogUserCommand(ctx); + lang = NormalizeLanguage(lang); + if (lang == null) - await ErrorMessage(ctx); + await ErrorMessage(ctx); + else if (TypeOfHelp.ToLowerInvariant() == "video") { + if (!languageLinks.ContainsKey(lang)) { + await ErrorMessage(ctx); + } + else + await GenerateHelpfulAnswer(ctx, lang); + } + else if (TypeOfHelp.ToLowerInvariant() == "course") { + if (!languageCourseLink.ContainsKey(lang)) { + await ErrorMessage(ctx); + } + else + await GenerateHelpfulAnswerCourse(ctx, lang); + } + } + + private string NormalizeLanguage(string language) { + if (language == null) return null; + language = language.ToLowerInvariant(); + switch (language) { + case "c++": return "C++"; + case "cpp": return "C++"; + case "cp": return "C++"; + case "cs": return "C#"; + case "csharp": return "C#"; + case "c#": return "C#"; + case "java": return "Java"; + case "javascript": return "JavaScript"; + case "jscript": return "JavaScript"; + case "js": return "JavaScript"; + case "python": return "Python"; + case "phyton": return "Python"; + case "py": return "Python"; + } + return language; + } - lang = lang.Trim().ToLowerInvariant(); - if (!languageLinks.ContainsKey(lang)) - { - await ErrorMessage(ctx); - } - - await GenerateHelpfulAnswer(ctx, lang); + private Task GenerateHelpfulAnswer(CommandContext ctx, string language) { + DiscordMember member = ctx.Member; + ulong memberId = member.Id; + DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); + + deb.Title = $"Help Language - {language}"; + deb.WithColor(new DiscordColor(colors[language])); + + // Find the last request + LastRequestByMember lastRequest = null; + foreach (LastRequestByMember lr in lastRequests) { + if (lr.MemberId == memberId) { + lastRequest = lr; + break; + } } - private Task GenerateHelpfulAnswer(CommandContext ctx, string rawLang) + if (lastRequest == null) // No last request, create one + { + lastRequest = new LastRequestByMember { MemberId = memberId, DateTime = DateTime.Now, Num = 0 }; + lastRequests.Add(lastRequest); + } + else { + lastRequest.DateTime = DateTime.Now; + lastRequest.Num++; + if (lastRequest.Num >= helpfulAnswers.Length) + lastRequest.Num = 0; + } + string msg = helpfulAnswers[lastRequest.Num]; + msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) + .Replace("///", languageLinks[language]); + deb.Description = msg; + return ctx.RespondAsync(deb.Build()); + } + private Task GenerateHelpfulAnswerCourse(CommandContext ctx, string rawLang) { + + DiscordMember member = ctx.Member; + ulong memberId = member.Id; + DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); + + string language = ""; + if (rawLang == "cpp" || rawLang == "CPP" || rawLang == "c++" || rawLang == "C++") // all possible words to type (including the same words as command one) { - DiscordMember member = ctx.Member; - ulong memberId = member.Id; - DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); - - string language = ""; - if (rawLang == "cpp") - { - language = rawLang.ToUpperInvariant(); - rawLang = "c++"; - } - else - language = char.ToUpperInvariant(rawLang[0]) + rawLang.Substring(1); - - deb.Title = $"Help Language - {language}"; - deb.WithColor(new DiscordColor(colors[rawLang])); - - // Find the last request - LastRequestByMember lastRequest = null; - foreach (LastRequestByMember lr in lastRequests) - { - if (lr.MemberId == memberId) - { - lastRequest = lr; - break; - } - } - - if (lastRequest == null) // No last request, create one - { - lastRequest = new LastRequestByMember { MemberId = memberId, DateTime = DateTime.Now, Num = 0 }; - lastRequests.Add(lastRequest); - } - else - { - lastRequest.DateTime = DateTime.Now; - lastRequest.Num++; - if (lastRequest.Num >= helpfulAnswers.Length) - lastRequest.Num = 0; - } - - string msg = helpfulAnswers[lastRequest.Num]; - msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) - .Replace("///", languageLinks[rawLang]); - deb.Description = msg; - return ctx.RespondAsync(deb.Build()); + language = rawLang.ToUpperInvariant(); + language = rawLang = "C++"; } + else if (rawLang == "c#" || rawLang == "C#" || rawLang == "csharp" || rawLang == "CSharp") { + language = rawLang.ToUpperInvariant(); + language = rawLang = "C#"; + } + else if (rawLang == "python" || rawLang == "Python" || rawLang == "py" || rawLang == "PY") { + language = rawLang = "Python"; + } + else if (rawLang == "js" || rawLang == "JS" || rawLang == "javascript" || rawLang == "JavaScript") { + language = rawLang = "JavaScript"; + } + else if (rawLang == "java") { + language = rawLang = "Java"; + } + else + language = char.ToUpperInvariant(rawLang[0]) + rawLang.Substring(1); + + deb.Title = $"Help Language - {language}"; + deb.WithColor(new DiscordColor(colors[rawLang])); - public class LastRequestByMember + // Find the last request + LastRequestByMember lastRequest = null; + foreach (LastRequestByMember lr in lastRequests) { + if (lr.MemberId == memberId) { + lastRequest = lr; + break; + } + } + + if (lastRequest == null) // No last request, create one { - public ulong MemberId { get; set; } - public DateTime DateTime { get; set; } - public int Num { get; set; } + lastRequest = new LastRequestByMember { MemberId = memberId, DateTime = DateTime.Now, Num = 0 }; + lastRequests.Add(lastRequest); } + else { + lastRequest.DateTime = DateTime.Now; + lastRequest.Num++; + if (lastRequest.Num >= helpFulCourseAnswers.Length) + lastRequest.Num = 0; + } + string msg = helpFulCourseAnswers[lastRequest.Num]; + msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) + .Replace("///", languageCourseLink[rawLang]); + deb.Description = msg; + return ctx.RespondAsync(deb.Build()); + } + + public class LastRequestByMember { + public ulong MemberId { get; set; } + public DateTime DateTime { get; set; } + public int Num { get; set; } + } } From 324d159e4adc28ad9f26f4895ca1d7839d47a7e2 Mon Sep 17 00:00:00 2001 From: CPULL Date: Mon, 16 Aug 2021 19:01:47 +0200 Subject: [PATCH 14/54] Empty change to test --- UPBot Code/Commands/HelpLanguages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 0c84bb9..0812692 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -70,7 +70,7 @@ public async Task ErrorMessage(CommandContext ctx) { public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string TypeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { UtilityFunctions.LogUserCommand(ctx); - lang = NormalizeLanguage(lang); + lang = NormalizeLanguage(lang);s if (lang == null) await ErrorMessage(ctx); From d64f063186bc9c1b749557824a419cf788b4265e Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 19:16:24 +0200 Subject: [PATCH 15/54] Small changes to description - Just some small changes, too small to be mentioned --- UPBot Code/Commands/BannedWords.cs | 4 ++-- UPBot Code/Commands/CustomCommandsService.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index fe9d50d..02bea3e 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -155,7 +155,7 @@ void SaveList() { } class BannedWord { - public string word = null; + public string word; public ulong creator = 0; public DateTime date = DateTime.MinValue; @@ -174,7 +174,7 @@ public BannedWord(string line) { } public override string ToString() { - return word + "\t" + creator + "\t" + date.ToString() + "\n"; + return word + "\t" + creator + "\t" + date + "\n"; } } diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 16ce94f..5c17e61 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -24,7 +24,7 @@ public class CustomCommandsService : BaseCommandModule [Description("**Create** a new Custom Command (so-called 'CC') with a specified name and all aliases if desired " + "(no duplicate alias allowed).\nAfter doing this, the bot will ask you to input the content, which will " + "be displayed once someone invokes this CC. Your entire next message will be used for the content, so " + - "be careful what you type!\n\n**Usage:**\n\n- `newcc name` (without alias)\n-`newcc name alias1 alias2`" + + "be careful what you type!\n\n**Usage:**\n\n- `newcc name` (without alias)\n- `newcc name alias1 alias2`" + " (with 2 aliases)\n\nThis command can only be invoked by a Mod.")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task CreateCommand(CommandContext ctx, params string[] names) From e78a1b8b7d1a7a0c239561a4dd1bd0c246d94599 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 19:41:51 +0200 Subject: [PATCH 16/54] Edited WhoIs.cs - Added description to 'whois' command - Changed the code in 'WhoIs.cs' to use the 'BuildEmbed' utility function, instead of doing everything manually. Code has been cleaned up. --- UPBot Code/Commands/WhoIs.cs | 45 +++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/UPBot Code/Commands/WhoIs.cs b/UPBot Code/Commands/WhoIs.cs index deecde2..938466c 100644 --- a/UPBot Code/Commands/WhoIs.cs +++ b/UPBot Code/Commands/WhoIs.cs @@ -11,11 +11,14 @@ public class WhoIs : BaseCommandModule { [Command("whois")] + [Aliases("userinfo")] + [Description("Get information about a specific user.")] public async Task WhoIsCommand(CommandContext ctx) { // Basic version without parameters await GenerateWhoIs(ctx, null); } [Command("whoami")] + [Description("Get information about your own Discord account.")] public async Task WhoAmICommand(CommandContext ctx) { // Alternate version without parameters await GenerateWhoIs(ctx, null); } @@ -31,12 +34,7 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { m = ctx.Member; } bool you = m == ctx.Member; - - DiscordEmbedBuilder b = new DiscordEmbedBuilder(); - b.Title = "Who Is user " + m.DisplayName + "#" + m.Discriminator; - b.WithThumbnail(m.AvatarUrl, 64, 64); - - b.WithColor(m.Color); + DateTimeOffset jdate = m.JoinedAt.UtcDateTime; string joined = jdate.Year + "/" + jdate.Month + "/" + jdate.Day; DateTimeOffset cdate = m.CreationTimestamp.UtcDateTime; @@ -45,24 +43,29 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { int daysJ = (int)(DateTime.Now - m.JoinedAt.DateTime).TotalDays; int daysA = (int)(DateTime.Now - m.CreationTimestamp.DateTime).TotalDays; double years = daysA / 365.25; - b.WithDescription(m.Username + " joined on " + joined + " (" + daysJ + " days)\n Account created on " + creation + " (" + daysA + " days, " + years.ToString("N1") + " years)"); - b.AddField("Is you", you ? "✓" : "❌", true); - b.AddField("Is a bot", m.IsBot ? "🤖" : "❌", true); - b.AddField("Is the boss", m.IsOwner ? "👑" : "❌", true); - b.AddField("Is Muted", m.IsMuted ? "✓" : "❌", true); - b.AddField("Is Deafened", m.IsDeafened ? "✓" : "❌", true); + string title = "Who is the user " + m.DisplayName + "#" + m.Discriminator; + string description = m.Username + " joined on " + joined + " (" + daysJ + " days)\n Account created on " + + creation + " (" + daysA + " days, " + years.ToString("N1") + " years)"; + var embed = UtilityFunctions.BuildEmbed(title, description, m.Color); + embed.WithThumbnail(m.AvatarUrl, 64, 64); + + embed.AddField("Is you", you ? "✓" : "❌", true); + embed.AddField("Is a bot", m.IsBot ? "🤖" : "❌", true); + embed.AddField("Is the boss", m.IsOwner ? "👑" : "❌", true); + embed.AddField("Is Muted", m.IsMuted ? "✓" : "❌", true); + embed.AddField("Is Deafened", m.IsDeafened ? "✓" : "❌", true); - if (m.Locale != null) b.AddField("Speaks", m.Locale, true); - if (m.Nickname != null) b.AddField("Is called", m.Nickname, true); - b.AddField("Avatar Hex Color", m.Color.ToString(), true); + if (m.Locale != null) embed.AddField("Speaks", m.Locale, true); + if (m.Nickname != null) embed.AddField("Is called", m.Nickname, true); + embed.AddField("Avatar Hex Color", m.Color.ToString(), true); if (m.PremiumSince != null) { DateTimeOffset bdate = ((DateTimeOffset)m.PremiumSince).UtcDateTime; string booster = bdate.Year + "/" + bdate.Month + "/" + bdate.Day; - b.AddField("Booster", "Form " + booster, true); + embed.AddField("Booster", "Form " + booster, true); } - if (m.Flags != null) b.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags + if (m.Flags != null) embed.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags string roles = ""; int num = 0; @@ -71,9 +74,9 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { num++; } if (num == 1) - b.AddField("Role", roles, false); + embed.AddField("Role", roles, false); else - b.AddField(num + " Roles", roles, false); + embed.AddField(num + " Roles", roles, false); string perms = ""; // Not all permissions are shown if (m.Permissions.HasFlag(DSharpPlus.Permissions.CreateInstantInvite)) perms += ", Invite"; @@ -93,8 +96,8 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageEmojis)) perms += ", Manage Emojis"; if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseSlashCommands)) perms += ", Use Bot"; if (m.Permissions.HasFlag(DSharpPlus.Permissions.UsePublicThreads)) perms += ", Use Threads"; - if (perms.Length > 0) b.AddField("Permissions", perms.Substring(2), false); + if (perms.Length > 0) embed.AddField("Permissions", perms.Substring(2), false); - return ctx.RespondAsync(b.Build()); + return ctx.RespondAsync(embed.Build()); } } \ No newline at end of file From 776eb2644d8c4cb0beb010b3a0eed251e8463347 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 19:58:44 +0200 Subject: [PATCH 17/54] Changed redundant character in 'HelpLanguages.cs' - Please take care of what you commit and avoid typos :) --- UPBot Code/Commands/HelpLanguages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 0812692..0c84bb9 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -70,7 +70,7 @@ public async Task ErrorMessage(CommandContext ctx) { public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string TypeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { UtilityFunctions.LogUserCommand(ctx); - lang = NormalizeLanguage(lang);s + lang = NormalizeLanguage(lang); if (lang == null) await ErrorMessage(ctx); From 161a6dbbbfdae7d4f17e095841aec19c8f02902d Mon Sep 17 00:00:00 2001 From: CPULL Date: Mon, 16 Aug 2021 22:07:05 +0200 Subject: [PATCH 18/54] Fixed the null problem with BannedWords --- UPBot Code/Commands/BannedWords.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index 02bea3e..257e87e 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -16,15 +16,15 @@ public class BannedWords : BaseCommandModule { private static List bannedWords = null; - private static Regex valid = new Regex(@"^[a-zA-Z0-9]+$"); - private static Regex letters = new Regex(@"[a-zA-Z0-9]"); + readonly static Regex valid = new Regex(@"^[a-zA-Z0-9]+$"); + readonly static Regex letters = new Regex(@"[a-zA-Z0-9]"); private const string directoryName = "Restrictions"; public static void Init() { + bannedWords = new List(); string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); if (!File.Exists(path)) return; string[] all = File.ReadAllLines(path); - bannedWords = new List(); foreach (string line in all) { BannedWord word = new BannedWord(line); if (word.word == null) continue; @@ -127,6 +127,7 @@ void SaveWord(BannedWord w) { try { using (StreamWriter sw = File.AppendText(path)) { sw.Write(w.ToString()); + sw.FlushAsync(); } } catch (Exception e) { UtilityFunctions.Log(e.Message); @@ -147,6 +148,7 @@ void SaveList() { using (StreamWriter sw = File.CreateText(path)) { foreach (BannedWord w in bannedWords) { sw.Write(w.ToString()); + sw.FlushAsync(); } } } catch (Exception e) { @@ -208,7 +210,7 @@ internal static async Task CheckMessage(DiscordClient client, MessageCreateEvent return; } foreach (DiscordRole role in member.Roles) { - // if (role.Id == 831050318171078718ul /* Helper */ || role.Id == 830901743624650783ul /* Mod */ || role.Id == 830901562960117780ul /* Owner */) return; + if (role.Id == 831050318171078718ul /* Helper */ || role.Id == 830901743624650783ul /* Mod */ || role.Id == 830901562960117780ul /* Owner */) return; } string msg = args.Message.Content.ToLowerInvariant(); From b315969755ccec1c7d270c168b194d1f79834020 Mon Sep 17 00:00:00 2001 From: CPULL Date: Mon, 16 Aug 2021 18:03:14 +0200 Subject: [PATCH 19/54] =?UTF-8?q?=F0=9F=A9=B9=20improved=20the=20logging?= =?UTF-8?q?=20for=20users=20joining=20and=20leaving?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UPBot Code/Actions/MembersTracking.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/UPBot Code/Actions/MembersTracking.cs b/UPBot Code/Actions/MembersTracking.cs index 63299af..57b4cd5 100644 --- a/UPBot Code/Actions/MembersTracking.cs +++ b/UPBot Code/Actions/MembersTracking.cs @@ -31,17 +31,19 @@ public static async Task DiscordMemberRemoved(DiscordClient client, DSharpPlus.E public static async Task DiscordMemberAdded(DiscordClient client, DSharpPlus.EventArgs.GuildMemberAddEventArgs args) { if (tracking == null) tracking = new Dictionary(); if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); - tracking[args.Member.Id] = DateTime.Now; - await Task.Delay(15000); - if (tracking.ContainsKey(args.Member.Id)) { - string msgC = UtilityFunctions.GetEmojiSnowflakeID(EmojiEnum.OK) + " User " + args.Member.Mention + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; - string msgL = "+ User " + args.Member.DisplayName + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; + _ = SomethingAsync(args.Member.Id, args.Member.DisplayName, args.Member.Mention, args.Guild.MemberCount); + await Task.Delay(10); + } + + static async Task SomethingAsync(ulong id, string name, string mention, int numMembers) { + await Task.Delay(25000); + if (tracking.ContainsKey(id)) { + string msgC = UtilityFunctions.GetEmojiSnowflakeID(EmojiEnum.OK) + " User " + mention + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; + string msgL = "+ User " + name + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; await trackChannel.SendMessageAsync(msgC); UtilityFunctions.Log(msgL); - tracking.Remove(args.Member.Id); + tracking.Remove(id); } - await Task.Delay(10); } - } From 0a7e4cb14e81269d52d1843d2a3bcb5ff01a3eaa Mon Sep 17 00:00:00 2001 From: CPULL Date: Mon, 16 Aug 2021 19:01:47 +0200 Subject: [PATCH 20/54] Empty change to test --- UPBot Code/Commands/HelpLanguages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 0c84bb9..0812692 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -70,7 +70,7 @@ public async Task ErrorMessage(CommandContext ctx) { public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string TypeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { UtilityFunctions.LogUserCommand(ctx); - lang = NormalizeLanguage(lang); + lang = NormalizeLanguage(lang);s if (lang == null) await ErrorMessage(ctx); From 5331a1f1d92746969b49db453d25ed04b0b2bf6c Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Mon, 16 Aug 2021 19:58:44 +0200 Subject: [PATCH 21/54] Changed redundant character in 'HelpLanguages.cs' - Please take care of what you commit and avoid typos :) --- UPBot Code/Commands/HelpLanguages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 0812692..0c84bb9 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -70,7 +70,7 @@ public async Task ErrorMessage(CommandContext ctx) { public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string TypeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { UtilityFunctions.LogUserCommand(ctx); - lang = NormalizeLanguage(lang);s + lang = NormalizeLanguage(lang); if (lang == null) await ErrorMessage(ctx); From f73b49cd70e6ebe54943c9c0791dc7000f49d050 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 14:24:53 +0200 Subject: [PATCH 22/54] Small change (redundant qualifier) to test something - Just removed a redundant qualifier for static const field to test something --- UPBot Code/UtilityFunctions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPBot Code/UtilityFunctions.cs b/UPBot Code/UtilityFunctions.cs index c62cc5b..e1b21d0 100644 --- a/UPBot Code/UtilityFunctions.cs +++ b/UPBot Code/UtilityFunctions.cs @@ -196,7 +196,7 @@ internal static void Log(string msg) { internal static async Task ErrorCallback(CommandErrors error, CommandContext ctx, params object[] additionalParams) { - DiscordColor red = UtilityFunctions.Red; + DiscordColor red = Red; string message = string.Empty; bool respond = false; switch (error) From 1720c4935ba21e5798ef2a796019ad449233f074 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:17:57 +0200 Subject: [PATCH 23/54] Optimized and reworked the 'HelpLanguages.cs' file - Optimized and reworked the 'helplanguage' command with a few simplifications and optimizations regarding readability, accessibility and performance (for example, not 3 Dictionaries anymore, but one Dictionary with a ContainerClass 'LanguageInfo' for all the information about the language (course link, video link, color) Everything has been tested and it seems to work so far --- UPBot Code/Commands/HelpLanguages.cs | 211 ++++++++++----------------- 1 file changed, 81 insertions(+), 130 deletions(-) diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 0c84bb9..af8c779 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -12,113 +12,103 @@ public class HelpLanguagesModel : BaseCommandModule { private List lastRequests = new List(); - private Dictionary languageLinks = new Dictionary() // video course + private Dictionary languages = new Dictionary() { - { "C#", "<:csharp:831465428214743060>!\nLink: https://youtu.be/GhQdlIFylQ8" }, - { "C++", "<:cpp:831465408874676273>!\nLink: https://youtu.be/vLnPwxZdW4Y" }, - { "Python", "<:python:831465381016895500>!\nLink: https://youtu.be/rfscVS0vtbw" }, - { "JavaScript", "<:Javascript:876103767068647435>!\nLink: https://youtu.be/PkZNo7MFNFg" }, - { "Java", "<:java:875852276017815634>!\nLink: https://youtu.be/grEKMHGYyns" } - }; - private Dictionary languageCourseLink = new Dictionary() // site course - { - { "C#", "<:csharp:831465428214743060>!\nLink: https://www.w3schools.com/cs/"}, - { "C++", "<:cpp:831465408874676273>!\nLink: https://www.w3schools.com/cpp/"}, - { "Python", "<:python:831465381016895500>!\nLink: https://www.w3schools.com/python/"}, - { "JavaScript", "<:Javascript:876103767068647435>!\nLink: https://www.w3schools.com/js/" }, - { "Java", "<:java:875852276017815634>!\nLink: https://www.w3schools.com/java/" } - }; + { "C#", new LanguageInfo("<:csharp:831465428214743060>!\nLink: https://youtu.be/GhQdlIFylQ8", + "<:csharp:831465428214743060>!\nLink: https://www.w3schools.com/cs/", "#812f84") + }, + { "C++", new LanguageInfo("<:cpp:831465408874676273>!\nLink: https://youtu.be/vLnPwxZdW4Y", + "<:cpp:831465408874676273>!\nLink: https://www.w3schools.com/cpp/", "#3f72db") + }, - private Dictionary colors = new Dictionary() // colors for embed - { - { "C#", "812f84" }, - { "C++", "3f72db" }, - { "Python", "d1e13b" }, - { "JavaScript", "f8ff00"}, - { "Java", "e92c2c" } - }; + { "Python", new LanguageInfo("<:python:831465381016895500>!\nLink: https://youtu.be/rfscVS0vtbw", + "<:python:831465381016895500>!\nLink: https://www.w3schools.com/python/", "#d1e13b") + }, + + { "JavaScript", new LanguageInfo("<:Javascript:876103767068647435>!\nLink: https://youtu.be/PkZNo7MFNFg", + "<:Javascript:876103767068647435>!\nLink: https://www.w3schools.com/js/", "#f8ff00") + }, + + { "Java", new LanguageInfo("<:java:875852276017815634>!\nLink: https://youtu.be/grEKMHGYyns", + "<:java:875852276017815634>!\nLink: https://www.w3schools.com/java/", "#e92c2c") + } + }; private string[] helpfulAnswers = // words for video course { "Hello! @@@, here is a good video tutorial about ///", "Hey! hey! @@@, here is a sick video tutorial about ///", "Hello @@@, here is your video tutorial ///" - }; - private string[] helpFulCourseAnswers = { // words for site course - "Hello! @@@, here is a good course about ///", - "Hey! hey! @@@, here is a sick course about ///", - "Hello @@@, here is your course ///" - }; - - [Description("Gives good tutorials on specific language.")] // for \help helplanguage info + }; + [Command("helplanguage")] public async Task ErrorMessage(CommandContext ctx) { - DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); - deb.Title = "Help Language - How To Use"; - deb.WithColor(new DiscordColor("ff0025")); + string title = "Help Language - How To Use"; DiscordMember member = ctx.Member; - deb.Description = member.Mention + " , if you want to get video course about specific language type: `\\helplanguage video C#`" + + string description = member.Mention + " , if you want to get video course about specific language type: `\\helplanguage video C#`" + " \nIf you want to get full online course about specific language type: \n`\\helplanguage course C#`" + " \nAvailable languages: `ะก#, C++, Python, JavaScript, Java`"; - await ctx.RespondAsync(deb.Build()); // \helplanguage + await UtilityFunctions.BuildEmbedAndExecute(title, description, UtilityFunctions.Red, ctx, true); } - [Description("For use this command you should type `\\helplanguage C#`")] [Command("helplanguage")] - public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string TypeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# + [Description("Gives good tutorials on specific language.\n**Usage:** `\\helplanguage language`")] + public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string typeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { UtilityFunctions.LogUserCommand(ctx); lang = NormalizeLanguage(lang); if (lang == null) + { await ErrorMessage(ctx); - else if (TypeOfHelp.ToLowerInvariant() == "video") { - if (!languageLinks.ContainsKey(lang)) { - await ErrorMessage(ctx); - } - else - await GenerateHelpfulAnswer(ctx, lang); + return; } - else if (TypeOfHelp.ToLowerInvariant() == "course") { - if (!languageCourseLink.ContainsKey(lang)) { - await ErrorMessage(ctx); - } - else - await GenerateHelpfulAnswerCourse(ctx, lang); + + if (!languages.ContainsKey(lang)) { + await ErrorMessage(ctx); + return; } + + bool course = typeOfHelp.ToLowerInvariant() != "video"; + await GenerateHelpfulAnswer(ctx, lang, course); } private string NormalizeLanguage(string language) { if (language == null) return null; language = language.ToLowerInvariant(); switch (language) { - case "c++": return "C++"; - case "cpp": return "C++"; - case "cp": return "C++"; - case "cs": return "C#"; - case "csharp": return "C#"; - case "c#": return "C#"; - case "java": return "Java"; - case "javascript": return "JavaScript"; - case "jscript": return "JavaScript"; - case "js": return "JavaScript"; - case "python": return "Python"; - case "phyton": return "Python"; - case "py": return "Python"; + case "c++": + case "cpp": + case "cp": + return "C++"; + case "cs": + case "csharp": + case "c#": + return "C#"; + case "java": + return "Java"; + case "javascript": + case "jscript": + case "js": + return "JavaScript"; + case "python": + case "phyton": + case "py": + return "Python"; + default: + return char.ToUpperInvariant(language[0]) + language.Substring(1); } - return language; + + //return language; } - - - private Task GenerateHelpfulAnswer(CommandContext ctx, string language) { + + private async Task GenerateHelpfulAnswer(CommandContext ctx, string language, bool isCourse) { DiscordMember member = ctx.Member; ulong memberId = member.Id; - DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); - deb.Title = $"Help Language - {language}"; - deb.WithColor(new DiscordColor(colors[language])); + string title = $"Help Language - {language}"; // Find the last request LastRequestByMember lastRequest = null; @@ -140,73 +130,34 @@ private Task GenerateHelpfulAnswer(CommandContext ctx, string language) { if (lastRequest.Num >= helpfulAnswers.Length) lastRequest.Num = 0; } + + string link = isCourse ? languages[language].CourseLink :languages[language].VideoLink; string msg = helpfulAnswers[lastRequest.Num]; msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) - .Replace("///", languageLinks[language]); - deb.Description = msg; - return ctx.RespondAsync(deb.Build()); - } - private Task GenerateHelpfulAnswerCourse(CommandContext ctx, string rawLang) { - - DiscordMember member = ctx.Member; - ulong memberId = member.Id; - DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); - - string language = ""; - if (rawLang == "cpp" || rawLang == "CPP" || rawLang == "c++" || rawLang == "C++") // all possible words to type (including the same words as command one) - { - language = rawLang.ToUpperInvariant(); - language = rawLang = "C++"; - } - else if (rawLang == "c#" || rawLang == "C#" || rawLang == "csharp" || rawLang == "CSharp") { - language = rawLang.ToUpperInvariant(); - language = rawLang = "C#"; - } - else if (rawLang == "python" || rawLang == "Python" || rawLang == "py" || rawLang == "PY") { - language = rawLang = "Python"; - } - else if (rawLang == "js" || rawLang == "JS" || rawLang == "javascript" || rawLang == "JavaScript") { - language = rawLang = "JavaScript"; - } - else if (rawLang == "java") { - language = rawLang = "Java"; - } - else - language = char.ToUpperInvariant(rawLang[0]) + rawLang.Substring(1); - - deb.Title = $"Help Language - {language}"; - deb.WithColor(new DiscordColor(colors[rawLang])); + .Replace("///", link); + if (isCourse) + msg = msg.Replace("video", "course"); - // Find the last request - LastRequestByMember lastRequest = null; - foreach (LastRequestByMember lr in lastRequests) { - if (lr.MemberId == memberId) { - lastRequest = lr; - break; - } - } - - if (lastRequest == null) // No last request, create one - { - lastRequest = new LastRequestByMember { MemberId = memberId, DateTime = DateTime.Now, Num = 0 }; - lastRequests.Add(lastRequest); - } - else { - lastRequest.DateTime = DateTime.Now; - lastRequest.Num++; - if (lastRequest.Num >= helpFulCourseAnswers.Length) - lastRequest.Num = 0; - } - string msg = helpFulCourseAnswers[lastRequest.Num]; - msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) - .Replace("///", languageCourseLink[rawLang]); - deb.Description = msg; - return ctx.RespondAsync(deb.Build()); + await UtilityFunctions.BuildEmbedAndExecute(title, msg, languages[language].Color, ctx, true); } - public class LastRequestByMember { + private class LastRequestByMember { public ulong MemberId { get; set; } public DateTime DateTime { get; set; } public int Num { get; set; } } + + private class LanguageInfo + { + public LanguageInfo(string videoLink, string courseLink, string colorHex) + { + this.VideoLink = videoLink; + this.CourseLink = courseLink; + this.Color = new DiscordColor(colorHex); + } + + internal readonly string VideoLink; + internal readonly string CourseLink; + internal readonly DiscordColor Color; + } } From 20f632d204aac4d5aea34fe73832bfee9500486a Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:26:51 +0200 Subject: [PATCH 24/54] Removed unused `using` declarations in 'Game.cs' - Removed unused `using` declarations in 'Game.cs' --- UPBot Code/Commands/Game.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/UPBot Code/Commands/Game.cs b/UPBot Code/Commands/Game.cs index cf9aee6..a65db33 100644 --- a/UPBot Code/Commands/Game.cs +++ b/UPBot Code/Commands/Game.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using DSharpPlus.CommandsNext; using DSharpPlus.CommandsNext.Attributes; -using DSharpPlus.Entities; /// /// This command implements simple games like: From a13b03a5348057c58e1851ce275cb3b603178f24 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:36:25 +0200 Subject: [PATCH 25/54] Let 'stats' command use Utility Functions to build embed - Let 'stats' command use Utility Functions to build embed ('Stats.cs') --- UPBot Code/Commands/Stats.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UPBot Code/Commands/Stats.cs b/UPBot Code/Commands/Stats.cs index 6a5461b..ecb8472 100644 --- a/UPBot Code/Commands/Stats.cs +++ b/UPBot Code/Commands/Stats.cs @@ -72,12 +72,12 @@ public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int } DiscordGuild g = ctx.Guild; - DiscordEmbedBuilder e = new DiscordEmbedBuilder(); - e.Title = UtilityFunctions.GetEmojiSnowflakeID(UtilityFunctions.GetEmoji(EmojiEnum.UnitedProgramming)) + " United Programming Statistics"; - e.Description = " ---- Fetching data 0/" + channelIDs.Length + " ---- Channel " + channelNames[0] + " ---- "; - + string title = UtilityFunctions.GetEmojiSnowflakeID(UtilityFunctions.GetEmoji(EmojiEnum.UnitedProgramming)) + " United Programming Statistics"; + string description = " ---- Fetching data 0/" + channelIDs.Length + " ---- Channel " + channelNames[0] + " ---- "; + + var e = UtilityFunctions.BuildEmbed(title, description, DiscordColor.Black); int fieldPos = e.Fields.Count - 1; - DiscordMessage m = await ctx.Message.RespondAsync(e.Build()); + DiscordMessage m = await UtilityFunctions.LogEmbed(e, ctx, true); int step = 0; From df8484e6e81ace120ee36ef0c10a9343848e6e7d Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 18:01:11 +0200 Subject: [PATCH 26/54] 'stats' overwrites "Fetching" message - Once all stats are loaded, the "Fetching" message, which is displayed while loading the stats, will be overwritten with the final stats (instead of sending a new message and keeping the "Fetching" one) --- UPBot Code/Commands/Stats.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UPBot Code/Commands/Stats.cs b/UPBot Code/Commands/Stats.cs index ecb8472..8fc41d0 100644 --- a/UPBot Code/Commands/Stats.cs +++ b/UPBot Code/Commands/Stats.cs @@ -169,9 +169,9 @@ public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int e.WithFooter("Statistics from " + channelIDs.Length + " channels and " + numMessages + " messages per channel.\nGenerated in " + time.ToString("N1") + " seconds"); } catch (Exception ex) { - string err = ex.Message; + } - await ctx.Message.RespondAsync(e.Build()); + await m.ModifyAsync(e.Build()); } public class Mentioner { From b3ff3d61b5fb4bdd26e1b403e1d06b178c0131d2 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 18:24:46 +0200 Subject: [PATCH 27/54] You can't input duplicate aliases in CC commands - You can't input duplicate aliases in CC commands (affects 'ccnew' and 'ccedit') --- UPBot Code/Commands/CustomCommandsService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 31fe3de..9454abe 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -29,6 +29,7 @@ public class CustomCommandsService : BaseCommandModule [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of all aliases. The first term is the **main name**, the other ones, separated by a space, are aliases")] params string[] names) { + names = names.Distinct().ToArray(); foreach (var name in names) { if (DiscordClient.GetCommandsNext().RegisteredCommands.ContainsKey(name)) // Check if there is a command with one of the names already @@ -120,6 +121,7 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the "of the CC whose name you want to edit, the **SECOND** term " + "is the new **main name** and all the other terms are new aliases")] params string[] names) { + names = names.Distinct().ToArray(); if (names.Length < 2) { await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParams, ctx); From 8321ac2aa4ba314b1efbc9e9ca3000defc90450a Mon Sep 17 00:00:00 2001 From: CPULL Date: Thu, 19 Aug 2021 08:58:51 +0200 Subject: [PATCH 28/54] Completed the tracking functions --- UPBot Code/Actions/AppreciationTracking.cs | 517 +++++++++++++++++++++ UPBot Code/Actions/MembersTracking.cs | 34 ++ UPBot Code/Commands/Ping.cs | 2 - UPBot Code/Commands/Refactor.cs | 2 +- UPBot Code/Program.cs | 10 +- UPBot Code/UtilityFunctions.cs | 23 + 6 files changed, 582 insertions(+), 6 deletions(-) create mode 100644 UPBot Code/Actions/AppreciationTracking.cs diff --git a/UPBot Code/Actions/AppreciationTracking.cs b/UPBot Code/Actions/AppreciationTracking.cs new file mode 100644 index 0000000..37b68e5 --- /dev/null +++ b/UPBot Code/Actions/AppreciationTracking.cs @@ -0,0 +1,517 @@ +using DSharpPlus; +using DSharpPlus.CommandsNext; +using DSharpPlus.CommandsNext.Attributes; +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +public class AppreciationTracking : BaseCommandModule { + static ReputationTracking tracking = null; + static EmojisForRole emojisForRole = null; + static bool savingRequested = false; + + public static void Init() { + tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); + emojisForRole = new EmojisForRole(UtilityFunctions.ConstructPath("Tracking", "EmojisForRole", ".dat")); + } + + + [Command("Appreciation")] + [Description("It shows the statistics for users")] + public async Task ShowAppreciationCommand(CommandContext ctx) { + UtilityFunctions.LogUserCommand(ctx); + if (tracking == null) { + tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); + if (tracking == null) return; + } + DiscordEmbedBuilder e = UtilityFunctions.BuildEmbed("Appreciation", "These are the most appreciated people of this server (tracking started at " + tracking.GetStartDate() + ")", DiscordColor.Azure); + + List vals = new List(tracking.GetReputation()); + Dictionary users = new Dictionary(); + e.AddField("Reputation ----------------", "For receving these emojis in the posts: <:OK:830907665869570088>👍❤️🥰😍🤩😘💯<:whatthisguysaid:840702597216337990>", false); + vals.Sort((a, b) => { return b.reputation.CompareTo(a.reputation); }); + for (int i = 0; i < 6; i++) { + if (i >= vals.Count) break; + Reputation r = vals[i]; + if (r.reputation == 0) break; + if (!users.ContainsKey(r.user)) { + users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + } + e.AddField(users[r.user], "Reputation: _" + r.reputation + "_", true); + } + + e.AddField("Fun -----------------------", "For receving these emojis in the posts: 😀😃😄😁😆😅🤣😂🙂🙃😉😊😇<:StrongSmile:830907626928996454>", false); + vals.Sort((a, b) => { return b.fun.CompareTo(a.fun); }); + for (int i = 0; i < 6; i++) { + if (i >= vals.Count) break; + Reputation r = vals[i]; + if (r.fun == 0) break; + if (!users.ContainsKey(r.user)) { + users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + } + e.AddField(users[r.user], "Fun: _" + r.fun + "_", (i < vals.Count - 1 && i < 5)); + } + + await ctx.Message.RespondAsync(e.Build()); + } + + + [Command("EmojiForRole")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + [Description("Reply to a message what should add and remove a role when an emoji (any or specific) is added")] + public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role, [Description("The emoji to watch")] DiscordEmoji emoji) { + if (ctx.Message.ReferencedMessage == null) { + await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched!", UtilityFunctions.Red, ctx, true); + } + else { + string msg = ctx.Message.ReferencedMessage.Content; + if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; + if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, emoji)) { + msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing the emoji: " + emoji.GetDiscordName(); + } + else { + msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding the emoji: " + emoji.GetDiscordName(); + } + UtilityFunctions.Log(msg); + await ctx.RespondAsync(msg); + } + } + + + [Command("EmojiForRole")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EmojiForRoleCommand(CommandContext ctx) { + await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nyou have to specify the Role to add/remove, and the emoji to watch.", UtilityFunctions.Red, ctx, true); + } + + [Command("EmojiForRole")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role) { + if (ctx.Message.ReferencedMessage == null) { + await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\n and you have to specify the emoji to watch.", UtilityFunctions.Red, ctx, true); + } + else { + string msg = ctx.Message.ReferencedMessage.Content; + if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; + if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, null)) { + msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing any emoji"; + } + else { + msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding an emoji"; + } + UtilityFunctions.Log(msg); + await ctx.RespondAsync(msg); + } + } + + [Command("EmojiForRole")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The emoji to watch")] DiscordEmoji emoji) { + if (ctx.Message.ReferencedMessage == null) { + await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nand y ou have to specify the Role to add/remove.", UtilityFunctions.Red, ctx, true); + } + else { + await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to specify the Role to add/remove.", UtilityFunctions.Red, ctx, true); + } + } + + + + + internal static Task ReacionAdded(DiscordClient sender, MessageReactionAddEventArgs a) { + ulong emojiId = a.Emoji.Id; + string emojiName = a.Emoji.Name; + emojisForRole.HandleAddingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); + + DiscordUser author = a.Message.Author; + if (author == null) { + ulong msgId = a.Message.Id; + ulong chId = a.Message.ChannelId; + DiscordChannel c = a.Guild.GetChannel(chId); + DiscordMessage m = c.GetMessageAsync(msgId).Result; + author = m.Author; + } + ulong authorId = author.Id; + if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) + return HandleReactions(emojiId, emojiName, authorId, true); + } + internal static Task ReactionRemoved(DiscordClient sender, MessageReactionRemoveEventArgs a) { + ulong emojiId = a.Emoji.Id; + string emojiName = a.Emoji.Name; + emojisForRole.HandleRemovingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); + + DiscordUser author = a.Message.Author; + if (author == null) { + ulong msgId = a.Message.Id; + ulong chId = a.Message.ChannelId; + DiscordChannel c = a.Guild.GetChannel(chId); + DiscordMessage m = c.GetMessageAsync(msgId).Result; + author = m.Author; + } + ulong authorId = author.Id; + if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) + return HandleReactions(emojiId, emojiName, authorId, false); + } + + static Task HandleReactions(ulong emojiId, string emojiName, ulong authorId, bool added) { + bool save = false; + // check if emoji is :smile: :rolf: :strongsmil: (find valid emojis -> increase fun level of user + + if (emojiName == "😀" || + emojiName == "😃" || + emojiName == "😄" || + emojiName == "😁" || + emojiName == "😆" || + emojiName == "😅" || + emojiName == "🤣" || + emojiName == "😂" || + emojiName == "🙂" || + emojiName == "🙃" || + emojiName == "😉" || + emojiName == "😊" || + emojiName == "😇" || + emojiId == 830907626928996454ul || // :StrongSmile: + false) { // just to keep the other lines aligned + if (tracking == null) { + tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); + if (tracking == null) return Task.Delay(10); + } + save = tracking.AlterFun(authorId, added); + } + // check if emoji is :OK: or :ThumbsUp: -> Increase reputation for user + if (emojiId == 830907665869570088ul || // :OK: + emojiName == "👍" || // :thumbsup: + emojiName == "❤️" || // :hearth: + emojiName == "🥰" || // :hearth: + emojiName == "😍" || // :hearth: + emojiName == "🤩" || // :hearth: + emojiName == "😘" || // :hearth: + emojiName == "💯" || // :100: + emojiId == 840702597216337990ul || // :whatthisguysaid: + emojiId == 552147917876625419ul || // :thoose: + false) { // just to keep the other lines aligned + if (tracking == null) { + tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); + if (tracking == null) return Task.Delay(10); + } + save = tracking.AlterRep(authorId, added); + } + + // Start a delayed saving if one is not yet present + if (save && !savingRequested) { + savingRequested = true; + _ = SaveDelayedAsync(); + } + + return Task.Delay(10); + } + + + + static async Task SaveDelayedAsync() { + await Task.Delay(30000); + try { + tracking.Save(); + } catch (Exception e) { + UtilityFunctions.Log("ERROR: problems in saving the Reputation file: " + e.Message); + } + savingRequested = false; + } + + + public class Reputation { // 16 bytes + public ulong user; // 8 + public ushort reputation; // 2 + public ushort fun; // 2 + public DateTime startTracking; // 4 + } + + public class ReputationTracking { + DateTime trackingStarted; + Dictionary dic; + string path = null; + + public ReputationTracking(string path) { + this.path = path; + dic = new Dictionary(); + if (!File.Exists(path)) { + trackingStarted = DateTime.Now; + return; + } + byte[] data = new byte[20]; + using (FileStream f = new FileStream(path, FileMode.Open)) { + // 32 bits for the date (ymd) + if (f.Read(data, 0, 4) < 4) { + UtilityFunctions.Log("ERROR: wrong Reputation file: " + path); + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + UtilityFunctions.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); + } + return; + } + trackingStarted = GetDateFromBytes(data, 0); + while (f.Read(data, 0, 16) == 16) { + ulong usrid = BitConverter.ToUInt64(data); + ushort rep = BitConverter.ToUInt16(data, 8); + ushort fun = BitConverter.ToUInt16(data, 10); + DateTime start = GetDateFromBytes(data, 12); + dic[usrid] = new Reputation { user = usrid, reputation = rep, fun = fun, startTracking = start }; + } + } + UtilityFunctions.Log("ReputationTracking: Loaded " + dic.Count + " users"); + } + + public void Save() { + lock (dic) { + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + UtilityFunctions.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); + return; + } + using (FileStream f = new FileStream(path, FileMode.CreateNew)) { + byte[] data = new byte[16]; + SetDateToBytes(trackingStarted, data, 0); + f.Write(data, 0, 4); + foreach (Reputation r in dic.Values) { + byte[] d = BitConverter.GetBytes(r.user); + int pos = 0; + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + d = BitConverter.GetBytes(r.reputation); + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + d = BitConverter.GetBytes(r.fun); + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + SetDateToBytes(r.startTracking, data, pos); + f.Write(data, 0, 16); + } + f.Flush(); + } + } + UtilityFunctions.Log("ReputationTracking: Saved " + dic.Count + " users"); + } + + private void SetDateToBytes(DateTime d, byte[] data, int offset) { + data[offset + 0] = (byte)((d.Year & 0xff00) >> 8); + data[offset + 1] = (byte)(d.Year & 0xff); + data[offset + 2] = (byte)(d.Month & 0xff); + data[offset + 3] = (byte)(d.Day & 0xff); + } + + private DateTime GetDateFromBytes(byte[] data, int offset) { + return new DateTime((data[offset + 0] << 8) + data[offset + 1], data[offset + 2], data[offset + 3]); + } + + public bool AlterRep(ulong id, bool add) { + if (add) { + if (dic.ContainsKey(id)) dic[id].reputation++; + else { + dic.Add(id, new Reputation { user = id, reputation = 1, fun = 0, startTracking = DateTime.Now }); + } + return true; + } + else { + if (dic.ContainsKey(id) && dic[id].reputation > 0) { + dic[id].reputation--; + return true; + } + } + return false; + } + public bool AlterFun(ulong id, bool add) { + if (add) { + if (dic.ContainsKey(id)) dic[id].fun++; + else { + dic.Add(id, new Reputation { user = id, reputation = 0, fun = 1, startTracking = DateTime.Now }); + } + return true; + } + else { + if (dic.ContainsKey(id) && dic[id].fun > 0) { + dic[id].fun--; + return true; + } + } + return false; + } + + internal string GetStartDate() { + return trackingStarted.ToString("yyyy/MM/dd"); + } + + internal IEnumerable GetReputation() { + return dic.Values; + } + } + + public class EmojisForRole { + List values = null; + string path = null; + + public EmojisForRole(string path) { + values = new List(); + this.path = path; + if (!File.Exists(path)) return; + + DiscordGuild g = UtilityFunctions.GetGuild(); + byte[] data = new byte[36]; + using (FileStream f = new FileStream(path, FileMode.Open)) { + while (f.Read(data, 0, 36) == 36) { + ReactionValue v = new ReactionValue { + channel = BitConverter.ToUInt64(data, 0), + message = BitConverter.ToUInt64(data, 8), + role = BitConverter.ToUInt64(data, 16), + emojiId = BitConverter.ToUInt64(data, 24), + emojiName = Char.ConvertFromUtf32(BitConverter.ToInt32(data, 32)), + dRole = null + }; + + DiscordMessage m; + try { + // Does the message exists? + DiscordChannel c = g.GetChannel(v.channel); + if (c == null || c.Id == 0) continue; // Bad + m = c.GetMessageAsync(v.message).Result; + if (m == null || m.Id == 0) continue; // Bad; + // Does the role exists? + DiscordRole r = g.GetRole(v.role); + if (r == null || r.Id == 0) continue; // Bad + } catch (Exception ex) { + UtilityFunctions.Log("Error while checking a ReactionValue: " + ex.Message); + continue; + } + // Check that the message has the required emojis, if not add it + DiscordEmoji e = null; + if (v.emojiId != 0) { + e = g.GetEmojiAsync(v.emojiId).Result; + } else if (v.emojiName != "!") { + e = DiscordEmoji.FromUnicode(v.emojiName); + } + if (e == null) continue; // Bad + if (m.GetReactionsAsync(e, 1).Result.Count == 0) { // Need to add + m.CreateReactionAsync(e).Wait(); + } + + values.Add(v); + } + } + UtilityFunctions.Log("Loaded " + values.Count + " tracked messages for emojis."); + } + + public void Save() { + lock (values) { + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + UtilityFunctions.Log("ERROR: cannot delete old EmojisForRole file: " + path + "\nException: " + e.Message); + return; + } + using (FileStream f = new FileStream(path, FileMode.CreateNew)) { + byte[] data = new byte[36]; + foreach (ReactionValue v in values) { + int pos = 0; + byte[] d = BitConverter.GetBytes(v.channel); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.message); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.role); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.emojiId); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + if (v.emojiId != 0 || v.emojiName == null || v.emojiName.Length == 0) + for (int i = 0; i < 4; i++) data[pos++] = 0; + else { + d = BitConverter.GetBytes(Char.ConvertToUtf32(v.emojiName, 0)); + for (int i = 0; i < 4; i++) data[pos++] = d[i]; + } + f.Write(data, 0, pos); + } + f.Flush(); + } + } + UtilityFunctions.Log("EmojisForRole: Saved " + values.Count + " tracked messages and emojis"); + } + + internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji) { + bool here = false; + ReactionValue toRemove = null; + foreach (ReactionValue v in values) { + if (v.channel == msg.ChannelId && v.message == msg.Id && v.role == role.Id) { + if ((emoji == null && v.emojiId == 0 && v.emojiName == "!") || + (emoji != null && ((emoji.Id != 0 && emoji.Id == v.emojiId) || (emoji.Id == 0 && emoji.Name.Equals(v.emojiName))))) { + toRemove = v; // Remove + break; + } + } + } + if (toRemove != null) { + values.Remove(toRemove); + } + else { + here = true; + ReactionValue v = new ReactionValue { + channel = msg.ChannelId, + message = msg.Id, + role = role.Id, + emojiId = emoji == null ? 0 : emoji.Id, + emojiName = emoji == null ? "!" : emoji.Name, + dRole = role + }; + values.Add(v); + + if (emoji != null) { + // Check if we have the emoji already, if not add it + if (msg.GetReactionsAsync(emoji, 1).Result.Count == 0) { + msg.CreateReactionAsync(emoji).Wait(); + } + } + + } + Save(); + return here; + } + + internal void HandleAddingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { + DiscordMember dm = (DiscordMember)user; + if (dm == null) return; // Not a valid member for the Guild/Context + if (values == null) return; + foreach (ReactionValue v in values) { + if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + dm.GrantRoleAsync(v.dRole).Wait(); + UtilityFunctions.Log("Role " + v.dRole.Name + " was granted to " + user.Username + " by emoji " + eId); + return; + } + } + } + + internal void HandleRemovingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { + DiscordMember dm = (DiscordMember)user; + if (dm == null) return; // Not a valid member for the Guild/Context + if (values == null) return; + foreach (ReactionValue v in values) { + if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + dm.RevokeRoleAsync(v.dRole).Wait(); + UtilityFunctions.Log("Role " + v.dRole.Name + " was removed from " + user.Username + " by emoji " + eId); + return; + } + } + } + + public class ReactionValue { + public ulong channel; // 8 bytes + public ulong message; // 8 bytes + public ulong role; // 8 bytes + public ulong emojiId; // 8 bytes + public string emojiName; // 4 bytes (utf32, 1 character) + public DiscordRole dRole; + } + } +} diff --git a/UPBot Code/Actions/MembersTracking.cs b/UPBot Code/Actions/MembersTracking.cs index 57b4cd5..4939542 100644 --- a/UPBot Code/Actions/MembersTracking.cs +++ b/UPBot Code/Actions/MembersTracking.cs @@ -36,6 +36,40 @@ public static async Task DiscordMemberAdded(DiscordClient client, DSharpPlus.Eve await Task.Delay(10); } + public static async Task DiscordMemberUpdated(DiscordClient client, DSharpPlus.EventArgs.GuildMemberUpdateEventArgs args) { + if (tracking == null) tracking = new Dictionary(); + if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); + + IReadOnlyList rolesBefore = args.RolesBefore; + IReadOnlyList rolesAfter = args.RolesAfter; + List rolesAdded = new List(); + // Changed role? + foreach (DiscordRole r1 in rolesAfter) { + bool addedRole = true; + foreach (DiscordRole r2 in rolesBefore) { + if (r1.Equals(r2)) { + addedRole = false; + } + } + if (addedRole) rolesAdded.Add(r1); + } + string msgC; + string msgL; + if (rolesAdded.Count > 0) { + msgC = "User " + args.Member.Mention + " has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); + msgL = "User \"" + args.Member.DisplayName + "\" has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); + foreach (DiscordRole r in rolesAdded) { + msgC += r.Mention; + msgL += r.Name; + } + await trackChannel.SendMessageAsync(msgC); + UtilityFunctions.Log(msgL); + } + + await Task.Delay(10); + } + + static async Task SomethingAsync(ulong id, string name, string mention, int numMembers) { await Task.Delay(25000); if (tracking.ContainsKey(id)) { diff --git a/UPBot Code/Commands/Ping.cs b/UPBot Code/Commands/Ping.cs index e9d0e87..f9b1475 100644 --- a/UPBot Code/Commands/Ping.cs +++ b/UPBot Code/Commands/Ping.cs @@ -196,8 +196,6 @@ internal int AddRequest() { break; } - Console.WriteLine(memberId + ": idx=" + index + " num=" + num + " avg=" + averageBetweenRequests + " amt=" + amount.TotalSeconds); // FIXME remove the debug line - return index; } } diff --git a/UPBot Code/Commands/Refactor.cs b/UPBot Code/Commands/Refactor.cs index 8fce2bb..63d05e5 100644 --- a/UPBot Code/Commands/Refactor.cs +++ b/UPBot Code/Commands/Refactor.cs @@ -133,7 +133,7 @@ private async Task> RefactorCode(CommandContext ctx, Discor } if (deleteOrig) { await Task.Delay(120); - List toDelete = new List { /*toRefactor, FIXME*/ctx.Message }; + List toDelete = new List { toRefactor, ctx.Message }; await ctx.Channel.DeleteMessagesAsync(toDelete); } else { diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index 52fdafa..2b20291 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -33,14 +33,18 @@ static async Task MainAsync(string token, string prefix) { BannedWords.Init(); discord.MessageCreated += async (s, e) => { await BannedWords.CheckMessage(s, e); }; - discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; - discord.GuildMemberRemoved += MembersTracking.DiscordMemberRemoved; - await CustomCommandsService.LoadCustomCommands(); await discord.ConnectAsync(); // Connects and wait forever UtilityFunctions.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd")); + AppreciationTracking.Init(); + discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; + discord.GuildMemberRemoved += MembersTracking.DiscordMemberRemoved; + discord.GuildMemberUpdated += MembersTracking.DiscordMemberUpdated; + discord.MessageReactionAdded += AppreciationTracking.ReacionAdded; + discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; + await Task.Delay(-1); } } diff --git a/UPBot Code/UtilityFunctions.cs b/UPBot Code/UtilityFunctions.cs index e1b21d0..9727cc8 100644 --- a/UPBot Code/UtilityFunctions.cs +++ b/UPBot Code/UtilityFunctions.cs @@ -24,6 +24,29 @@ public static class UtilityFunctions private static DiscordClient client; private static DateTimeFormatInfo sortableDateTimeFormat; private static StreamWriter logs; + private static DiscordMember mySelf; + private static DiscordGuild guild; + + public static void SetMyself(DiscordClient c) { + + } + + public static DiscordMember GetMyself() { + if (mySelf==null) mySelf = client.Guilds[830900174553481236ul].CurrentMember; + return mySelf; + } + + public static DiscordGuild GetGuild() { + if (guild != null) return guild; + while (client == null) Task.Delay(1000); + while (client.Guilds == null) Task.Delay(1000); + while (client.Guilds.Count == 0) Task.Delay(1000); + + guild = client.Guilds[830900174553481236ul]; + return guild; + } + + public static void InitClient(DiscordClient c) { client = c; From c69fbf7b103dd67dc49e78af5a2451f37e221306 Mon Sep 17 00:00:00 2001 From: CPULL Date: Thu, 19 Aug 2021 12:28:48 +0200 Subject: [PATCH 29/54] Completed bot finalization --- ToDo.txt | 30 + UPBot Code/Actions/AppreciationTracking.cs | 547 ++++++++++--------- UPBot Code/Actions/MembersTracking.cs | 68 ++- UPBot Code/Commands/BannedWords.cs | 180 +++--- UPBot Code/Commands/CustomCommand.cs | 2 +- UPBot Code/Commands/CustomCommandsService.cs | 45 +- UPBot Code/Commands/DebugCommand.cs | 26 - UPBot Code/Commands/Delete.cs | 155 +++--- UPBot Code/Commands/Game.cs | 8 +- UPBot Code/Commands/HelpLanguages.cs | 25 +- UPBot Code/Commands/Ping.cs | 74 +-- UPBot Code/Commands/Refactor.cs | 162 +++--- UPBot Code/Commands/Stats.cs | 79 ++- UPBot Code/Commands/Version.cs | 27 + UPBot Code/Commands/WhoIs.cs | 138 ++--- UPBot Code/Program.cs | 6 +- UPBot Code/{UtilityFunctions.cs => Utils.cs} | 76 ++- 17 files changed, 920 insertions(+), 728 deletions(-) create mode 100644 ToDo.txt delete mode 100644 UPBot Code/Commands/DebugCommand.cs create mode 100644 UPBot Code/Commands/Version.cs rename UPBot Code/{UtilityFunctions.cs => Utils.cs} (81%) diff --git a/ToDo.txt b/ToDo.txt new file mode 100644 index 0000000..176e056 --- /dev/null +++ b/ToDo.txt @@ -0,0 +1,30 @@ +Have command to alter the command character [NOT POSSIBLE!] +Check all command names +Add wikis for each command on GitHub +Uniform all error messages with Embeds (use Duck code) +Have all messages and commands to disappear after a while (configurable) if not needed to stay +Have a cmd to configure how long messages should stay +Add TryCatch to all code and commands + + + Errors DelAnsw EmbedRes TryCatch Log@Begin +MemberTracking | | | | X | | +Appreciation | | | | X | X | +EmojiForRole | | | | X | X | +BannedWords | | X | | X | X | +newcc | | | | | X | +ccdel | | | | | X | +ccedit | | | | | X | +cceditname | | | | | X | +cclist | | | | | X | +delete | X | X | | X | X | +game | | | | | X | +bool | | | | | X | +rps | | | | | X | +helplanguage | | | X | X | X | +ping | | X | | X | X | +checklanguage | | | | X | X | +reformat | | | | X | X | +stats | | | X | X | X | +whois | | | X | X | X | + diff --git a/UPBot Code/Actions/AppreciationTracking.cs b/UPBot Code/Actions/AppreciationTracking.cs index 37b68e5..8401a28 100644 --- a/UPBot Code/Actions/AppreciationTracking.cs +++ b/UPBot Code/Actions/AppreciationTracking.cs @@ -12,71 +12,81 @@ public class AppreciationTracking : BaseCommandModule { static ReputationTracking tracking = null; static EmojisForRole emojisForRole = null; static bool savingRequested = false; - + public static void Init() { - tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); - emojisForRole = new EmojisForRole(UtilityFunctions.ConstructPath("Tracking", "EmojisForRole", ".dat")); + tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); + emojisForRole = new EmojisForRole(Utils.ConstructPath("Tracking", "EmojisForRole", ".dat")); } [Command("Appreciation")] [Description("It shows the statistics for users")] public async Task ShowAppreciationCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); - if (tracking == null) { - tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); - if (tracking == null) return; - } - DiscordEmbedBuilder e = UtilityFunctions.BuildEmbed("Appreciation", "These are the most appreciated people of this server (tracking started at " + tracking.GetStartDate() + ")", DiscordColor.Azure); - - List vals = new List(tracking.GetReputation()); - Dictionary users = new Dictionary(); - e.AddField("Reputation ----------------", "For receving these emojis in the posts: <:OK:830907665869570088>👍❤️🥰😍🤩😘💯<:whatthisguysaid:840702597216337990>", false); - vals.Sort((a, b) => { return b.reputation.CompareTo(a.reputation); }); - for (int i = 0; i < 6; i++) { - if (i >= vals.Count) break; - Reputation r = vals[i]; - if (r.reputation == 0) break; - if (!users.ContainsKey(r.user)) { - users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + Utils.LogUserCommand(ctx); + try { + if (tracking == null) { + tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); + if (tracking == null) return; + } + DiscordEmbedBuilder e = Utils.BuildEmbed("Appreciation", "These are the most appreciated people of this server (tracking started at " + tracking.GetStartDate() + ")", DiscordColor.Azure); + + List vals = new List(tracking.GetReputation()); + Dictionary users = new Dictionary(); + e.AddField("Reputation ----------------", "For receving these emojis in the posts: <:OK:830907665869570088>👍❤️🥰😍🤩😘💯<:whatthisguysaid:840702597216337990>", false); + vals.Sort((a, b) => { return b.reputation.CompareTo(a.reputation); }); + for (int i = 0; i < 6; i++) { + if (i >= vals.Count) break; + Reputation r = vals[i]; + if (r.reputation == 0) break; + if (!users.ContainsKey(r.user)) { + users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + } + e.AddField(users[r.user], "Reputation: _" + r.reputation + "_", true); } - e.AddField(users[r.user], "Reputation: _" + r.reputation + "_", true); - } - e.AddField("Fun -----------------------", "For receving these emojis in the posts: 😀😃😄😁😆😅🤣😂🙂🙃😉😊😇<:StrongSmile:830907626928996454>", false); - vals.Sort((a, b) => { return b.fun.CompareTo(a.fun); }); - for (int i = 0; i < 6; i++) { - if (i >= vals.Count) break; - Reputation r = vals[i]; - if (r.fun == 0) break; - if (!users.ContainsKey(r.user)) { - users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + e.AddField("Fun -----------------------", "For receving these emojis in the posts: 😀😃😄😁😆😅🤣😂🙂🙃😉😊😇<:StrongSmile:830907626928996454>", false); + vals.Sort((a, b) => { return b.fun.CompareTo(a.fun); }); + for (int i = 0; i < 6; i++) { + if (i >= vals.Count) break; + Reputation r = vals[i]; + if (r.fun == 0) break; + if (!users.ContainsKey(r.user)) { + users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + } + e.AddField(users[r.user], "Fun: _" + r.fun + "_", (i < vals.Count - 1 && i < 5)); } - e.AddField(users[r.user], "Fun: _" + r.fun + "_", (i < vals.Count - 1 && i < 5)); - } - await ctx.Message.RespondAsync(e.Build()); + await ctx.Message.RespondAsync(e.Build()); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("Appreciation", ex)); + } } [Command("EmojiForRole")] + [Aliases("RoleForEmoji")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only [Description("Reply to a message what should add and remove a role when an emoji (any or specific) is added")] public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role, [Description("The emoji to watch")] DiscordEmoji emoji) { - if (ctx.Message.ReferencedMessage == null) { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched!", UtilityFunctions.Red, ctx, true); - } - else { - string msg = ctx.Message.ReferencedMessage.Content; - if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; - if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, emoji)) { - msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing the emoji: " + emoji.GetDiscordName(); + Utils.LogUserCommand(ctx); + try { + if (ctx.Message.ReferencedMessage == null) { + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched!", Utils.Red, ctx, true); } else { - msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding the emoji: " + emoji.GetDiscordName(); + string msg = ctx.Message.ReferencedMessage.Content; + if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; + if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, emoji)) { + msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing the emoji: " + emoji.GetDiscordName(); + } + else { + msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding the emoji: " + emoji.GetDiscordName(); + } + Utils.Log(msg); + await ctx.RespondAsync(msg); } - UtilityFunctions.Log(msg); - await ctx.RespondAsync(msg); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("EmojiForRole", ex)); } } @@ -84,37 +94,44 @@ public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role [Command("EmojiForRole")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EmojiForRoleCommand(CommandContext ctx) { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nyou have to specify the Role to add/remove, and the emoji to watch.", UtilityFunctions.Red, ctx, true); + Utils.LogUserCommand(ctx); + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nyou have to specify the Role to add/remove, and the emoji to watch.", Utils.Red, ctx, true); } [Command("EmojiForRole")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role) { - if (ctx.Message.ReferencedMessage == null) { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\n and you have to specify the emoji to watch.", UtilityFunctions.Red, ctx, true); - } - else { - string msg = ctx.Message.ReferencedMessage.Content; - if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; - if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, null)) { - msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing any emoji"; + Utils.LogUserCommand(ctx); + try { + if (ctx.Message.ReferencedMessage == null) { + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\n and you have to specify the emoji to watch.", Utils.Red, ctx, true); } else { - msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding an emoji"; + string msg = ctx.Message.ReferencedMessage.Content; + if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; + if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, null)) { + msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing any emoji"; + } + else { + msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding an emoji"; + } + Utils.Log(msg); + await ctx.RespondAsync(msg); } - UtilityFunctions.Log(msg); - await ctx.RespondAsync(msg); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("EmojiForRole", ex)); } } [Command("EmojiForRole")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The emoji to watch")] DiscordEmoji emoji) { + Utils.LogUserCommand(ctx); if (ctx.Message.ReferencedMessage == null) { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nand y ou have to specify the Role to add/remove.", UtilityFunctions.Red, ctx, true); + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nand y ou have to specify the Role to add/remove.", Utils.Red, ctx, true); } else { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to specify the Role to add/remove.", UtilityFunctions.Red, ctx, true); + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to specify the Role to add/remove.", Utils.Red, ctx, true); } } @@ -122,61 +139,72 @@ public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The emoj internal static Task ReacionAdded(DiscordClient sender, MessageReactionAddEventArgs a) { - ulong emojiId = a.Emoji.Id; - string emojiName = a.Emoji.Name; - emojisForRole.HandleAddingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); - - DiscordUser author = a.Message.Author; - if (author == null) { - ulong msgId = a.Message.Id; - ulong chId = a.Message.ChannelId; - DiscordChannel c = a.Guild.GetChannel(chId); - DiscordMessage m = c.GetMessageAsync(msgId).Result; - author = m.Author; + try { + ulong emojiId = a.Emoji.Id; + string emojiName = a.Emoji.Name; + emojisForRole.HandleAddingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); + + DiscordUser author = a.Message.Author; + if (author == null) { + ulong msgId = a.Message.Id; + ulong chId = a.Message.ChannelId; + DiscordChannel c = a.Guild.GetChannel(chId); + DiscordMessage m = c.GetMessageAsync(msgId).Result; + author = m.Author; + } + ulong authorId = author.Id; + if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) + return HandleReactions(emojiId, emojiName, authorId, true); + } catch (Exception ex) { + Utils.Log("Error in ReacionAdded: " + ex.Message); + return Task.FromResult(0); } - ulong authorId = author.Id; - if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) - return HandleReactions(emojiId, emojiName, authorId, true); } + internal static Task ReactionRemoved(DiscordClient sender, MessageReactionRemoveEventArgs a) { - ulong emojiId = a.Emoji.Id; - string emojiName = a.Emoji.Name; - emojisForRole.HandleRemovingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); - - DiscordUser author = a.Message.Author; - if (author == null) { - ulong msgId = a.Message.Id; - ulong chId = a.Message.ChannelId; - DiscordChannel c = a.Guild.GetChannel(chId); - DiscordMessage m = c.GetMessageAsync(msgId).Result; - author = m.Author; + try { + ulong emojiId = a.Emoji.Id; + string emojiName = a.Emoji.Name; + emojisForRole.HandleRemovingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); + + DiscordUser author = a.Message.Author; + if (author == null) { + ulong msgId = a.Message.Id; + ulong chId = a.Message.ChannelId; + DiscordChannel c = a.Guild.GetChannel(chId); + DiscordMessage m = c.GetMessageAsync(msgId).Result; + author = m.Author; + } + ulong authorId = author.Id; + if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) + return HandleReactions(emojiId, emojiName, authorId, false); + } catch (Exception ex) { + Utils.Log("Error in ReacionAdded: " + ex.Message); + return Task.FromResult(0); } - ulong authorId = author.Id; - if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) - return HandleReactions(emojiId, emojiName, authorId, false); } static Task HandleReactions(ulong emojiId, string emojiName, ulong authorId, bool added) { bool save = false; // check if emoji is :smile: :rolf: :strongsmil: (find valid emojis -> increase fun level of user - if (emojiName == "😀" || - emojiName == "😃" || - emojiName == "😄" || - emojiName == "😁" || - emojiName == "😆" || - emojiName == "😅" || - emojiName == "🤣" || - emojiName == "😂" || - emojiName == "🙂" || - emojiName == "🙃" || - emojiName == "😉" || - emojiName == "😊" || - emojiName == "😇" || + if (emojiName == "😀" || + emojiName == "😃" || + emojiName == "😄" || + emojiName == "😁" || + emojiName == "😆" || + emojiName == "😅" || + emojiName == "🤣" || + emojiName == "😂" || + emojiName == "🙂" || + emojiName == "🙃" || + emojiName == "😉" || + emojiName == "😊" || + emojiName == "😇" || emojiId == 830907626928996454ul || // :StrongSmile: false) { // just to keep the other lines aligned if (tracking == null) { - tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); + tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); if (tracking == null) return Task.Delay(10); } save = tracking.AlterFun(authorId, added); @@ -194,7 +222,7 @@ static Task HandleReactions(ulong emojiId, string emojiName, ulong authorId, boo emojiId == 552147917876625419ul || // :thoose: false) { // just to keep the other lines aligned if (tracking == null) { - tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); + tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); if (tracking == null) return Task.Delay(10); } save = tracking.AlterRep(authorId, added); @@ -216,7 +244,7 @@ static async Task SaveDelayedAsync() { try { tracking.Save(); } catch (Exception e) { - UtilityFunctions.Log("ERROR: problems in saving the Reputation file: " + e.Message); + Utils.Log("ERROR: problems in saving the Reputation file: " + e.Message); } savingRequested = false; } @@ -230,71 +258,79 @@ public class Reputation { // 16 bytes } public class ReputationTracking { - DateTime trackingStarted; - Dictionary dic; - string path = null; + readonly DateTime trackingStarted; + readonly Dictionary dic; + readonly string path = null; public ReputationTracking(string path) { - this.path = path; - dic = new Dictionary(); - if (!File.Exists(path)) { - trackingStarted = DateTime.Now; - return; - } - byte[] data = new byte[20]; - using (FileStream f = new FileStream(path, FileMode.Open)) { - // 32 bits for the date (ymd) - if (f.Read(data, 0, 4) < 4) { - UtilityFunctions.Log("ERROR: wrong Reputation file: " + path); - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - UtilityFunctions.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); - } + try { + this.path = path; + dic = new Dictionary(); + if (!File.Exists(path)) { + trackingStarted = DateTime.Now; return; } - trackingStarted = GetDateFromBytes(data, 0); - while (f.Read(data, 0, 16) == 16) { - ulong usrid = BitConverter.ToUInt64(data); - ushort rep = BitConverter.ToUInt16(data, 8); - ushort fun = BitConverter.ToUInt16(data, 10); - DateTime start = GetDateFromBytes(data, 12); - dic[usrid] = new Reputation { user = usrid, reputation = rep, fun = fun, startTracking = start }; + byte[] data = new byte[20]; + using (FileStream f = new FileStream(path, FileMode.Open)) { + // 32 bits for the date (ymd) + if (f.Read(data, 0, 4) < 4) { + Utils.Log("ERROR: wrong Reputation file: " + path); + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); + } + return; + } + trackingStarted = GetDateFromBytes(data, 0); + while (f.Read(data, 0, 16) == 16) { + ulong usrid = BitConverter.ToUInt64(data); + ushort rep = BitConverter.ToUInt16(data, 8); + ushort fun = BitConverter.ToUInt16(data, 10); + DateTime start = GetDateFromBytes(data, 12); + dic[usrid] = new Reputation { user = usrid, reputation = rep, fun = fun, startTracking = start }; + } } + Utils.Log("ReputationTracking: Loaded " + dic.Count + " users"); + } catch (Exception e) { + Utils.Log("ERROR: problems in loading the Reputation file: " + e.Message); } - UtilityFunctions.Log("ReputationTracking: Loaded " + dic.Count + " users"); } public void Save() { - lock (dic) { - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - UtilityFunctions.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); - return; - } - using (FileStream f = new FileStream(path, FileMode.CreateNew)) { - byte[] data = new byte[16]; - SetDateToBytes(trackingStarted, data, 0); - f.Write(data, 0, 4); - foreach (Reputation r in dic.Values) { - byte[] d = BitConverter.GetBytes(r.user); - int pos = 0; - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - d = BitConverter.GetBytes(r.reputation); - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - d = BitConverter.GetBytes(r.fun); - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - SetDateToBytes(r.startTracking, data, pos); - f.Write(data, 0, 16); + try { + lock (dic) { + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); + return; + } + using (FileStream f = new FileStream(path, FileMode.CreateNew)) { + byte[] data = new byte[16]; + SetDateToBytes(trackingStarted, data, 0); + f.Write(data, 0, 4); + foreach (Reputation r in dic.Values) { + byte[] d = BitConverter.GetBytes(r.user); + int pos = 0; + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + d = BitConverter.GetBytes(r.reputation); + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + d = BitConverter.GetBytes(r.fun); + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + SetDateToBytes(r.startTracking, data, pos); + f.Write(data, 0, 16); + } + f.Flush(); } - f.Flush(); } + Utils.Log("ReputationTracking: Saved " + dic.Count + " users"); + } catch (Exception e) { + Utils.Log("ERROR: problems in saving the Reputation file: " + e.Message); } - UtilityFunctions.Log("ReputationTracking: Saved " + dic.Count + " users"); } private void SetDateToBytes(DateTime d, byte[] data, int offset) { @@ -305,7 +341,11 @@ private void SetDateToBytes(DateTime d, byte[] data, int offset) { } private DateTime GetDateFromBytes(byte[] data, int offset) { - return new DateTime((data[offset + 0] << 8) + data[offset + 1], data[offset + 2], data[offset + 3]); + try { + return new DateTime((data[offset + 0] << 8) + data[offset + 1], data[offset + 2], data[offset + 3]); + } catch (Exception) { + return DateTime.Now; + } } public bool AlterRep(ulong id, bool add) { @@ -355,87 +395,96 @@ public class EmojisForRole { string path = null; public EmojisForRole(string path) { - values = new List(); - this.path = path; - if (!File.Exists(path)) return; - - DiscordGuild g = UtilityFunctions.GetGuild(); - byte[] data = new byte[36]; - using (FileStream f = new FileStream(path, FileMode.Open)) { - while (f.Read(data, 0, 36) == 36) { - ReactionValue v = new ReactionValue { - channel = BitConverter.ToUInt64(data, 0), - message = BitConverter.ToUInt64(data, 8), - role = BitConverter.ToUInt64(data, 16), - emojiId = BitConverter.ToUInt64(data, 24), - emojiName = Char.ConvertFromUtf32(BitConverter.ToInt32(data, 32)), - dRole = null - }; - - DiscordMessage m; - try { - // Does the message exists? - DiscordChannel c = g.GetChannel(v.channel); - if (c == null || c.Id == 0) continue; // Bad - m = c.GetMessageAsync(v.message).Result; - if (m == null || m.Id == 0) continue; // Bad; - // Does the role exists? - DiscordRole r = g.GetRole(v.role); - if (r == null || r.Id == 0) continue; // Bad - } catch (Exception ex) { - UtilityFunctions.Log("Error while checking a ReactionValue: " + ex.Message); - continue; - } - // Check that the message has the required emojis, if not add it - DiscordEmoji e = null; - if (v.emojiId != 0) { - e = g.GetEmojiAsync(v.emojiId).Result; - } else if (v.emojiName != "!") { - e = DiscordEmoji.FromUnicode(v.emojiName); - } - if (e == null) continue; // Bad - if (m.GetReactionsAsync(e, 1).Result.Count == 0) { // Need to add - m.CreateReactionAsync(e).Wait(); - } + try { + values = new List(); + this.path = path; + if (!File.Exists(path)) return; + + DiscordGuild g = Utils.GetGuild(); + byte[] data = new byte[36]; + using (FileStream f = new FileStream(path, FileMode.Open)) { + while (f.Read(data, 0, 36) == 36) { + ReactionValue v = new ReactionValue { + channel = BitConverter.ToUInt64(data, 0), + message = BitConverter.ToUInt64(data, 8), + role = BitConverter.ToUInt64(data, 16), + emojiId = BitConverter.ToUInt64(data, 24), + emojiName = Char.ConvertFromUtf32(BitConverter.ToInt32(data, 32)), + dRole = null + }; + + DiscordMessage m; + try { + // Does the message exists? + DiscordChannel c = g.GetChannel(v.channel); + if (c == null || c.Id == 0) continue; // Bad + m = c.GetMessageAsync(v.message).Result; + if (m == null || m.Id == 0) continue; // Bad; + // Does the role exists? + DiscordRole r = g.GetRole(v.role); + if (r == null || r.Id == 0) continue; // Bad + } catch (Exception ex) { + Utils.Log("Error while checking a ReactionValue: " + ex.Message); + continue; + } + // Check that the message has the required emojis, if not add it + DiscordEmoji e = null; + if (v.emojiId != 0) { + e = g.GetEmojiAsync(v.emojiId).Result; + } + else if (v.emojiName != "!") { + e = DiscordEmoji.FromUnicode(v.emojiName); + } + if (e == null) continue; // Bad + if (m.GetReactionsAsync(e, 1).Result.Count == 0) { // Need to add + m.CreateReactionAsync(e).Wait(); + } - values.Add(v); + values.Add(v); + } } + Utils.Log("Loaded " + values.Count + " tracked messages for emojis."); + } catch (Exception e) { + Utils.Log("ERROR: problems in loading the EmojiForRole file: " + e.Message); } - UtilityFunctions.Log("Loaded " + values.Count + " tracked messages for emojis."); } public void Save() { - lock (values) { - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - UtilityFunctions.Log("ERROR: cannot delete old EmojisForRole file: " + path + "\nException: " + e.Message); - return; - } - using (FileStream f = new FileStream(path, FileMode.CreateNew)) { - byte[] data = new byte[36]; - foreach (ReactionValue v in values) { - int pos = 0; - byte[] d = BitConverter.GetBytes(v.channel); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.message); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.role); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.emojiId); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - if (v.emojiId != 0 || v.emojiName == null || v.emojiName.Length == 0) - for (int i = 0; i < 4; i++) data[pos++] = 0; - else { - d = BitConverter.GetBytes(Char.ConvertToUtf32(v.emojiName, 0)); - for (int i = 0; i < 4; i++) data[pos++] = d[i]; + try { + lock (values) { + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old EmojisForRole file: " + path + "\nException: " + e.Message); + return; + } + using (FileStream f = new FileStream(path, FileMode.CreateNew)) { + byte[] data = new byte[36]; + foreach (ReactionValue v in values) { + int pos = 0; + byte[] d = BitConverter.GetBytes(v.channel); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.message); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.role); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.emojiId); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + if (v.emojiId != 0 || v.emojiName == null || v.emojiName.Length == 0) + for (int i = 0; i < 4; i++) data[pos++] = 0; + else { + d = BitConverter.GetBytes(Char.ConvertToUtf32(v.emojiName, 0)); + for (int i = 0; i < 4; i++) data[pos++] = d[i]; + } + f.Write(data, 0, pos); } - f.Write(data, 0, pos); + f.Flush(); } - f.Flush(); } + Utils.Log("EmojisForRole: Saved " + values.Count + " tracked messages and emojis"); + } catch (Exception e) { + Utils.Log("ERROR: problems in saving the EmojiForRole file: " + e.Message); } - UtilityFunctions.Log("EmojisForRole: Saved " + values.Count + " tracked messages and emojis"); } internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji) { @@ -478,30 +527,38 @@ internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji } internal void HandleAddingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { - DiscordMember dm = (DiscordMember)user; - if (dm == null) return; // Not a valid member for the Guild/Context - if (values == null) return; - foreach (ReactionValue v in values) { - if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { - if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); - dm.GrantRoleAsync(v.dRole).Wait(); - UtilityFunctions.Log("Role " + v.dRole.Name + " was granted to " + user.Username + " by emoji " + eId); - return; + try { + DiscordMember dm = (DiscordMember)user; + if (dm == null) return; // Not a valid member for the Guild/Context + if (values == null) return; + foreach (ReactionValue v in values) { + if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + dm.GrantRoleAsync(v.dRole).Wait(); + Utils.Log("Role " + v.dRole.Name + " was granted to " + user.Username + " by emoji " + eId); + return; + } } + } catch (Exception e) { + Utils.Log("ERROR: problems in HandleAddingEmojiForRole: " + e.Message); } } internal void HandleRemovingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { - DiscordMember dm = (DiscordMember)user; - if (dm == null) return; // Not a valid member for the Guild/Context - if (values == null) return; - foreach (ReactionValue v in values) { - if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { - if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); - dm.RevokeRoleAsync(v.dRole).Wait(); - UtilityFunctions.Log("Role " + v.dRole.Name + " was removed from " + user.Username + " by emoji " + eId); - return; + try { + DiscordMember dm = (DiscordMember)user; + if (dm == null) return; // Not a valid member for the Guild/Context + if (values == null) return; + foreach (ReactionValue v in values) { + if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + dm.RevokeRoleAsync(v.dRole).Wait(); + Utils.Log("Role " + v.dRole.Name + " was removed from " + user.Username + " by emoji " + eId); + return; + } } + } catch (Exception e) { + Utils.Log("ERROR: problems in HandleRemovingEmojiForRole: " + e.Message); } } diff --git a/UPBot Code/Actions/MembersTracking.cs b/UPBot Code/Actions/MembersTracking.cs index 4939542..0149868 100644 --- a/UPBot Code/Actions/MembersTracking.cs +++ b/UPBot Code/Actions/MembersTracking.cs @@ -9,6 +9,7 @@ public class MembersTracking { static DiscordChannel trackChannel = null; public static async Task DiscordMemberRemoved(DiscordClient client, DSharpPlus.EventArgs.GuildMemberRemoveEventArgs args) { + try{ if (tracking == null) tracking = new Dictionary(); if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); @@ -16,54 +17,65 @@ public static async Task DiscordMemberRemoved(DiscordClient client, DSharpPlus.E tracking.Remove(args.Member.Id); string msg = "User " + args.Member.DisplayName + " did a kiss and go."; await trackChannel.SendMessageAsync(msg); - UtilityFunctions.Log(msg); + Utils.Log(msg); } else { - string msgC = UtilityFunctions.GetEmojiSnowflakeID(EmojiEnum.KO) + " User " + args.Member.Mention + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; + string msgC = Utils.GetEmojiSnowflakeID(EmojiEnum.KO) + " User " + args.Member.Mention + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; string msgL = "- User " + args.Member.DisplayName + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; await trackChannel.SendMessageAsync(msgC); - UtilityFunctions.Log(msgL); + Utils.Log(msgL); + } + } catch (Exception ex) { + Utils.Log("Error in DiscordMemberRemoved: " + ex.Message); } await Task.Delay(10); } public static async Task DiscordMemberAdded(DiscordClient client, DSharpPlus.EventArgs.GuildMemberAddEventArgs args) { + try{ if (tracking == null) tracking = new Dictionary(); if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); tracking[args.Member.Id] = DateTime.Now; _ = SomethingAsync(args.Member.Id, args.Member.DisplayName, args.Member.Mention, args.Guild.MemberCount); + } catch (Exception ex) { + Utils.Log("Error in DiscordMemberAdded: " + ex.Message); + } await Task.Delay(10); } public static async Task DiscordMemberUpdated(DiscordClient client, DSharpPlus.EventArgs.GuildMemberUpdateEventArgs args) { - if (tracking == null) tracking = new Dictionary(); - if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); + try { + if (tracking == null) tracking = new Dictionary(); + if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); - IReadOnlyList rolesBefore = args.RolesBefore; - IReadOnlyList rolesAfter = args.RolesAfter; - List rolesAdded = new List(); - // Changed role? - foreach (DiscordRole r1 in rolesAfter) { - bool addedRole = true; - foreach (DiscordRole r2 in rolesBefore) { - if (r1.Equals(r2)) { - addedRole = false; + IReadOnlyList rolesBefore = args.RolesBefore; + IReadOnlyList rolesAfter = args.RolesAfter; + List rolesAdded = new List(); + // Changed role? + foreach (DiscordRole r1 in rolesAfter) { + bool addedRole = true; + foreach (DiscordRole r2 in rolesBefore) { + if (r1.Equals(r2)) { + addedRole = false; + } } + if (addedRole) rolesAdded.Add(r1); } - if (addedRole) rolesAdded.Add(r1); - } - string msgC; - string msgL; - if (rolesAdded.Count > 0) { - msgC = "User " + args.Member.Mention + " has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); - msgL = "User \"" + args.Member.DisplayName + "\" has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); - foreach (DiscordRole r in rolesAdded) { - msgC += r.Mention; - msgL += r.Name; + string msgC; + string msgL; + if (rolesAdded.Count > 0) { + msgC = "User " + args.Member.Mention + " has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); + msgL = "User \"" + args.Member.DisplayName + "\" has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); + foreach (DiscordRole r in rolesAdded) { + msgC += r.Mention; + msgL += r.Name; + } + await trackChannel.SendMessageAsync(msgC); + Utils.Log(msgL); } - await trackChannel.SendMessageAsync(msgC); - UtilityFunctions.Log(msgL); + } catch (Exception ex) { + Utils.Log("Error in DiscordMemberUpdated: " + ex.Message); } await Task.Delay(10); @@ -73,10 +85,10 @@ public static async Task DiscordMemberUpdated(DiscordClient client, DSharpPlus.E static async Task SomethingAsync(ulong id, string name, string mention, int numMembers) { await Task.Delay(25000); if (tracking.ContainsKey(id)) { - string msgC = UtilityFunctions.GetEmojiSnowflakeID(EmojiEnum.OK) + " User " + mention + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; + string msgC = Utils.GetEmojiSnowflakeID(EmojiEnum.OK) + " User " + mention + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; string msgL = "+ User " + name + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; await trackChannel.SendMessageAsync(msgC); - UtilityFunctions.Log(msgL); + Utils.Log(msgL); tracking.Remove(id); } } diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index 257e87e..63cca44 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -22,7 +22,7 @@ public class BannedWords : BaseCommandModule { public static void Init() { bannedWords = new List(); - string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); + string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); if (!File.Exists(path)) return; string[] all = File.ReadAllLines(path); foreach (string line in all) { @@ -37,7 +37,7 @@ public static void Init() { [Description("To handle banned words. It can be done only by Mods and Helpers")] [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task BannedWordsCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await ctx.Channel.SendMessageAsync("Use the commands `list`, `add`, and `remove` to handle banned words."); } @@ -45,7 +45,7 @@ public async Task BannedWordsCommand(CommandContext ctx) { [Description("To handle banned words. It can be done only by Mods and Helpers")] [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task BannedWordsCommand(CommandContext ctx, [Description("Command to use (list, add, remove)")] string command) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await HandleListOfBannedWords(ctx, command); } @@ -53,76 +53,81 @@ public async Task BannedWordsCommand(CommandContext ctx, [Description("Command t [Description("To handle banned words. It can be done only by Mods and Helpers")] [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task BannedWordsCommand(CommandContext ctx, [Description("Command to use (list, add, remove)")] string command, [Description("The word to add or remove (not used when listing)")] string word) { - UtilityFunctions.LogUserCommand(ctx); - Task msg = await HandleAddRemoveOfBannedWords(ctx, command, word); - await Task.Delay(5000); - await msg.Result.DeleteAsync(); - await Task.Delay(1000); - await ctx.Message.DeleteAsync(); - + Utils.LogUserCommand(ctx); + Task msg = HandleAddRemoveOfBannedWords(ctx, command, word).Result; + Utils.DeleteDelayed(30, msg.Result).Wait(); + await Utils.DeleteDelayed(10, ctx.Message); } private async Task> HandleListOfBannedWords(CommandContext ctx, string command) { - if (command.ToLowerInvariant() != "list") return ctx.Channel.SendMessageAsync("Use: list, add, or remove."); - else if (bannedWords == null || bannedWords.Count == 0) return ctx.Channel.SendMessageAsync("There are no banned words I am aware of."); - else { - string message = "I have " + bannedWords.Count + " banned word" + (bannedWords.Count == 1 ? "" : "s") + ":\n"; - for (int i = 0; i < bannedWords.Count; i++) { - message += bannedWords[i].word + " (" + GetUserName(bannedWords[i].creator, ctx) + " " + bannedWords[i].date.ToString("yyyy/MM/dd") + ")"; - if (i < bannedWords.Count - 1) message += ",\n"; + try { + if (command.ToLowerInvariant() != "list") return ctx.Channel.SendMessageAsync("Use: list, add, or remove."); + else if (bannedWords == null || bannedWords.Count == 0) return ctx.Channel.SendMessageAsync("There are no banned words I am aware of."); + else { + string message = "I have " + bannedWords.Count + " banned word" + (bannedWords.Count == 1 ? "" : "s") + ":\n"; + for (int i = 0; i < bannedWords.Count; i++) { + message += bannedWords[i].word + " (" + GetUserName(bannedWords[i].creator, ctx) + " " + bannedWords[i].date.ToString("yyyy/MM/dd") + ")"; + if (i < bannedWords.Count - 1) message += ",\n"; + } + await Task.Delay(10); + return ctx.Channel.SendMessageAsync(message); } - await Task.Delay(10); - return ctx.Channel.SendMessageAsync(message); + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("BannedWords.List", ex)); } } private async Task> HandleAddRemoveOfBannedWords(CommandContext ctx, string command, string word) { - await Task.Delay(10); - if (command.ToLowerInvariant() == "add") { - word = word.Trim(' ', '\r', '\n').ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(word) || !valid.IsMatch(word)) return ctx.Channel.SendMessageAsync("Not a valid word"); - if (bannedWords == null) bannedWords = new List(); - // Do we have it? - foreach (BannedWord bw in bannedWords) { - if (bw.word.Equals(word)) { - await ctx.Message.CreateReactionAsync(UtilityFunctions.GetEmoji(EmojiEnum.KO)); - return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is already in the list."); + try { + await Task.Delay(10); + if (command.ToLowerInvariant() == "add") { + word = word.Trim(' ', '\r', '\n').ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(word) || !valid.IsMatch(word)) return ctx.Channel.SendMessageAsync("Not a valid word"); + if (bannedWords == null) bannedWords = new List(); + // Do we have it? + foreach (BannedWord bw in bannedWords) { + if (bw.word.Equals(word)) { + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.KO)); + return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is already in the list."); + } } - } - BannedWord w = new BannedWord(word, ctx.Message.Author.Id); - bannedWords.Add(w); - bannedWords.Sort((a, b) => { return a.word.CompareTo(b.word); }); - SaveWord(w); + BannedWord w = new BannedWord(word, ctx.Message.Author.Id); + bannedWords.Add(w); + bannedWords.Sort((a, b) => { return a.word.CompareTo(b.word); }); + SaveWord(w); - await ctx.Message.CreateReactionAsync(UtilityFunctions.GetEmoji(EmojiEnum.OK)); - return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been added."); - } - else if (command.ToLowerInvariant() == "remove") { - word = word.Trim(' ', '\r', '\n').ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(word) || !Regex.IsMatch(word, @"^[a-zA-Z0-9]+$")) return ctx.Channel.SendMessageAsync("Not a valid word"); - if (bannedWords == null) bannedWords = new List(); - // Do we have it? - BannedWord found = null; - foreach (BannedWord bw in bannedWords) { - if (bw.word.Equals(word)) { - found = bw; - break; - } + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.OK)); + return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been added."); } - if (found == null) { - await ctx.Message.CreateReactionAsync(UtilityFunctions.GetEmoji(EmojiEnum.KO)); - return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is not in the list."); + else if (command.ToLowerInvariant() == "remove") { + word = word.Trim(' ', '\r', '\n').ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(word) || !Regex.IsMatch(word, @"^[a-zA-Z0-9]+$")) return ctx.Channel.SendMessageAsync("Not a valid word"); + if (bannedWords == null) bannedWords = new List(); + // Do we have it? + BannedWord found = null; + foreach (BannedWord bw in bannedWords) { + if (bw.word.Equals(word)) { + found = bw; + break; + } + } + if (found == null) { + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.KO)); + return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is not in the list."); + } + bannedWords.Remove(found); + SaveList(); + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.OK)); + return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been removed."); } - bannedWords.Remove(found); - SaveList(); - await ctx.Message.CreateReactionAsync(UtilityFunctions.GetEmoji(EmojiEnum.OK)); - return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been removed."); + else return ctx.Channel.SendMessageAsync("Use: add or remove and then the word."); + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("BannedWords.Handle", ex)); } - else return ctx.Channel.SendMessageAsync("Use: add or remove and then the word."); } void SaveWord(BannedWord w) { - string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); + string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); if (!File.Exists(path)) File.CreateText(path); try { using (StreamWriter sw = File.AppendText(path)) { @@ -130,17 +135,17 @@ void SaveWord(BannedWord w) { sw.FlushAsync(); } } catch (Exception e) { - UtilityFunctions.Log(e.Message); + Utils.Log(e.Message); } } void SaveList() { - string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); + string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); if (File.Exists(path)) { try { File.Delete(path); } catch (Exception e) { - UtilityFunctions.Log(e.Message); + Utils.Log(e.Message); return; } } @@ -152,7 +157,7 @@ void SaveList() { } } } catch (Exception e) { - UtilityFunctions.Log(e.Message); + Utils.Log(e.Message); } } @@ -199,33 +204,36 @@ async Task GetUserNameFromDiscord(ulong userId, CommandContext ctx) } internal static async Task CheckMessage(DiscordClient client, MessageCreateEventArgs args) { - // Who is the author? If the bot or a mod then ignore - if (args.Author.Equals(client.CurrentUser)) return; - DiscordUser user = args.Author; - DiscordGuild guild = await client.GetGuildAsync((ulong)args.Message.Channel.GuildId); - DiscordMember member; try { - member = await guild.GetMemberAsync(user.Id); - } catch (Exception) { - return; - } - foreach (DiscordRole role in member.Roles) { - if (role.Id == 831050318171078718ul /* Helper */ || role.Id == 830901743624650783ul /* Mod */ || role.Id == 830901562960117780ul /* Owner */) return; - } + // Who is the author? If the bot or a mod then ignore + if (args.Author.Equals(client.CurrentUser)) return; + DiscordUser user = args.Author; + DiscordGuild guild = await client.GetGuildAsync((ulong)args.Message.Channel.GuildId); + DiscordMember member; + try { + member = await guild.GetMemberAsync(user.Id); + } catch (Exception) { + return; + } + foreach (DiscordRole role in member.Roles) { + if (role.Id == 831050318171078718ul /* Helper */ || role.Id == 830901743624650783ul /* Mod */ || role.Id == 830901562960117780ul /* Owner */) return; + } - string msg = args.Message.Content.ToLowerInvariant(); - foreach (BannedWord w in bannedWords) { - int pos = msg.IndexOf(w.word); - if (pos == -1) continue; - if (pos > 0 && letters.IsMatch(msg[pos - 1].ToString())) continue; - if (pos + w.word.Length < msg.Length && letters.IsMatch(msg[pos + w.word.Length].ToString())) continue; - - UtilityFunctions.Log("Removed word \"" + w.word + "\" from " + user.Username + " in: " + msg); - DiscordMessage warning = await args.Message.Channel.SendMessageAsync("Moderate your language, " + user.Mention + "."); - await args.Message.DeleteAsync("Bad words: " + w.word); - await Task.Delay(10000); - await warning.DeleteAsync(); - return; + string msg = args.Message.Content.ToLowerInvariant(); + foreach (BannedWord w in bannedWords) { + int pos = msg.IndexOf(w.word); + if (pos == -1) continue; + if (pos > 0 && letters.IsMatch(msg[pos - 1].ToString())) continue; + if (pos + w.word.Length < msg.Length && letters.IsMatch(msg[pos + w.word.Length].ToString())) continue; + + Utils.Log("Removed word \"" + w.word + "\" from " + user.Username + " in: " + msg); + DiscordMessage warning = await args.Message.Channel.SendMessageAsync("Moderate your language, " + user.Mention + "."); + await args.Message.DeleteAsync("Bad words: " + w.word); + Utils.DeleteDelayed(10000, warning).Wait(); + return; + } + } catch (Exception ex) { + await args.Message.RespondAsync(Utils.GenerateErrorAnswer("BannedWords.CheckMessage", ex)); } } diff --git a/UPBot Code/Commands/CustomCommand.cs b/UPBot Code/Commands/CustomCommand.cs index 85c6055..413b77f 100644 --- a/UPBot Code/Commands/CustomCommand.cs +++ b/UPBot Code/Commands/CustomCommand.cs @@ -10,7 +10,7 @@ public class CustomCommand public CustomCommand(string[] names, string content) { this.Names = names; - this.FilePath = UtilityFunctions.ConstructPath(CustomCommandsService.DirectoryNameCC, names[0], ".txt"); + this.FilePath = Utils.ConstructPath(CustomCommandsService.DirectoryNameCC, names[0], ".txt"); this.Content = content; } diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 9454abe..79568f7 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -20,7 +20,7 @@ public class CustomCommandsService : BaseCommandModule internal const string DirectoryNameCC = "CustomCommands"; [Command("newcc")] - [Aliases("createcc", "addcc", "ccadd", "cccreate")] + [Aliases("createcc", "addcc", "ccadd", "cccreate", "ccnew")] [Description("**Create** a new Custom Command (so-called 'CC') with a specified name and all aliases if desired " + "(no duplicate alias allowed).\nAfter doing this, the bot will ask you to input the content, which will " + "be displayed once someone invokes this CC. Your entire next message will be used for the content, so " + @@ -29,12 +29,13 @@ public class CustomCommandsService : BaseCommandModule [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of all aliases. The first term is the **main name**, the other ones, separated by a space, are aliases")] params string[] names) { - names = names.Distinct().ToArray(); + Utils.LogUserCommand(ctx); + names = names.Distinct().ToArray(); foreach (var name in names) { if (DiscordClient.GetCommandsNext().RegisteredCommands.ContainsKey(name)) // Check if there is a command with one of the names already { - await UtilityFunctions.ErrorCallback(CommandErrors.CommandExists, ctx, name); + await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); return; } @@ -42,7 +43,7 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al { if (cmd.Names.Contains(name)) // Check if there is already a CC with one of the names { - await UtilityFunctions.ErrorCallback(CommandErrors.CommandExists, ctx, name); + await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); return; } } @@ -54,7 +55,7 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al await WriteToFile(command); string embedMessage = $"CC {names[0]} successfully created and saved!"; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, false); + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); } [Command("ccdel")] @@ -65,7 +66,8 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task DeleteCommand(CommandContext ctx, [Description("Main name of the CC you want to delete")] string name) { - string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, name, ".txt"); + Utils.LogUserCommand(ctx); + string filePath = Utils.ConstructPath(DirectoryNameCC, name, ".txt"); if (File.Exists(filePath)) { File.Delete(filePath); @@ -73,7 +75,7 @@ public async Task DeleteCommand(CommandContext ctx, [Description("Main name of t Commands.Remove(cmd); string embedMessage = $"CC {name} successfully deleted!"; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, true); + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, true); } } @@ -85,7 +87,8 @@ public async Task DeleteCommand(CommandContext ctx, [Description("Main name of t [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EditCommand(CommandContext ctx, [Description("Main name of the CC you want to edit")] string name) { - string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, name, ".txt"); + Utils.LogUserCommand(ctx); + string filePath = Utils.ConstructPath(DirectoryNameCC, name, ".txt"); if (File.Exists(filePath)) { string content = await WaitForContent(ctx, name); @@ -104,10 +107,10 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the command.EditCommand(content); string embedMessage = $"CC **{name}** successfully edited!"; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, false); + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); } else - await UtilityFunctions.ErrorCallback(CommandErrors.MissingCommand, ctx); + await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); } [Command("cceditname")] @@ -121,14 +124,15 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the "of the CC whose name you want to edit, the **SECOND** term " + "is the new **main name** and all the other terms are new aliases")] params string[] names) { - names = names.Distinct().ToArray(); + Utils.LogUserCommand(ctx); + names = names.Distinct().ToArray(); if (names.Length < 2) { - await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParams, ctx); + await Utils.ErrorCallback(CommandErrors.InvalidParams, ctx); return; } - string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, names[0], ".txt"); + string filePath = Utils.ConstructPath(DirectoryNameCC, names[0], ".txt"); if (File.Exists(filePath)) { if (TryGetCommand(names[0], out CustomCommand command)) @@ -143,7 +147,7 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the content += c + System.Environment.NewLine; } - string newPath = UtilityFunctions.ConstructPath(DirectoryNameCC, names[1], ".txt"); + string newPath = Utils.ConstructPath(DirectoryNameCC, names[1], ".txt"); File.Move(filePath, newPath); using (StreamWriter sw = File.CreateText(newPath)) { @@ -152,10 +156,10 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the } string embedDescription = "The CC names have been successfully edited."; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedDescription, UtilityFunctions.Green, ctx, false); + await Utils.BuildEmbedAndExecute("Success", embedDescription, Utils.Green, ctx, false); } else - await UtilityFunctions.ErrorCallback(CommandErrors.MissingCommand, ctx); + await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); } [Command("cclist")] @@ -163,9 +167,10 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the [Description("Get a list of all Custom Commands (CC's).")] public async Task ListCC(CommandContext ctx) { - if (Commands.Count <= 0) + Utils.LogUserCommand(ctx); + if (Commands.Count <= 0) { - await UtilityFunctions.ErrorCallback(CommandErrors.NoCustomCommands, ctx); + await Utils.ErrorCallback(CommandErrors.NoCustomCommands, ctx); return; } @@ -176,7 +181,7 @@ public async Task ListCC(CommandContext ctx) allCommands += $"- {cmd.Names[0]} ({(cmd.Names.Length > 1 ? string.Join(", ", cmd.Names.Skip(1)) : string.Empty)}){System.Environment.NewLine}"; } - await UtilityFunctions.BuildEmbedAndExecute("CC List", allCommands, UtilityFunctions.Yellow, ctx, true); + await Utils.BuildEmbedAndExecute("CC List", allCommands, Utils.Yellow, ctx, true); } internal static async Task LoadCustomCommands() @@ -229,7 +234,7 @@ private async Task WriteToFile(CustomCommand command) private async Task WaitForContent(CommandContext ctx, string name) { string embedMessage = $"Please input the content of the CC **{name}** in one single message. Your next message will count as the content."; - await UtilityFunctions.BuildEmbedAndExecute("Waiting for interaction", embedMessage, UtilityFunctions.LightBlue, ctx, true); + await Utils.BuildEmbedAndExecute("Waiting for interaction", embedMessage, Utils.LightBlue, ctx, true); string content = string.Empty; await ctx.Message.GetNextMessageAsync(m => diff --git a/UPBot Code/Commands/DebugCommand.cs b/UPBot Code/Commands/DebugCommand.cs deleted file mode 100644 index 06b964d..0000000 --- a/UPBot Code/Commands/DebugCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -using DSharpPlus; -using DSharpPlus.CommandsNext; -using DSharpPlus.CommandsNext.Attributes; -using DSharpPlus.Entities; -using System.Threading.Tasks; - -/// -/// Debug command to quickly test some new stuff -/// author: CPU -/// -public class DebugCommand : BaseCommandModule { - - [Command("qwe")] - [Description("This is a command just to quickly debug stuff in development")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task DoDebug(CommandContext ctx) { // Refactors the previous post, if it is code - UtilityFunctions.LogUserCommand(ctx); - DiscordMessage msg = await ctx.Channel.SendMessageAsync("Test message"); - - DiscordEmoji emoji = UtilityFunctions.GetEmoji(EmojiEnum.Godot); - await msg.CreateReactionAsync(emoji); - - await ctx.Message.RespondAsync("Done"); - } -} diff --git a/UPBot Code/Commands/Delete.cs b/UPBot Code/Commands/Delete.cs index 084c6e0..a45c7cc 100644 --- a/UPBot Code/Commands/Delete.cs +++ b/UPBot Code/Commands/Delete.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DSharpPlus; @@ -11,96 +12,96 @@ /// or the last x messages of a specific user /// author: Duck /// -public class Delete : BaseCommandModule -{ - private const int MessageLimit = 50; - private const string CallbackLimitExceeded = ", since you can't delete more than 50 messages at a time."; +public class Delete : BaseCommandModule { + private const int MessageLimit = 50; + private const string CallbackLimitExceeded = ", since you can't delete more than 50 messages at a time."; - /// - /// Delete the last x messages of any user - /// - [Command("delete")] - [Aliases("clear", "purge")] - [Description("Deletes the last x messages in the channel, the command was invoked in (e.g. `\\delete 10`)." + - "\nIt contains an overload to delete the last x messages of a specified user (e.g. `\\delete @User 10`)." + - "\nThis command can only be invoked by a Helper or Mod.")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task DeleteCommand(CommandContext ctx, [Description("How many messages should be deleted?")] int count) - { - UtilityFunctions.LogUserCommand(ctx); - if (count <= 0) - { - await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); - return; - } + /// + /// Delete the last x messages of any user + /// + [Command("delete")] + [Aliases("clear", "purge")] + [Description("Deletes the last x messages in the channel, the command was invoked in (e.g. `\\delete 10`)." + + "\nIt contains an overload to delete the last x messages of a specified user (e.g. `\\delete @User 10`)." + + "\nThis command can only be invoked by a Helper or Mod.")] + [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission + [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only + public async Task DeleteCommand(CommandContext ctx, [Description("How many messages should be deleted?")] int count) { + Utils.LogUserCommand(ctx); + if (count <= 0) { + await Utils.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); + return; + } + + bool limitExceeded = CheckLimit(count); - bool limitExceeded = CheckLimit(count); + var messages = ctx.Channel.GetMessagesAsync(count + 1).Result; + await DeleteMessages(ctx.Message, messages); - var messages = ctx.Channel.GetMessagesAsync(count + 1).Result; - await DeleteMessages(ctx, messages); + await Success(ctx, limitExceeded, count); + } - await Success(ctx, limitExceeded, count); + /// + /// Delete the last x messages of the specified user + /// + [Command("delete")] + [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission + [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only + public async Task DeleteCommand(CommandContext ctx, [Description("Whose last x messages should get deleted?")] DiscordMember targetUser, + [Description("How many messages should get deleted?")] int count) { + Utils.LogUserCommand(ctx); + if (count <= 0) { + await Utils.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); + return; } - /// - /// Delete the last x messages of the specified user - /// - [Command("delete")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task DeleteCommand(CommandContext ctx, [Description("Whose last x messages should get deleted?")]DiscordMember targetUser, - [Description("How many messages should get deleted?")] int count) - { - UtilityFunctions.LogUserCommand(ctx); - if (count <= 0) - { - await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); - return; - } + bool limitExceeded = CheckLimit(count); - bool limitExceeded = CheckLimit(count); + var allMessages = ctx.Channel.GetMessagesAsync().Result; // Get last 100 messages + var userMessages = allMessages.Where(x => x.Author == targetUser).Take(count + 1); + await DeleteMessages(ctx.Message, userMessages); - var allMessages = ctx.Channel.GetMessagesAsync().Result; // Get last 100 messages - var userMessages = allMessages.Where(x => x.Author == targetUser).Take(count + 1); - await DeleteMessages(ctx, userMessages); + await Success(ctx, limitExceeded, count, targetUser); + } - await Success(ctx, limitExceeded, count, targetUser); + /// + /// The core-process of deleting the messages + /// + public async Task DeleteMessages(DiscordMessage request, IEnumerable messages) { + try{ + List toDelete = new List(); + foreach (DiscordMessage m in messages) { + if (m != request) toDelete.Add(m); } - - /// - /// The core-process of deleting the messages - /// - public async Task DeleteMessages(CommandContext ctx, IEnumerable messages) - { - foreach (DiscordMessage m in messages) - { - if (m != ctx.Message) - await m.DeleteAsync(); - } + await request.Channel.DeleteMessagesAsync(toDelete); + } catch (Exception ex) { + await request.RespondAsync(Utils.GenerateErrorAnswer("DeleteMessages", ex)); } - /// - /// Will be called at the end of every execution of this command and tells the user that the execution succeeded - /// including a short summary of the command (how many messages, by which user etc.) - /// - private async Task Success(CommandContext ctx, bool limitExceeded, int count, DiscordMember targetUser = null) - { - string mentionUserStr = targetUser == null ? string.Empty : $"by '{targetUser.DisplayName}'"; - string overLimitStr = limitExceeded ? CallbackLimitExceeded : string.Empty; - string messagesLiteral = UtilityFunctions.PluralFormatter(count, "message", "messages"); - string hasLiteral = UtilityFunctions.PluralFormatter(count, "has", "have"); + } - await ctx.Message.DeleteAsync(); - string embedMessage = $"The last {count} {messagesLiteral} {mentionUserStr} {hasLiteral} been successfully deleted{overLimitStr}."; + /// + /// Will be called at the end of every execution of this command and tells the user that the execution succeeded + /// including a short summary of the command (how many messages, by which user etc.) + /// + private async Task Success(CommandContext ctx, bool limitExceeded, int count, DiscordMember targetUser = null) { + try{ + string mentionUserStr = targetUser == null ? string.Empty : $"by '{targetUser.DisplayName}'"; + string overLimitStr = limitExceeded ? CallbackLimitExceeded : string.Empty; + string messagesLiteral = Utils.PluralFormatter(count, "message", "messages"); + string hasLiteral = Utils.PluralFormatter(count, "has", "have"); - var message = await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, true); - await Task.Delay(10_000); - await message.DeleteAsync(); - } + await ctx.Message.DeleteAsync(); + string embedMessage = $"The last {count} {messagesLiteral} {mentionUserStr} {hasLiteral} been successfully deleted{overLimitStr}."; - private bool CheckLimit(int count) - { - return count > MessageLimit; + var message = await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, true); + await Utils.DeleteDelayed(10, message); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("Delete", ex)); } + } + + private bool CheckLimit(int count) { + return count > MessageLimit; + } } \ No newline at end of file diff --git a/UPBot Code/Commands/Game.cs b/UPBot Code/Commands/Game.cs index a65db33..7006266 100644 --- a/UPBot Code/Commands/Game.cs +++ b/UPBot Code/Commands/Game.cs @@ -14,7 +14,7 @@ public class GameModule : BaseCommandModule [Command("game")] public async Task GameCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); StringBuilder sb = new StringBuilder("Available game commmands\n"); sb.AppendLine("========================"); sb.AppendLine(String.Format("{0, -10}: {1}", "bool", "True or False")); @@ -26,21 +26,21 @@ public async Task GameCommand(CommandContext ctx) [Command("bool")] public async Task BoolCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await PlayBool(ctx); } [Command("rps")] public async Task RPSCommand(CommandContext ctx, string kind) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await PlayRockPaperScissors(ctx, kind); } [Command("rps")] public async Task RPSCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await PlayRockPaperScissors(ctx, null); } diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index af8c779..58f8537 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -47,17 +47,18 @@ public async Task ErrorMessage(CommandContext ctx) { string title = "Help Language - How To Use"; DiscordMember member = ctx.Member; - string description = member.Mention + " , if you want to get video course about specific language type: `\\helplanguage video C#`" + - " \nIf you want to get full online course about specific language type: \n`\\helplanguage course C#`" + + string description = member.Mention + " , if you want to get video course about specific language type: `helplanguage video C#`" + + " \nIf you want to get full online course about specific language type: \n`helplanguage course C#`" + " \nAvailable languages: `ะก#, C++, Python, JavaScript, Java`"; - await UtilityFunctions.BuildEmbedAndExecute(title, description, UtilityFunctions.Red, ctx, true); + await Utils.BuildEmbedAndExecute(title, description, Utils.Red, ctx, true); } [Command("helplanguage")] - [Description("Gives good tutorials on specific language.\n**Usage:** `\\helplanguage language`")] + [Description("Gives good tutorials on specific language.\n**Usage**: `helplanguage language`")] public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string typeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); + try { lang = NormalizeLanguage(lang); if (lang == null) @@ -73,6 +74,9 @@ public async Task HelpCommand(CommandContext ctx, [Description("Choose what you bool course = typeOfHelp.ToLowerInvariant() != "video"; await GenerateHelpfulAnswer(ctx, lang, course); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("WhoIs", ex)); + } } private string NormalizeLanguage(string language) { @@ -100,11 +104,10 @@ private string NormalizeLanguage(string language) { default: return char.ToUpperInvariant(language[0]) + language.Substring(1); } - - //return language; } private async Task GenerateHelpfulAnswer(CommandContext ctx, string language, bool isCourse) { + try{ DiscordMember member = ctx.Member; ulong memberId = member.Id; @@ -133,12 +136,14 @@ private async Task GenerateHelpfulAnswer(CommandContext ctx, string language, bo string link = isCourse ? languages[language].CourseLink :languages[language].VideoLink; string msg = helpfulAnswers[lastRequest.Num]; - msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) - .Replace("///", link); + msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention).Replace("///", link); if (isCourse) msg = msg.Replace("video", "course"); - await UtilityFunctions.BuildEmbedAndExecute(title, msg, languages[language].Color, ctx, true); + await Utils.BuildEmbedAndExecute(title, msg, languages[language].Color, ctx, true); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("HelpLanguage", ex)); + } } private class LastRequestByMember { diff --git a/UPBot Code/Commands/Ping.cs b/UPBot Code/Commands/Ping.cs index f9b1475..96bd43f 100644 --- a/UPBot Code/Commands/Ping.cs +++ b/UPBot Code/Commands/Ping.cs @@ -14,11 +14,9 @@ public class PingModule : BaseCommandModule { private List lastRequests = null; [Command("ping")] - public async Task GreetCommand(CommandContext ctx) { - await GeneratePong(ctx); - } - [Command("upbot")] - public async Task GreetCommand2(CommandContext ctx) { + [Aliases("upbot")] + [Description("Checks if the bot is alive")] + public async Task PongCommand(CommandContext ctx) { await GeneratePong(ctx); } @@ -37,39 +35,47 @@ public async Task GreetCommand2(CommandContext ctx) { int lastGlobal = -1; Task GeneratePong(CommandContext ctx) { + Utils.LogUserCommand(ctx); + try { + + // Check if we have to initiialize our history of pings + if (lastRequests == null) lastRequests = new List(); + + // Grab the current member id + DiscordMember member = ctx.Member; + ulong memberId = member.Id; + + // Find the last request + LastRequestByMember lastRequest = null; + int annoyedLevel = 0; + foreach (LastRequestByMember lr in lastRequests) + if (lr.memberId == memberId) { + lastRequest = lr; + break; + } + if (lastRequest == null) { // No last request, create one + lastRequest = new LastRequestByMember(memberId); + lastRequests.Add(lastRequest); + } + else { + annoyedLevel = lastRequest.AddRequest(); + } + if (annoyedLevel == -1) return ctx.RespondAsync(""); // No answer - // Check if we have to initiialize our history of pings - if (lastRequests == null) lastRequests = new List(); + // Was the request already done recently? + int rnd = random.Next(0, 7); + while (rnd == lastRequest.lastRandom || rnd == lastGlobal) rnd = random.Next(0, 7); // Find one that is not the same of last one + lastRequest.lastRandom = rnd; // Record for the next time + lastGlobal = rnd; // Record for the next time + string msg = answers[annoyedLevel, rnd]; + msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention); - // Grab the current member id - DiscordMember member = ctx.Member; - ulong memberId = member.Id; - // Find the last request - LastRequestByMember lastRequest = null; - int annoyedLevel = 0; - foreach (LastRequestByMember lr in lastRequests) - if (lr.memberId == memberId) { - lastRequest = lr; - break; - } - if (lastRequest == null) { // No last request, create one - lastRequest = new LastRequestByMember(memberId); - lastRequests.Add(lastRequest); - } - else { - annoyedLevel = lastRequest.AddRequest(); + DiscordMessage answer = ctx.RespondAsync(msg).Result; + return Utils.DeleteDelayed(30, ctx.Message, answer); // We want to remove the ping and the answer after a minute + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("Ping", ex)); } - if (annoyedLevel == -1) return ctx.RespondAsync(""); // No answer - - // Was the request already done recently? - int rnd = random.Next(0, 7); - while (rnd == lastRequest.lastRandom || rnd == lastGlobal) rnd = random.Next(0, 7); // Find one that is not the same of last one - lastRequest.lastRandom = rnd; // Record for the next time - lastGlobal = rnd; // Record for the next time - string msg = answers[annoyedLevel, rnd]; - msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention); - return ctx.RespondAsync(msg); } const int MaxTrackedRequests = 10; diff --git a/UPBot Code/Commands/Refactor.cs b/UPBot Code/Commands/Refactor.cs index 63d05e5..86d9e12 100644 --- a/UPBot Code/Commands/Refactor.cs +++ b/UPBot Code/Commands/Refactor.cs @@ -13,19 +13,19 @@ public class Refactor : BaseCommandModule { [Command("checklanguage")] - [Description("Check what language is in the last post or in the post you replied to")] + [Description("Checks what language is in the post you replied to, or the last post of a specified user or just the last post.")] public async Task CheckLanguage(CommandContext ctx) { // Refactors the previous post, if it is code await RefactorCode(ctx, null, "best"); } [Command("checklanguage")] - [Description("Check what language is in the last post of the user")] + [Description("Checks what language is in the post you replied to, or the last post of a specified user or just the last post.")] public async Task CheckLanguage(CommandContext ctx, [Description("The user the posted the message to check")] DiscordMember member) { // Refactors the previous post, if it is code await RefactorCode(ctx, member, "best"); } [Command("reformat")] - [Description("Replace the last post of the specified user or the post you replied to with a formatted code block using the specified language")] + [Description("Replace a specified post with a reformatted code block using the specified language or the best language")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task RefactorCommand(CommandContext ctx) { // Refactors the previous post, if it is code @@ -36,7 +36,7 @@ public class Refactor : BaseCommandModule { [Description("Replace the last post of the specified user or the post you replied to with a formatted code block")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task ReformatCommand(CommandContext ctx, [Description("Force the Language to use. Use 'best' or 'Analyze' to find the best language.")] string language) { // Refactors the previous post, if it is code + public async Task ReformatCommand(CommandContext ctx, [Description("Force the Language to use. Use **Best** or **Analyze** to find the best language.")] string language) { // Refactors the previous post, if it is code await RefactorCode(ctx, null, language); } @@ -55,96 +55,100 @@ public async Task RefacReformatCommandtorCommand(CommandContext ctx, [Descriptio } private async Task> RefactorCode(CommandContext ctx, DiscordMember m, string language) { - UtilityFunctions.LogUserCommand(ctx); - DiscordChannel c = ctx.Channel; - DiscordMessage toRefactor = null; - if (ctx.Message.Reference != null) toRefactor = ctx.Message.Reference.Message; - else { - IReadOnlyList msgs = await c.GetMessagesAsync(50); - if (m == null) toRefactor = msgs[1]; + Utils.LogUserCommand(ctx); + try { + DiscordChannel c = ctx.Channel; + DiscordMessage toRefactor = null; + if (ctx.Message.Reference != null) toRefactor = ctx.Message.Reference.Message; else { - for (int i = 1; i < msgs.Count; i++) { - if (msgs[i].Author.Id.Equals(m.Id)) { - toRefactor = msgs[i]; - break; + IReadOnlyList msgs = await c.GetMessagesAsync(50); + if (m == null) toRefactor = msgs[1]; + else { + for (int i = 1; i < msgs.Count; i++) { + if (msgs[i].Author.Id.Equals(m.Id)) { + toRefactor = msgs[i]; + break; + } } } + if (toRefactor == null) return ctx.RespondAsync("Nothing to refactor found"); } - if (toRefactor == null) return ctx.RespondAsync("Nothing to refactor found"); - } - // Is the message some code? - string code = toRefactor.Content; - int weightCs = 0, weightCp = 0, weightJv = 0, weightJs = 0, weightPy = 0; - foreach (LangKWord k in keywords) { - if (k.regexp.IsMatch(code)) { - weightCs += k.wCs; - weightCp += k.wCp; - weightJv += k.wJv; - weightJs += k.wJs; - weightPy += k.wPy; + // Is the message some code? + string code = toRefactor.Content; + int weightCs = 0, weightCp = 0, weightJv = 0, weightJs = 0, weightPy = 0; + foreach (LangKWord k in keywords) { + if (k.regexp.IsMatch(code)) { + weightCs += k.wCs; + weightCp += k.wCp; + weightJv += k.wJv; + weightJs += k.wJs; + weightPy += k.wPy; + } } - } - string guessed = "no one"; - string best = ""; - EmojiEnum langEmoji = EmojiEnum.None; - int w = 0; - if (weightCs > w) { guessed = "<:csharp:831465428214743060> C#"; w = weightCs; best = "cs"; langEmoji = EmojiEnum.CSharp; } - if (weightCp > w) { guessed = "<:cpp:831465408874676273> C++"; w = weightCp; best = "cpp"; langEmoji = EmojiEnum.Cpp; } - if (weightJs > w) { guessed = "<:Javascript:876103767068647435> Javascript"; w = weightJs; best = "js"; langEmoji = EmojiEnum.Javascript; } - if (weightJv > w) { guessed = "<:java:875852276017815634> Java"; w = weightJv; best = "java"; langEmoji = EmojiEnum.Java; } - if (weightPy > w) { guessed = "<:python:831465381016895500> Python"; w = weightPy; best = "python"; langEmoji = EmojiEnum.Python; } - if (w == 0 && language == null) return ctx.RespondAsync("Nothing to reformat"); + string guessed = "no one"; + string best = ""; + EmojiEnum langEmoji = EmojiEnum.None; + int w = 0; + if (weightCs > w) { guessed = "<:csharp:831465428214743060> C#"; w = weightCs; best = "cs"; langEmoji = EmojiEnum.CSharp; } + if (weightCp > w) { guessed = "<:cpp:831465408874676273> C++"; w = weightCp; best = "cpp"; langEmoji = EmojiEnum.Cpp; } + if (weightJs > w) { guessed = "<:Javascript:876103767068647435> Javascript"; w = weightJs; best = "js"; langEmoji = EmojiEnum.Javascript; } + if (weightJv > w) { guessed = "<:java:875852276017815634> Java"; w = weightJv; best = "java"; langEmoji = EmojiEnum.Java; } + if (weightPy > w) { guessed = "<:python:831465381016895500> Python"; w = weightPy; best = "python"; langEmoji = EmojiEnum.Python; } + if (w == 0 && language == null) return ctx.RespondAsync("Nothing to reformat"); - language = NormalizeLanguage(language, best); - if (language == null) - return ctx.RespondAsync("Best guess for the language is: " + guessed + "\nC# = " + weightCs + " C++ = " + weightCp + " Java = " + weightJv + " Javascript = " + weightJs + " Python = " + weightPy); + language = NormalizeLanguage(language, best); + if (language == null) + return ctx.RespondAsync("Best guess for the language is: " + guessed + "\nC# = " + weightCs + " C++ = " + weightCp + " Java = " + weightJv + " Javascript = " + weightJs + " Python = " + weightPy); - // Remove the ``` at begin and end, if any. And the code name after initial ``` - bool deleteOrig = true; - Match codeMatch = codeBlock.Match(code); - if (codeMatch.Success) { - code = codeMatch.Groups[5].Value; - deleteOrig = string.IsNullOrWhiteSpace(codeMatch.Groups[1].Value); - } - code = code.Trim(' ', '\t', '\r', '\n'); - code = emptyLines.Replace(code, "\n"); - - if (langEmoji == EmojiEnum.CSharp || langEmoji == EmojiEnum.Cpp || langEmoji == EmojiEnum.Java || langEmoji == EmojiEnum.Javascript) code = FixIndentation(code); + // Remove the ``` at begin and end, if any. And the code name after initial ``` + bool deleteOrig = true; + Match codeMatch = codeBlock.Match(code); + if (codeMatch.Success) { + code = codeMatch.Groups[5].Value; + deleteOrig = string.IsNullOrWhiteSpace(codeMatch.Groups[1].Value); + } + code = code.Trim(' ', '\t', '\r', '\n'); + code = emptyLines.Replace(code, "\n"); - code = "Reformatted " + toRefactor.Author.Mention + " code\n" + "```" + language + "\n" + code + "\n```"; + if (langEmoji == EmojiEnum.CSharp || langEmoji == EmojiEnum.Cpp || langEmoji == EmojiEnum.Java || langEmoji == EmojiEnum.Javascript) code = FixIndentation(code); - if (guessed == "no one" && language != null) { - langEmoji = GetLanguageEmoji(language); - } + code = "Reformatted " + toRefactor.Author.Mention + " code\n" + "```" + language + "\n" + code + "\n```"; - DiscordMessage replacement = await ctx.Channel.SendMessageAsync(code); - DiscordEmoji autoRefactored = UtilityFunctions.GetEmoji(EmojiEnum.AutoRefactored); - DiscordEmoji emoji = UtilityFunctions.GetEmoji(langEmoji); - try { - if (autoRefactored != null) { - await Task.Delay(120); - await replacement.CreateReactionAsync(autoRefactored); + if (guessed == "no one" && language != null) { + langEmoji = GetLanguageEmoji(language); } - if (emoji != null) { - await Task.Delay(120); - await replacement.CreateReactionAsync(emoji); - } - if (deleteOrig) { - await Task.Delay(120); - List toDelete = new List { toRefactor, ctx.Message }; - await ctx.Channel.DeleteMessagesAsync(toDelete); - } - else { - await Task.Delay(120); - await ctx.Message.DeleteAsync(); + + DiscordMessage replacement = await ctx.Channel.SendMessageAsync(code); + DiscordEmoji autoRefactored = Utils.GetEmoji(EmojiEnum.AutoRefactored); + DiscordEmoji emoji = Utils.GetEmoji(langEmoji); + try { + if (autoRefactored != null) { + await Task.Delay(120); + await replacement.CreateReactionAsync(autoRefactored); + } + if (emoji != null) { + await Task.Delay(120); + await replacement.CreateReactionAsync(emoji); + } + if (deleteOrig) { + await Task.Delay(120); + List toDelete = new List { toRefactor, ctx.Message }; + await ctx.Channel.DeleteMessagesAsync(toDelete); + } + else { + await Task.Delay(120); + await ctx.Message.DeleteAsync(); + } + } catch (Exception e) { + return ctx.RespondAsync("Exception: " + e.Message); } - } catch (Exception e) { - return ctx.RespondAsync("Exception: " + e.Message); + await Task.Delay(150); + return ctx.RespondAsync(""); + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("Refactor", ex)); } - await Task.Delay(150); - return ctx.RespondAsync(""); } readonly Regex lineOpenBlock = new Regex("^{(\\s*//.*|\\s*/\\*/.*)?$", RegexOptions.Multiline, TimeSpan.FromSeconds(1)); diff --git a/UPBot Code/Commands/Stats.cs b/UPBot Code/Commands/Stats.cs index 8fc41d0..3923e10 100644 --- a/UPBot Code/Commands/Stats.cs +++ b/UPBot Code/Commands/Stats.cs @@ -29,59 +29,57 @@ public class Stats : BaseCommandModule { [Command("stats")] - [Description("Provides server stats for the 4 most used channels (Unity, CSharp, Help 1, and Help 2")] + [Description("Provides server stats for specific channels, with no parameters checks the most 4 commons channels")] + [Cooldown(1, 60, CooldownBucketType.Channel | CooldownBucketType.User)] public async Task DoStats(CommandContext ctx) { await GenerateStats(ctx, null, 100); } [Command("stats")] - [Description("Provides server stats for a specific channel")] - public async Task DoStats(CommandContext ctx, DiscordChannel channel) { + [Description("Provides server stats for specific channels")] + [Cooldown(1, 60, CooldownBucketType.Channel | CooldownBucketType.User)] + public async Task DoStats(CommandContext ctx, [Description("Specific channel for the stats")] DiscordChannel channel) { await GenerateStats(ctx, channel, 100); } [Command("stats")] - [Description("Provides server stats, for a specified chanenl and with the required number of messages")] - public async Task DoStats(CommandContext ctx, DiscordChannel channel, int numMessages) { + [Description("Provides server stats for specific channels")] + [Cooldown(1, 60, CooldownBucketType.Channel | CooldownBucketType.User)] + public async Task DoStats(CommandContext ctx, [Description("Specific channel for the stats")] DiscordChannel channel, [Description("Number of messages to check")] int numMessages) { await GenerateStats(ctx, channel, numMessages); } [Command("stats")] - [Description("Provides server stats, for a specified chanenl and with the required number of messages")] - public async Task DoStats(CommandContext ctx, int numMessages, DiscordChannel channel) { - await GenerateStats(ctx, channel, numMessages); - } - [Command("stats")] - [Description("Provides server stats for the 4 most used channels, with the required number of messages")] - public async Task DoStats(CommandContext ctx, int numMessages) { + [Description("Provides server stats for specific channels")] + [Cooldown(1, 60, CooldownBucketType.Channel | CooldownBucketType.User)] + public async Task DoStats(CommandContext ctx, [Description("Number of messages to check")] int numMessages) { await GenerateStats(ctx, null, numMessages); } public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int numMessages) { - UtilityFunctions.LogUserCommand(ctx); - - DateTime start = DateTime.Now; - if (numMessages > 2000) numMessages = 2000; + Utils.LogUserCommand(ctx); + try { + DateTime start = DateTime.Now; + if (numMessages > 2000) numMessages = 2000; + + ulong[] channelIDs; + string[] channelNames; + if (channel == null) { + channelIDs = defChannelIDs; + channelNames = defChannelNames; + } + else { + channelIDs = new ulong[] { channel.Id }; + channelNames = new string[] { channel.Name }; + } - ulong[] channelIDs; - string[] channelNames; - if (channel == null) { - channelIDs = defChannelIDs; - channelNames = defChannelNames; - } - else { - channelIDs = new ulong[]{ channel.Id }; - channelNames = new string[] { channel.Name }; - } + DiscordGuild g = ctx.Guild; + string title = Utils.GetEmojiSnowflakeID(Utils.GetEmoji(EmojiEnum.UnitedProgramming)) + " United Programming Statistics"; + string description = " ---- Fetching data 0/" + channelIDs.Length + " ---- Channel " + channelNames[0] + " ---- "; - DiscordGuild g = ctx.Guild; - string title = UtilityFunctions.GetEmojiSnowflakeID(UtilityFunctions.GetEmoji(EmojiEnum.UnitedProgramming)) + " United Programming Statistics"; - string description = " ---- Fetching data 0/" + channelIDs.Length + " ---- Channel " + channelNames[0] + " ---- "; - - var e = UtilityFunctions.BuildEmbed(title, description, DiscordColor.Black); - int fieldPos = e.Fields.Count - 1; - DiscordMessage m = await UtilityFunctions.LogEmbed(e, ctx, true); + var e = Utils.BuildEmbed(title, description, DiscordColor.Black); + int fieldPos = e.Fields.Count - 1; + DiscordMessage m = await Utils.LogEmbed(e, ctx, true); - int step = 0; - try { + int step = 0; await Task.Delay(120); Dictionary mentioners = new Dictionary(); Dictionary mentioneds = new Dictionary(); @@ -164,25 +162,20 @@ public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int double time = (DateTime.Now - start).TotalMilliseconds / 1000; if (channelIDs.Length == 1) - e.WithFooter("Statistics from channel " + channelNames[0] + " and " + numMessages + " messages per channel.\nGenerated in " + time.ToString("N1") + " seconds"); + e.WithFooter("Statistics from channel " + channelNames[0] + " and " + numMessages + " messages per channel.\nGenerated in " + time.ToString("N1") + " seconds"); else e.WithFooter("Statistics from " + channelIDs.Length + " channels and " + numMessages + " messages per channel.\nGenerated in " + time.ToString("N1") + " seconds"); + await m.ModifyAsync(e.Build()); } catch (Exception ex) { - + await ctx.RespondAsync(Utils.GenerateErrorAnswer("Stats", ex)); } - await m.ModifyAsync(e.Build()); } public class Mentioner { public ulong member; public int num; - } - const ulong idUnityChannel = 830904407540367441ul; - const ulong idCSharpChannel = 830904726375628850ul; - const ulong idHelp1Channel = 830921265648631878ul; - const ulong idHelp2Channel = 830921315657449472ul; } diff --git a/UPBot Code/Commands/Version.cs b/UPBot Code/Commands/Version.cs new file mode 100644 index 0000000..69cc8d1 --- /dev/null +++ b/UPBot Code/Commands/Version.cs @@ -0,0 +1,27 @@ +using DSharpPlus.CommandsNext; +using DSharpPlus.CommandsNext.Attributes; +using DSharpPlus.Entities; +using System; +using System.Threading.Tasks; +/// +/// This command implements a WhoIs command. +/// It gives info about a Discord User or yourself +/// author: CPU +/// +public class Version : BaseCommandModule { + + [Command("Version")] + [Description("Get version information about the bot.")] + public async Task VersionCommand(CommandContext ctx) { + DiscordMember cpu = ctx.Guild.GetMemberAsync(231753250687287296ul).Result; + DiscordMember duck = ctx.Guild.GetMemberAsync(411526180873961494ul).Result; + DiscordMember eremiell = ctx.Guild.GetMemberAsync(340661564556312591ul).Result; + DiscordMember slice = ctx.Guild.GetMemberAsync(486133356858310656ul).Result; + DiscordMember jonathan = ctx.Guild.GetMemberAsync(608994148313333763ul).Result; + + await ctx.Message.RespondAsync(Utils.BuildEmbed("United Programming Bot", + "**Version**: " + Utils.GetVersion() + "\n\nContributors: " + + cpu.Mention + ", " + duck.Mention + ", " + eremiell.Mention + ", " + slice.Mention + ", " + jonathan.Mention + + "\n\nCode available on https://github.com/United-Programming/UPBot/", Utils.Yellow).Build()); + } +} \ No newline at end of file diff --git a/UPBot Code/Commands/WhoIs.cs b/UPBot Code/Commands/WhoIs.cs index 4d14048..4e238e2 100644 --- a/UPBot Code/Commands/WhoIs.cs +++ b/UPBot Code/Commands/WhoIs.cs @@ -17,87 +17,91 @@ public async Task WhoIsCommand(CommandContext ctx) { // Basic version without pa await GenerateWhoIs(ctx, null); } + [Command("whois")] + public async Task WhoIsCommand(CommandContext ctx, [Description("The user to get info from.")] DiscordMember member) { // Standard version with a user + await GenerateWhoIs(ctx, member); + } + [Command("whoami")] [Description("Get information about your own Discord account.")] public async Task WhoAmICommand(CommandContext ctx) { // Alternate version without parameters await GenerateWhoIs(ctx, null); } - [Command("whois")] - public async Task WhoIsCommand(CommandContext ctx, DiscordMember member) { // Standard version with a user - await GenerateWhoIs(ctx, member); - } - private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { - UtilityFunctions.LogUserCommand(ctx); - if (m == null) { // If we do not have a user we use the member that invoked the command - m = ctx.Member; - } - bool you = m == ctx.Member; - - DateTimeOffset jdate = m.JoinedAt.UtcDateTime; - string joined = jdate.Year + "/" + jdate.Month + "/" + jdate.Day; - DateTimeOffset cdate = m.CreationTimestamp.UtcDateTime; - string creation = cdate.Year + "/" + cdate.Month + "/" + cdate.Day; + Utils.LogUserCommand(ctx); + try { + if (m == null) { // If we do not have a user we use the member that invoked the command + m = ctx.Member; + } + bool you = m == ctx.Member; - int daysJ = (int)(DateTime.Now - m.JoinedAt.DateTime).TotalDays; - int daysA = (int)(DateTime.Now - m.CreationTimestamp.DateTime).TotalDays; - double years = daysA / 365.25; + DateTimeOffset jdate = m.JoinedAt.UtcDateTime; + string joined = jdate.Year + "/" + jdate.Month + "/" + jdate.Day; + DateTimeOffset cdate = m.CreationTimestamp.UtcDateTime; + string creation = cdate.Year + "/" + cdate.Month + "/" + cdate.Day; - string title = "Who is the user " + m.DisplayName + "#" + m.Discriminator; - string description = m.Username + " joined on " + joined + " (" + daysJ + " days)\n Account created on " + - creation + " (" + daysA + " days, " + years.ToString("N1") + " years)"; - var embed = UtilityFunctions.BuildEmbed(title, description, m.Color); - embed.WithThumbnail(m.AvatarUrl, 64, 64); + int daysJ = (int)(DateTime.Now - m.JoinedAt.DateTime).TotalDays; + int daysA = (int)(DateTime.Now - m.CreationTimestamp.DateTime).TotalDays; + double years = daysA / 365.25; - embed.AddField("Is you", you ? "✓" : "❌", true); - embed.AddField("Is a bot", m.IsBot ? "🤖" : "❌", true); - embed.AddField("Is the boss", m.IsOwner ? "👑" : "❌", true); - embed.AddField("Is Muted", m.IsMuted ? "✓" : "❌", true); - embed.AddField("Is Deafened", m.IsDeafened ? "✓" : "❌", true); + string title = "Who is the user " + m.DisplayName + "#" + m.Discriminator; + string description = m.Username + " joined on " + joined + " (" + daysJ + " days)\n Account created on " + + creation + " (" + daysA + " days, " + years.ToString("N1") + " years)"; + var embed = Utils.BuildEmbed(title, description, m.Color); + embed.WithThumbnail(m.AvatarUrl, 64, 64); - if (m.Locale != null) embed.AddField("Speaks", m.Locale, true); - if (m.Nickname != null) embed.AddField("Is called", m.Nickname, true); - embed.AddField("Avatar Hex Color", m.Color.ToString(), true); + embed.AddField("Is you", you ? "✓" : "❌", true); + embed.AddField("Is a bot", m.IsBot ? "🤖" : "❌", true); + embed.AddField("Is the boss", m.IsOwner ? "👑" : "❌", true); + embed.AddField("Is Muted", m.IsMuted ? "✓" : "❌", true); + embed.AddField("Is Deafened", m.IsDeafened ? "✓" : "❌", true); - if (m.PremiumSince != null) { - DateTimeOffset bdate = ((DateTimeOffset)m.PremiumSince).UtcDateTime; - string booster = bdate.Year + "/" + bdate.Month + "/" + bdate.Day; - embed.AddField("Booster", "From " + booster, true); - } - if (m.Flags != null) embed.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags + if (m.Locale != null) embed.AddField("Speaks", m.Locale, true); + if (m.Nickname != null) embed.AddField("Is called", m.Nickname, true); + embed.AddField("Avatar Hex Color", m.Color.ToString(), true); - string roles = ""; - int num = 0; - foreach (DiscordRole role in m.Roles) { - roles += role.Mention + " "; - num++; - } - if (num == 1) - embed.AddField("Role", roles, false); - else - embed.AddField(num + " Roles", roles, false); + if (m.PremiumSince != null) { + DateTimeOffset bdate = ((DateTimeOffset)m.PremiumSince).UtcDateTime; + string booster = bdate.Year + "/" + bdate.Month + "/" + bdate.Day; + embed.AddField("Booster", "From " + booster, true); + } + if (m.Flags != null) embed.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags + + string roles = ""; + int num = 0; + foreach (DiscordRole role in m.Roles) { + roles += role.Mention + " "; + num++; + } + if (num == 1) + embed.AddField("Role", roles, false); + else + embed.AddField(num + " Roles", roles, false); - string perms = ""; // Not all permissions are shown - if (m.Permissions.HasFlag(DSharpPlus.Permissions.CreateInstantInvite)) perms += ", Invite"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.KickMembers)) perms += ", Kick"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.BanMembers)) perms += ", Ban"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.Administrator)) perms += ", Admin"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageChannels)) perms += ", Manage Channels"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageGuild)) perms += ", Manage Server"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.AddReactions)) perms += ", Reactions"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ViewAuditLog)) perms += ", Audit"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageMessages)) perms += ", Manage Messages"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.EmbedLinks)) perms += ", Links"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.AttachFiles)) perms += ", Files"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseExternalEmojis)) perms += ", Ext Emojis"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.Speak)) perms += ", Speak"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageRoles)) perms += ", Manage Roles"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageEmojis)) perms += ", Manage Emojis"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseSlashCommands)) perms += ", Use Bot"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.UsePublicThreads)) perms += ", Use Threads"; - if (perms.Length > 0) embed.AddField("Permissions", perms.Substring(2), false); + string perms = ""; // Not all permissions are shown + if (m.Permissions.HasFlag(DSharpPlus.Permissions.CreateInstantInvite)) perms += ", Invite"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.KickMembers)) perms += ", Kick"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.BanMembers)) perms += ", Ban"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.Administrator)) perms += ", Admin"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageChannels)) perms += ", Manage Channels"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageGuild)) perms += ", Manage Server"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.AddReactions)) perms += ", Reactions"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ViewAuditLog)) perms += ", Audit"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageMessages)) perms += ", Manage Messages"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.EmbedLinks)) perms += ", Links"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.AttachFiles)) perms += ", Files"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseExternalEmojis)) perms += ", Ext Emojis"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.Speak)) perms += ", Speak"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageRoles)) perms += ", Manage Roles"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageEmojis)) perms += ", Manage Emojis"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseSlashCommands)) perms += ", Use Bot"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.UsePublicThreads)) perms += ", Use Threads"; + if (perms.Length > 0) embed.AddField("Permissions", perms.Substring(2), false); - return ctx.RespondAsync(embed.Build()); + return ctx.RespondAsync(embed.Build()); + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("WhoIs", ex)); + } } } \ No newline at end of file diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index 2b20291..1544434 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -23,8 +23,8 @@ static async Task MainAsync(string token, string prefix) { }); CustomCommandsService.DiscordClient = discord; - UtilityFunctions.InitClient(discord); - var commands = discord.UseCommandsNext(new CommandsNextConfiguration() { + Utils.InitClient(discord); + CommandsNextExtension commands = discord.UseCommandsNext(new CommandsNextConfiguration() { StringPrefixes = new[] { prefix[0].ToString() } // The backslash will be the default command prefix if not specified in the parameters }); commands.CommandErrored += CustomCommandsService.CommandError; @@ -36,7 +36,7 @@ static async Task MainAsync(string token, string prefix) { await CustomCommandsService.LoadCustomCommands(); await discord.ConnectAsync(); // Connects and wait forever - UtilityFunctions.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd")); + Utils.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd") + " --------------------------------"); AppreciationTracking.Init(); discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; diff --git a/UPBot Code/UtilityFunctions.cs b/UPBot Code/Utils.cs similarity index 81% rename from UPBot Code/UtilityFunctions.cs rename to UPBot Code/Utils.cs index 9727cc8..5145f56 100644 --- a/UPBot Code/UtilityFunctions.cs +++ b/UPBot Code/Utils.cs @@ -2,6 +2,7 @@ using DSharpPlus.CommandsNext; using DSharpPlus.Entities; using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading.Tasks; @@ -10,7 +11,7 @@ /// Utility functions that don't belong to a specific class or a specific command /// "General-purpose" function, which can be needed anywhere. /// -public static class UtilityFunctions +public static class Utils { /// /// Common colors @@ -27,15 +28,23 @@ public static class UtilityFunctions private static DiscordMember mySelf; private static DiscordGuild guild; - public static void SetMyself(DiscordClient c) { - + public static string GetVersion() { + return "0.1 beta - 2021/08/19"; } + /// + /// Returns the Bot user as Member of the United Programming Guild + /// + /// public static DiscordMember GetMyself() { if (mySelf==null) mySelf = client.Guilds[830900174553481236ul].CurrentMember; return mySelf; } + /// + /// Gets the UNitedProgramming Guild + /// + /// public static DiscordGuild GetGuild() { if (guild != null) return guild; while (client == null) Task.Delay(1000); @@ -132,6 +141,21 @@ public static async Task BuildEmbedAndExecute(string title, stri return await LogEmbed(embedBuilder, ctx, respond); } + /// + /// Quick shortcut to generate an error message + /// + /// The error to display + /// + internal static DiscordEmbed GenerateErrorAnswer(string cmd, Exception exception) { + DiscordEmbedBuilder e = new DiscordEmbedBuilder { + Color = Red, + Title = "Error in " + cmd, + Description = exception.Message + }; + Log("Error in " + cmd + ": " + exception.Message); + return e.Build(); + } + /// /// Logs an embed as a message in the relevant channel /// @@ -208,7 +232,7 @@ internal static void LogUserCommand(CommandContext ctx) { /// /// internal static void Log(string msg) { - Console.WriteLine(DateTime.Now.ToString(sortableDateTimeFormat.SortableDateTimePattern) + "=> " + msg); + Console.WriteLine(msg); try { logs.WriteLine(msg); logs.FlushAsync(); @@ -255,8 +279,50 @@ internal static async Task ErrorCallback(CommandErrors error, CommandContext ctx break; } - await UtilityFunctions.BuildEmbedAndExecute("Error", message, red, ctx, respond); + await Utils.BuildEmbedAndExecute("Error", message, red, ctx, respond); + } + + /// + /// Used to delete some messages after a while + /// + /// + public static Task DeleteDelayed(int seconds, DiscordMessage msg1) { + Task.Run(() => DelayAfterAWhile(msg1, seconds * 1000)); + return Task.FromResult(0); + } + + /// + /// Used to delete some messages after a while + /// + /// + /// + public static Task DeleteDelayed(int seconds, DiscordMessage msg1, DiscordMessage msg2) { + Task.Run(() => DelayAfterAWhile(msg1, seconds * 1000)); + Task.Run(() => DelayAfterAWhile(msg2, seconds * 1000)); + return Task.FromResult(0); } + + /// + /// Used to delete some messages after a while + /// + /// + /// + /// + public static Task DeleteDelayed(int seconds, DiscordMessage msg1, DiscordMessage msg2, DiscordMessage msg3) { + Task.Run(() => DelayAfterAWhile(msg1, seconds * 1000)); + Task.Run(() => DelayAfterAWhile(msg2, seconds * 1000)); + Task.Run(() => DelayAfterAWhile(msg3, seconds * 1000)); + return Task.FromResult(0); + } + + static void DelayAfterAWhile(DiscordMessage msg, int delay) { + try { + Task.Delay(delay).Wait(); + msg.DeleteAsync().Wait(); + } catch (Exception) { } + } + + } public enum EmojiEnum { From 1bbd0bc089f50c924c2431c42de65148a1e21715 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:17:57 +0200 Subject: [PATCH 30/54] Optimized and reworked the 'HelpLanguages.cs' file - Optimized and reworked the 'helplanguage' command with a few simplifications and optimizations regarding readability, accessibility and performance (for example, not 3 Dictionaries anymore, but one Dictionary with a ContainerClass 'LanguageInfo' for all the information about the language (course link, video link, color) Everything has been tested and it seems to work so far --- UPBot Code/Commands/HelpLanguages.cs | 211 ++++++++++----------------- 1 file changed, 81 insertions(+), 130 deletions(-) diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 0c84bb9..af8c779 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -12,113 +12,103 @@ public class HelpLanguagesModel : BaseCommandModule { private List lastRequests = new List(); - private Dictionary languageLinks = new Dictionary() // video course + private Dictionary languages = new Dictionary() { - { "C#", "<:csharp:831465428214743060>!\nLink: https://youtu.be/GhQdlIFylQ8" }, - { "C++", "<:cpp:831465408874676273>!\nLink: https://youtu.be/vLnPwxZdW4Y" }, - { "Python", "<:python:831465381016895500>!\nLink: https://youtu.be/rfscVS0vtbw" }, - { "JavaScript", "<:Javascript:876103767068647435>!\nLink: https://youtu.be/PkZNo7MFNFg" }, - { "Java", "<:java:875852276017815634>!\nLink: https://youtu.be/grEKMHGYyns" } - }; - private Dictionary languageCourseLink = new Dictionary() // site course - { - { "C#", "<:csharp:831465428214743060>!\nLink: https://www.w3schools.com/cs/"}, - { "C++", "<:cpp:831465408874676273>!\nLink: https://www.w3schools.com/cpp/"}, - { "Python", "<:python:831465381016895500>!\nLink: https://www.w3schools.com/python/"}, - { "JavaScript", "<:Javascript:876103767068647435>!\nLink: https://www.w3schools.com/js/" }, - { "Java", "<:java:875852276017815634>!\nLink: https://www.w3schools.com/java/" } - }; + { "C#", new LanguageInfo("<:csharp:831465428214743060>!\nLink: https://youtu.be/GhQdlIFylQ8", + "<:csharp:831465428214743060>!\nLink: https://www.w3schools.com/cs/", "#812f84") + }, + { "C++", new LanguageInfo("<:cpp:831465408874676273>!\nLink: https://youtu.be/vLnPwxZdW4Y", + "<:cpp:831465408874676273>!\nLink: https://www.w3schools.com/cpp/", "#3f72db") + }, - private Dictionary colors = new Dictionary() // colors for embed - { - { "C#", "812f84" }, - { "C++", "3f72db" }, - { "Python", "d1e13b" }, - { "JavaScript", "f8ff00"}, - { "Java", "e92c2c" } - }; + { "Python", new LanguageInfo("<:python:831465381016895500>!\nLink: https://youtu.be/rfscVS0vtbw", + "<:python:831465381016895500>!\nLink: https://www.w3schools.com/python/", "#d1e13b") + }, + + { "JavaScript", new LanguageInfo("<:Javascript:876103767068647435>!\nLink: https://youtu.be/PkZNo7MFNFg", + "<:Javascript:876103767068647435>!\nLink: https://www.w3schools.com/js/", "#f8ff00") + }, + + { "Java", new LanguageInfo("<:java:875852276017815634>!\nLink: https://youtu.be/grEKMHGYyns", + "<:java:875852276017815634>!\nLink: https://www.w3schools.com/java/", "#e92c2c") + } + }; private string[] helpfulAnswers = // words for video course { "Hello! @@@, here is a good video tutorial about ///", "Hey! hey! @@@, here is a sick video tutorial about ///", "Hello @@@, here is your video tutorial ///" - }; - private string[] helpFulCourseAnswers = { // words for site course - "Hello! @@@, here is a good course about ///", - "Hey! hey! @@@, here is a sick course about ///", - "Hello @@@, here is your course ///" - }; - - [Description("Gives good tutorials on specific language.")] // for \help helplanguage info + }; + [Command("helplanguage")] public async Task ErrorMessage(CommandContext ctx) { - DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); - deb.Title = "Help Language - How To Use"; - deb.WithColor(new DiscordColor("ff0025")); + string title = "Help Language - How To Use"; DiscordMember member = ctx.Member; - deb.Description = member.Mention + " , if you want to get video course about specific language type: `\\helplanguage video C#`" + + string description = member.Mention + " , if you want to get video course about specific language type: `\\helplanguage video C#`" + " \nIf you want to get full online course about specific language type: \n`\\helplanguage course C#`" + " \nAvailable languages: `ะก#, C++, Python, JavaScript, Java`"; - await ctx.RespondAsync(deb.Build()); // \helplanguage + await UtilityFunctions.BuildEmbedAndExecute(title, description, UtilityFunctions.Red, ctx, true); } - [Description("For use this command you should type `\\helplanguage C#`")] [Command("helplanguage")] - public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string TypeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# + [Description("Gives good tutorials on specific language.\n**Usage:** `\\helplanguage language`")] + public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string typeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { UtilityFunctions.LogUserCommand(ctx); lang = NormalizeLanguage(lang); if (lang == null) + { await ErrorMessage(ctx); - else if (TypeOfHelp.ToLowerInvariant() == "video") { - if (!languageLinks.ContainsKey(lang)) { - await ErrorMessage(ctx); - } - else - await GenerateHelpfulAnswer(ctx, lang); + return; } - else if (TypeOfHelp.ToLowerInvariant() == "course") { - if (!languageCourseLink.ContainsKey(lang)) { - await ErrorMessage(ctx); - } - else - await GenerateHelpfulAnswerCourse(ctx, lang); + + if (!languages.ContainsKey(lang)) { + await ErrorMessage(ctx); + return; } + + bool course = typeOfHelp.ToLowerInvariant() != "video"; + await GenerateHelpfulAnswer(ctx, lang, course); } private string NormalizeLanguage(string language) { if (language == null) return null; language = language.ToLowerInvariant(); switch (language) { - case "c++": return "C++"; - case "cpp": return "C++"; - case "cp": return "C++"; - case "cs": return "C#"; - case "csharp": return "C#"; - case "c#": return "C#"; - case "java": return "Java"; - case "javascript": return "JavaScript"; - case "jscript": return "JavaScript"; - case "js": return "JavaScript"; - case "python": return "Python"; - case "phyton": return "Python"; - case "py": return "Python"; + case "c++": + case "cpp": + case "cp": + return "C++"; + case "cs": + case "csharp": + case "c#": + return "C#"; + case "java": + return "Java"; + case "javascript": + case "jscript": + case "js": + return "JavaScript"; + case "python": + case "phyton": + case "py": + return "Python"; + default: + return char.ToUpperInvariant(language[0]) + language.Substring(1); } - return language; + + //return language; } - - - private Task GenerateHelpfulAnswer(CommandContext ctx, string language) { + + private async Task GenerateHelpfulAnswer(CommandContext ctx, string language, bool isCourse) { DiscordMember member = ctx.Member; ulong memberId = member.Id; - DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); - deb.Title = $"Help Language - {language}"; - deb.WithColor(new DiscordColor(colors[language])); + string title = $"Help Language - {language}"; // Find the last request LastRequestByMember lastRequest = null; @@ -140,73 +130,34 @@ private Task GenerateHelpfulAnswer(CommandContext ctx, string language) { if (lastRequest.Num >= helpfulAnswers.Length) lastRequest.Num = 0; } + + string link = isCourse ? languages[language].CourseLink :languages[language].VideoLink; string msg = helpfulAnswers[lastRequest.Num]; msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) - .Replace("///", languageLinks[language]); - deb.Description = msg; - return ctx.RespondAsync(deb.Build()); - } - private Task GenerateHelpfulAnswerCourse(CommandContext ctx, string rawLang) { - - DiscordMember member = ctx.Member; - ulong memberId = member.Id; - DiscordEmbedBuilder deb = new DiscordEmbedBuilder(); - - string language = ""; - if (rawLang == "cpp" || rawLang == "CPP" || rawLang == "c++" || rawLang == "C++") // all possible words to type (including the same words as command one) - { - language = rawLang.ToUpperInvariant(); - language = rawLang = "C++"; - } - else if (rawLang == "c#" || rawLang == "C#" || rawLang == "csharp" || rawLang == "CSharp") { - language = rawLang.ToUpperInvariant(); - language = rawLang = "C#"; - } - else if (rawLang == "python" || rawLang == "Python" || rawLang == "py" || rawLang == "PY") { - language = rawLang = "Python"; - } - else if (rawLang == "js" || rawLang == "JS" || rawLang == "javascript" || rawLang == "JavaScript") { - language = rawLang = "JavaScript"; - } - else if (rawLang == "java") { - language = rawLang = "Java"; - } - else - language = char.ToUpperInvariant(rawLang[0]) + rawLang.Substring(1); - - deb.Title = $"Help Language - {language}"; - deb.WithColor(new DiscordColor(colors[rawLang])); + .Replace("///", link); + if (isCourse) + msg = msg.Replace("video", "course"); - // Find the last request - LastRequestByMember lastRequest = null; - foreach (LastRequestByMember lr in lastRequests) { - if (lr.MemberId == memberId) { - lastRequest = lr; - break; - } - } - - if (lastRequest == null) // No last request, create one - { - lastRequest = new LastRequestByMember { MemberId = memberId, DateTime = DateTime.Now, Num = 0 }; - lastRequests.Add(lastRequest); - } - else { - lastRequest.DateTime = DateTime.Now; - lastRequest.Num++; - if (lastRequest.Num >= helpFulCourseAnswers.Length) - lastRequest.Num = 0; - } - string msg = helpFulCourseAnswers[lastRequest.Num]; - msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) - .Replace("///", languageCourseLink[rawLang]); - deb.Description = msg; - return ctx.RespondAsync(deb.Build()); + await UtilityFunctions.BuildEmbedAndExecute(title, msg, languages[language].Color, ctx, true); } - public class LastRequestByMember { + private class LastRequestByMember { public ulong MemberId { get; set; } public DateTime DateTime { get; set; } public int Num { get; set; } } + + private class LanguageInfo + { + public LanguageInfo(string videoLink, string courseLink, string colorHex) + { + this.VideoLink = videoLink; + this.CourseLink = courseLink; + this.Color = new DiscordColor(colorHex); + } + + internal readonly string VideoLink; + internal readonly string CourseLink; + internal readonly DiscordColor Color; + } } From 9e3bc08aa1cada1abe4fe1d2b61cd52899f5fe5b Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:26:51 +0200 Subject: [PATCH 31/54] Removed unused `using` declarations in 'Game.cs' - Removed unused `using` declarations in 'Game.cs' --- UPBot Code/Commands/Game.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/UPBot Code/Commands/Game.cs b/UPBot Code/Commands/Game.cs index cf9aee6..a65db33 100644 --- a/UPBot Code/Commands/Game.cs +++ b/UPBot Code/Commands/Game.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using DSharpPlus.CommandsNext; using DSharpPlus.CommandsNext.Attributes; -using DSharpPlus.Entities; /// /// This command implements simple games like: From d2b203ea944fe9b6838421330260293a16215f2d Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:36:25 +0200 Subject: [PATCH 32/54] Let 'stats' command use Utility Functions to build embed - Let 'stats' command use Utility Functions to build embed ('Stats.cs') --- UPBot Code/Commands/Stats.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UPBot Code/Commands/Stats.cs b/UPBot Code/Commands/Stats.cs index 6a5461b..ecb8472 100644 --- a/UPBot Code/Commands/Stats.cs +++ b/UPBot Code/Commands/Stats.cs @@ -72,12 +72,12 @@ public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int } DiscordGuild g = ctx.Guild; - DiscordEmbedBuilder e = new DiscordEmbedBuilder(); - e.Title = UtilityFunctions.GetEmojiSnowflakeID(UtilityFunctions.GetEmoji(EmojiEnum.UnitedProgramming)) + " United Programming Statistics"; - e.Description = " ---- Fetching data 0/" + channelIDs.Length + " ---- Channel " + channelNames[0] + " ---- "; - + string title = UtilityFunctions.GetEmojiSnowflakeID(UtilityFunctions.GetEmoji(EmojiEnum.UnitedProgramming)) + " United Programming Statistics"; + string description = " ---- Fetching data 0/" + channelIDs.Length + " ---- Channel " + channelNames[0] + " ---- "; + + var e = UtilityFunctions.BuildEmbed(title, description, DiscordColor.Black); int fieldPos = e.Fields.Count - 1; - DiscordMessage m = await ctx.Message.RespondAsync(e.Build()); + DiscordMessage m = await UtilityFunctions.LogEmbed(e, ctx, true); int step = 0; From 83526e8547ac116fb7ec833fe6a009c7a81c1b80 Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 18:01:11 +0200 Subject: [PATCH 33/54] 'stats' overwrites "Fetching" message - Once all stats are loaded, the "Fetching" message, which is displayed while loading the stats, will be overwritten with the final stats (instead of sending a new message and keeping the "Fetching" one) --- UPBot Code/Commands/Stats.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UPBot Code/Commands/Stats.cs b/UPBot Code/Commands/Stats.cs index ecb8472..8fc41d0 100644 --- a/UPBot Code/Commands/Stats.cs +++ b/UPBot Code/Commands/Stats.cs @@ -169,9 +169,9 @@ public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int e.WithFooter("Statistics from " + channelIDs.Length + " channels and " + numMessages + " messages per channel.\nGenerated in " + time.ToString("N1") + " seconds"); } catch (Exception ex) { - string err = ex.Message; + } - await ctx.Message.RespondAsync(e.Build()); + await m.ModifyAsync(e.Build()); } public class Mentioner { From baa58fe55fad92c0e5d96ad94694b4e9caba5dff Mon Sep 17 00:00:00 2001 From: Duck <61479436+NiceDuck@users.noreply.github.com> Date: Tue, 17 Aug 2021 18:24:46 +0200 Subject: [PATCH 34/54] You can't input duplicate aliases in CC commands - You can't input duplicate aliases in CC commands (affects 'ccnew' and 'ccedit') --- UPBot Code/Commands/CustomCommandsService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 31fe3de..9454abe 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -29,6 +29,7 @@ public class CustomCommandsService : BaseCommandModule [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of all aliases. The first term is the **main name**, the other ones, separated by a space, are aliases")] params string[] names) { + names = names.Distinct().ToArray(); foreach (var name in names) { if (DiscordClient.GetCommandsNext().RegisteredCommands.ContainsKey(name)) // Check if there is a command with one of the names already @@ -120,6 +121,7 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the "of the CC whose name you want to edit, the **SECOND** term " + "is the new **main name** and all the other terms are new aliases")] params string[] names) { + names = names.Distinct().ToArray(); if (names.Length < 2) { await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParams, ctx); From 7a9995134b1643274f81ba03d537c2e0dd133d16 Mon Sep 17 00:00:00 2001 From: CPULL Date: Thu, 19 Aug 2021 12:28:48 +0200 Subject: [PATCH 35/54] Completed bot finalization --- ToDo.txt | 30 + UPBot Code/Actions/AppreciationTracking.cs | 547 ++++++++++--------- UPBot Code/Actions/MembersTracking.cs | 68 ++- UPBot Code/Commands/BannedWords.cs | 180 +++--- UPBot Code/Commands/CustomCommand.cs | 2 +- UPBot Code/Commands/CustomCommandsService.cs | 45 +- UPBot Code/Commands/DebugCommand.cs | 26 - UPBot Code/Commands/Delete.cs | 155 +++--- UPBot Code/Commands/Game.cs | 8 +- UPBot Code/Commands/HelpLanguages.cs | 25 +- UPBot Code/Commands/Ping.cs | 74 +-- UPBot Code/Commands/Refactor.cs | 162 +++--- UPBot Code/Commands/Stats.cs | 79 ++- UPBot Code/Commands/Version.cs | 27 + UPBot Code/Commands/WhoIs.cs | 138 ++--- UPBot Code/Program.cs | 6 +- UPBot Code/{UtilityFunctions.cs => Utils.cs} | 76 ++- 17 files changed, 920 insertions(+), 728 deletions(-) create mode 100644 ToDo.txt delete mode 100644 UPBot Code/Commands/DebugCommand.cs create mode 100644 UPBot Code/Commands/Version.cs rename UPBot Code/{UtilityFunctions.cs => Utils.cs} (81%) diff --git a/ToDo.txt b/ToDo.txt new file mode 100644 index 0000000..176e056 --- /dev/null +++ b/ToDo.txt @@ -0,0 +1,30 @@ +Have command to alter the command character [NOT POSSIBLE!] +Check all command names +Add wikis for each command on GitHub +Uniform all error messages with Embeds (use Duck code) +Have all messages and commands to disappear after a while (configurable) if not needed to stay +Have a cmd to configure how long messages should stay +Add TryCatch to all code and commands + + + Errors DelAnsw EmbedRes TryCatch Log@Begin +MemberTracking | | | | X | | +Appreciation | | | | X | X | +EmojiForRole | | | | X | X | +BannedWords | | X | | X | X | +newcc | | | | | X | +ccdel | | | | | X | +ccedit | | | | | X | +cceditname | | | | | X | +cclist | | | | | X | +delete | X | X | | X | X | +game | | | | | X | +bool | | | | | X | +rps | | | | | X | +helplanguage | | | X | X | X | +ping | | X | | X | X | +checklanguage | | | | X | X | +reformat | | | | X | X | +stats | | | X | X | X | +whois | | | X | X | X | + diff --git a/UPBot Code/Actions/AppreciationTracking.cs b/UPBot Code/Actions/AppreciationTracking.cs index 37b68e5..8401a28 100644 --- a/UPBot Code/Actions/AppreciationTracking.cs +++ b/UPBot Code/Actions/AppreciationTracking.cs @@ -12,71 +12,81 @@ public class AppreciationTracking : BaseCommandModule { static ReputationTracking tracking = null; static EmojisForRole emojisForRole = null; static bool savingRequested = false; - + public static void Init() { - tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); - emojisForRole = new EmojisForRole(UtilityFunctions.ConstructPath("Tracking", "EmojisForRole", ".dat")); + tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); + emojisForRole = new EmojisForRole(Utils.ConstructPath("Tracking", "EmojisForRole", ".dat")); } [Command("Appreciation")] [Description("It shows the statistics for users")] public async Task ShowAppreciationCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); - if (tracking == null) { - tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); - if (tracking == null) return; - } - DiscordEmbedBuilder e = UtilityFunctions.BuildEmbed("Appreciation", "These are the most appreciated people of this server (tracking started at " + tracking.GetStartDate() + ")", DiscordColor.Azure); - - List vals = new List(tracking.GetReputation()); - Dictionary users = new Dictionary(); - e.AddField("Reputation ----------------", "For receving these emojis in the posts: <:OK:830907665869570088>👍❤️🥰😍🤩😘💯<:whatthisguysaid:840702597216337990>", false); - vals.Sort((a, b) => { return b.reputation.CompareTo(a.reputation); }); - for (int i = 0; i < 6; i++) { - if (i >= vals.Count) break; - Reputation r = vals[i]; - if (r.reputation == 0) break; - if (!users.ContainsKey(r.user)) { - users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + Utils.LogUserCommand(ctx); + try { + if (tracking == null) { + tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); + if (tracking == null) return; + } + DiscordEmbedBuilder e = Utils.BuildEmbed("Appreciation", "These are the most appreciated people of this server (tracking started at " + tracking.GetStartDate() + ")", DiscordColor.Azure); + + List vals = new List(tracking.GetReputation()); + Dictionary users = new Dictionary(); + e.AddField("Reputation ----------------", "For receving these emojis in the posts: <:OK:830907665869570088>👍❤️🥰😍🤩😘💯<:whatthisguysaid:840702597216337990>", false); + vals.Sort((a, b) => { return b.reputation.CompareTo(a.reputation); }); + for (int i = 0; i < 6; i++) { + if (i >= vals.Count) break; + Reputation r = vals[i]; + if (r.reputation == 0) break; + if (!users.ContainsKey(r.user)) { + users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + } + e.AddField(users[r.user], "Reputation: _" + r.reputation + "_", true); } - e.AddField(users[r.user], "Reputation: _" + r.reputation + "_", true); - } - e.AddField("Fun -----------------------", "For receving these emojis in the posts: 😀😃😄😁😆😅🤣😂🙂🙃😉😊😇<:StrongSmile:830907626928996454>", false); - vals.Sort((a, b) => { return b.fun.CompareTo(a.fun); }); - for (int i = 0; i < 6; i++) { - if (i >= vals.Count) break; - Reputation r = vals[i]; - if (r.fun == 0) break; - if (!users.ContainsKey(r.user)) { - users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + e.AddField("Fun -----------------------", "For receving these emojis in the posts: 😀😃😄😁😆😅🤣😂🙂🙃😉😊😇<:StrongSmile:830907626928996454>", false); + vals.Sort((a, b) => { return b.fun.CompareTo(a.fun); }); + for (int i = 0; i < 6; i++) { + if (i >= vals.Count) break; + Reputation r = vals[i]; + if (r.fun == 0) break; + if (!users.ContainsKey(r.user)) { + users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + } + e.AddField(users[r.user], "Fun: _" + r.fun + "_", (i < vals.Count - 1 && i < 5)); } - e.AddField(users[r.user], "Fun: _" + r.fun + "_", (i < vals.Count - 1 && i < 5)); - } - await ctx.Message.RespondAsync(e.Build()); + await ctx.Message.RespondAsync(e.Build()); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("Appreciation", ex)); + } } [Command("EmojiForRole")] + [Aliases("RoleForEmoji")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only [Description("Reply to a message what should add and remove a role when an emoji (any or specific) is added")] public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role, [Description("The emoji to watch")] DiscordEmoji emoji) { - if (ctx.Message.ReferencedMessage == null) { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched!", UtilityFunctions.Red, ctx, true); - } - else { - string msg = ctx.Message.ReferencedMessage.Content; - if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; - if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, emoji)) { - msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing the emoji: " + emoji.GetDiscordName(); + Utils.LogUserCommand(ctx); + try { + if (ctx.Message.ReferencedMessage == null) { + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched!", Utils.Red, ctx, true); } else { - msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding the emoji: " + emoji.GetDiscordName(); + string msg = ctx.Message.ReferencedMessage.Content; + if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; + if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, emoji)) { + msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing the emoji: " + emoji.GetDiscordName(); + } + else { + msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding the emoji: " + emoji.GetDiscordName(); + } + Utils.Log(msg); + await ctx.RespondAsync(msg); } - UtilityFunctions.Log(msg); - await ctx.RespondAsync(msg); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("EmojiForRole", ex)); } } @@ -84,37 +94,44 @@ public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role [Command("EmojiForRole")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EmojiForRoleCommand(CommandContext ctx) { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nyou have to specify the Role to add/remove, and the emoji to watch.", UtilityFunctions.Red, ctx, true); + Utils.LogUserCommand(ctx); + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nyou have to specify the Role to add/remove, and the emoji to watch.", Utils.Red, ctx, true); } [Command("EmojiForRole")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role) { - if (ctx.Message.ReferencedMessage == null) { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\n and you have to specify the emoji to watch.", UtilityFunctions.Red, ctx, true); - } - else { - string msg = ctx.Message.ReferencedMessage.Content; - if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; - if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, null)) { - msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing any emoji"; + Utils.LogUserCommand(ctx); + try { + if (ctx.Message.ReferencedMessage == null) { + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\n and you have to specify the emoji to watch.", Utils.Red, ctx, true); } else { - msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding an emoji"; + string msg = ctx.Message.ReferencedMessage.Content; + if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; + if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, null)) { + msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing any emoji"; + } + else { + msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding an emoji"; + } + Utils.Log(msg); + await ctx.RespondAsync(msg); } - UtilityFunctions.Log(msg); - await ctx.RespondAsync(msg); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("EmojiForRole", ex)); } } [Command("EmojiForRole")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The emoji to watch")] DiscordEmoji emoji) { + Utils.LogUserCommand(ctx); if (ctx.Message.ReferencedMessage == null) { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nand y ou have to specify the Role to add/remove.", UtilityFunctions.Red, ctx, true); + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nand y ou have to specify the Role to add/remove.", Utils.Red, ctx, true); } else { - await UtilityFunctions.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to specify the Role to add/remove.", UtilityFunctions.Red, ctx, true); + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to specify the Role to add/remove.", Utils.Red, ctx, true); } } @@ -122,61 +139,72 @@ public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The emoj internal static Task ReacionAdded(DiscordClient sender, MessageReactionAddEventArgs a) { - ulong emojiId = a.Emoji.Id; - string emojiName = a.Emoji.Name; - emojisForRole.HandleAddingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); - - DiscordUser author = a.Message.Author; - if (author == null) { - ulong msgId = a.Message.Id; - ulong chId = a.Message.ChannelId; - DiscordChannel c = a.Guild.GetChannel(chId); - DiscordMessage m = c.GetMessageAsync(msgId).Result; - author = m.Author; + try { + ulong emojiId = a.Emoji.Id; + string emojiName = a.Emoji.Name; + emojisForRole.HandleAddingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); + + DiscordUser author = a.Message.Author; + if (author == null) { + ulong msgId = a.Message.Id; + ulong chId = a.Message.ChannelId; + DiscordChannel c = a.Guild.GetChannel(chId); + DiscordMessage m = c.GetMessageAsync(msgId).Result; + author = m.Author; + } + ulong authorId = author.Id; + if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) + return HandleReactions(emojiId, emojiName, authorId, true); + } catch (Exception ex) { + Utils.Log("Error in ReacionAdded: " + ex.Message); + return Task.FromResult(0); } - ulong authorId = author.Id; - if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) - return HandleReactions(emojiId, emojiName, authorId, true); } + internal static Task ReactionRemoved(DiscordClient sender, MessageReactionRemoveEventArgs a) { - ulong emojiId = a.Emoji.Id; - string emojiName = a.Emoji.Name; - emojisForRole.HandleRemovingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); - - DiscordUser author = a.Message.Author; - if (author == null) { - ulong msgId = a.Message.Id; - ulong chId = a.Message.ChannelId; - DiscordChannel c = a.Guild.GetChannel(chId); - DiscordMessage m = c.GetMessageAsync(msgId).Result; - author = m.Author; + try { + ulong emojiId = a.Emoji.Id; + string emojiName = a.Emoji.Name; + emojisForRole.HandleRemovingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); + + DiscordUser author = a.Message.Author; + if (author == null) { + ulong msgId = a.Message.Id; + ulong chId = a.Message.ChannelId; + DiscordChannel c = a.Guild.GetChannel(chId); + DiscordMessage m = c.GetMessageAsync(msgId).Result; + author = m.Author; + } + ulong authorId = author.Id; + if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) + return HandleReactions(emojiId, emojiName, authorId, false); + } catch (Exception ex) { + Utils.Log("Error in ReacionAdded: " + ex.Message); + return Task.FromResult(0); } - ulong authorId = author.Id; - if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) - return HandleReactions(emojiId, emojiName, authorId, false); } static Task HandleReactions(ulong emojiId, string emojiName, ulong authorId, bool added) { bool save = false; // check if emoji is :smile: :rolf: :strongsmil: (find valid emojis -> increase fun level of user - if (emojiName == "😀" || - emojiName == "😃" || - emojiName == "😄" || - emojiName == "😁" || - emojiName == "😆" || - emojiName == "😅" || - emojiName == "🤣" || - emojiName == "😂" || - emojiName == "🙂" || - emojiName == "🙃" || - emojiName == "😉" || - emojiName == "😊" || - emojiName == "😇" || + if (emojiName == "😀" || + emojiName == "😃" || + emojiName == "😄" || + emojiName == "😁" || + emojiName == "😆" || + emojiName == "😅" || + emojiName == "🤣" || + emojiName == "😂" || + emojiName == "🙂" || + emojiName == "🙃" || + emojiName == "😉" || + emojiName == "😊" || + emojiName == "😇" || emojiId == 830907626928996454ul || // :StrongSmile: false) { // just to keep the other lines aligned if (tracking == null) { - tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); + tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); if (tracking == null) return Task.Delay(10); } save = tracking.AlterFun(authorId, added); @@ -194,7 +222,7 @@ static Task HandleReactions(ulong emojiId, string emojiName, ulong authorId, boo emojiId == 552147917876625419ul || // :thoose: false) { // just to keep the other lines aligned if (tracking == null) { - tracking = new ReputationTracking(UtilityFunctions.ConstructPath("Tracking", "Tracking", ".Tracking")); + tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); if (tracking == null) return Task.Delay(10); } save = tracking.AlterRep(authorId, added); @@ -216,7 +244,7 @@ static async Task SaveDelayedAsync() { try { tracking.Save(); } catch (Exception e) { - UtilityFunctions.Log("ERROR: problems in saving the Reputation file: " + e.Message); + Utils.Log("ERROR: problems in saving the Reputation file: " + e.Message); } savingRequested = false; } @@ -230,71 +258,79 @@ public class Reputation { // 16 bytes } public class ReputationTracking { - DateTime trackingStarted; - Dictionary dic; - string path = null; + readonly DateTime trackingStarted; + readonly Dictionary dic; + readonly string path = null; public ReputationTracking(string path) { - this.path = path; - dic = new Dictionary(); - if (!File.Exists(path)) { - trackingStarted = DateTime.Now; - return; - } - byte[] data = new byte[20]; - using (FileStream f = new FileStream(path, FileMode.Open)) { - // 32 bits for the date (ymd) - if (f.Read(data, 0, 4) < 4) { - UtilityFunctions.Log("ERROR: wrong Reputation file: " + path); - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - UtilityFunctions.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); - } + try { + this.path = path; + dic = new Dictionary(); + if (!File.Exists(path)) { + trackingStarted = DateTime.Now; return; } - trackingStarted = GetDateFromBytes(data, 0); - while (f.Read(data, 0, 16) == 16) { - ulong usrid = BitConverter.ToUInt64(data); - ushort rep = BitConverter.ToUInt16(data, 8); - ushort fun = BitConverter.ToUInt16(data, 10); - DateTime start = GetDateFromBytes(data, 12); - dic[usrid] = new Reputation { user = usrid, reputation = rep, fun = fun, startTracking = start }; + byte[] data = new byte[20]; + using (FileStream f = new FileStream(path, FileMode.Open)) { + // 32 bits for the date (ymd) + if (f.Read(data, 0, 4) < 4) { + Utils.Log("ERROR: wrong Reputation file: " + path); + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); + } + return; + } + trackingStarted = GetDateFromBytes(data, 0); + while (f.Read(data, 0, 16) == 16) { + ulong usrid = BitConverter.ToUInt64(data); + ushort rep = BitConverter.ToUInt16(data, 8); + ushort fun = BitConverter.ToUInt16(data, 10); + DateTime start = GetDateFromBytes(data, 12); + dic[usrid] = new Reputation { user = usrid, reputation = rep, fun = fun, startTracking = start }; + } } + Utils.Log("ReputationTracking: Loaded " + dic.Count + " users"); + } catch (Exception e) { + Utils.Log("ERROR: problems in loading the Reputation file: " + e.Message); } - UtilityFunctions.Log("ReputationTracking: Loaded " + dic.Count + " users"); } public void Save() { - lock (dic) { - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - UtilityFunctions.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); - return; - } - using (FileStream f = new FileStream(path, FileMode.CreateNew)) { - byte[] data = new byte[16]; - SetDateToBytes(trackingStarted, data, 0); - f.Write(data, 0, 4); - foreach (Reputation r in dic.Values) { - byte[] d = BitConverter.GetBytes(r.user); - int pos = 0; - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - d = BitConverter.GetBytes(r.reputation); - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - d = BitConverter.GetBytes(r.fun); - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - SetDateToBytes(r.startTracking, data, pos); - f.Write(data, 0, 16); + try { + lock (dic) { + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); + return; + } + using (FileStream f = new FileStream(path, FileMode.CreateNew)) { + byte[] data = new byte[16]; + SetDateToBytes(trackingStarted, data, 0); + f.Write(data, 0, 4); + foreach (Reputation r in dic.Values) { + byte[] d = BitConverter.GetBytes(r.user); + int pos = 0; + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + d = BitConverter.GetBytes(r.reputation); + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + d = BitConverter.GetBytes(r.fun); + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + SetDateToBytes(r.startTracking, data, pos); + f.Write(data, 0, 16); + } + f.Flush(); } - f.Flush(); } + Utils.Log("ReputationTracking: Saved " + dic.Count + " users"); + } catch (Exception e) { + Utils.Log("ERROR: problems in saving the Reputation file: " + e.Message); } - UtilityFunctions.Log("ReputationTracking: Saved " + dic.Count + " users"); } private void SetDateToBytes(DateTime d, byte[] data, int offset) { @@ -305,7 +341,11 @@ private void SetDateToBytes(DateTime d, byte[] data, int offset) { } private DateTime GetDateFromBytes(byte[] data, int offset) { - return new DateTime((data[offset + 0] << 8) + data[offset + 1], data[offset + 2], data[offset + 3]); + try { + return new DateTime((data[offset + 0] << 8) + data[offset + 1], data[offset + 2], data[offset + 3]); + } catch (Exception) { + return DateTime.Now; + } } public bool AlterRep(ulong id, bool add) { @@ -355,87 +395,96 @@ public class EmojisForRole { string path = null; public EmojisForRole(string path) { - values = new List(); - this.path = path; - if (!File.Exists(path)) return; - - DiscordGuild g = UtilityFunctions.GetGuild(); - byte[] data = new byte[36]; - using (FileStream f = new FileStream(path, FileMode.Open)) { - while (f.Read(data, 0, 36) == 36) { - ReactionValue v = new ReactionValue { - channel = BitConverter.ToUInt64(data, 0), - message = BitConverter.ToUInt64(data, 8), - role = BitConverter.ToUInt64(data, 16), - emojiId = BitConverter.ToUInt64(data, 24), - emojiName = Char.ConvertFromUtf32(BitConverter.ToInt32(data, 32)), - dRole = null - }; - - DiscordMessage m; - try { - // Does the message exists? - DiscordChannel c = g.GetChannel(v.channel); - if (c == null || c.Id == 0) continue; // Bad - m = c.GetMessageAsync(v.message).Result; - if (m == null || m.Id == 0) continue; // Bad; - // Does the role exists? - DiscordRole r = g.GetRole(v.role); - if (r == null || r.Id == 0) continue; // Bad - } catch (Exception ex) { - UtilityFunctions.Log("Error while checking a ReactionValue: " + ex.Message); - continue; - } - // Check that the message has the required emojis, if not add it - DiscordEmoji e = null; - if (v.emojiId != 0) { - e = g.GetEmojiAsync(v.emojiId).Result; - } else if (v.emojiName != "!") { - e = DiscordEmoji.FromUnicode(v.emojiName); - } - if (e == null) continue; // Bad - if (m.GetReactionsAsync(e, 1).Result.Count == 0) { // Need to add - m.CreateReactionAsync(e).Wait(); - } + try { + values = new List(); + this.path = path; + if (!File.Exists(path)) return; + + DiscordGuild g = Utils.GetGuild(); + byte[] data = new byte[36]; + using (FileStream f = new FileStream(path, FileMode.Open)) { + while (f.Read(data, 0, 36) == 36) { + ReactionValue v = new ReactionValue { + channel = BitConverter.ToUInt64(data, 0), + message = BitConverter.ToUInt64(data, 8), + role = BitConverter.ToUInt64(data, 16), + emojiId = BitConverter.ToUInt64(data, 24), + emojiName = Char.ConvertFromUtf32(BitConverter.ToInt32(data, 32)), + dRole = null + }; + + DiscordMessage m; + try { + // Does the message exists? + DiscordChannel c = g.GetChannel(v.channel); + if (c == null || c.Id == 0) continue; // Bad + m = c.GetMessageAsync(v.message).Result; + if (m == null || m.Id == 0) continue; // Bad; + // Does the role exists? + DiscordRole r = g.GetRole(v.role); + if (r == null || r.Id == 0) continue; // Bad + } catch (Exception ex) { + Utils.Log("Error while checking a ReactionValue: " + ex.Message); + continue; + } + // Check that the message has the required emojis, if not add it + DiscordEmoji e = null; + if (v.emojiId != 0) { + e = g.GetEmojiAsync(v.emojiId).Result; + } + else if (v.emojiName != "!") { + e = DiscordEmoji.FromUnicode(v.emojiName); + } + if (e == null) continue; // Bad + if (m.GetReactionsAsync(e, 1).Result.Count == 0) { // Need to add + m.CreateReactionAsync(e).Wait(); + } - values.Add(v); + values.Add(v); + } } + Utils.Log("Loaded " + values.Count + " tracked messages for emojis."); + } catch (Exception e) { + Utils.Log("ERROR: problems in loading the EmojiForRole file: " + e.Message); } - UtilityFunctions.Log("Loaded " + values.Count + " tracked messages for emojis."); } public void Save() { - lock (values) { - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - UtilityFunctions.Log("ERROR: cannot delete old EmojisForRole file: " + path + "\nException: " + e.Message); - return; - } - using (FileStream f = new FileStream(path, FileMode.CreateNew)) { - byte[] data = new byte[36]; - foreach (ReactionValue v in values) { - int pos = 0; - byte[] d = BitConverter.GetBytes(v.channel); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.message); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.role); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.emojiId); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - if (v.emojiId != 0 || v.emojiName == null || v.emojiName.Length == 0) - for (int i = 0; i < 4; i++) data[pos++] = 0; - else { - d = BitConverter.GetBytes(Char.ConvertToUtf32(v.emojiName, 0)); - for (int i = 0; i < 4; i++) data[pos++] = d[i]; + try { + lock (values) { + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old EmojisForRole file: " + path + "\nException: " + e.Message); + return; + } + using (FileStream f = new FileStream(path, FileMode.CreateNew)) { + byte[] data = new byte[36]; + foreach (ReactionValue v in values) { + int pos = 0; + byte[] d = BitConverter.GetBytes(v.channel); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.message); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.role); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.emojiId); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + if (v.emojiId != 0 || v.emojiName == null || v.emojiName.Length == 0) + for (int i = 0; i < 4; i++) data[pos++] = 0; + else { + d = BitConverter.GetBytes(Char.ConvertToUtf32(v.emojiName, 0)); + for (int i = 0; i < 4; i++) data[pos++] = d[i]; + } + f.Write(data, 0, pos); } - f.Write(data, 0, pos); + f.Flush(); } - f.Flush(); } + Utils.Log("EmojisForRole: Saved " + values.Count + " tracked messages and emojis"); + } catch (Exception e) { + Utils.Log("ERROR: problems in saving the EmojiForRole file: " + e.Message); } - UtilityFunctions.Log("EmojisForRole: Saved " + values.Count + " tracked messages and emojis"); } internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji) { @@ -478,30 +527,38 @@ internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji } internal void HandleAddingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { - DiscordMember dm = (DiscordMember)user; - if (dm == null) return; // Not a valid member for the Guild/Context - if (values == null) return; - foreach (ReactionValue v in values) { - if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { - if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); - dm.GrantRoleAsync(v.dRole).Wait(); - UtilityFunctions.Log("Role " + v.dRole.Name + " was granted to " + user.Username + " by emoji " + eId); - return; + try { + DiscordMember dm = (DiscordMember)user; + if (dm == null) return; // Not a valid member for the Guild/Context + if (values == null) return; + foreach (ReactionValue v in values) { + if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + dm.GrantRoleAsync(v.dRole).Wait(); + Utils.Log("Role " + v.dRole.Name + " was granted to " + user.Username + " by emoji " + eId); + return; + } } + } catch (Exception e) { + Utils.Log("ERROR: problems in HandleAddingEmojiForRole: " + e.Message); } } internal void HandleRemovingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { - DiscordMember dm = (DiscordMember)user; - if (dm == null) return; // Not a valid member for the Guild/Context - if (values == null) return; - foreach (ReactionValue v in values) { - if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { - if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); - dm.RevokeRoleAsync(v.dRole).Wait(); - UtilityFunctions.Log("Role " + v.dRole.Name + " was removed from " + user.Username + " by emoji " + eId); - return; + try { + DiscordMember dm = (DiscordMember)user; + if (dm == null) return; // Not a valid member for the Guild/Context + if (values == null) return; + foreach (ReactionValue v in values) { + if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + dm.RevokeRoleAsync(v.dRole).Wait(); + Utils.Log("Role " + v.dRole.Name + " was removed from " + user.Username + " by emoji " + eId); + return; + } } + } catch (Exception e) { + Utils.Log("ERROR: problems in HandleRemovingEmojiForRole: " + e.Message); } } diff --git a/UPBot Code/Actions/MembersTracking.cs b/UPBot Code/Actions/MembersTracking.cs index 4939542..0149868 100644 --- a/UPBot Code/Actions/MembersTracking.cs +++ b/UPBot Code/Actions/MembersTracking.cs @@ -9,6 +9,7 @@ public class MembersTracking { static DiscordChannel trackChannel = null; public static async Task DiscordMemberRemoved(DiscordClient client, DSharpPlus.EventArgs.GuildMemberRemoveEventArgs args) { + try{ if (tracking == null) tracking = new Dictionary(); if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); @@ -16,54 +17,65 @@ public static async Task DiscordMemberRemoved(DiscordClient client, DSharpPlus.E tracking.Remove(args.Member.Id); string msg = "User " + args.Member.DisplayName + " did a kiss and go."; await trackChannel.SendMessageAsync(msg); - UtilityFunctions.Log(msg); + Utils.Log(msg); } else { - string msgC = UtilityFunctions.GetEmojiSnowflakeID(EmojiEnum.KO) + " User " + args.Member.Mention + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; + string msgC = Utils.GetEmojiSnowflakeID(EmojiEnum.KO) + " User " + args.Member.Mention + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; string msgL = "- User " + args.Member.DisplayName + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; await trackChannel.SendMessageAsync(msgC); - UtilityFunctions.Log(msgL); + Utils.Log(msgL); + } + } catch (Exception ex) { + Utils.Log("Error in DiscordMemberRemoved: " + ex.Message); } await Task.Delay(10); } public static async Task DiscordMemberAdded(DiscordClient client, DSharpPlus.EventArgs.GuildMemberAddEventArgs args) { + try{ if (tracking == null) tracking = new Dictionary(); if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); tracking[args.Member.Id] = DateTime.Now; _ = SomethingAsync(args.Member.Id, args.Member.DisplayName, args.Member.Mention, args.Guild.MemberCount); + } catch (Exception ex) { + Utils.Log("Error in DiscordMemberAdded: " + ex.Message); + } await Task.Delay(10); } public static async Task DiscordMemberUpdated(DiscordClient client, DSharpPlus.EventArgs.GuildMemberUpdateEventArgs args) { - if (tracking == null) tracking = new Dictionary(); - if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); + try { + if (tracking == null) tracking = new Dictionary(); + if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); - IReadOnlyList rolesBefore = args.RolesBefore; - IReadOnlyList rolesAfter = args.RolesAfter; - List rolesAdded = new List(); - // Changed role? - foreach (DiscordRole r1 in rolesAfter) { - bool addedRole = true; - foreach (DiscordRole r2 in rolesBefore) { - if (r1.Equals(r2)) { - addedRole = false; + IReadOnlyList rolesBefore = args.RolesBefore; + IReadOnlyList rolesAfter = args.RolesAfter; + List rolesAdded = new List(); + // Changed role? + foreach (DiscordRole r1 in rolesAfter) { + bool addedRole = true; + foreach (DiscordRole r2 in rolesBefore) { + if (r1.Equals(r2)) { + addedRole = false; + } } + if (addedRole) rolesAdded.Add(r1); } - if (addedRole) rolesAdded.Add(r1); - } - string msgC; - string msgL; - if (rolesAdded.Count > 0) { - msgC = "User " + args.Member.Mention + " has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); - msgL = "User \"" + args.Member.DisplayName + "\" has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); - foreach (DiscordRole r in rolesAdded) { - msgC += r.Mention; - msgL += r.Name; + string msgC; + string msgL; + if (rolesAdded.Count > 0) { + msgC = "User " + args.Member.Mention + " has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); + msgL = "User \"" + args.Member.DisplayName + "\" has the new role" + (rolesAdded.Count > 1 ? "s:" : ":"); + foreach (DiscordRole r in rolesAdded) { + msgC += r.Mention; + msgL += r.Name; + } + await trackChannel.SendMessageAsync(msgC); + Utils.Log(msgL); } - await trackChannel.SendMessageAsync(msgC); - UtilityFunctions.Log(msgL); + } catch (Exception ex) { + Utils.Log("Error in DiscordMemberUpdated: " + ex.Message); } await Task.Delay(10); @@ -73,10 +85,10 @@ public static async Task DiscordMemberUpdated(DiscordClient client, DSharpPlus.E static async Task SomethingAsync(ulong id, string name, string mention, int numMembers) { await Task.Delay(25000); if (tracking.ContainsKey(id)) { - string msgC = UtilityFunctions.GetEmojiSnowflakeID(EmojiEnum.OK) + " User " + mention + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; + string msgC = Utils.GetEmojiSnowflakeID(EmojiEnum.OK) + " User " + mention + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; string msgL = "+ User " + name + " joined on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + numMembers + " memebrs total)"; await trackChannel.SendMessageAsync(msgC); - UtilityFunctions.Log(msgL); + Utils.Log(msgL); tracking.Remove(id); } } diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index 257e87e..63cca44 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -22,7 +22,7 @@ public class BannedWords : BaseCommandModule { public static void Init() { bannedWords = new List(); - string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); + string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); if (!File.Exists(path)) return; string[] all = File.ReadAllLines(path); foreach (string line in all) { @@ -37,7 +37,7 @@ public static void Init() { [Description("To handle banned words. It can be done only by Mods and Helpers")] [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task BannedWordsCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await ctx.Channel.SendMessageAsync("Use the commands `list`, `add`, and `remove` to handle banned words."); } @@ -45,7 +45,7 @@ public async Task BannedWordsCommand(CommandContext ctx) { [Description("To handle banned words. It can be done only by Mods and Helpers")] [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task BannedWordsCommand(CommandContext ctx, [Description("Command to use (list, add, remove)")] string command) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await HandleListOfBannedWords(ctx, command); } @@ -53,76 +53,81 @@ public async Task BannedWordsCommand(CommandContext ctx, [Description("Command t [Description("To handle banned words. It can be done only by Mods and Helpers")] [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task BannedWordsCommand(CommandContext ctx, [Description("Command to use (list, add, remove)")] string command, [Description("The word to add or remove (not used when listing)")] string word) { - UtilityFunctions.LogUserCommand(ctx); - Task msg = await HandleAddRemoveOfBannedWords(ctx, command, word); - await Task.Delay(5000); - await msg.Result.DeleteAsync(); - await Task.Delay(1000); - await ctx.Message.DeleteAsync(); - + Utils.LogUserCommand(ctx); + Task msg = HandleAddRemoveOfBannedWords(ctx, command, word).Result; + Utils.DeleteDelayed(30, msg.Result).Wait(); + await Utils.DeleteDelayed(10, ctx.Message); } private async Task> HandleListOfBannedWords(CommandContext ctx, string command) { - if (command.ToLowerInvariant() != "list") return ctx.Channel.SendMessageAsync("Use: list, add, or remove."); - else if (bannedWords == null || bannedWords.Count == 0) return ctx.Channel.SendMessageAsync("There are no banned words I am aware of."); - else { - string message = "I have " + bannedWords.Count + " banned word" + (bannedWords.Count == 1 ? "" : "s") + ":\n"; - for (int i = 0; i < bannedWords.Count; i++) { - message += bannedWords[i].word + " (" + GetUserName(bannedWords[i].creator, ctx) + " " + bannedWords[i].date.ToString("yyyy/MM/dd") + ")"; - if (i < bannedWords.Count - 1) message += ",\n"; + try { + if (command.ToLowerInvariant() != "list") return ctx.Channel.SendMessageAsync("Use: list, add, or remove."); + else if (bannedWords == null || bannedWords.Count == 0) return ctx.Channel.SendMessageAsync("There are no banned words I am aware of."); + else { + string message = "I have " + bannedWords.Count + " banned word" + (bannedWords.Count == 1 ? "" : "s") + ":\n"; + for (int i = 0; i < bannedWords.Count; i++) { + message += bannedWords[i].word + " (" + GetUserName(bannedWords[i].creator, ctx) + " " + bannedWords[i].date.ToString("yyyy/MM/dd") + ")"; + if (i < bannedWords.Count - 1) message += ",\n"; + } + await Task.Delay(10); + return ctx.Channel.SendMessageAsync(message); } - await Task.Delay(10); - return ctx.Channel.SendMessageAsync(message); + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("BannedWords.List", ex)); } } private async Task> HandleAddRemoveOfBannedWords(CommandContext ctx, string command, string word) { - await Task.Delay(10); - if (command.ToLowerInvariant() == "add") { - word = word.Trim(' ', '\r', '\n').ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(word) || !valid.IsMatch(word)) return ctx.Channel.SendMessageAsync("Not a valid word"); - if (bannedWords == null) bannedWords = new List(); - // Do we have it? - foreach (BannedWord bw in bannedWords) { - if (bw.word.Equals(word)) { - await ctx.Message.CreateReactionAsync(UtilityFunctions.GetEmoji(EmojiEnum.KO)); - return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is already in the list."); + try { + await Task.Delay(10); + if (command.ToLowerInvariant() == "add") { + word = word.Trim(' ', '\r', '\n').ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(word) || !valid.IsMatch(word)) return ctx.Channel.SendMessageAsync("Not a valid word"); + if (bannedWords == null) bannedWords = new List(); + // Do we have it? + foreach (BannedWord bw in bannedWords) { + if (bw.word.Equals(word)) { + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.KO)); + return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is already in the list."); + } } - } - BannedWord w = new BannedWord(word, ctx.Message.Author.Id); - bannedWords.Add(w); - bannedWords.Sort((a, b) => { return a.word.CompareTo(b.word); }); - SaveWord(w); + BannedWord w = new BannedWord(word, ctx.Message.Author.Id); + bannedWords.Add(w); + bannedWords.Sort((a, b) => { return a.word.CompareTo(b.word); }); + SaveWord(w); - await ctx.Message.CreateReactionAsync(UtilityFunctions.GetEmoji(EmojiEnum.OK)); - return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been added."); - } - else if (command.ToLowerInvariant() == "remove") { - word = word.Trim(' ', '\r', '\n').ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(word) || !Regex.IsMatch(word, @"^[a-zA-Z0-9]+$")) return ctx.Channel.SendMessageAsync("Not a valid word"); - if (bannedWords == null) bannedWords = new List(); - // Do we have it? - BannedWord found = null; - foreach (BannedWord bw in bannedWords) { - if (bw.word.Equals(word)) { - found = bw; - break; - } + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.OK)); + return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been added."); } - if (found == null) { - await ctx.Message.CreateReactionAsync(UtilityFunctions.GetEmoji(EmojiEnum.KO)); - return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is not in the list."); + else if (command.ToLowerInvariant() == "remove") { + word = word.Trim(' ', '\r', '\n').ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(word) || !Regex.IsMatch(word, @"^[a-zA-Z0-9]+$")) return ctx.Channel.SendMessageAsync("Not a valid word"); + if (bannedWords == null) bannedWords = new List(); + // Do we have it? + BannedWord found = null; + foreach (BannedWord bw in bannedWords) { + if (bw.word.Equals(word)) { + found = bw; + break; + } + } + if (found == null) { + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.KO)); + return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is not in the list."); + } + bannedWords.Remove(found); + SaveList(); + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.OK)); + return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been removed."); } - bannedWords.Remove(found); - SaveList(); - await ctx.Message.CreateReactionAsync(UtilityFunctions.GetEmoji(EmojiEnum.OK)); - return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been removed."); + else return ctx.Channel.SendMessageAsync("Use: add or remove and then the word."); + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("BannedWords.Handle", ex)); } - else return ctx.Channel.SendMessageAsync("Use: add or remove and then the word."); } void SaveWord(BannedWord w) { - string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); + string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); if (!File.Exists(path)) File.CreateText(path); try { using (StreamWriter sw = File.AppendText(path)) { @@ -130,17 +135,17 @@ void SaveWord(BannedWord w) { sw.FlushAsync(); } } catch (Exception e) { - UtilityFunctions.Log(e.Message); + Utils.Log(e.Message); } } void SaveList() { - string path = UtilityFunctions.ConstructPath(directoryName, "BannedWords", ".txt"); + string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); if (File.Exists(path)) { try { File.Delete(path); } catch (Exception e) { - UtilityFunctions.Log(e.Message); + Utils.Log(e.Message); return; } } @@ -152,7 +157,7 @@ void SaveList() { } } } catch (Exception e) { - UtilityFunctions.Log(e.Message); + Utils.Log(e.Message); } } @@ -199,33 +204,36 @@ async Task GetUserNameFromDiscord(ulong userId, CommandContext ctx) } internal static async Task CheckMessage(DiscordClient client, MessageCreateEventArgs args) { - // Who is the author? If the bot or a mod then ignore - if (args.Author.Equals(client.CurrentUser)) return; - DiscordUser user = args.Author; - DiscordGuild guild = await client.GetGuildAsync((ulong)args.Message.Channel.GuildId); - DiscordMember member; try { - member = await guild.GetMemberAsync(user.Id); - } catch (Exception) { - return; - } - foreach (DiscordRole role in member.Roles) { - if (role.Id == 831050318171078718ul /* Helper */ || role.Id == 830901743624650783ul /* Mod */ || role.Id == 830901562960117780ul /* Owner */) return; - } + // Who is the author? If the bot or a mod then ignore + if (args.Author.Equals(client.CurrentUser)) return; + DiscordUser user = args.Author; + DiscordGuild guild = await client.GetGuildAsync((ulong)args.Message.Channel.GuildId); + DiscordMember member; + try { + member = await guild.GetMemberAsync(user.Id); + } catch (Exception) { + return; + } + foreach (DiscordRole role in member.Roles) { + if (role.Id == 831050318171078718ul /* Helper */ || role.Id == 830901743624650783ul /* Mod */ || role.Id == 830901562960117780ul /* Owner */) return; + } - string msg = args.Message.Content.ToLowerInvariant(); - foreach (BannedWord w in bannedWords) { - int pos = msg.IndexOf(w.word); - if (pos == -1) continue; - if (pos > 0 && letters.IsMatch(msg[pos - 1].ToString())) continue; - if (pos + w.word.Length < msg.Length && letters.IsMatch(msg[pos + w.word.Length].ToString())) continue; - - UtilityFunctions.Log("Removed word \"" + w.word + "\" from " + user.Username + " in: " + msg); - DiscordMessage warning = await args.Message.Channel.SendMessageAsync("Moderate your language, " + user.Mention + "."); - await args.Message.DeleteAsync("Bad words: " + w.word); - await Task.Delay(10000); - await warning.DeleteAsync(); - return; + string msg = args.Message.Content.ToLowerInvariant(); + foreach (BannedWord w in bannedWords) { + int pos = msg.IndexOf(w.word); + if (pos == -1) continue; + if (pos > 0 && letters.IsMatch(msg[pos - 1].ToString())) continue; + if (pos + w.word.Length < msg.Length && letters.IsMatch(msg[pos + w.word.Length].ToString())) continue; + + Utils.Log("Removed word \"" + w.word + "\" from " + user.Username + " in: " + msg); + DiscordMessage warning = await args.Message.Channel.SendMessageAsync("Moderate your language, " + user.Mention + "."); + await args.Message.DeleteAsync("Bad words: " + w.word); + Utils.DeleteDelayed(10000, warning).Wait(); + return; + } + } catch (Exception ex) { + await args.Message.RespondAsync(Utils.GenerateErrorAnswer("BannedWords.CheckMessage", ex)); } } diff --git a/UPBot Code/Commands/CustomCommand.cs b/UPBot Code/Commands/CustomCommand.cs index 85c6055..413b77f 100644 --- a/UPBot Code/Commands/CustomCommand.cs +++ b/UPBot Code/Commands/CustomCommand.cs @@ -10,7 +10,7 @@ public class CustomCommand public CustomCommand(string[] names, string content) { this.Names = names; - this.FilePath = UtilityFunctions.ConstructPath(CustomCommandsService.DirectoryNameCC, names[0], ".txt"); + this.FilePath = Utils.ConstructPath(CustomCommandsService.DirectoryNameCC, names[0], ".txt"); this.Content = content; } diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 9454abe..79568f7 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -20,7 +20,7 @@ public class CustomCommandsService : BaseCommandModule internal const string DirectoryNameCC = "CustomCommands"; [Command("newcc")] - [Aliases("createcc", "addcc", "ccadd", "cccreate")] + [Aliases("createcc", "addcc", "ccadd", "cccreate", "ccnew")] [Description("**Create** a new Custom Command (so-called 'CC') with a specified name and all aliases if desired " + "(no duplicate alias allowed).\nAfter doing this, the bot will ask you to input the content, which will " + "be displayed once someone invokes this CC. Your entire next message will be used for the content, so " + @@ -29,12 +29,13 @@ public class CustomCommandsService : BaseCommandModule [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of all aliases. The first term is the **main name**, the other ones, separated by a space, are aliases")] params string[] names) { - names = names.Distinct().ToArray(); + Utils.LogUserCommand(ctx); + names = names.Distinct().ToArray(); foreach (var name in names) { if (DiscordClient.GetCommandsNext().RegisteredCommands.ContainsKey(name)) // Check if there is a command with one of the names already { - await UtilityFunctions.ErrorCallback(CommandErrors.CommandExists, ctx, name); + await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); return; } @@ -42,7 +43,7 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al { if (cmd.Names.Contains(name)) // Check if there is already a CC with one of the names { - await UtilityFunctions.ErrorCallback(CommandErrors.CommandExists, ctx, name); + await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); return; } } @@ -54,7 +55,7 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al await WriteToFile(command); string embedMessage = $"CC {names[0]} successfully created and saved!"; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, false); + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); } [Command("ccdel")] @@ -65,7 +66,8 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task DeleteCommand(CommandContext ctx, [Description("Main name of the CC you want to delete")] string name) { - string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, name, ".txt"); + Utils.LogUserCommand(ctx); + string filePath = Utils.ConstructPath(DirectoryNameCC, name, ".txt"); if (File.Exists(filePath)) { File.Delete(filePath); @@ -73,7 +75,7 @@ public async Task DeleteCommand(CommandContext ctx, [Description("Main name of t Commands.Remove(cmd); string embedMessage = $"CC {name} successfully deleted!"; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, true); + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, true); } } @@ -85,7 +87,8 @@ public async Task DeleteCommand(CommandContext ctx, [Description("Main name of t [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EditCommand(CommandContext ctx, [Description("Main name of the CC you want to edit")] string name) { - string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, name, ".txt"); + Utils.LogUserCommand(ctx); + string filePath = Utils.ConstructPath(DirectoryNameCC, name, ".txt"); if (File.Exists(filePath)) { string content = await WaitForContent(ctx, name); @@ -104,10 +107,10 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the command.EditCommand(content); string embedMessage = $"CC **{name}** successfully edited!"; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, false); + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); } else - await UtilityFunctions.ErrorCallback(CommandErrors.MissingCommand, ctx); + await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); } [Command("cceditname")] @@ -121,14 +124,15 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the "of the CC whose name you want to edit, the **SECOND** term " + "is the new **main name** and all the other terms are new aliases")] params string[] names) { - names = names.Distinct().ToArray(); + Utils.LogUserCommand(ctx); + names = names.Distinct().ToArray(); if (names.Length < 2) { - await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParams, ctx); + await Utils.ErrorCallback(CommandErrors.InvalidParams, ctx); return; } - string filePath = UtilityFunctions.ConstructPath(DirectoryNameCC, names[0], ".txt"); + string filePath = Utils.ConstructPath(DirectoryNameCC, names[0], ".txt"); if (File.Exists(filePath)) { if (TryGetCommand(names[0], out CustomCommand command)) @@ -143,7 +147,7 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the content += c + System.Environment.NewLine; } - string newPath = UtilityFunctions.ConstructPath(DirectoryNameCC, names[1], ".txt"); + string newPath = Utils.ConstructPath(DirectoryNameCC, names[1], ".txt"); File.Move(filePath, newPath); using (StreamWriter sw = File.CreateText(newPath)) { @@ -152,10 +156,10 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the } string embedDescription = "The CC names have been successfully edited."; - await UtilityFunctions.BuildEmbedAndExecute("Success", embedDescription, UtilityFunctions.Green, ctx, false); + await Utils.BuildEmbedAndExecute("Success", embedDescription, Utils.Green, ctx, false); } else - await UtilityFunctions.ErrorCallback(CommandErrors.MissingCommand, ctx); + await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); } [Command("cclist")] @@ -163,9 +167,10 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the [Description("Get a list of all Custom Commands (CC's).")] public async Task ListCC(CommandContext ctx) { - if (Commands.Count <= 0) + Utils.LogUserCommand(ctx); + if (Commands.Count <= 0) { - await UtilityFunctions.ErrorCallback(CommandErrors.NoCustomCommands, ctx); + await Utils.ErrorCallback(CommandErrors.NoCustomCommands, ctx); return; } @@ -176,7 +181,7 @@ public async Task ListCC(CommandContext ctx) allCommands += $"- {cmd.Names[0]} ({(cmd.Names.Length > 1 ? string.Join(", ", cmd.Names.Skip(1)) : string.Empty)}){System.Environment.NewLine}"; } - await UtilityFunctions.BuildEmbedAndExecute("CC List", allCommands, UtilityFunctions.Yellow, ctx, true); + await Utils.BuildEmbedAndExecute("CC List", allCommands, Utils.Yellow, ctx, true); } internal static async Task LoadCustomCommands() @@ -229,7 +234,7 @@ private async Task WriteToFile(CustomCommand command) private async Task WaitForContent(CommandContext ctx, string name) { string embedMessage = $"Please input the content of the CC **{name}** in one single message. Your next message will count as the content."; - await UtilityFunctions.BuildEmbedAndExecute("Waiting for interaction", embedMessage, UtilityFunctions.LightBlue, ctx, true); + await Utils.BuildEmbedAndExecute("Waiting for interaction", embedMessage, Utils.LightBlue, ctx, true); string content = string.Empty; await ctx.Message.GetNextMessageAsync(m => diff --git a/UPBot Code/Commands/DebugCommand.cs b/UPBot Code/Commands/DebugCommand.cs deleted file mode 100644 index 06b964d..0000000 --- a/UPBot Code/Commands/DebugCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -using DSharpPlus; -using DSharpPlus.CommandsNext; -using DSharpPlus.CommandsNext.Attributes; -using DSharpPlus.Entities; -using System.Threading.Tasks; - -/// -/// Debug command to quickly test some new stuff -/// author: CPU -/// -public class DebugCommand : BaseCommandModule { - - [Command("qwe")] - [Description("This is a command just to quickly debug stuff in development")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task DoDebug(CommandContext ctx) { // Refactors the previous post, if it is code - UtilityFunctions.LogUserCommand(ctx); - DiscordMessage msg = await ctx.Channel.SendMessageAsync("Test message"); - - DiscordEmoji emoji = UtilityFunctions.GetEmoji(EmojiEnum.Godot); - await msg.CreateReactionAsync(emoji); - - await ctx.Message.RespondAsync("Done"); - } -} diff --git a/UPBot Code/Commands/Delete.cs b/UPBot Code/Commands/Delete.cs index 084c6e0..a45c7cc 100644 --- a/UPBot Code/Commands/Delete.cs +++ b/UPBot Code/Commands/Delete.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DSharpPlus; @@ -11,96 +12,96 @@ /// or the last x messages of a specific user /// author: Duck /// -public class Delete : BaseCommandModule -{ - private const int MessageLimit = 50; - private const string CallbackLimitExceeded = ", since you can't delete more than 50 messages at a time."; +public class Delete : BaseCommandModule { + private const int MessageLimit = 50; + private const string CallbackLimitExceeded = ", since you can't delete more than 50 messages at a time."; - /// - /// Delete the last x messages of any user - /// - [Command("delete")] - [Aliases("clear", "purge")] - [Description("Deletes the last x messages in the channel, the command was invoked in (e.g. `\\delete 10`)." + - "\nIt contains an overload to delete the last x messages of a specified user (e.g. `\\delete @User 10`)." + - "\nThis command can only be invoked by a Helper or Mod.")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task DeleteCommand(CommandContext ctx, [Description("How many messages should be deleted?")] int count) - { - UtilityFunctions.LogUserCommand(ctx); - if (count <= 0) - { - await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); - return; - } + /// + /// Delete the last x messages of any user + /// + [Command("delete")] + [Aliases("clear", "purge")] + [Description("Deletes the last x messages in the channel, the command was invoked in (e.g. `\\delete 10`)." + + "\nIt contains an overload to delete the last x messages of a specified user (e.g. `\\delete @User 10`)." + + "\nThis command can only be invoked by a Helper or Mod.")] + [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission + [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only + public async Task DeleteCommand(CommandContext ctx, [Description("How many messages should be deleted?")] int count) { + Utils.LogUserCommand(ctx); + if (count <= 0) { + await Utils.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); + return; + } + + bool limitExceeded = CheckLimit(count); - bool limitExceeded = CheckLimit(count); + var messages = ctx.Channel.GetMessagesAsync(count + 1).Result; + await DeleteMessages(ctx.Message, messages); - var messages = ctx.Channel.GetMessagesAsync(count + 1).Result; - await DeleteMessages(ctx, messages); + await Success(ctx, limitExceeded, count); + } - await Success(ctx, limitExceeded, count); + /// + /// Delete the last x messages of the specified user + /// + [Command("delete")] + [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission + [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only + public async Task DeleteCommand(CommandContext ctx, [Description("Whose last x messages should get deleted?")] DiscordMember targetUser, + [Description("How many messages should get deleted?")] int count) { + Utils.LogUserCommand(ctx); + if (count <= 0) { + await Utils.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); + return; } - /// - /// Delete the last x messages of the specified user - /// - [Command("delete")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task DeleteCommand(CommandContext ctx, [Description("Whose last x messages should get deleted?")]DiscordMember targetUser, - [Description("How many messages should get deleted?")] int count) - { - UtilityFunctions.LogUserCommand(ctx); - if (count <= 0) - { - await UtilityFunctions.ErrorCallback(CommandErrors.InvalidParamsDelete, ctx, count); - return; - } + bool limitExceeded = CheckLimit(count); - bool limitExceeded = CheckLimit(count); + var allMessages = ctx.Channel.GetMessagesAsync().Result; // Get last 100 messages + var userMessages = allMessages.Where(x => x.Author == targetUser).Take(count + 1); + await DeleteMessages(ctx.Message, userMessages); - var allMessages = ctx.Channel.GetMessagesAsync().Result; // Get last 100 messages - var userMessages = allMessages.Where(x => x.Author == targetUser).Take(count + 1); - await DeleteMessages(ctx, userMessages); + await Success(ctx, limitExceeded, count, targetUser); + } - await Success(ctx, limitExceeded, count, targetUser); + /// + /// The core-process of deleting the messages + /// + public async Task DeleteMessages(DiscordMessage request, IEnumerable messages) { + try{ + List toDelete = new List(); + foreach (DiscordMessage m in messages) { + if (m != request) toDelete.Add(m); } - - /// - /// The core-process of deleting the messages - /// - public async Task DeleteMessages(CommandContext ctx, IEnumerable messages) - { - foreach (DiscordMessage m in messages) - { - if (m != ctx.Message) - await m.DeleteAsync(); - } + await request.Channel.DeleteMessagesAsync(toDelete); + } catch (Exception ex) { + await request.RespondAsync(Utils.GenerateErrorAnswer("DeleteMessages", ex)); } - /// - /// Will be called at the end of every execution of this command and tells the user that the execution succeeded - /// including a short summary of the command (how many messages, by which user etc.) - /// - private async Task Success(CommandContext ctx, bool limitExceeded, int count, DiscordMember targetUser = null) - { - string mentionUserStr = targetUser == null ? string.Empty : $"by '{targetUser.DisplayName}'"; - string overLimitStr = limitExceeded ? CallbackLimitExceeded : string.Empty; - string messagesLiteral = UtilityFunctions.PluralFormatter(count, "message", "messages"); - string hasLiteral = UtilityFunctions.PluralFormatter(count, "has", "have"); + } - await ctx.Message.DeleteAsync(); - string embedMessage = $"The last {count} {messagesLiteral} {mentionUserStr} {hasLiteral} been successfully deleted{overLimitStr}."; + /// + /// Will be called at the end of every execution of this command and tells the user that the execution succeeded + /// including a short summary of the command (how many messages, by which user etc.) + /// + private async Task Success(CommandContext ctx, bool limitExceeded, int count, DiscordMember targetUser = null) { + try{ + string mentionUserStr = targetUser == null ? string.Empty : $"by '{targetUser.DisplayName}'"; + string overLimitStr = limitExceeded ? CallbackLimitExceeded : string.Empty; + string messagesLiteral = Utils.PluralFormatter(count, "message", "messages"); + string hasLiteral = Utils.PluralFormatter(count, "has", "have"); - var message = await UtilityFunctions.BuildEmbedAndExecute("Success", embedMessage, UtilityFunctions.Green, ctx, true); - await Task.Delay(10_000); - await message.DeleteAsync(); - } + await ctx.Message.DeleteAsync(); + string embedMessage = $"The last {count} {messagesLiteral} {mentionUserStr} {hasLiteral} been successfully deleted{overLimitStr}."; - private bool CheckLimit(int count) - { - return count > MessageLimit; + var message = await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, true); + await Utils.DeleteDelayed(10, message); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("Delete", ex)); } + } + + private bool CheckLimit(int count) { + return count > MessageLimit; + } } \ No newline at end of file diff --git a/UPBot Code/Commands/Game.cs b/UPBot Code/Commands/Game.cs index a65db33..7006266 100644 --- a/UPBot Code/Commands/Game.cs +++ b/UPBot Code/Commands/Game.cs @@ -14,7 +14,7 @@ public class GameModule : BaseCommandModule [Command("game")] public async Task GameCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); StringBuilder sb = new StringBuilder("Available game commmands\n"); sb.AppendLine("========================"); sb.AppendLine(String.Format("{0, -10}: {1}", "bool", "True or False")); @@ -26,21 +26,21 @@ public async Task GameCommand(CommandContext ctx) [Command("bool")] public async Task BoolCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await PlayBool(ctx); } [Command("rps")] public async Task RPSCommand(CommandContext ctx, string kind) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await PlayRockPaperScissors(ctx, kind); } [Command("rps")] public async Task RPSCommand(CommandContext ctx) { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); await PlayRockPaperScissors(ctx, null); } diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index af8c779..58f8537 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -47,17 +47,18 @@ public async Task ErrorMessage(CommandContext ctx) { string title = "Help Language - How To Use"; DiscordMember member = ctx.Member; - string description = member.Mention + " , if you want to get video course about specific language type: `\\helplanguage video C#`" + - " \nIf you want to get full online course about specific language type: \n`\\helplanguage course C#`" + + string description = member.Mention + " , if you want to get video course about specific language type: `helplanguage video C#`" + + " \nIf you want to get full online course about specific language type: \n`helplanguage course C#`" + " \nAvailable languages: `ะก#, C++, Python, JavaScript, Java`"; - await UtilityFunctions.BuildEmbedAndExecute(title, description, UtilityFunctions.Red, ctx, true); + await Utils.BuildEmbedAndExecute(title, description, Utils.Red, ctx, true); } [Command("helplanguage")] - [Description("Gives good tutorials on specific language.\n**Usage:** `\\helplanguage language`")] + [Description("Gives good tutorials on specific language.\n**Usage**: `helplanguage language`")] public async Task HelpCommand(CommandContext ctx, [Description("Choose what you want video or course on specific language.")] string typeOfHelp, [Description("As string `` put the name of language that you want to learn")] string lang) // C# { - UtilityFunctions.LogUserCommand(ctx); + Utils.LogUserCommand(ctx); + try { lang = NormalizeLanguage(lang); if (lang == null) @@ -73,6 +74,9 @@ public async Task HelpCommand(CommandContext ctx, [Description("Choose what you bool course = typeOfHelp.ToLowerInvariant() != "video"; await GenerateHelpfulAnswer(ctx, lang, course); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("WhoIs", ex)); + } } private string NormalizeLanguage(string language) { @@ -100,11 +104,10 @@ private string NormalizeLanguage(string language) { default: return char.ToUpperInvariant(language[0]) + language.Substring(1); } - - //return language; } private async Task GenerateHelpfulAnswer(CommandContext ctx, string language, bool isCourse) { + try{ DiscordMember member = ctx.Member; ulong memberId = member.Id; @@ -133,12 +136,14 @@ private async Task GenerateHelpfulAnswer(CommandContext ctx, string language, bo string link = isCourse ? languages[language].CourseLink :languages[language].VideoLink; string msg = helpfulAnswers[lastRequest.Num]; - msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention) - .Replace("///", link); + msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention).Replace("///", link); if (isCourse) msg = msg.Replace("video", "course"); - await UtilityFunctions.BuildEmbedAndExecute(title, msg, languages[language].Color, ctx, true); + await Utils.BuildEmbedAndExecute(title, msg, languages[language].Color, ctx, true); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("HelpLanguage", ex)); + } } private class LastRequestByMember { diff --git a/UPBot Code/Commands/Ping.cs b/UPBot Code/Commands/Ping.cs index f9b1475..96bd43f 100644 --- a/UPBot Code/Commands/Ping.cs +++ b/UPBot Code/Commands/Ping.cs @@ -14,11 +14,9 @@ public class PingModule : BaseCommandModule { private List lastRequests = null; [Command("ping")] - public async Task GreetCommand(CommandContext ctx) { - await GeneratePong(ctx); - } - [Command("upbot")] - public async Task GreetCommand2(CommandContext ctx) { + [Aliases("upbot")] + [Description("Checks if the bot is alive")] + public async Task PongCommand(CommandContext ctx) { await GeneratePong(ctx); } @@ -37,39 +35,47 @@ public async Task GreetCommand2(CommandContext ctx) { int lastGlobal = -1; Task GeneratePong(CommandContext ctx) { + Utils.LogUserCommand(ctx); + try { + + // Check if we have to initiialize our history of pings + if (lastRequests == null) lastRequests = new List(); + + // Grab the current member id + DiscordMember member = ctx.Member; + ulong memberId = member.Id; + + // Find the last request + LastRequestByMember lastRequest = null; + int annoyedLevel = 0; + foreach (LastRequestByMember lr in lastRequests) + if (lr.memberId == memberId) { + lastRequest = lr; + break; + } + if (lastRequest == null) { // No last request, create one + lastRequest = new LastRequestByMember(memberId); + lastRequests.Add(lastRequest); + } + else { + annoyedLevel = lastRequest.AddRequest(); + } + if (annoyedLevel == -1) return ctx.RespondAsync(""); // No answer - // Check if we have to initiialize our history of pings - if (lastRequests == null) lastRequests = new List(); + // Was the request already done recently? + int rnd = random.Next(0, 7); + while (rnd == lastRequest.lastRandom || rnd == lastGlobal) rnd = random.Next(0, 7); // Find one that is not the same of last one + lastRequest.lastRandom = rnd; // Record for the next time + lastGlobal = rnd; // Record for the next time + string msg = answers[annoyedLevel, rnd]; + msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention); - // Grab the current member id - DiscordMember member = ctx.Member; - ulong memberId = member.Id; - // Find the last request - LastRequestByMember lastRequest = null; - int annoyedLevel = 0; - foreach (LastRequestByMember lr in lastRequests) - if (lr.memberId == memberId) { - lastRequest = lr; - break; - } - if (lastRequest == null) { // No last request, create one - lastRequest = new LastRequestByMember(memberId); - lastRequests.Add(lastRequest); - } - else { - annoyedLevel = lastRequest.AddRequest(); + DiscordMessage answer = ctx.RespondAsync(msg).Result; + return Utils.DeleteDelayed(30, ctx.Message, answer); // We want to remove the ping and the answer after a minute + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("Ping", ex)); } - if (annoyedLevel == -1) return ctx.RespondAsync(""); // No answer - - // Was the request already done recently? - int rnd = random.Next(0, 7); - while (rnd == lastRequest.lastRandom || rnd == lastGlobal) rnd = random.Next(0, 7); // Find one that is not the same of last one - lastRequest.lastRandom = rnd; // Record for the next time - lastGlobal = rnd; // Record for the next time - string msg = answers[annoyedLevel, rnd]; - msg = msg.Replace("$$$", member.DisplayName).Replace("@@@", member.Mention); - return ctx.RespondAsync(msg); } const int MaxTrackedRequests = 10; diff --git a/UPBot Code/Commands/Refactor.cs b/UPBot Code/Commands/Refactor.cs index 63d05e5..86d9e12 100644 --- a/UPBot Code/Commands/Refactor.cs +++ b/UPBot Code/Commands/Refactor.cs @@ -13,19 +13,19 @@ public class Refactor : BaseCommandModule { [Command("checklanguage")] - [Description("Check what language is in the last post or in the post you replied to")] + [Description("Checks what language is in the post you replied to, or the last post of a specified user or just the last post.")] public async Task CheckLanguage(CommandContext ctx) { // Refactors the previous post, if it is code await RefactorCode(ctx, null, "best"); } [Command("checklanguage")] - [Description("Check what language is in the last post of the user")] + [Description("Checks what language is in the post you replied to, or the last post of a specified user or just the last post.")] public async Task CheckLanguage(CommandContext ctx, [Description("The user the posted the message to check")] DiscordMember member) { // Refactors the previous post, if it is code await RefactorCode(ctx, member, "best"); } [Command("reformat")] - [Description("Replace the last post of the specified user or the post you replied to with a formatted code block using the specified language")] + [Description("Replace a specified post with a reformatted code block using the specified language or the best language")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task RefactorCommand(CommandContext ctx) { // Refactors the previous post, if it is code @@ -36,7 +36,7 @@ public class Refactor : BaseCommandModule { [Description("Replace the last post of the specified user or the post you replied to with a formatted code block")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task ReformatCommand(CommandContext ctx, [Description("Force the Language to use. Use 'best' or 'Analyze' to find the best language.")] string language) { // Refactors the previous post, if it is code + public async Task ReformatCommand(CommandContext ctx, [Description("Force the Language to use. Use **Best** or **Analyze** to find the best language.")] string language) { // Refactors the previous post, if it is code await RefactorCode(ctx, null, language); } @@ -55,96 +55,100 @@ public async Task RefacReformatCommandtorCommand(CommandContext ctx, [Descriptio } private async Task> RefactorCode(CommandContext ctx, DiscordMember m, string language) { - UtilityFunctions.LogUserCommand(ctx); - DiscordChannel c = ctx.Channel; - DiscordMessage toRefactor = null; - if (ctx.Message.Reference != null) toRefactor = ctx.Message.Reference.Message; - else { - IReadOnlyList msgs = await c.GetMessagesAsync(50); - if (m == null) toRefactor = msgs[1]; + Utils.LogUserCommand(ctx); + try { + DiscordChannel c = ctx.Channel; + DiscordMessage toRefactor = null; + if (ctx.Message.Reference != null) toRefactor = ctx.Message.Reference.Message; else { - for (int i = 1; i < msgs.Count; i++) { - if (msgs[i].Author.Id.Equals(m.Id)) { - toRefactor = msgs[i]; - break; + IReadOnlyList msgs = await c.GetMessagesAsync(50); + if (m == null) toRefactor = msgs[1]; + else { + for (int i = 1; i < msgs.Count; i++) { + if (msgs[i].Author.Id.Equals(m.Id)) { + toRefactor = msgs[i]; + break; + } } } + if (toRefactor == null) return ctx.RespondAsync("Nothing to refactor found"); } - if (toRefactor == null) return ctx.RespondAsync("Nothing to refactor found"); - } - // Is the message some code? - string code = toRefactor.Content; - int weightCs = 0, weightCp = 0, weightJv = 0, weightJs = 0, weightPy = 0; - foreach (LangKWord k in keywords) { - if (k.regexp.IsMatch(code)) { - weightCs += k.wCs; - weightCp += k.wCp; - weightJv += k.wJv; - weightJs += k.wJs; - weightPy += k.wPy; + // Is the message some code? + string code = toRefactor.Content; + int weightCs = 0, weightCp = 0, weightJv = 0, weightJs = 0, weightPy = 0; + foreach (LangKWord k in keywords) { + if (k.regexp.IsMatch(code)) { + weightCs += k.wCs; + weightCp += k.wCp; + weightJv += k.wJv; + weightJs += k.wJs; + weightPy += k.wPy; + } } - } - string guessed = "no one"; - string best = ""; - EmojiEnum langEmoji = EmojiEnum.None; - int w = 0; - if (weightCs > w) { guessed = "<:csharp:831465428214743060> C#"; w = weightCs; best = "cs"; langEmoji = EmojiEnum.CSharp; } - if (weightCp > w) { guessed = "<:cpp:831465408874676273> C++"; w = weightCp; best = "cpp"; langEmoji = EmojiEnum.Cpp; } - if (weightJs > w) { guessed = "<:Javascript:876103767068647435> Javascript"; w = weightJs; best = "js"; langEmoji = EmojiEnum.Javascript; } - if (weightJv > w) { guessed = "<:java:875852276017815634> Java"; w = weightJv; best = "java"; langEmoji = EmojiEnum.Java; } - if (weightPy > w) { guessed = "<:python:831465381016895500> Python"; w = weightPy; best = "python"; langEmoji = EmojiEnum.Python; } - if (w == 0 && language == null) return ctx.RespondAsync("Nothing to reformat"); + string guessed = "no one"; + string best = ""; + EmojiEnum langEmoji = EmojiEnum.None; + int w = 0; + if (weightCs > w) { guessed = "<:csharp:831465428214743060> C#"; w = weightCs; best = "cs"; langEmoji = EmojiEnum.CSharp; } + if (weightCp > w) { guessed = "<:cpp:831465408874676273> C++"; w = weightCp; best = "cpp"; langEmoji = EmojiEnum.Cpp; } + if (weightJs > w) { guessed = "<:Javascript:876103767068647435> Javascript"; w = weightJs; best = "js"; langEmoji = EmojiEnum.Javascript; } + if (weightJv > w) { guessed = "<:java:875852276017815634> Java"; w = weightJv; best = "java"; langEmoji = EmojiEnum.Java; } + if (weightPy > w) { guessed = "<:python:831465381016895500> Python"; w = weightPy; best = "python"; langEmoji = EmojiEnum.Python; } + if (w == 0 && language == null) return ctx.RespondAsync("Nothing to reformat"); - language = NormalizeLanguage(language, best); - if (language == null) - return ctx.RespondAsync("Best guess for the language is: " + guessed + "\nC# = " + weightCs + " C++ = " + weightCp + " Java = " + weightJv + " Javascript = " + weightJs + " Python = " + weightPy); + language = NormalizeLanguage(language, best); + if (language == null) + return ctx.RespondAsync("Best guess for the language is: " + guessed + "\nC# = " + weightCs + " C++ = " + weightCp + " Java = " + weightJv + " Javascript = " + weightJs + " Python = " + weightPy); - // Remove the ``` at begin and end, if any. And the code name after initial ``` - bool deleteOrig = true; - Match codeMatch = codeBlock.Match(code); - if (codeMatch.Success) { - code = codeMatch.Groups[5].Value; - deleteOrig = string.IsNullOrWhiteSpace(codeMatch.Groups[1].Value); - } - code = code.Trim(' ', '\t', '\r', '\n'); - code = emptyLines.Replace(code, "\n"); - - if (langEmoji == EmojiEnum.CSharp || langEmoji == EmojiEnum.Cpp || langEmoji == EmojiEnum.Java || langEmoji == EmojiEnum.Javascript) code = FixIndentation(code); + // Remove the ``` at begin and end, if any. And the code name after initial ``` + bool deleteOrig = true; + Match codeMatch = codeBlock.Match(code); + if (codeMatch.Success) { + code = codeMatch.Groups[5].Value; + deleteOrig = string.IsNullOrWhiteSpace(codeMatch.Groups[1].Value); + } + code = code.Trim(' ', '\t', '\r', '\n'); + code = emptyLines.Replace(code, "\n"); - code = "Reformatted " + toRefactor.Author.Mention + " code\n" + "```" + language + "\n" + code + "\n```"; + if (langEmoji == EmojiEnum.CSharp || langEmoji == EmojiEnum.Cpp || langEmoji == EmojiEnum.Java || langEmoji == EmojiEnum.Javascript) code = FixIndentation(code); - if (guessed == "no one" && language != null) { - langEmoji = GetLanguageEmoji(language); - } + code = "Reformatted " + toRefactor.Author.Mention + " code\n" + "```" + language + "\n" + code + "\n```"; - DiscordMessage replacement = await ctx.Channel.SendMessageAsync(code); - DiscordEmoji autoRefactored = UtilityFunctions.GetEmoji(EmojiEnum.AutoRefactored); - DiscordEmoji emoji = UtilityFunctions.GetEmoji(langEmoji); - try { - if (autoRefactored != null) { - await Task.Delay(120); - await replacement.CreateReactionAsync(autoRefactored); + if (guessed == "no one" && language != null) { + langEmoji = GetLanguageEmoji(language); } - if (emoji != null) { - await Task.Delay(120); - await replacement.CreateReactionAsync(emoji); - } - if (deleteOrig) { - await Task.Delay(120); - List toDelete = new List { toRefactor, ctx.Message }; - await ctx.Channel.DeleteMessagesAsync(toDelete); - } - else { - await Task.Delay(120); - await ctx.Message.DeleteAsync(); + + DiscordMessage replacement = await ctx.Channel.SendMessageAsync(code); + DiscordEmoji autoRefactored = Utils.GetEmoji(EmojiEnum.AutoRefactored); + DiscordEmoji emoji = Utils.GetEmoji(langEmoji); + try { + if (autoRefactored != null) { + await Task.Delay(120); + await replacement.CreateReactionAsync(autoRefactored); + } + if (emoji != null) { + await Task.Delay(120); + await replacement.CreateReactionAsync(emoji); + } + if (deleteOrig) { + await Task.Delay(120); + List toDelete = new List { toRefactor, ctx.Message }; + await ctx.Channel.DeleteMessagesAsync(toDelete); + } + else { + await Task.Delay(120); + await ctx.Message.DeleteAsync(); + } + } catch (Exception e) { + return ctx.RespondAsync("Exception: " + e.Message); } - } catch (Exception e) { - return ctx.RespondAsync("Exception: " + e.Message); + await Task.Delay(150); + return ctx.RespondAsync(""); + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("Refactor", ex)); } - await Task.Delay(150); - return ctx.RespondAsync(""); } readonly Regex lineOpenBlock = new Regex("^{(\\s*//.*|\\s*/\\*/.*)?$", RegexOptions.Multiline, TimeSpan.FromSeconds(1)); diff --git a/UPBot Code/Commands/Stats.cs b/UPBot Code/Commands/Stats.cs index 8fc41d0..3923e10 100644 --- a/UPBot Code/Commands/Stats.cs +++ b/UPBot Code/Commands/Stats.cs @@ -29,59 +29,57 @@ public class Stats : BaseCommandModule { [Command("stats")] - [Description("Provides server stats for the 4 most used channels (Unity, CSharp, Help 1, and Help 2")] + [Description("Provides server stats for specific channels, with no parameters checks the most 4 commons channels")] + [Cooldown(1, 60, CooldownBucketType.Channel | CooldownBucketType.User)] public async Task DoStats(CommandContext ctx) { await GenerateStats(ctx, null, 100); } [Command("stats")] - [Description("Provides server stats for a specific channel")] - public async Task DoStats(CommandContext ctx, DiscordChannel channel) { + [Description("Provides server stats for specific channels")] + [Cooldown(1, 60, CooldownBucketType.Channel | CooldownBucketType.User)] + public async Task DoStats(CommandContext ctx, [Description("Specific channel for the stats")] DiscordChannel channel) { await GenerateStats(ctx, channel, 100); } [Command("stats")] - [Description("Provides server stats, for a specified chanenl and with the required number of messages")] - public async Task DoStats(CommandContext ctx, DiscordChannel channel, int numMessages) { + [Description("Provides server stats for specific channels")] + [Cooldown(1, 60, CooldownBucketType.Channel | CooldownBucketType.User)] + public async Task DoStats(CommandContext ctx, [Description("Specific channel for the stats")] DiscordChannel channel, [Description("Number of messages to check")] int numMessages) { await GenerateStats(ctx, channel, numMessages); } [Command("stats")] - [Description("Provides server stats, for a specified chanenl and with the required number of messages")] - public async Task DoStats(CommandContext ctx, int numMessages, DiscordChannel channel) { - await GenerateStats(ctx, channel, numMessages); - } - [Command("stats")] - [Description("Provides server stats for the 4 most used channels, with the required number of messages")] - public async Task DoStats(CommandContext ctx, int numMessages) { + [Description("Provides server stats for specific channels")] + [Cooldown(1, 60, CooldownBucketType.Channel | CooldownBucketType.User)] + public async Task DoStats(CommandContext ctx, [Description("Number of messages to check")] int numMessages) { await GenerateStats(ctx, null, numMessages); } public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int numMessages) { - UtilityFunctions.LogUserCommand(ctx); - - DateTime start = DateTime.Now; - if (numMessages > 2000) numMessages = 2000; + Utils.LogUserCommand(ctx); + try { + DateTime start = DateTime.Now; + if (numMessages > 2000) numMessages = 2000; + + ulong[] channelIDs; + string[] channelNames; + if (channel == null) { + channelIDs = defChannelIDs; + channelNames = defChannelNames; + } + else { + channelIDs = new ulong[] { channel.Id }; + channelNames = new string[] { channel.Name }; + } - ulong[] channelIDs; - string[] channelNames; - if (channel == null) { - channelIDs = defChannelIDs; - channelNames = defChannelNames; - } - else { - channelIDs = new ulong[]{ channel.Id }; - channelNames = new string[] { channel.Name }; - } + DiscordGuild g = ctx.Guild; + string title = Utils.GetEmojiSnowflakeID(Utils.GetEmoji(EmojiEnum.UnitedProgramming)) + " United Programming Statistics"; + string description = " ---- Fetching data 0/" + channelIDs.Length + " ---- Channel " + channelNames[0] + " ---- "; - DiscordGuild g = ctx.Guild; - string title = UtilityFunctions.GetEmojiSnowflakeID(UtilityFunctions.GetEmoji(EmojiEnum.UnitedProgramming)) + " United Programming Statistics"; - string description = " ---- Fetching data 0/" + channelIDs.Length + " ---- Channel " + channelNames[0] + " ---- "; - - var e = UtilityFunctions.BuildEmbed(title, description, DiscordColor.Black); - int fieldPos = e.Fields.Count - 1; - DiscordMessage m = await UtilityFunctions.LogEmbed(e, ctx, true); + var e = Utils.BuildEmbed(title, description, DiscordColor.Black); + int fieldPos = e.Fields.Count - 1; + DiscordMessage m = await Utils.LogEmbed(e, ctx, true); - int step = 0; - try { + int step = 0; await Task.Delay(120); Dictionary mentioners = new Dictionary(); Dictionary mentioneds = new Dictionary(); @@ -164,25 +162,20 @@ public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int double time = (DateTime.Now - start).TotalMilliseconds / 1000; if (channelIDs.Length == 1) - e.WithFooter("Statistics from channel " + channelNames[0] + " and " + numMessages + " messages per channel.\nGenerated in " + time.ToString("N1") + " seconds"); + e.WithFooter("Statistics from channel " + channelNames[0] + " and " + numMessages + " messages per channel.\nGenerated in " + time.ToString("N1") + " seconds"); else e.WithFooter("Statistics from " + channelIDs.Length + " channels and " + numMessages + " messages per channel.\nGenerated in " + time.ToString("N1") + " seconds"); + await m.ModifyAsync(e.Build()); } catch (Exception ex) { - + await ctx.RespondAsync(Utils.GenerateErrorAnswer("Stats", ex)); } - await m.ModifyAsync(e.Build()); } public class Mentioner { public ulong member; public int num; - } - const ulong idUnityChannel = 830904407540367441ul; - const ulong idCSharpChannel = 830904726375628850ul; - const ulong idHelp1Channel = 830921265648631878ul; - const ulong idHelp2Channel = 830921315657449472ul; } diff --git a/UPBot Code/Commands/Version.cs b/UPBot Code/Commands/Version.cs new file mode 100644 index 0000000..69cc8d1 --- /dev/null +++ b/UPBot Code/Commands/Version.cs @@ -0,0 +1,27 @@ +using DSharpPlus.CommandsNext; +using DSharpPlus.CommandsNext.Attributes; +using DSharpPlus.Entities; +using System; +using System.Threading.Tasks; +/// +/// This command implements a WhoIs command. +/// It gives info about a Discord User or yourself +/// author: CPU +/// +public class Version : BaseCommandModule { + + [Command("Version")] + [Description("Get version information about the bot.")] + public async Task VersionCommand(CommandContext ctx) { + DiscordMember cpu = ctx.Guild.GetMemberAsync(231753250687287296ul).Result; + DiscordMember duck = ctx.Guild.GetMemberAsync(411526180873961494ul).Result; + DiscordMember eremiell = ctx.Guild.GetMemberAsync(340661564556312591ul).Result; + DiscordMember slice = ctx.Guild.GetMemberAsync(486133356858310656ul).Result; + DiscordMember jonathan = ctx.Guild.GetMemberAsync(608994148313333763ul).Result; + + await ctx.Message.RespondAsync(Utils.BuildEmbed("United Programming Bot", + "**Version**: " + Utils.GetVersion() + "\n\nContributors: " + + cpu.Mention + ", " + duck.Mention + ", " + eremiell.Mention + ", " + slice.Mention + ", " + jonathan.Mention + + "\n\nCode available on https://github.com/United-Programming/UPBot/", Utils.Yellow).Build()); + } +} \ No newline at end of file diff --git a/UPBot Code/Commands/WhoIs.cs b/UPBot Code/Commands/WhoIs.cs index 4d14048..4e238e2 100644 --- a/UPBot Code/Commands/WhoIs.cs +++ b/UPBot Code/Commands/WhoIs.cs @@ -17,87 +17,91 @@ public async Task WhoIsCommand(CommandContext ctx) { // Basic version without pa await GenerateWhoIs(ctx, null); } + [Command("whois")] + public async Task WhoIsCommand(CommandContext ctx, [Description("The user to get info from.")] DiscordMember member) { // Standard version with a user + await GenerateWhoIs(ctx, member); + } + [Command("whoami")] [Description("Get information about your own Discord account.")] public async Task WhoAmICommand(CommandContext ctx) { // Alternate version without parameters await GenerateWhoIs(ctx, null); } - [Command("whois")] - public async Task WhoIsCommand(CommandContext ctx, DiscordMember member) { // Standard version with a user - await GenerateWhoIs(ctx, member); - } - private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { - UtilityFunctions.LogUserCommand(ctx); - if (m == null) { // If we do not have a user we use the member that invoked the command - m = ctx.Member; - } - bool you = m == ctx.Member; - - DateTimeOffset jdate = m.JoinedAt.UtcDateTime; - string joined = jdate.Year + "/" + jdate.Month + "/" + jdate.Day; - DateTimeOffset cdate = m.CreationTimestamp.UtcDateTime; - string creation = cdate.Year + "/" + cdate.Month + "/" + cdate.Day; + Utils.LogUserCommand(ctx); + try { + if (m == null) { // If we do not have a user we use the member that invoked the command + m = ctx.Member; + } + bool you = m == ctx.Member; - int daysJ = (int)(DateTime.Now - m.JoinedAt.DateTime).TotalDays; - int daysA = (int)(DateTime.Now - m.CreationTimestamp.DateTime).TotalDays; - double years = daysA / 365.25; + DateTimeOffset jdate = m.JoinedAt.UtcDateTime; + string joined = jdate.Year + "/" + jdate.Month + "/" + jdate.Day; + DateTimeOffset cdate = m.CreationTimestamp.UtcDateTime; + string creation = cdate.Year + "/" + cdate.Month + "/" + cdate.Day; - string title = "Who is the user " + m.DisplayName + "#" + m.Discriminator; - string description = m.Username + " joined on " + joined + " (" + daysJ + " days)\n Account created on " + - creation + " (" + daysA + " days, " + years.ToString("N1") + " years)"; - var embed = UtilityFunctions.BuildEmbed(title, description, m.Color); - embed.WithThumbnail(m.AvatarUrl, 64, 64); + int daysJ = (int)(DateTime.Now - m.JoinedAt.DateTime).TotalDays; + int daysA = (int)(DateTime.Now - m.CreationTimestamp.DateTime).TotalDays; + double years = daysA / 365.25; - embed.AddField("Is you", you ? "✓" : "❌", true); - embed.AddField("Is a bot", m.IsBot ? "🤖" : "❌", true); - embed.AddField("Is the boss", m.IsOwner ? "👑" : "❌", true); - embed.AddField("Is Muted", m.IsMuted ? "✓" : "❌", true); - embed.AddField("Is Deafened", m.IsDeafened ? "✓" : "❌", true); + string title = "Who is the user " + m.DisplayName + "#" + m.Discriminator; + string description = m.Username + " joined on " + joined + " (" + daysJ + " days)\n Account created on " + + creation + " (" + daysA + " days, " + years.ToString("N1") + " years)"; + var embed = Utils.BuildEmbed(title, description, m.Color); + embed.WithThumbnail(m.AvatarUrl, 64, 64); - if (m.Locale != null) embed.AddField("Speaks", m.Locale, true); - if (m.Nickname != null) embed.AddField("Is called", m.Nickname, true); - embed.AddField("Avatar Hex Color", m.Color.ToString(), true); + embed.AddField("Is you", you ? "✓" : "❌", true); + embed.AddField("Is a bot", m.IsBot ? "🤖" : "❌", true); + embed.AddField("Is the boss", m.IsOwner ? "👑" : "❌", true); + embed.AddField("Is Muted", m.IsMuted ? "✓" : "❌", true); + embed.AddField("Is Deafened", m.IsDeafened ? "✓" : "❌", true); - if (m.PremiumSince != null) { - DateTimeOffset bdate = ((DateTimeOffset)m.PremiumSince).UtcDateTime; - string booster = bdate.Year + "/" + bdate.Month + "/" + bdate.Day; - embed.AddField("Booster", "From " + booster, true); - } - if (m.Flags != null) embed.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags + if (m.Locale != null) embed.AddField("Speaks", m.Locale, true); + if (m.Nickname != null) embed.AddField("Is called", m.Nickname, true); + embed.AddField("Avatar Hex Color", m.Color.ToString(), true); - string roles = ""; - int num = 0; - foreach (DiscordRole role in m.Roles) { - roles += role.Mention + " "; - num++; - } - if (num == 1) - embed.AddField("Role", roles, false); - else - embed.AddField(num + " Roles", roles, false); + if (m.PremiumSince != null) { + DateTimeOffset bdate = ((DateTimeOffset)m.PremiumSince).UtcDateTime; + string booster = bdate.Year + "/" + bdate.Month + "/" + bdate.Day; + embed.AddField("Booster", "From " + booster, true); + } + if (m.Flags != null) embed.AddField("Flags", m.Flags.ToString(), true); // Only the default flags will be shown. This bot will not be very diffused so probably we do not need specific checks for flags + + string roles = ""; + int num = 0; + foreach (DiscordRole role in m.Roles) { + roles += role.Mention + " "; + num++; + } + if (num == 1) + embed.AddField("Role", roles, false); + else + embed.AddField(num + " Roles", roles, false); - string perms = ""; // Not all permissions are shown - if (m.Permissions.HasFlag(DSharpPlus.Permissions.CreateInstantInvite)) perms += ", Invite"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.KickMembers)) perms += ", Kick"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.BanMembers)) perms += ", Ban"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.Administrator)) perms += ", Admin"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageChannels)) perms += ", Manage Channels"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageGuild)) perms += ", Manage Server"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.AddReactions)) perms += ", Reactions"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ViewAuditLog)) perms += ", Audit"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageMessages)) perms += ", Manage Messages"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.EmbedLinks)) perms += ", Links"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.AttachFiles)) perms += ", Files"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseExternalEmojis)) perms += ", Ext Emojis"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.Speak)) perms += ", Speak"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageRoles)) perms += ", Manage Roles"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageEmojis)) perms += ", Manage Emojis"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseSlashCommands)) perms += ", Use Bot"; - if (m.Permissions.HasFlag(DSharpPlus.Permissions.UsePublicThreads)) perms += ", Use Threads"; - if (perms.Length > 0) embed.AddField("Permissions", perms.Substring(2), false); + string perms = ""; // Not all permissions are shown + if (m.Permissions.HasFlag(DSharpPlus.Permissions.CreateInstantInvite)) perms += ", Invite"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.KickMembers)) perms += ", Kick"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.BanMembers)) perms += ", Ban"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.Administrator)) perms += ", Admin"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageChannels)) perms += ", Manage Channels"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageGuild)) perms += ", Manage Server"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.AddReactions)) perms += ", Reactions"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ViewAuditLog)) perms += ", Audit"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageMessages)) perms += ", Manage Messages"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.EmbedLinks)) perms += ", Links"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.AttachFiles)) perms += ", Files"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseExternalEmojis)) perms += ", Ext Emojis"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.Speak)) perms += ", Speak"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageRoles)) perms += ", Manage Roles"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.ManageEmojis)) perms += ", Manage Emojis"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.UseSlashCommands)) perms += ", Use Bot"; + if (m.Permissions.HasFlag(DSharpPlus.Permissions.UsePublicThreads)) perms += ", Use Threads"; + if (perms.Length > 0) embed.AddField("Permissions", perms.Substring(2), false); - return ctx.RespondAsync(embed.Build()); + return ctx.RespondAsync(embed.Build()); + } catch (Exception ex) { + return ctx.RespondAsync(Utils.GenerateErrorAnswer("WhoIs", ex)); + } } } \ No newline at end of file diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index 2b20291..1544434 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -23,8 +23,8 @@ static async Task MainAsync(string token, string prefix) { }); CustomCommandsService.DiscordClient = discord; - UtilityFunctions.InitClient(discord); - var commands = discord.UseCommandsNext(new CommandsNextConfiguration() { + Utils.InitClient(discord); + CommandsNextExtension commands = discord.UseCommandsNext(new CommandsNextConfiguration() { StringPrefixes = new[] { prefix[0].ToString() } // The backslash will be the default command prefix if not specified in the parameters }); commands.CommandErrored += CustomCommandsService.CommandError; @@ -36,7 +36,7 @@ static async Task MainAsync(string token, string prefix) { await CustomCommandsService.LoadCustomCommands(); await discord.ConnectAsync(); // Connects and wait forever - UtilityFunctions.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd")); + Utils.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd") + " --------------------------------"); AppreciationTracking.Init(); discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; diff --git a/UPBot Code/UtilityFunctions.cs b/UPBot Code/Utils.cs similarity index 81% rename from UPBot Code/UtilityFunctions.cs rename to UPBot Code/Utils.cs index 9727cc8..5145f56 100644 --- a/UPBot Code/UtilityFunctions.cs +++ b/UPBot Code/Utils.cs @@ -2,6 +2,7 @@ using DSharpPlus.CommandsNext; using DSharpPlus.Entities; using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading.Tasks; @@ -10,7 +11,7 @@ /// Utility functions that don't belong to a specific class or a specific command /// "General-purpose" function, which can be needed anywhere. /// -public static class UtilityFunctions +public static class Utils { /// /// Common colors @@ -27,15 +28,23 @@ public static class UtilityFunctions private static DiscordMember mySelf; private static DiscordGuild guild; - public static void SetMyself(DiscordClient c) { - + public static string GetVersion() { + return "0.1 beta - 2021/08/19"; } + /// + /// Returns the Bot user as Member of the United Programming Guild + /// + /// public static DiscordMember GetMyself() { if (mySelf==null) mySelf = client.Guilds[830900174553481236ul].CurrentMember; return mySelf; } + /// + /// Gets the UNitedProgramming Guild + /// + /// public static DiscordGuild GetGuild() { if (guild != null) return guild; while (client == null) Task.Delay(1000); @@ -132,6 +141,21 @@ public static async Task BuildEmbedAndExecute(string title, stri return await LogEmbed(embedBuilder, ctx, respond); } + /// + /// Quick shortcut to generate an error message + /// + /// The error to display + /// + internal static DiscordEmbed GenerateErrorAnswer(string cmd, Exception exception) { + DiscordEmbedBuilder e = new DiscordEmbedBuilder { + Color = Red, + Title = "Error in " + cmd, + Description = exception.Message + }; + Log("Error in " + cmd + ": " + exception.Message); + return e.Build(); + } + /// /// Logs an embed as a message in the relevant channel /// @@ -208,7 +232,7 @@ internal static void LogUserCommand(CommandContext ctx) { /// /// internal static void Log(string msg) { - Console.WriteLine(DateTime.Now.ToString(sortableDateTimeFormat.SortableDateTimePattern) + "=> " + msg); + Console.WriteLine(msg); try { logs.WriteLine(msg); logs.FlushAsync(); @@ -255,8 +279,50 @@ internal static async Task ErrorCallback(CommandErrors error, CommandContext ctx break; } - await UtilityFunctions.BuildEmbedAndExecute("Error", message, red, ctx, respond); + await Utils.BuildEmbedAndExecute("Error", message, red, ctx, respond); + } + + /// + /// Used to delete some messages after a while + /// + /// + public static Task DeleteDelayed(int seconds, DiscordMessage msg1) { + Task.Run(() => DelayAfterAWhile(msg1, seconds * 1000)); + return Task.FromResult(0); + } + + /// + /// Used to delete some messages after a while + /// + /// + /// + public static Task DeleteDelayed(int seconds, DiscordMessage msg1, DiscordMessage msg2) { + Task.Run(() => DelayAfterAWhile(msg1, seconds * 1000)); + Task.Run(() => DelayAfterAWhile(msg2, seconds * 1000)); + return Task.FromResult(0); } + + /// + /// Used to delete some messages after a while + /// + /// + /// + /// + public static Task DeleteDelayed(int seconds, DiscordMessage msg1, DiscordMessage msg2, DiscordMessage msg3) { + Task.Run(() => DelayAfterAWhile(msg1, seconds * 1000)); + Task.Run(() => DelayAfterAWhile(msg2, seconds * 1000)); + Task.Run(() => DelayAfterAWhile(msg3, seconds * 1000)); + return Task.FromResult(0); + } + + static void DelayAfterAWhile(DiscordMessage msg, int delay) { + try { + Task.Delay(delay).Wait(); + msg.DeleteAsync().Wait(); + } catch (Exception) { } + } + + } public enum EmojiEnum { From 19c6243bdac45a01e89c5804fc601971f5ca48a6 Mon Sep 17 00:00:00 2001 From: CPULL Date: Thu, 19 Aug 2021 15:48:36 +0200 Subject: [PATCH 36/54] =?UTF-8?q?=F0=9F=92=A5=20Started=20partitioning=20c?= =?UTF-8?q?lasses=20to=20move=20to=20SQLLite=20-=20SQL=20Lite=20Nuget=20pa?= =?UTF-8?q?ckage=20from=20Microsoft=20is=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ToDo.txt | 10 +- UPBot Code/Actions/AppreciationTracking.cs | 321 ------------------ UPBot Code/Commands/HelpLanguages.cs | 19 -- UPBot Code/Commands/Stats.cs | 6 - UPBot Code/DataClasses/BannedWord.cs | 25 ++ UPBot Code/DataClasses/CustomCommand.cs | 35 ++ UPBot Code/DataClasses/EmojisForRole.cs | 186 ++++++++++ UPBot Code/DataClasses/LanguageInfo.cs | 13 + UPBot Code/DataClasses/LastRequestByMember.cs | 7 + UPBot Code/DataClasses/Mentioner.cs | 4 + UPBot Code/DataClasses/Reputation.cs | 8 + UPBot Code/DataClasses/ReputationTracking.cs | 136 ++++++++ UPBot Code/Utils.cs | 2 +- 13 files changed, 420 insertions(+), 352 deletions(-) create mode 100644 UPBot Code/DataClasses/BannedWord.cs create mode 100644 UPBot Code/DataClasses/CustomCommand.cs create mode 100644 UPBot Code/DataClasses/EmojisForRole.cs create mode 100644 UPBot Code/DataClasses/LanguageInfo.cs create mode 100644 UPBot Code/DataClasses/LastRequestByMember.cs create mode 100644 UPBot Code/DataClasses/Mentioner.cs create mode 100644 UPBot Code/DataClasses/Reputation.cs create mode 100644 UPBot Code/DataClasses/ReputationTracking.cs diff --git a/ToDo.txt b/ToDo.txt index 176e056..a85b07d 100644 --- a/ToDo.txt +++ b/ToDo.txt @@ -1,11 +1,11 @@ Have command to alter the command character [NOT POSSIBLE!] -Check all command names +!Check all command names Add wikis for each command on GitHub -Uniform all error messages with Embeds (use Duck code) -Have all messages and commands to disappear after a while (configurable) if not needed to stay +!Uniform all error messages with Embeds (use Duck code) +!Have all messages and commands to disappear after a while (configurable) if not needed to stay Have a cmd to configure how long messages should stay -Add TryCatch to all code and commands - +!Add TryCatch to all code and commands +Add "keep" option to reformat Errors DelAnsw EmbedRes TryCatch Log@Begin MemberTracking | | | | X | | diff --git a/UPBot Code/Actions/AppreciationTracking.cs b/UPBot Code/Actions/AppreciationTracking.cs index 8401a28..8034cf5 100644 --- a/UPBot Code/Actions/AppreciationTracking.cs +++ b/UPBot Code/Actions/AppreciationTracking.cs @@ -250,325 +250,4 @@ static async Task SaveDelayedAsync() { } - public class Reputation { // 16 bytes - public ulong user; // 8 - public ushort reputation; // 2 - public ushort fun; // 2 - public DateTime startTracking; // 4 - } - - public class ReputationTracking { - readonly DateTime trackingStarted; - readonly Dictionary dic; - readonly string path = null; - - public ReputationTracking(string path) { - try { - this.path = path; - dic = new Dictionary(); - if (!File.Exists(path)) { - trackingStarted = DateTime.Now; - return; - } - byte[] data = new byte[20]; - using (FileStream f = new FileStream(path, FileMode.Open)) { - // 32 bits for the date (ymd) - if (f.Read(data, 0, 4) < 4) { - Utils.Log("ERROR: wrong Reputation file: " + path); - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); - } - return; - } - trackingStarted = GetDateFromBytes(data, 0); - while (f.Read(data, 0, 16) == 16) { - ulong usrid = BitConverter.ToUInt64(data); - ushort rep = BitConverter.ToUInt16(data, 8); - ushort fun = BitConverter.ToUInt16(data, 10); - DateTime start = GetDateFromBytes(data, 12); - dic[usrid] = new Reputation { user = usrid, reputation = rep, fun = fun, startTracking = start }; - } - } - Utils.Log("ReputationTracking: Loaded " + dic.Count + " users"); - } catch (Exception e) { - Utils.Log("ERROR: problems in loading the Reputation file: " + e.Message); - } - } - - public void Save() { - try { - lock (dic) { - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); - return; - } - using (FileStream f = new FileStream(path, FileMode.CreateNew)) { - byte[] data = new byte[16]; - SetDateToBytes(trackingStarted, data, 0); - f.Write(data, 0, 4); - foreach (Reputation r in dic.Values) { - byte[] d = BitConverter.GetBytes(r.user); - int pos = 0; - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - d = BitConverter.GetBytes(r.reputation); - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - d = BitConverter.GetBytes(r.fun); - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - SetDateToBytes(r.startTracking, data, pos); - f.Write(data, 0, 16); - } - f.Flush(); - } - } - Utils.Log("ReputationTracking: Saved " + dic.Count + " users"); - } catch (Exception e) { - Utils.Log("ERROR: problems in saving the Reputation file: " + e.Message); - } - } - - private void SetDateToBytes(DateTime d, byte[] data, int offset) { - data[offset + 0] = (byte)((d.Year & 0xff00) >> 8); - data[offset + 1] = (byte)(d.Year & 0xff); - data[offset + 2] = (byte)(d.Month & 0xff); - data[offset + 3] = (byte)(d.Day & 0xff); - } - - private DateTime GetDateFromBytes(byte[] data, int offset) { - try { - return new DateTime((data[offset + 0] << 8) + data[offset + 1], data[offset + 2], data[offset + 3]); - } catch (Exception) { - return DateTime.Now; - } - } - - public bool AlterRep(ulong id, bool add) { - if (add) { - if (dic.ContainsKey(id)) dic[id].reputation++; - else { - dic.Add(id, new Reputation { user = id, reputation = 1, fun = 0, startTracking = DateTime.Now }); - } - return true; - } - else { - if (dic.ContainsKey(id) && dic[id].reputation > 0) { - dic[id].reputation--; - return true; - } - } - return false; - } - public bool AlterFun(ulong id, bool add) { - if (add) { - if (dic.ContainsKey(id)) dic[id].fun++; - else { - dic.Add(id, new Reputation { user = id, reputation = 0, fun = 1, startTracking = DateTime.Now }); - } - return true; - } - else { - if (dic.ContainsKey(id) && dic[id].fun > 0) { - dic[id].fun--; - return true; - } - } - return false; - } - - internal string GetStartDate() { - return trackingStarted.ToString("yyyy/MM/dd"); - } - - internal IEnumerable GetReputation() { - return dic.Values; - } - } - - public class EmojisForRole { - List values = null; - string path = null; - - public EmojisForRole(string path) { - try { - values = new List(); - this.path = path; - if (!File.Exists(path)) return; - - DiscordGuild g = Utils.GetGuild(); - byte[] data = new byte[36]; - using (FileStream f = new FileStream(path, FileMode.Open)) { - while (f.Read(data, 0, 36) == 36) { - ReactionValue v = new ReactionValue { - channel = BitConverter.ToUInt64(data, 0), - message = BitConverter.ToUInt64(data, 8), - role = BitConverter.ToUInt64(data, 16), - emojiId = BitConverter.ToUInt64(data, 24), - emojiName = Char.ConvertFromUtf32(BitConverter.ToInt32(data, 32)), - dRole = null - }; - - DiscordMessage m; - try { - // Does the message exists? - DiscordChannel c = g.GetChannel(v.channel); - if (c == null || c.Id == 0) continue; // Bad - m = c.GetMessageAsync(v.message).Result; - if (m == null || m.Id == 0) continue; // Bad; - // Does the role exists? - DiscordRole r = g.GetRole(v.role); - if (r == null || r.Id == 0) continue; // Bad - } catch (Exception ex) { - Utils.Log("Error while checking a ReactionValue: " + ex.Message); - continue; - } - // Check that the message has the required emojis, if not add it - DiscordEmoji e = null; - if (v.emojiId != 0) { - e = g.GetEmojiAsync(v.emojiId).Result; - } - else if (v.emojiName != "!") { - e = DiscordEmoji.FromUnicode(v.emojiName); - } - if (e == null) continue; // Bad - if (m.GetReactionsAsync(e, 1).Result.Count == 0) { // Need to add - m.CreateReactionAsync(e).Wait(); - } - - values.Add(v); - } - } - Utils.Log("Loaded " + values.Count + " tracked messages for emojis."); - } catch (Exception e) { - Utils.Log("ERROR: problems in loading the EmojiForRole file: " + e.Message); - } - } - - public void Save() { - try { - lock (values) { - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - Utils.Log("ERROR: cannot delete old EmojisForRole file: " + path + "\nException: " + e.Message); - return; - } - using (FileStream f = new FileStream(path, FileMode.CreateNew)) { - byte[] data = new byte[36]; - foreach (ReactionValue v in values) { - int pos = 0; - byte[] d = BitConverter.GetBytes(v.channel); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.message); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.role); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.emojiId); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - if (v.emojiId != 0 || v.emojiName == null || v.emojiName.Length == 0) - for (int i = 0; i < 4; i++) data[pos++] = 0; - else { - d = BitConverter.GetBytes(Char.ConvertToUtf32(v.emojiName, 0)); - for (int i = 0; i < 4; i++) data[pos++] = d[i]; - } - f.Write(data, 0, pos); - } - f.Flush(); - } - } - Utils.Log("EmojisForRole: Saved " + values.Count + " tracked messages and emojis"); - } catch (Exception e) { - Utils.Log("ERROR: problems in saving the EmojiForRole file: " + e.Message); - } - } - - internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji) { - bool here = false; - ReactionValue toRemove = null; - foreach (ReactionValue v in values) { - if (v.channel == msg.ChannelId && v.message == msg.Id && v.role == role.Id) { - if ((emoji == null && v.emojiId == 0 && v.emojiName == "!") || - (emoji != null && ((emoji.Id != 0 && emoji.Id == v.emojiId) || (emoji.Id == 0 && emoji.Name.Equals(v.emojiName))))) { - toRemove = v; // Remove - break; - } - } - } - if (toRemove != null) { - values.Remove(toRemove); - } - else { - here = true; - ReactionValue v = new ReactionValue { - channel = msg.ChannelId, - message = msg.Id, - role = role.Id, - emojiId = emoji == null ? 0 : emoji.Id, - emojiName = emoji == null ? "!" : emoji.Name, - dRole = role - }; - values.Add(v); - - if (emoji != null) { - // Check if we have the emoji already, if not add it - if (msg.GetReactionsAsync(emoji, 1).Result.Count == 0) { - msg.CreateReactionAsync(emoji).Wait(); - } - } - - } - Save(); - return here; - } - - internal void HandleAddingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { - try { - DiscordMember dm = (DiscordMember)user; - if (dm == null) return; // Not a valid member for the Guild/Context - if (values == null) return; - foreach (ReactionValue v in values) { - if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { - if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); - dm.GrantRoleAsync(v.dRole).Wait(); - Utils.Log("Role " + v.dRole.Name + " was granted to " + user.Username + " by emoji " + eId); - return; - } - } - } catch (Exception e) { - Utils.Log("ERROR: problems in HandleAddingEmojiForRole: " + e.Message); - } - } - - internal void HandleRemovingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { - try { - DiscordMember dm = (DiscordMember)user; - if (dm == null) return; // Not a valid member for the Guild/Context - if (values == null) return; - foreach (ReactionValue v in values) { - if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { - if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); - dm.RevokeRoleAsync(v.dRole).Wait(); - Utils.Log("Role " + v.dRole.Name + " was removed from " + user.Username + " by emoji " + eId); - return; - } - } - } catch (Exception e) { - Utils.Log("ERROR: problems in HandleRemovingEmojiForRole: " + e.Message); - } - } - - public class ReactionValue { - public ulong channel; // 8 bytes - public ulong message; // 8 bytes - public ulong role; // 8 bytes - public ulong emojiId; // 8 bytes - public string emojiName; // 4 bytes (utf32, 1 character) - public DiscordRole dRole; - } - } } diff --git a/UPBot Code/Commands/HelpLanguages.cs b/UPBot Code/Commands/HelpLanguages.cs index 58f8537..37a43e2 100644 --- a/UPBot Code/Commands/HelpLanguages.cs +++ b/UPBot Code/Commands/HelpLanguages.cs @@ -146,23 +146,4 @@ private async Task GenerateHelpfulAnswer(CommandContext ctx, string language, bo } } - private class LastRequestByMember { - public ulong MemberId { get; set; } - public DateTime DateTime { get; set; } - public int Num { get; set; } - } - - private class LanguageInfo - { - public LanguageInfo(string videoLink, string courseLink, string colorHex) - { - this.VideoLink = videoLink; - this.CourseLink = courseLink; - this.Color = new DiscordColor(colorHex); - } - - internal readonly string VideoLink; - internal readonly string CourseLink; - internal readonly DiscordColor Color; - } } diff --git a/UPBot Code/Commands/Stats.cs b/UPBot Code/Commands/Stats.cs index 3923e10..2a85cba 100644 --- a/UPBot Code/Commands/Stats.cs +++ b/UPBot Code/Commands/Stats.cs @@ -172,10 +172,4 @@ public async Task GenerateStats(CommandContext ctx, DiscordChannel channel, int } } - public class Mentioner { - public ulong member; - public int num; - } - - } diff --git a/UPBot Code/DataClasses/BannedWord.cs b/UPBot Code/DataClasses/BannedWord.cs new file mode 100644 index 0000000..dc5971c --- /dev/null +++ b/UPBot Code/DataClasses/BannedWord.cs @@ -0,0 +1,25 @@ +using System; + +public class BannedWord { + public string word; + public ulong creator = 0; + public DateTime date = DateTime.MinValue; + + public BannedWord(string w, ulong id) { + word = w; + creator = id; + date = DateTime.Now; + } + + public BannedWord(string line) { + string[] parts = line.Trim(' ', '\r', '\n').Split('\t'); + if (parts.Length != 3) return; + word = parts[0].Trim(' ', '\r', '\n', '\t').ToLowerInvariant(); + ulong.TryParse(parts[1].Trim(' ', '\r', '\n', '\t'), out creator); + DateTime.TryParse(parts[2].Trim(' ', '\r', '\n', '\t'), out date); + } + + public override string ToString() { + return word + "\t" + creator + "\t" + date + "\n"; + } +} \ No newline at end of file diff --git a/UPBot Code/DataClasses/CustomCommand.cs b/UPBot Code/DataClasses/CustomCommand.cs new file mode 100644 index 0000000..413b77f --- /dev/null +++ b/UPBot Code/DataClasses/CustomCommand.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using DSharpPlus.CommandsNext; + +/// +/// Holds information about a CustomCommand +/// and contains functions which execute or edit the command +/// +public class CustomCommand +{ + public CustomCommand(string[] names, string content) + { + this.Names = names; + this.FilePath = Utils.ConstructPath(CustomCommandsService.DirectoryNameCC, names[0], ".txt"); + this.Content = content; + } + + internal string[] Names { get; private set; } + internal string FilePath { get; } + internal string Content { get; private set; } + + internal async Task ExecuteCommand(CommandContext ctx) + { + await ctx.Channel.SendMessageAsync(Content); + } + + internal void EditCommand(string newContent) + { + this.Content = newContent; + } + + internal void EditCommand(string[] newNames) + { + this.Names = newNames; + } +} \ No newline at end of file diff --git a/UPBot Code/DataClasses/EmojisForRole.cs b/UPBot Code/DataClasses/EmojisForRole.cs new file mode 100644 index 0000000..3104384 --- /dev/null +++ b/UPBot Code/DataClasses/EmojisForRole.cs @@ -0,0 +1,186 @@ +using DSharpPlus.Entities; +using System; +using System.Collections.Generic; +using System.IO; + +public class EmojisForRole { + List values = null; + string path = null; + + public EmojisForRole(string path) { + try { + values = new List(); + this.path = path; + if (!File.Exists(path)) return; + + DiscordGuild g = Utils.GetGuild(); + byte[] data = new byte[36]; + using (FileStream f = new FileStream(path, FileMode.Open)) { + while (f.Read(data, 0, 36) == 36) { + ReactionValue v = new ReactionValue { + channel = BitConverter.ToUInt64(data, 0), + message = BitConverter.ToUInt64(data, 8), + role = BitConverter.ToUInt64(data, 16), + emojiId = BitConverter.ToUInt64(data, 24), + emojiName = Char.ConvertFromUtf32(BitConverter.ToInt32(data, 32)), + dRole = null + }; + + DiscordMessage m; + try { + // Does the message exists? + DiscordChannel c = g.GetChannel(v.channel); + if (c == null || c.Id == 0) continue; // Bad + m = c.GetMessageAsync(v.message).Result; + if (m == null || m.Id == 0) continue; // Bad; + // Does the role exists? + DiscordRole r = g.GetRole(v.role); + if (r == null || r.Id == 0) continue; // Bad + } catch (Exception ex) { + Utils.Log("Error while checking a ReactionValue: " + ex.Message); + continue; + } + // Check that the message has the required emojis, if not add it + DiscordEmoji e = null; + if (v.emojiId != 0) { + e = g.GetEmojiAsync(v.emojiId).Result; + } + else if (v.emojiName != "!") { + e = DiscordEmoji.FromUnicode(v.emojiName); + } + if (e == null) continue; // Bad + if (m.GetReactionsAsync(e, 1).Result.Count == 0) { // Need to add + m.CreateReactionAsync(e).Wait(); + } + + values.Add(v); + } + } + Utils.Log("Loaded " + values.Count + " tracked messages for emojis."); + } catch (Exception e) { + Utils.Log("ERROR: problems in loading the EmojiForRole file: " + e.Message); + } + } + + public void Save() { + try { + lock (values) { + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old EmojisForRole file: " + path + "\nException: " + e.Message); + return; + } + using (FileStream f = new FileStream(path, FileMode.CreateNew)) { + byte[] data = new byte[36]; + foreach (ReactionValue v in values) { + int pos = 0; + byte[] d = BitConverter.GetBytes(v.channel); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.message); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.role); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + d = BitConverter.GetBytes(v.emojiId); + for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; + if (v.emojiId != 0 || v.emojiName == null || v.emojiName.Length == 0) + for (int i = 0; i < 4; i++) data[pos++] = 0; + else { + d = BitConverter.GetBytes(Char.ConvertToUtf32(v.emojiName, 0)); + for (int i = 0; i < 4; i++) data[pos++] = d[i]; + } + f.Write(data, 0, pos); + } + f.Flush(); + } + } + Utils.Log("EmojisForRole: Saved " + values.Count + " tracked messages and emojis"); + } catch (Exception e) { + Utils.Log("ERROR: problems in saving the EmojiForRole file: " + e.Message); + } + } + + internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji) { + bool here = false; + ReactionValue toRemove = null; + foreach (ReactionValue v in values) { + if (v.channel == msg.ChannelId && v.message == msg.Id && v.role == role.Id) { + if ((emoji == null && v.emojiId == 0 && v.emojiName == "!") || + (emoji != null && ((emoji.Id != 0 && emoji.Id == v.emojiId) || (emoji.Id == 0 && emoji.Name.Equals(v.emojiName))))) { + toRemove = v; // Remove + break; + } + } + } + if (toRemove != null) { + values.Remove(toRemove); + } + else { + here = true; + ReactionValue v = new ReactionValue { + channel = msg.ChannelId, + message = msg.Id, + role = role.Id, + emojiId = emoji == null ? 0 : emoji.Id, + emojiName = emoji == null ? "!" : emoji.Name, + dRole = role + }; + values.Add(v); + + if (emoji != null) { + // Check if we have the emoji already, if not add it + if (msg.GetReactionsAsync(emoji, 1).Result.Count == 0) { + msg.CreateReactionAsync(emoji).Wait(); + } + } + + } + Save(); + return here; + } + + internal void HandleAddingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { + try { + DiscordMember dm = (DiscordMember)user; + if (dm == null) return; // Not a valid member for the Guild/Context + if (values == null) return; + foreach (ReactionValue v in values) { + if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + dm.GrantRoleAsync(v.dRole).Wait(); + Utils.Log("Role " + v.dRole.Name + " was granted to " + user.Username + " by emoji " + eId); + return; + } + } + } catch (Exception e) { + Utils.Log("ERROR: problems in HandleAddingEmojiForRole: " + e.Message); + } + } + + internal void HandleRemovingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { + try { + DiscordMember dm = (DiscordMember)user; + if (dm == null) return; // Not a valid member for the Guild/Context + if (values == null) return; + foreach (ReactionValue v in values) { + if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + dm.RevokeRoleAsync(v.dRole).Wait(); + Utils.Log("Role " + v.dRole.Name + " was removed from " + user.Username + " by emoji " + eId); + return; + } + } + } catch (Exception e) { + Utils.Log("ERROR: problems in HandleRemovingEmojiForRole: " + e.Message); + } + } + + public class ReactionValue { + public ulong channel; // 8 bytes + public ulong message; // 8 bytes + public ulong role; // 8 bytes + public ulong emojiId; // 8 bytes + public string emojiName; // 4 bytes (utf32, 1 character) + public DiscordRole dRole; + } +} diff --git a/UPBot Code/DataClasses/LanguageInfo.cs b/UPBot Code/DataClasses/LanguageInfo.cs new file mode 100644 index 0000000..647a210 --- /dev/null +++ b/UPBot Code/DataClasses/LanguageInfo.cs @@ -0,0 +1,13 @@ +using DSharpPlus.Entities; + +public class LanguageInfo { + public LanguageInfo(string videoLink, string courseLink, string colorHex) { + this.VideoLink = videoLink; + this.CourseLink = courseLink; + this.Color = new DiscordColor(colorHex); + } + + internal readonly string VideoLink; + internal readonly string CourseLink; + internal readonly DiscordColor Color; +} \ No newline at end of file diff --git a/UPBot Code/DataClasses/LastRequestByMember.cs b/UPBot Code/DataClasses/LastRequestByMember.cs new file mode 100644 index 0000000..5b50093 --- /dev/null +++ b/UPBot Code/DataClasses/LastRequestByMember.cs @@ -0,0 +1,7 @@ +using System; + +public class LastRequestByMember { + public ulong MemberId { get; set; } + public DateTime DateTime { get; set; } + public int Num { get; set; } +} \ No newline at end of file diff --git a/UPBot Code/DataClasses/Mentioner.cs b/UPBot Code/DataClasses/Mentioner.cs new file mode 100644 index 0000000..29a3b81 --- /dev/null +++ b/UPBot Code/DataClasses/Mentioner.cs @@ -0,0 +1,4 @@ +public class Mentioner { + public ulong member; + public int num; +} \ No newline at end of file diff --git a/UPBot Code/DataClasses/Reputation.cs b/UPBot Code/DataClasses/Reputation.cs new file mode 100644 index 0000000..b60c39f --- /dev/null +++ b/UPBot Code/DataClasses/Reputation.cs @@ -0,0 +1,8 @@ +using System; + +public class Reputation { // 16 bytes + public ulong user; // 8 + public ushort reputation; // 2 + public ushort fun; // 2 + public DateTime startTracking; // 4 +} diff --git a/UPBot Code/DataClasses/ReputationTracking.cs b/UPBot Code/DataClasses/ReputationTracking.cs new file mode 100644 index 0000000..e57c4c9 --- /dev/null +++ b/UPBot Code/DataClasses/ReputationTracking.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.IO; + +public class ReputationTracking { + readonly DateTime trackingStarted; + readonly Dictionary dic; + readonly string path = null; + + public ReputationTracking(string path) { + try { + this.path = path; + dic = new Dictionary(); + if (!File.Exists(path)) { + trackingStarted = DateTime.Now; + return; + } + byte[] data = new byte[20]; + using (FileStream f = new FileStream(path, FileMode.Open)) { + // 32 bits for the date (ymd) + if (f.Read(data, 0, 4) < 4) { + Utils.Log("ERROR: wrong Reputation file: " + path); + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); + } + return; + } + trackingStarted = GetDateFromBytes(data, 0); + while (f.Read(data, 0, 16) == 16) { + ulong usrid = BitConverter.ToUInt64(data); + ushort rep = BitConverter.ToUInt16(data, 8); + ushort fun = BitConverter.ToUInt16(data, 10); + DateTime start = GetDateFromBytes(data, 12); + dic[usrid] = new Reputation { user = usrid, reputation = rep, fun = fun, startTracking = start }; + } + } + Utils.Log("ReputationTracking: Loaded " + dic.Count + " users"); + } catch (Exception e) { + Utils.Log("ERROR: problems in loading the Reputation file: " + e.Message); + } + } + + public void Save() { + try { + lock (dic) { + try { + if (File.Exists(path)) File.Delete(path); + } catch (Exception e) { + Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); + return; + } + using (FileStream f = new FileStream(path, FileMode.CreateNew)) { + byte[] data = new byte[16]; + SetDateToBytes(trackingStarted, data, 0); + f.Write(data, 0, 4); + foreach (Reputation r in dic.Values) { + byte[] d = BitConverter.GetBytes(r.user); + int pos = 0; + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + d = BitConverter.GetBytes(r.reputation); + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + d = BitConverter.GetBytes(r.fun); + for (int i = 0; i < d.Length; i++) + data[pos++] = d[i]; + SetDateToBytes(r.startTracking, data, pos); + f.Write(data, 0, 16); + } + f.Flush(); + } + } + Utils.Log("ReputationTracking: Saved " + dic.Count + " users"); + } catch (Exception e) { + Utils.Log("ERROR: problems in saving the Reputation file: " + e.Message); + } + } + + private void SetDateToBytes(DateTime d, byte[] data, int offset) { + data[offset + 0] = (byte)((d.Year & 0xff00) >> 8); + data[offset + 1] = (byte)(d.Year & 0xff); + data[offset + 2] = (byte)(d.Month & 0xff); + data[offset + 3] = (byte)(d.Day & 0xff); + } + + private DateTime GetDateFromBytes(byte[] data, int offset) { + try { + return new DateTime((data[offset + 0] << 8) + data[offset + 1], data[offset + 2], data[offset + 3]); + } catch (Exception) { + return DateTime.Now; + } + } + + public bool AlterRep(ulong id, bool add) { + if (add) { + if (dic.ContainsKey(id)) dic[id].reputation++; + else { + dic.Add(id, new Reputation { user = id, reputation = 1, fun = 0, startTracking = DateTime.Now }); + } + return true; + } + else { + if (dic.ContainsKey(id) && dic[id].reputation > 0) { + dic[id].reputation--; + return true; + } + } + return false; + } + public bool AlterFun(ulong id, bool add) { + if (add) { + if (dic.ContainsKey(id)) dic[id].fun++; + else { + dic.Add(id, new Reputation { user = id, reputation = 0, fun = 1, startTracking = DateTime.Now }); + } + return true; + } + else { + if (dic.ContainsKey(id) && dic[id].fun > 0) { + dic[id].fun--; + return true; + } + } + return false; + } + + internal string GetStartDate() { + return trackingStarted.ToString("yyyy/MM/dd"); + } + + internal IEnumerable GetReputation() { + return dic.Values; + } +} \ No newline at end of file diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index 5145f56..cbcd2ab 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -29,7 +29,7 @@ public static class Utils private static DiscordGuild guild; public static string GetVersion() { - return "0.1 beta - 2021/08/19"; + return "0.1.1 dev - 2021/08/19"; } /// From 31811b9d1e664ace37150295d6d0f85d93a6024e Mon Sep 17 00:00:00 2001 From: CPULL Date: Thu, 19 Aug 2021 16:25:03 +0200 Subject: [PATCH 37/54] Removed duplicated class CustomCommands --- UPBot Code/Commands/CustomCommand.cs | 35 ---------------------------- 1 file changed, 35 deletions(-) delete mode 100644 UPBot Code/Commands/CustomCommand.cs diff --git a/UPBot Code/Commands/CustomCommand.cs b/UPBot Code/Commands/CustomCommand.cs deleted file mode 100644 index 413b77f..0000000 --- a/UPBot Code/Commands/CustomCommand.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Threading.Tasks; -using DSharpPlus.CommandsNext; - -/// -/// Holds information about a CustomCommand -/// and contains functions which execute or edit the command -/// -public class CustomCommand -{ - public CustomCommand(string[] names, string content) - { - this.Names = names; - this.FilePath = Utils.ConstructPath(CustomCommandsService.DirectoryNameCC, names[0], ".txt"); - this.Content = content; - } - - internal string[] Names { get; private set; } - internal string FilePath { get; } - internal string Content { get; private set; } - - internal async Task ExecuteCommand(CommandContext ctx) - { - await ctx.Channel.SendMessageAsync(Content); - } - - internal void EditCommand(string newContent) - { - this.Content = newContent; - } - - internal void EditCommand(string[] newNames) - { - this.Names = newNames; - } -} \ No newline at end of file From e3a27368ae9535024ff10ca9564afcd4f04faf45 Mon Sep 17 00:00:00 2001 From: CPULL Date: Thu, 19 Aug 2021 17:16:40 +0200 Subject: [PATCH 38/54] Basic implementaton of SQL Lite Just testing --- UPBot Code/BotDbContext.cs | 22 ++++++++++++++++++++++ UPBot Code/DataClasses/HelperMember.cs | 14 ++++++++++++++ UPBot Code/Program.cs | 26 ++++++++++++++++++++++++++ UPBot.csproj | 2 ++ 4 files changed, 64 insertions(+) create mode 100644 UPBot Code/BotDbContext.cs create mode 100644 UPBot Code/DataClasses/HelperMember.cs diff --git a/UPBot Code/BotDbContext.cs b/UPBot Code/BotDbContext.cs new file mode 100644 index 0000000..fd14010 --- /dev/null +++ b/UPBot Code/BotDbContext.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using System.Reflection; + +public class BotDbContext : DbContext { + public DbSet Helpers { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + optionsBuilder.UseSqlite("Filename=Database/UPBot.db", options => { + options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); + }); + base.OnConfiguring(optionsBuilder); + } + protected override void OnModelCreating(ModelBuilder modelBuilder) { + // Map table names + modelBuilder.Entity().ToTable("HelperMember", "test"); + modelBuilder.Entity(entity => { + entity.HasKey(e => e.Id); + entity.HasIndex(e => e.Name);// .IsUnique(); + entity.Property(e => e.DateAdded).HasDefaultValueSql("CURRENT_TIMESTAMP"); + }); + base.OnModelCreating(modelBuilder); + } +} \ No newline at end of file diff --git a/UPBot Code/DataClasses/HelperMember.cs b/UPBot Code/DataClasses/HelperMember.cs new file mode 100644 index 0000000..ac5a790 --- /dev/null +++ b/UPBot Code/DataClasses/HelperMember.cs @@ -0,0 +1,14 @@ +using System; +using System.ComponentModel.DataAnnotations; +/// +/// HelperMember entity +/// +public class HelperMember { + [Key] + public ulong Id { get; set; } + [Required] + [MaxLength(128)] + public string Name { get; set; } + [Required] + public DateTime DateAdded { get; set; } +} \ No newline at end of file diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index 1544434..ba84db6 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Reflection; using System.Threading.Tasks; using DSharpPlus; @@ -45,7 +46,32 @@ static async Task MainAsync(string token, string prefix) { discord.MessageReactionAdded += AppreciationTracking.ReacionAdded; discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; + + TestDb(); + await Task.Delay(-1); } + + static void TestDb() { + string dbName = "TestDatabase.db"; + if (System.IO.File.Exists(dbName)) { + System.IO.File.Delete(dbName); + } + using (var dbContext = new BotDbContext()) { + //Ensure database is created + dbContext.Database.EnsureCreated(); + if (!dbContext.Helpers.Any()) { + dbContext.Helpers.AddRange(new HelperMember[] { + new HelperMember{ Id=1, Name="CPU" }, + new HelperMember{ Id=2, Name="Duck" }, + new HelperMember{ Id=3, Name="Erem" } + }); + dbContext.SaveChanges(); + } + foreach (var help in dbContext.Helpers) { + Console.WriteLine($"HID={help.Id}\tName={help.Name}\tDateTimeAdd={help.DateAdded}"); + } + } + } } } \ No newline at end of file diff --git a/UPBot.csproj b/UPBot.csproj index b13f22a..1998b82 100644 --- a/UPBot.csproj +++ b/UPBot.csproj @@ -9,6 +9,8 @@ + + From a329b3762796179c0da2d4df437685615d6d97bd Mon Sep 17 00:00:00 2001 From: CPULL Date: Fri, 20 Aug 2021 12:22:25 +0200 Subject: [PATCH 39/54] First table working with SQLLite: bannedwords --- ToDo.txt | 40 ++++++------ UPBot Code/BotDbContext.cs | 12 +++- UPBot Code/Commands/BannedWords.cs | 96 ++++++---------------------- UPBot Code/DataClasses/BannedWord.cs | 38 +++++++---- UPBot Code/Program.cs | 9 ++- UPBot Code/Utils.cs | 18 ++++++ 6 files changed, 95 insertions(+), 118 deletions(-) diff --git a/ToDo.txt b/ToDo.txt index a85b07d..48c8875 100644 --- a/ToDo.txt +++ b/ToDo.txt @@ -7,24 +7,24 @@ Have a cmd to configure how long messages should stay !Add TryCatch to all code and commands Add "keep" option to reformat - Errors DelAnsw EmbedRes TryCatch Log@Begin -MemberTracking | | | | X | | -Appreciation | | | | X | X | -EmojiForRole | | | | X | X | -BannedWords | | X | | X | X | -newcc | | | | | X | -ccdel | | | | | X | -ccedit | | | | | X | -cceditname | | | | | X | -cclist | | | | | X | -delete | X | X | | X | X | -game | | | | | X | -bool | | | | | X | -rps | | | | | X | -helplanguage | | | X | X | X | -ping | | X | | X | X | -checklanguage | | | | X | X | -reformat | | | | X | X | -stats | | | X | X | X | -whois | | | X | X | X | + Errors DelAnsw EmbedRes TryCatch Log@Begin UseSQL +MemberTracking | | | | X | | | +Appreciation | | | | X | X | | +EmojiForRole | | | | X | X | | +BannedWords | | X | | X | X | X | +newcc | | | | | X | | +ccdel | | | | | X | | +ccedit | | | | | X | | +cceditname | | | | | X | | +cclist | | | | | X | | +delete | X | X | | X | X | | +game | | | | | X | | +bool | | | | | X | | +rps | | | | | X | | +helplanguage | | | X | X | X | | +ping | | X | | X | X | | +checklanguage | | | | X | X | | +reformat | | | | X | X | | +stats | | | X | X | X | | +whois | | | X | X | X | | diff --git a/UPBot Code/BotDbContext.cs b/UPBot Code/BotDbContext.cs index fd14010..9be7412 100644 --- a/UPBot Code/BotDbContext.cs +++ b/UPBot Code/BotDbContext.cs @@ -3,6 +3,7 @@ public class BotDbContext : DbContext { public DbSet Helpers { get; set; } + public DbSet BannedWords { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Filename=Database/UPBot.db", options => { options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); @@ -11,12 +12,19 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { // Map table names - modelBuilder.Entity().ToTable("HelperMember", "test"); + modelBuilder.Entity().ToTable("HelperMember", "UPBotSchema"); modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.HasIndex(e => e.Name);// .IsUnique(); entity.Property(e => e.DateAdded).HasDefaultValueSql("CURRENT_TIMESTAMP"); }); + modelBuilder.Entity().ToTable("BannedWord", "UPBotSchema"); + modelBuilder.Entity(entity => { + entity.HasKey(e => e.Word); + entity.HasIndex(e => e.Word).IsUnique(); + entity.Property(e => e.Creator); + entity.Property(e => e.Date).HasDefaultValueSql("CURRENT_TIMESTAMP"); + }); base.OnModelCreating(modelBuilder); } -} \ No newline at end of file +} diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index 63cca44..fcf6a3a 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -22,15 +22,12 @@ public class BannedWords : BaseCommandModule { public static void Init() { bannedWords = new List(); - string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); - if (!File.Exists(path)) return; - string[] all = File.ReadAllLines(path); - foreach (string line in all) { - BannedWord word = new BannedWord(line); - if (word.word == null) continue; - bannedWords.Add(word); - } - bannedWords.Sort((a, b) => { return a.word.CompareTo(b.word); }); + if (Utils.db.BannedWords != null) + foreach (BannedWord bannedWord in Utils.db.BannedWords) { + Console.WriteLine($"bannedWord ={bannedWord.Word}"); + bannedWords.Add(bannedWord); + } + bannedWords.Sort((a, b) => { return a.Word.CompareTo(b.Word); }); } [Command("bannedwords")] @@ -66,7 +63,7 @@ private async Task> HandleListOfBannedWords(CommandContext else { string message = "I have " + bannedWords.Count + " banned word" + (bannedWords.Count == 1 ? "" : "s") + ":\n"; for (int i = 0; i < bannedWords.Count; i++) { - message += bannedWords[i].word + " (" + GetUserName(bannedWords[i].creator, ctx) + " " + bannedWords[i].date.ToString("yyyy/MM/dd") + ")"; + message += bannedWords[i].Word + " (" + GetUserName(bannedWords[i].Creator, ctx) + " " + bannedWords[i].Date.ToString("yyyy/MM/dd") + ")"; if (i < bannedWords.Count - 1) message += ",\n"; } await Task.Delay(10); @@ -86,15 +83,16 @@ private async Task> HandleAddRemoveOfBannedWords(CommandCon if (bannedWords == null) bannedWords = new List(); // Do we have it? foreach (BannedWord bw in bannedWords) { - if (bw.word.Equals(word)) { + if (bw.Word.Equals(word)) { await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.KO)); return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is already in the list."); } } BannedWord w = new BannedWord(word, ctx.Message.Author.Id); bannedWords.Add(w); - bannedWords.Sort((a, b) => { return a.word.CompareTo(b.word); }); - SaveWord(w); + bannedWords.Sort((a, b) => { return a.Word.CompareTo(b.Word); }); + Utils.db.BannedWords.Add(w); + Utils.db.SaveChanges(); await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.OK)); return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been added."); @@ -106,7 +104,7 @@ private async Task> HandleAddRemoveOfBannedWords(CommandCon // Do we have it? BannedWord found = null; foreach (BannedWord bw in bannedWords) { - if (bw.word.Equals(word)) { + if (bw.Word.Equals(word)) { found = bw; break; } @@ -116,7 +114,9 @@ private async Task> HandleAddRemoveOfBannedWords(CommandCon return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is not in the list."); } bannedWords.Remove(found); - SaveList(); + Utils.db.BannedWords.Remove(found); + Utils.db.SaveChanges(); + await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.OK)); return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been removed."); } @@ -126,64 +126,6 @@ private async Task> HandleAddRemoveOfBannedWords(CommandCon } } - void SaveWord(BannedWord w) { - string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); - if (!File.Exists(path)) File.CreateText(path); - try { - using (StreamWriter sw = File.AppendText(path)) { - sw.Write(w.ToString()); - sw.FlushAsync(); - } - } catch (Exception e) { - Utils.Log(e.Message); - } - } - - void SaveList() { - string path = Utils.ConstructPath(directoryName, "BannedWords", ".txt"); - if (File.Exists(path)) { - try { - File.Delete(path); - } catch (Exception e) { - Utils.Log(e.Message); - return; - } - } - try { - using (StreamWriter sw = File.CreateText(path)) { - foreach (BannedWord w in bannedWords) { - sw.Write(w.ToString()); - sw.FlushAsync(); - } - } - } catch (Exception e) { - Utils.Log(e.Message); - } - } - - class BannedWord { - public string word; - public ulong creator = 0; - public DateTime date = DateTime.MinValue; - - public BannedWord(string w, ulong id) { - word = w; - creator = id; - date = DateTime.Now; - } - - public BannedWord(string line) { - string[] parts = line.Trim(' ', '\r', '\n').Split('\t'); - if (parts.Length != 3) return; - word = parts[0].Trim(' ', '\r', '\n', '\t').ToLowerInvariant(); - ulong.TryParse(parts[1].Trim(' ', '\r', '\n', '\t'), out creator); - DateTime.TryParse(parts[2].Trim(' ', '\r', '\n', '\t'), out date); - } - - public override string ToString() { - return word + "\t" + creator + "\t" + date + "\n"; - } - } Dictionary knownUsers; @@ -221,14 +163,14 @@ internal static async Task CheckMessage(DiscordClient client, MessageCreateEvent string msg = args.Message.Content.ToLowerInvariant(); foreach (BannedWord w in bannedWords) { - int pos = msg.IndexOf(w.word); + int pos = msg.IndexOf(w.Word); if (pos == -1) continue; if (pos > 0 && letters.IsMatch(msg[pos - 1].ToString())) continue; - if (pos + w.word.Length < msg.Length && letters.IsMatch(msg[pos + w.word.Length].ToString())) continue; + if (pos + w.Word.Length < msg.Length && letters.IsMatch(msg[pos + w.Word.Length].ToString())) continue; - Utils.Log("Removed word \"" + w.word + "\" from " + user.Username + " in: " + msg); + Utils.Log("Removed word \"" + w.Word + "\" from " + user.Username + " in: " + msg); DiscordMessage warning = await args.Message.Channel.SendMessageAsync("Moderate your language, " + user.Mention + "."); - await args.Message.DeleteAsync("Bad words: " + w.word); + await args.Message.DeleteAsync("Bad words: " + w.Word); Utils.DeleteDelayed(10000, warning).Wait(); return; } diff --git a/UPBot Code/DataClasses/BannedWord.cs b/UPBot Code/DataClasses/BannedWord.cs index dc5971c..33b8292 100644 --- a/UPBot Code/DataClasses/BannedWord.cs +++ b/UPBot Code/DataClasses/BannedWord.cs @@ -1,25 +1,35 @@ using System; +using System.ComponentModel.DataAnnotations; + public class BannedWord { - public string word; - public ulong creator = 0; - public DateTime date = DateTime.MinValue; + [Key] + [Required] + public string Word { get; set; } + [Required] + public ulong Creator { get; set; } + [Required] + public DateTime Date { get; set; } public BannedWord(string w, ulong id) { - word = w; - creator = id; - date = DateTime.Now; + Word = w; + Creator = id; + Date = DateTime.Now; + } + public BannedWord() { + Word = ""; + Creator = 0; + Date = DateTime.Now; } - public BannedWord(string line) { - string[] parts = line.Trim(' ', '\r', '\n').Split('\t'); - if (parts.Length != 3) return; - word = parts[0].Trim(' ', '\r', '\n', '\t').ToLowerInvariant(); - ulong.TryParse(parts[1].Trim(' ', '\r', '\n', '\t'), out creator); - DateTime.TryParse(parts[2].Trim(' ', '\r', '\n', '\t'), out date); + public BannedWord(string w, ulong id, DateTime d) { + Word = w; + Creator = id; + Date = d; } public override string ToString() { - return word + "\t" + creator + "\t" + date + "\n"; + return Word + "\t" + Creator + "\t" + Date + "\n"; } -} \ No newline at end of file +} + diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index ba84db6..f6d88cf 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -25,6 +25,8 @@ static async Task MainAsync(string token, string prefix) { CustomCommandsService.DiscordClient = discord; Utils.InitClient(discord); + Utils.ConnectToDb(); + CommandsNextExtension commands = discord.UseCommandsNext(new CommandsNextConfiguration() { StringPrefixes = new[] { prefix[0].ToString() } // The backslash will be the default command prefix if not specified in the parameters }); @@ -46,14 +48,11 @@ static async Task MainAsync(string token, string prefix) { discord.MessageReactionAdded += AppreciationTracking.ReacionAdded; discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; - - TestDb(); - await Task.Delay(-1); } - static void TestDb() { - string dbName = "TestDatabase.db"; + static void TestDb() { // FIXME code to be removed + string dbName = "Database/UPBot.db"; if (System.IO.File.Exists(dbName)) { System.IO.File.Delete(dbName); } diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index cbcd2ab..1fbb852 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -81,6 +81,24 @@ public static void InitClient(DiscordClient c) { else logs = File.CreateText(logPath); } + internal static BotDbContext db; + internal static void ConnectToDb() { + // db => "Database/UPBot.db"; + db = new BotDbContext(); + db.Database.EnsureCreated(); //Ensure database is created + if (false /*!db.Helpers.Any()*/) { + db.Helpers.AddRange(new HelperMember[] { + new HelperMember{ Id=1, Name="CPU" }, + new HelperMember{ Id=2, Name="Duck" }, + new HelperMember{ Id=3, Name="Erem" } + }); + db.SaveChanges(); + } + + + Log("DB Connected: " + db.Database.ProviderName); + } + /// /// Change a string based on the count it's referring to (e.g. "one apple", "two apples") /// From a8599a35cfa3762d41473496107546cde72fd320 Mon Sep 17 00:00:00 2001 From: CPULL Date: Fri, 20 Aug 2021 15:53:59 +0200 Subject: [PATCH 40/54] SQL is on Appreciation, EmojiForRole, and BannedWords Code looks working --- ToDo.txt | 5 +- UPBot Code/Actions/AppreciationTracking.cs | 183 +++++--------- UPBot Code/BotDbContext.cs | 21 +- UPBot Code/Commands/BannedWords.cs | 12 +- UPBot Code/DataClasses/BannedWord.cs | 10 +- UPBot Code/DataClasses/EmojiForRoleValue.cs | 18 ++ UPBot Code/DataClasses/EmojisForRole.cs | 247 ++++++++++--------- UPBot Code/DataClasses/Reputation.cs | 20 +- UPBot Code/DataClasses/ReputationTracking.cs | 156 ++++-------- UPBot Code/Program.cs | 26 +- UPBot Code/Utils.cs | 11 - 11 files changed, 316 insertions(+), 393 deletions(-) create mode 100644 UPBot Code/DataClasses/EmojiForRoleValue.cs diff --git a/ToDo.txt b/ToDo.txt index 48c8875..5dd139e 100644 --- a/ToDo.txt +++ b/ToDo.txt @@ -6,11 +6,12 @@ Add wikis for each command on GitHub Have a cmd to configure how long messages should stay !Add TryCatch to all code and commands Add "keep" option to reformat +Extend the reputation by checking "tanks and thank you" in messages and add to mentioned mebers or author of referenced message Errors DelAnsw EmbedRes TryCatch Log@Begin UseSQL MemberTracking | | | | X | | | -Appreciation | | | | X | X | | -EmojiForRole | | | | X | X | | +Appreciation | | | | X | X | X | +EmojiForRole | | | | X | X | X | BannedWords | | X | | X | X | X | newcc | | | | | X | | ccdel | | | | | X | | diff --git a/UPBot Code/Actions/AppreciationTracking.cs b/UPBot Code/Actions/AppreciationTracking.cs index 8034cf5..8af8c4d 100644 --- a/UPBot Code/Actions/AppreciationTracking.cs +++ b/UPBot Code/Actions/AppreciationTracking.cs @@ -5,55 +5,66 @@ using DSharpPlus.EventArgs; using System; using System.Collections.Generic; -using System.IO; +using System.Text.RegularExpressions; using System.Threading.Tasks; public class AppreciationTracking : BaseCommandModule { - static ReputationTracking tracking = null; - static EmojisForRole emojisForRole = null; - static bool savingRequested = false; - - public static void Init() { - tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); - emojisForRole = new EmojisForRole(Utils.ConstructPath("Tracking", "EmojisForRole", ".dat")); + readonly static Regex thanks = new Regex("(^|[^a-z0-9_])thanks($|[^a-z0-9_])", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); + readonly static Regex thankyou = new Regex("(^|[^a-z0-9_])thanks{0,1}\\s{0,1}you($|[^a-z0-9_])", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); + readonly static Regex thank2you = new Regex("(^|[^a-z0-9_])thanks{0,1}\\s{0,1}to\\s{0,1}you($|[^a-z0-9_])", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); + readonly static Regex thank4n = new Regex("(^|[^a-z0-9_])thanks{0,1}\\s{0,1}for\\s{0,1}nothing($|[^a-z0-9_])", RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); + public static ReputationTracking tracking = null; + + private static bool GetTracking() { + if (tracking != null) return false; + tracking = new ReputationTracking(); + return (tracking == null); } - [Command("Appreciation")] [Description("It shows the statistics for users")] public async Task ShowAppreciationCommand(CommandContext ctx) { Utils.LogUserCommand(ctx); try { - if (tracking == null) { - tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); - if (tracking == null) return; - } - DiscordEmbedBuilder e = Utils.BuildEmbed("Appreciation", "These are the most appreciated people of this server (tracking started at " + tracking.GetStartDate() + ")", DiscordColor.Azure); + if (GetTracking()) return; + DiscordEmbedBuilder e = Utils.BuildEmbed("Appreciation", "These are the most appreciated people of this server", DiscordColor.Azure); List vals = new List(tracking.GetReputation()); Dictionary users = new Dictionary(); e.AddField("Reputation ----------------", "For receving these emojis in the posts: <:OK:830907665869570088>👍❤️🥰😍🤩😘💯<:whatthisguysaid:840702597216337990>", false); - vals.Sort((a, b) => { return b.reputation.CompareTo(a.reputation); }); + vals.Sort((a, b) => { return b.Rep.CompareTo(a.Rep); }); for (int i = 0; i < 6; i++) { if (i >= vals.Count) break; Reputation r = vals[i]; - if (r.reputation == 0) break; - if (!users.ContainsKey(r.user)) { - users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + if (r.Rep == 0) break; + if (!users.ContainsKey(r.User)) { + users[r.User] = ctx.Guild.GetMemberAsync(r.User).Result.DisplayName; } - e.AddField(users[r.user], "Reputation: _" + r.reputation + "_", true); + e.AddField(users[r.User], "Reputation: _" + r.Rep + "_", true); } e.AddField("Fun -----------------------", "For receving these emojis in the posts: 😀😃😄😁😆😅🤣😂🙂🙃😉😊😇<:StrongSmile:830907626928996454>", false); - vals.Sort((a, b) => { return b.fun.CompareTo(a.fun); }); + vals.Sort((a, b) => { return b.Fun.CompareTo(a.Fun); }); + for (int i = 0; i < 6; i++) { + if (i >= vals.Count) break; + Reputation r = vals[i]; + if (r.Fun == 0) break; + if (!users.ContainsKey(r.User)) { + users[r.User] = ctx.Guild.GetMemberAsync(r.User).Result.DisplayName; + } + e.AddField(users[r.User], "Fun: _" + r.Fun + "_", (i < vals.Count - 1 && i < 5)); + } + + e.AddField("Thanks --------------------", "For receving a message with _Thanks_ or _Thank you_", false); + vals.Sort((a, b) => { return b.Tnk.CompareTo(a.Tnk); }); for (int i = 0; i < 6; i++) { if (i >= vals.Count) break; Reputation r = vals[i]; - if (r.fun == 0) break; - if (!users.ContainsKey(r.user)) { - users[r.user] = ctx.Guild.GetMemberAsync(r.user).Result.DisplayName; + if (r.Tnk == 0) break; + if (!users.ContainsKey(r.User)) { + users[r.User] = ctx.Guild.GetMemberAsync(r.User).Result.DisplayName; } - e.AddField(users[r.user], "Fun: _" + r.fun + "_", (i < vals.Count - 1 && i < 5)); + e.AddField(users[r.User], "Thanks: _" + r.Tnk + "_", (i < vals.Count - 1 && i < 5)); } await ctx.Message.RespondAsync(e.Build()); @@ -62,78 +73,33 @@ public async Task ShowAppreciationCommand(CommandContext ctx) { } } - - [Command("EmojiForRole")] - [Aliases("RoleForEmoji")] - [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only - [Description("Reply to a message what should add and remove a role when an emoji (any or specific) is added")] - public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role, [Description("The emoji to watch")] DiscordEmoji emoji) { - Utils.LogUserCommand(ctx); + internal static Task ThanksAdded(DiscordClient sender, MessageCreateEventArgs args) { try { - if (ctx.Message.ReferencedMessage == null) { - await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched!", Utils.Red, ctx, true); - } - else { - string msg = ctx.Message.ReferencedMessage.Content; - if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; - if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, emoji)) { - msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing the emoji: " + emoji.GetDiscordName(); - } - else { - msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding the emoji: " + emoji.GetDiscordName(); - } - Utils.Log(msg); - await ctx.RespondAsync(msg); + if (args.Message.Reference == null && (args.Message.MentionedUsers == null || args.Message.MentionedUsers.Count == 0)) return Task.FromResult(0); + + string msg = args.Message.Content.ToLowerInvariant(); + if (thanks.IsMatch(msg) || thankyou.IsMatch(msg) || thank2you.IsMatch(msg)) { // Add thanks + if (thank4n.IsMatch(msg)) return Task.FromResult(0); + if (GetTracking()) return Task.FromResult(0); + + IReadOnlyList mentions = args.Message.MentionedUsers; + ulong authorId = args.Message.Author.Id; + ulong refAuthorId = args.Message.Reference != null ? args.Message.Reference.Message.Author.Id : 0; + if (mentions != null) + foreach (DiscordUser u in mentions) + if (u.Id != authorId && u.Id != refAuthorId) tracking.AlterThankYou(u.Id); + if (args.Message.Reference != null) + if (args.Message.Reference.Message.Author.Id != authorId) tracking.AlterThankYou(args.Message.Reference.Message.Author.Id); } - } catch (Exception ex) { - await ctx.RespondAsync(Utils.GenerateErrorAnswer("EmojiForRole", ex)); - } - } - - [Command("EmojiForRole")] - [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task EmojiForRoleCommand(CommandContext ctx) { - Utils.LogUserCommand(ctx); - await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nyou have to specify the Role to add/remove, and the emoji to watch.", Utils.Red, ctx, true); - } - - [Command("EmojiForRole")] - [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role) { - Utils.LogUserCommand(ctx); - try { - if (ctx.Message.ReferencedMessage == null) { - await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\n and you have to specify the emoji to watch.", Utils.Red, ctx, true); - } - else { - string msg = ctx.Message.ReferencedMessage.Content; - if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; - if (emojisForRole.AddRemove(ctx.Message.ReferencedMessage, role, null)) { - msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing any emoji"; - } - else { - msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding an emoji"; - } - Utils.Log(msg); - await ctx.RespondAsync(msg); - } + return Task.FromResult(0); } catch (Exception ex) { - await ctx.RespondAsync(Utils.GenerateErrorAnswer("EmojiForRole", ex)); + Utils.Log("Error in ThanksAdded: " + ex.Message); + return Task.FromResult(0); } } - [Command("EmojiForRole")] - [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The emoji to watch")] DiscordEmoji emoji) { - Utils.LogUserCommand(ctx); - if (ctx.Message.ReferencedMessage == null) { - await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nand y ou have to specify the Role to add/remove.", Utils.Red, ctx, true); - } - else { - await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to specify the Role to add/remove.", Utils.Red, ctx, true); - } - } + @@ -142,7 +108,6 @@ internal static Task ReacionAdded(DiscordClient sender, MessageReactionAddEventA try { ulong emojiId = a.Emoji.Id; string emojiName = a.Emoji.Name; - emojisForRole.HandleAddingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); DiscordUser author = a.Message.Author; if (author == null) { @@ -165,7 +130,6 @@ internal static Task ReactionRemoved(DiscordClient sender, MessageReactionRemove try { ulong emojiId = a.Emoji.Id; string emojiName = a.Emoji.Name; - emojisForRole.HandleRemovingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); DiscordUser author = a.Message.Author; if (author == null) { @@ -185,9 +149,7 @@ internal static Task ReactionRemoved(DiscordClient sender, MessageReactionRemove } static Task HandleReactions(ulong emojiId, string emojiName, ulong authorId, bool added) { - bool save = false; // check if emoji is :smile: :rolf: :strongsmil: (find valid emojis -> increase fun level of user - if (emojiName == "😀" || emojiName == "😃" || emojiName == "😄" || @@ -203,12 +165,10 @@ static Task HandleReactions(ulong emojiId, string emojiName, ulong authorId, boo emojiName == "😇" || emojiId == 830907626928996454ul || // :StrongSmile: false) { // just to keep the other lines aligned - if (tracking == null) { - tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); - if (tracking == null) return Task.Delay(10); - } - save = tracking.AlterFun(authorId, added); + if (GetTracking()) return Task.FromResult(0); + tracking.AlterFun(authorId, added); } + // check if emoji is :OK: or :ThumbsUp: -> Increase reputation for user if (emojiId == 830907665869570088ul || // :OK: emojiName == "👍" || // :thumbsup: @@ -221,33 +181,10 @@ static Task HandleReactions(ulong emojiId, string emojiName, ulong authorId, boo emojiId == 840702597216337990ul || // :whatthisguysaid: emojiId == 552147917876625419ul || // :thoose: false) { // just to keep the other lines aligned - if (tracking == null) { - tracking = new ReputationTracking(Utils.ConstructPath("Tracking", "Tracking", ".Tracking")); - if (tracking == null) return Task.Delay(10); - } - save = tracking.AlterRep(authorId, added); - } - - // Start a delayed saving if one is not yet present - if (save && !savingRequested) { - savingRequested = true; - _ = SaveDelayedAsync(); + if (GetTracking()) return Task.FromResult(0); + tracking.AlterRep(authorId, added); } return Task.Delay(10); } - - - - static async Task SaveDelayedAsync() { - await Task.Delay(30000); - try { - tracking.Save(); - } catch (Exception e) { - Utils.Log("ERROR: problems in saving the Reputation file: " + e.Message); - } - savingRequested = false; - } - - } diff --git a/UPBot Code/BotDbContext.cs b/UPBot Code/BotDbContext.cs index 9be7412..b935396 100644 --- a/UPBot Code/BotDbContext.cs +++ b/UPBot Code/BotDbContext.cs @@ -4,6 +4,8 @@ public class BotDbContext : DbContext { public DbSet Helpers { get; set; } public DbSet BannedWords { get; set; } + public DbSet Reputations { get; set; } + public DbSet EmojiForRoles { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Filename=Database/UPBot.db", options => { options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); @@ -23,7 +25,24 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { entity.HasKey(e => e.Word); entity.HasIndex(e => e.Word).IsUnique(); entity.Property(e => e.Creator); - entity.Property(e => e.Date).HasDefaultValueSql("CURRENT_TIMESTAMP"); + entity.Property(e => e.DateAdded).HasDefaultValueSql("CURRENT_TIMESTAMP"); + }); + modelBuilder.Entity().ToTable("Reputation", "UPBotSchema"); + modelBuilder.Entity(entity => { + entity.HasKey(e => e.User); + entity.HasIndex(e => e.User).IsUnique(); + entity.Property(e => e.Rep); + entity.Property(e => e.Fun); + entity.Property(e => e.Tnk); + entity.Property(e => e.DateAdded).HasDefaultValueSql("CURRENT_TIMESTAMP"); + }); + modelBuilder.Entity().ToTable("EmojiForRole", "UPBotSchema"); + modelBuilder.Entity(entity => { + entity.Property(e => e.Channel); + entity.HasKey(e => e.Message); + entity.Property(e => e.Role); + entity.Property(e => e.EmojiId); + entity.Property(e => e. EmojiName); }); base.OnModelCreating(modelBuilder); } diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index fcf6a3a..f67528e 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -22,11 +22,14 @@ public class BannedWords : BaseCommandModule { public static void Init() { bannedWords = new List(); - if (Utils.db.BannedWords != null) + if (Utils.db.BannedWords != null) { + int num = 0; foreach (BannedWord bannedWord in Utils.db.BannedWords) { - Console.WriteLine($"bannedWord ={bannedWord.Word}"); + num++; bannedWords.Add(bannedWord); } + Utils.Log("Found " + num + " banned words"); + } bannedWords.Sort((a, b) => { return a.Word.CompareTo(b.Word); }); } @@ -63,7 +66,7 @@ private async Task> HandleListOfBannedWords(CommandContext else { string message = "I have " + bannedWords.Count + " banned word" + (bannedWords.Count == 1 ? "" : "s") + ":\n"; for (int i = 0; i < bannedWords.Count; i++) { - message += bannedWords[i].Word + " (" + GetUserName(bannedWords[i].Creator, ctx) + " " + bannedWords[i].Date.ToString("yyyy/MM/dd") + ")"; + message += bannedWords[i].Word + " (" + GetUserName(bannedWords[i].Creator, ctx) + " " + bannedWords[i].DateAdded.ToString("yyyy/MM/dd") + ")"; if (i < bannedWords.Count - 1) message += ",\n"; } await Task.Delay(10); @@ -150,10 +153,9 @@ internal static async Task CheckMessage(DiscordClient client, MessageCreateEvent // Who is the author? If the bot or a mod then ignore if (args.Author.Equals(client.CurrentUser)) return; DiscordUser user = args.Author; - DiscordGuild guild = await client.GetGuildAsync((ulong)args.Message.Channel.GuildId); DiscordMember member; try { - member = await guild.GetMemberAsync(user.Id); + member = await Utils.GetGuild().GetMemberAsync(user.Id); } catch (Exception) { return; } diff --git a/UPBot Code/DataClasses/BannedWord.cs b/UPBot Code/DataClasses/BannedWord.cs index 33b8292..5139aad 100644 --- a/UPBot Code/DataClasses/BannedWord.cs +++ b/UPBot Code/DataClasses/BannedWord.cs @@ -9,27 +9,27 @@ public class BannedWord { [Required] public ulong Creator { get; set; } [Required] - public DateTime Date { get; set; } + public DateTime DateAdded { get; set; } public BannedWord(string w, ulong id) { Word = w; Creator = id; - Date = DateTime.Now; + DateAdded = DateTime.Now; } public BannedWord() { Word = ""; Creator = 0; - Date = DateTime.Now; + DateAdded = DateTime.Now; } public BannedWord(string w, ulong id, DateTime d) { Word = w; Creator = id; - Date = d; + DateAdded = d; } public override string ToString() { - return Word + "\t" + Creator + "\t" + Date + "\n"; + return Word + "\t" + Creator + "\t" + DateAdded + "\n"; } } diff --git a/UPBot Code/DataClasses/EmojiForRoleValue.cs b/UPBot Code/DataClasses/EmojiForRoleValue.cs new file mode 100644 index 0000000..2dd14d2 --- /dev/null +++ b/UPBot Code/DataClasses/EmojiForRoleValue.cs @@ -0,0 +1,18 @@ +using DSharpPlus.Entities; +using System.ComponentModel.DataAnnotations; + +public class EmojiForRoleValue { + [Required] + public ulong Channel { get; set; } + [Key] + [Required] + public ulong Message { get; set; } + [Required] + public ulong Role { get; set; } + [Required] + public ulong EmojiId { get; set; } + [Required] + public string EmojiName { get; set; } + + public DiscordRole dRole; // Not in DB +} diff --git a/UPBot Code/DataClasses/EmojisForRole.cs b/UPBot Code/DataClasses/EmojisForRole.cs index 3104384..96c85ff 100644 --- a/UPBot Code/DataClasses/EmojisForRole.cs +++ b/UPBot Code/DataClasses/EmojisForRole.cs @@ -1,112 +1,133 @@ -using DSharpPlus.Entities; +using DSharpPlus; +using DSharpPlus.CommandsNext; +using DSharpPlus.CommandsNext.Attributes; +using DSharpPlus.Entities; +using DSharpPlus.EventArgs; using System; using System.Collections.Generic; -using System.IO; +using System.Threading.Tasks; -public class EmojisForRole { - List values = null; - string path = null; +public class EmojisForRole : BaseCommandModule { + static List values = null; - public EmojisForRole(string path) { + static void GetValues() { + if (values != null) return; + values = new List(); + if (Utils.db.EmojiForRoles != null) { + int num = 0; + foreach (EmojiForRoleValue v in Utils.db.EmojiForRoles) { + num++; + values.Add(v); + } + Utils.Log("Found " + num + " EmojiForRoles entries"); + } + } + + [Command("EmojiForRole")] + [Aliases("RoleForEmoji")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + [Description("Reply to a message what should add and remove a role when an emoji (any or specific) is added")] + public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role, [Description("The emoji to watch")] DiscordEmoji emoji) { + Utils.LogUserCommand(ctx); try { - values = new List(); - this.path = path; - if (!File.Exists(path)) return; - - DiscordGuild g = Utils.GetGuild(); - byte[] data = new byte[36]; - using (FileStream f = new FileStream(path, FileMode.Open)) { - while (f.Read(data, 0, 36) == 36) { - ReactionValue v = new ReactionValue { - channel = BitConverter.ToUInt64(data, 0), - message = BitConverter.ToUInt64(data, 8), - role = BitConverter.ToUInt64(data, 16), - emojiId = BitConverter.ToUInt64(data, 24), - emojiName = Char.ConvertFromUtf32(BitConverter.ToInt32(data, 32)), - dRole = null - }; - - DiscordMessage m; - try { - // Does the message exists? - DiscordChannel c = g.GetChannel(v.channel); - if (c == null || c.Id == 0) continue; // Bad - m = c.GetMessageAsync(v.message).Result; - if (m == null || m.Id == 0) continue; // Bad; - // Does the role exists? - DiscordRole r = g.GetRole(v.role); - if (r == null || r.Id == 0) continue; // Bad - } catch (Exception ex) { - Utils.Log("Error while checking a ReactionValue: " + ex.Message); - continue; - } - // Check that the message has the required emojis, if not add it - DiscordEmoji e = null; - if (v.emojiId != 0) { - e = g.GetEmojiAsync(v.emojiId).Result; - } - else if (v.emojiName != "!") { - e = DiscordEmoji.FromUnicode(v.emojiName); - } - if (e == null) continue; // Bad - if (m.GetReactionsAsync(e, 1).Result.Count == 0) { // Need to add - m.CreateReactionAsync(e).Wait(); - } - - values.Add(v); + if (ctx.Message.ReferencedMessage == null) { + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched!", Utils.Red, ctx, true); + } + else { + string msg = ctx.Message.ReferencedMessage.Content; + if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; + if (AddRemove(ctx.Message.ReferencedMessage, role, emoji)) { + msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing the emoji: " + emoji.GetDiscordName(); + } + else { + msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding the emoji: " + emoji.GetDiscordName(); } + Utils.Log(msg); + await ctx.RespondAsync(msg); } - Utils.Log("Loaded " + values.Count + " tracked messages for emojis."); - } catch (Exception e) { - Utils.Log("ERROR: problems in loading the EmojiForRole file: " + e.Message); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("EmojiForRole", ex)); } } - public void Save() { + + [Command("EmojiForRole")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EmojiForRoleCommand(CommandContext ctx) { + Utils.LogUserCommand(ctx); + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nyou have to specify the Role to add/remove, and the emoji to watch.", Utils.Red, ctx, true); + } + + [Command("EmojiForRole")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The role to add")] DiscordRole role) { + Utils.LogUserCommand(ctx); try { - lock (values) { - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - Utils.Log("ERROR: cannot delete old EmojisForRole file: " + path + "\nException: " + e.Message); - return; + if (ctx.Message.ReferencedMessage == null) { + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\n and you have to specify the emoji to watch.", Utils.Red, ctx, true); + } + else { + string msg = ctx.Message.ReferencedMessage.Content; + if (msg.Length > 20) msg = msg.Substring(0, 20) + "..."; + if (AddRemove(ctx.Message.ReferencedMessage, role, null)) { + msg = "The referenced message (_" + msg + "_) will grant/remove the role *" + role.Name + "* when adding/removing any emoji"; } - using (FileStream f = new FileStream(path, FileMode.CreateNew)) { - byte[] data = new byte[36]; - foreach (ReactionValue v in values) { - int pos = 0; - byte[] d = BitConverter.GetBytes(v.channel); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.message); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.role); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - d = BitConverter.GetBytes(v.emojiId); - for (int i = 0; i < d.Length; i++) data[pos++] = d[i]; - if (v.emojiId != 0 || v.emojiName == null || v.emojiName.Length == 0) - for (int i = 0; i < 4; i++) data[pos++] = 0; - else { - d = BitConverter.GetBytes(Char.ConvertToUtf32(v.emojiName, 0)); - for (int i = 0; i < 4; i++) data[pos++] = d[i]; - } - f.Write(data, 0, pos); - } - f.Flush(); + else { + msg = "The message referenced (_" + msg + "_) will not grant anymore the role *" + role.Name + "* when adding an emoji"; } + Utils.Log(msg); + await ctx.RespondAsync(msg); } - Utils.Log("EmojisForRole: Saved " + values.Count + " tracked messages and emojis"); - } catch (Exception e) { - Utils.Log("ERROR: problems in saving the EmojiForRole file: " + e.Message); + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("EmojiForRole", ex)); } } + [Command("EmojiForRole")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EmojiForRoleCommand(CommandContext ctx, [Description("The emoji to watch")] DiscordEmoji emoji) { + Utils.LogUserCommand(ctx); + if (ctx.Message.ReferencedMessage == null) { + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to reply to the message that should be watched,\nand y ou have to specify the Role to add/remove.", Utils.Red, ctx, true); + } + else { + await Utils.BuildEmbedAndExecute("EmojiForRole - Bad parameters", "You have to specify the Role to add/remove.", Utils.Red, ctx, true); + } + } + + + internal static Task ReacionAdded(DiscordClient sender, MessageReactionAddEventArgs a) { + try { + ulong emojiId = a.Emoji.Id; + string emojiName = a.Emoji.Name; + HandleAddingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); + } catch (Exception ex) { + Utils.Log("Error in ReacionAdded: " + ex.Message); + } + return Task.FromResult(0); + } + + internal static Task ReactionRemoved(DiscordClient sender, MessageReactionRemoveEventArgs a) { + try { + ulong emojiId = a.Emoji.Id; + string emojiName = a.Emoji.Name; + HandleRemovingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); + } catch (Exception ex) { + Utils.Log("Error in ReacionRemoved: " + ex.Message); + } + return Task.FromResult(0); + } + + + internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji) { + GetValues(); bool here = false; - ReactionValue toRemove = null; - foreach (ReactionValue v in values) { - if (v.channel == msg.ChannelId && v.message == msg.Id && v.role == role.Id) { - if ((emoji == null && v.emojiId == 0 && v.emojiName == "!") || - (emoji != null && ((emoji.Id != 0 && emoji.Id == v.emojiId) || (emoji.Id == 0 && emoji.Name.Equals(v.emojiName))))) { + EmojiForRoleValue toRemove = null; + foreach (EmojiForRoleValue v in values) { + if (v.Channel == msg.ChannelId && v.Message == msg.Id && v.Role == role.Id) { + if ((emoji == null && v.EmojiId == 0 && v.EmojiName == "!") || + (emoji != null && ((emoji.Id != 0 && emoji.Id == v.EmojiId) || (emoji.Id == 0 && emoji.Name.Equals(v.EmojiName))))) { toRemove = v; // Remove break; } @@ -114,18 +135,22 @@ internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji } if (toRemove != null) { values.Remove(toRemove); + Utils.db.EmojiForRoles.Remove(toRemove); + Utils.db.SaveChanges(); } else { here = true; - ReactionValue v = new ReactionValue { - channel = msg.ChannelId, - message = msg.Id, - role = role.Id, - emojiId = emoji == null ? 0 : emoji.Id, - emojiName = emoji == null ? "!" : emoji.Name, + EmojiForRoleValue v = new EmojiForRoleValue { + Channel = msg.ChannelId, + Message = msg.Id, + Role = role.Id, + EmojiId = emoji == null ? 0 : emoji.Id, + EmojiName = emoji == null ? "!" : emoji.Name, dRole = role }; values.Add(v); + Utils.db.EmojiForRoles.Add(v); + Utils.db.SaveChanges(); if (emoji != null) { // Check if we have the emoji already, if not add it @@ -133,20 +158,18 @@ internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji msg.CreateReactionAsync(emoji).Wait(); } } - } - Save(); return here; } - internal void HandleAddingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { + internal static void HandleAddingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { try { DiscordMember dm = (DiscordMember)user; if (dm == null) return; // Not a valid member for the Guild/Context - if (values == null) return; - foreach (ReactionValue v in values) { - if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { - if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + GetValues(); + foreach (EmojiForRoleValue v in values) { + if (cId == v.Channel && msgId == v.Message && (v.EmojiId == 0 && v.EmojiName == "!") || (eId != 0 && v.EmojiId == eId) || (eId == 0 && eN.Equals(v.EmojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.Role); dm.GrantRoleAsync(v.dRole).Wait(); Utils.Log("Role " + v.dRole.Name + " was granted to " + user.Username + " by emoji " + eId); return; @@ -157,14 +180,14 @@ internal void HandleAddingEmojiForRole(ulong cId, ulong eId, string eN, DiscordU } } - internal void HandleRemovingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { + internal static void HandleRemovingEmojiForRole(ulong cId, ulong eId, string eN, DiscordUser user, ulong msgId) { try { DiscordMember dm = (DiscordMember)user; if (dm == null) return; // Not a valid member for the Guild/Context - if (values == null) return; - foreach (ReactionValue v in values) { - if (cId == v.channel && msgId == v.message && (v.emojiId == 0 && v.emojiName == "!") || (eId != 0 && v.emojiId == eId) || (eId == 0 && eN.Equals(v.emojiName))) { - if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.role); + GetValues(); + foreach (EmojiForRoleValue v in values) { + if (cId == v.Channel && msgId == v.Message && (v.EmojiId == 0 && v.EmojiName == "!") || (eId != 0 && v.EmojiId == eId) || (eId == 0 && eN.Equals(v.EmojiName))) { + if (v.dRole == null) v.dRole = dm.Guild.GetRole(v.Role); dm.RevokeRoleAsync(v.dRole).Wait(); Utils.Log("Role " + v.dRole.Name + " was removed from " + user.Username + " by emoji " + eId); return; @@ -175,12 +198,4 @@ internal void HandleRemovingEmojiForRole(ulong cId, ulong eId, string eN, Discor } } - public class ReactionValue { - public ulong channel; // 8 bytes - public ulong message; // 8 bytes - public ulong role; // 8 bytes - public ulong emojiId; // 8 bytes - public string emojiName; // 4 bytes (utf32, 1 character) - public DiscordRole dRole; - } } diff --git a/UPBot Code/DataClasses/Reputation.cs b/UPBot Code/DataClasses/Reputation.cs index b60c39f..4eb687c 100644 --- a/UPBot Code/DataClasses/Reputation.cs +++ b/UPBot Code/DataClasses/Reputation.cs @@ -1,8 +1,18 @@ using System; +using System.ComponentModel.DataAnnotations; -public class Reputation { // 16 bytes - public ulong user; // 8 - public ushort reputation; // 2 - public ushort fun; // 2 - public DateTime startTracking; // 4 +public class Reputation { + [Key] + [Required] + public ulong User { get; set; } + [Required] + public ushort Rep { get; set; } + [Required] + public ushort Fun { get; set; } + [Required] + public ushort Tnk { get; set; } + [Required] + public DateTime DateAdded { get; set; } } + + diff --git a/UPBot Code/DataClasses/ReputationTracking.cs b/UPBot Code/DataClasses/ReputationTracking.cs index e57c4c9..cb307a6 100644 --- a/UPBot Code/DataClasses/ReputationTracking.cs +++ b/UPBot Code/DataClasses/ReputationTracking.cs @@ -1,136 +1,88 @@ using System; using System.Collections.Generic; -using System.IO; public class ReputationTracking { - readonly DateTime trackingStarted; readonly Dictionary dic; - readonly string path = null; - public ReputationTracking(string path) { - try { - this.path = path; - dic = new Dictionary(); - if (!File.Exists(path)) { - trackingStarted = DateTime.Now; - return; - } - byte[] data = new byte[20]; - using (FileStream f = new FileStream(path, FileMode.Open)) { - // 32 bits for the date (ymd) - if (f.Read(data, 0, 4) < 4) { - Utils.Log("ERROR: wrong Reputation file: " + path); - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); - } - return; - } - trackingStarted = GetDateFromBytes(data, 0); - while (f.Read(data, 0, 16) == 16) { - ulong usrid = BitConverter.ToUInt64(data); - ushort rep = BitConverter.ToUInt16(data, 8); - ushort fun = BitConverter.ToUInt16(data, 10); - DateTime start = GetDateFromBytes(data, 12); - dic[usrid] = new Reputation { user = usrid, reputation = rep, fun = fun, startTracking = start }; - } - } - Utils.Log("ReputationTracking: Loaded " + dic.Count + " users"); - } catch (Exception e) { - Utils.Log("ERROR: problems in loading the Reputation file: " + e.Message); - } - } - public void Save() { - try { - lock (dic) { - try { - if (File.Exists(path)) File.Delete(path); - } catch (Exception e) { - Utils.Log("ERROR: cannot delete old Reputation file: " + path + "\nException: " + e.Message); - return; - } - using (FileStream f = new FileStream(path, FileMode.CreateNew)) { - byte[] data = new byte[16]; - SetDateToBytes(trackingStarted, data, 0); - f.Write(data, 0, 4); - foreach (Reputation r in dic.Values) { - byte[] d = BitConverter.GetBytes(r.user); - int pos = 0; - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - d = BitConverter.GetBytes(r.reputation); - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - d = BitConverter.GetBytes(r.fun); - for (int i = 0; i < d.Length; i++) - data[pos++] = d[i]; - SetDateToBytes(r.startTracking, data, pos); - f.Write(data, 0, 16); - } - f.Flush(); - } + public ReputationTracking() { + dic = new Dictionary(); + if (Utils.db.Reputations != null) { + int num = 0; + foreach (Reputation rep in Utils.db.Reputations) { + num++; + dic[rep.User] = rep; } - Utils.Log("ReputationTracking: Saved " + dic.Count + " users"); - } catch (Exception e) { - Utils.Log("ERROR: problems in saving the Reputation file: " + e.Message); + Utils.Log("Found " + num + " reputation entries"); } } - private void SetDateToBytes(DateTime d, byte[] data, int offset) { - data[offset + 0] = (byte)((d.Year & 0xff00) >> 8); - data[offset + 1] = (byte)(d.Year & 0xff); - data[offset + 2] = (byte)(d.Month & 0xff); - data[offset + 3] = (byte)(d.Day & 0xff); - } - - private DateTime GetDateFromBytes(byte[] data, int offset) { - try { - return new DateTime((data[offset + 0] << 8) + data[offset + 1], data[offset + 2], data[offset + 3]); - } catch (Exception) { - return DateTime.Now; - } - } - - public bool AlterRep(ulong id, bool add) { + public void AlterRep(ulong id, bool add) { if (add) { - if (dic.ContainsKey(id)) dic[id].reputation++; + if (dic.ContainsKey(id)) { + Reputation r = dic[id]; + r.Rep++; + Utils.db.Reputations.Update(r); + Utils.db.SaveChanges(); + } else { - dic.Add(id, new Reputation { user = id, reputation = 1, fun = 0, startTracking = DateTime.Now }); + Reputation r = new Reputation { User = id, Rep = 1, Fun = 0, Tnk = 0, DateAdded = DateTime.Now }; + dic.Add(id, r); + Utils.db.Reputations.Add(r); + Utils.db.SaveChanges(); } - return true; } else { - if (dic.ContainsKey(id) && dic[id].reputation > 0) { - dic[id].reputation--; - return true; + if (dic.ContainsKey(id) && dic[id].Rep > 0) { + Reputation r = dic[id]; + r.Rep--; + Utils.db.Reputations.Update(r); + Utils.db.SaveChanges(); } } - return false; } - public bool AlterFun(ulong id, bool add) { + public void AlterFun(ulong id, bool add) { if (add) { - if (dic.ContainsKey(id)) dic[id].fun++; + if (dic.ContainsKey(id)) { + Reputation r = dic[id]; + r.Fun++; + Utils.db.Reputations.Update(r); + Utils.db.SaveChanges(); + } else { - dic.Add(id, new Reputation { user = id, reputation = 0, fun = 1, startTracking = DateTime.Now }); + Reputation r = new Reputation { User = id, Rep = 0, Fun = 1, Tnk = 0, DateAdded = DateTime.Now }; + dic.Add(id, r); + Utils.db.Reputations.Add(r); + Utils.db.SaveChanges(); } - return true; } else { - if (dic.ContainsKey(id) && dic[id].fun > 0) { - dic[id].fun--; - return true; + if (dic.ContainsKey(id) && dic[id].Fun > 0) { + Reputation r = dic[id]; + r.Fun--; + Utils.db.Reputations.Update(r); + Utils.db.SaveChanges(); } } - return false; } - internal string GetStartDate() { - return trackingStarted.ToString("yyyy/MM/dd"); + internal void AlterThankYou(ulong id) { + if (dic.ContainsKey(id)) { + Reputation r = dic[id]; + r.Tnk++; + Utils.db.Reputations.Update(r); + Utils.db.SaveChanges(); + } + else { + Reputation r = new Reputation { User = id, Rep = 0, Fun = 0, Tnk = 1, DateAdded = DateTime.Now }; + dic.Add(id, r); + Utils.db.Reputations.Add(r); + Utils.db.SaveChanges(); + } } internal IEnumerable GetReputation() { return dic.Values; } + } \ No newline at end of file diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index f6d88cf..ff56806 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Reflection; using System.Threading.Tasks; using DSharpPlus; @@ -35,42 +34,23 @@ static async Task MainAsync(string token, string prefix) { BannedWords.Init(); discord.MessageCreated += async (s, e) => { await BannedWords.CheckMessage(s, e); }; + discord.MessageCreated += AppreciationTracking.ThanksAdded; await CustomCommandsService.LoadCustomCommands(); await discord.ConnectAsync(); // Connects and wait forever Utils.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd") + " --------------------------------"); - AppreciationTracking.Init(); discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; discord.GuildMemberRemoved += MembersTracking.DiscordMemberRemoved; discord.GuildMemberUpdated += MembersTracking.DiscordMemberUpdated; discord.MessageReactionAdded += AppreciationTracking.ReacionAdded; + discord.MessageReactionAdded += EmojisForRole.ReacionAdded; discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; + discord.MessageReactionRemoved += EmojisForRole.ReactionRemoved; await Task.Delay(-1); } - static void TestDb() { // FIXME code to be removed - string dbName = "Database/UPBot.db"; - if (System.IO.File.Exists(dbName)) { - System.IO.File.Delete(dbName); - } - using (var dbContext = new BotDbContext()) { - //Ensure database is created - dbContext.Database.EnsureCreated(); - if (!dbContext.Helpers.Any()) { - dbContext.Helpers.AddRange(new HelperMember[] { - new HelperMember{ Id=1, Name="CPU" }, - new HelperMember{ Id=2, Name="Duck" }, - new HelperMember{ Id=3, Name="Erem" } - }); - dbContext.SaveChanges(); - } - foreach (var help in dbContext.Helpers) { - Console.WriteLine($"HID={help.Id}\tName={help.Name}\tDateTimeAdd={help.DateAdded}"); - } - } - } } } \ No newline at end of file diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index 1fbb852..bbc8e16 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -2,7 +2,6 @@ using DSharpPlus.CommandsNext; using DSharpPlus.Entities; using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading.Tasks; @@ -86,16 +85,6 @@ internal static void ConnectToDb() { // db => "Database/UPBot.db"; db = new BotDbContext(); db.Database.EnsureCreated(); //Ensure database is created - if (false /*!db.Helpers.Any()*/) { - db.Helpers.AddRange(new HelperMember[] { - new HelperMember{ Id=1, Name="CPU" }, - new HelperMember{ Id=2, Name="Duck" }, - new HelperMember{ Id=3, Name="Erem" } - }); - db.SaveChanges(); - } - - Log("DB Connected: " + db.Database.ProviderName); } From dad54060cb6ee76bc10c2cf3c57885f0c4115823 Mon Sep 17 00:00:00 2001 From: CPULL Date: Fri, 20 Aug 2021 16:57:17 +0200 Subject: [PATCH 41/54] More control on EmojiForRoles --- UPBot Code/Actions/AppreciationTracking.cs | 4 +- UPBot Code/DataClasses/EmojiForRoleValue.cs | 26 +++++ UPBot Code/DataClasses/EmojisForRole.cs | 104 +++++++++++++++++++- 3 files changed, 130 insertions(+), 4 deletions(-) diff --git a/UPBot Code/Actions/AppreciationTracking.cs b/UPBot Code/Actions/AppreciationTracking.cs index 8af8c4d..d105d1f 100644 --- a/UPBot Code/Actions/AppreciationTracking.cs +++ b/UPBot Code/Actions/AppreciationTracking.cs @@ -121,7 +121,7 @@ internal static Task ReacionAdded(DiscordClient sender, MessageReactionAddEventA if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) return HandleReactions(emojiId, emojiName, authorId, true); } catch (Exception ex) { - Utils.Log("Error in ReacionAdded: " + ex.Message); + Utils.Log("Error in AppreciationTracking.ReatcionAdded: " + ex.Message); return Task.FromResult(0); } } @@ -143,7 +143,7 @@ internal static Task ReactionRemoved(DiscordClient sender, MessageReactionRemove if (authorId == a.User.Id) return Task.Delay(10); // If member is equal to author ignore (no self emojis) return HandleReactions(emojiId, emojiName, authorId, false); } catch (Exception ex) { - Utils.Log("Error in ReacionAdded: " + ex.Message); + Utils.Log("Error in AppreciationTracking.ReactionAdded: " + ex.Message); return Task.FromResult(0); } } diff --git a/UPBot Code/DataClasses/EmojiForRoleValue.cs b/UPBot Code/DataClasses/EmojiForRoleValue.cs index 2dd14d2..2e59d2d 100644 --- a/UPBot Code/DataClasses/EmojiForRoleValue.cs +++ b/UPBot Code/DataClasses/EmojiForRoleValue.cs @@ -15,4 +15,30 @@ public class EmojiForRoleValue { public string EmojiName { get; set; } public DiscordRole dRole; // Not in DB + + internal byte GetId() { + byte res = 0; + + ulong v = Channel; + for (int i = 0; i < 8; i++) { + res ^= (byte)(v & 0xff); + v >>= 8; + } + v = Message; + for (int i = 0; i < 8; i++) { + res ^= (byte)(v & 0xff); + v >>= 8; + } + v = Role; + for (int i = 0; i < 8; i++) { + res ^= (byte)(v & 0xff); + v >>= 8; + } + v = EmojiId; + for (int i = 0; i < 8; i++) { + res ^= (byte)(v & 0xff); + v >>= 8; + } + return res; + } } diff --git a/UPBot Code/DataClasses/EmojisForRole.cs b/UPBot Code/DataClasses/EmojisForRole.cs index 96c85ff..b450c6e 100644 --- a/UPBot Code/DataClasses/EmojisForRole.cs +++ b/UPBot Code/DataClasses/EmojisForRole.cs @@ -23,6 +23,106 @@ static void GetValues() { } } + [Command("WhatRole")] + [Aliases("WhatRoles", "WhatRoleCanIGet", "WhatRolesCanIGet")] + [Description("List all roles that can be got via an emoji, and a link to the message")] + public async Task WhatRoleCommand(CommandContext ctx) { + Utils.LogUserCommand(ctx); + try { + GetValues(); + if (values.Count == 0) { + DiscordMessage msg = await Utils.BuildEmbedAndExecute("EmojiForRoles List", "No messages are available to provide a role with an emoji.", Utils.LightBlue, ctx, true); + await Utils.DeleteDelayed(30, msg); + await Task.FromResult(0); + } + else { + DiscordEmbedBuilder e = new DiscordEmbedBuilder(); + e.Title = values.Count + (values.Count != 1 ? " messages are" : " message is") + " known to give a Role by Emoji\n"; + DiscordGuild guild = Utils.GetGuild(); + string msg = ""; + foreach (EmojiForRoleValue val in values) { + if (val.dRole == null) val.dRole = guild.GetRole(val.Role); + msg += "- " + val.dRole.Mention + " from the message: [Link to message](https://discord.com/channels/" + guild.Id + "/" + val.Channel + "/" + val.Message + ")\n"; + } + e.Description = msg; + e.Color = Utils.LightBlue; + DiscordMessage answer = await ctx.RespondAsync(e.Build()); + await Utils.DeleteDelayed(30, answer); + await Task.FromResult(0); + } + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("WhatRole", ex)); + } + } + + [Command("ListAndHandleEmojisForRoles")] + [Description("List all roles that can be got via an emoji, with a link to the message, a button to check if the message is still valid, and and a button to remove it")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task ListAndHandleEmojisForRolesCommand(CommandContext ctx) { + Utils.LogUserCommand(ctx); + try { + GetValues(); + if (values.Count == 0) { + DiscordMessage msg = await Utils.BuildEmbedAndExecute("EmojiForRoles List", "No messages are available to provide a role with an emoji.", Utils.Red, ctx, true); + await Utils.DeleteDelayed(30, msg); + await Task.FromResult(0); + } + else { + DiscordEmbedBuilder e = new DiscordEmbedBuilder(); + e.Title = values.Count + (values.Count != 1 ? " messages are" : " message is") + " known to give a Role by Emoji\n"; + DiscordGuild guild = Utils.GetGuild(); + string msg = ""; + foreach (EmojiForRoleValue val in values) { + if (val.dRole == null) val.dRole = guild.GetRole(val.Role); + msg += "- [Jump to message](https://discord.com/channels/" + guild.Id + "/" + val.Channel + "/" + val.Message + ") ❌Remove command: `e4rremove " + val.GetId() + "` Role " + val.dRole.Mention + "\n"; + } + e.Description = msg; + e.Color = Utils.Red; + DiscordMessage answer = await ctx.RespondAsync(e.Build()); + await Utils.DeleteDelayed(30, answer); + await Task.FromResult(0); + } + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("WhatRole", ex)); + } + } + + [Command("e4rremove")] + [Description("used to quickly remove an EmojiForRole command")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task E4rRemoveCommand(CommandContext ctx, int code) { + Utils.LogUserCommand(ctx); + try { + GetValues(); + EmojiForRoleValue toRemove = null; + foreach (EmojiForRoleValue val in values) { + if (val.GetId() == code) { + toRemove = val; + break; + } + } + if (toRemove == null) { + DiscordMessage msg = await Utils.BuildEmbedAndExecute("EmojiForRoles Removal error", "No entry with code " + code + " found!", Utils.Red, ctx, true); + await Utils.DeleteDelayed(30, msg); + await Task.FromResult(0); + } + else { + values.Remove(toRemove); + Utils.db.EmojiForRoles.Remove(toRemove); + Utils.db.SaveChanges(); + Utils.Log("Memeber " + ctx.Member.DisplayName + " removed EmojiForRoles with code " + code + " https://discord.com/channels/" + Utils.GetGuild().Id + "/" + toRemove.Channel + "/" + toRemove.Message); + DiscordMessage answer = await Utils.BuildEmbedAndExecute("EmojiForRoles removal", "Entry with code " + code + " has been removed", Utils.Red, ctx, true); + await Utils.DeleteDelayed(30, answer); + await Task.FromResult(0); + } + } catch (Exception ex) { + await ctx.RespondAsync(Utils.GenerateErrorAnswer("WhatRole", ex)); + } + } + + + + [Command("EmojiForRole")] [Aliases("RoleForEmoji")] [RequireRoles(RoleCheckMode.Any, "Mod", "Owner", "Helper")] // Restrict access to users with the "Mod" or "Owner" role only @@ -102,7 +202,7 @@ internal static Task ReacionAdded(DiscordClient sender, MessageReactionAddEventA string emojiName = a.Emoji.Name; HandleAddingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); } catch (Exception ex) { - Utils.Log("Error in ReacionAdded: " + ex.Message); + Utils.Log("Error in EmojisForRole.ReactionAdded: " + ex.Message); } return Task.FromResult(0); } @@ -113,7 +213,7 @@ internal static Task ReactionRemoved(DiscordClient sender, MessageReactionRemove string emojiName = a.Emoji.Name; HandleRemovingEmojiForRole(a.Message.ChannelId, emojiId, emojiName, a.User, a.Message.Id); } catch (Exception ex) { - Utils.Log("Error in ReacionRemoved: " + ex.Message); + Utils.Log("Error in EmojisForRole.ReactionRemoved: " + ex.Message); } return Task.FromResult(0); } From 2234632ac2bf179149b7623c4dfab040f4ca8bae Mon Sep 17 00:00:00 2001 From: CPULL Date: Sat, 21 Aug 2021 18:45:35 +0200 Subject: [PATCH 42/54] First working Database system with custom Framework --- UPBot Code/BotDbContext.cs | 21 +- UPBot Code/Commands/CustomCommandsService.cs | 62 ++-- UPBot Code/DataClasses/CustomCommand.cs | 88 +++-- UPBot Code/DataClasses/Entity.cs | 41 +++ UPBot Code/DataClasses/ExampleEntity.cs | 13 + UPBot Code/DataClasses/HelperMember.cs | 14 - UPBot Code/Database.cs | 324 +++++++++++++++++++ UPBot Code/Program.cs | 18 +- UPBot Code/Utils.cs | 25 +- UPBot.csproj | 3 + 10 files changed, 509 insertions(+), 100 deletions(-) create mode 100644 UPBot Code/DataClasses/Entity.cs create mode 100644 UPBot Code/DataClasses/ExampleEntity.cs delete mode 100644 UPBot Code/DataClasses/HelperMember.cs create mode 100644 UPBot Code/Database.cs diff --git a/UPBot Code/BotDbContext.cs b/UPBot Code/BotDbContext.cs index b935396..5a69492 100644 --- a/UPBot Code/BotDbContext.cs +++ b/UPBot Code/BotDbContext.cs @@ -2,24 +2,19 @@ using System.Reflection; public class BotDbContext : DbContext { - public DbSet Helpers { get; set; } public DbSet BannedWords { get; set; } public DbSet Reputations { get; set; } public DbSet EmojiForRoles { get; set; } + public DbSet CustomCommands { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Filename=Database/UPBot.db", options => { options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); }); base.OnConfiguring(optionsBuilder); } + protected override void OnModelCreating(ModelBuilder modelBuilder) { - // Map table names - modelBuilder.Entity().ToTable("HelperMember", "UPBotSchema"); - modelBuilder.Entity(entity => { - entity.HasKey(e => e.Id); - entity.HasIndex(e => e.Name);// .IsUnique(); - entity.Property(e => e.DateAdded).HasDefaultValueSql("CURRENT_TIMESTAMP"); - }); modelBuilder.Entity().ToTable("BannedWord", "UPBotSchema"); modelBuilder.Entity(entity => { entity.HasKey(e => e.Word); @@ -44,6 +39,16 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { entity.Property(e => e.EmojiId); entity.Property(e => e. EmojiName); }); + modelBuilder.Entity().ToTable("CustomCommand", "UPBotSchema"); + modelBuilder.Entity(entity => { + entity.Property(e => e.Name); + entity.HasKey(e => e.Name); + entity.Property(e => e.Content); + entity.Property(e => e.Alias0); + entity.Property(e => e.Alias1); + entity.Property(e => e.Alias2); + entity.Property(e => e.Alias3); + }); base.OnModelCreating(modelBuilder); } } diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 79568f7..a6e0a91 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -19,8 +19,8 @@ public class CustomCommandsService : BaseCommandModule internal static DiscordClient DiscordClient { get; set; } internal const string DirectoryNameCC = "CustomCommands"; - [Command("newcc")] - [Aliases("createcc", "addcc", "ccadd", "cccreate", "ccnew")] + [Command("ccnew")] + [Aliases("createcc", "addcc", "ccadd", "cccreate", "newcc")] [Description("**Create** a new Custom Command (so-called 'CC') with a specified name and all aliases if desired " + "(no duplicate alias allowed).\nAfter doing this, the bot will ask you to input the content, which will " + "be displayed once someone invokes this CC. Your entire next message will be used for the content, so " + @@ -30,6 +30,10 @@ public class CustomCommandsService : BaseCommandModule public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of all aliases. The first term is the **main name**, the other ones, separated by a space, are aliases")] params string[] names) { Utils.LogUserCommand(ctx); + if (names.Length == 0) { + await Utils.ErrorCallback(CommandErrors.CommandNotSpecified, ctx); + return; + } names = names.Distinct().ToArray(); foreach (var name in names) { @@ -39,9 +43,9 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al return; } - foreach (var cmd in Commands) + foreach (CustomCommand cmd in Commands) { - if (cmd.Names.Contains(name)) // Check if there is already a CC with one of the names + if (cmd.Contains(name)) // Check if there is already a CC with one of the names { await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); return; @@ -52,8 +56,9 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al string content = await WaitForContent(ctx, names[0]); CustomCommand command = new CustomCommand(names, content); Commands.Add(command); - await WriteToFile(command); - + Utils.db.CustomCommands.Add(command); + Utils.db.SaveChanges(); + string embedMessage = $"CC {names[0]} successfully created and saved!"; await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); } @@ -177,39 +182,19 @@ public async Task ListCC(CommandContext ctx) string allCommands = string.Empty; foreach (var cmd in Commands) { - if(cmd.Names.Length > 0) - allCommands += $"- {cmd.Names[0]} ({(cmd.Names.Length > 1 ? string.Join(", ", cmd.Names.Skip(1)) : string.Empty)}){System.Environment.NewLine}"; + allCommands += $"- {cmd.GetNames()}{System.Environment.NewLine}"; } await Utils.BuildEmbedAndExecute("CC List", allCommands, Utils.Yellow, ctx, true); } - internal static async Task LoadCustomCommands() + internal static void LoadCustomCommands() { - string path = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "CustomCommands"); - if (!Directory.Exists(path)) - Directory.CreateDirectory(path); - - foreach (string fileName in Directory.GetFiles(path)) - { - using (StreamReader sr = File.OpenText(fileName)) - { - string names = await sr.ReadLineAsync(); - if (string.IsNullOrEmpty(names)) - continue; - - string content = string.Empty; - string c; - while ((c = await sr.ReadLineAsync()) != null) - content += c + System.Environment.NewLine; + foreach(CustomCommand cmd in Utils.db.CustomCommands) + Commands.Add(cmd); + } - CustomCommand cmd = new CustomCommand(names.Split(','), content); - Commands.Add(cmd); - } - } - } - - internal static async Task CommandError(CommandsNextExtension extension, CommandErrorEventArgs args) + internal static async Task CommandError(CommandsNextExtension extension, CommandErrorEventArgs args) { if (args.Exception is DSharpPlus.CommandsNext.Exceptions.CommandNotFoundException) { @@ -219,17 +204,6 @@ internal static async Task CommandError(CommandsNextExtension extension, Command } } - private async Task WriteToFile(CustomCommand command) - { - if (!File.Exists(command.FilePath)) - { - await using (StreamWriter sw = File.AppendText(command.FilePath)) - { - await sw.WriteLineAsync(string.Join(',', command.Names)); - await sw.WriteLineAsync(command.Content); - } - } - } private async Task WaitForContent(CommandContext ctx, string name) { @@ -254,6 +228,6 @@ private static bool TryGetCommand(string name, out CustomCommand command) private static CustomCommand GetCommandByName(string name) { - return Commands.FirstOrDefault(cc => cc.Names.Contains(name)); + return Commands.FirstOrDefault(cc => cc.Contains(name)); } } \ No newline at end of file diff --git a/UPBot Code/DataClasses/CustomCommand.cs b/UPBot Code/DataClasses/CustomCommand.cs index 413b77f..dd914d8 100644 --- a/UPBot Code/DataClasses/CustomCommand.cs +++ b/UPBot Code/DataClasses/CustomCommand.cs @@ -1,35 +1,67 @@ -using System.Threading.Tasks; +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; using DSharpPlus.CommandsNext; /// /// Holds information about a CustomCommand /// and contains functions which execute or edit the command /// -public class CustomCommand -{ - public CustomCommand(string[] names, string content) - { - this.Names = names; - this.FilePath = Utils.ConstructPath(CustomCommandsService.DirectoryNameCC, names[0], ".txt"); - this.Content = content; - } - - internal string[] Names { get; private set; } - internal string FilePath { get; } - internal string Content { get; private set; } - - internal async Task ExecuteCommand(CommandContext ctx) - { - await ctx.Channel.SendMessageAsync(Content); - } - - internal void EditCommand(string newContent) - { - this.Content = newContent; - } - - internal void EditCommand(string[] newNames) - { - this.Names = newNames; - } +public class CustomCommand { + + public CustomCommand() { + } + + public CustomCommand(string[] names, string content) { + Name = names[0]; + Content = content; + if (names.Length > 1) Alias0 = names[1]; + if (names.Length > 2) Alias1 = names[2]; + if (names.Length > 3) Alias2 = names[3]; + if (names.Length >= 4) Alias3 = names[4]; + } + + [Key] + [Required] + public string Name { get; set; } + public string Alias0 { get; set; } + public string Alias1 { get; set; } + public string Alias2 { get; set; } + public string Alias3 { get; set; } + [Required] + public string Content { get; set; } + + internal async Task ExecuteCommand(CommandContext ctx) { + await ctx.Channel.SendMessageAsync(Content); + } + + internal void EditCommand(string newContent) { + Content = newContent; + } + + internal void EditCommand(string[] newNames) { + Name = newNames[0]; + if (newNames.Length > 1) Alias0 = newNames[1]; + if (newNames.Length > 2) Alias1 = newNames[2]; + if (newNames.Length > 3) Alias2 = newNames[3]; + if (newNames.Length >= 4) Alias3 = newNames[4]; + } + + internal bool Contains(string name) { + return (Name.Equals(name) || Alias0.Equals(name) || Alias1.Equals(name) || Alias2.Equals(name) || Alias3.Equals(name)); + } + + internal string GetNames() { + string res = ""; + if (string.IsNullOrWhiteSpace(Name)) return string.Empty; + res += Name; + if (string.IsNullOrWhiteSpace(Alias0)) return res; + res += ", " + Alias0; + if (string.IsNullOrWhiteSpace(Alias1)) return res; + res += ", " + Alias1; + if (string.IsNullOrWhiteSpace(Alias2)) return res; + res += ", " + Alias2; + if (string.IsNullOrWhiteSpace(Alias3)) return res; + return res + ", " + Alias3; + } } \ No newline at end of file diff --git a/UPBot Code/DataClasses/Entity.cs b/UPBot Code/DataClasses/Entity.cs new file mode 100644 index 0000000..5d7f063 --- /dev/null +++ b/UPBot Code/DataClasses/Entity.cs @@ -0,0 +1,41 @@ +using System; +using System.Reflection; + +public class Entity { + + public void Debug() { + Type t = GetType(); + + string msg = t.ToString() + "\n"; + foreach (var a in t.GetFields()) { + msg += "- " + a.Name + " " + a.FieldType.Name; + foreach (CustomAttributeData attr in a.CustomAttributes) + msg += " " + attr.ToString(); + msg += "\n"; + } + + Console.WriteLine(msg); + } + + + public class Key : Attribute {} + public class NotNull : Attribute {} + public class Index : Attribute {} + public class Comment : Attribute {} + public class Blob : Attribute {} + + + private FieldInfo _key = null; + public FieldInfo GetKey() { + if (_key != null) return _key; + foreach (FieldInfo field in GetType().GetFields()) { + foreach (CustomAttributeData attr in field.CustomAttributes) + if (attr.AttributeType.Equals(typeof(Key))) { + _key = field; + return _key; + } + } + return null; + } + +} \ No newline at end of file diff --git a/UPBot Code/DataClasses/ExampleEntity.cs b/UPBot Code/DataClasses/ExampleEntity.cs new file mode 100644 index 0000000..b748e8c --- /dev/null +++ b/UPBot Code/DataClasses/ExampleEntity.cs @@ -0,0 +1,13 @@ +using System; + +public class ExampleEntity : Entity { + [Key] + public int id; + public string name; + [Comment] + public string comment; + [Blob] + public byte[] blob; + public long l; + public ulong ul; +} diff --git a/UPBot Code/DataClasses/HelperMember.cs b/UPBot Code/DataClasses/HelperMember.cs deleted file mode 100644 index ac5a790..0000000 --- a/UPBot Code/DataClasses/HelperMember.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -/// -/// HelperMember entity -/// -public class HelperMember { - [Key] - public ulong Id { get; set; } - [Required] - [MaxLength(128)] - public string Name { get; set; } - [Required] - public DateTime DateAdded { get; set; } -} \ No newline at end of file diff --git a/UPBot Code/Database.cs b/UPBot Code/Database.cs new file mode 100644 index 0000000..82c2d1e --- /dev/null +++ b/UPBot Code/Database.cs @@ -0,0 +1,324 @@ +using System; +using System.IO; +using System.Data.SQLite; +using System.Reflection; +using System.Collections.Generic; + +public class Database { + static SQLiteConnection connection = null; + const string DbName = "BotDb"; + static Dictionary entities; + + + public static void InitDb() { + // Do we have the db? + if (File.Exists("Database/BotDb.db")) + connection = new SQLiteConnection("Data Source=Database/" + DbName + ".db; Version=3; Journal Mode=Off; UTF8Encoding=True;"); // Open the database + else + connection = new SQLiteConnection("Data Source=Database/" + DbName + ".db; Version=3; Journal Mode=Off; New=True; UTF8Encoding=True;"); // Create a new database + + // Open the connection + try { + connection.Open(); + Console.WriteLine("DB connection open"); + } catch (Exception ex) { + throw new Exception("Cannot open the database: " + ex.Message); + } + + entities = new Dictionary(); + + } + + public static void AddTable() { + Type t = typeof(T); + if (!typeof(Entity).IsAssignableFrom(t)) + throw new Exception("The class " + t + " does not derive from Entity and cannot be used as database table!"); + + // Check if we have the table in the db + string tableName = t.ToString(); + SQLiteCommand command = new SQLiteCommand(connection); + command.CommandText = "SELECT count(*) FROM " + tableName + ";"; + bool exists = true; + try { + SQLiteDataReader reader = command.ExecuteReader(); + } catch (Exception) { + exists = false; + } + + if (exists) + Console.WriteLine("Table " + tableName + " exists."); + else + Console.WriteLine("Table " + tableName + " does NOT exist!"); + + string theKey = null; + bool donefirst = false; + if (!exists) { + string sql = "create table " + tableName + " ("; + donefirst = false; + foreach (FieldInfo field in t.GetFields()) { + bool comment = false; + bool blob = false; + bool notnull = false; + bool key = false; + bool index = false; // FIXME this is to do + foreach (CustomAttributeData attr in field.CustomAttributes) { + if (attr.AttributeType == typeof(Entity.Blob)) blob = true; + if (attr.AttributeType == typeof(Entity.Comment)) comment = true; + if (attr.AttributeType == typeof(Entity.Key)) { key = true; notnull = true; theKey = field.Name; } + if (attr.AttributeType == typeof(Entity.NotNull)) notnull = true; + if (attr.AttributeType == typeof(Entity.Index)) index = true; + } + + if (donefirst) sql += ", "; + else donefirst = true; + if (blob) sql += field.Name + " BLOB"; + else switch (field.FieldType.Name.ToLowerInvariant()) { + case "int8": sql += field.Name + " SMALLINT"; break; + case "uint8": sql += field.Name + " SMALLINT"; break; + case "byte": sql += field.Name + " SMALLINT"; break; + case "int32": sql += field.Name + " INT"; break; + case "int64": sql += field.Name + " BIGINT"; break; + case "uint64": sql += field.Name + " UNSIGNED BIG INT"; break; + case "string": { + if (comment) sql += field.Name + " TEXT"; + else sql += field.Name + " VARCHAR(256)"; + break; + } + case "bool": sql += field.Name + " TINYINT"; break; + case "datetime": sql += field.Name + " NUMERIC"; break; + case "single": sql += field.Name + " REAL"; break; + case "double": sql += field.Name + " REAL"; break; + case "byte[]": sql += field.Name + " BLOB"; break; + default: + throw new Exception("Unmanaged type: " + field.FieldType.Name + " for class " + t.Name); + } + if (notnull) sql += " NOT NULL"; + if (key) sql += " PRIMARY KEY"; + } + sql += ");"; + if (theKey == null) throw new Exception("Missing [Key] for class " + typeof(T)); + command.CommandText = sql; + command.ExecuteNonQuery(); + } + + + // Construct the entity + EntityDef ed = new EntityDef { type = t }; + foreach (FieldInfo field in t.GetFields()) { + bool blob = false; + foreach (CustomAttributeData attr in field.CustomAttributes) { + if (attr.AttributeType == typeof(Entity.Key)) { theKey = field.Name; ed.key = field; } + if (attr.AttributeType == typeof(Entity.Blob)) { blob = true; } + } + if (blob) ed.fields[field.Name] = FieldType.ByteArray; + else switch (field.FieldType.Name.ToLowerInvariant()) { + case "int8": ed.fields[field.Name] = FieldType.Byte; break; + case "uint8": ed.fields[field.Name] = FieldType.Byte; break; + case "byte": ed.fields[field.Name] = FieldType.Byte; break; + case "int32": ed.fields[field.Name] = FieldType.Int; break; + case "int64": ed.fields[field.Name] = FieldType.Long; break; + case "uint64": ed.fields[field.Name] = FieldType.ULong; break; + case "string": { + if (blob) ed.fields[field.Name] = FieldType.Blob; + else ed.fields[field.Name] = FieldType.String; + break; + } + case "bool": ed.fields[field.Name] = FieldType.Bool; break; + case "datetime": ed.fields[field.Name] = FieldType.Date; break; + case "single": ed.fields[field.Name] = FieldType.Float; break; + case "double": ed.fields[field.Name] = FieldType.Double; break; + case "byte[]": ed.fields[field.Name] = FieldType.ByteArray; break; + default: + throw new Exception("Unmanaged type: " + field.FieldType.Name + " for class " + t.Name); + } + } + if (theKey == null) throw new Exception("Missing key for class " + t); + + // Build the query strings + ed.count = "SELECT Count(*) FROM " + t.ToString() + " WHERE " + theKey + "=@param1"; + ed.select = "SELECT * FROM " + t.ToString(); + ed.delete = "DELETE FROM " + t.Name + " WHERE " + theKey + "=@param1"; + // Insert, Update + string insert = "INSERT INTO " + t.ToString() + " ("; + string insertpost = ") VALUES ("; + string update = "UPDATE " + t.ToString() + " SET "; + donefirst = false; + foreach (FieldInfo field in t.GetFields()) { + if (donefirst) { insert += ", "; insertpost += ", "; update += ", "; } + else donefirst = true; + insert += field.Name; + insertpost += "@p" + field.Name; + update += field.Name + "=@p" + field.Name; + } + ed.insert = insert + insertpost + ");"; + ed.update = update; + entities.Add(t, ed); + + /* + * FIXME add indexes if we will need them + */ + + + } + + public static int Count() { + SQLiteCommand cmd = new SQLiteCommand("SELECT count(*) FROM " + typeof(T), connection); + return Convert.ToInt32(cmd.ExecuteScalar()); + } + + public static void Update(T val) { + Add(val); + } + public static void Insert(T val) { + Add(val); + } + + public static void Add(T val) { + try { + Type t = val.GetType(); + EntityDef ed = entities[t]; + // Get the values with this key from the db + SQLiteCommand cmd = new SQLiteCommand(ed.count, connection); + cmd.Parameters.Add(new SQLiteParameter("@param1", (val as Entity).GetKey().GetValue(val))); + // Do we have our value? + if (Convert.ToInt32(cmd.ExecuteScalar()) > 0) { // Yes -> Update + SQLiteCommand update = new SQLiteCommand(ed.update, connection); + foreach (FieldInfo field in t.GetFields()) { + update.Parameters.Add(new SQLiteParameter("@p" + field.Name, field.GetValue(val))); + } + update.ExecuteNonQuery(); + } + else { // No - Insert + SQLiteCommand insert = new SQLiteCommand(ed.insert, connection); + foreach (FieldInfo field in t.GetFields()) { + insert.Parameters.Add(new SQLiteParameter("@p" + field.Name, field.GetValue(val))); + } + insert.ExecuteNonQuery(); + } + } catch (Exception ex) { + Utils.Log("Error in Adding data for " + val.GetType() + ": " + ex.Message); + } + } + + public static void Delete(T val) { + try { + EntityDef ed = entities[val.GetType()]; + SQLiteCommand cmd = new SQLiteCommand(ed.delete, connection); + cmd.Parameters.Add(new SQLiteParameter("@param1", (val as Entity).GetKey().GetValue(val))); + cmd.ExecuteNonQuery(); + } catch (Exception ex) { + Utils.Log("Error in Deleting data for " + val.GetType() + ": " + ex.Message); + } + } + + public static T Get(object keyvalue) { + try { + Type t = typeof(T); + EntityDef ed = entities[t]; + SQLiteCommand cmd = new SQLiteCommand(ed.select + " WHERE " + ed.key.Name + "=@param1;", connection); + cmd.Parameters.Add(new SQLiteParameter("@param1", keyvalue)); + SQLiteDataReader reader = cmd.ExecuteReader(); + if (!reader.Read()) return default(T); + T res = (T)Activator.CreateInstance(t); + int num = 0; + foreach (FieldInfo field in t.GetFields()) { + FieldType ft = ed.fields[field.Name]; + switch (ft) { + case FieldType.Bool: field.SetValue(res, reader.GetByte(num) != 0); break; + case FieldType.Byte: field.SetValue(res, reader.GetByte(num)); break; + case FieldType.Int: field.SetValue(res, reader.GetInt32(num)); break; + case FieldType.Long: field.SetValue(res, reader.GetInt64(num)); break; + case FieldType.ULong: field.SetValue(res, (ulong)reader.GetInt64(num)); break; + case FieldType.String: field.SetValue(res, reader.GetString(num)); break; + case FieldType.Comment: field.SetValue(res, reader.GetString(num)); break; + case FieldType.Date: field.SetValue(res, reader.GetDateTime(num)); break; + case FieldType.Float: field.SetValue(res, reader.GetFloat(num)); break; + case FieldType.Double: field.SetValue(res, reader.GetDouble(num)); break; + case FieldType.Blob: + case FieldType.ByteArray: + field.SetValue(res, (byte[])reader[field.Name]); + break; + } + num++; + } + return res; + } catch (Exception ex) { + Utils.Log("Error in Reading data for " + typeof(T) + ": " + ex.Message); + } + return default(T); + } + + public static List GetAll() { + try { + Type t = typeof(T); + EntityDef ed = entities[t]; + SQLiteCommand cmd = new SQLiteCommand(ed.select, connection); + SQLiteDataReader reader = cmd.ExecuteReader(); + List res = new List(); + while (reader.Read()) { + T val = (T)Activator.CreateInstance(t); + int num = 0; + foreach (FieldInfo field in t.GetFields()) { + FieldType ft = ed.fields[field.Name]; + switch (ft) { + case FieldType.Bool: field.SetValue(val, reader.GetByte(num) != 0); break; + case FieldType.Byte: field.SetValue(val, reader.GetByte(num)); break; + case FieldType.Int: field.SetValue(val, reader.GetInt32(num)); break; + case FieldType.Long: field.SetValue(val, reader.GetInt64(num)); break; + case FieldType.ULong: field.SetValue(val, (ulong)reader.GetInt64(num)); break; + case FieldType.String: field.SetValue(val, reader.GetString(num)); break; + case FieldType.Comment: field.SetValue(val, reader.GetString(num)); break; + case FieldType.Date: field.SetValue(val, reader.GetDateTime(num)); break; + case FieldType.Float: field.SetValue(val, reader.GetFloat(num)); break; + case FieldType.Double: field.SetValue(val, reader.GetDouble(num)); break; + case FieldType.Blob: + case FieldType.ByteArray: + field.SetValue(val, (byte[])reader[field.Name]); + break; + } + num++; + } + res.Add(val); + } + return res; + } catch (Exception ex) { + Utils.Log("Error in Reading data for " + typeof(T) + ": " + ex.Message); + } + return null; + } + + + /* + GetValue + GetAllValues + + + */ + + class EntityDef { + public Type type; + public FieldInfo key; + public Dictionary fields = new Dictionary(); + public string count; + public string select; + public string insert; + public string update; + public string delete; + } + + enum FieldType { + Bool, + Byte, + Int, + Long, + ULong, + String, + Comment, + Date, + Float, + Double, + Blob, + ByteArray, + } +} + diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index ff56806..dfaad12 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -9,7 +9,21 @@ namespace UPBot { class Program { static void Main(string[] args) { - MainAsync(args[0], (args.Length > 1 && args[1].Length > 0) ? args[1] : "\\").GetAwaiter().GetResult(); + Database.InitDb(); + Database.AddTable(); + ExampleEntity ee = new ExampleEntity { id = 123, comment = "A comment", blob = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, l = long.MaxValue, ul = ulong.MaxValue, name = "The first row in the db" }; + Database.Add(ee); + + ExampleEntity read = Database.Get(123); + Console.WriteLine(read.comment); + + ee.comment = "new comment"; + Database.Add(ee); + + read = Database.Get(123); + Console.WriteLine(read.comment); + + // MainAsync(args[0], (args.Length > 1 && args[1].Length > 0) ? args[1] : "\\").GetAwaiter().GetResult(); } static async Task MainAsync(string token, string prefix) { @@ -36,7 +50,7 @@ static async Task MainAsync(string token, string prefix) { discord.MessageCreated += async (s, e) => { await BannedWords.CheckMessage(s, e); }; discord.MessageCreated += AppreciationTracking.ThanksAdded; - await CustomCommandsService.LoadCustomCommands(); + CustomCommandsService.LoadCustomCommands(); await discord.ConnectAsync(); // Connects and wait forever Utils.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd") + " --------------------------------"); diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index bbc8e16..f8c7cd4 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -1,6 +1,7 @@ using DSharpPlus; using DSharpPlus.CommandsNext; using DSharpPlus.Entities; +using Microsoft.EntityFrameworkCore; using System; using System.Globalization; using System.IO; @@ -12,6 +13,9 @@ /// public static class Utils { + public const int vmajor = 0, vminor = 1, vbuild = 2; + + /// /// Common colors /// @@ -28,7 +32,7 @@ public static class Utils private static DiscordGuild guild; public static string GetVersion() { - return "0.1.1 dev - 2021/08/19"; + return vmajor + "." + vminor + "." + vbuild + " dev - 2021/08/19"; } /// @@ -84,6 +88,14 @@ public static void InitClient(DiscordClient c) { internal static void ConnectToDb() { // db => "Database/UPBot.db"; db = new BotDbContext(); + + + var entityType = db.Model.FindEntityType(typeof(CustomCommand)); + var schema = entityType.GetSchema(); + var tableName = entityType.GetTableName(); + var stableName = entityType.GetSchemaQualifiedTableName(); + + // db.Database.Migrate(); db.Database.EnsureCreated(); //Ensure database is created Log("DB Connected: " + db.Database.ProviderName); } @@ -244,7 +256,7 @@ internal static void Log(string msg) { logs.WriteLine(msg); logs.FlushAsync(); } catch (Exception e) { - string m = e.Message; + _ = e.Message; } } @@ -267,7 +279,7 @@ internal static async Task ErrorCallback(CommandErrors error, CommandContext ctx respond = false; break; case CommandErrors.InvalidParams: - message = "The given parameters are invalid. Enter \\help [commandName] to get help with the usage of the command."; + message = "The given parameters are invalid. Enter `\\help [commandName]` to get help with the usage of the command."; respond = true; break; case CommandErrors.InvalidParamsDelete: @@ -284,6 +296,10 @@ internal static async Task ErrorCallback(CommandErrors error, CommandContext ctx message = "There are no CC's currently."; respond = false; break; + case CommandErrors.CommandNotSpecified: + message = "No command name was specified. Enter `\\help ccnew` to get help with the usage of the command."; + respond = true; + break; } await Utils.BuildEmbedAndExecute("Error", message, red, ctx, respond); @@ -356,5 +372,6 @@ public enum CommandErrors CommandExists, UnknownError, MissingCommand, - NoCustomCommands + NoCustomCommands, + CommandNotSpecified } diff --git a/UPBot.csproj b/UPBot.csproj index 1998b82..f8938ce 100644 --- a/UPBot.csproj +++ b/UPBot.csproj @@ -11,6 +11,9 @@ + + + From f9a87a6eb9052f3480c3bddd23f991f5b0ba88a6 Mon Sep 17 00:00:00 2001 From: CPULL Date: Sat, 21 Aug 2021 20:59:43 +0200 Subject: [PATCH 43/54] Completed the new Database syste, on SQLite --- UPBot Code/BotDbContext.cs | 54 --- UPBot Code/Commands/BannedWords.cs | 17 +- UPBot Code/Commands/CustomCommandsService.cs | 335 +++++++++---------- UPBot Code/DataClasses/BannedWord.cs | 25 +- UPBot Code/DataClasses/CustomCommand.cs | 23 +- UPBot Code/DataClasses/EmojiForRoleValue.cs | 19 +- UPBot Code/DataClasses/EmojisForRole.cs | 20 +- UPBot Code/DataClasses/Entity.cs | 1 + UPBot Code/DataClasses/Reputation.cs | 18 +- UPBot Code/DataClasses/ReputationTracking.cs | 35 +- UPBot Code/Database.cs | 29 +- UPBot Code/Program.cs | 22 +- UPBot Code/Utils.cs | 17 - UPBot.csproj | 2 - 14 files changed, 234 insertions(+), 383 deletions(-) delete mode 100644 UPBot Code/BotDbContext.cs diff --git a/UPBot Code/BotDbContext.cs b/UPBot Code/BotDbContext.cs deleted file mode 100644 index 5a69492..0000000 --- a/UPBot Code/BotDbContext.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using System.Reflection; - -public class BotDbContext : DbContext { - public DbSet BannedWords { get; set; } - public DbSet Reputations { get; set; } - public DbSet EmojiForRoles { get; set; } - public DbSet CustomCommands { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseSqlite("Filename=Database/UPBot.db", options => { - options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName); - }); - base.OnConfiguring(optionsBuilder); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity().ToTable("BannedWord", "UPBotSchema"); - modelBuilder.Entity(entity => { - entity.HasKey(e => e.Word); - entity.HasIndex(e => e.Word).IsUnique(); - entity.Property(e => e.Creator); - entity.Property(e => e.DateAdded).HasDefaultValueSql("CURRENT_TIMESTAMP"); - }); - modelBuilder.Entity().ToTable("Reputation", "UPBotSchema"); - modelBuilder.Entity(entity => { - entity.HasKey(e => e.User); - entity.HasIndex(e => e.User).IsUnique(); - entity.Property(e => e.Rep); - entity.Property(e => e.Fun); - entity.Property(e => e.Tnk); - entity.Property(e => e.DateAdded).HasDefaultValueSql("CURRENT_TIMESTAMP"); - }); - modelBuilder.Entity().ToTable("EmojiForRole", "UPBotSchema"); - modelBuilder.Entity(entity => { - entity.Property(e => e.Channel); - entity.HasKey(e => e.Message); - entity.Property(e => e.Role); - entity.Property(e => e.EmojiId); - entity.Property(e => e. EmojiName); - }); - modelBuilder.Entity().ToTable("CustomCommand", "UPBotSchema"); - modelBuilder.Entity(entity => { - entity.Property(e => e.Name); - entity.HasKey(e => e.Name); - entity.Property(e => e.Content); - entity.Property(e => e.Alias0); - entity.Property(e => e.Alias1); - entity.Property(e => e.Alias2); - entity.Property(e => e.Alias3); - }); - base.OnModelCreating(modelBuilder); - } -} diff --git a/UPBot Code/Commands/BannedWords.cs b/UPBot Code/Commands/BannedWords.cs index f67528e..a699dae 100644 --- a/UPBot Code/Commands/BannedWords.cs +++ b/UPBot Code/Commands/BannedWords.cs @@ -21,15 +21,8 @@ public class BannedWords : BaseCommandModule { private const string directoryName = "Restrictions"; public static void Init() { - bannedWords = new List(); - if (Utils.db.BannedWords != null) { - int num = 0; - foreach (BannedWord bannedWord in Utils.db.BannedWords) { - num++; - bannedWords.Add(bannedWord); - } - Utils.Log("Found " + num + " banned words"); - } + bannedWords = Database.GetAll(); + Utils.Log("Found " + bannedWords.Count + " banned words"); bannedWords.Sort((a, b) => { return a.Word.CompareTo(b.Word); }); } @@ -94,8 +87,7 @@ private async Task> HandleAddRemoveOfBannedWords(CommandCon BannedWord w = new BannedWord(word, ctx.Message.Author.Id); bannedWords.Add(w); bannedWords.Sort((a, b) => { return a.Word.CompareTo(b.Word); }); - Utils.db.BannedWords.Add(w); - Utils.db.SaveChanges(); + Database.Add(w); await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.OK)); return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been added."); @@ -117,8 +109,7 @@ private async Task> HandleAddRemoveOfBannedWords(CommandCon return ctx.Channel.SendMessageAsync("The word \"" + word + "\" is not in the list."); } bannedWords.Remove(found); - Utils.db.BannedWords.Remove(found); - Utils.db.SaveChanges(); + Database.Delete(found); await ctx.Message.CreateReactionAsync(Utils.GetEmoji(EmojiEnum.OK)); return ctx.Channel.SendMessageAsync("The word \"" + word + "\" has been removed."); diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index a6e0a91..97b9d7b 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -13,221 +13,196 @@ /// Moderators can add a new Custom Command using a Discord command /// These "Custom Commands" will only display a specified text as a callback when someone calls them /// -public class CustomCommandsService : BaseCommandModule -{ - private static readonly List Commands = new List(); - internal static DiscordClient DiscordClient { get; set; } - internal const string DirectoryNameCC = "CustomCommands"; - - [Command("ccnew")] - [Aliases("createcc", "addcc", "ccadd", "cccreate", "newcc")] - [Description("**Create** a new Custom Command (so-called 'CC') with a specified name and all aliases if desired " + - "(no duplicate alias allowed).\nAfter doing this, the bot will ask you to input the content, which will " + - "be displayed once someone invokes this CC. Your entire next message will be used for the content, so " + - "be careful what you type!\n\n**Usage:**\n\n- `newcc name` (without alias)\n- `newcc name alias1 alias2`" + - " (with 2 aliases)\n\nThis command can only be invoked by a Mod.")] - [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of all aliases. The first term is the **main name**, the other ones, separated by a space, are aliases")] params string[] names) - { +public class CustomCommandsService : BaseCommandModule { + private static List Commands = null; + internal static DiscordClient DiscordClient { get; set; } + internal const string DirectoryNameCC = "CustomCommands"; + + [Command("ccnew")] + [Aliases("createcc", "addcc", "ccadd", "cccreate", "newcc")] + [Description("**Create** a new Custom Command (so-called 'CC') with a specified name and all aliases if desired " + + "(no duplicate alias allowed).\nAfter doing this, the bot will ask you to input the content, which will " + + "be displayed once someone invokes this CC. Your entire next message will be used for the content, so " + + "be careful what you type!\n\n**Usage:**\n\n- `newcc name` (without alias)\n- `newcc name alias1 alias2`" + + " (with 2 aliases)\n\nThis command can only be invoked by a Mod.")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of all aliases. The first term is the **main name**, the other ones, separated by a space, are aliases")] params string[] names) { Utils.LogUserCommand(ctx); if (names.Length == 0) { await Utils.ErrorCallback(CommandErrors.CommandNotSpecified, ctx); return; } names = names.Distinct().ToArray(); - foreach (var name in names) + foreach (var name in names) { + if (DiscordClient.GetCommandsNext().RegisteredCommands.ContainsKey(name)) // Check if there is a command with one of the names already + { + await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); + return; + } + + foreach (CustomCommand cmd in Commands) { + if (cmd.Contains(name)) // Check if there is already a CC with one of the names { - if (DiscordClient.GetCommandsNext().RegisteredCommands.ContainsKey(name)) // Check if there is a command with one of the names already - { - await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); - return; - } - - foreach (CustomCommand cmd in Commands) - { - if (cmd.Contains(name)) // Check if there is already a CC with one of the names - { - await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); - return; - } - } + await Utils.ErrorCallback(CommandErrors.CommandExists, ctx, name); + return; } + } + } - string content = await WaitForContent(ctx, names[0]); - CustomCommand command = new CustomCommand(names, content); - Commands.Add(command); - Utils.db.CustomCommands.Add(command); - Utils.db.SaveChanges(); + string content = await WaitForContent(ctx, names[0]); + CustomCommand command = new CustomCommand(names, content); + Commands.Add(command); + Database.Add(command); - string embedMessage = $"CC {names[0]} successfully created and saved!"; - await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); - } + string embedMessage = $"CC {names[0]} successfully created and saved!"; + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); + } - [Command("ccdel")] - [Aliases("ccdelete", "ccremove", "delcc", "deletecc", "removecc")] - [Description("**Delete** a Custom Command (so-called 'CC').\n**Attention!** Use the main name of the CC " + - "you entered first when you created it, **not an alias!**\nThe CC will be irrevocably deleted." + - "\n\nThis command can only be invoked by a Mod.")] - [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task DeleteCommand(CommandContext ctx, [Description("Main name of the CC you want to delete")] string name) - { + [Command("ccdel")] + [Aliases("ccdelete", "ccremove", "delcc", "deletecc", "removecc")] + [Description("**Delete** a Custom Command (so-called 'CC').\n**Attention!** Use the main name of the CC " + + "you entered first when you created it, **not an alias!**\nThe CC will be irrevocably deleted." + + "\n\nThis command can only be invoked by a Mod.")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task DeleteCommand(CommandContext ctx, [Description("Main name of the CC you want to delete")] string name) { Utils.LogUserCommand(ctx); string filePath = Utils.ConstructPath(DirectoryNameCC, name, ".txt"); - if (File.Exists(filePath)) - { - File.Delete(filePath); - if (TryGetCommand(name, out CustomCommand cmd)) - Commands.Remove(cmd); - - string embedMessage = $"CC {name} successfully deleted!"; - await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, true); - } + if (File.Exists(filePath)) { + File.Delete(filePath); + if (TryGetCommand(name, out CustomCommand cmd)) + Commands.Remove(cmd); + + string embedMessage = $"CC {name} successfully deleted!"; + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, true); } + } - [Command("ccedit")] - [Aliases("editcc")] - [Description("**Edit** the **content** of a Custom Command (so-called 'CC')." + - "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**" + - "\n\nThis command can only be invoked by a Mod.")] - [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task EditCommand(CommandContext ctx, [Description("Main name of the CC you want to edit")] string name) - { + [Command("ccedit")] + [Aliases("editcc")] + [Description("**Edit** the **content** of a Custom Command (so-called 'CC')." + + "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**" + + "\n\nThis command can only be invoked by a Mod.")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EditCommand(CommandContext ctx, [Description("Main name of the CC you want to edit")] string name) { Utils.LogUserCommand(ctx); string filePath = Utils.ConstructPath(DirectoryNameCC, name, ".txt"); - if (File.Exists(filePath)) - { - string content = await WaitForContent(ctx, name); - string firstLine; - using (StreamReader sr = File.OpenText(filePath)) - firstLine = await sr.ReadLineAsync(); - - - await using (StreamWriter sw = File.CreateText(filePath)) - { - await sw.WriteLineAsync(firstLine); - await sw.WriteLineAsync(content); - } - - if (TryGetCommand(name, out CustomCommand command)) - command.EditCommand(content); - - string embedMessage = $"CC **{name}** successfully edited!"; - await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); - } - else - await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); + if (File.Exists(filePath)) { + string content = await WaitForContent(ctx, name); + string firstLine; + using (StreamReader sr = File.OpenText(filePath)) + firstLine = await sr.ReadLineAsync(); + + + await using (StreamWriter sw = File.CreateText(filePath)) { + await sw.WriteLineAsync(firstLine); + await sw.WriteLineAsync(content); + } + + if (TryGetCommand(name, out CustomCommand command)) + command.EditCommand(content); + + string embedMessage = $"CC **{name}** successfully edited!"; + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); } + else + await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); + } - [Command("cceditname")] - [Aliases("ccnameedit", "editnamecc")] - [Description("**Edit** the **names** (including aliases) of an existing CC." + - "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**" + - "\n\nThis command can only be invoked by a Mod.")] - [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only - public async Task EditCommandName(CommandContext ctx, [Description("A list of new names and aliases, " + + [Command("cceditname")] + [Aliases("ccnameedit", "editnamecc")] + [Description("**Edit** the **names** (including aliases) of an existing CC." + + "\n**Attention!** Use the main name of the CC you entered first when you created it, **not an alias!**" + + "\n\nThis command can only be invoked by a Mod.")] + [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only + public async Task EditCommandName(CommandContext ctx, [Description("A list of new names and aliases, " + "__**BUT**__ the **FIRST** term is the current **main name** " + "of the CC whose name you want to edit, the **SECOND** term " + - "is the new **main name** and all the other terms are new aliases")] params string[] names) - { + "is the new **main name** and all the other terms are new aliases")] params string[] names) { Utils.LogUserCommand(ctx); names = names.Distinct().ToArray(); - if (names.Length < 2) - { - await Utils.ErrorCallback(CommandErrors.InvalidParams, ctx); - return; - } - - string filePath = Utils.ConstructPath(DirectoryNameCC, names[0], ".txt"); - if (File.Exists(filePath)) - { - if (TryGetCommand(names[0], out CustomCommand command)) - command.EditCommand(names.Skip(1).ToArray()); - - string content = string.Empty; - using (StreamReader sr = File.OpenText(filePath)) - { - string c; - await sr.ReadLineAsync(); - while ((c = await sr.ReadLineAsync()) != null) - content += c + System.Environment.NewLine; - } - - string newPath = Utils.ConstructPath(DirectoryNameCC, names[1], ".txt"); - File.Move(filePath, newPath); - using (StreamWriter sw = File.CreateText(newPath)) - { - await sw.WriteLineAsync(string.Join(',', names.Skip(1))); - await sw.WriteLineAsync(content); - } - - string embedDescription = "The CC names have been successfully edited."; - await Utils.BuildEmbedAndExecute("Success", embedDescription, Utils.Green, ctx, false); - } - else - await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); + if (names.Length < 2) { + await Utils.ErrorCallback(CommandErrors.InvalidParams, ctx); + return; } - [Command("cclist")] - [Aliases("listcc")] - [Description("Get a list of all Custom Commands (CC's).")] - public async Task ListCC(CommandContext ctx) - { - Utils.LogUserCommand(ctx); - if (Commands.Count <= 0) - { - await Utils.ErrorCallback(CommandErrors.NoCustomCommands, ctx); - return; - } + string filePath = Utils.ConstructPath(DirectoryNameCC, names[0], ".txt"); + if (File.Exists(filePath)) { + if (TryGetCommand(names[0], out CustomCommand command)) + command.EditCommand(names.Skip(1).ToArray()); + + string content = string.Empty; + using (StreamReader sr = File.OpenText(filePath)) { + string c; + await sr.ReadLineAsync(); + while ((c = await sr.ReadLineAsync()) != null) + content += c + System.Environment.NewLine; + } + + string newPath = Utils.ConstructPath(DirectoryNameCC, names[1], ".txt"); + File.Move(filePath, newPath); + using (StreamWriter sw = File.CreateText(newPath)) { + await sw.WriteLineAsync(string.Join(',', names.Skip(1))); + await sw.WriteLineAsync(content); + } + + string embedDescription = "The CC names have been successfully edited."; + await Utils.BuildEmbedAndExecute("Success", embedDescription, Utils.Green, ctx, false); + } + else + await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); + } - string allCommands = string.Empty; - foreach (var cmd in Commands) - { - allCommands += $"- {cmd.GetNames()}{System.Environment.NewLine}"; - } + [Command("cclist")] + [Aliases("listcc")] + [Description("Get a list of all Custom Commands (CC's).")] + public async Task ListCC(CommandContext ctx) { + Utils.LogUserCommand(ctx); + if (Commands.Count <= 0) { + await Utils.ErrorCallback(CommandErrors.NoCustomCommands, ctx); + return; + } - await Utils.BuildEmbedAndExecute("CC List", allCommands, Utils.Yellow, ctx, true); + string allCommands = string.Empty; + foreach (var cmd in Commands) { + allCommands += $"- {cmd.GetNames()}{System.Environment.NewLine}"; } - internal static void LoadCustomCommands() - { - foreach(CustomCommand cmd in Utils.db.CustomCommands) - Commands.Add(cmd); + await Utils.BuildEmbedAndExecute("CC List", allCommands, Utils.Yellow, ctx, true); } - internal static async Task CommandError(CommandsNextExtension extension, CommandErrorEventArgs args) - { - if (args.Exception is DSharpPlus.CommandsNext.Exceptions.CommandNotFoundException) - { - string commandName = args.Context.Message.Content.Split(' ')[0].Substring(1); - if (TryGetCommand(commandName, out CustomCommand command)) - await command.ExecuteCommand(args.Context); - } + internal static void LoadCustomCommands() { + + Commands = Database.GetAll(); + } + + internal static async Task CommandError(CommandsNextExtension extension, CommandErrorEventArgs args) { + if (args.Exception is DSharpPlus.CommandsNext.Exceptions.CommandNotFoundException) { + string commandName = args.Context.Message.Content.Split(' ')[0].Substring(1); + if (TryGetCommand(commandName, out CustomCommand command)) + await command.ExecuteCommand(args.Context); } + } - private async Task WaitForContent(CommandContext ctx, string name) - { - string embedMessage = $"Please input the content of the CC **{name}** in one single message. Your next message will count as the content."; - await Utils.BuildEmbedAndExecute("Waiting for interaction", embedMessage, Utils.LightBlue, ctx, true); + private async Task WaitForContent(CommandContext ctx, string name) { + string embedMessage = $"Please input the content of the CC **{name}** in one single message. Your next message will count as the content."; + await Utils.BuildEmbedAndExecute("Waiting for interaction", embedMessage, Utils.LightBlue, ctx, true); - string content = string.Empty; - await ctx.Message.GetNextMessageAsync(m => - { - content = m.Content; - return true; - }); - - return content; - } + string content = string.Empty; + await ctx.Message.GetNextMessageAsync(m => { + content = m.Content; + return true; + }); - private static bool TryGetCommand(string name, out CustomCommand command) - { - command = GetCommandByName(name); - return command != null; - } + return content; + } - private static CustomCommand GetCommandByName(string name) - { - return Commands.FirstOrDefault(cc => cc.Contains(name)); - } + private static bool TryGetCommand(string name, out CustomCommand command) { + command = GetCommandByName(name); + return command != null; + } + + private static CustomCommand GetCommandByName(string name) { + return Commands.FirstOrDefault(cc => cc.Contains(name)); + } } \ No newline at end of file diff --git a/UPBot Code/DataClasses/BannedWord.cs b/UPBot Code/DataClasses/BannedWord.cs index 5139aad..eb35c8d 100644 --- a/UPBot Code/DataClasses/BannedWord.cs +++ b/UPBot Code/DataClasses/BannedWord.cs @@ -1,32 +1,19 @@ using System; -using System.ComponentModel.DataAnnotations; -public class BannedWord { +public class BannedWord : Entity { [Key] - [Required] - public string Word { get; set; } - [Required] - public ulong Creator { get; set; } - [Required] - public DateTime DateAdded { get; set; } + public string Word; + public ulong Creator; + public DateTime DateAdded; + + public BannedWord() { } public BannedWord(string w, ulong id) { Word = w; Creator = id; DateAdded = DateTime.Now; } - public BannedWord() { - Word = ""; - Creator = 0; - DateAdded = DateTime.Now; - } - - public BannedWord(string w, ulong id, DateTime d) { - Word = w; - Creator = id; - DateAdded = d; - } public override string ToString() { return Word + "\t" + Creator + "\t" + DateAdded + "\n"; diff --git a/UPBot Code/DataClasses/CustomCommand.cs b/UPBot Code/DataClasses/CustomCommand.cs index dd914d8..b30ab68 100644 --- a/UPBot Code/DataClasses/CustomCommand.cs +++ b/UPBot Code/DataClasses/CustomCommand.cs @@ -1,16 +1,13 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Threading.Tasks; +using System.Threading.Tasks; using DSharpPlus.CommandsNext; /// /// Holds information about a CustomCommand /// and contains functions which execute or edit the command /// -public class CustomCommand { +public class CustomCommand : Entity { - public CustomCommand() { - } + public CustomCommand() { } public CustomCommand(string[] names, string content) { Name = names[0]; @@ -22,14 +19,12 @@ public CustomCommand(string[] names, string content) { } [Key] - [Required] - public string Name { get; set; } - public string Alias0 { get; set; } - public string Alias1 { get; set; } - public string Alias2 { get; set; } - public string Alias3 { get; set; } - [Required] - public string Content { get; set; } + public string Name; + public string Alias0; + public string Alias1; + public string Alias2; + public string Alias3; + public string Content; internal async Task ExecuteCommand(CommandContext ctx) { await ctx.Channel.SendMessageAsync(Content); diff --git a/UPBot Code/DataClasses/EmojiForRoleValue.cs b/UPBot Code/DataClasses/EmojiForRoleValue.cs index 2e59d2d..da29802 100644 --- a/UPBot Code/DataClasses/EmojiForRoleValue.cs +++ b/UPBot Code/DataClasses/EmojiForRoleValue.cs @@ -1,19 +1,14 @@ using DSharpPlus.Entities; -using System.ComponentModel.DataAnnotations; -public class EmojiForRoleValue { - [Required] - public ulong Channel { get; set; } +public class EmojiForRoleValue : Entity { + public ulong Channel; [Key] - [Required] - public ulong Message { get; set; } - [Required] - public ulong Role { get; set; } - [Required] - public ulong EmojiId { get; set; } - [Required] - public string EmojiName { get; set; } + public ulong Message; + public ulong Role; + public ulong EmojiId; + public string EmojiName; + [NotPersistent] public DiscordRole dRole; // Not in DB internal byte GetId() { diff --git a/UPBot Code/DataClasses/EmojisForRole.cs b/UPBot Code/DataClasses/EmojisForRole.cs index b450c6e..a100006 100644 --- a/UPBot Code/DataClasses/EmojisForRole.cs +++ b/UPBot Code/DataClasses/EmojisForRole.cs @@ -12,15 +12,8 @@ public class EmojisForRole : BaseCommandModule { static void GetValues() { if (values != null) return; - values = new List(); - if (Utils.db.EmojiForRoles != null) { - int num = 0; - foreach (EmojiForRoleValue v in Utils.db.EmojiForRoles) { - num++; - values.Add(v); - } - Utils.Log("Found " + num + " EmojiForRoles entries"); - } + values = Database.GetAll(); + Utils.Log("Found " + values.Count + " EmojiForRoles entries"); } [Command("WhatRole")] @@ -108,8 +101,7 @@ public async Task E4rRemoveCommand(CommandContext ctx, int code) { } else { values.Remove(toRemove); - Utils.db.EmojiForRoles.Remove(toRemove); - Utils.db.SaveChanges(); + Database.Delete(toRemove); Utils.Log("Memeber " + ctx.Member.DisplayName + " removed EmojiForRoles with code " + code + " https://discord.com/channels/" + Utils.GetGuild().Id + "/" + toRemove.Channel + "/" + toRemove.Message); DiscordMessage answer = await Utils.BuildEmbedAndExecute("EmojiForRoles removal", "Entry with code " + code + " has been removed", Utils.Red, ctx, true); await Utils.DeleteDelayed(30, answer); @@ -235,8 +227,7 @@ internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji } if (toRemove != null) { values.Remove(toRemove); - Utils.db.EmojiForRoles.Remove(toRemove); - Utils.db.SaveChanges(); + Database.Delete(toRemove); } else { here = true; @@ -249,8 +240,7 @@ internal bool AddRemove(DiscordMessage msg, DiscordRole role, DiscordEmoji emoji dRole = role }; values.Add(v); - Utils.db.EmojiForRoles.Add(v); - Utils.db.SaveChanges(); + Database.Add(v); if (emoji != null) { // Check if we have the emoji already, if not add it diff --git a/UPBot Code/DataClasses/Entity.cs b/UPBot Code/DataClasses/Entity.cs index 5d7f063..0670924 100644 --- a/UPBot Code/DataClasses/Entity.cs +++ b/UPBot Code/DataClasses/Entity.cs @@ -23,6 +23,7 @@ public class NotNull : Attribute {} public class Index : Attribute {} public class Comment : Attribute {} public class Blob : Attribute {} + public class NotPersistent : Attribute {} private FieldInfo _key = null; diff --git a/UPBot Code/DataClasses/Reputation.cs b/UPBot Code/DataClasses/Reputation.cs index 4eb687c..c5db471 100644 --- a/UPBot Code/DataClasses/Reputation.cs +++ b/UPBot Code/DataClasses/Reputation.cs @@ -1,18 +1,12 @@ using System; -using System.ComponentModel.DataAnnotations; -public class Reputation { +public class Reputation : Entity { [Key] - [Required] - public ulong User { get; set; } - [Required] - public ushort Rep { get; set; } - [Required] - public ushort Fun { get; set; } - [Required] - public ushort Tnk { get; set; } - [Required] - public DateTime DateAdded { get; set; } + public ulong User; + public int Rep; + public int Fun; + public int Tnk; + public DateTime DateAdded; } diff --git a/UPBot Code/DataClasses/ReputationTracking.cs b/UPBot Code/DataClasses/ReputationTracking.cs index cb307a6..fb40447 100644 --- a/UPBot Code/DataClasses/ReputationTracking.cs +++ b/UPBot Code/DataClasses/ReputationTracking.cs @@ -7,14 +7,11 @@ public class ReputationTracking { public ReputationTracking() { dic = new Dictionary(); - if (Utils.db.Reputations != null) { - int num = 0; - foreach (Reputation rep in Utils.db.Reputations) { - num++; - dic[rep.User] = rep; - } - Utils.Log("Found " + num + " reputation entries"); + List all = Database.GetAll(); + foreach (Reputation rep in all) { + dic[rep.User] = rep; } + Utils.Log("Found " + all.Count + " reputation entries"); } public void AlterRep(ulong id, bool add) { @@ -22,22 +19,19 @@ public void AlterRep(ulong id, bool add) { if (dic.ContainsKey(id)) { Reputation r = dic[id]; r.Rep++; - Utils.db.Reputations.Update(r); - Utils.db.SaveChanges(); + Database.Update(r); } else { Reputation r = new Reputation { User = id, Rep = 1, Fun = 0, Tnk = 0, DateAdded = DateTime.Now }; dic.Add(id, r); - Utils.db.Reputations.Add(r); - Utils.db.SaveChanges(); + Database.Add(r); } } else { if (dic.ContainsKey(id) && dic[id].Rep > 0) { Reputation r = dic[id]; r.Rep--; - Utils.db.Reputations.Update(r); - Utils.db.SaveChanges(); + Database.Update(r); } } } @@ -46,22 +40,19 @@ public void AlterFun(ulong id, bool add) { if (dic.ContainsKey(id)) { Reputation r = dic[id]; r.Fun++; - Utils.db.Reputations.Update(r); - Utils.db.SaveChanges(); + Database.Update(r); } else { Reputation r = new Reputation { User = id, Rep = 0, Fun = 1, Tnk = 0, DateAdded = DateTime.Now }; dic.Add(id, r); - Utils.db.Reputations.Add(r); - Utils.db.SaveChanges(); + Database.Add(r); } } else { if (dic.ContainsKey(id) && dic[id].Fun > 0) { Reputation r = dic[id]; r.Fun--; - Utils.db.Reputations.Update(r); - Utils.db.SaveChanges(); + Database.Update(r); } } } @@ -70,14 +61,12 @@ internal void AlterThankYou(ulong id) { if (dic.ContainsKey(id)) { Reputation r = dic[id]; r.Tnk++; - Utils.db.Reputations.Update(r); - Utils.db.SaveChanges(); + Database.Update(r); } else { Reputation r = new Reputation { User = id, Rep = 0, Fun = 0, Tnk = 1, DateAdded = DateTime.Now }; dic.Add(id, r); - Utils.db.Reputations.Add(r); - Utils.db.SaveChanges(); + Database.Add(r); } } diff --git a/UPBot Code/Database.cs b/UPBot Code/Database.cs index 82c2d1e..fd076c2 100644 --- a/UPBot Code/Database.cs +++ b/UPBot Code/Database.cs @@ -51,23 +51,29 @@ public static void AddTable() { Console.WriteLine("Table " + tableName + " does NOT exist!"); string theKey = null; - bool donefirst = false; + bool donefirst; if (!exists) { string sql = "create table " + tableName + " ("; donefirst = false; + string index = null; foreach (FieldInfo field in t.GetFields()) { bool comment = false; bool blob = false; bool notnull = false; bool key = false; - bool index = false; // FIXME this is to do + bool ignore = false; foreach (CustomAttributeData attr in field.CustomAttributes) { if (attr.AttributeType == typeof(Entity.Blob)) blob = true; if (attr.AttributeType == typeof(Entity.Comment)) comment = true; if (attr.AttributeType == typeof(Entity.Key)) { key = true; notnull = true; theKey = field.Name; } if (attr.AttributeType == typeof(Entity.NotNull)) notnull = true; - if (attr.AttributeType == typeof(Entity.Index)) index = true; + if (attr.AttributeType == typeof(Entity.Index)) { + if (index == null) index = "CREATE INDEX idx_" + tableName + " ON " + tableName + "(" + field.Name; + else index += ", " + field.Name; + } + if (attr.AttributeType == typeof(Entity.NotPersistent)) ignore = true; } + if (ignore) continue; if (donefirst) sql += ", "; else donefirst = true; @@ -99,16 +105,26 @@ public static void AddTable() { if (theKey == null) throw new Exception("Missing [Key] for class " + typeof(T)); command.CommandText = sql; command.ExecuteNonQuery(); - } + if (index != null) { + command.CommandText = index; + command.ExecuteNonQuery(); + } + } // Construct the entity EntityDef ed = new EntityDef { type = t }; foreach (FieldInfo field in t.GetFields()) { bool blob = false; + bool ignore = false; foreach (CustomAttributeData attr in field.CustomAttributes) { if (attr.AttributeType == typeof(Entity.Key)) { theKey = field.Name; ed.key = field; } if (attr.AttributeType == typeof(Entity.Blob)) { blob = true; } + if (attr.AttributeType == typeof(Entity.NotPersistent)) { ignore = true; } + } + if (ignore) { + ed.fields[field.Name] = FieldType.IGNORE; + continue; } if (blob) ed.fields[field.Name] = FieldType.ByteArray; else switch (field.FieldType.Name.ToLowerInvariant()) { @@ -204,7 +220,7 @@ public static void Delete(T val) { try { EntityDef ed = entities[val.GetType()]; SQLiteCommand cmd = new SQLiteCommand(ed.delete, connection); - cmd.Parameters.Add(new SQLiteParameter("@param1", (val as Entity).GetKey().GetValue(val))); + cmd.Parameters.Add(new SQLiteParameter("@param1", ed.key.GetValue(val))); cmd.ExecuteNonQuery(); } catch (Exception ex) { Utils.Log("Error in Deleting data for " + val.GetType() + ": " + ex.Message); @@ -252,7 +268,7 @@ public static List GetAll() { try { Type t = typeof(T); EntityDef ed = entities[t]; - SQLiteCommand cmd = new SQLiteCommand(ed.select, connection); + SQLiteCommand cmd = new SQLiteCommand(ed.select+";", connection); SQLiteDataReader reader = cmd.ExecuteReader(); List res = new List(); while (reader.Read()) { @@ -307,6 +323,7 @@ class EntityDef { } enum FieldType { + IGNORE, Bool, Byte, Int, diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index dfaad12..c09a7fb 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -9,21 +9,7 @@ namespace UPBot { class Program { static void Main(string[] args) { - Database.InitDb(); - Database.AddTable(); - ExampleEntity ee = new ExampleEntity { id = 123, comment = "A comment", blob = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, l = long.MaxValue, ul = ulong.MaxValue, name = "The first row in the db" }; - Database.Add(ee); - - ExampleEntity read = Database.Get(123); - Console.WriteLine(read.comment); - - ee.comment = "new comment"; - Database.Add(ee); - - read = Database.Get(123); - Console.WriteLine(read.comment); - - // MainAsync(args[0], (args.Length > 1 && args[1].Length > 0) ? args[1] : "\\").GetAwaiter().GetResult(); + MainAsync(args[0], (args.Length > 1 && args[1].Length > 0) ? args[1] : "\\").GetAwaiter().GetResult(); } static async Task MainAsync(string token, string prefix) { @@ -38,7 +24,11 @@ static async Task MainAsync(string token, string prefix) { CustomCommandsService.DiscordClient = discord; Utils.InitClient(discord); - Utils.ConnectToDb(); + Database.InitDb(); + Database.AddTable(); + Database.AddTable(); + Database.AddTable(); + Database.AddTable(); CommandsNextExtension commands = discord.UseCommandsNext(new CommandsNextConfiguration() { StringPrefixes = new[] { prefix[0].ToString() } // The backslash will be the default command prefix if not specified in the parameters diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index f8c7cd4..15a529d 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -1,7 +1,6 @@ using DSharpPlus; using DSharpPlus.CommandsNext; using DSharpPlus.Entities; -using Microsoft.EntityFrameworkCore; using System; using System.Globalization; using System.IO; @@ -84,22 +83,6 @@ public static void InitClient(DiscordClient c) { else logs = File.CreateText(logPath); } - internal static BotDbContext db; - internal static void ConnectToDb() { - // db => "Database/UPBot.db"; - db = new BotDbContext(); - - - var entityType = db.Model.FindEntityType(typeof(CustomCommand)); - var schema = entityType.GetSchema(); - var tableName = entityType.GetTableName(); - var stableName = entityType.GetSchemaQualifiedTableName(); - - // db.Database.Migrate(); - db.Database.EnsureCreated(); //Ensure database is created - Log("DB Connected: " + db.Database.ProviderName); - } - /// /// Change a string based on the count it's referring to (e.g. "one apple", "two apples") /// diff --git a/UPBot.csproj b/UPBot.csproj index f8938ce..d2560f5 100644 --- a/UPBot.csproj +++ b/UPBot.csproj @@ -9,8 +9,6 @@ - - From cfbb4a860b321d7567bf793d9902cf5218e389de Mon Sep 17 00:00:00 2001 From: CPULL Date: Sat, 21 Aug 2021 21:07:57 +0200 Subject: [PATCH 44/54] Fixed a part of the code of CustomCommands that was still using files --- UPBot Code/Commands/CustomCommandsService.cs | 55 ++++---------------- UPBot Code/DataClasses/CustomCommand.cs | 2 + UPBot Code/Database.cs | 10 ++++ UPBot Code/Utils.cs | 3 +- 4 files changed, 23 insertions(+), 47 deletions(-) diff --git a/UPBot Code/Commands/CustomCommandsService.cs b/UPBot Code/Commands/CustomCommandsService.cs index 97b9d7b..416645c 100644 --- a/UPBot Code/Commands/CustomCommandsService.cs +++ b/UPBot Code/Commands/CustomCommandsService.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; using DSharpPlus; @@ -16,7 +15,6 @@ public class CustomCommandsService : BaseCommandModule { private static List Commands = null; internal static DiscordClient DiscordClient { get; set; } - internal const string DirectoryNameCC = "CustomCommands"; [Command("ccnew")] [Aliases("createcc", "addcc", "ccadd", "cccreate", "newcc")] @@ -66,11 +64,8 @@ public async Task CreateCommand(CommandContext ctx, [Description("A 'list' of al [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task DeleteCommand(CommandContext ctx, [Description("Main name of the CC you want to delete")] string name) { Utils.LogUserCommand(ctx); - string filePath = Utils.ConstructPath(DirectoryNameCC, name, ".txt"); - if (File.Exists(filePath)) { - File.Delete(filePath); - if (TryGetCommand(name, out CustomCommand cmd)) - Commands.Remove(cmd); + if (TryGetCommand(name, out CustomCommand cmd)) { + Commands.Remove(cmd); string embedMessage = $"CC {name} successfully deleted!"; await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, true); @@ -85,27 +80,12 @@ public async Task DeleteCommand(CommandContext ctx, [Description("Main name of t [RequireRoles(RoleCheckMode.Any, "Mod", "Owner")] // Restrict access to users with the "Mod" or "Owner" role only public async Task EditCommand(CommandContext ctx, [Description("Main name of the CC you want to edit")] string name) { Utils.LogUserCommand(ctx); - string filePath = Utils.ConstructPath(DirectoryNameCC, name, ".txt"); - if (File.Exists(filePath)) { - string content = await WaitForContent(ctx, name); - string firstLine; - using (StreamReader sr = File.OpenText(filePath)) - firstLine = await sr.ReadLineAsync(); + string content = await WaitForContent(ctx, name); + if (TryGetCommand(name, out CustomCommand command)) + command.EditCommand(content); - - await using (StreamWriter sw = File.CreateText(filePath)) { - await sw.WriteLineAsync(firstLine); - await sw.WriteLineAsync(content); - } - - if (TryGetCommand(name, out CustomCommand command)) - command.EditCommand(content); - - string embedMessage = $"CC **{name}** successfully edited!"; - await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); - } - else - await Utils.ErrorCallback(CommandErrors.MissingCommand, ctx); + string embedMessage = $"CC **{name}** successfully edited!"; + await Utils.BuildEmbedAndExecute("Success", embedMessage, Utils.Green, ctx, false); } [Command("cceditname")] @@ -125,25 +105,10 @@ public async Task EditCommand(CommandContext ctx, [Description("Main name of the return; } - string filePath = Utils.ConstructPath(DirectoryNameCC, names[0], ".txt"); - if (File.Exists(filePath)) { - if (TryGetCommand(names[0], out CustomCommand command)) - command.EditCommand(names.Skip(1).ToArray()); - - string content = string.Empty; - using (StreamReader sr = File.OpenText(filePath)) { - string c; - await sr.ReadLineAsync(); - while ((c = await sr.ReadLineAsync()) != null) - content += c + System.Environment.NewLine; - } + Database.DeleteByKey(names[0]); - string newPath = Utils.ConstructPath(DirectoryNameCC, names[1], ".txt"); - File.Move(filePath, newPath); - using (StreamWriter sw = File.CreateText(newPath)) { - await sw.WriteLineAsync(string.Join(',', names.Skip(1))); - await sw.WriteLineAsync(content); - } + if (TryGetCommand(names[0], out CustomCommand command)) { + command.EditCommand(names.Skip(1).ToArray()); string embedDescription = "The CC names have been successfully edited."; await Utils.BuildEmbedAndExecute("Success", embedDescription, Utils.Green, ctx, false); diff --git a/UPBot Code/DataClasses/CustomCommand.cs b/UPBot Code/DataClasses/CustomCommand.cs index b30ab68..15f7406 100644 --- a/UPBot Code/DataClasses/CustomCommand.cs +++ b/UPBot Code/DataClasses/CustomCommand.cs @@ -32,6 +32,7 @@ internal async Task ExecuteCommand(CommandContext ctx) { internal void EditCommand(string newContent) { Content = newContent; + Database.Update(this); } internal void EditCommand(string[] newNames) { @@ -40,6 +41,7 @@ internal void EditCommand(string[] newNames) { if (newNames.Length > 2) Alias1 = newNames[2]; if (newNames.Length > 3) Alias2 = newNames[3]; if (newNames.Length >= 4) Alias3 = newNames[4]; + Database.Update(this); } internal bool Contains(string name) { diff --git a/UPBot Code/Database.cs b/UPBot Code/Database.cs index fd076c2..7431387 100644 --- a/UPBot Code/Database.cs +++ b/UPBot Code/Database.cs @@ -226,6 +226,16 @@ public static void Delete(T val) { Utils.Log("Error in Deleting data for " + val.GetType() + ": " + ex.Message); } } + public static void DeleteByKey(object key) { + try { + EntityDef ed = entities[typeof(T)]; + SQLiteCommand cmd = new SQLiteCommand(ed.delete, connection); + cmd.Parameters.Add(new SQLiteParameter("@param1", key)); + cmd.ExecuteNonQuery(); + } catch (Exception ex) { + Utils.Log("Error in Deleting data for " + typeof(T) + ": " + ex.Message); + } + } public static T Get(object keyvalue) { try { diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index 15a529d..89ae0ba 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -348,8 +348,7 @@ public enum EmojiEnum { AutoRefactored = 12, } -public enum CommandErrors -{ +public enum CommandErrors { InvalidParams, InvalidParamsDelete, CommandExists, From 5beccc6ad4d358e7fce685b831a0b12f3dfb2f50 Mon Sep 17 00:00:00 2001 From: CPULL Date: Sun, 22 Aug 2021 11:28:53 +0200 Subject: [PATCH 45/54] Fixed an error when the SQL data field was null --- ToDo.txt | 6 +++--- UPBot Code/Database.cs | 14 +++++--------- UPBot Code/Program.cs | 2 ++ UPBot Code/Utils.cs | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/ToDo.txt b/ToDo.txt index 5dd139e..20d067b 100644 --- a/ToDo.txt +++ b/ToDo.txt @@ -1,12 +1,12 @@ Have command to alter the command character [NOT POSSIBLE!] !Check all command names -Add wikis for each command on GitHub !Uniform all error messages with Embeds (use Duck code) !Have all messages and commands to disappear after a while (configurable) if not needed to stay -Have a cmd to configure how long messages should stay !Add TryCatch to all code and commands +!Extend the reputation by checking "tanks and thank you" in messages and add to mentioned mebers or author of referenced message +Add wikis for each command on GitHub +Have a cmd to configure how long messages should stay Add "keep" option to reformat -Extend the reputation by checking "tanks and thank you" in messages and add to mentioned mebers or author of referenced message Errors DelAnsw EmbedRes TryCatch Log@Begin UseSQL MemberTracking | | | | X | | | diff --git a/UPBot Code/Database.cs b/UPBot Code/Database.cs index 7431387..905a90c 100644 --- a/UPBot Code/Database.cs +++ b/UPBot Code/Database.cs @@ -169,12 +169,6 @@ public static void AddTable() { ed.insert = insert + insertpost + ");"; ed.update = update; entities.Add(t, ed); - - /* - * FIXME add indexes if we will need them - */ - - } public static int Count() { @@ -226,11 +220,11 @@ public static void Delete(T val) { Utils.Log("Error in Deleting data for " + val.GetType() + ": " + ex.Message); } } - public static void DeleteByKey(object key) { + public static void DeleteByKey(object keyvalue) { try { EntityDef ed = entities[typeof(T)]; SQLiteCommand cmd = new SQLiteCommand(ed.delete, connection); - cmd.Parameters.Add(new SQLiteParameter("@param1", key)); + cmd.Parameters.Add(new SQLiteParameter("@param1", keyvalue)); cmd.ExecuteNonQuery(); } catch (Exception ex) { Utils.Log("Error in Deleting data for " + typeof(T) + ": " + ex.Message); @@ -248,6 +242,7 @@ public static T Get(object keyvalue) { T res = (T)Activator.CreateInstance(t); int num = 0; foreach (FieldInfo field in t.GetFields()) { + if (reader.IsDBNull(num)) continue; FieldType ft = ed.fields[field.Name]; switch (ft) { case FieldType.Bool: field.SetValue(res, reader.GetByte(num) != 0); break; @@ -286,6 +281,7 @@ public static List GetAll() { int num = 0; foreach (FieldInfo field in t.GetFields()) { FieldType ft = ed.fields[field.Name]; + if (reader.IsDBNull(num)) continue; switch (ft) { case FieldType.Bool: field.SetValue(val, reader.GetByte(num) != 0); break; case FieldType.Byte: field.SetValue(val, reader.GetByte(num)); break; @@ -308,7 +304,7 @@ public static List GetAll() { } return res; } catch (Exception ex) { - Utils.Log("Error in Reading data for " + typeof(T) + ": " + ex.Message); + Utils.Log(" " + typeof(T) + ": " + ex.Message); } return null; } diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index c09a7fb..f5ffa92 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -53,6 +53,8 @@ static async Task MainAsync(string token, string prefix) { discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; discord.MessageReactionRemoved += EmojisForRole.ReactionRemoved; + var b = new DSharpPlus.Entities.DiscordButtonComponent(ButtonStyle.Primary, "TestButton", "I am a test button", false); + await Task.Delay(-1); } diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index 89ae0ba..cbcaacd 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -31,7 +31,7 @@ public static class Utils private static DiscordGuild guild; public static string GetVersion() { - return vmajor + "." + vminor + "." + vbuild + " dev - 2021/08/19"; + return vmajor + "." + vminor + "." + vbuild + " dev - 2021/08/22"; } /// From 4c10a2f3c4594cfbe20062125b2a457b0dcc2909 Mon Sep 17 00:00:00 2001 From: CPULL Date: Sun, 22 Aug 2021 15:59:24 +0200 Subject: [PATCH 46/54] Fixed a problem in updating existing values in the Add method of the Database --- UPBot Code/Database.cs | 6 ++++-- UPBot Code/Program.cs | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/UPBot Code/Database.cs b/UPBot Code/Database.cs index 905a90c..75ea0ae 100644 --- a/UPBot Code/Database.cs +++ b/UPBot Code/Database.cs @@ -167,7 +167,7 @@ public static void AddTable() { update += field.Name + "=@p" + field.Name; } ed.insert = insert + insertpost + ");"; - ed.update = update; + ed.update = update + " WHERE " + theKey + "=@param1"; entities.Add(t, ed); } @@ -189,13 +189,15 @@ public static void Add(T val) { EntityDef ed = entities[t]; // Get the values with this key from the db SQLiteCommand cmd = new SQLiteCommand(ed.count, connection); - cmd.Parameters.Add(new SQLiteParameter("@param1", (val as Entity).GetKey().GetValue(val))); + object key = (val as Entity).GetKey().GetValue(val); + cmd.Parameters.Add(new SQLiteParameter("@param1", key)); // Do we have our value? if (Convert.ToInt32(cmd.ExecuteScalar()) > 0) { // Yes -> Update SQLiteCommand update = new SQLiteCommand(ed.update, connection); foreach (FieldInfo field in t.GetFields()) { update.Parameters.Add(new SQLiteParameter("@p" + field.Name, field.GetValue(val))); } + update.Parameters.Add(new SQLiteParameter("@param1", key)); update.ExecuteNonQuery(); } else { // No - Insert diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index f5ffa92..c09a7fb 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -53,8 +53,6 @@ static async Task MainAsync(string token, string prefix) { discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; discord.MessageReactionRemoved += EmojisForRole.ReactionRemoved; - var b = new DSharpPlus.Entities.DiscordButtonComponent(ButtonStyle.Primary, "TestButton", "I am a test button", false); - await Task.Delay(-1); } From ac07fddbd06cd1caccbcf5d1f1ec26953fd70319 Mon Sep 17 00:00:00 2001 From: CPULL Date: Mon, 23 Aug 2021 12:40:21 +0200 Subject: [PATCH 47/54] Changed the reformat, now it does not delete automatically the reformatted code Fixed a small bug in the sql version of customcommands Added a function in the Utils to check if a user has [semi]admin rights --- UPBot Code/Commands/Refactor.cs | 288 ++++++++++++++++-------- UPBot Code/DataClasses/CustomCommand.cs | 6 +- UPBot Code/Utils.cs | 7 + 3 files changed, 206 insertions(+), 95 deletions(-) diff --git a/UPBot Code/Commands/Refactor.cs b/UPBot Code/Commands/Refactor.cs index 86d9e12..0275bbe 100644 --- a/UPBot Code/Commands/Refactor.cs +++ b/UPBot Code/Commands/Refactor.cs @@ -12,98 +12,167 @@ /// public class Refactor : BaseCommandModule { + enum Action { + Analyze, + Replace, + Keep + } + + enum Langs { + NONE, cs, js, cpp, java, python + } + + /* + /refactor + /refactor lng + /refactor lng replace + /refactor keep lng + /refactor best + /refactor keep + + /refactor + /refactor lng + /refactor lng replace + /refactor keep lng + /refactor best + /refactor keep + + normal users can reformat their own posts + admins can reformat messages from other users + + */ + + [Command("checklanguage")] [Description("Checks what language is in the post you replied to, or the last post of a specified user or just the last post.")] public async Task CheckLanguage(CommandContext ctx) { // Refactors the previous post, if it is code - await RefactorCode(ctx, null, "best"); + await RefactorCode(ctx, null, Action.Analyze, Langs.NONE); } [Command("checklanguage")] [Description("Checks what language is in the post you replied to, or the last post of a specified user or just the last post.")] - public async Task CheckLanguage(CommandContext ctx, [Description("The user the posted the message to check")] DiscordMember member) { // Refactors the previous post, if it is code - await RefactorCode(ctx, member, "best"); + public async Task CheckLanguage(CommandContext ctx, [Description("The user that posted the message to check")] DiscordMember member) { // Refactors the previous post, if it is code + await RefactorCode(ctx, member, Action.Analyze, Langs.NONE); } [Command("reformat")] [Description("Replace a specified post with a reformatted code block using the specified language or the best language")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task RefactorCommand(CommandContext ctx) { // Refactors the previous post, if it is code - await RefactorCode(ctx, null, null); + await RefactorCode(ctx, null, Action.Keep, Langs.NONE); } [Command("reformat")] [Description("Replace the last post of the specified user or the post you replied to with a formatted code block")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task ReformatCommand(CommandContext ctx, [Description("Force the Language to use. Use **Best** or **Analyze** to find the best language.")] string language) { // Refactors the previous post, if it is code - await RefactorCode(ctx, null, language); + public async Task ReformatCommand(CommandContext ctx, [Description("Analyze the language with **Best** or **Analyze**, use **Replace** to replace the refactored post (only your own posts or if you are an admin), specify a **language** if you want to force one.")] string what) { // Refactors the previous post, if it is code + if (IsBest(what)) await RefactorCode(ctx, null, Action.Analyze, Langs.NONE); + else if (IsReplace(what)) await RefactorCode(ctx, null, Action.Replace, Langs.NONE); + else await RefactorCode(ctx, null, Action.Keep, NormalizeLanguage(what)); + } + + [Command("reformat")] + [Description("Replace the last post of the specified user or the post you replied to with a formatted code block")] + public async Task ReformatCommand(CommandContext ctx, [Description("Use **Replace** to replace the refactored post (only your own posts or if you are an admin), or specify a **language** if you want to force one.")] string cmd1, [Description("Use **Replace** to replace the refactored post (only your own posts or if you are an admin), or sa **language** if you want to force one.")] string cmd2) { // Refactors the previous post, if it is code + if (IsBest(cmd1) || IsBest(cmd2)) await RefactorCode(ctx, null, Action.Analyze, Langs.NONE); + else if (IsReplace(cmd1)) await RefactorCode(ctx, null, Action.Replace, NormalizeLanguage(cmd2)); + else if (IsReplace(cmd2)) await RefactorCode(ctx, null, Action.Replace, NormalizeLanguage(cmd1)); + else { + Langs l = NormalizeLanguage(cmd1); + if (l == Langs.NONE) l = NormalizeLanguage(cmd2); + await RefactorCode(ctx, null, Action.Keep, l); + } + } + + [Command("reformat")] + public async Task ReformatCommand(CommandContext ctx, [Description("The user that posted the message to refactor")] DiscordMember member) { // Refactor the last post of the specified user in the channel + await RefactorCode(ctx, member, Action.Keep, Langs.NONE); } [Command("reformat")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task ReformatCommand(CommandContext ctx, [Description("The user the posted the message to refactor")] DiscordMember member) { // Refactor the last post of the specified user in the channel - await RefactorCode(ctx, member, null); + public async Task RefacReformatCommandtorCommand(CommandContext ctx, [Description("The user that posted the message to refactor")] DiscordMember member, [Description("Analyze the language with **Best** or **Analyze**, use **Replace** to replace the refactored post, specify a **language** if you want to force one.")] string what) { // Refactor the last post of the specified user in the channel + if (IsBest(what)) await RefactorCode(ctx, member, Action.Analyze, Langs.NONE); + else if (IsReplace(what)) await RefactorCode(ctx, member, Action.Replace, Langs.NONE); + else await RefactorCode(ctx, member, Action.Keep, NormalizeLanguage(what)); } [Command("reformat")] [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only - public async Task RefacReformatCommandtorCommand(CommandContext ctx, [Description("The user the posted the message to refactor")] DiscordMember member, [Description("Force the Language to use. Use 'best' or 'Analyze' to find the best language.")] string language) { // Refactor the last post of the specified user in the channel - await RefactorCode(ctx, member, language); + public async Task RefacReformatCommandtorCommand(CommandContext ctx, [Description("The user that posted the message to refactor")] DiscordMember member, [Description("Use **Replace** to replace the refactored post (only your own posts or if you are an admin), or specify a **language** if you want to force one.")] string cmd1, [Description("Use **Replace** to replace the refactored post (only your own posts or if you are an admin), or sa **language** if you want to force one.")] string cmd2) { // Refactors the previous post, if it is code + if (IsBest(cmd1) || IsBest(cmd2)) await RefactorCode(ctx, member, Action.Analyze, Langs.NONE); + else if (IsReplace(cmd1)) await RefactorCode(ctx, member, Action.Replace, NormalizeLanguage(cmd2)); + else if (IsReplace(cmd2)) await RefactorCode(ctx, member, Action.Replace, NormalizeLanguage(cmd1)); + else { + Langs l = NormalizeLanguage(cmd1); + if (l == Langs.NONE) l = NormalizeLanguage(cmd2); + await RefactorCode(ctx, member, Action.Keep, l); + } } - private async Task> RefactorCode(CommandContext ctx, DiscordMember m, string language) { + private async Task> RefactorCode(CommandContext ctx, DiscordMember m, Action action, Langs lang) { Utils.LogUserCommand(ctx); + try { - DiscordChannel c = ctx.Channel; + // Find the message DiscordMessage toRefactor = null; + Langs msgLang = Langs.NONE; if (ctx.Message.Reference != null) toRefactor = ctx.Message.Reference.Message; - else { - IReadOnlyList msgs = await c.GetMessagesAsync(50); - if (m == null) toRefactor = msgs[1]; - else { - for (int i = 1; i < msgs.Count; i++) { - if (msgs[i].Author.Id.Equals(m.Id)) { - toRefactor = msgs[i]; - break; - } + else if (m != null) { // Get last post of the specified member + IReadOnlyList msgs = await ctx.Channel.GetMessagesAsync(50); + for (int i = 1; i < msgs.Count; i++) { + if (msgs[i].Author.Id.Equals(m.Id)) { + toRefactor = msgs[i]; + break; + } + } + } + else { // Get last post that looks like code + IReadOnlyList msgs = await ctx.Channel.GetMessagesAsync(50); + for (int i = 1; i < msgs.Count; i++) { + string content = msgs[i].Content; + msgLang = GetBestMatch(content); + if (msgLang != Langs.NONE) { + toRefactor = msgs[i]; + break; } } - if (toRefactor == null) return ctx.RespondAsync("Nothing to refactor found"); } - // Is the message some code? + if (toRefactor == null) return ctx.RespondAsync("Nothing to refactor found"); + // FIXME If we are not an admin, and the message is not from ourselves, do not accept the replace option. + if (action == Action.Replace && !Utils.IsAdmin(ctx.Member) && toRefactor.Author.Id != ctx.Member.Id) action = Action.Keep; + + // Is the message some code, or at least somethign we recognize? string code = toRefactor.Content; - int weightCs = 0, weightCp = 0, weightJv = 0, weightJs = 0, weightPy = 0; - foreach (LangKWord k in keywords) { - if (k.regexp.IsMatch(code)) { - weightCs += k.wCs; - weightCp += k.wCp; - weightJv += k.wJv; - weightJs += k.wJs; - weightPy += k.wPy; + if (msgLang==Langs.NONE) + msgLang = GetBestMatch(code); + + if (action == Action.Analyze) { + int weightCs = 0, weightCp = 0, weightJv = 0, weightJs = 0, weightPy = 0; + foreach (LangKWord k in keywords) { + if (k.regexp.IsMatch(code)) { + weightCs += k.wCs; + weightCp += k.wCp; + weightJv += k.wJv; + weightJs += k.wJs; + weightPy += k.wPy; + } } + string guessed = "no one"; + switch (msgLang) { + case Langs.cs: guessed = "<:csharp:831465428214743060> C#"; break; + case Langs.js: guessed = "<:Javascript:876103767068647435> Javascript"; break; + case Langs.cpp: guessed = "<:cpp:831465408874676273> C++"; break; + case Langs.java: guessed = "<:java:875852276017815634> Java"; break; + case Langs.python: guessed = "<:python:831465381016895500> Python"; break; + } + return ctx.RespondAsync("Best guess for the language is: " + guessed + "\nC# = " + weightCs + " C++ = " + weightCp + " Java = " + weightJv + " Javascript = " + weightJs + " Python = " + weightPy); } - string guessed = "no one"; - string best = ""; - EmojiEnum langEmoji = EmojiEnum.None; - int w = 0; - if (weightCs > w) { guessed = "<:csharp:831465428214743060> C#"; w = weightCs; best = "cs"; langEmoji = EmojiEnum.CSharp; } - if (weightCp > w) { guessed = "<:cpp:831465408874676273> C++"; w = weightCp; best = "cpp"; langEmoji = EmojiEnum.Cpp; } - if (weightJs > w) { guessed = "<:Javascript:876103767068647435> Javascript"; w = weightJs; best = "js"; langEmoji = EmojiEnum.Javascript; } - if (weightJv > w) { guessed = "<:java:875852276017815634> Java"; w = weightJv; best = "java"; langEmoji = EmojiEnum.Java; } - if (weightPy > w) { guessed = "<:python:831465381016895500> Python"; w = weightPy; best = "python"; langEmoji = EmojiEnum.Python; } - if (w == 0 && language == null) return ctx.RespondAsync("Nothing to reformat"); - - language = NormalizeLanguage(language, best); - if (language == null) - return ctx.RespondAsync("Best guess for the language is: " + guessed + "\nC# = " + weightCs + " C++ = " + weightCp + " Java = " + weightJv + " Javascript = " + weightJs + " Python = " + weightPy); // Remove the ``` at begin and end, if any. And the code name after initial ``` - bool deleteOrig = true; + bool deleteOrig = action == Action.Replace; Match codeMatch = codeBlock.Match(code); if (codeMatch.Success) { code = codeMatch.Groups[5].Value; @@ -112,13 +181,20 @@ private async Task> RefactorCode(CommandContext ctx, Discor code = code.Trim(' ', '\t', '\r', '\n'); code = emptyLines.Replace(code, "\n"); - if (langEmoji == EmojiEnum.CSharp || langEmoji == EmojiEnum.Cpp || langEmoji == EmojiEnum.Java || langEmoji == EmojiEnum.Javascript) code = FixIndentation(code); + if (lang != Langs.NONE) msgLang = lang; + EmojiEnum langEmoji = EmojiEnum.None; + string lmd = ""; + switch (msgLang) { + case Langs.cs: langEmoji = EmojiEnum.CSharp; lmd = "cs"; break; + case Langs.js: langEmoji = EmojiEnum.Javascript; lmd = "js"; break; + case Langs.cpp: langEmoji = EmojiEnum.Cpp; lmd = "cpp"; break; + case Langs.java: langEmoji = EmojiEnum.Java; lmd = "java"; break; + case Langs.python: langEmoji = EmojiEnum.Python; lmd = "python"; break; + } - code = "Reformatted " + toRefactor.Author.Mention + " code\n" + "```" + language + "\n" + code + "\n```"; + if ( langEmoji != EmojiEnum.None && langEmoji != EmojiEnum.Python) code = FixIndentation(code); - if (guessed == "no one" && language != null) { - langEmoji = GetLanguageEmoji(language); - } + code = "Reformatted " + toRefactor.Author.Mention + " code\n" + "```" + lmd + "\n" + code + "\n```"; DiscordMessage replacement = await ctx.Channel.SendMessageAsync(code); DiscordEmoji autoRefactored = Utils.GetEmoji(EmojiEnum.AutoRefactored); @@ -201,47 +277,71 @@ private string FixIndentation(string code) { return res; } - private string NormalizeLanguage(string language, string best) { - if (language == null) return best; - language = language.ToLowerInvariant(); - if (language == "best") return null; - if (language == "what") return null; - if (language == "whatis") return null; - if (language == "analyze") return null; - if (language == "analysis") return null; - if (language == "c#") return "cs"; - if (language == "cs") return "cs"; - if (language == "csharp") return "cs"; - if (language == "cpp") return "cpp"; - if (language == "c++") return "cpp"; - if (language == "java") return "java"; - if (language == "javascript") return "js"; - if (language == "jscript") return "js"; - if (language == "js") return "js"; - if (language == "json") return "js"; - if (language == "typescript") return "js"; - if (language == "phyton") return "python"; - if (language == "python") return "python"; - if (language == "py") return "python"; - return ""; + private Langs GetBestMatch(string code) { + int weightCs = 0, weightCp = 0, weightJv = 0, weightJs = 0, weightPy = 0; + foreach (LangKWord k in keywords) { + if (k.regexp.IsMatch(code)) { + weightCs += k.wCs; + weightCp += k.wCp; + weightJv += k.wJv; + weightJs += k.wJs; + weightPy += k.wPy; + } + } + int w = 0; + Langs res = Langs.NONE; + if (weightCs > w) { w = weightCs; res = Langs.cs; } + if (weightCp > w) { w = weightCp; res = Langs.cpp; } + if (weightJs > w) { w = weightJs; res = Langs.js; } + if (weightJv > w) { w = weightJv; res = Langs.java; } + if (weightPy > w) { w = weightPy; res = Langs.python; } + return res; } - private EmojiEnum GetLanguageEmoji(string language) { + + private bool IsBest(string what) { + if (what == null) return false; + what = what.ToLowerInvariant(); + if (what == "best") return true; + if (what == "what") return true; + if (what == "whatis") return true; + if (what == "analyze") return true; + if (what == "analysis") return true; + return false; + } + + private bool IsReplace(string what) { + if (what == null) return false; + what = what.ToLowerInvariant(); + if (what == "rep") return true; + if (what == "repl") return true; + if (what == "replace") return true; + if (what == "remove") return true; + if (what == "change") return true; + if (what == "substitute") return true; + if (what == "destroy") return true; + if (what == "delete") return true; + return false; + } + + private Langs NormalizeLanguage(string language) { + if (language == null) return Langs.NONE; language = language.ToLowerInvariant(); - if (language == "c#") return EmojiEnum.CSharp; - if (language == "cs") return EmojiEnum.CSharp; - if (language == "csharp") return EmojiEnum.CSharp; - if (language == "cpp") return EmojiEnum.Cpp; - if (language == "c++") return EmojiEnum.Cpp; - if (language == "java") return EmojiEnum.Java; - if (language == "javascript") return EmojiEnum.Javascript; - if (language == "jscript") return EmojiEnum.Javascript; - if (language == "js") return EmojiEnum.Javascript; - if (language == "typescript") return EmojiEnum.Javascript; - if (language == "json") return EmojiEnum.Javascript; - if (language == "phyton") return EmojiEnum.Python; - if (language == "python") return EmojiEnum.Python; - if (language == "py") return EmojiEnum.Python; - return EmojiEnum.None; + if (language == "c#") return Langs.cs; + if (language == "cs") return Langs.cs; + if (language == "csharp") return Langs.cs; + if (language == "cpp") return Langs.cpp; + if (language == "c++") return Langs.cpp; + if (language == "c") return Langs.cpp; + if (language == "java") return Langs.java; + if (language == "javascript") return Langs.js; + if (language == "jscript") return Langs.js; + if (language == "js") return Langs.js; + if (language == "json") return Langs.js; + if (language == "typescript") return Langs.js; + if (language == "phyton") return Langs.python; + if (language == "python") return Langs.python; + if (language == "py") return Langs.python; + return Langs.NONE; } readonly Regex codeBlock = new Regex("(.*)(\\n|\\r|\\r\\n)?(```[a-z]*(\\n|\\r|\\r\\n))(.*)(```[a-z]*(\\n|\\r|\\r\\n)?)", RegexOptions.Singleline, TimeSpan.FromSeconds(1)); diff --git a/UPBot Code/DataClasses/CustomCommand.cs b/UPBot Code/DataClasses/CustomCommand.cs index 15f7406..edee929 100644 --- a/UPBot Code/DataClasses/CustomCommand.cs +++ b/UPBot Code/DataClasses/CustomCommand.cs @@ -45,7 +45,11 @@ internal void EditCommand(string[] newNames) { } internal bool Contains(string name) { - return (Name.Equals(name) || Alias0.Equals(name) || Alias1.Equals(name) || Alias2.Equals(name) || Alias3.Equals(name)); + return ((Name != null && Name.Equals(name)) || + (Alias0 != null && Alias0.Equals(name)) || + (Alias1 != null && Alias1.Equals(name)) || + (Alias2 != null && Alias2.Equals(name)) || + (Alias3 != null && Alias3.Equals(name))); } internal string GetNames() { diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index cbcaacd..919c779 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -328,6 +328,13 @@ static void DelayAfterAWhile(DiscordMessage msg, int delay) { } catch (Exception) { } } + public static bool IsAdmin(DiscordMember m) { + if (m.Permissions.HasFlag(Permissions.Administrator)) return true; + if (m.Permissions.HasFlag(Permissions.ManageMessages)) return true; + foreach (DiscordRole r in m.Roles) + if (r.Id == 830901562960117780ul /* Owner */ || r.Id == 830901743624650783ul /* Mod */ || r.Id == 831050318171078718ul /* Helper */) return true; + return false; + } } From a99226a681025f7f69d68a892f972fc202c4830a Mon Sep 17 00:00:00 2001 From: CPULL Date: Tue, 24 Aug 2021 00:15:18 +0200 Subject: [PATCH 48/54] Fixed a small problems with WhoIs when the user had no relos --- UPBot Code/Commands/WhoIs.cs | 2 +- UPBot Code/Utils.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UPBot Code/Commands/WhoIs.cs b/UPBot Code/Commands/WhoIs.cs index 4e238e2..ec72c59 100644 --- a/UPBot Code/Commands/WhoIs.cs +++ b/UPBot Code/Commands/WhoIs.cs @@ -76,7 +76,7 @@ private Task GenerateWhoIs(CommandContext ctx, DiscordMember m) { } if (num == 1) embed.AddField("Role", roles, false); - else + else if (num != 0) embed.AddField(num + " Roles", roles, false); string perms = ""; // Not all permissions are shown diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index 919c779..52329ee 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -31,7 +31,7 @@ public static class Utils private static DiscordGuild guild; public static string GetVersion() { - return vmajor + "." + vminor + "." + vbuild + " dev - 2021/08/22"; + return vmajor + "." + vminor + "." + vbuild + " dev - 2021/08/23"; } /// From fecba81cfedae07466e2d048892d2f6561ec2345 Mon Sep 17 00:00:00 2001 From: CPULL Date: Tue, 24 Aug 2021 18:44:22 +0200 Subject: [PATCH 49/54] When a user leaves not only the mention but also the name are displayed on the channel --- UPBot Code/Actions/MembersTracking.cs | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/UPBot Code/Actions/MembersTracking.cs b/UPBot Code/Actions/MembersTracking.cs index 0149868..02a3918 100644 --- a/UPBot Code/Actions/MembersTracking.cs +++ b/UPBot Code/Actions/MembersTracking.cs @@ -9,22 +9,22 @@ public class MembersTracking { static DiscordChannel trackChannel = null; public static async Task DiscordMemberRemoved(DiscordClient client, DSharpPlus.EventArgs.GuildMemberRemoveEventArgs args) { - try{ - if (tracking == null) tracking = new Dictionary(); - if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); + try { + if (tracking == null) tracking = new Dictionary(); + if (trackChannel == null) trackChannel = args.Guild.GetChannel(831186370445443104ul); - if (tracking.ContainsKey(args.Member.Id)) { - tracking.Remove(args.Member.Id); - string msg = "User " + args.Member.DisplayName + " did a kiss and go."; - await trackChannel.SendMessageAsync(msg); - Utils.Log(msg); - } - else { - string msgC = Utils.GetEmojiSnowflakeID(EmojiEnum.KO) + " User " + args.Member.Mention + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; - string msgL = "- User " + args.Member.DisplayName + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; - await trackChannel.SendMessageAsync(msgC); - Utils.Log(msgL); - } + if (tracking.ContainsKey(args.Member.Id)) { + tracking.Remove(args.Member.Id); + string msg = "User " + args.Member.DisplayName + " did a kiss and go."; + await trackChannel.SendMessageAsync(msg); + Utils.Log(msg); + } + else { + string msgC = Utils.GetEmojiSnowflakeID(EmojiEnum.KO) + " User " + args.Member.Mention + " (" + args.Member.DisplayName + ") left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; + string msgL = "- User " + args.Member.DisplayName + " left on " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + " (" + args.Guild.MemberCount + " memebrs total)"; + await trackChannel.SendMessageAsync(msgC); + Utils.Log(msgL); + } } catch (Exception ex) { Utils.Log("Error in DiscordMemberRemoved: " + ex.Message); } From a34712b32e9145e1f9ca191de53c4aaaa5eb5e5c Mon Sep 17 00:00:00 2001 From: CPULL Date: Tue, 24 Aug 2021 23:36:44 +0200 Subject: [PATCH 50/54] Fixed a problem in reformatting when the ``` was directly followed with some code --- ToDo.txt | 5 +++-- UPBot Code/Commands/Refactor.cs | 12 +++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ToDo.txt b/ToDo.txt index 20d067b..174fc3a 100644 --- a/ToDo.txt +++ b/ToDo.txt @@ -4,9 +4,10 @@ !Have all messages and commands to disappear after a while (configurable) if not needed to stay !Add TryCatch to all code and commands !Extend the reputation by checking "tanks and thank you" in messages and add to mentioned mebers or author of referenced message -Add wikis for each command on GitHub Have a cmd to configure how long messages should stay -Add "keep" option to reformat +!Add "delete" option to reformat +Add wikis for each command on GitHub +Check if a message to reformat has something after the ```, consider the ``` the end of the block to reformat Errors DelAnsw EmbedRes TryCatch Log@Begin UseSQL MemberTracking | | | | X | | | diff --git a/UPBot Code/Commands/Refactor.cs b/UPBot Code/Commands/Refactor.cs index 0275bbe..1ff098d 100644 --- a/UPBot Code/Commands/Refactor.cs +++ b/UPBot Code/Commands/Refactor.cs @@ -88,8 +88,6 @@ public async Task ReformatCommand(CommandContext ctx, [Description("The user tha } [Command("reformat")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task RefacReformatCommandtorCommand(CommandContext ctx, [Description("The user that posted the message to refactor")] DiscordMember member, [Description("Analyze the language with **Best** or **Analyze**, use **Replace** to replace the refactored post, specify a **language** if you want to force one.")] string what) { // Refactor the last post of the specified user in the channel if (IsBest(what)) await RefactorCode(ctx, member, Action.Analyze, Langs.NONE); else if (IsReplace(what)) await RefactorCode(ctx, member, Action.Replace, Langs.NONE); @@ -97,8 +95,6 @@ public async Task ReformatCommand(CommandContext ctx, [Description("The user tha } [Command("reformat")] - [RequirePermissions(Permissions.ManageMessages)] // Restrict this command to users/roles who have the "Manage Messages" permission - [RequireRoles(RoleCheckMode.Any, "Helper", "Mod", "Owner")] // Restrict this command to "Helper", "Mod" and "Owner" roles only public async Task RefacReformatCommandtorCommand(CommandContext ctx, [Description("The user that posted the message to refactor")] DiscordMember member, [Description("Use **Replace** to replace the refactored post (only your own posts or if you are an admin), or specify a **language** if you want to force one.")] string cmd1, [Description("Use **Replace** to replace the refactored post (only your own posts or if you are an admin), or sa **language** if you want to force one.")] string cmd2) { // Refactors the previous post, if it is code if (IsBest(cmd1) || IsBest(cmd2)) await RefactorCode(ctx, member, Action.Analyze, Langs.NONE); else if (IsReplace(cmd1)) await RefactorCode(ctx, member, Action.Replace, NormalizeLanguage(cmd2)); @@ -175,8 +171,10 @@ private async Task> RefactorCode(CommandContext ctx, Discor bool deleteOrig = action == Action.Replace; Match codeMatch = codeBlock.Match(code); if (codeMatch.Success) { - code = codeMatch.Groups[5].Value; - deleteOrig = string.IsNullOrWhiteSpace(codeMatch.Groups[1].Value); + if (codeMatch.Groups[3].Value.IndexOf(';') != -1) + code = codeMatch.Groups[3].Value.Substring(3) + codeMatch.Groups[6].Value; + else + code = codeMatch.Groups[6].Value; } code = code.Trim(' ', '\t', '\r', '\n'); code = emptyLines.Replace(code, "\n"); @@ -344,7 +342,7 @@ private Langs NormalizeLanguage(string language) { return Langs.NONE; } - readonly Regex codeBlock = new Regex("(.*)(\\n|\\r|\\r\\n)?(```[a-z]*(\\n|\\r|\\r\\n))(.*)(```[a-z]*(\\n|\\r|\\r\\n)?)", RegexOptions.Singleline, TimeSpan.FromSeconds(1)); + readonly Regex codeBlock = new Regex("(.*)(\\n|\\r|\\r\\n)?(```[a-z]*(\\n|\\r|\\r\\n)|```[^;]*;(\\n|\\r|\\r\\n))(.*)(```[a-z]*(\\n|\\r|\\r\\n)?)", RegexOptions.Singleline, TimeSpan.FromSeconds(1)); readonly Regex emptyLines = new Regex("(\\r?\\n\\s*){1,}(\\r?\\n)", RegexOptions.Singleline, TimeSpan.FromSeconds(1)); readonly LangKWord[] keywords = { From ad2e641507c45581f2c007d6fee9a19594d34a4e Mon Sep 17 00:00:00 2001 From: CPULL Date: Wed, 25 Aug 2021 16:49:16 +0200 Subject: [PATCH 51/54] Now also a simple thank you message, without reference an dmention will add the Thanks to the author of th eprevious post --- UPBot Code/Actions/AppreciationTracking.cs | 25 ++++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/UPBot Code/Actions/AppreciationTracking.cs b/UPBot Code/Actions/AppreciationTracking.cs index d105d1f..dac45f3 100644 --- a/UPBot Code/Actions/AppreciationTracking.cs +++ b/UPBot Code/Actions/AppreciationTracking.cs @@ -75,21 +75,32 @@ public async Task ShowAppreciationCommand(CommandContext ctx) { internal static Task ThanksAdded(DiscordClient sender, MessageCreateEventArgs args) { try { - if (args.Message.Reference == null && (args.Message.MentionedUsers == null || args.Message.MentionedUsers.Count == 0)) return Task.FromResult(0); - string msg = args.Message.Content.ToLowerInvariant(); if (thanks.IsMatch(msg) || thankyou.IsMatch(msg) || thank2you.IsMatch(msg)) { // Add thanks if (thank4n.IsMatch(msg)) return Task.FromResult(0); if (GetTracking()) return Task.FromResult(0); - IReadOnlyList mentions = args.Message.MentionedUsers; - ulong authorId = args.Message.Author.Id; - ulong refAuthorId = args.Message.Reference != null ? args.Message.Reference.Message.Author.Id : 0; + DiscordMessage theMsg = args.Message; + ulong authorId = theMsg.Author.Id; + if (theMsg.Reference == null && (theMsg.MentionedUsers == null || theMsg.MentionedUsers.Count == 0)) { + // Unrelated thank you, get the previous message and check + IReadOnlyList msgs = theMsg.Channel.GetMessagesBeforeAsync(theMsg.Id, 2).Result; + theMsg = null; + foreach (DiscordMessage m in msgs) + if (m.Author.Id != authorId) { + theMsg = m; + break; + } + if (theMsg == null) return Task.FromResult(0); + } + + IReadOnlyList mentions = theMsg.MentionedUsers; + ulong refAuthorId = theMsg.Reference != null ? theMsg.Reference.Message.Author.Id : 0; if (mentions != null) foreach (DiscordUser u in mentions) if (u.Id != authorId && u.Id != refAuthorId) tracking.AlterThankYou(u.Id); - if (args.Message.Reference != null) - if (args.Message.Reference.Message.Author.Id != authorId) tracking.AlterThankYou(args.Message.Reference.Message.Author.Id); + if (theMsg.Reference != null) + if (theMsg.Reference.Message.Author.Id != authorId) tracking.AlterThankYou(theMsg.Reference.Message.Author.Id); } return Task.FromResult(0); From dea0a18ebb62e558b55e992da541462c7ee2139a Mon Sep 17 00:00:00 2001 From: CPULL Date: Thu, 26 Aug 2021 18:32:01 +0200 Subject: [PATCH 52/54] Updated version information --- UPBot Code/Utils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPBot Code/Utils.cs b/UPBot Code/Utils.cs index 52329ee..42c9439 100644 --- a/UPBot Code/Utils.cs +++ b/UPBot Code/Utils.cs @@ -31,7 +31,7 @@ public static class Utils private static DiscordGuild guild; public static string GetVersion() { - return vmajor + "." + vminor + "." + vbuild + " dev - 2021/08/23"; + return vmajor + "." + vminor + "." + vbuild + " - 2021/08/26"; } /// From afb00beab2c273fc9bc6e6fe7f1619baa4a20dd6 Mon Sep 17 00:00:00 2001 From: CPULL Date: Sat, 28 Aug 2021 00:21:52 +0200 Subject: [PATCH 53/54] Added another log to debug the linux deployment in the main program --- UPBot Code/Program.cs | 103 +++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index c09a7fb..a7f3b1f 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Reflection; using System.Threading.Tasks; using DSharpPlus; @@ -8,51 +9,71 @@ namespace UPBot { class Program { + static StreamWriter lw = null; static void Main(string[] args) { - MainAsync(args[0], (args.Length > 1 && args[1].Length > 0) ? args[1] : "\\").GetAwaiter().GetResult(); + lw = File.CreateText(args.Length >= 3 ? args[2] : "debug.log"); + lw.WriteLine("Log Started. Woho."); + MainAsync(args[0], (args.Length > 1 && args[1].Length > 0) ? args[1] : "\\", args[2]).GetAwaiter().GetResult(); } - static async Task MainAsync(string token, string prefix) { - var discord = new DiscordClient(new DiscordConfiguration() { - Token = token, // token has to be passed as parameter - TokenType = TokenType.Bot, // We are a bot - Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers - }); - discord.UseInteractivity(new InteractivityConfiguration() { - Timeout = TimeSpan.FromHours(2) - }); - CustomCommandsService.DiscordClient = discord; - - Utils.InitClient(discord); - Database.InitDb(); - Database.AddTable(); - Database.AddTable(); - Database.AddTable(); - Database.AddTable(); - - CommandsNextExtension commands = discord.UseCommandsNext(new CommandsNextConfiguration() { - StringPrefixes = new[] { prefix[0].ToString() } // The backslash will be the default command prefix if not specified in the parameters - }); - commands.CommandErrored += CustomCommandsService.CommandError; - commands.RegisterCommands(Assembly.GetExecutingAssembly()); // Registers all defined commands - - BannedWords.Init(); - discord.MessageCreated += async (s, e) => { await BannedWords.CheckMessage(s, e); }; - discord.MessageCreated += AppreciationTracking.ThanksAdded; - - CustomCommandsService.LoadCustomCommands(); - await discord.ConnectAsync(); // Connects and wait forever - - Utils.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd") + " --------------------------------"); - - discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; - discord.GuildMemberRemoved += MembersTracking.DiscordMemberRemoved; - discord.GuildMemberUpdated += MembersTracking.DiscordMemberUpdated; - discord.MessageReactionAdded += AppreciationTracking.ReacionAdded; - discord.MessageReactionAdded += EmojisForRole.ReacionAdded; - discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; - discord.MessageReactionRemoved += EmojisForRole.ReactionRemoved; + static async Task MainAsync(string token, string prefix, string logPath) { + try { + lw?.WriteLine("Init MainAsync"); + var discord = new DiscordClient(new DiscordConfiguration() { + Token = token, // token has to be passed as parameter + TokenType = TokenType.Bot, // We are a bot + Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers + }); + lw?.WriteLine("discord object"); + discord.UseInteractivity(new InteractivityConfiguration() { + Timeout = TimeSpan.FromHours(2) + }); + lw?.WriteLine("use interactivity"); + CustomCommandsService.DiscordClient = discord; + lw?.WriteLine("CustomCommandsService"); + Utils.InitClient(discord); + lw?.WriteLine("Utils.InitClient"); + Database.InitDb(); + lw?.WriteLine("Database.InitDb"); + Database.AddTable(); + Database.AddTable(); + Database.AddTable(); + Database.AddTable(); + lw?.WriteLine("Added Tables"); + + CommandsNextExtension commands = discord.UseCommandsNext(new CommandsNextConfiguration() { + StringPrefixes = new[] { prefix[0].ToString() } // The backslash will be the default command prefix if not specified in the parameters + }); + lw?.WriteLine("CommandsNextExtension"); + commands.CommandErrored += CustomCommandsService.CommandError; + commands.RegisterCommands(Assembly.GetExecutingAssembly()); // Registers all defined commands + lw?.WriteLine("RegisterCommands"); + + BannedWords.Init(); + lw?.WriteLine("BannedWords"); + discord.MessageCreated += async (s, e) => { await BannedWords.CheckMessage(s, e); }; + discord.MessageCreated += AppreciationTracking.ThanksAdded; + + CustomCommandsService.LoadCustomCommands(); + lw?.WriteLine("CustomCommandsService.LoadCustomCommands"); + await discord.ConnectAsync(); // Connects and wait forever + + lw?.WriteLine("connecting to discord"); + Utils.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd") + " --------------------------------"); + + discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; + discord.GuildMemberRemoved += MembersTracking.DiscordMemberRemoved; + discord.GuildMemberUpdated += MembersTracking.DiscordMemberUpdated; + discord.MessageReactionAdded += AppreciationTracking.ReacionAdded; + discord.MessageReactionAdded += EmojisForRole.ReacionAdded; + discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; + discord.MessageReactionRemoved += EmojisForRole.ReactionRemoved; + lw?.WriteLine("done"); + + } catch (Exception ex) { + lw?.WriteLine("with exception: " + ex.Message); + } await Task.Delay(-1); } From 29764c8232d2b596dee94d24999b4fa843cdaa40 Mon Sep 17 00:00:00 2001 From: CPULL Date: Sat, 28 Aug 2021 00:25:34 +0200 Subject: [PATCH 54/54] Fixed wrong parameter on the main function --- UPBot Code/Program.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/UPBot Code/Program.cs b/UPBot Code/Program.cs index a7f3b1f..0a8f28c 100644 --- a/UPBot Code/Program.cs +++ b/UPBot Code/Program.cs @@ -13,53 +13,66 @@ class Program { static void Main(string[] args) { lw = File.CreateText(args.Length >= 3 ? args[2] : "debug.log"); lw.WriteLine("Log Started. Woho."); - MainAsync(args[0], (args.Length > 1 && args[1].Length > 0) ? args[1] : "\\", args[2]).GetAwaiter().GetResult(); + lw.Flush(); + MainAsync(args[0], (args.Length > 1 && args[1].Length > 0) ? args[1] : "\\").GetAwaiter().GetResult(); } - static async Task MainAsync(string token, string prefix, string logPath) { + static async Task MainAsync(string token, string prefix) { try { lw?.WriteLine("Init MainAsync"); + lw.Flush(); var discord = new DiscordClient(new DiscordConfiguration() { Token = token, // token has to be passed as parameter TokenType = TokenType.Bot, // We are a bot Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers }); lw?.WriteLine("discord object"); + lw.Flush(); discord.UseInteractivity(new InteractivityConfiguration() { Timeout = TimeSpan.FromHours(2) }); lw?.WriteLine("use interactivity"); + lw.Flush(); CustomCommandsService.DiscordClient = discord; lw?.WriteLine("CustomCommandsService"); + lw.Flush(); Utils.InitClient(discord); lw?.WriteLine("Utils.InitClient"); + lw.Flush(); Database.InitDb(); lw?.WriteLine("Database.InitDb"); + lw.Flush(); Database.AddTable(); Database.AddTable(); Database.AddTable(); Database.AddTable(); lw?.WriteLine("Added Tables"); + lw.Flush(); CommandsNextExtension commands = discord.UseCommandsNext(new CommandsNextConfiguration() { StringPrefixes = new[] { prefix[0].ToString() } // The backslash will be the default command prefix if not specified in the parameters }); lw?.WriteLine("CommandsNextExtension"); + lw.Flush(); commands.CommandErrored += CustomCommandsService.CommandError; commands.RegisterCommands(Assembly.GetExecutingAssembly()); // Registers all defined commands lw?.WriteLine("RegisterCommands"); + lw.Flush(); BannedWords.Init(); lw?.WriteLine("BannedWords"); + lw.Flush(); discord.MessageCreated += async (s, e) => { await BannedWords.CheckMessage(s, e); }; discord.MessageCreated += AppreciationTracking.ThanksAdded; CustomCommandsService.LoadCustomCommands(); lw?.WriteLine("CustomCommandsService.LoadCustomCommands"); + lw.Flush(); await discord.ConnectAsync(); // Connects and wait forever lw?.WriteLine("connecting to discord"); + lw.Flush(); Utils.Log("Logging [re]Started at: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:dd") + " --------------------------------"); discord.GuildMemberAdded += MembersTracking.DiscordMemberAdded; @@ -70,9 +83,11 @@ static async Task MainAsync(string token, string prefix, string logPath) { discord.MessageReactionRemoved += AppreciationTracking.ReactionRemoved; discord.MessageReactionRemoved += EmojisForRole.ReactionRemoved; lw?.WriteLine("done"); + lw.Flush(); } catch (Exception ex) { lw?.WriteLine("with exception: " + ex.Message); + lw.Flush(); } await Task.Delay(-1); }