using Telegram.Bot; using Telegram.Bot.Polling; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using VeloBrawl.Logic.Database; using VeloBrawl.Logic.Database.Account; using VeloBrawl.Logic.Environment.LaserListener; using VeloBrawl.Logic.Environment.LaserMessage.Laser.Node_1; using VeloBrawl.Logic.Environment.LaserMessage.Sepo.Home.Laser.Laser; namespace VeloBrawl.Bot; public static class TelegramBotService { private static TelegramBotClient _bot = null!; private static BotConfig _config = null!; public static void Start(BotConfig config) { _config = config; if (!config.Enabled || string.IsNullOrWhiteSpace(config.Token) || config.Token == "idk") { Console.WriteLine("[Bot] Telegram bot disabled or token not set."); return; } _bot = new TelegramBotClient(config.Token); var cts = new CancellationTokenSource(); _bot.StartReceiving( HandleUpdateAsync, HandleErrorAsync, new ReceiverOptions { AllowedUpdates = [UpdateType.Message] }, cts.Token ); Console.WriteLine("[Bot] Telegram bot started."); } private static async Task HandleUpdateAsync(ITelegramBotClient bot, Update update, CancellationToken ct) { if (update.Message is not { Text: { } text } msg) return; var chatId = msg.Chat.Id; var userId = msg.From?.Id ?? 0; var trimmed = text.Trim(); var parts = trimmed.Split(' ', StringSplitOptions.RemoveEmptyEntries); var rawCmd = parts[0]; var cmd = rawCmd.ToLower().TrimStart('/'); switch (cmd) { case "start": await HandleStart(chatId, parts, userId, ct); break; case "help": await HandleHelp(chatId, userId, ct); break; case "info": await HandleInfo(chatId, userId, parts, ct); break; case "switch": await HandleSwitch(chatId, userId, parts, ct); break; case "give": if (!IsAdmin(userId)) { await bot.SendMessage(chatId, "⛔ Нет доступа.", cancellationToken: ct); return; } await HandleGive(chatId, parts, ct); break; case "ban": if (!IsAdmin(userId)) { await bot.SendMessage(chatId, "⛔ Нет доступа.", cancellationToken: ct); return; } await HandleBan(chatId, parts, ban: true, ct); break; case "unban": if (!IsAdmin(userId)) { await bot.SendMessage(chatId, "⛔ Нет доступа.", cancellationToken: ct); return; } await HandleBan(chatId, parts, ban: false, ct); break; default: if (trimmed.Length == 8 && trimmed.All(c => char.IsLetterOrDigit(c))) await HandleCodeInput(chatId, userId, trimmed.ToUpper(), ct); break; } } private static async Task HandleStart(long chatId, string[] parts, long userId, CancellationToken ct) { if (parts.Length >= 2) { var code = parts[1].ToUpper(); await HandleCodeInput(chatId, userId, code, ct); return; } var linkedId = Databases.AccountDatabase.GetAccountIdByTelegramId(userId); if (linkedId.HasValue) { await _bot.SendMessage(chatId, $"👋 Привет! Аккаунт уже привязан (ID: {linkedId}).\n\n" + "/info — инфо\n/switch — другой аккаунт\n/help — команды", cancellationToken: ct); } else { await _bot.SendMessage(chatId, "👋 Привет!\n\nЗайди в игру — увидишь код привязки.\nОтправь его сюда и аккаунт привяжется.\n\n/help — все команды", cancellationToken: ct); } } private static async Task HandleCodeInput(long chatId, long userId, string code, CancellationToken ct) { var result = Databases.AccountDatabase.ConsumeCode(code); if (result == null) { await _bot.SendMessage(chatId, "❌ Код не найден или истёк.\nЗайди в игру заново — придёт новый код.", cancellationToken: ct); return; } var (accountId, isSwitchCode) = result.Value; var account = Databases.AccountDatabase.LoadAccount(accountId); var name = account?.GetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.AvatarName)?.ToString() ?? "Brawler"; if (!isSwitchCode) { var existingTg = Databases.AccountDatabase.GetTelegramIdByAccountId(accountId); if (existingTg.HasValue && existingTg.Value != 0 && existingTg.Value != userId) { await _bot.SendMessage(chatId, "❌ Этот аккаунт уже привязан к другому Telegram.", cancellationToken: ct); return; } Databases.AccountDatabase.SetTelegramId(accountId, userId); await _bot.SendMessage(chatId, $"✅ Аккаунт #{accountId} ({name}) привязан!\n\nТеперь при входе ты заходишь без кода.", cancellationToken: ct); SendLoginFailedToPlayer(accountId, "✅ Аккаунт привязан к Telegram! Заходи снова."); } else { Databases.AccountDatabase.SetTelegramId(accountId, userId); await _bot.SendMessage(chatId, $"✅ Переключение на аккаунт #{accountId} ({name}) подтверждено!\n\n" + "Очисти данные приложения и зайди снова.", cancellationToken: ct); var currentId = Databases.AccountDatabase.GetAccountIdByTelegramId(userId); if (currentId.HasValue && currentId.Value != accountId) SendLoginFailedToPlayer(currentId.Value, "🔄 Переключение аккаунта подтверждено. Очисти данные и зайди снова."); } } private static async Task HandleSwitch(long chatId, long userId, string[] parts, CancellationToken ct) { if (parts.Length < 2) { await _bot.SendMessage(chatId, "❌ Использование: /switch ", cancellationToken: ct); return; } if (!long.TryParse(parts[1], out var targetId)) { await _bot.SendMessage(chatId, "❌ Неверный accountId. Нужно число.", cancellationToken: ct); return; } var account = Databases.AccountDatabase.LoadAccount(targetId); if (account == null) { await _bot.SendMessage(chatId, "❌ Аккаунт не найден.", cancellationToken: ct); return; } var tgOfTarget = Databases.AccountDatabase.GetTelegramIdByAccountId(targetId); if (tgOfTarget.HasValue && tgOfTarget.Value != 0 && tgOfTarget.Value != userId && !IsAdmin(userId)) { await _bot.SendMessage(chatId, "❌ Этот аккаунт принадлежит другому пользователю.", cancellationToken: ct); return; } var switchCode = GenerateCode(); Databases.AccountDatabase.SetSwitchCode(targetId, switchCode); var name = account.GetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.AvatarName)?.ToString() ?? "Brawler"; await _bot.SendMessage(chatId, $"🔄 Переключение → #{targetId} ({name})\n\n" + $"Отправь боту этот код:\n{switchCode}\n\n" + "Действует 10 минут. После подтверждения — очисти данные приложения и зайди снова.", parseMode: ParseMode.Html, cancellationToken: ct); } private static async Task HandleInfo(long chatId, long userId, string[] parts, CancellationToken ct) { long? targetId = null; if (parts.Length >= 2 && IsAdmin(userId) && long.TryParse(parts[1], out var adminTarget)) targetId = adminTarget; else targetId = Databases.AccountDatabase.GetAccountIdByTelegramId(userId); if (!targetId.HasValue) { await _bot.SendMessage(chatId, "❌ Аккаунт не привязан. Зайди в игру для получения кода.", cancellationToken: ct); return; } var account = Databases.AccountDatabase.LoadAccount(targetId.Value); if (account == null) { await _bot.SendMessage(chatId, "❌ Аккаунт не найден.", cancellationToken: ct); return; } var name = account.GetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.AvatarName)?.ToString() ?? "N/A"; var trophies = account.GetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.Trophies)?.ToString() ?? "0"; var maxTrophies = account.GetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.MaxTrophies)?.ToString() ?? "0"; var intX = account.GetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.IntValueEntryX) as Dictionary; int coins = intX?.TryGetValue(1, out var c) == true ? c.Value : 0; int gems = intX?.TryGetValue(16, out var g) == true ? g.Value : 0; int starPoints = intX?.TryGetValue(17, out var s) == true ? s.Value : 0; int tokens = intX?.TryGetValue(7, out var t) == true ? t.Value : 0; var online = IdentifierListener.GetV2LogicGameListenerByAccountId(targetId.Value) ? "🟢 В сети" : "⚫ Оффлайн"; await _bot.SendMessage(chatId, $"👤 Аккаунт #{targetId}\n" + $"📛 {name}\n" + $"🔌 {online}\n" + $"🏆 {trophies} (макс: {maxTrophies})\n" + $"💰 Монеты: {coins}\n" + $"💎 Гемы: {gems}\n" + $"⭐ Star Points: {starPoints}\n" + $"🎟 Токены: {tokens}", cancellationToken: ct); } private static async Task HandleGive(long chatId, string[] parts, CancellationToken ct) { if (parts.Length < 4) { await _bot.SendMessage(chatId, "❌ /give <тип> <кол-во>\nТипы: coins, gems, starpoints, tokens", cancellationToken: ct); return; } if (!long.TryParse(parts[1], out var accountId) || !int.TryParse(parts[3], out var amount) || amount <= 0) { await _bot.SendMessage(chatId, "❌ Неверный формат.", cancellationToken: ct); return; } var account = Databases.AccountDatabase.LoadAccount(accountId); if (account == null) { await _bot.SendMessage(chatId, "❌ Аккаунт не найден.", cancellationToken: ct); return; } int resourceKey = parts[2].ToLower() switch { "coins" => 1, "gems" => 16, "starpoints" => 17, "tokens" => 7, _ => -1 }; if (resourceKey < 0) { await _bot.SendMessage(chatId, "❌ Неизвестный тип: coins / gems / starpoints / tokens", cancellationToken: ct); return; } var intX = account.GetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.IntValueEntryX) as Dictionary ?? new Dictionary(); int current = intX.TryGetValue(resourceKey, out var existing) ? existing.Value : 0; int newVal = current + amount; intX[resourceKey] = new IntValueEntry(resourceKey, newVal); account.SetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.IntValueEntryX, intX); Databases.AccountDatabase.OnAssistByAccountInFile(account, true, accountId); await _bot.SendMessage(chatId, $"✅ +{amount} {parts[2]} → #{accountId}\nБаланс: {newVal}", cancellationToken: ct); SendLoginFailedToPlayer(accountId, $"🎁 Вам выдано {amount} {parts[2]}! Зайдите снова."); } private static async Task HandleBan(long chatId, string[] parts, bool ban, CancellationToken ct) { if (parts.Length < 2 || !long.TryParse(parts[1], out var accountId)) { await _bot.SendMessage(chatId, $"❌ /{(ban ? "ban" : "unban")} ", cancellationToken: ct); return; } var account = Databases.AccountDatabase.LoadAccount(accountId); if (account == null) { await _bot.SendMessage(chatId, "❌ Аккаунт не найден.", cancellationToken: ct); return; } account.SetFieldValueByAccountStructureParameterFromAccountModel(AccountStructure.UserIsAuthed, !ban); Databases.AccountDatabase.OnAssistByAccountInFile(account, true, accountId); await _bot.SendMessage(chatId, $"{(ban ? "🔨 Забанен" : "✅ Разбанен")} аккаунт #{accountId}.", cancellationToken: ct); if (ban) SendLoginFailedToPlayer(accountId, "🔨 Ваш аккаунт заблокирован."); } private static async Task HandleHelp(long chatId, long userId, CancellationToken ct) { var text = "📋 Команды:\n\n" + "/start — приветствие\n" + "/info — инфо о своём аккаунте\n" + "/switch — зайти на другой аккаунт\n" + "<8-значный код> — привязать / подтвердить переключение\n" + "/help — эта справка"; if (IsAdmin(userId)) { text += "\n\n🔧 Админ:\n" + "/give <тип> <кол-во>\n типы: coins, gems, starpoints, tokens\n" + "/ban \n/unban \n/info "; } await _bot.SendMessage(chatId, text, cancellationToken: ct); } public static void SendLoginFailedToPlayer(long accountId, string message) { try { if (!IdentifierListener.GetV2LogicGameListenerByAccountId(accountId)) return; var listener = IdentifierListener.GetLogicGameListenerByAccountId(accountId); var failMsg = new LoginFailedMessage(); failMsg.SetErrorCode(7); failMsg.SetReason(message); failMsg.SetContentUrl(""); failMsg.SetRedirectDomain(""); failMsg.SetUpdateUrl(""); failMsg.SetRemoveResourceFingerprintData(""); failMsg.SetSecondsUntilMaintenanceEnd(-1); failMsg.SetShowContactSupportForBan(false); listener.Send(failMsg); IdentifierListener.RemoveLogicGameListenerByAccountId(accountId); } catch { } } private static bool IsAdmin(long userId) => _config.AdminIds.Contains(userId); private static string GenerateCode() { const string chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; var rng = new Random(); return new string(Enumerable.Range(0, 8).Select(_ => chars[rng.Next(chars.Length)]).ToArray()); } private static Task HandleErrorAsync(ITelegramBotClient bot, Exception ex, CancellationToken ct) { Console.WriteLine("[Bot] Error: " + ex.Message); return Task.CompletedTask; } }