Browse Source

Mapping project structure, deleting bot and user messages

Tigran 4 years ago
parent
commit
1929a0bdaa

+ 11 - 0
CardCollector/Bot.cs

@@ -0,0 +1,11 @@
+using Telegram.Bot;
+
+namespace CardCollector
+{
+    using static Resources.AppSettings;
+    public static class Bot
+    {
+        private static TelegramBotClient _client;
+        public static TelegramBotClient Client => _client ??= new TelegramBotClient(TOKEN);
+    }
+}

+ 4 - 1
CardCollector/CardCollector.csproj

@@ -6,8 +6,11 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="EntityFramework" Version="6.4.4" />
+        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.7" />
+        <PackageReference Include="MySql.Data" Version="8.0.25" />
+        <PackageReference Include="MySql.EntityFrameworkCore" Version="5.0.3.1" />
       <PackageReference Include="Telegram.Bot" Version="17.0.0-alpha.3" />
+      <PackageReference Include="Telegram.Bot.Extensions.Polling" Version="1.0.0-alpha.1" />
     </ItemGroup>
 
 </Project>

+ 7 - 0
CardCollector/Commands/CallbackQueries/CallbackQuery.cs

@@ -0,0 +1,7 @@
+namespace CardCollector.Commands.CallbackQueries
+{
+    public class CallbackQuery
+    {
+        
+    }
+}

+ 19 - 0
CardCollector/Commands/MessageCommands/CommandNotFound.cs

@@ -0,0 +1,19 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.MessageCommands
+{
+    public class CommandNotFound : MessageCommand
+    {
+        protected override string Command => "";
+
+        public override async Task<Message> Execute()
+        {
+            return await MessageController.SendMessage(User.ChatId, "Команда не найдена");
+        }
+
+        public CommandNotFound(UserEntity user) : base(user) { }
+    }
+}

+ 46 - 0
CardCollector/Commands/MessageCommands/MessageCommand.cs

@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.MessageCommands
+{
+    public abstract class MessageCommand : UpdateModel
+    {
+        private static readonly List<MessageCommand> List = new()
+        {
+
+        };
+
+        public static async Task<MessageCommand> Factory(Update update)
+        {
+            // Текст команды
+            var command = update.Message!.Text;
+            
+            // Id пользователя, отправившего команду
+            var userId = update.Message!.From!.Id;
+            // Объект пользователя
+            var user = (await UserDao.GetById(userId)) ?? (await UserDao.AddNew(update.Message.From));
+            
+            // Удаляем предыдущие сообщения пользователя и бота
+            MessageController.AddNewMessageToPool(update.Message.Chat.Id, update.Message.MessageId);
+            await MessageController.DeleteMessagesFromPool(update.Message.Chat.Id);
+            
+            // Возвращаем объект, если команда совпала
+            foreach (var item in List)
+                if (item.IsMatches(command))
+                    return (MessageCommand) Activator.CreateInstance(item.GetType(), user);
+            
+            // Возвращаем команда не найдена, если код дошел до сюда
+            return new CommandNotFound(user);
+        }
+
+        protected MessageCommand(UserEntity user)
+        {
+            User = user;
+        }
+    }
+}

+ 21 - 0
CardCollector/Commands/UpdateModel.cs

@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using CardCollector.Commands.MessageCommands;
+using CardCollector.DataBase.Entity;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands
+{
+    public abstract class UpdateModel
+    {
+        protected abstract string Command { get; }
+        protected UserEntity User;
+
+        public abstract Task<Message> Execute();
+
+        protected virtual bool IsMatches(string command)
+        {
+            return command.Contains(Command);
+        }
+    }
+}

+ 96 - 0
CardCollector/Controllers/MessageController.cs

@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using CardCollector.Commands.MessageCommands;
+using Telegram.Bot;
+using Telegram.Bot.Exceptions;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+using Telegram.Bot.Types.ReplyMarkups;
+
+namespace CardCollector.Controllers
+{
+    using static Logs;
+
+    public static class MessageController
+    {
+        private static Dictionary<long, List<int>> _deletingMessagePool = new();
+
+        public static async Task HandleUpdateAsync(ITelegramBotClient client, Update update, CancellationToken ct)
+        {
+            try
+            {
+                var executor = update.Type switch
+                {
+                    UpdateType.Message => await MessageCommand.Factory(update),
+                    _ => throw new ArgumentOutOfRangeException()
+                };
+                var message = await executor.Execute();
+                AddNewMessageToPool(message.Chat.Id, message.MessageId);
+            }
+            catch (ArgumentOutOfRangeException)
+            {
+                LogOut(update.Type.ToString());
+            }
+            catch (Exception e)
+            {
+                await HandleErrorAsync(client, e, ct);
+            }
+        }
+
+        public static Task HandleErrorAsync(ITelegramBotClient client, Exception e, CancellationToken ct)
+        {
+            switch (e)
+            {
+                case ApiRequestException apiRequestException:
+                    LogOutWarning($"API Error:[{apiRequestException.ErrorCode}] - {apiRequestException.Message}");
+                    break;
+                default:
+                    LogOutError(e.ToString());
+                    break;
+            }
+
+            return Task.CompletedTask;
+        }
+
+        public static async Task DeleteMessageAsync(long chatId, int messageId)
+        {
+            await Bot.Client.DeleteMessageAsync(chatId, messageId);
+        }
+
+        public static void AddNewMessageToPool(long chatId, int messageId)
+        {
+            try
+            {
+                _deletingMessagePool[chatId].Add(messageId);
+            }
+            catch (Exception)
+            {
+                _deletingMessagePool.Add(chatId, new List<int>());
+                _deletingMessagePool[chatId].Add(messageId);
+            }
+        }
+
+        public static async Task DeleteMessagesFromPool(long chatId)
+        {
+            try
+            {
+                foreach (var id in _deletingMessagePool[chatId])
+                    await Bot.Client.DeleteMessageAsync(chatId, id);
+                _deletingMessagePool[chatId].Clear();
+                _deletingMessagePool.Remove(chatId);
+            }
+            catch (Exception)
+            {
+                /* ignore */
+            }
+        }
+
+        public static async Task<Message> SendMessage(long chatId, string messageText, IReplyMarkup keyboard = null)
+        {
+            return await Bot.Client.SendTextMessageAsync(chatId, messageText, ParseMode.Html,
+                replyMarkup: keyboard, disableNotification: true);
+        }
+    }
+}

+ 40 - 0
CardCollector/DataBase/CardCollectorDatabase.cs

@@ -0,0 +1,40 @@
+using CardCollector.DataBase.Entity;
+using Microsoft.EntityFrameworkCore;
+
+namespace CardCollector.DataBase
+{
+    using static Resources.AppSettings;
+    public class CardCollectorDatabase : DbContext
+    {
+        private CardCollectorDatabase()
+        {
+            Database.EnsureCreated();
+        }
+        private static CardCollectorDatabase _instance;
+        public static CardCollectorDatabase Instance
+        {
+            get
+            {
+                if (_instance != null) return _instance;
+                _instance = new CardCollectorDatabase();
+                return _instance;
+            }
+        }
+        
+        // Таблицы базы данных, представленные Entity объектами
+        public DbSet<UserEntity> Users { get; set; }
+
+        
+        // Конфигурация подключения к БД
+        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+        {
+            optionsBuilder.UseMySQL(
+                $"server={DB_IP};" +
+                $"port={DB_PORT};" +
+                $"database={DB_SCHEMA};" +
+                $"uid={DB_UID};" +
+                $"pwd={DB_PWD}"
+            );
+        }
+    }
+}

+ 13 - 0
CardCollector/DataBase/Entity/UserEntity.cs

@@ -0,0 +1,13 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace CardCollector.DataBase.Entity
+{
+    [Table("users")]
+    public class UserEntity
+    {
+        [Column("id"), MaxLength(127)] public long Id { get; set; }
+        [Column("chat_id"), MaxLength(127)] public long ChatId { get; set; }
+        [Column("username"), MaxLength(256)] public string Username { get; set; }
+    }
+}

+ 50 - 0
CardCollector/DataBase/EntityDao/UserDao.cs

@@ -0,0 +1,50 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using CardCollector.DataBase.Entity;
+using Microsoft.EntityFrameworkCore;
+using Telegram.Bot.Types;
+
+namespace CardCollector.DataBase.EntityDao
+{
+    public static class UserDao
+    {
+        private static CardCollectorDatabase DataBase = CardCollectorDatabase.Instance;
+        
+        private static Dictionary<long, UserEntity> ActiveUsers = new();
+        
+        public static async Task<UserEntity?> GetById(long userId)
+        {
+            if (!await UserExists(userId)) return null;
+            try
+            {
+                return ActiveUsers[userId];
+            }
+            catch (Exception)
+            {
+                var user = await DataBase.Users.FindAsync(userId);
+                
+                ActiveUsers.Add(user.Id, user);
+                return user;
+            }
+        }
+
+        public static async Task<UserEntity> AddNew(User user)
+        {
+            var userEntity = new UserEntity();
+            userEntity.Id = user.Id;
+            userEntity.ChatId = user.Id;
+            userEntity.Username = user.Username;
+            await DataBase.Users.AddAsync(userEntity);
+            await DataBase.SaveChangesAsync();
+            return userEntity;
+        }
+
+        private static async Task<bool> UserExists(long userId)
+        {
+            if(ActiveUsers.ContainsKey(userId)) return true;
+            return await DataBase.Users.AnyAsync(e => e.Id == userId);
+        }
+    }
+}

+ 6 - 0
CardCollector/Extensions/DictionaryExtensions.cs

@@ -0,0 +1,6 @@
+namespace CardCollector.Extensions
+{
+    public static class DictionaryExtensions
+    {
+    }
+}

+ 22 - 0
CardCollector/Logs.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Globalization;
+// ReSharper disable LocalizableElement
+
+namespace CardCollector
+{
+    public static class Logs
+    {
+        public static void LogOut(string message)
+        {
+            Console.WriteLine($"[INFO] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {message}");
+        }
+        public static void LogOutWarning(string message)
+        {
+            Console.WriteLine($"[WARNING] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {message}");
+        }
+        public static void LogOutError(string message)
+        {
+            Console.WriteLine($"[ERROR] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {message}");
+        }
+    }
+}

+ 7 - 1
CardCollector/Program.cs

@@ -1,12 +1,18 @@
 using System;
+using System.Threading;
+using Telegram.Bot;
 
 namespace CardCollector
 {
+    using static Controllers.MessageController;
     class Program
     {
         static void Main(string[] args)
         {
-            Console.WriteLine("Hello World!");
+            var cts = new CancellationTokenSource();
+            Bot.Client.StartReceiving(HandleUpdateAsync, HandleErrorAsync, cancellationToken: cts.Token);
+            Console.ReadLine();
+            cts.Cancel();
         }
     }
 }

+ 14 - 0
CardCollector/Resources/AppSettings.cs

@@ -0,0 +1,14 @@
+namespace CardCollector.Resources
+{
+    using static Constants;
+    public static class AppSettings
+    {
+        public const string TOKEN = DEBUG ? "1947951335:AAFX_kMdRbM_lih3dGUdvQ4d4-0qQzy7igg" : "";
+        public const string NAME = DEBUG ? "TigranCardCollectorBot" : "";
+        public const string DB_IP = "localhost";
+        public const string DB_PORT = "3306";
+        public const string DB_UID = "card_collector";
+        public const string DB_PWD = DEBUG ? "Password1*" : "";
+        public const string DB_SCHEMA = DEBUG ? "card_collector" : "";
+    }
+}

+ 9 - 0
CardCollector/Resources/Constants.cs

@@ -0,0 +1,9 @@
+namespace CardCollector.Resources
+{
+    public class Constants
+    {
+        public const bool DEBUG = true;
+
+        public const int MEMORY_CLEANER_INTERVAL = 60 * 1000;
+    }
+}

+ 4 - 1
CardCollectorChat/CardCollectorChat.csproj

@@ -6,8 +6,11 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <PackageReference Include="EntityFramework" Version="6.4.4" />
+        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.7" />
+        <PackageReference Include="MySql.Data" Version="8.0.25" />
+        <PackageReference Include="MySql.EntityFrameworkCore" Version="5.0.3.1" />
       <PackageReference Include="Telegram.Bot" Version="17.0.0-alpha.3" />
+      <PackageReference Include="Telegram.Bot.Extensions.Polling" Version="1.0.0-alpha.1" />
     </ItemGroup>
 
 </Project>