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 01/15] 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 02/15] 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 03/15] 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 04/15] '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 05/15] 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 06/15] 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 07/15] =?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 08/15] 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 09/15] 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 10/15] 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 11/15] 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 12/15] 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 13/15] 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 14/15] 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 15/15] 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,