From 7a18e4a14bb40d9710f112533d133dc6705876bf Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Thu, 6 Aug 2020 16:11:27 +0200 Subject: [PATCH 1/9] Add version command --- ChaosBot/Discord/Modules/User/Info.cs | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 ChaosBot/Discord/Modules/User/Info.cs diff --git a/ChaosBot/Discord/Modules/User/Info.cs b/ChaosBot/Discord/Modules/User/Info.cs new file mode 100644 index 0000000..e2c6e41 --- /dev/null +++ b/ChaosBot/Discord/Modules/User/Info.cs @@ -0,0 +1,49 @@ +using System; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using ChaosBot.Discord.PreConditions; +using ChaosBot.Repositories; +using Discord; +using Discord.Commands; +using Microsoft.Extensions.Configuration; +using NLog; + +namespace ChaosBot.Discord.Modules.User +{ + public class Info : ModuleBase + { + private static readonly ILogger Logger = Program.Logger; + + [Command("info")] + [Alias("version")] + [CheckCommandPerm("User")] + public async Task ShowInfo() + { + try + { + var sb = new StringBuilder(); + var embed = new EmbedBuilder(); + + embed.WithColor(new Color(255, 255, 0)); + embed.Title = $"Information {Program.AppSettingsHandler.GetValue("Bot:Name")} v{Program.AppSettingsHandler.GetValue("Bot:Version")}"; + sb.AppendLine($"Prefix: {ConfigurationRepository.GetValue("Discord:Prefix", Context.Guild.Id)}"); + + /* + * Add the string to the Embed + */ + embed.Description = sb.ToString(); + + /* + * Reply with the Embed created above + */ + await ReplyAsync(null, false, embed.Build()); + } + catch(Exception ex) + { + Logger.Error( + $"{MethodBase.GetCurrentMethod().ReflectedType.FullName}: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + } + } +} \ No newline at end of file From 93f55adf7b5e53dd1882fe565dc4bb2ad2a2c553 Mon Sep 17 00:00:00 2001 From: Sean Stoves Date: Thu, 6 Aug 2020 10:51:56 -0400 Subject: [PATCH 2/9] Adding Function to permit a restriction of configurations. --- ChaosBot/Services/RestrictedConfig.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 ChaosBot/Services/RestrictedConfig.cs diff --git a/ChaosBot/Services/RestrictedConfig.cs b/ChaosBot/Services/RestrictedConfig.cs new file mode 100644 index 0000000..8457b45 --- /dev/null +++ b/ChaosBot/Services/RestrictedConfig.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ChaosBot.Models; +using ChaosBot.Repositories; +using Discord.Commands; +using Discord.WebSocket; +using NLog; + +namespace ChaosBot.Services +{ + public class RestrictedConfig + { + private static ILogger _logger = Program.Logger; + + public static async Task IsAllowed(string key) + { + List restrictedCfg = new List {"Database", "Bot", "NLog", "WebServer", "Discord:Token"}; + + if (restrictedCfg.Contains(key)) + return false; + + return true; + } + } +} \ No newline at end of file From f1577ba5b7777919a74821c1c6c6b6cef4677e7e Mon Sep 17 00:00:00 2001 From: Sean Stoves Date: Thu, 6 Aug 2020 10:52:06 -0400 Subject: [PATCH 3/9] Augmenting code to use isAllowed --- ChaosBot/Discord/Modules/Admin/Config.cs | 28 ++++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/ChaosBot/Discord/Modules/Admin/Config.cs b/ChaosBot/Discord/Modules/Admin/Config.cs index 339ddc8..d95fd4a 100644 --- a/ChaosBot/Discord/Modules/Admin/Config.cs +++ b/ChaosBot/Discord/Modules/Admin/Config.cs @@ -6,6 +6,7 @@ using System.Text; using ChaosBot.Discord.PreConditions; using ChaosBot.Models; using ChaosBot.Repositories; +using ChaosBot.Services; using Discord; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; @@ -78,20 +79,23 @@ namespace ChaosBot.Discord.Modules.Admin { try { - if ((key != null) && (value != null)) + if ((key != null) && (value != null) ) { - using (ChaosbotContext dbContext = new ChaosbotContext()) + if(await RestrictedConfig.IsAllowed(key)) { - Configuration cnfSet = new Configuration(); - - cnfSet.Key = key; - cnfSet.DiscordGuildId = Context.Guild.Id; - cnfSet.SerializedValue = JsonConvert.SerializeObject(value); + using (ChaosbotContext dbContext = new ChaosbotContext()) + { + Configuration cnfSet = new Configuration(); - await dbContext.Configuration.Upsert(cnfSet) - .On(x => new {x.Key, x.DiscordGuildId}).RunAsync(); - - await ConfigGet(key, true); + cnfSet.Key = key; + cnfSet.DiscordGuildId = Context.Guild.Id; + cnfSet.SerializedValue = JsonConvert.SerializeObject(value); + + await dbContext.Configuration.Upsert(cnfSet) + .On(x => new {x.Key, x.DiscordGuildId}).RunAsync(); + + await ConfigGet(key, true); + } } } else @@ -111,7 +115,7 @@ namespace ChaosBot.Discord.Modules.Admin { try { - if (key != null) + if ((key != null) && (await RestrictedConfig.IsAllowed(key))) { StringBuilder sb = new StringBuilder(); EmbedBuilder embed = new EmbedBuilder(); From 56edd2056224b280ccf9a4be8f4b1c17e6594982 Mon Sep 17 00:00:00 2001 From: "Sean \"Solao Bajiuik\" Stoves" Date: Thu, 6 Aug 2020 17:19:33 -0400 Subject: [PATCH 4/9] Adding ExperiencePoints table and Model --- ChaosBot/Models/ChaosbotContext.cs | 3 +++ ChaosBot/Models/Experience.cs | 15 +++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 ChaosBot/Models/Experience.cs diff --git a/ChaosBot/Models/ChaosbotContext.cs b/ChaosBot/Models/ChaosbotContext.cs index 4f74541..7715eb6 100644 --- a/ChaosBot/Models/ChaosbotContext.cs +++ b/ChaosBot/Models/ChaosbotContext.cs @@ -11,6 +11,7 @@ namespace ChaosBot.Models public DbSet Raffles { get; set; } public DbSet CommandPermissions { get; set; } public DbSet Configuration { get; set; } + public DbSet ExperiencePoints { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -41,6 +42,8 @@ namespace ChaosBot.Models .HasKey(x => new {x.DiscordGuildId, x.LodestoneId}); modelBuilder.Entity() .HasKey(x => new {x.DiscordGuildId, x.DiscordUserId}); + modelBuilder.Entity() + .HasKey(x => new {x.DiscordGuildId, x.DiscordUserId}); modelBuilder.Entity() .HasKey(x => new {x.DiscordGuildId, x.Key}); } diff --git a/ChaosBot/Models/Experience.cs b/ChaosBot/Models/Experience.cs new file mode 100644 index 0000000..f46148e --- /dev/null +++ b/ChaosBot/Models/Experience.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace ChaosBot.Models +{ + #region Required + public class Experience + { + [Required] + public ulong DiscordUserId { get; set; } + [Required] + public ulong DiscordGuildId { get; set; } + public ulong Amount { get; set; } + } + #endregion +} \ No newline at end of file From 0ebcfdb605aa4cbcb79a52e4a3406a380203eb9c Mon Sep 17 00:00:00 2001 From: "Sean \"Solao Bajiuik\" Stoves" Date: Thu, 6 Aug 2020 17:19:49 -0400 Subject: [PATCH 5/9] Creating Experience Handler code to add XP based on Configuration setting. --- .../Discord/Services/ExperienceHandler.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 ChaosBot/Discord/Services/ExperienceHandler.cs diff --git a/ChaosBot/Discord/Services/ExperienceHandler.cs b/ChaosBot/Discord/Services/ExperienceHandler.cs new file mode 100644 index 0000000..9b74856 --- /dev/null +++ b/ChaosBot/Discord/Services/ExperienceHandler.cs @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using System.Reflection; +using ChaosBot.Models; +using ChaosBot.Repositories; +using Microsoft.EntityFrameworkCore; +using NLog; + +namespace ChaosBot.Discord.Services +{ + public class ExperienceHandler + { + private static readonly ILogger _logger = Program.Logger; + + public static async void addXP(ulong DiscordGuildId, ulong DiscordUserId) + { + try + { + using (ChaosbotContext dbContext = new ChaosbotContext()) + { + IQueryable ctxUser = dbContext.ExperiencePoints; + IQueryable usrXp = ctxUser + .Where(p => p.DiscordGuildId.Equals(DiscordGuildId)) + .Where(p => p.DiscordUserId.Equals(DiscordUserId)); + + Experience usrNewXp; + if (usrXp.Any()) + { + usrNewXp = usrXp.First(); + usrNewXp.Amount = usrNewXp.Amount + Convert.ToUInt64(ConfigurationRepository.GetValue("Experience:PerMsg", DiscordGuildId, "0")); + } + else + { + usrNewXp = new Experience(); + usrNewXp.Amount = Convert.ToUInt64(ConfigurationRepository.GetValue("Experience:PerMsg", DiscordGuildId, "0")); + } + usrNewXp.DiscordGuildId = DiscordGuildId; + usrNewXp.DiscordUserId = DiscordUserId; + + await dbContext.ExperiencePoints.Upsert(usrNewXp) + .On(x => new { x.DiscordGuildId, x.DiscordUserId}).RunAsync(); + } + } + catch (Exception ex) + { + _logger.Error( + $"{MethodBase.GetCurrentMethod().ReflectedType.FullName}: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + } + } +} \ No newline at end of file From 0058a68e4862329b977d4705b32be939493160a7 Mon Sep 17 00:00:00 2001 From: "Sean \"Solao Bajiuik\" Stoves" Date: Thu, 6 Aug 2020 17:20:25 -0400 Subject: [PATCH 6/9] Linking Experience Handler into the code, Also checking IF they they have config enabled for commands to count towards Experience --- ChaosBot/Discord/Services/CommandHandler.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ChaosBot/Discord/Services/CommandHandler.cs b/ChaosBot/Discord/Services/CommandHandler.cs index 106b3fd..d00c5a3 100644 --- a/ChaosBot/Discord/Services/CommandHandler.cs +++ b/ChaosBot/Discord/Services/CommandHandler.cs @@ -56,9 +56,16 @@ namespace ChaosBot.Discord.Services int argPos = 0; string prefix = ConfigurationRepository.GetValue("Discord:Prefix", context.Guild.Id, "!"); - if (!(message.HasMentionPrefix(_client.CurrentUser, ref argPos) || message.HasStringPrefix(prefix, ref argPos))) + if (!(message.HasMentionPrefix(_client.CurrentUser, ref argPos) || + message.HasStringPrefix(prefix, ref argPos))) + { + ExperienceHandler.addXP(context.Guild.Id, context.User.Id); return; + } + if(Convert.ToBoolean(ConfigurationRepository.GetValue("Experience:Commands", context.Guild.Id, "false"))) + ExperienceHandler.addXP(context.Guild.Id, context.User.Id); + await _commands.ExecuteAsync(context, argPos, _services); } catch (Exception ex) From 0af2ebbd969c610bcd2df4f5c13e50f83e015818 Mon Sep 17 00:00:00 2001 From: "Sean \"Solao Bajiuik\" Stoves" Date: Thu, 6 Aug 2020 20:24:03 -0400 Subject: [PATCH 7/9] Adding Handler to properly utilize the API results --- ChaosBot/Lodestone/LodestoneRank.cs | 155 ++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 ChaosBot/Lodestone/LodestoneRank.cs diff --git a/ChaosBot/Lodestone/LodestoneRank.cs b/ChaosBot/Lodestone/LodestoneRank.cs new file mode 100644 index 0000000..e70983b --- /dev/null +++ b/ChaosBot/Lodestone/LodestoneRank.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace ChaosBot.Lodestone +{ + public partial class LodestoneRankApi + { + [JsonProperty("success")] + public bool Success { get; set; } + + [JsonProperty("error")] + public object Error { get; set; } + + [JsonProperty("data")] + public List Data { get; set; } + } + + public partial class LodestoneRank + { + [JsonProperty("lodestoneId")] + [JsonConverter(typeof(ParseStringConverter))] + public long LodestoneId { get; set; } + + [JsonProperty("ingameRole")] + public ERole IngameRole { get; set; } + + [JsonProperty("firstSeen")] + public DateTimeOffset FirstSeen { get; set; } + + [JsonProperty("displayName")] + public string DisplayName { get; set; } + + [JsonProperty("shouldBeRole")] + public ERole? ShouldBeRole { get; set; } + + [JsonProperty("discordId")] + public string DiscordId { get; set; } + } + + public enum ERole { Council, Initiate, Member, Mentor, Recruit }; + + public partial class LodestoneRankApi + { + public static LodestoneRankApi FromJson(string json) => JsonConvert.DeserializeObject(json, ChaosBot.Lodestone.Converter.Settings); + } + + public static class Serialize + { + public static string ToJson(this LodestoneRankApi self) => JsonConvert.SerializeObject(self, ChaosBot.Lodestone.Converter.Settings); + } + + internal static class Converter + { + public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + DateParseHandling = DateParseHandling.None, + Converters = + { + ERoleConverter.Singleton, + new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal } + }, + }; + } + + internal class ERoleConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(ERole) || t == typeof(ERole?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "Council": + return ERole.Council; + case "Initiate": + return ERole.Initiate; + case "Member": + return ERole.Member; + case "Mentor": + return ERole.Mentor; + case "Recruit": + return ERole.Recruit; + } + throw new Exception("Cannot unmarshal type ERole"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (ERole)untypedValue; + switch (value) + { + case ERole.Council: + serializer.Serialize(writer, "Council"); + return; + case ERole.Initiate: + serializer.Serialize(writer, "Initiate"); + return; + case ERole.Member: + serializer.Serialize(writer, "Member"); + return; + case ERole.Mentor: + serializer.Serialize(writer, "Mentor"); + return; + case ERole.Recruit: + serializer.Serialize(writer, "Recruit"); + return; + } + throw new Exception("Cannot marshal type ERole"); + } + + public static readonly ERoleConverter Singleton = new ERoleConverter(); + } + + internal class ParseStringConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(long) || t == typeof(long?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + long l; + if (Int64.TryParse(value, out l)) + { + return l; + } + throw new Exception("Cannot unmarshal type long"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (long)untypedValue; + serializer.Serialize(writer, value.ToString()); + return; + } + + public static readonly ParseStringConverter Singleton = new ParseStringConverter(); + } +} \ No newline at end of file From 9cdab68c64e9aa8718e43cc80e1a6e9c4243fd60 Mon Sep 17 00:00:00 2001 From: "Sean \"Solao Bajiuik\" Stoves" Date: Thu, 6 Aug 2020 20:24:15 -0400 Subject: [PATCH 8/9] Adding RankCheck command --- ChaosBot/Discord/Modules/Admin/RankCheck.cs | 110 ++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 ChaosBot/Discord/Modules/Admin/RankCheck.cs diff --git a/ChaosBot/Discord/Modules/Admin/RankCheck.cs b/ChaosBot/Discord/Modules/Admin/RankCheck.cs new file mode 100644 index 0000000..a2643ed --- /dev/null +++ b/ChaosBot/Discord/Modules/Admin/RankCheck.cs @@ -0,0 +1,110 @@ +using System; +using Discord; +using Discord.Commands; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Reflection; +using System.Text; +using Antlr4.Runtime.Misc; +using ChaosBot.Discord.PreConditions; +using ChaosBot.Lodestone; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using NLog; + +namespace ChaosBot.Discord.Modules.Admin +{ + public class RankCheck : ModuleBase + { + private static readonly ILogger _logger = Program.Logger; + + [Command("rankCheck")] + [Alias("rc")] + [CheckCommandPerm("Admin")] + public async Task rankCheck() + { + try + { + List ranks = await GetRank(); + + var sb = new StringBuilder(); + var embed = new EmbedBuilder(); + + embed.WithColor(new Color(255, 255, 0)); + embed.Title = $"Pending Promotions"; + sb.AppendLine(); + sb.AppendLine(); + + sb.AppendLine($"**Recruits Pending Promotion to Initiate**"); + if(ranks.FindAll(x => x.IngameRole == ERole.Recruit).Any()) + { + foreach (var lsID in ranks.FindAll(x => x.IngameRole == ERole.Recruit)) + { + if ((lsID.ShouldBeRole != lsID.IngameRole) && (lsID.ShouldBeRole != null)) + sb.AppendLine(string.Format("{0} {1}", lsID.DisplayName, $"linked to <@{lsID.DiscordId}>")); + } + } + else + sb.AppendLine($"None at this time."); + + sb.AppendLine(); + sb.AppendLine($"**Initiates Pending Promotion to Member**"); + if (ranks.FindAll(x => x.IngameRole == ERole.Initiate).Any()) + { + foreach (var lsID in ranks.FindAll(x => x.IngameRole == ERole.Initiate)) + { + if ((lsID.ShouldBeRole != lsID.IngameRole) && (lsID.ShouldBeRole != null)) + sb.AppendLine(string.Format("{0} {1}", lsID.DisplayName, $"linked to <@{lsID.DiscordId}>")); + } + } + else + sb.AppendLine($"None at this time."); + + sb.AppendLine(); + sb.AppendLine($"Report Generated by {Context.User.Mention} at {DateTime.Now.ToString("dddd, dd MMMM yyyy h:mm tt")}."); + + /* + * Add the string to the Embed + */ + embed.Description = sb.ToString(); + + /* + * Reply with the Embed created above + */ + await ReplyAsync(null, false, embed.Build()); + } + catch (Exception ex) + { + _logger.Error($"{MethodBase.GetCurrentMethod().ReflectedType.FullName}: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + } + + public async Task> GetRank() + { + string response = null; + + try + { + using (var client = new HttpClient()) + { + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13; + + var result = await client.GetAsync("https://www.ffxivhelix.com/rapi/clogiclodestone/v1/users"); + result.EnsureSuccessStatusCode(); + + response = await result.Content.ReadAsStringAsync(); + } + } + catch (Exception ex) + { + _logger.Error( + $"{MethodBase.GetCurrentMethod().ReflectedType.FullName}: Exception [{ex}] thrown, <[{ex.Message}]>."); + } + + return JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(JsonConvert.DeserializeObject(response).Data)); + } + } +} \ No newline at end of file From 59525c4c6384a7e619102b2cfdbc25cfb0e94438 Mon Sep 17 00:00:00 2001 From: "Sean \"Solao Bajiuik\" Stoves" Date: Thu, 6 Aug 2020 20:30:39 -0400 Subject: [PATCH 9/9] Adding additional restricted values for Config --- ChaosBot/Services/RestrictedConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChaosBot/Services/RestrictedConfig.cs b/ChaosBot/Services/RestrictedConfig.cs index 8457b45..8c19c8b 100644 --- a/ChaosBot/Services/RestrictedConfig.cs +++ b/ChaosBot/Services/RestrictedConfig.cs @@ -16,7 +16,7 @@ namespace ChaosBot.Services public static async Task IsAllowed(string key) { - List restrictedCfg = new List {"Database", "Bot", "NLog", "WebServer", "Discord:Token"}; + List restrictedCfg = new List {"Database:Host", "Database:Port", "Database:Name", "Database:User", "Database:Pass", "Bot:Version", "NLog", "WebServer", "Discord:Token"}; if (restrictedCfg.Contains(key)) return false;