public static Task Nav_ShopDecorPreview(SocketReaction reaction, MenuIdStructure menuSession) { if (reaction.Emote.Name == "↩️") { // Stop the timeout timer associated with the menu menuSession.MenuTimer.Stop(); // Go to a new menu _ = ShopMenu.ShopMainMenu(menuSession.User, menuSession.MenuMessage); return(Task.CompletedTask); } // If the user reacts with the checkmark emote, it's time to purchase the décor! else if (reaction.Emote.Name == "✅") { // Get the account information of the user. var account = UserInfoClasses.GetAccount(menuSession.User); // Search for an item list that corresponds to the user's ID. If a menu entry was found, this should also exist alongside it. var itemSession = Global.ItemIdList.SingleOrDefault(x => x.User.Id == reaction.UserId); // Get the information of the chosen décor index. var decor_info = DecorInfoMethods.GetDecorInfo(itemSession.SelectedItem); // If the user has not maxed out their Star Level rank, deduct the cost of the purchased décor from the user's P-Medal value. if (account.Level_Resets < 3) { account.P_Medals -= decor_info.Price; } // Add the purchased item to the user's list of owned décor. account.Decor_Owned += $"{itemSession.SelectedItem};"; //Update the user's account. UserInfoClasses.UpdateAccount(account); // Stop the timeout timer associated with the menu menuSession.MenuTimer.Stop(); // Go to a new menu _ = ShopMenu.ShopDecorPurchased(menuSession.User, menuSession.MenuMessage); return(Task.CompletedTask); } return(Task.CompletedTask); }
public static async Task ShopDecorPurchased(SocketGuildUser user, RestUserMessage message) { // Get the account information of the command's target var account = UserInfoClasses.GetAccount(user); // Find both the menu session and item session associated with the current user and store them in variables. var menuSession = Global.MenuIdList.SingleOrDefault(x => x.User.Id == user.Id); var itemSession = Global.ItemIdList.SingleOrDefault(x => x.User.Id == user.Id); // Get the information of the chosen décor index. var decor_info = DecorInfoMethods.GetDecorInfo(itemSession.SelectedItem); // Create a new embed that will be displayed in the message. var embed = new EmbedBuilder(); var author = new EmbedAuthorBuilder { Name = "Purchase Complete!", IconUrl = user.GetAvatarUrl() }; embed.WithAuthor(author); // Determine the color and thumbnail for the embeded message. if (account.Profile_Theme == "P3") { embed.WithColor(37, 149, 255); } else if (account.Profile_Theme == "P4") { embed.WithColor(255, 229, 49); } else if (account.Profile_Theme == "P5") { embed.WithColor(213, 27, 4); } embed.WithDescription($"Do you want to set `{decor_info.Title}` as your décor right now?"); var footer = new EmbedFooterBuilder { Text = "❌ No | ✅ Yes" }; // Add the footer to the embed. embed.WithFooter(footer); // Attempt deleting the message if it hasn't been deleted by the user yet. try { // Delete the current message from the channel. await message.DeleteAsync(); } catch (Exception ex) { Console.WriteLine(ex); } // If the bot lacks permission to send messages, catch the exception and return. try { // Reassign the menu session's message to a new message generated from the created embed. menuSession.MenuMessage = (RestUserMessage)await message.Channel.SendMessageAsync("", false, embed.Build()); } catch (Exception ex) { Console.WriteLine(ex); return; } // Set the "message" variable to the menu session's message. message = menuSession.MenuMessage; // Edit the menu session according to the current message. menuSession.CurrentMenu = "Shop_Decor_Purchased"; menuSession.MenuTimer = new Timer() { // Create a timer that expires as a "time out" duration for the user. Interval = MenuConfig.menu.timerDuration, AutoReset = false, Enabled = true }; // If the menu timer runs out, activate a function. menuSession.MenuTimer.Elapsed += (sender, e) => MenuTimer_Elapsed(sender, e, menuSession, itemSession); // Create an empty list for reactions. List <IEmote> reaction_list = new List <IEmote> { }; // Add needed emote reactions for the menu. reaction_list.Add(new Emoji("❌")); reaction_list.Add(new Emoji("✅")); // Add the reactions to the message. _ = ReactionHandling.AddReactionsToMenu(message, reaction_list); }
public static async Task ShopMainMenu(SocketGuildUser user, RestUserMessage message) { //Get the account information of the command's target var account = UserInfoClasses.GetAccount(user); // Find both the menu session and item session associated with the current user and store them in variables. var menuSession = Global.MenuIdList.SingleOrDefault(x => x.User.Id == user.Id); var itemSession = Global.ItemIdList.SingleOrDefault(x => x.User.Id == user.Id); var embed = new EmbedBuilder(); var author = new EmbedAuthorBuilder { Name = "Décor Shop", IconUrl = user.GetAvatarUrl() }; embed.WithAuthor(author); //Determine color for embeded message if (account.Profile_Theme == "P3") { embed.WithColor(37, 149, 255); } else if (account.Profile_Theme == "P4") { embed.WithColor(255, 229, 49); } else if (account.Profile_Theme == "P5") { embed.WithColor(213, 27, 4); } embed.AddField("Wallet", $"<:PMedals:672637091171139615> **{account.P_Medals}**"); // Create a string variable to store the text that will be displayed on the message's body. string displayed_shop_list = ""; // Create an int variable from the number of items in the list minus the starting index to count from. // Since the ItemIndexBase should always initially start at zero, nothing will be subtracted at first but will adjust as the index moves when the page changes. int remaining_list_length = itemSession.ItemList.Count - itemSession.ItemIndexBase; // Create another int variable that will indicate a subset of the item list that the user is currently viewing. int sublist_length = 0; // If the remaining number of items in the list is greater than or equal to the max amount of items that should be displayed, make the sublist_length int also equal to max_items_displayed. if (remaining_list_length >= itemSession.MaxItemsDisplayed) { sublist_length = itemSession.MaxItemsDisplayed; } // Else, if the number of remaining items is less than max_items_displayed, make sublist_length equal to the remaining number of items. else { sublist_length = remaining_list_length; } // Create an int to properly display the needed emotes when iterating through the item list. int displayed_list_counter = 0; // Iterate through the item list starting from the ItemBaseIndex and up until sublist_length. for (int i = itemSession.ItemIndexBase; i < (itemSession.ItemIndexBase + sublist_length); i++) { // Increase the displayed_list_counter by one. displayed_list_counter += 1; // Get the information of the current décor iteration. var decor_info = DecorInfoMethods.GetDecorInfo(itemSession.ItemList[i]); // Add the entry to the displayed_shop_list string. displayed_shop_list += $":{DecorInfoMethods.NumberToWords(displayed_list_counter)}: {decor_info.Title} - <:cost:780352551945895936> **{decor_info.Price}**\n"; } // Add the displayed_shop_list as a new field to the embed. embed.AddField("What would you like to purchase? Select a number you wish to view.", $"{displayed_shop_list}"); // Create a string variable to store text for the footer. This will change depending on the state of the menu. string footer_text = ""; // Check if the starting item index is greater than or equal to max_items_displayed. if (itemSession.ItemIndexBase >= itemSession.MaxItemsDisplayed) { // If so, there will be a "Previous Page" button displayed on the footer. footer_text += "◀️ Previous Page | "; } // Check if the number of items in the list minus the starting item index is more than max_items_displayed. if (remaining_list_length > itemSession.MaxItemsDisplayed) { // If so, there will be a "Next Page" button on the footer. footer_text += "▶️ Next Page | "; } // Calculate the amount of pages there will be in total and store it in a variable. int pageCount = (itemSession.ItemList.Count + itemSession.MaxItemsDisplayed - 1) / itemSession.MaxItemsDisplayed; // Add two icons to the end of footer_text regardless of the state, plus a page counter on a new line. footer_text += $"⚙️ Sort | ❌ Exit Shop\nPage {itemSession.CurrentPage} / {pageCount}"; // Create the footer object for the embed. var footer = new EmbedFooterBuilder { Text = footer_text }; // Add the footer to the embed. embed.WithFooter(footer); // Attach a locally generated image to the embed. This image hasn't been created yet, so the filename is just a placeholder for now. embed.WithImageUrl($"attachment://preview.png"); // Create a new stream. We'll use this to create the locally generated image. MemoryStream memoryStream = new MemoryStream(); // Generate a bitmap comprised of thumbnail previews of the décor being listed on the current page. Bitmap decor_preview = DecorInfoMethods.DecorPreviews(itemSession, sublist_length); // Save the décor preview bitmap to the stream as a PNG. decor_preview.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); // Ensure the stream is set to the beginning of itself. memoryStream.Seek(0, SeekOrigin.Begin); // Attempt deleting the message if it hasn't been deleted by the user yet. try { // Delete the current message from the channel. await message.DeleteAsync(); } catch (Exception ex) { Console.WriteLine(ex); } // If the bot lacks permission to attach files, catch the exception, send an error message, and return. try { // Reassign the menu session's message to a new message generated from the created embed and preview image. menuSession.MenuMessage = (RestUserMessage)await message.Channel.SendFileAsync(memoryStream, "preview.png", "", false, embed.Build()); } catch (Exception ex) { await ErrorHandling.AttachFilesError((SocketTextChannel)message.Channel); Console.WriteLine(ex); return; } // Set the "message" variable to the menu session's message. message = menuSession.MenuMessage; // Edit the menu session according to the current message. menuSession.CurrentMenu = "Shop_Main_Menu"; menuSession.MenuTimer = new Timer() { // Create a timer that expires as a "time out" duration for the user. Interval = MenuConfig.menu.timerDuration, AutoReset = false, Enabled = true }; // If the timer runs out, activate a function. menuSession.MenuTimer.Elapsed += (sender, e) => MenuTimer_Elapsed(sender, e, menuSession, itemSession); // Create an empty list for reactions. List <IEmote> reaction_list = new List <IEmote> { }; // Add needed emote reactions for the menu. // Check if the starting item index is greater than or equal to max_items_displayed. if (itemSession.ItemIndexBase >= itemSession.MaxItemsDisplayed) { // If so, there will be a "Previous Page" button added as a reaction. reaction_list.Add(new Emoji("◀️")); } // Check if the number of items in the list minus the starting item index is more than max_items_displayed. if (remaining_list_length > itemSession.MaxItemsDisplayed) { // If so, there will be a "Next Page" button added as a reaction. reaction_list.Add(new Emoji("▶️")); } // Reset the displayed_list_counter to zero. displayed_list_counter = 0; for (int i = 0; i < sublist_length; i++) { // Increase the displayed_list_counter by one. displayed_list_counter += 1; // For each loop iteration, add a keycap emote representing an item entry being displayed to the user. reaction_list.Add(new Emoji($"{DecorInfoMethods.NumberToKeycapEmoji(displayed_list_counter)}")); } // Add two more reactions to the end of the message. reaction_list.Add(new Emoji("⚙️")); reaction_list.Add(new Emoji("❌")); // Add the reactions to the message. _ = ReactionHandling.AddReactionsToMenu(message, reaction_list); }
public static async Task ShopDecorPreview(SocketGuildUser user, RestUserMessage message, int item_index) { // Get the account information of the command's target var account = UserInfoClasses.GetAccount(user); // Find both the menu session and item session associated with the current user and store them in variables. var menuSession = Global.MenuIdList.SingleOrDefault(x => x.User.Id == user.Id); var itemSession = Global.ItemIdList.SingleOrDefault(x => x.User.Id == user.Id); // Get the information of the chosen décor index. var decor_info = DecorInfoMethods.GetDecorInfo(itemSession.ItemList[item_index]); // Create a new embed that will be displayed in the message. var embed = new EmbedBuilder(); var author = new EmbedAuthorBuilder { Name = "Décor Preview", IconUrl = user.GetAvatarUrl() }; embed.WithAuthor(author); // Create an empty string variable for the description text. string description_text = ""; // Create an empty string list. List <string> owned_decor = new List <string> { }; // Check if the user owns any décor. if (account.Decor_Owned != "") { // If so, convert their Decor_Owned value into a string list and assign it to the owned_decor string list. owned_decor = DecorInfoMethods.StringToStringArray(account.Decor_Owned); } // Perform a check to see if the user has enough money to purchase the décor. If so, state the cost. if (account.P_Medals >= decor_info.Price) { description_text = $"Purchase this décor for <:cost:780352551945895936> **{decor_info.Price}**?"; } // If the user does not have enough money, prevent them from purchasing the décor. else { description_text = $"You do not have enough P-Medals to purchase this décor."; } // Add the description to the embed. embed.WithDescription(description_text); embed.WithThumbnailUrl($"{decor_info.Thumbnail_Link}"); embed.AddField("Wallet", $"<:PMedals:672637091171139615> **{account.P_Medals}**"); embed.AddField("Title", $"{decor_info.Title}", true); embed.AddField("Game", $"{decor_info.Game}", true); embed.AddField("Designer", $"[{decor_info.Designer_Name}]({decor_info.Designer_Link})", true); // If a description exists for the décor itself, add it as a field. if (decor_info.Description != null) { embed.AddField("Description", $"{decor_info.Description}", false); } // Set the color of the embed by converting the décor's stored hex value to a usable format. embed.WithColor((Discord.Color)System.Drawing.ColorTranslator.FromHtml($"{decor_info.Embed_Color}")); // Create a string variable for text that will be displayed in the footer. string footer_text = "↩️ Back"; // If the user doesn't own the décor being displayed and has enough money to purchase it, add a "Confirm" emote to the footer. if (owned_decor.Contains(decor_info.Decor_ID) == false && account.P_Medals >= decor_info.Price) { footer_text += $" | ✅ Confirm"; } var footer = new EmbedFooterBuilder { Text = footer_text }; // Add the footer to the embed. embed.WithFooter(footer); // Attach a locally generated image to the embed. This image hasn't been created yet, so the filename is just a placeholder for now. embed.WithImageUrl($"attachment://{decor_info.Decor_ID}_preview.png"); // Create a new stream. We'll use this to create the locally generated image. MemoryStream memoryStream = new MemoryStream(); // Generate a bitmap comprised of the thumbnail of the current décor. Bitmap decor_preview = (Bitmap)System.Drawing.Image.FromFile($@"{AssetDirectoryConfig.assetDirectory.assetFolderPath}//Profile//StatusScreens//Decor//{decor_info.Decor_ID}//_Thumbnails//preview_1.png"); // Save the décor preview bitmap to the stream as a PNG. decor_preview.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); // Ensure the stream is set to the beginning of itself. memoryStream.Seek(0, SeekOrigin.Begin); // Attempt deleting the message if it hasn't been deleted by the user yet. try { // Delete the current message from the channel. await message.DeleteAsync(); } catch (Exception ex) { Console.WriteLine(ex); } // If the bot lacks permission to attach files, catch the exception, send an error message, and return. try { // Reassign the menu session's message to a new message generated from the created embed and preview image. menuSession.MenuMessage = (RestUserMessage)await message.Channel.SendFileAsync(memoryStream, $"{decor_info.Decor_ID}_preview.png", "", false, embed.Build()); } catch (Exception ex) { await ErrorHandling.AttachFilesError((SocketTextChannel)message.Channel); Console.WriteLine(ex); return; } // Set the "message" variable to the menu session's message. message = menuSession.MenuMessage; // Edit the menu session according to the current message. menuSession.CurrentMenu = "Shop_Decor_Preview"; menuSession.MenuTimer = new Timer() { // Create a timer that expires as a "time out" duration for the user. Interval = MenuConfig.menu.timerDuration, AutoReset = false, Enabled = true }; // Edit the item session to save the selected décor's ID to a variable. // If the user chooses to buy it, we will be able to pass its information to other methods. itemSession.SelectedItem = decor_info.Decor_ID; // If the menu timer runs out, activate a function. menuSession.MenuTimer.Elapsed += (sender, e) => MenuTimer_Elapsed(sender, e, menuSession, itemSession); // Create an empty list for reactions. List <IEmote> reaction_list = new List <IEmote> { }; // Add needed emote reactions for the menu. reaction_list.Add(new Emoji("↩️")); // If the user does not own this décor and has enough money to purchase it, add a checkmark reaction to the message. if (owned_decor.Contains(decor_info.Decor_ID) == false && account.P_Medals >= decor_info.Price) { reaction_list.Add(new Emoji("✅")); } // Add the reactions to the message. _ = ReactionHandling.AddReactionsToMenu(message, reaction_list); }
public static async Task Status_Decor_Main(SocketGuildUser user, RestUserMessage message) { // Get the account information of the command's user. var account = UserInfoClasses.GetAccount(user); // Find both the menu session and item session associated with the current user and store them in variables. var menuSession = Global.MenuIdList.SingleOrDefault(x => x.User.Id == user.Id); var itemSession = Global.ItemIdList.SingleOrDefault(x => x.User.Id == user.Id); var embed = new EmbedBuilder(); var author = new EmbedAuthorBuilder { Name = "Status Screen Décor", IconUrl = user.GetAvatarUrl() }; embed.WithAuthor(author); // Determine the color and thumbnail for the embeded message. embed.WithColor(EmbedSettings.Get_Profile_Embed_Color(account)); embed.WithThumbnailUrl(EmbedSettings.Get_Profile_Config_Thumbnail(account)); // Create a string variable to store the text that will be displayed on the message's body. string displayed_decor_list = ""; // Create an int variable from the number of items in the list minus the starting index to count from. // Since the ItemIndexBase should always initially start at zero, nothing will be subtracted at first but will adjust as the index moves when the page changes. int remaining_list_length = itemSession.ItemList.Count - itemSession.ItemIndexBase; // Create another int variable that will indicate a subset of the item list that the user is currently viewing. int sublist_length = 0; // If the remaining number of items in the list is greater than or equal to the max amount of items that should be displayed, make the sublist_length int also equal to max_items_displayed. if (remaining_list_length >= itemSession.MaxItemsDisplayed) { sublist_length = itemSession.MaxItemsDisplayed; } // Else, if the number of remaining items is less than max_items_displayed, make sublist_length equal to the remaining number of items. else { sublist_length = remaining_list_length; } // Create an int to properly display the needed emotes when iterating through the item list. int displayed_list_counter = 0; // Iterate through the item list starting from the ItemBaseIndex and up until sublist_length. for (int i = itemSession.ItemIndexBase; i < (itemSession.ItemIndexBase + sublist_length); i++) { // Increase the displayed_list_counter by one. displayed_list_counter += 1; // Get the information of the current décor iteration. var decor_info = DecorInfoMethods.GetDecorInfo(itemSession.ItemList[i]); // Add the entry to the displayed_shop_list string. displayed_decor_list += $":{DecorInfoMethods.NumberToWords(displayed_list_counter)}: {decor_info.Title}\n"; } // Create a string variable to store text for the footer. This will change depending on the state of the menu. string footer_text = ""; // Depending on whether or not the user owns or can set any décor, perform different actions. if (displayed_decor_list.Length > 0) { // Add a "Back" button to be displayed on the footer. footer_text += "↩️ Profile Settings | "; // Check if the starting item index is greater than or equal to max_items_displayed. if (itemSession.ItemIndexBase >= itemSession.MaxItemsDisplayed) { // If so, there will be a "Previous Page" button displayed on the footer. footer_text += "◀️ Previous Page | "; } // Check if the number of items in the list minus the starting item index is more than max_items_displayed. if (remaining_list_length > itemSession.MaxItemsDisplayed) { // If so, there will be a "Next Page" button on the footer. footer_text += "▶️ Next Page | "; } // Calculate the amount of pages there will be in total and store it in a variable. int pageCount = (itemSession.ItemList.Count + itemSession.MaxItemsDisplayed - 1) / itemSession.MaxItemsDisplayed; // Add two icons to the end of footer_text regardless of the state, plus a page counter on a new line. footer_text += $"⚙️ Sort\nPage {itemSession.CurrentPage} / {pageCount}"; // Create the footer object for the embed. var footer = new EmbedFooterBuilder { Text = footer_text }; // Add the footer to the embed. embed.WithFooter(footer); // Create an empty string variable. This will hold part of the embeded message's description string description_text = ""; // If the user has a décor and a profile theme currently set, create a description text explaining how to remove the currently set décor. if (account.Decor_Setting != "" && account.Profile_Theme != "") { description_text = "" + "**Select a décor to view.**\n" + "**To remove your current décor and set the default one for your profile theme, select :white_square_button:.**"; } // If not, create a default description text instructing to select a décor. else { description_text = "**Select a décor to view.**"; } // Create a string variable that will hold the title of the user's currently set décor. string set_decor_title = DecorInfoMethods.GetDecorTitle(user); embed.WithDescription("" + $"{description_text}\n" + "\n" + $"⚙️ **Current setting:** **`{set_decor_title}`**\n" + "\n" + $"{displayed_decor_list}"); // Attach a locally generated image to the embed. embed.WithImageUrl($"attachment://preview.png"); // Create a new stream. We'll use this to create the locally generated image. MemoryStream memoryStream = new MemoryStream(); // Generate a bitmap comprised of thumbnail previews of the décor being listed on the current page. Bitmap decor_preview = DecorInfoMethods.DecorPreviews(itemSession, sublist_length); // Save the décor preview bitmap to the stream as a PNG. decor_preview.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); // Ensure the stream is set to the beginning of itself. memoryStream.Seek(0, SeekOrigin.Begin); // Attempt deleting the message if it hasn't been deleted by the user yet. try { // Delete the current message from the channel. await message.DeleteAsync(); } catch (Exception ex) { Console.WriteLine(ex); } // If the bot lacks permission to attach files, catch the exception, send an error message, and return. try { // Reassign the menu session's message to a new message generated from the created embed and preview image. menuSession.MenuMessage = (RestUserMessage)await message.Channel.SendFileAsync(memoryStream, "preview.png", "", false, embed.Build()); } catch (Exception ex) { await ErrorHandling.AttachFilesError((SocketTextChannel)message.Channel); Console.WriteLine(ex); return; } // Set the "message" variable to the menu session's message. message = menuSession.MenuMessage; } else { // Add a "Back" button to be displayed on the footer. footer_text += "↩️ Profile Settings"; // Create the footer object for the embed. var footer = new EmbedFooterBuilder { Text = footer_text }; // Add the footer to the embed. embed.WithFooter(footer); embed.WithDescription("You don't have any other décor to set. Visit the Décor Shop with the **`>shop`** command to browse and buy décor for your collection."); // Attempt editing the message if it hasn't been deleted by the user yet. If it has, catch the exception, send an error message, and return. try { // Remove all reactions from the current message. await message.RemoveAllReactionsAsync(); // Edit the current active message by replacing it with the recently created embed. await message.ModifyAsync(x => { x.Embed = embed.Build(); }); } catch (Exception ex) { await ErrorHandling.MissingMessageError((SocketTextChannel)message.Channel); Console.WriteLine(ex); return; } } // Edit the menu session according to the current message. menuSession.CurrentMenu = "Status_Decor_Main"; menuSession.MenuTimer = new Timer() { // Create a timer that expires as a "time out" duration for the user. Interval = MenuConfig.menu.timerDuration, AutoReset = false, Enabled = true }; // If the timer runs out, activate a function. menuSession.MenuTimer.Elapsed += (sender, e) => MenuTimer_Elapsed(sender, e, menuSession, itemSession); // Create an empty list for reactions. List <IEmote> reaction_list = new List <IEmote> { }; // Add needed emote reactions for the menu. reaction_list.Add(new Emoji("↩️")); // Check if the starting item index is greater than or equal to max_items_displayed. if (itemSession.ItemIndexBase >= itemSession.MaxItemsDisplayed) { // If so, there will be a "Previous Page" button added as a reaction. reaction_list.Add(new Emoji("◀️")); } // Check if the number of items in the list minus the starting item index is more than max_items_displayed. if (remaining_list_length > itemSession.MaxItemsDisplayed) { // If so, there will be a "Next Page" button added as a reaction. reaction_list.Add(new Emoji("▶️")); } // Reset the displayed_list_counter to zero. displayed_list_counter = 0; for (int i = 0; i < sublist_length; i++) { // Increase the displayed_list_counter by one. displayed_list_counter += 1; // For each loop iteration, add a keycap emote representing an item entry being displayed to the user. reaction_list.Add(new Emoji($"{DecorInfoMethods.NumberToKeycapEmoji(displayed_list_counter)}")); } // If the user owns any décor, add a gear reaction in order to sort entries. if (displayed_decor_list.Length > 0) { reaction_list.Add(new Emoji("⚙️")); } // If the user has a décor and a profile theme currently set, add a box reaction. This gives the option to remove the set décor and return to the default one. if (account.Decor_Setting != "" && account.Profile_Theme != "") { reaction_list.Add(new Emoji("🔳")); } // Add the reactions to the message. _ = ReactionHandling.AddReactionsToMenu(message, reaction_list); }