From a457ea1a19d0938b925ae49ae114f032d2f1c3fc Mon Sep 17 00:00:00 2001 From: Slavasil Date: Sun, 20 Apr 2025 22:22:07 +0300 Subject: [PATCH] TelegramBot: add method to work with the database --- SimpleTGBot/Config.cs | 3 +- SimpleTGBot/Interactions.cs | 34 +++++++- SimpleTGBot/MemeGen/Types.cs | 2 +- SimpleTGBot/Program.cs | 27 ++++++- SimpleTGBot/SimpleTGBot.csproj | 1 + SimpleTGBot/TelegramBot.cs | 144 ++++++++++++++++++++++++++++++++- SimpleTGBot/UserPreset.cs | 23 ++++++ 7 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 SimpleTGBot/UserPreset.cs diff --git a/SimpleTGBot/Config.cs b/SimpleTGBot/Config.cs index 64f6758..812949a 100644 --- a/SimpleTGBot/Config.cs +++ b/SimpleTGBot/Config.cs @@ -5,7 +5,8 @@ namespace SimpleTGBot; internal class Config { public const string DEFAULT_BOT_TOKEN_FILENAME = "telegram_token.txt"; - + public const string DEFAULT_DATABASE_FILENAME = "demotivatorbot.db"; + public static string? TryReadBotTokenFile() { try diff --git a/SimpleTGBot/Interactions.cs b/SimpleTGBot/Interactions.cs index a643120..b440179 100644 --- a/SimpleTGBot/Interactions.cs +++ b/SimpleTGBot/Interactions.cs @@ -1,4 +1,5 @@ -using Telegram.Bot.Types.ReplyMarkups; +using System.Text; +using Telegram.Bot.Types.ReplyMarkups; namespace SimpleTGBot; @@ -28,6 +29,8 @@ internal static class Interactions public static readonly IReplyMarkup resultActionReplyMarkup = new ReplyKeyboardMarkup([new KeyboardButton(doneButtonText)]); public static readonly IReplyMarkup settingsReplyMarkup = new ReplyKeyboardMarkup([[new KeyboardButton(gotoPresetsButtonText)], [new KeyboardButton(backButtonText)]]); + static readonly string[] digitEmojis = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"]; + public static bool IsStartCommand(string message) { return message.Split(' ').FirstOrDefault() == "/start"; @@ -55,4 +58,33 @@ internal static class Interactions { return message == backButtonText; } + + public static string MakePresetListMessage(string[] presetNames) + { + StringBuilder msg = new StringBuilder(); + msg.Append("Твои сохранённые стили:\n"); + for (int i = 0; i < presetNames.Length; i++) + { + msg.Append(DigitsToEmoji((i + 1).ToString())); + msg.Append(' '); + msg.Append(presetNames[i]); + msg.Append('\n'); + } + if (presetNames.Length == 0) + { + msg.Append("<пусто>"); + } + msg.Append("\n"); + return msg.ToString(); + } + + public static string DigitsToEmoji(string s) + { + StringBuilder sb = new StringBuilder(s.Length * 4); + foreach (char digit in s) + { + sb.Append(digitEmojis[digit - '0']); + } + return sb.ToString(); + } } diff --git a/SimpleTGBot/MemeGen/Types.cs b/SimpleTGBot/MemeGen/Types.cs index 5cf6e27..afb0421 100644 --- a/SimpleTGBot/MemeGen/Types.cs +++ b/SimpleTGBot/MemeGen/Types.cs @@ -13,7 +13,7 @@ public record DemotivatorStyle public float Padding { get; set; } public float OuterMargin { get; set; } public float CaptionSpacing { get; set; } - public float Wtf1 { get; set; } + public float AdditionalTextWidth { get; set; } public Color OutlineColor { get; set; } public Color TitleColor { get; set; } public Color SubtitleColor { get; set; } diff --git a/SimpleTGBot/Program.cs b/SimpleTGBot/Program.cs index 02c40f7..71c6b3f 100644 --- a/SimpleTGBot/Program.cs +++ b/SimpleTGBot/Program.cs @@ -1,4 +1,5 @@ using System.Text; +using Microsoft.Data.Sqlite; using SimpleTGBot.Logging; namespace SimpleTGBot; @@ -16,6 +17,11 @@ public static class Program // Православная кодировка Console.OutputEncoding = Encoding.UTF8; + using SqliteConnection db = new("Data Source=" + Config.DEFAULT_DATABASE_FILENAME); + db.Open(); + + PrepareDatabaseTables(db); + string? botToken = Config.TryReadBotTokenFile(); if (botToken == null) @@ -33,10 +39,29 @@ public static class Program using (Logger logger = new Logger()) { logger.Sinks.Add(new StdoutSink()); - TelegramBot telegramBot = new TelegramBot(botToken, logger); + TelegramBot telegramBot = new TelegramBot(botToken, logger, db); await telegramBot.Run(); } + db.Close(); + return 0; } + + static void PrepareDatabaseTables(SqliteConnection db) + { + var cmd = db.CreateCommand(); + cmd.CommandText = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, selected_preset INTEGER)"; + cmd.ExecuteNonQuery(); + + cmd = db.CreateCommand(); + cmd.CommandText = @"CREATE TABLE IF NOT EXISTS user_presets ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + name TEXT, + outline_color INTEGER, + title_color INTEGER, + subtitle_color INTEGER)"; + cmd.ExecuteNonQuery(); + } } diff --git a/SimpleTGBot/SimpleTGBot.csproj b/SimpleTGBot/SimpleTGBot.csproj index ed4a1e7..6f82ec6 100644 --- a/SimpleTGBot/SimpleTGBot.csproj +++ b/SimpleTGBot/SimpleTGBot.csproj @@ -8,6 +8,7 @@ + diff --git a/SimpleTGBot/TelegramBot.cs b/SimpleTGBot/TelegramBot.cs index 196f25e..7f2fd96 100644 --- a/SimpleTGBot/TelegramBot.cs +++ b/SimpleTGBot/TelegramBot.cs @@ -1,4 +1,6 @@ -using SimpleTGBot.Logging; +using System.Drawing; +using Microsoft.Data.Sqlite; +using SimpleTGBot.Logging; using SimpleTGBot.MemeGen; using Telegram.Bot; using Telegram.Bot.Exceptions; @@ -12,17 +14,19 @@ internal class TelegramBot { private string token; private Logger logger; + private SqliteConnection database; private Dictionary dialogs; private TempStorage temp; private HttpClient httpClient; - public TelegramBot(string token, Logger logger) + public TelegramBot(string token, Logger logger, SqliteConnection db) { this.token = token; this.logger = logger; dialogs = new Dictionary(); temp = new TempStorage(); httpClient = new HttpClient(); + database = db; } /// @@ -72,12 +76,17 @@ internal class TelegramBot { if (update.Message is not { } message) return; if (message.Chat.Type != ChatType.Private) return; + if (message.From is not { } user) return; DialogData dialogData; if (!dialogs.ContainsKey(message.Chat.Id)) { dialogData = new DialogData() { state = DialogState.Initial }; dialogs[message.Chat.Id] = dialogData; + if (!await IsUserInDatabase(user)) + { + await AddUserToDatabase(user); + } } else { dialogData = dialogs[message.Chat.Id]; @@ -203,7 +212,7 @@ internal class TelegramBot { replied = true; dialogData.state = DialogState.ViewingPresets; - await botClient.SendTextMessageAsync(message.Chat.Id, "<заглушка>", replyMarkup: Interactions.backButtonReplyMarkup); + await botClient.SendTextMessageAsync(message.Chat.Id, Interactions.MakePresetListMessage((await GetUserPresets(user)).Select(preset => preset.Name).ToArray()), replyMarkup: Interactions.backButtonReplyMarkup); } else if (messageText == Interactions.backButtonText) { @@ -330,6 +339,135 @@ internal class TelegramBot temp.deleteTemporaryFile(dialogData.inputPictureFilename); } + async Task AddUserToDatabase(User u) + { + var cmd = database.CreateCommand(); + cmd.CommandText = "INSERT INTO users (id) VALUES ($1)"; + cmd.Parameters.AddWithValue("$1", u.Id); + await cmd.ExecuteNonQueryAsync(); + + cmd = database.CreateCommand(); + UserPreset defaultPreset = UserPreset.Default(); + defaultPreset.OwnerId = u.Id; + await AddPresetToDatabase(defaultPreset); + } + + async Task AddPresetToDatabase(UserPreset p) + { + var cmd = database.CreateCommand(); + cmd.CommandText = "INSERT INTO user_presets (user_id, name, outline_color, title_color, subtitle_color) VALUES ($1, $2, $3, $4, $5)"; + cmd.Parameters.AddWithValue("$1", p.OwnerId); + cmd.Parameters.AddWithValue("$2", p.Name); + cmd.Parameters.AddWithValue("$3", (long)p.OutlineColor.ToArgb() & 0xFFFFFFL); + cmd.Parameters.AddWithValue("$4", (long)p.TitleColor.ToArgb() & 0xFFFFFFL); + cmd.Parameters.AddWithValue("$5", (long)p.SubtitleColor.ToArgb() & 0xFFFFFFL); + await cmd.ExecuteNonQueryAsync(); + } + + async Task IsUserInDatabase(User u) + { + var cmd = database.CreateCommand(); + cmd.CommandText = "SELECT id FROM users WHERE id = $1"; + cmd.Parameters.AddWithValue("$1", u.Id); + using var reader = await cmd.ExecuteReaderAsync(); + return await reader.ReadAsync(); + } + + async Task GetUserPresets(User u) + { + var cmd = database.CreateCommand(); + cmd.CommandText = "SELECT id, user_id, name, outline_color, title_color, subtitle_color FROM user_presets WHERE id = $1"; + cmd.Parameters.AddWithValue("$1", u.Id); + using var reader = await cmd.ExecuteReaderAsync(); + List result = new List(); + while (reader.Read()) + { + result.Add(new UserPreset() + { + Id = reader.GetInt64(0), + OwnerId = reader.GetInt64(1), + Name = reader.GetString(2), + OutlineColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(3))), + TitleColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(4))), + SubtitleColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(5))), + }); + } + return result.ToArray(); + } + + async Task GetUserPresetByName(User u, string name) + { + var cmd = database.CreateCommand(); + cmd.CommandText = "SELECT id, outline_color, title_color, subtitle_color FROM user_presets WHERE name = $1"; + cmd.Parameters.AddWithValue("$1", name); + using (var reader = await cmd.ExecuteReaderAsync()) + { + if (reader.Read()) + { + return new UserPreset() + { + Id = reader.GetInt64(0), + OwnerId = u.Id, + Name = name, + OutlineColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(1))), + TitleColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(2))), + SubtitleColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(3))), + }; + } + else + { + return null; + } + } + } + + async Task DeleteUserPreset(long id) + { + var cmd = database.CreateCommand(); + cmd.CommandText = "DELETE FROM user_presets WHERE id = $1"; + cmd.Parameters.AddWithValue("$1", id); + cmd.ExecuteNonQuery(); + } + + async Task GetActiveUserPreset(User u) + { + var cmd = database.CreateCommand(); + cmd.CommandText = "SELECT selected_preset FROM users WHERE id = $1"; + cmd.Parameters.AddWithValue("$1", u.Id); + int activePreset = -1; + using (var reader = await cmd.ExecuteReaderAsync()) { + if (reader.Read()) + { + activePreset = reader.GetInt32(0); + } else + { + throw new Exception("пользователь не найден"); + } + } + + cmd = database.CreateCommand(); + cmd.CommandText = "SELECT name, outline_color, title_color, subtitle_color FROM user_presets WHERE id = $1"; + cmd.Parameters.AddWithValue("$1", activePreset); + using (var reader = await cmd.ExecuteReaderAsync()) + { + if (reader.Read()) + { + return new UserPreset() + { + Id = activePreset, + OwnerId = u.Id, + Name = reader.GetString(0), + OutlineColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(1))), + TitleColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(2))), + SubtitleColor = System.Drawing.Color.FromArgb((int)(0xff000000L | reader.GetInt64(3))), + }; + } else + { + throw new Exception("выбранный пресет пользователя " + u.Id + " не найден"); + } + } + } + /// /// Обработчик исключений, возникших при работе бота /// diff --git a/SimpleTGBot/UserPreset.cs b/SimpleTGBot/UserPreset.cs new file mode 100644 index 0000000..1926eb2 --- /dev/null +++ b/SimpleTGBot/UserPreset.cs @@ -0,0 +1,23 @@ +using System.Drawing; + +namespace SimpleTGBot; + +internal struct UserPreset +{ + public long Id; + public long OwnerId; + public string Name; + public Color OutlineColor; + public Color TitleColor; + public Color SubtitleColor; + + public static UserPreset Default() => new UserPreset() + { + Id = 0, + OwnerId = 0, + Name = "По умолчанию", + OutlineColor = Color.FromArgb(255, 255, 255, 255), + TitleColor = Color.FromArgb(255, 255, 255, 255), + SubtitleColor = Color.FromArgb(255, 255, 255, 255), + }; +}