namespace Supercell.Laser.Bot.DataBase
{
using System;
using Npgsql;
using Supercell.Laser.Bot;
using Supercell.Laser.Bot.Settings;
///
/// База данных бота: подключение, создание таблиц, взаимодействие с ними.
///
/// Подключается через PostgreSQL (Npgsql) — так же, как и сам Supercell.Laser.Server
/// (см. Supercell.Laser.Server/Database/Accounts.cs). Либо к базе данных всего сервера
/// (mysql_name: "default" в config.json бота), либо к отдельной — см.
/// Configuration.ResolveMySql. Имена таблиц дополнительно префиксуются table_prefix,
/// чтобы не пересекаться с таблицами самой игры при общей базе.
///
public static class BotData
{
private static string _connectionString;
private static string _prefix;
public static void Init(Configuration.ResolvedMySql db, string tablePrefix)
{
_prefix = string.IsNullOrWhiteSpace(tablePrefix) ? "bot_" : tablePrefix;
var builder = new NpgsqlConnectionStringBuilder
{
Host = db.Host,
Port = db.Port,
Username = db.User,
Password = db.Password,
Database = db.Database,
Pooling = true,
MinPoolSize = 1,
MaxPoolSize = 20
};
_connectionString = builder.ToString();
CreateTables();
}
public static NpgsqlConnection OpenConnection()
{
var connection = new NpgsqlConnection(_connectionString);
connection.Open();
return connection;
}
private static void CreateTables()
{
using var connection = OpenConnection();
using (var cmd = new NpgsqlCommand($@"
CREATE TABLE IF NOT EXISTS ""{_prefix}kv_store"" (
""key"" VARCHAR(191) NOT NULL PRIMARY KEY,
""value"" TEXT NULL
);", connection))
{
cmd.ExecuteNonQuery();
}
using (var cmd = new NpgsqlCommand($@"
CREATE TABLE IF NOT EXISTS ""{_prefix}admin_log"" (
""id"" BIGSERIAL PRIMARY KEY,
""telegram_user_id"" BIGINT NOT NULL,
""action"" VARCHAR(64) NOT NULL,
""created_at"" TIMESTAMP NOT NULL
);", connection))
{
cmd.ExecuteNonQuery();
}
// Подробности действия (тэг игрока, баланс до/после) — чтобы по выдаче
// ресурсов оставался разбираемый след, а не только имя действия.
// ADD COLUMN IF NOT EXISTS — на уже созданных базах колонка просто добавится.
using (var cmd = new NpgsqlCommand($@"
ALTER TABLE ""{_prefix}admin_log""
ADD COLUMN IF NOT EXISTS ""details"" VARCHAR(191) NULL;", connection))
{
cmd.ExecuteNonQuery();
}
}
/// Пишет в БД, кто из админов и какое действие выполнил в панели бота.
public static void LogAdminAction(long telegramUserId, string action, string details = null)
{
try
{
using var connection = OpenConnection();
using var cmd = new NpgsqlCommand(
$"INSERT INTO \"{_prefix}admin_log\" (telegram_user_id, action, details, created_at) VALUES (@uid, @action, @details, @now);",
connection);
cmd.Parameters.AddWithValue("@uid", telegramUserId);
cmd.Parameters.AddWithValue("@action", action);
cmd.Parameters.AddWithValue("@details", (object)details ?? DBNull.Value);
cmd.Parameters.AddWithValue("@now", DateTime.UtcNow);
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
Start.Log($"не удалось записать admin_log: {e.Message}");
}
}
/// Простое key-value хранилище настроек бота в БД (на будущее, для новых фич).
public static string GetSetting(string key, string defaultValue = null)
{
using var connection = OpenConnection();
using var cmd = new NpgsqlCommand($"SELECT \"value\" FROM \"{_prefix}kv_store\" WHERE \"key\" = @key LIMIT 1;", connection);
cmd.Parameters.AddWithValue("@key", key);
object result = cmd.ExecuteScalar();
return result == null || result == DBNull.Value ? defaultValue : result.ToString();
}
public static void SetSetting(string key, string value)
{
using var connection = OpenConnection();
using var cmd = new NpgsqlCommand(
$"INSERT INTO \"{_prefix}kv_store\" (\"key\", \"value\") VALUES (@key, @value) " +
"ON CONFLICT (\"key\") DO UPDATE SET \"value\" = EXCLUDED.\"value\";", connection);
cmd.Parameters.AddWithValue("@key", key);
cmd.Parameters.AddWithValue("@value", value);
cmd.ExecuteNonQuery();
}
}
}