From ce2d96b8c36c86a3d873f26ec2f1263ed1524b47 Mon Sep 17 00:00:00 2001 From: KMohZaid <68484509+KMohZaid@users.noreply.github.com> Date: Sun, 15 Jun 2025 18:20:01 -0700 Subject: [PATCH 1/2] add(topic support) 1. added `/setgrouptopic` command which admin can use to set/unset group topic for werewolf bot 2. added new column in `dbo.Group` table : `GroupTopicId` to store topic id 3. restricts bot command to specified topic only, except admin command and manually allowed command with Attribute `AllowOutsideConfiguredTopic` Note : I may have forget some places to update and make topic support --- Werewolf for Telegram/Database/Group.cs | 1 + .../Database/WerewolfModel.Designer.cs | 2 +- .../Database/WerewolfModel.edmx | 5 +- .../Werewolf Control/Attributes/Command.cs | 5 + .../Commands/AdminCommands.cs | 110 +++++++++++++++++- .../Werewolf Control/Commands/GameCommands.cs | 3 +- .../Commands/GeneralCommands.cs | 6 +- .../Werewolf Control/Commands/GifCommands.cs | 22 ++-- .../Werewolf Control/Commands/Helpers.cs | 4 +- .../Handlers/UpdateHandler.cs | 32 +++++ .../Werewolf Control/Helpers/Bot.cs | 17 ++- .../Helpers/LanguageHelper.cs | 2 +- .../Werewolf Control/Models/Command.cs | 1 + .../Werewolf Node/Program.cs | 22 +++- .../Werewolf Node/Werewolf.cs | 30 ++--- werewolf.sql | 1 + 16 files changed, 224 insertions(+), 39 deletions(-) diff --git a/Werewolf for Telegram/Database/Group.cs b/Werewolf for Telegram/Database/Group.cs index deaf32e80..0ad60f324 100644 --- a/Werewolf for Telegram/Database/Group.cs +++ b/Werewolf for Telegram/Database/Group.cs @@ -25,6 +25,7 @@ public Group() public int Id { get; set; } public string Name { get; set; } public long GroupId { get; set; } + public Nullable GroupTopicId { get; set; } public Nullable Preferred { get; set; } public string Language { get; set; } public Nullable DisableNotification { get; set; } diff --git a/Werewolf for Telegram/Database/WerewolfModel.Designer.cs b/Werewolf for Telegram/Database/WerewolfModel.Designer.cs index 31386a04d..6b6a0f4be 100644 --- a/Werewolf for Telegram/Database/WerewolfModel.Designer.cs +++ b/Werewolf for Telegram/Database/WerewolfModel.Designer.cs @@ -1,4 +1,4 @@ -// T4 code generation is enabled for model 'C:\Users\parab\Source\Repos\Werewolf\Werewolf for Telegram\Database\WerewolfModel.edmx'. +// T4 code generation is enabled for model 'C:\Users\waifu\Desktop\Werewolf\Werewolf for Telegram\Database\WerewolfModel.edmx'. // To enable legacy code generation, change the value of the 'Code Generation Strategy' designer // property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model // is open in the designer. diff --git a/Werewolf for Telegram/Database/WerewolfModel.edmx b/Werewolf for Telegram/Database/WerewolfModel.edmx index 74ef243a4..d360ef315 100644 --- a/Werewolf for Telegram/Database/WerewolfModel.edmx +++ b/Werewolf for Telegram/Database/WerewolfModel.edmx @@ -178,6 +178,7 @@ + @@ -884,6 +885,7 @@ warning 6002: The table/view 'werewolf.dbo.v_IdleKill24HoursMain' does not have + @@ -1664,6 +1666,7 @@ warning 6002: The table/view 'werewolf.dbo.v_IdleKill24HoursMain' does not have + @@ -2176,4 +2179,4 @@ warning 6002: The table/view 'werewolf.dbo.v_IdleKill24HoursMain' does not have - \ No newline at end of file + diff --git a/Werewolf for Telegram/Werewolf Control/Attributes/Command.cs b/Werewolf for Telegram/Werewolf Control/Attributes/Command.cs index 9c897e3ef..dfb91e115 100644 --- a/Werewolf for Telegram/Werewolf Control/Attributes/Command.cs +++ b/Werewolf for Telegram/Werewolf Control/Attributes/Command.cs @@ -44,5 +44,10 @@ public class Command : Attribute /// Can this command be run by anonymous admins in groups /// public bool AllowAnonymousAdmins { get; set; } = false; + + /// + /// Allow commands to be run outside configured topic in group. + /// + public bool AllowOutsideConfiguredTopic {get; set; } = false; } } diff --git a/Werewolf for Telegram/Werewolf Control/Commands/AdminCommands.cs b/Werewolf for Telegram/Werewolf Control/Commands/AdminCommands.cs index 2674466ad..0358843f1 100644 --- a/Werewolf for Telegram/Werewolf Control/Commands/AdminCommands.cs +++ b/Werewolf for Telegram/Werewolf Control/Commands/AdminCommands.cs @@ -1,4 +1,4 @@ -using Database; +using Database; using Newtonsoft.Json; using System; using System.Collections; @@ -23,6 +23,114 @@ namespace Werewolf_Control { public static partial class Commands { + [Attributes.Command(Trigger = "setgrouptopic", GroupAdminOnly = true, InGroupOnly = true)] + public static void SetGroupTopic(Update update, string[] args) + { + long chatId = update.Message.Chat.Id; + int? topicId = update.Message.MessageThreadId; + + int? currentTopicId; + + using (var db = new WWContext()) + { + currentTopicId = db.Groups + .Where(g => g.GroupId == chatId) + .Select(g => g.GroupTopicId) + .FirstOrDefault(); + } + + bool inGeneralTopic = topicId == null || topicId == 0; + + string infoText = $"Current Topic ID: `{(currentTopicId.HasValue ? currentTopicId.Value.ToString() : "None")}`\n\n"; + + if (inGeneralTopic) + { + infoText += "This message is in the general topic (no thread ID).\n" + + "You cannot set this as the group topic. Only specific threads can be set."; + } + else + { + infoText += $"> You are currently inside topic ID: `{topicId}`.\n" + + "Choose what you'd like to do:"; + } + + var buttons = new List(); + + if (!inGeneralTopic) + { + buttons.Add(new[]{ + InlineKeyboardButton.WithCallbackData("Set Topic", "setgrouptopic_cmd|set"), + }); + } + + buttons.Add(new[]{ + InlineKeyboardButton.WithCallbackData("Unset Topic", "setgrouptopic_cmd|unset") + }); + + + var inlineKeyboard = new InlineKeyboardMarkup(buttons); + + Bot.Send( + infoText, + chatId, + customMenu: inlineKeyboard, + parseMode: ParseMode.Markdown, + messageThreadId: topicId, + forceTopic: false // TODO: use this param in other admin cmd, cmd which admin are allowed to use anywhere + ); + } + + internal static void SetGroupTopicCallback(CallbackQuery query) + { + var chatId = query.Message.Chat.Id; + var topicId = query.Message.MessageThreadId; + + using (var db = new WWContext()) + { + var group = db.Groups.FirstOrDefault(g => g.GroupId == chatId); + if (group == null) + { + group = MakeDefaultGroup(chatId, query.Message.Chat.Title, "setgrouptopic_callback"); + db.Groups.Add(group); + } + + string[] args = query.Data.Split('|'); + string action = args.Length > 1 ? args[1] : ""; + + switch (action) + { + case "set": + if (topicId == null) + { + Bot.ReplyToCallback(query, "This must be used inside a topic/thread."); + return; + } + + group.GroupTopicId = topicId; + db.SaveChanges(); + + Bot.Api.EditMessageTextAsync(chatId, query.Message.MessageId, + "Group topic has been set successfully."); + break; + + case "unset": + group.GroupTopicId = null; + db.SaveChanges(); + + Bot.Api.EditMessageTextAsync(chatId, query.Message.MessageId, + "Group topic has been unset."); + break; + + default: + Bot.ReplyToCallback(query, "Invalid topic action."); + break; + } + + Bot.Api.AnswerCallbackQueryAsync(query.Id); + } + } + + [Attributes.Command(Trigger = "smite", GroupAdminOnly = true, Blockable = true, InGroupOnly = true, AllowAnonymousAdmins = true)] public static void Smite(Update u, string[] args) { diff --git a/Werewolf for Telegram/Werewolf Control/Commands/GameCommands.cs b/Werewolf for Telegram/Werewolf Control/Commands/GameCommands.cs index 6a2bea184..3d72e7e1e 100644 --- a/Werewolf for Telegram/Werewolf Control/Commands/GameCommands.cs +++ b/Werewolf for Telegram/Werewolf Control/Commands/GameCommands.cs @@ -186,7 +186,8 @@ public static void Extend(Update update, string[] args) } } - [Command(Trigger = "stopwaiting", Blockable = true)] + // allowing outside topic, as it doesn't response into topic command was ran. dm user that their next game notification is turned off + [Command(Trigger = "stopwaiting", Blockable = true, AllowOutsideConfiguredTopic = true)] public static void StopWaiting(Update update, string[] args) { long groupid = 0; diff --git a/Werewolf for Telegram/Werewolf Control/Commands/GeneralCommands.cs b/Werewolf for Telegram/Werewolf Control/Commands/GeneralCommands.cs index c2f975894..6f40ebedf 100644 --- a/Werewolf for Telegram/Werewolf Control/Commands/GeneralCommands.cs +++ b/Werewolf for Telegram/Werewolf Control/Commands/GeneralCommands.cs @@ -179,9 +179,9 @@ public static void SetLang(Update update, string[] args) var curLangFileName = GetLanguage(update.Message.From.Id); var curLang = langs.First(x => x.FileName == curLangFileName); Bot.Api.SendTextMessageAsync(chatId: update.Message.From.Id, text: GetLocaleString("WhatLang", curLangFileName, curLang.Base), - replyMarkup: menu); + replyMarkup: menu, messageThreadId: update.Message.MessageThreadId); if (update.Message.Chat.Type != ChatType.Private) - Send(GetLocaleString("SentPrivate", GetLanguage(update.Message.From.Id)), update.Message.Chat.Id); + Send(GetLocaleString("SentPrivate", GetLanguage(update.Message.From.Id)), update.Message.Chat.Id, messageThreadId: update.Message.MessageThreadId); } [Command(Trigger = "start")] @@ -581,7 +581,7 @@ public static void MyIdles(Update update, string[] args) try { - var result = Bot.Api.SendTextMessageAsync(chatId: update.Message.From.Id, text: reply).Result; + var result = Bot.Api.SendTextMessageAsync(chatId: update.Message.From.Id, text: reply, messageThreadId: update.Message.MessageThreadId).Result; if (update.Message.Chat.Type != ChatType.Private) Send(GetLocaleString("SentPrivate", GetLanguage(update.Message.From.Id)), update.Message.Chat.Id); } diff --git a/Werewolf for Telegram/Werewolf Control/Commands/GifCommands.cs b/Werewolf for Telegram/Werewolf Control/Commands/GifCommands.cs index 0493f4fd4..a4a7ed403 100644 --- a/Werewolf for Telegram/Werewolf Control/Commands/GifCommands.cs +++ b/Werewolf for Telegram/Werewolf Control/Commands/GifCommands.cs @@ -28,7 +28,7 @@ public static void Donate(Update u, string[] args) { // Donations disabled as of 2024-06-08 var link = $"currently disabled"; - Bot.Api.SendTextMessageAsync(chatId: u.Message.Chat.Id, text: $"Donations are {link}, sorry!", parseMode: ParseMode.Html, disableWebPagePreview: true).Wait(); + Bot.Api.SendTextMessageAsync(chatId: u.Message.Chat.Id, text: $"Donations are {link}, sorry!", parseMode: ParseMode.Html, disableWebPagePreview: true, messageThreadId: u.Message.MessageThreadId).Wait(); return; //Bot.Api.SendTextMessageAsync(u.Message.Chat.Id, @@ -106,7 +106,7 @@ public static void SetCustomGifs(Update u, string[] args) "\n\n" + "PLEASE NOTE: Changing any gifs will automatically remove the approval for your pack, and an admin will need to approve it again\n" + "Let's begin! Select the situation you want to set a gif for", - replyMarkup: GetGifMenu(data)); + replyMarkup: GetGifMenu(data), messageThreadId: u.Message.MessageThreadId); var msg = "Current Approval Status:\n"; switch (data.Approved) @@ -123,7 +123,7 @@ public static void SetCustomGifs(Update u, string[] args) msg += "Disapproved By " + dby.Name + " for: " + data.DenyReason; break; } - Bot.Send(msg, u.Message.From.Id); + Bot.Send(msg, u.Message.From.Id, messageThreadId: u.Message.MessageThreadId); } } @@ -304,7 +304,7 @@ public static void RequestGif(CallbackQuery q) Bot.Api.SendTextMessageAsync(chatId: q.From.Id, text: q.Data.Split('|')[1] + "\nOk, send me the GIF you want to use for this situation, as a reply\n" + "#" + choice, - replyMarkup: new ForceReplyMarkup()); + replyMarkup: new ForceReplyMarkup(), messageThreadId: q.Message.MessageThreadId); } public static void AddGif(Message m) @@ -337,14 +337,14 @@ public static void AddGif(Message m) "users are unable to view them, we require you to use telegram's " + "[new GIFs in .mp4 format](https://telegram.org/blog/gif-revolution). " + "To fix this, try reuploading the GIF, your telegram app should then render it as .mp4. " + - "Please send me the GIF you want to use for this situation, as a reply\n#" + gifchoice, replyMarkup: new ForceReplyMarkup(), parseMode: ParseMode.Markdown); + "Please send me the GIF you want to use for this situation, as a reply\n#" + gifchoice, replyMarkup: new ForceReplyMarkup(), parseMode: ParseMode.Markdown, messageThreadId: m.MessageThreadId); return; } if (m.Animation.FileSize >= 1048576) // Maximum size is 1 MB { Bot.Api.SendTextMessageAsync(chatId: m.From.Id, text: "This GIF is too large, the maximum allowed size is 1MB.\n\n" + "Please send me the GIF you want to use for this situation, as a reply\n#" + gifchoice, - replyMarkup: new ForceReplyMarkup()); + replyMarkup: new ForceReplyMarkup(), messageThreadId: m.MessageThreadId); return; } @@ -431,7 +431,7 @@ public static void GetXsollaLink(CallbackQuery q = null, Message m = null) { // Donations disabled as of 2024-06-08 var link = $"currently disabled"; - Bot.Api.SendTextMessageAsync(chatId: (q?.Message ?? m).Chat.Id, text: $"Donations are {link}, sorry!", parseMode: ParseMode.Html, disableWebPagePreview: true).Wait(); + Bot.Api.SendTextMessageAsync(chatId: (q?.Message ?? m).Chat.Id, text: $"Donations are {link}, sorry!", parseMode: ParseMode.Html, disableWebPagePreview: true, messageThreadId: (q?.Message ?? m).MessageThreadId).Wait(); return; var from = q?.From ?? m?.From; @@ -465,13 +465,13 @@ public static void GetDonationInfo(CallbackQuery q = null, Message m = null) { // Donations disabled as of 2024-06-08 var link = $"currently disabled"; - Bot.Api.SendTextMessageAsync(chatId: q?.From.Id ?? m.From.Id, text: $"Donations are {link}, sorry!", parseMode: ParseMode.Html, disableWebPagePreview: true).Wait(); + Bot.Api.SendTextMessageAsync(chatId: q?.From.Id ?? m.From.Id, text: $"Donations are {link}, sorry!", parseMode: ParseMode.Html, disableWebPagePreview: true, messageThreadId: q.Message.MessageThreadId).Wait(); return; var menu = new Menu(); Bot.Api.SendTextMessageAsync(chatId: q?.From.Id ?? m.From.Id, text: "How much would you like to donate? Please enter a whole number, in US Dollars (USD), in reply to this message", - replyMarkup: new ForceReplyMarkup()); + replyMarkup: new ForceReplyMarkup(), messageThreadId: (q?.Message ?? m).MessageThreadId); } public static void ValidateDonationAmount(Message m) @@ -488,14 +488,14 @@ public static void ValidateDonationAmount(Message m) var api = RegHelper.GetRegValue("MainStripeProdAPI"); #endif Bot.Api.SendInvoiceAsync(chatId: m.From.Id, title: "Werewolf Donation", description: "Make a donation to Werewolf to help keep us online", payload: "somepayloadtest", providerToken: api, - currency: "USD", prices: new[] { new LabeledPrice("Donation", amt * 100) }, startParameter: "donatetg").Wait(); + currency: "USD", prices: new[] { new LabeledPrice("Donation", amt * 100) }, startParameter: "donatetg", messageThreadId: m.MessageThreadId).Wait(); } else { Bot.Api.SendTextMessageAsync(chatId: m.From.Id, text: "Invalid input.\n" + "How much would you like to donate? Please enter a whole number, in US Dollars (USD), in reply to this message", - replyMarkup: new ForceReplyMarkup()); + replyMarkup: new ForceReplyMarkup(), messageThreadId: m.MessageThreadId); } } diff --git a/Werewolf for Telegram/Werewolf Control/Commands/Helpers.cs b/Werewolf for Telegram/Werewolf Control/Commands/Helpers.cs index 2d1995379..7a98c31b6 100644 --- a/Werewolf for Telegram/Werewolf Control/Commands/Helpers.cs +++ b/Werewolf for Telegram/Werewolf Control/Commands/Helpers.cs @@ -170,9 +170,9 @@ private static void StartGame(GameMode gameMode, Update update) } } - internal static Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null) + internal static Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, Nullable messageThreadId = null) { - return Bot.Send(message, id, clearKeyboard, customMenu); + return Bot.Send(message, id, clearKeyboard, customMenu, messageThreadId: messageThreadId); } diff --git a/Werewolf for Telegram/Werewolf Control/Handlers/UpdateHandler.cs b/Werewolf for Telegram/Werewolf Control/Handlers/UpdateHandler.cs index 82d4ed353..8ca805da0 100644 --- a/Werewolf for Telegram/Werewolf Control/Handlers/UpdateHandler.cs +++ b/Werewolf for Telegram/Werewolf Control/Handlers/UpdateHandler.cs @@ -438,6 +438,31 @@ internal static void HandleUpdate(Update update) id); return; } + // If command is not allow outside topic, or it is not admin / dev command. Check topic id and restrict to topic. + // Kmoh COOKED : (Oops... i forgot another thing that i had to write down. It was in my mind a second ago...) + if (!(command.AllowOutsideConfiguredTopic || command.GlobalAdminOnly || command.LangAdminOnly || command.GroupAdminOnly || command.DevOnly) ) + { + // Only apply this restriction in groups (topics are only meaningful in groups) + if (update.Message.Chat.Type == ChatType.Group || update.Message.Chat.Type == ChatType.Supergroup) + { + int? currentTopicId; + using (var db = new WWContext()) + { + currentTopicId = db.Groups + .Where(g => g.GroupId == id) + .Select(g => g.GroupTopicId) + .FirstOrDefault(); + } + + // If a specific topic is set for the group and this message is NOT in that topic + if (currentTopicId == null || currentTopicId != update.Message.MessageThreadId) + { + Bot.Send($"This command can only be used in the configured topic/thread. (Topic id : {currentTopicId})", id,messageThreadId: update.Message.MessageThreadId, forceTopic : false); + return; + } + } + } + Bot.CommandsReceived++; command.Method.Invoke(update, args); } @@ -791,6 +816,12 @@ internal static void HandleCallback(CallbackQuery query) return; } + if (args[0] == "setgrouptopic_cmd") + { + Commands.SetGroupTopicCallback(query); + return; + } + //first off, if it's a game, send it to the node. if (args[0] == "vote") { @@ -824,6 +855,7 @@ internal static void HandleCallback(CallbackQuery query) var id = query.From.Id; Send($"Sending gifs for {pid}", id); Thread.Sleep(1000); + // INFO: dumping gifs, no need of topic send (this is mostly done in pm...) Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.CultWins), caption: "Cult Wins"); Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.LoversWin), caption: "Lovers Win"); Thread.Sleep(250); diff --git a/Werewolf for Telegram/Werewolf Control/Helpers/Bot.cs b/Werewolf for Telegram/Werewolf Control/Helpers/Bot.cs index 055ab05ca..264feeab7 100644 --- a/Werewolf for Telegram/Werewolf Control/Helpers/Bot.cs +++ b/Werewolf for Telegram/Werewolf Control/Helpers/Bot.cs @@ -102,6 +102,7 @@ public static void Initialize(string updateid = null) c.InGroupOnly = ca.InGroupOnly; c.LangAdminOnly = ca.LangAdminOnly; c.AllowAnonymousAdmins = ca.AllowAnonymousAdmins; + c.AllowOutsideConfiguredTopic = ca.AllowOutsideConfiguredTopic; Commands.Add(c); } } @@ -509,10 +510,24 @@ public static Node GetBestAvailableNode() } - internal static Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, ParseMode parseMode = ParseMode.Html, int? messageThreadId = null) + internal static Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, ParseMode parseMode = ParseMode.Html, int? messageThreadId = null, bool forceTopic = true) { //MessagesSent++; //message = message.Replace("`",@"\`"); + + // Try to load GroupTopicId from the database if no thread ID is provided + if (messageThreadId == null && forceTopic) + { + using (var db = new WWContext()) + { + var group = db.Groups.FirstOrDefault(g => g.GroupId == id); + if (group?.GroupTopicId != null) + { + messageThreadId = group.GroupTopicId; + } + } + } + if (clearKeyboard) { //var menu = new ReplyKeyboardRemove() { RemoveKeyboard = true }; diff --git a/Werewolf for Telegram/Werewolf Control/Helpers/LanguageHelper.cs b/Werewolf for Telegram/Werewolf Control/Helpers/LanguageHelper.cs index b1a3f0d4f..ddb0f07a3 100644 --- a/Werewolf for Telegram/Werewolf Control/Helpers/LanguageHelper.cs +++ b/Werewolf for Telegram/Werewolf Control/Helpers/LanguageHelper.cs @@ -109,7 +109,7 @@ public static void ValidateFiles(long id, int msgId, int? messageThreadId, strin result += "\n"; } - Bot.Api.SendTextMessageAsync(chatId: id, text: result, parseMode: ParseMode.Markdown); + Bot.Api.SendTextMessageAsync(chatId: id, text: result, parseMode: ParseMode.Markdown, messageThreadId: messageThreadId); var sortedfiles = Directory.GetFiles(Bot.LanguageDirectory).Select(x => new LangFile(x)).Where(x => x.Base == (choice ?? x.Base)).OrderBy(x => x.LatestUpdate); result = $"*Validation complete*\nErrors: {errors.Count(x => x.Level == ErrorLevel.Error)}\nMissing strings: {errors.Count(x => x.Level == ErrorLevel.MissingString)}"; result += $"\nMost recently updated file: {sortedfiles.Last().FileName}.xml ({sortedfiles.Last().LatestUpdate.ToString("MMM dd")})\nLeast recently updated file: {sortedfiles.First().FileName}.xml ({sortedfiles.First().LatestUpdate.ToString("MMM dd")})"; diff --git a/Werewolf for Telegram/Werewolf Control/Models/Command.cs b/Werewolf for Telegram/Werewolf Control/Models/Command.cs index effbf14e1..6b78de584 100644 --- a/Werewolf for Telegram/Werewolf Control/Models/Command.cs +++ b/Werewolf for Telegram/Werewolf Control/Models/Command.cs @@ -19,5 +19,6 @@ class Command public bool InGroupOnly { get; set; } public bool LangAdminOnly { get; set; } public bool AllowAnonymousAdmins { get; set; } + public bool AllowOutsideConfiguredTopic { get; set; } } } diff --git a/Werewolf for Telegram/Werewolf Node/Program.cs b/Werewolf for Telegram/Werewolf Node/Program.cs index 9aec7ff3a..89d53773c 100644 --- a/Werewolf for Telegram/Werewolf Node/Program.cs +++ b/Werewolf for Telegram/Werewolf Node/Program.cs @@ -380,23 +380,37 @@ public static void RemoveGame(Werewolf werewolf) } } - internal static async Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, Werewolf game = null, bool notify = false, bool preview = false) + internal static async Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, Werewolf game = null, bool notify = false, bool preview = false, bool isPlayerDM = false) { //MessagesSent++; //message = message.FormatHTML(); //message = message.Replace("`",@"\`"); + + // Try to load GroupTopicId from the database, only if it is not player dm + int? messageThreadId = null; + if(!isPlayerDM) + using (var db = new WWContext()) + { + var group = db.Groups.FirstOrDefault(g => g.GroupId == id); + if (group?.GroupTopicId != null) + { + messageThreadId = group.GroupTopicId; + } + } + + if (clearKeyboard) { var menu = new ReplyKeyboardRemove(); - return await Bot.SendTextMessageAsync(chatId: id, text: message, replyMarkup: menu, disableWebPagePreview: !preview, parseMode: ParseMode.Html, disableNotification: notify); + return await Bot.SendTextMessageAsync(chatId: id, text: message, replyMarkup: menu, disableWebPagePreview: !preview, parseMode: ParseMode.Html, disableNotification: notify, messageThreadId: messageThreadId); } else if (customMenu != null) { - return await Bot.SendTextMessageAsync(chatId: id, text: message, replyMarkup: customMenu, disableWebPagePreview: !preview, parseMode: ParseMode.Html, disableNotification: notify); + return await Bot.SendTextMessageAsync(chatId: id, text: message, replyMarkup: customMenu, disableWebPagePreview: !preview, parseMode: ParseMode.Html, disableNotification: notify, messageThreadId: messageThreadId); } else { - return await Bot.SendTextMessageAsync(chatId: id, text: message, disableWebPagePreview: !preview, parseMode: ParseMode.Html, disableNotification: notify); + return await Bot.SendTextMessageAsync(chatId: id, text: message, disableWebPagePreview: !preview, parseMode: ParseMode.Html, disableNotification: notify, messageThreadId: messageThreadId); } } diff --git a/Werewolf for Telegram/Werewolf Node/Werewolf.cs b/Werewolf for Telegram/Werewolf Node/Werewolf.cs index bb9c57fcd..783330337 100644 --- a/Werewolf for Telegram/Werewolf Node/Werewolf.cs +++ b/Werewolf for Telegram/Werewolf Node/Werewolf.cs @@ -231,9 +231,9 @@ public Werewolf(long chatid, User u, string chatGroup, GameMode gameMode) case GameMode.Chaos: FirstMessage = GetLocaleString("PlayerStartedChaosGame", u.FirstName); #if RELEASE - _joinMsgId = Program.Bot.SendDocumentAsync(chatId: ChatId, document: new InputFileId(GetRandomImage(StartChaosGame)), caption: FirstMessage, replyMarkup: _joinButton).Result.MessageId; + _joinMsgId = Program.Bot.SendDocumentAsync(chatId: ChatId, document: new InputFileId(GetRandomImage(StartChaosGame)), caption: FirstMessage, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId).Result.MessageId; #else - _joinMsgId = Program.Bot.SendTextMessageAsync(chatId: chatid, text: $"\u200C{FirstMessage.FormatHTML()}", replyMarkup: _joinButton, parseMode: ParseMode.Html).Result.MessageId; + _joinMsgId = Program.Bot.SendTextMessageAsync(chatId: chatid, text: $"\u200C{FirstMessage.FormatHTML()}", replyMarkup: _joinButton, parseMode: ParseMode.Html, messageThreadId: DbGroup.GroupTopicId).Result.MessageId; #endif break; @@ -241,9 +241,9 @@ public Werewolf(long chatid, User u, string chatGroup, GameMode gameMode) default: FirstMessage = GetLocaleString("PlayerStartedGame", u.FirstName); #if RELEASE - _joinMsgId = Program.Bot.SendDocumentAsync(chatId: ChatId, document: new InputFileId(GetRandomImage(StartGame)), caption: FirstMessage, replyMarkup: _joinButton).Result.MessageId; + _joinMsgId = Program.Bot.SendDocumentAsync(chatId: ChatId, document: new InputFileId(GetRandomImage(StartGame)), caption: FirstMessage, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId).Result.MessageId; #else - _joinMsgId = Program.Bot.SendTextMessageAsync(chatId: chatid, text: $"\u200C{FirstMessage.FormatHTML()}", replyMarkup: _joinButton, parseMode: ParseMode.Html).Result.MessageId; + _joinMsgId = Program.Bot.SendTextMessageAsync(chatId: chatid, text: $"\u200C{FirstMessage.FormatHTML()}", replyMarkup: _joinButton, parseMode: ParseMode.Html, messageThreadId: DbGroup.GroupTopicId).Result.MessageId; #endif break; } @@ -469,7 +469,7 @@ private void GameTimer() if (i == Settings.GameJoinTime - s) { var str = s == 60 ? GetLocaleString("MinuteLeftToJoin") : GetLocaleString("SecondsLeftToJoin", s.ToString().ToBold()); - r = Program.Bot.SendTextMessageAsync(chatId: ChatId, text: str, parseMode: ParseMode.Html, replyMarkup: _joinButton).Result; + r = Program.Bot.SendTextMessageAsync(chatId: ChatId, text: str, parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId).Result; break; } } @@ -485,7 +485,7 @@ private void GameTimer() _secondsToAdd > 0 ? "SecondsAdded" : "SecondsRemoved", Math.Abs(_secondsToAdd).ToString().ToBold(), TimeSpan.FromSeconds(Settings.GameJoinTime - i).ToString(@"mm\:ss").ToBold() - ), parseMode: ParseMode.Html, replyMarkup: _joinButton + ), parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId ).Result; _secondsToAdd = 0; @@ -1162,28 +1162,32 @@ public void HandleReply(CallbackQuery query) } } - private Task Send(string message, long id = 0, bool clearKeyboard = false, InlineKeyboardMarkup menu = null, bool notify = false, bool preview = false) + private Task Send(string message, long id = 0, bool clearKeyboard = false, InlineKeyboardMarkup menu = null, bool notify = false, bool preview = false, bool isPlayerDM = false) { if (id == 0) id = ChatId; - return Program.Send(message, id, clearKeyboard, menu, game: this, notify: notify, preview: preview); + return Program.Send(message, id, clearKeyboard, menu, game: this, notify: notify, preview: preview, isPlayerDM: isPlayerDM); } private void SendGif(string text, string image, long id = 0) { //Program.MessagesSent++; + bool isPlayerDM = false; if (id == 0) id = ChatId; + else + isPlayerDM = true; + //Log.WriteLine($"{id} -> {image} {text}"); if (!String.IsNullOrWhiteSpace(image)) #if RELEASE - Program.Bot.SendDocumentAsync(chatId: id, document: new InputFileId(image), caption: text); + Program.Bot.SendDocumentAsync(chatId: id, document: new InputFileId(image), caption: text, messageThreadId: DbGroup.GroupTopicId); #else - Send($"\u200C{text}", id, preview: true); + Send($"\u200C{text}", id, preview: true, isPlayerDM: isPlayerDM ); #endif else - Send(text, id, preview: false); + Send(text, id, preview: false, isPlayerDM: isPlayerDM); } private void SendWithQueue(string text, string gif = null, bool requestPM = false) @@ -1421,7 +1425,7 @@ public void OutputPlayers() LastPlayersOutput = DateTime.Now; try { - Program.Bot.SendTextMessageAsync(chatId: ChatId, text: GetLocaleString(_playerListId != 0 ? "LatestList" : "UnableToGetList"), parseMode: ParseMode.Html, replyToMessageId: _playerListId); + Program.Bot.SendTextMessageAsync(chatId: ChatId, text: GetLocaleString(_playerListId != 0 ? "LatestList" : "UnableToGetList"), parseMode: ParseMode.Html, replyToMessageId: _playerListId, messageThreadId: DbGroup.GroupTopicId); } catch { } } @@ -1433,7 +1437,7 @@ public async void ShowJoinButton() LastJoinButtonShowed = DateTime.Now; try { - var r = await Program.Bot.SendTextMessageAsync(chatId: ChatId, text: GetLocaleString("JoinByButton"), parseMode: ParseMode.Html, replyMarkup: _joinButton); + var r = await Program.Bot.SendTextMessageAsync(chatId: ChatId, text: GetLocaleString("JoinByButton"), parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId); _joinButtons.Add(r.MessageId); } catch diff --git a/werewolf.sql b/werewolf.sql index c5a350413..aedefa5e4 100644 --- a/werewolf.sql +++ b/werewolf.sql @@ -381,6 +381,7 @@ CREATE TABLE [dbo].[Group]( [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](max) NOT NULL, [GroupId] [bigint] NOT NULL, + [GroupTopicId] [int] NULL, [Preferred] [bit] NULL, [Language] [nvarchar](max) NULL, [DisableNotification] [bit] NULL, From 3f701747d61dc871cb1227adf10381e1cc965fcf Mon Sep 17 00:00:00 2001 From: KMohZaid <68484509+KMohZaid@users.noreply.github.com> Date: Wed, 25 Jun 2025 22:01:45 -0700 Subject: [PATCH 2/2] resolves requested changes and improve --- .../Database/WerewolfModel.Designer.cs | 2 +- Werewolf for Telegram/Languages/English.xml | 14 +++ .../Werewolf Control/Attributes/Command.cs | 2 +- .../Commands/AdminCommands.cs | 110 +++++------------- .../Handlers/UpdateHandler.cs | 63 +++++----- .../Werewolf Control/Helpers/Bot.cs | 25 ++-- .../Werewolf Node/Program.cs | 15 +-- .../Werewolf Node/Werewolf.cs | 33 +++--- 8 files changed, 110 insertions(+), 154 deletions(-) diff --git a/Werewolf for Telegram/Database/WerewolfModel.Designer.cs b/Werewolf for Telegram/Database/WerewolfModel.Designer.cs index 6b6a0f4be..31386a04d 100644 --- a/Werewolf for Telegram/Database/WerewolfModel.Designer.cs +++ b/Werewolf for Telegram/Database/WerewolfModel.Designer.cs @@ -1,4 +1,4 @@ -// T4 code generation is enabled for model 'C:\Users\waifu\Desktop\Werewolf\Werewolf for Telegram\Database\WerewolfModel.edmx'. +// T4 code generation is enabled for model 'C:\Users\parab\Source\Repos\Werewolf\Werewolf for Telegram\Database\WerewolfModel.edmx'. // To enable legacy code generation, change the value of the 'Code Generation Strategy' designer // property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model // is open in the designer. diff --git a/Werewolf for Telegram/Languages/English.xml b/Werewolf for Telegram/Languages/English.xml index c9a49f611..15532fe17 100644 --- a/Werewolf for Telegram/Languages/English.xml +++ b/Werewolf for Telegram/Languages/English.xml @@ -2269,5 +2269,19 @@ As you were preparing your kerosene and lighter, a gust of wind blew in from the doorway, causing you to freeze 🐺☃️. The snow wolf was here! But if you know one thing, it is that ice can be beaten by fire. Standing right next to one of the numerous candles in your house, you feel how its heat thaws you, allowing you to move. Nothing can stop your fire! 🔥 + + + ❌ You must run this command inside a topic/thread (not the general topic). + + + ✅ Group topic has been set successfully. + + + 🗑️ Group topic has been unset. + + + + This command can only be used in the configured topic/thread. (Topic id : {0}) + diff --git a/Werewolf for Telegram/Werewolf Control/Attributes/Command.cs b/Werewolf for Telegram/Werewolf Control/Attributes/Command.cs index dfb91e115..1718bb350 100644 --- a/Werewolf for Telegram/Werewolf Control/Attributes/Command.cs +++ b/Werewolf for Telegram/Werewolf Control/Attributes/Command.cs @@ -48,6 +48,6 @@ public class Command : Attribute /// /// Allow commands to be run outside configured topic in group. /// - public bool AllowOutsideConfiguredTopic {get; set; } = false; + public bool AllowOutsideConfiguredTopic { get; set; } = false; } } diff --git a/Werewolf for Telegram/Werewolf Control/Commands/AdminCommands.cs b/Werewolf for Telegram/Werewolf Control/Commands/AdminCommands.cs index 0358843f1..a91698308 100644 --- a/Werewolf for Telegram/Werewolf Control/Commands/AdminCommands.cs +++ b/Werewolf for Telegram/Werewolf Control/Commands/AdminCommands.cs @@ -23,111 +23,57 @@ namespace Werewolf_Control { public static partial class Commands { - [Attributes.Command(Trigger = "setgrouptopic", GroupAdminOnly = true, InGroupOnly = true)] - public static void SetGroupTopic(Update update, string[] args) + [Attributes.Command(Trigger = "settopic", GroupAdminOnly = true, InGroupOnly = true)] + public static void SetTopic(Update update, string[] args) { long chatId = update.Message.Chat.Id; int? topicId = update.Message.MessageThreadId; - int? currentTopicId; - - using (var db = new WWContext()) + // TODO: allow general topic? some group may use general topic for game. + if (topicId == null) { - currentTopicId = db.Groups - .Where(g => g.GroupId == chatId) - .Select(g => g.GroupTopicId) - .FirstOrDefault(); + Bot.Send(GetLocaleString("SetTopicCmdNotInGeneral",GetLanguage(chatId)).ToBold(), chatId, messageThreadId: 0); + return; } - bool inGeneralTopic = topicId == null || topicId == 0; - - string infoText = $"Current Topic ID: `{(currentTopicId.HasValue ? currentTopicId.Value.ToString() : "None")}`\n\n"; - - if (inGeneralTopic) - { - infoText += "This message is in the general topic (no thread ID).\n" + - "You cannot set this as the group topic. Only specific threads can be set."; - } - else + using (var db = new WWContext()) { - infoText += $"> You are currently inside topic ID: `{topicId}`.\n" + - "Choose what you'd like to do:"; - } - - var buttons = new List(); + var group = db.Groups.FirstOrDefault(g => g.GroupId == chatId); + if (group == null) + { + group = MakeDefaultGroup(chatId, update.Message.Chat.Title, "settopic_cmd"); + db.Groups.Add(group); + } - if (!inGeneralTopic) - { - buttons.Add(new[]{ - InlineKeyboardButton.WithCallbackData("Set Topic", "setgrouptopic_cmd|set"), - }); + group.GroupTopicId = topicId; + Helpers.Bot.ChatTopicIdCache[chatId] = topicId; + db.SaveChanges(); } - - buttons.Add(new[]{ - InlineKeyboardButton.WithCallbackData("Unset Topic", "setgrouptopic_cmd|unset") - }); - - - var inlineKeyboard = new InlineKeyboardMarkup(buttons); - - Bot.Send( - infoText, - chatId, - customMenu: inlineKeyboard, - parseMode: ParseMode.Markdown, - messageThreadId: topicId, - forceTopic: false // TODO: use this param in other admin cmd, cmd which admin are allowed to use anywhere - ); + + Bot.Send(GetLocaleString("SetTopicSuccess",GetLanguage(chatId)).ToBold(), chatId, messageThreadId: topicId.Value); } - internal static void SetGroupTopicCallback(CallbackQuery query) + [Attributes.Command(Trigger = "remtopic", GroupAdminOnly = true, InGroupOnly = true)] + public static void RemoveTopic(Update update, string[] args) { - var chatId = query.Message.Chat.Id; - var topicId = query.Message.MessageThreadId; + long chatId = update.Message.Chat.Id; + int? topicId = update.Message.MessageThreadId; using (var db = new WWContext()) { var group = db.Groups.FirstOrDefault(g => g.GroupId == chatId); if (group == null) { - group = MakeDefaultGroup(chatId, query.Message.Chat.Title, "setgrouptopic_callback"); + group = MakeDefaultGroup(chatId, update.Message.Chat.Title, "remtopic_cmd"); db.Groups.Add(group); } - string[] args = query.Data.Split('|'); - string action = args.Length > 1 ? args[1] : ""; - - switch (action) - { - case "set": - if (topicId == null) - { - Bot.ReplyToCallback(query, "This must be used inside a topic/thread."); - return; - } - - group.GroupTopicId = topicId; - db.SaveChanges(); - - Bot.Api.EditMessageTextAsync(chatId, query.Message.MessageId, - "Group topic has been set successfully."); - break; - - case "unset": - group.GroupTopicId = null; - db.SaveChanges(); - - Bot.Api.EditMessageTextAsync(chatId, query.Message.MessageId, - "Group topic has been unset."); - break; - - default: - Bot.ReplyToCallback(query, "Invalid topic action."); - break; - } - - Bot.Api.AnswerCallbackQueryAsync(query.Id); + group.GroupTopicId = null; + Helpers.Bot.ChatTopicIdCache[chatId] = null; + db.SaveChanges(); } + + Bot.Send(GetLocaleString("SetTopicSuccess", GetLanguage(chatId)).ToBold(), chatId, messageThreadId: topicId ?? 0); } diff --git a/Werewolf for Telegram/Werewolf Control/Handlers/UpdateHandler.cs b/Werewolf for Telegram/Werewolf Control/Handlers/UpdateHandler.cs index 8ca805da0..8506146c0 100644 --- a/Werewolf for Telegram/Werewolf Control/Handlers/UpdateHandler.cs +++ b/Werewolf for Telegram/Werewolf Control/Handlers/UpdateHandler.cs @@ -439,25 +439,27 @@ internal static void HandleUpdate(Update update) return; } // If command is not allow outside topic, or it is not admin / dev command. Check topic id and restrict to topic. - // Kmoh COOKED : (Oops... i forgot another thing that i had to write down. It was in my mind a second ago...) - if (!(command.AllowOutsideConfiguredTopic || command.GlobalAdminOnly || command.LangAdminOnly || command.GroupAdminOnly || command.DevOnly) ) + if (!(command.AllowOutsideConfiguredTopic || command.GlobalAdminOnly || command.LangAdminOnly || command.DevOnly) ) { // Only apply this restriction in groups (topics are only meaningful in groups) if (update.Message.Chat.Type == ChatType.Group || update.Message.Chat.Type == ChatType.Supergroup) { int? currentTopicId; - using (var db = new WWContext()) - { - currentTopicId = db.Groups - .Where(g => g.GroupId == id) - .Select(g => g.GroupTopicId) - .FirstOrDefault(); - } + if (Bot.ChatTopicIdCache.ContainsKey(id)) + currentTopicId = Bot.ChatTopicIdCache[id]; + else + using (var db = new WWContext()) + { + currentTopicId = db.Groups + .Where(g => g.GroupId == id) + .Select(g => g.GroupTopicId) + .FirstOrDefault(); + } // If a specific topic is set for the group and this message is NOT in that topic - if (currentTopicId == null || currentTopicId != update.Message.MessageThreadId) + if (currentTopicId != update.Message.MessageThreadId) { - Bot.Send($"This command can only be used in the configured topic/thread. (Topic id : {currentTopicId})", id,messageThreadId: update.Message.MessageThreadId, forceTopic : false); + Bot.Send(GetLocaleString("MustRunInConfiguredTopic", GetLanguage(id), currentTopicId), id, messageThreadId: update.Message.MessageThreadId ?? 0); return; } } @@ -816,12 +818,6 @@ internal static void HandleCallback(CallbackQuery query) return; } - if (args[0] == "setgrouptopic_cmd") - { - Commands.SetGroupTopicCallback(query); - return; - } - //first off, if it's a game, send it to the node. if (args[0] == "vote") { @@ -855,27 +851,30 @@ internal static void HandleCallback(CallbackQuery query) var id = query.From.Id; Send($"Sending gifs for {pid}", id); Thread.Sleep(1000); - // INFO: dumping gifs, no need of topic send (this is mostly done in pm...) - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.CultWins), caption: "Cult Wins"); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.LoversWin), caption: "Lovers Win"); + // TODO: make Send() work with document also + + int? topicId = DB.Groups.FirstOrDefault(g => g.Id == id).GroupTopicId; + + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.CultWins), caption: "Cult Wins", messageThreadId: topicId); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.LoversWin), caption: "Lovers Win", messageThreadId: topicId); Thread.Sleep(250); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.NoWinner), caption: "No Winner"); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.SerialKillerWins), caption: "SK Wins"); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.NoWinner), caption: "No Winner", messageThreadId: topicId); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.SerialKillerWins), caption: "SK Wins", messageThreadId: topicId); Thread.Sleep(250); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.StartChaosGame), caption: "Chaos Start"); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.StartGame), caption: "Normal Start"); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.StartChaosGame), caption: "Chaos Start", messageThreadId: topicId); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.StartGame), caption: "Normal Start", messageThreadId: topicId); Thread.Sleep(250); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.TannerWin), caption: "Tanner Wins"); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.VillagerDieImage), caption: "Villager Eaten"); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.TannerWin), caption: "Tanner Wins", messageThreadId: topicId); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.VillagerDieImage), caption: "Villager Eaten", messageThreadId: topicId); Thread.Sleep(250); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.VillagersWin), caption: "Village Wins"); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.WolfWin), caption: "Single Wolf Wins"); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.VillagersWin), caption: "Village Wins", messageThreadId: topicId); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.WolfWin), caption: "Single Wolf Wins", messageThreadId: topicId); Thread.Sleep(250); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.WolvesWin), caption: "Wolf new InputFileId(pack.Wins)"); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.SKKilled), caption: "SK Killed"); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.WolvesWin), caption: "Wolf new InputFileId(pack.Wins)", messageThreadId: topicId); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.SKKilled), caption: "SK Killed", messageThreadId: topicId); Thread.Sleep(250); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.ArsonistWins), caption: "Arsonist Wins"); - Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.BurnToDeath), caption: "Arsonist Burnt"); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.ArsonistWins), caption: "Arsonist Wins", messageThreadId: topicId); + Bot.Api.SendDocumentAsync(chatId: id, document: new InputFileId(pack.BurnToDeath), caption: "Arsonist Burnt", messageThreadId: topicId); Thread.Sleep(500); var msg = $"Approval Status: "; switch (pack.Approved) diff --git a/Werewolf for Telegram/Werewolf Control/Helpers/Bot.cs b/Werewolf for Telegram/Werewolf Control/Helpers/Bot.cs index 264feeab7..29e8732b6 100644 --- a/Werewolf for Telegram/Werewolf Control/Helpers/Bot.cs +++ b/Werewolf for Telegram/Werewolf Control/Helpers/Bot.cs @@ -32,6 +32,7 @@ internal static class Bot public static User Me; public static DateTime StartTime = DateTime.UtcNow; public static bool Running = true; + public static Dictionary ChatTopicIdCache = new Dictionary(); public static long CommandsReceived = 0; public static long MessagesProcessed = 0; public static long MessagesReceived = 0; @@ -510,22 +511,26 @@ public static Node GetBestAvailableNode() } - internal static Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, ParseMode parseMode = ParseMode.Html, int? messageThreadId = null, bool forceTopic = true) + internal static Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, ParseMode parseMode = ParseMode.Html, int? messageThreadId = null) { //MessagesSent++; //message = message.Replace("`",@"\`"); - // Try to load GroupTopicId from the database if no thread ID is provided - if (messageThreadId == null && forceTopic) + // Try to load GroupTopicId from the database if no thread ID is provided, with caching + if (messageThreadId == null) { - using (var db = new WWContext()) - { - var group = db.Groups.FirstOrDefault(g => g.GroupId == id); - if (group?.GroupTopicId != null) + if (ChatTopicIdCache.ContainsKey(id)) + messageThreadId = ChatTopicIdCache[id]; + else + using (var db = new WWContext()) { - messageThreadId = group.GroupTopicId; - } - } + var group = db.Groups.FirstOrDefault(g => g.GroupId == id); + if (group?.GroupTopicId != null) + { + messageThreadId = group.GroupTopicId; + ChatTopicIdCache[id] = messageThreadId; + } + } } if (clearKeyboard) diff --git a/Werewolf for Telegram/Werewolf Node/Program.cs b/Werewolf for Telegram/Werewolf Node/Program.cs index 89d53773c..06391be97 100644 --- a/Werewolf for Telegram/Werewolf Node/Program.cs +++ b/Werewolf for Telegram/Werewolf Node/Program.cs @@ -380,25 +380,12 @@ public static void RemoveGame(Werewolf werewolf) } } - internal static async Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, Werewolf game = null, bool notify = false, bool preview = false, bool isPlayerDM = false) + internal static async Task Send(string message, long id, bool clearKeyboard = false, InlineKeyboardMarkup customMenu = null, Werewolf game = null, bool notify = false, bool preview = false, int? messageThreadId = null) { //MessagesSent++; //message = message.FormatHTML(); //message = message.Replace("`",@"\`"); - // Try to load GroupTopicId from the database, only if it is not player dm - int? messageThreadId = null; - if(!isPlayerDM) - using (var db = new WWContext()) - { - var group = db.Groups.FirstOrDefault(g => g.GroupId == id); - if (group?.GroupTopicId != null) - { - messageThreadId = group.GroupTopicId; - } - } - - if (clearKeyboard) { var menu = new ReplyKeyboardRemove(); diff --git a/Werewolf for Telegram/Werewolf Node/Werewolf.cs b/Werewolf for Telegram/Werewolf Node/Werewolf.cs index 783330337..3ea6cd3bd 100644 --- a/Werewolf for Telegram/Werewolf Node/Werewolf.cs +++ b/Werewolf for Telegram/Werewolf Node/Werewolf.cs @@ -23,6 +23,7 @@ namespace Werewolf_Node public class Werewolf : IDisposable { public long ChatId; + public int? TopicId; public int GameDay, GameId; private int _secondsToAdd = 0; public List Players = new List(); @@ -148,6 +149,7 @@ public Werewolf(long chatid, User u, string chatGroup, GameMode gameMode) // ignored } AllowNSFW = DbGroup.HasFlag(GroupConfig.AllowNSFW); + TopicId = DbGroup.GroupTopicId; var player = db.Players.FirstOrDefault(x => x.TelegramId == u.Id); if (player?.CustomGifSet != null) @@ -231,9 +233,9 @@ public Werewolf(long chatid, User u, string chatGroup, GameMode gameMode) case GameMode.Chaos: FirstMessage = GetLocaleString("PlayerStartedChaosGame", u.FirstName); #if RELEASE - _joinMsgId = Program.Bot.SendDocumentAsync(chatId: ChatId, document: new InputFileId(GetRandomImage(StartChaosGame)), caption: FirstMessage, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId).Result.MessageId; + _joinMsgId = Program.Bot.SendDocumentAsync(chatId: ChatId, document: new InputFileId(GetRandomImage(StartChaosGame)), caption: FirstMessage, replyMarkup: _joinButton, messageThreadId: TopicId).Result.MessageId; #else - _joinMsgId = Program.Bot.SendTextMessageAsync(chatId: chatid, text: $"\u200C{FirstMessage.FormatHTML()}", replyMarkup: _joinButton, parseMode: ParseMode.Html, messageThreadId: DbGroup.GroupTopicId).Result.MessageId; + _joinMsgId = Program.Bot.SendTextMessageAsync(chatId: chatid, text: $"\u200C{FirstMessage.FormatHTML()}", replyMarkup: _joinButton, parseMode: ParseMode.Html, messageThreadId: TopicId).Result.MessageId; #endif break; @@ -241,9 +243,9 @@ public Werewolf(long chatid, User u, string chatGroup, GameMode gameMode) default: FirstMessage = GetLocaleString("PlayerStartedGame", u.FirstName); #if RELEASE - _joinMsgId = Program.Bot.SendDocumentAsync(chatId: ChatId, document: new InputFileId(GetRandomImage(StartGame)), caption: FirstMessage, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId).Result.MessageId; + _joinMsgId = Program.Bot.SendDocumentAsync(chatId: ChatId, document: new InputFileId(GetRandomImage(StartGame)), caption: FirstMessage, replyMarkup: _joinButton, messageThreadId: TopicId).Result.MessageId; #else - _joinMsgId = Program.Bot.SendTextMessageAsync(chatId: chatid, text: $"\u200C{FirstMessage.FormatHTML()}", replyMarkup: _joinButton, parseMode: ParseMode.Html, messageThreadId: DbGroup.GroupTopicId).Result.MessageId; + _joinMsgId = Program.Bot.SendTextMessageAsync(chatId: chatid, text: $"\u200C{FirstMessage.FormatHTML()}", replyMarkup: _joinButton, parseMode: ParseMode.Html, messageThreadId: TopicId).Result.MessageId; #endif break; } @@ -259,7 +261,7 @@ public Werewolf(long chatid, User u, string chatGroup, GameMode gameMode) while (ex.InnerException != null) ex = ex.InnerException; - Program.Send("Hmm.. something went wrong, please try starting the game again...\n" + ex.Message, chatid); + Send("Hmm.. something went wrong, please try starting the game again...\n" + ex.Message, chatid); #if DEBUG Send(ex.StackTrace); #else @@ -469,7 +471,7 @@ private void GameTimer() if (i == Settings.GameJoinTime - s) { var str = s == 60 ? GetLocaleString("MinuteLeftToJoin") : GetLocaleString("SecondsLeftToJoin", s.ToString().ToBold()); - r = Program.Bot.SendTextMessageAsync(chatId: ChatId, text: str, parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId).Result; + r = Program.Bot.SendTextMessageAsync(chatId: ChatId, text: str, parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: TopicId).Result; break; } } @@ -485,7 +487,7 @@ private void GameTimer() _secondsToAdd > 0 ? "SecondsAdded" : "SecondsRemoved", Math.Abs(_secondsToAdd).ToString().ToBold(), TimeSpan.FromSeconds(Settings.GameJoinTime - i).ToString(@"mm\:ss").ToBold() - ), parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId + ), parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: TopicId ).Result; _secondsToAdd = 0; @@ -1162,11 +1164,14 @@ public void HandleReply(CallbackQuery query) } } - private Task Send(string message, long id = 0, bool clearKeyboard = false, InlineKeyboardMarkup menu = null, bool notify = false, bool preview = false, bool isPlayerDM = false) + private Task Send(string message, long id = 0, bool clearKeyboard = false, InlineKeyboardMarkup menu = null, bool notify = false, bool preview = false, bool isPlayerDM = false, int? messageThreadId = null) { if (id == 0) id = ChatId; - return Program.Send(message, id, clearKeyboard, menu, game: this, notify: notify, preview: preview, isPlayerDM: isPlayerDM); + if (messageThreadId == null) + messageThreadId = TopicId; + + return Program.Send(message, id, clearKeyboard, menu, game: this, notify: notify, preview: preview, messageThreadId: messageThreadId); } private void SendGif(string text, string image, long id = 0) @@ -1182,7 +1187,7 @@ private void SendGif(string text, string image, long id = 0) if (!String.IsNullOrWhiteSpace(image)) #if RELEASE - Program.Bot.SendDocumentAsync(chatId: id, document: new InputFileId(image), caption: text, messageThreadId: DbGroup.GroupTopicId); + Program.Bot.SendDocumentAsync(chatId: id, document: new InputFileId(image), caption: text, messageThreadId: TopicId); #else Send($"\u200C{text}", id, preview: true, isPlayerDM: isPlayerDM ); #endif @@ -1425,7 +1430,7 @@ public void OutputPlayers() LastPlayersOutput = DateTime.Now; try { - Program.Bot.SendTextMessageAsync(chatId: ChatId, text: GetLocaleString(_playerListId != 0 ? "LatestList" : "UnableToGetList"), parseMode: ParseMode.Html, replyToMessageId: _playerListId, messageThreadId: DbGroup.GroupTopicId); + Program.Bot.SendTextMessageAsync(chatId: ChatId, text: GetLocaleString(_playerListId != 0 ? "LatestList" : "UnableToGetList"), parseMode: ParseMode.Html, replyToMessageId: _playerListId, messageThreadId: TopicId); } catch { } } @@ -1437,7 +1442,7 @@ public async void ShowJoinButton() LastJoinButtonShowed = DateTime.Now; try { - var r = await Program.Bot.SendTextMessageAsync(chatId: ChatId, text: GetLocaleString("JoinByButton"), parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: DbGroup.GroupTopicId); + var r = await Program.Bot.SendTextMessageAsync(chatId: ChatId, text: GetLocaleString("JoinByButton"), parseMode: ParseMode.Html, replyMarkup: _joinButton, messageThreadId: TopicId); _joinButtons.Add(r.MessageId); } catch @@ -1630,7 +1635,7 @@ private void NotifyRoles() try { // ReSharper disable once UnusedVariable - var result = Program.Send(msg, p.Id, true).Result; + var result = Send(msg, p.Id, true).Result; } catch (AggregateException ae) { @@ -4988,7 +4993,7 @@ private void SendMenu(List choices, IPlayer to, string t try { - var result = Program.Send(text, to.Id, false, menu).Result; + var result = Send(text, to.Id, false, menu).Result; msgId = result.MessageId; } catch (AggregateException ex)