From 44d5520a7f693e2dce30d76328674c5fb56c7018 Mon Sep 17 00:00:00 2001 From: KMohZaid <68484509+KMohZaid@users.noreply.github.com> Date: Tue, 17 Jun 2025 18:28:51 -0700 Subject: [PATCH] feat(auto detects unreadable name): tell user if there name is unreadable, allow them to force join with warning --- .../Commands/GeneralCommands.cs | 44 ++++++++ .../Werewolf Control/Commands/Helpers.cs | 100 +++++++++++++++++- .../Werewolf Control/Program.cs | 5 + 3 files changed, 146 insertions(+), 3 deletions(-) diff --git a/Werewolf for Telegram/Werewolf Control/Commands/GeneralCommands.cs b/Werewolf for Telegram/Werewolf Control/Commands/GeneralCommands.cs index c2f97589..43168de2 100644 --- a/Werewolf for Telegram/Werewolf Control/Commands/GeneralCommands.cs +++ b/Werewolf for Telegram/Werewolf Control/Commands/GeneralCommands.cs @@ -239,8 +239,44 @@ public static void Start(Update u, string[] args) return; } + // check for force join + bool isForceJoin = false; + if (args[1].StartsWith("forcejoin")) + { + isForceJoin = true; + // remove "force" from starting, so below if statement work for force join also + args[1] = args[1].Substring("force".Length); + } + + // handle join command if (args[1].StartsWith("join") && args[1].Length == 48) // 4 "join" + 22 node id + 22 game id { + // check if user have validate name + bool isValid; + string msg; + (isValid, msg) = ValidatePlayerName(p.Name); + if(!isValid && !isForceJoin) + { + + + // TODO: move to string xml file for translation support + string warningMsg = "⚠️ If you join the game using the button below, you can join successfully, " + + "but you may receive a warning if your name is unreadable or invalid."; + + + string forceJoinURI = $"https://t.me/{Bot.Me.Username}/?start={"force" + args[1]}"; + + // send user messaage + Bot.Send(msg+"\n\n"+warningMsg, u.Message.Chat.Id, customMenu: new InlineKeyboardMarkup(new[] + { + new[] + { + InlineKeyboardButton.WithUrl("Force Join Game", forceJoinURI) + } + })); + return; + } + //okay, they are joining a game. string nodeid = ""; string gameid = ""; @@ -340,6 +376,14 @@ public static void Start(Update u, string[] args) } game.AddPlayer(u, gameid); + + // notify group about force join + if (isForceJoin) { + // TODO: move to strings xml for translation support + string forceJoinNotice = $"⚠️ Player with name '[{p.Name}](tg://user?id={p.TelegramId})' used force join button, their name is detected as unreadable by bot."; + + Bot.Send(forceJoinNotice, game.GroupId, parseMode: ParseMode.Markdown); + } return; } catch (AggregateException e) diff --git a/Werewolf for Telegram/Werewolf Control/Commands/Helpers.cs b/Werewolf for Telegram/Werewolf Control/Commands/Helpers.cs index 2d199537..eccebc7f 100644 --- a/Werewolf for Telegram/Werewolf Control/Commands/Helpers.cs +++ b/Werewolf for Telegram/Werewolf Control/Commands/Helpers.cs @@ -1,20 +1,21 @@ -using Shared; +using Database; +using Shared; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; -using Database; +using Telegram.Bot; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; using Werewolf_Control.Handler; using Werewolf_Control.Helpers; using Werewolf_Control.Models; -using Telegram.Bot; #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed namespace Werewolf_Control { @@ -33,6 +34,99 @@ public static partial class Commands }; #endif + internal static void TestPlayerNameValidation() + { + // Fix: enable Unicode output for fancy letters and emoji + Console.OutputEncoding = System.Text.Encoding.UTF8; + + var testCases = new List<(string Name, bool Expected, string Description)> + { + ("John", true, "Latin letters"), + ("𝕁𝕠𝕙𝕟", true, "Stylized Latin (math bold)"), + ("ABC", true, "Full-width Latin"), + ("李", false, "Single CJK (non-Latin) character"), + ("😎😎", false, "Emoji only"), + (" ", false, "Whitespace only"), + ("A", false, "Only one Latin letter"), + ("AB", true, "Two Latin letters"), + ("ㅤㅤㅤ", false, "Hangul fillers (invisible)"), + ("⌘ KMohZaid ⌘", true, "Latin letters surrounded by symbols"), + ("AswatthamA", true, "Regular Latin name"), + ("Cuenta Eliminada", true, "Spanish Latin name"), + ("ဂွေးဂျိနက်မမ", false, "Burmese script (non-Latin)"), + ("Jasmine", true, "Regular Latin name"), + ("Van", true, "Short but valid Latin name"), + ("", false, "Empty string"), + ("ℛ𝒶𝓃𝒶𝒹 ℬℯ𝓃 ℛ𝒶𝓂𝒶𝒹𝒶𝓃", true, "Fancy Unicode Latin") + }; + + foreach (var (name, expected, description) in testCases) + { + bool actual; + string msg; + (actual, msg) = Commands.ValidatePlayerName(name); + + Console.WriteLine($"Name: \"{name}\""); + Console.WriteLine($"Description: {description}"); + Console.WriteLine($"Message: {msg}"); + Console.WriteLine($"Expected: {(expected ? "Valid" : "Invalid")}, Actual: {(actual ? "Valid" : "Invalid")}"); + + if (actual == expected) + { + Console.WriteLine("✅ TEST PASS"); + } + else + { + Console.WriteLine("❌ TEST FAIL"); + } + + Console.WriteLine(new string('-', 50)); + } + + Console.WriteLine(new string('-', 50)); + Console.WriteLine("PRESSING ENTER WILL START ACTUAL BOT RUN, AFTER WHICH YOU WILL SEE MESSED UP BOT STATUS MESSAGE."); + Console.WriteLine(new string('-', 50)); + Console.ReadLine(); // this is to stop program from overriding output screen with running bot status message + } + + internal static (bool, string) ValidatePlayerName(string name) + { + + + // TODO: move to string xml file, for translation support + string blankMsg = "❌ Your name appears to be blank. Please set a readable name in your Telegram profile and try again."; + string noEnoughReadableChar = "❌ Your name must contain at least 2 readable letters. Please update your Telegram name."; + + if (string.IsNullOrWhiteSpace(name)) + { + return (false, blankMsg); + } + + var trimmedName = name.Trim(); + var textElements = StringInfo.GetTextElementEnumerator(trimmedName); + + int letterCount = 0; + + while (textElements.MoveNext()) + { + string element = textElements.GetTextElement(); + var category = CharUnicodeInfo.GetUnicodeCategory(element, 0); + + if (category == UnicodeCategory.UppercaseLetter || + category == UnicodeCategory.LowercaseLetter) + { + letterCount++; + } + } + + if (letterCount < 2) + { + return (false,noEnoughReadableChar); + } + + return (true,"Valid."); + } + private static Player GetDBPlayer(long id, WWContext db) { return db.Players.FirstOrDefault(x => x.TelegramId == id); diff --git a/Werewolf for Telegram/Werewolf Control/Program.cs b/Werewolf for Telegram/Werewolf Control/Program.cs index fff203a8..27f86eed 100644 --- a/Werewolf for Telegram/Werewolf Control/Program.cs +++ b/Werewolf for Telegram/Werewolf Control/Program.cs @@ -43,8 +43,13 @@ class Program internal static readonly HttpClient xsollaClient = new HttpClient(); internal const string MasterLanguage = "English.xml"; internal static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + static void Main(string[] args) { + // INFO: uncomment below method call for testing. Some names are already there for testing purpose. + //Commands.TestPlayerNameValidation(); + System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12; #if !DEBUG