diff --git a/SysBot.Pokemon.Discord/Commands/General/HelloModule.cs b/SysBot.Pokemon.Discord/Commands/General/HelloModule.cs index bd6dbd9..31e485b 100644 --- a/SysBot.Pokemon.Discord/Commands/General/HelloModule.cs +++ b/SysBot.Pokemon.Discord/Commands/General/HelloModule.cs @@ -1,5 +1,8 @@ -using Discord.Commands; +using Discord; +using Discord.Commands; +using System.Linq; // Include the System.Linq namespace. using System.Threading.Tasks; +using System.IO; //Weed need System.IO to r/w local system files such as "rules.txt" namespace SysBot.Pokemon.Discord { @@ -14,5 +17,40 @@ public async Task PingAsync() var msg = string.Format(str, Context.User.Mention); await ReplyAsync(msg).ConfigureAwait(false); } - } + + + + [Command("rules")] + [Summary("Get the server rules.")] + public async Task RulesAsync() + { + string folderPath = "variables"; + string rulesFilePath = Path.Combine(folderPath, "rules.txt"); + + // Ensure the folder exists, create it if it doesn't. + if (!Directory.Exists(folderPath)) + { + Directory.CreateDirectory(folderPath); + } + + if (!File.Exists(rulesFilePath)) + { + // If the file doesn't exist, create it with the specified content. + File.WriteAllText(rulesFilePath, "Rules File not Updated Yet"); + } + + string rulesContent = File.ReadAllText(rulesFilePath); + + var embed = new EmbedBuilder + { + Title = "Server Rules", + Description = rulesContent, + Color = new Color(0, 255, 0), // Green success commmand embed - + }; + + await ReplyAsync(embed: embed.Build()); + } + + + } } \ No newline at end of file diff --git a/SysBot.Pokemon.Discord/Commands/Management/OwnerModule.cs b/SysBot.Pokemon.Discord/Commands/Management/OwnerModule.cs index c0f31e3..fdcc934 100644 --- a/SysBot.Pokemon.Discord/Commands/Management/OwnerModule.cs +++ b/SysBot.Pokemon.Discord/Commands/Management/OwnerModule.cs @@ -1,40 +1,92 @@ -using Discord.Commands; +//Organized into System Libraries and sub Libraries, and Discord using System; using System.Linq; +using System.IO; //we need this for local file manipulation using System.Threading.Tasks; + +//I'm going to be using a lot of libraries and subliraries that we not initially used in the original project source code - +//These will be required to use parts of my codeusing Discord.WebSocket; // We need this subclass to be able to attach deleted Pk9 files and restore them. +using Discord.Net; // This is to handle deleted message exceptions +using System.Net; // We need this to catch exceptions for deleted messages +using Discord.Commands; using Discord; +using System.Collections.Generic; namespace SysBot.Pokemon.Discord { public class OwnerModule : SudoModule { + + private List ownerIds; // Declare a field to store the loaded owner IDs + + [Command("addSudo")] [Summary("Adds mentioned user to global sudo")] - [RequireOwner] + [RequireSudo] // ReSharper disable once UnusedParameter.Global public async Task SudoUsers([Remainder] string _) { + ownerIds = LoadOwnerIdsFromFile(); + var users = Context.Message.MentionedUsers; var objects = users.Select(GetReference); SysCordSettings.Settings.GlobalSudoList.AddIfNew(objects); await ReplyAsync("Done.").ConfigureAwait(false); } + + private List LoadOwnerIdsFromFile() + { + // Define the path to your owners.txt file + string ownersFilePath = Path.Combine("parameters", "owners.txt"); + + if (File.Exists(ownersFilePath)) + { + // Read the file and split it by commas to get a list of owner IDs + string[] ownerIdsStr = File.ReadAllText(ownersFilePath).Split(','); + // Convert the strings to ulong and return the list of owner IDs + List ownerIds = ownerIdsStr.Select(str => ulong.Parse(str.Trim())).ToList(); + return ownerIds; + } + else + { + // If the file doesn't exist, create it and make it empty + File.WriteAllText(ownersFilePath, string.Empty); + + // Return an empty list since there are no owner IDs yet + return new List();; + } +} [Command("removeSudo")] [Summary("Removes mentioned user from global sudo")] - [RequireOwner] + [RequireSudo] // ReSharper disable once UnusedParameter.Global public async Task RemoveSudoUsers([Remainder] string _) { - var users = Context.Message.MentionedUsers; - var objects = users.Select(GetReference); - SysCordSettings.Settings.GlobalSudoList.RemoveAll(z => objects.Any(o => o.ID == z.ID)); - await ReplyAsync("Done.").ConfigureAwait(false); + + ulong userId = Context.User.Id; + // Read the contents of owners.txt + string ownersFileContent = File.ReadAllText("parameters/owners.txt"); + // Split the content by commas to get an array of owner Discord IDs + string[] ownerIds = ownersFileContent.Split(','); + if (ownerIds.Contains(userId.ToString())) + { + var users = Context.Message.MentionedUsers; + var objects = users.Select(GetReference); + SysCordSettings.Settings.GlobalSudoList.RemoveAll(z => objects.Any(o => o.ID == z.ID)); + await ReplyAsync("Done -Owner Removed Sudo from User ").ConfigureAwait(false); + } + else + { + await ReplyAsync("You are a sudo user, but not the owner. You cannot execute this command."); + } } + + // Sudo required [Command("addChannel")] [Summary("Adds a channel to the list of channels that are accepting commands.")] - [RequireOwner] + [RequireSudo] // Changed this to require sudo perms instead of owner // ReSharper disable once UnusedParameter.Global public async Task AddChannel() { @@ -43,35 +95,86 @@ public async Task AddChannel() await ReplyAsync("Done.").ConfigureAwait(false); } - [Command("removeChannel")] - [Summary("Removes a channel from the list of channels that are accepting commands.")] - [RequireOwner] - // ReSharper disable once UnusedParameter.Global - public async Task RemoveChannel() + //This ccommand isn't complete yet - Sudo command only currently + [Command("undelete")] + [Summary("Undeletes a specific message by ID.")] + [RequireSudo] + public async Task UndeleteMessageAsync(ulong messageId) { - var obj = GetReference(Context.Message.Channel); - SysCordSettings.Settings.ChannelWhitelist.RemoveAll(z => z.ID == obj.ID); - await ReplyAsync("Done.").ConfigureAwait(false); + // Attempt to get the message by its ID using the Channel. + var channel = Context.Channel as ITextChannel; + + if (channel != null) + { + var message = await channel.GetMessageAsync(messageId).ConfigureAwait(false); + + if (message != null) + { + // Assuming you want to post the undeleted message with author and attachments as files if they exist. + var undeleteMessage = $"{Context.User.Mention} Restoring Deleted message by {message.Author} : {message.Content}"; + + if (message.Attachments.Any()) + { + var attachments = message.Attachments.Select(a => a.Url); + undeleteMessage += $"\nAttachments:\n{string.Join("\n", attachments)}"; + } + + await channel.SendMessageAsync(undeleteMessage).ConfigureAwait(false); } + else + { + await ReplyAsync("The specified message does not exist or is not accessible.").ConfigureAwait(false); + } + } + + else + { + await ReplyAsync("This command can only be used in a text channel.").ConfigureAwait(false); + } +} + + // Sudo + Owners.txt Verification method implemented [Command("leave")] [Alias("bye")] [Summary("Leaves the current server.")] - [RequireOwner] + [RequireSudo] // ReSharper disable once UnusedParameter.Global public async Task Leave() { - await ReplyAsync("Goodbye.").ConfigureAwait(false); - await Context.Guild.LeaveAsync().ConfigureAwait(false); + ulong userId = Context.User.Id; + // Read the contents of owners.txt + string ownersFileContent = File.ReadAllText("parameters/owners.txt"); + // Split the content by commas to get an array of owner Discord IDs + string[] ownerIds = ownersFileContent.Split(','); + if (ownerIds.Contains(userId.ToString())) + { + await ReplyAsync($"Goodbye - Exiting the Guild <@{userId}>").ConfigureAwait(false); + await Context.Guild.LeaveAsync().ConfigureAwait(false); + } + else + { + await ReplyAsync("You are a sudo user, but not the owner. You cannot execute this command."); + } } - [Command("leaveguild")] + // Sudo + Owners.txt Verification method implemented + [Command("leaveguild")] [Alias("lg")] [Summary("Leaves guild based on supplied ID.")] - [RequireOwner] + [RequireSudo] + //changed to require sudo and match id in parameters/owner // ReSharper disable once UnusedParameter.Global public async Task LeaveGuild(string userInput) { + + ulong userId = Context.User.Id; + // Read the contents of owners.txt + string ownersFileContent = File.ReadAllText("parameters/owners.txt"); + // Split the content by commas to get an array of owner Discord IDs + string[] ownerIds = ownersFileContent.Split(','); + if (ownerIds.Contains(userId.ToString())) + { if (!ulong.TryParse(userInput, out ulong id)) { await ReplyAsync("Please provide a valid Guild ID.").ConfigureAwait(false); @@ -87,30 +190,70 @@ public async Task LeaveGuild(string userInput) await ReplyAsync($"Leaving {guild}.").ConfigureAwait(false); await guild.LeaveAsync().ConfigureAwait(false); + } + else + { + await ReplyAsync("You are a sudo user, but not the owner. You cannot execute this command."); + } + + + + } + // Sudo + Owners.txt Verification method implemented [Command("leaveall")] [Summary("Leaves all servers the bot is currently in.")] - [RequireOwner] - // ReSharper disable once UnusedParameter.Global + [RequireSudo] + // ReSharper disable once UnusedParameter.Globa public async Task LeaveAll() { - await ReplyAsync("Leaving all servers.").ConfigureAwait(false); - foreach (var guild in Context.Client.Guilds) + + ulong userId = Context.User.Id; + // Read the contents of owners.txt + string ownersFileContent = File.ReadAllText("parameters/owners.txt"); + // Split the content by commas to get an array of owner Discord IDs + string[] ownerIds = ownersFileContent.Split(','); + if (ownerIds.Contains(userId.ToString())) + { + await ReplyAsync("You are the owner. You can execute this command."); + await ReplyAsync("Leaving all servers.").ConfigureAwait(false); + foreach (var guild in Context.Client.Guilds) + { + await guild.LeaveAsync().ConfigureAwait(false); + } + } + else { - await guild.LeaveAsync().ConfigureAwait(false); + await ReplyAsync("You are a sudo user, but not the owner. You cannot execute this command."); } + + } + // Sudo + Owners.txt Verification method implemented [Command("sudoku")] [Alias("kill", "shutdown")] [Summary("Causes the entire process to end itself!")] - [RequireOwner] + [RequireSudo] // ReSharper disable once UnusedParameter.Global public async Task ExitProgram() { - await Context.Channel.EchoAndReply("Shutting down... goodbye! **Bot services are going offline.**").ConfigureAwait(false); - Environment.Exit(0); + ulong userId = Context.User.Id; + // Read the contents of owners.txt + string ownersFileContent = File.ReadAllText("parameters/owners.txt"); + // Split the content by commas to get an array of owner Discord IDs + string[] ownerIds = ownersFileContent.Split(','); + if (ownerIds.Contains(userId.ToString())) + { + await ReplyAsync("You are the owner. You can execute this command."); + await Context.Channel.EchoAndReply("Shutting down... goodbye! **Bot services are going offline.**").ConfigureAwait(false); + Environment.Exit(0); + } + else + { + await ReplyAsync("You are a sudo user, but not the owner. You cannot execute this command."); + } } private RemoteControlAccess GetReference(IUser channel) => new() diff --git a/SysBot.Pokemon.Discord/Commands/Management/PoolModule.cs b/SysBot.Pokemon.Discord/Commands/Management/PoolModule.cs index 86959c5..dd12554 100644 --- a/SysBot.Pokemon.Discord/Commands/Management/PoolModule.cs +++ b/SysBot.Pokemon.Discord/Commands/Management/PoolModule.cs @@ -1,14 +1,20 @@ using Discord; using Discord.Commands; using PKHeX.Core; +using System; +using System.IO; using System.Linq; -using System.Threading.Tasks; +using System.Text; // Used for creating combined Stringe. +using System.Threading.Tasks; namespace SysBot.Pokemon.Discord { [Summary("Distribution Pool Module")] public class PoolModule : ModuleBase where T : PKM, new() { + private object msg; + private object options; + [Command("poolReload")] [Summary("Reloads the bot pool from the setting's folder.")] [RequireSudo] @@ -24,32 +30,93 @@ public async Task ReloadPoolAsync() await ReplyAsync($"Reloaded from folder. Pool count: {hub.Ledy.Pool.Count}").ConfigureAwait(false); } - [Command("pool")] - [Summary("Displays the details of Pokémon files in the random pool.")] - public async Task DisplayPoolCountAsync() - { - var me = SysCord.Runner; - var hub = me.Hub; - var pool = hub.Ledy.Pool; - var count = pool.Count; - if (count is > 0 and < 20) + //New command for displaying the pool. + [Command("pool")] + [Summary("Displays the details of Pokémon in the random pool.")] + public async Task DisplayPoolCountAsync() { - var lines = pool.Files.Select((z, i) => $"{i + 1:00}: {z.Key} = {(Species)z.Value.RequestInfo.Species}"); - var msg = string.Join("\n", lines); - - var embed = new EmbedBuilder(); - embed.AddField(x => + var me = SysCord.Runner; + var hub = me.Hub; + var pool = hub.Ledy.Pool; + var count = pool.Count; + + if (count > 0) { - x.Name = $"Count: {count}"; - x.Value = msg; - x.IsInline = false; - }); - await ReplyAsync("Pool Details", embed: embed.Build()).ConfigureAwait(false); - } - else - { - await ReplyAsync($"Pool Count: {count}").ConfigureAwait(false); + var total = count; + + // Initialize a counter to keep track of displayed Pokémon. + var displayedPokémon = 0; + + var embed = new EmbedBuilder(); + var pokémonInformation = new StringBuilder(); + + while (count > 0 && displayedPokémon < 10) // Continue until 20 Pokémon are displayed. + { + var lines = pool.Files.Select((z, i) => $"{i + 1:00}: {z.Key} = {(Species)z.Value.RequestInfo.Species}"); + var msg = string.Join("\n", lines); + + pokémonInformation.AppendLine($"Count: {count}"); + pokémonInformation.AppendLine(msg); + + count--; + displayedPokémon++; // Increment the counter. + } + + embed.AddField("Total Pokémon in Pool", total, true); + embed.AddField("Pokémon Information", pokémonInformation.ToString(), false); + + await ReplyAsync("Pool Details", embed: embed.Build()).ConfigureAwait(false); + } + else + { + await ReplyAsync($"Pool Count: {count}").ConfigureAwait(false); + } } + + [Command("poolpic")] + [Summary("Displays the first 10 Pokémon images in the pool.")] + public async Task DisplayPoolPicturesAsync(params string[] pokemonNames) + { + var me = SysCord.Runner; + var hub = me.Hub; + _ = hub.Ledy.Pool; + + var embed = new EmbedBuilder(); + embed.Title = "Pool Pictures"; + + var displayedPokémon = 0; + + foreach (var pokemonName in pokemonNames) + { + if (displayedPokémon >= 10) + { + break; // Limit to 10 thumbnails. + } + + var imagePath = $".\\pkmnpic\\{pokemonName}.png"; // Adjust the path as needed. + + if (File.Exists(imagePath)) + { + var thumbnailUrl = $"attachment://{pokemonName}.png"; + var imageStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read); + embed.AddField($"The First 10 Pokemon in the XGC Current Distro",$"The first 10 Pokemon", true); + // Add the Pokémon name as a field. + embed.AddField("Pokémon", pokemonName); + + // Set the thumbnail for the embed. + embed.WithThumbnailUrl(thumbnailUrl); + + displayedPokémon++; // Increment the counter. + } + } + + await ReplyAsync(embed: embed.Build()); + } + + + private Task ReplyAsync(Embed embed, bool isTTS, object options, bool isEphemeral, object allowedMentions, object value) + { + throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/SysBot.Pokemon.Discord/Commands/Management/SudoModule.cs b/SysBot.Pokemon.Discord/Commands/Management/SudoModule.cs index c77e83c..478c58c 100644 --- a/SysBot.Pokemon.Discord/Commands/Management/SudoModule.cs +++ b/SysBot.Pokemon.Discord/Commands/Management/SudoModule.cs @@ -1,9 +1,14 @@ -using Discord.Commands; +// System Libraries being used using System; +using System.IO; // added systtem.io for file manipulation using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + +//Discord Libraries using Discord; +using Discord.Commands; +using Discord.Net; //commented out until we use it, which eventually we weill. namespace SysBot.Pokemon.Discord { @@ -72,6 +77,41 @@ public async Task UnBlackListIDs([Summary("Comma Separated Discord IDs")][Remain await ReplyAsync("Done.").ConfigureAwait(false); } + [Command("permcheck")] + [RequireSudo] // Use RequireSudo attribute + public async Task PermCheck() + { + ulong userId = Context.User.Id; + // Read the contents of owners.txt + string ownersFileContent = File.ReadAllText("parameters/owners.txt"); + // Split the content by commas to get an array of owner Discord IDs + string[] ownerIds = ownersFileContent.Split(','); + if (ownerIds.Contains(userId.ToString())) + { + // You can execute the command logic here for owners + await ReplyAsync("You are the owner. You can execute this command."); + } + else + { + // Provide a message for sudo users who are not owners + await ReplyAsync("You are a sudo user, but not the owner. You cannot execute this command."); + } + } + + + // Sudo Verification required only + [Command("removeChannel")] + [Summary("Removes a channel from the list of channels that are accepting commands.")] + [RequireSudo] // Changed to allow sudo to execute + // ReSharper disable once UnusedParameter.Global + public async Task RemoveChannel() + { + var obj = GetReference((IUser)Context.Message.Channel); + SysCordSettings.Settings.ChannelWhitelist.RemoveAll(z => z.ID == obj.ID); + await ReplyAsync("Done.").ConfigureAwait(false); + } + + [Command("blacklistSummary")] [Alias("printBlacklist", "blacklistPrint")] [Summary("Prints the list of blacklisted users.")] diff --git a/SysBot.Pokemon.Discord/Helpers/DiscordTradeNotifier.cs b/SysBot.Pokemon.Discord/Helpers/DiscordTradeNotifier.cs index 0efbfa3..605c810 100644 --- a/SysBot.Pokemon.Discord/Helpers/DiscordTradeNotifier.cs +++ b/SysBot.Pokemon.Discord/Helpers/DiscordTradeNotifier.cs @@ -89,6 +89,8 @@ public async void TradeFinished(PokeRoutineExecutor routine, PokeTradeDetail< ////////////////////////////////////////////////////////////// //BEGINNING OF THE FULL EMBED CODE ADDED TO DISCORDTRADENOTIFIER.CS + + // Define the path to the folder containing PNG files var folderPath = @"C:\Users\Xieon\Desktop\repositories\test\SysBot.PokemonScarletViolet\SysBot.Pokemon.WinForms\bin\x64\Debug\net7.0-windows\pkmnpic"; // Create the full path to the PNG file based on {(Species)tradedToUser} @@ -122,11 +124,12 @@ public async void TradeFinished(PokeRoutineExecutor routine, PokeTradeDetail< // Define a dictionary to map integer values to emote strings // not used currently string? ballEmote = null; // Declare ballEmote outside of the if statement - string? ballEmbed = null; - string? noball = null; + // string? ballEmbed = null; + // string? noball = null; Dictionary ballEmotes = new() + { { 1, ":ball_Master:" }, { 2, ":ball_Ultra:" }, @@ -181,8 +184,28 @@ public async void TradeFinished(PokeRoutineExecutor routine, PokeTradeDetail< var tid = Data.DisplayTID; var stats = $"ATK:{atk} / DEF:{def} / SpD:{spd} / SpA:{spa} / Spe:{spe} / HP:{hp}"; -// Check if Data.IsShiny is true and set the shiny string accordingly -string shiny = Data.IsShiny ? ":sparkles:" : ""; + + // Held Item Code Block + string holding = $""; + var item = Data.HeldItem; + if(item < 0) + { + holding = "Pokemon not holding an item"; + } + else + { + //holding = Data.Name.HeldItem; + + holding = $"Pokemong is holding {item} "; + } + + var ability = Data.Ability; + var ot = Data.OT_Name; // The IGN of the trainer who requested a trade. + var tid = Data.DisplayTID; //The TID as it appears in game. + var stats = $"ATK:{atk} / DEF:{def} / SpD:{spd} / SpA:{spa} / Spe:{spe} / HP:{hp}"; //build the stats straings + // Check if Data.IsShiny is true and set the shiny string accordingly + string shiny = Data.IsShiny ? ":sparkles:" : ""; + // If the pokemon traded {(Species)tradedToUser} matches the name of a pokemon in our folder we display that image in the embed - @@ -191,16 +214,24 @@ public async void TradeFinished(PokeRoutineExecutor routine, PokeTradeDetail< if (File.Exists(imagePath)) { var embed = new EmbedBuilder(); - embed.WithTitle("XGC HAS COMPLETED A MEMBER TRADE REQUEST"); + + var colon = $":"; + var dream = $"ball_dream"; + _ = $"{colon}{dream}{colon}"; + + + //// Test Code for the Moves. + var moveList = Data.Moves.ToList; + + embed.WithTitle("XGC MEMBER TRADE REQUEST COMPLETED"); + embed.AddField("Trainer", Trader.Mention, true); // Display trainer's name in an inline field embed.AddField("Received Pokémon", $"{shiny}{(Species)tradedToUser}", true); // Display received Pokémon with or without shiny indicator embed.AddField("Trainer IG Info", $"OT: {ot} / TID: {tid}"); embed.AddField("IV Spread", $"{stats}", true); - embed.AddField("MOVES #'s", $"{m1}\n{m2}\n{m3}\n{m4}"); - embed.AddField("Breloom?", $"Breloom override is off, if it was on would you have gotten Breloom instead?{breloom}"); - - + embed.AddField("Ability:",$"{ability}", true); + //If the ball requested is in XGC Server - how do we display it as an image in the embed? /* if(Data.Ball== 7 )// https://cdn.discordapp.com/emojis/1089614882132996257.webp?size=160&quality=lossless Nest ball test @@ -238,6 +269,8 @@ public async void TradeFinished(PokeRoutineExecutor routine, PokeTradeDetail< embed.AddField("Received Pokémon", $"{(Species)tradedToUser}", true); // Display received Pokémon embed.AddField("Trainer Info",$"OT:{ot} / TID:{tid}"); embed.AddField("IV Spread",$"{stats}",true); + embed.AddField("Missing Image",$"Note to Staff {(Species)tradedToUser} is missing an image", true); // let staff know that there's a missing database image. + embed.AddField("Ability:",$"{ability}", true); //embed.AddField("Ball",$"{ballemote}",true); embed.AddField("Thanks for being a member", ":heart:"); // Display a heart thanking the user for using the bot await CommandSentChannel.SendMessageAsync(embed: embed.Build()).ConfigureAwait(false); // Send the embed in the same channel where the command was sent