namespace Supercell.Laser.Bot { using System; using System.Diagnostics; using Supercell.Laser.Bot.Bot; using Supercell.Laser.Bot.DataBase; using Supercell.Laser.Bot.Settings; /// /// Данные, которые Supercell.Laser.Server передаёт боту при старте (см. Program.cs сервера), /// когда бот включён (bot_enabled: true в config.json сервера) и "прицеплен" к серверу. /// Благодаря этому поля mysql_user / mysql_password / mysql_name со значением "default" /// в Supercell.Laser.Bot/config.json берут значения из уже загруженного Configuration.Instance /// сервера, а не читают его config.json заново с диска. /// public class ServerLink { public string DatabaseUsername; public string DatabasePassword; public string DatabaseName; /// Возвращает текущее количество игроков онлайн (обычно Sessions.Count). public Func GetOnlineCount; /// Данные игрока по id аккаунта. null — аккаунта нет в базе. public Func GetPlayer; /// /// Меняет кристаллы игрока на delta (отрицательная delta — забрать) и возвращает /// до/после. null — аккаунта нет в базе. /// public Func ChangeGems; /// То же для золота (монет). public Func ChangeGold; } /// /// Срез данных игрока для бота. Отдельный простой тип, потому что бот не видит /// ни Account, ни ClientAvatar — Supercell.Laser.Server ссылается на бота, а не наоборот. /// public class PlayerInfo { public long AccountId; /// Тэг без '#'. public string Tag; public string Name; /// Кристаллы (ClientAvatar.Diamonds). public int Gems; /// Золото / монеты (ClientAvatar.Gold). public int Gold; } /// Результат изменения ресурса: баланс до, после и сколько реально прошло. public class ResourceChange { /// Игрок уже с новым балансом. public PlayerInfo Player; public int OldValue; public int NewValue; /// /// Сколько реально начислено или списано. Отличается от запрошенного, если /// сервер обрезал операцию (нельзя уйти ниже нуля или выше int.MaxValue). /// public int Applied; } /// /// Старт бота, инициализация бота и так далее. /// Вызывается из Supercell.Laser.Server.Program.Main, если bot_enabled: true в config.json сервера. /// public static class Start { public static ServerLink Server { get; private set; } public static DateTime ProcessStartTime { get; private set; } public static void Run(ServerLink server = null) { Server = server; ProcessStartTime = Process.GetCurrentProcess().StartTime; try { Configuration.Instance = Configuration.LoadFromFile(Configuration.FindOwnConfigPath()); } catch (Exception e) { Log($"не удалось загрузить config.json: {e.Message}"); return; } if (string.IsNullOrWhiteSpace(Configuration.Instance.BotToken) || Configuration.Instance.BotToken == "PUT_YOUR_TELEGRAM_BOT_TOKEN_HERE") { Log("bot_token не задан в config.json — бот не запущен."); return; } try { Configuration.ResolvedMySql mysql = Configuration.Instance.ResolveMySql(server); BotData.Init(mysql, Configuration.Instance.TablePrefix); } catch (Exception e) { Log($"не удалось подключиться к базе данных: {e.Message}"); return; } try { TelegramBot.Init(Configuration.Instance.BotToken); } catch (Exception e) { Log($"не удалось запустить телеграм-бота: {e.Message}"); return; } Log(Configuration.Instance.HasAdmins ? "бот запущен." : "бот запущен, но telegram_admins пуст — панель админа никому не будет видна."); } public static int GetOnlineCount() { try { return Server?.GetOnlineCount != null ? Server.GetOnlineCount() : 0; } catch { return 0; } } /// /// Игрок по id аккаунта. null — аккаунта нет в базе (или бот запущен без сервера, /// и спрашивать некого). /// public static PlayerInfo GetPlayer(long accountId) { return Invoke(Server?.GetPlayer, accountId, "GetPlayer"); } /// Начислить (delta > 0) или списать (delta < 0) кристаллы. public static ResourceChange ChangeGems(long accountId, int delta) { return Invoke(Server?.ChangeGems, accountId, delta, "ChangeGems"); } /// Начислить или списать золото (монеты). public static ResourceChange ChangeGold(long accountId, int delta) { return Invoke(Server?.ChangeGold, accountId, delta, "ChangeGold"); } // Обёртки ниже гасят исключения со стороны сервера: упавший запрос к базе // не должен ронять обработку апдейта телеграма — бот просто ответит, // что игрок не найден. private static TResult Invoke(Func link, TArg arg, string name) where TResult : class { if (link == null) return null; try { return link(arg); } catch (Exception e) { Log($"{name} завершился ошибкой: {e.Message}"); return null; } } private static TResult Invoke(Func link, TArg1 arg1, TArg2 arg2, string name) where TResult : class { if (link == null) return null; try { return link(arg1, arg2); } catch (Exception e) { Log($"{name} завершился ошибкой: {e.Message}"); return null; } } internal static void Log(string message) { Console.WriteLine($"[Supercell.Laser.Bot] {message}"); } } }