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}");
}
}
}