TelegramBot: add method to work with the database

This commit is contained in:
Slavasil 2025-04-20 22:22:07 +03:00
parent 330abb90ff
commit a457ea1a19
7 changed files with 227 additions and 7 deletions

View File

@ -5,6 +5,7 @@ namespace SimpleTGBot;
internal class Config internal class Config
{ {
public const string DEFAULT_BOT_TOKEN_FILENAME = "telegram_token.txt"; public const string DEFAULT_BOT_TOKEN_FILENAME = "telegram_token.txt";
public const string DEFAULT_DATABASE_FILENAME = "demotivatorbot.db";
public static string? TryReadBotTokenFile() public static string? TryReadBotTokenFile()
{ {

View File

@ -1,4 +1,5 @@
using Telegram.Bot.Types.ReplyMarkups; using System.Text;
using Telegram.Bot.Types.ReplyMarkups;
namespace SimpleTGBot; namespace SimpleTGBot;
@ -28,6 +29,8 @@ internal static class Interactions
public static readonly IReplyMarkup resultActionReplyMarkup = new ReplyKeyboardMarkup([new KeyboardButton(doneButtonText)]); public static readonly IReplyMarkup resultActionReplyMarkup = new ReplyKeyboardMarkup([new KeyboardButton(doneButtonText)]);
public static readonly IReplyMarkup settingsReplyMarkup = new ReplyKeyboardMarkup([[new KeyboardButton(gotoPresetsButtonText)], [new KeyboardButton(backButtonText)]]); 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) public static bool IsStartCommand(string message)
{ {
return message.Split(' ').FirstOrDefault() == "/start"; return message.Split(' ').FirstOrDefault() == "/start";
@ -55,4 +58,33 @@ internal static class Interactions
{ {
return message == backButtonText; 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();
}
} }

View File

@ -13,7 +13,7 @@ public record DemotivatorStyle
public float Padding { get; set; } public float Padding { get; set; }
public float OuterMargin { get; set; } public float OuterMargin { get; set; }
public float CaptionSpacing { get; set; } public float CaptionSpacing { get; set; }
public float Wtf1 { get; set; } public float AdditionalTextWidth { get; set; }
public Color OutlineColor { get; set; } public Color OutlineColor { get; set; }
public Color TitleColor { get; set; } public Color TitleColor { get; set; }
public Color SubtitleColor { get; set; } public Color SubtitleColor { get; set; }

View File

@ -1,4 +1,5 @@
using System.Text; using System.Text;
using Microsoft.Data.Sqlite;
using SimpleTGBot.Logging; using SimpleTGBot.Logging;
namespace SimpleTGBot; namespace SimpleTGBot;
@ -16,6 +17,11 @@ public static class Program
// Православная кодировка // Православная кодировка
Console.OutputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8;
using SqliteConnection db = new("Data Source=" + Config.DEFAULT_DATABASE_FILENAME);
db.Open();
PrepareDatabaseTables(db);
string? botToken = Config.TryReadBotTokenFile(); string? botToken = Config.TryReadBotTokenFile();
if (botToken == null) if (botToken == null)
@ -33,10 +39,29 @@ public static class Program
using (Logger logger = new Logger()) using (Logger logger = new Logger())
{ {
logger.Sinks.Add(new StdoutSink()); logger.Sinks.Add(new StdoutSink());
TelegramBot telegramBot = new TelegramBot(botToken, logger); TelegramBot telegramBot = new TelegramBot(botToken, logger, db);
await telegramBot.Run(); await telegramBot.Run();
} }
db.Close();
return 0; 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();
}
} }

View File

@ -8,6 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.4" />
<PackageReference Include="System.Drawing.Common" Version="9.0.4" /> <PackageReference Include="System.Drawing.Common" Version="9.0.4" />
<PackageReference Include="Telegram.Bot" Version="19.0.0-preview.2" /> <PackageReference Include="Telegram.Bot" Version="19.0.0-preview.2" />
</ItemGroup> </ItemGroup>

View File

@ -1,4 +1,6 @@
using SimpleTGBot.Logging; using System.Drawing;
using Microsoft.Data.Sqlite;
using SimpleTGBot.Logging;
using SimpleTGBot.MemeGen; using SimpleTGBot.MemeGen;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Exceptions; using Telegram.Bot.Exceptions;
@ -12,17 +14,19 @@ internal class TelegramBot
{ {
private string token; private string token;
private Logger logger; private Logger logger;
private SqliteConnection database;
private Dictionary<long, DialogData> dialogs; private Dictionary<long, DialogData> dialogs;
private TempStorage temp; private TempStorage temp;
private HttpClient httpClient; private HttpClient httpClient;
public TelegramBot(string token, Logger logger) public TelegramBot(string token, Logger logger, SqliteConnection db)
{ {
this.token = token; this.token = token;
this.logger = logger; this.logger = logger;
dialogs = new Dictionary<long, DialogData>(); dialogs = new Dictionary<long, DialogData>();
temp = new TempStorage(); temp = new TempStorage();
httpClient = new HttpClient(); httpClient = new HttpClient();
database = db;
} }
/// <summary> /// <summary>
@ -72,12 +76,17 @@ internal class TelegramBot
{ {
if (update.Message is not { } message) return; if (update.Message is not { } message) return;
if (message.Chat.Type != ChatType.Private) return; if (message.Chat.Type != ChatType.Private) return;
if (message.From is not { } user) return;
DialogData dialogData; DialogData dialogData;
if (!dialogs.ContainsKey(message.Chat.Id)) if (!dialogs.ContainsKey(message.Chat.Id))
{ {
dialogData = new DialogData() { state = DialogState.Initial }; dialogData = new DialogData() { state = DialogState.Initial };
dialogs[message.Chat.Id] = dialogData; dialogs[message.Chat.Id] = dialogData;
if (!await IsUserInDatabase(user))
{
await AddUserToDatabase(user);
}
} else } else
{ {
dialogData = dialogs[message.Chat.Id]; dialogData = dialogs[message.Chat.Id];
@ -203,7 +212,7 @@ internal class TelegramBot
{ {
replied = true; replied = true;
dialogData.state = DialogState.ViewingPresets; 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) else if (messageText == Interactions.backButtonText)
{ {
@ -330,6 +339,135 @@ internal class TelegramBot
temp.deleteTemporaryFile(dialogData.inputPictureFilename); 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<bool> 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<UserPreset[]> 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<UserPreset> result = new List<UserPreset>();
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<UserPreset?> 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<UserPreset> 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 + " не найден");
}
}
}
/// <summary> /// <summary>
/// Обработчик исключений, возникших при работе бота /// Обработчик исключений, возникших при работе бота
/// </summary> /// </summary>

23
SimpleTGBot/UserPreset.cs Normal file
View File

@ -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),
};
}