add part of the dialog and implement logging to console
This commit is contained in:
parent
252a372477
commit
df6268323c
16
SimpleTGBot/DialogData.cs
Normal file
16
SimpleTGBot/DialogData.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace SimpleTGBot;
|
||||
|
||||
internal class DialogData
|
||||
{
|
||||
public DialogState state;
|
||||
public string? inputPictureFilename;
|
||||
}
|
||||
|
||||
enum DialogState
|
||||
{
|
||||
Initial,
|
||||
AwaitingPicture,
|
||||
AwaitingTitle,
|
||||
AwaitingSubtitle,
|
||||
ShowingResult,
|
||||
}
|
34
SimpleTGBot/Interactions.cs
Normal file
34
SimpleTGBot/Interactions.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace SimpleTGBot;
|
||||
|
||||
internal static class Interactions
|
||||
{
|
||||
public const string awaitingPictureMessage = "Привет. Я - бот, который умеет делать демотиваторы. Присылай картинку, а я скажу, что делать дальше. Или можешь нажать на кнопку в меню.";
|
||||
public const string sayHelloMessage = "Напиши \"привет\" или нажми на кнопку в меню, чтобы начать.";
|
||||
public const string sendPictureOrQuitMessage = "Пришли мне картинку для демотиватора, чтобы продолжить. Чтобы отменить, напиши \"назад\" или \"\"";
|
||||
|
||||
public static readonly IReplyMarkup initialReplyMarkup = new ReplyKeyboardMarkup([[new KeyboardButton("▶️Начать")]]);
|
||||
public static readonly IReplyMarkup backButtonReplyMarkup = new ReplyKeyboardMarkup(new KeyboardButton("↩️Назад"));
|
||||
public static readonly IReplyMarkup quickActionReplyMarkup = new ReplyKeyboardRemove();
|
||||
|
||||
static readonly string[] helloWords = ["прив","привет","▶️начать","ку","хай","приветик","превед","привки","хаюхай","здравствуй","здравствуйте","здорово","дарова","дороу","здарова","здорова"];
|
||||
static readonly string[] cancelWords = ["↩️назад", "назад", "выйти", "отмена", "отменить", "отменяй", "галя", "галина", "стоп"];
|
||||
|
||||
public static bool IsStartCommand(string message)
|
||||
{
|
||||
return message.Split(' ').FirstOrDefault() == "/start";
|
||||
}
|
||||
|
||||
public static bool IsHello(string message)
|
||||
{
|
||||
string[] messageWords = message.ToLower().Split(new char[] { ' ', ',', '.', ';', '(', ')' });
|
||||
return helloWords.Any(word => messageWords.Contains(word));
|
||||
}
|
||||
|
||||
public static bool IsCancellation(string message)
|
||||
{
|
||||
string[] messageWords = message.ToLower().Split(new char[] { ' ', ',', '.', ';', '(', ')' });
|
||||
return cancelWords.Any(word => messageWords.Contains(word));
|
||||
}
|
||||
}
|
19
SimpleTGBot/Logging/LogLevel.cs
Normal file
19
SimpleTGBot/Logging/LogLevel.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace SimpleTGBot.Logging;
|
||||
|
||||
internal enum LogLevel : int
|
||||
{
|
||||
Debug = 0, Info, Warning, Error, Fatal
|
||||
}
|
||||
static class LogLevelExt
|
||||
{
|
||||
public static string GetName(this LogLevel level)
|
||||
{
|
||||
return level switch {
|
||||
LogLevel.Debug => "DEBUG",
|
||||
LogLevel.Info => "INFO",
|
||||
LogLevel.Warning => "WARN",
|
||||
LogLevel.Error => "ERROR",
|
||||
LogLevel.Fatal => "FATAL"
|
||||
};
|
||||
}
|
||||
}
|
6
SimpleTGBot/Logging/LogSink.cs
Normal file
6
SimpleTGBot/Logging/LogSink.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace SimpleTGBot.Logging;
|
||||
|
||||
internal interface ILogSink : IDisposable
|
||||
{
|
||||
public void Log(DateTime time, LogLevel level, string message);
|
||||
}
|
34
SimpleTGBot/Logging/Logger.cs
Normal file
34
SimpleTGBot/Logging/Logger.cs
Normal file
@ -0,0 +1,34 @@
|
||||
namespace SimpleTGBot.Logging;
|
||||
|
||||
internal class Logger : IDisposable
|
||||
{
|
||||
public List<ILogSink> Sinks;
|
||||
|
||||
public Logger(params ILogSink[] sinks)
|
||||
{
|
||||
Sinks = new List<ILogSink>(sinks);
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, string message)
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
foreach (var sink in Sinks)
|
||||
{
|
||||
sink.Log(now, level, message);
|
||||
}
|
||||
}
|
||||
|
||||
public void Debug(string message) => Log(LogLevel.Debug, message);
|
||||
public void Info(string message) => Log(LogLevel.Info, message);
|
||||
public void Warn(string message) => Log(LogLevel.Warning, message);
|
||||
public void Error(string message) => Log(LogLevel.Error, message);
|
||||
public void Fatal(string message) => Log(LogLevel.Fatal, message);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var sink in Sinks)
|
||||
{
|
||||
sink.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
23
SimpleTGBot/Logging/StdoutSink.cs
Normal file
23
SimpleTGBot/Logging/StdoutSink.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace SimpleTGBot.Logging;
|
||||
|
||||
internal class StdoutSink : ILogSink
|
||||
{
|
||||
private readonly ConsoleColor[] colors = [ConsoleColor.White, ConsoleColor.Cyan, ConsoleColor.Yellow, ConsoleColor.DarkRed, ConsoleColor.Red];
|
||||
private ConsoleColor originalConsoleColor;
|
||||
|
||||
public StdoutSink()
|
||||
{
|
||||
originalConsoleColor = Console.ForegroundColor;
|
||||
}
|
||||
|
||||
public void Log(DateTime time, LogLevel level, string message)
|
||||
{
|
||||
Console.ForegroundColor = colors[(int)level];
|
||||
foreach (string line in message.Split(Environment.NewLine))
|
||||
Console.WriteLine($"({time:u}) [{level.GetName()}] {line}");
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Console.ForegroundColor = originalConsoleColor;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using SimpleTGBot.Logging;
|
||||
|
||||
namespace SimpleTGBot;
|
||||
|
||||
@ -29,8 +30,11 @@ public static class Program
|
||||
return 1;
|
||||
}
|
||||
|
||||
TelegramBot telegramBot = new TelegramBot(botToken);
|
||||
Logger logger = new Logger();
|
||||
logger.Sinks.Add(new StdoutSink());
|
||||
TelegramBot telegramBot = new TelegramBot(botToken, logger);
|
||||
await telegramBot.Run();
|
||||
logger.Dispose();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using SimpleTGBot.MemeGen;
|
||||
using SimpleTGBot.Logging;
|
||||
using SimpleTGBot.MemeGen;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Exceptions;
|
||||
using Telegram.Bot.Polling;
|
||||
@ -7,15 +8,21 @@ using Telegram.Bot.Types.Enums;
|
||||
|
||||
namespace SimpleTGBot;
|
||||
|
||||
public class TelegramBot
|
||||
internal class TelegramBot
|
||||
{
|
||||
private string token;
|
||||
private Logger logger;
|
||||
private Dictionary<long, DialogData> dialogs;
|
||||
private TempStorage temp;
|
||||
private HttpClient httpClient;
|
||||
|
||||
public TelegramBot(string token)
|
||||
public TelegramBot(string token, Logger logger)
|
||||
{
|
||||
this.token = token;
|
||||
this.logger = logger;
|
||||
dialogs = new Dictionary<long, DialogData>();
|
||||
temp = new TempStorage();
|
||||
httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -40,11 +47,11 @@ public class TelegramBot
|
||||
try
|
||||
{
|
||||
var me = await botClient.GetMeAsync(cancellationToken: cts.Token);
|
||||
Console.WriteLine($"Бот @{me.Username} запущен.\nДля остановки нажмите клавишу Esc...");
|
||||
logger.Info($"Бот @{me.Username} запущен.\r\nДля остановки нажмите клавишу Esc...");
|
||||
}
|
||||
catch (ApiRequestException)
|
||||
{
|
||||
Console.WriteLine("Указан неправильный токен");
|
||||
logger.Fatal("Указан неправильный токен");
|
||||
goto botQuit;
|
||||
}
|
||||
|
||||
@ -63,27 +70,94 @@ public class TelegramBot
|
||||
/// <param name="cancellationToken">Служебный токен для работы с многопоточностью</param>
|
||||
async Task OnMessageReceived(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
|
||||
{
|
||||
var message = update.Message;
|
||||
if (message is null)
|
||||
if (update.Message is not { } message) return;
|
||||
if (message.Chat.Type != ChatType.Private) return;
|
||||
|
||||
DialogData dialogData;
|
||||
if (!dialogs.ContainsKey(message.Chat.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (message.Text is not { } messageText)
|
||||
dialogData = new DialogData() { state = DialogState.Initial };
|
||||
dialogs[message.Chat.Id] = dialogData;
|
||||
} else
|
||||
{
|
||||
return;
|
||||
dialogData = dialogs[message.Chat.Id];
|
||||
}
|
||||
var chatId = message.Chat.Id;
|
||||
|
||||
Console.WriteLine($"Получено сообщение в чате {chatId}: '{messageText}'");
|
||||
switch (dialogData.state)
|
||||
{
|
||||
case DialogState.Initial:
|
||||
{
|
||||
bool replied = false;
|
||||
if (message.Photo is { } picture)
|
||||
{
|
||||
replied = true;
|
||||
await DialogHandleDemotivatorPicture(botClient, dialogData, message, picture, cancellationToken);
|
||||
}
|
||||
else if (message.Text is { } messageText)
|
||||
{
|
||||
if (Interactions.IsStartCommand(messageText) || Interactions.IsHello(messageText))
|
||||
{
|
||||
_ = botClient.SendTextMessageAsync(message.Chat.Id, Interactions.awaitingPictureMessage, replyMarkup: Interactions.backButtonReplyMarkup);
|
||||
dialogData.state = DialogState.AwaitingPicture;
|
||||
replied = true;
|
||||
}
|
||||
}
|
||||
if (!replied)
|
||||
{
|
||||
_ = botClient.SendTextMessageAsync(message.Chat.Id, Interactions.sayHelloMessage, replyMarkup: Interactions.initialReplyMarkup);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DialogState.AwaitingPicture:
|
||||
{
|
||||
if (message.Photo is { } picture)
|
||||
{
|
||||
await DialogHandleDemotivatorPicture(botClient, dialogData, message, picture, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool reacted = false;
|
||||
if (message.Text is { } messageText)
|
||||
{
|
||||
if (Interactions.IsCancellation(messageText))
|
||||
{
|
||||
dialogData.state = DialogState.Initial;
|
||||
_ = botClient.SendTextMessageAsync(message.Chat.Id, Interactions.awaitingPictureMessage, replyMarkup: Interactions.quickActionReplyMarkup);
|
||||
reacted = true;
|
||||
}
|
||||
}
|
||||
if (!reacted)
|
||||
_ = botClient.SendTextMessageAsync(message.Chat.Id, Interactions.sendPictureOrQuitMessage, replyMarkup: Interactions.backButtonReplyMarkup);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message sentMessage = await botClient.SendTextMessageAsync(
|
||||
chatId: chatId,
|
||||
text: "Ты написал:\n" + messageText,
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
// грязный тест
|
||||
MemoryStream demotivatorPng = DemotivatorGen.MakePictureDemotivator("pic.png", [new DemotivatorText() {Title=messageText, Subtitle=messageText}], DemotivatorGen.DefaultStyle());
|
||||
await botClient.SendPhotoAsync(message.Chat.Id, new InputFile(demotivatorPng, "dem.png"));
|
||||
async Task DialogHandleDemotivatorPicture(ITelegramBotClient botClient, DialogData dialogData, Message message, PhotoSize[] picture, CancellationToken cancellationToken)
|
||||
{
|
||||
string largestSizeId = picture[picture.Length - 1].FileId;
|
||||
Telegram.Bot.Types.File pictureFile = await botClient.GetFileAsync(largestSizeId, cancellationToken);
|
||||
string pictureExtension = pictureFile.FilePath.Substring(pictureFile.FilePath.LastIndexOf('.') + 1);
|
||||
try
|
||||
{
|
||||
using (HttpResponseMessage response = await httpClient.GetAsync(FilePathToUrl(pictureFile.FilePath), cancellationToken))
|
||||
{
|
||||
response.EnsureSuccessStatusCode();
|
||||
(string tempFileName, FileStream tempFile) = temp.newTemporaryFile("pic", pictureExtension);
|
||||
await response.Content.CopyToAsync(tempFile);
|
||||
tempFile.Close();
|
||||
logger.Info($"Файл картинки {tempFileName} загружен от пользователя {message.From.FirstName}[{message.From.Id}]");
|
||||
dialogData.inputPictureFilename = tempFileName;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Ошибка при скачивании картинки от пользователя: " + e.GetType().Name + ": " + e.Message);
|
||||
logger.Error(e.StackTrace ?? "");
|
||||
_ = botClient.SendTextMessageAsync(message.Chat.Id, "Ошибка :(");
|
||||
dialogData.state = DialogState.Initial;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -103,8 +177,13 @@ public class TelegramBot
|
||||
_ => exception.ToString()
|
||||
};
|
||||
|
||||
Console.WriteLine(errorMessage);
|
||||
logger.Error(errorMessage);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private string FilePathToUrl(string filePath)
|
||||
{
|
||||
return $"https://api.telegram.org/file/bot{token}/{filePath}";
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user