Tigran 4 سال پیش
والد
کامیت
31f1e52d9d
30فایلهای تغییر یافته به همراه430 افزوده شده و 196 حذف شده
  1. 2 2
      CardCollector.sln.DotSettings.user
  2. 18 24
      CardCollector/Auction/AuctionController.cs
  3. 22 23
      CardCollector/Commands/CallbackQuery/CallbackQuery.cs
  4. 28 21
      CardCollector/Commands/ChosenInlineResult/ChosenInlineResult.cs
  5. 5 1
      CardCollector/Commands/ChosenInlineResult/SendStickerResult.cs
  6. 1 0
      CardCollector/Commands/CommandNotFound.cs
  7. 1 0
      CardCollector/Commands/IgnoreUpdate.cs
  8. 27 21
      CardCollector/Commands/InlineQuery/InlineQuery.cs
  9. 9 1
      CardCollector/Commands/InlineQuery/ShowStickersInGroup.cs
  10. 30 26
      CardCollector/Commands/Message/Message.cs
  11. 13 12
      CardCollector/Commands/Message/ProfileMessage.cs
  12. 6 8
      CardCollector/Commands/Message/StartMessage.cs
  13. 3 0
      CardCollector/Commands/MyChatMember/MyChatMember.cs
  14. 7 0
      CardCollector/Commands/UpdateModel.cs
  15. 59 46
      CardCollector/Controllers/MessageController.cs
  16. 9 2
      CardCollector/DataBase/CardCollectorDatabase.cs
  17. 7 0
      CardCollector/DataBase/Entity/CashEntity.cs
  18. 21 1
      CardCollector/DataBase/Entity/StickerEntity.cs
  19. 12 0
      CardCollector/DataBase/Entity/UserEntity.cs
  20. 10 0
      CardCollector/DataBase/Entity/UserStickerRelationEntity.cs
  21. 4 0
      CardCollector/DataBase/EntityDao/CashDao.cs
  22. 12 0
      CardCollector/DataBase/EntityDao/StickerDao.cs
  23. 13 3
      CardCollector/DataBase/EntityDao/UserDao.cs
  24. 4 5
      CardCollector/DataBase/EntityDao/UserStickerRelationDao.cs
  25. 9 0
      CardCollector/Resources/CallbackQueryCommands.Designer.cs
  26. 3 0
      CardCollector/Resources/CallbackQueryCommands.resx
  27. 22 0
      CardCollector/Resources/Keyboard.cs
  28. 45 0
      CardCollector/Resources/MessageCommands.Designer.cs
  29. 15 0
      CardCollector/Resources/MessageCommands.resx
  30. 13 0
      CardCollector/Resources/ResultCode.cs

+ 2 - 2
CardCollector.sln.DotSettings.user

@@ -1,6 +1,6 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FCallbackQueryCommands/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FCallbackQueryCommands/@EntryIndexedValue">False</s:Boolean>
 	
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FMessageCommands/@EntryIndexedValue">False</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FMessageCommands/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FMessages/@EntryIndexedValue">False</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>

+ 18 - 24
CardCollector/Auction/AuctionController.cs

@@ -1,33 +1,27 @@
-using System.Diagnostics;
-using CardCollector.DataBase;
+using System.Threading.Tasks;
 using CardCollector.DataBase.Entity;
-using CardCollector.DataBase.EntityDao;
-using Microsoft.EntityFrameworkCore;
-using Telegram.Bot.Types;
+using CardCollector.Resources;
 
-namespace CardCollector.Auction
+namespace CardCollector.Auction 
 {
+    /* Контроллер аукциона, управляет всеми транзакциями
+     между пользователями */
     public static class AuctionController 
     {
-
-        private static async void SellCard(UserEntity user, string hashCode)
+        /*Метод используется для продажи стикеров на аукционе
+        user - пользователь, продающий стикер
+        stickerShortHashCode - MD5 хеш представляющий собой сумму id стикера и id пользователя, используется в словаре как ключ
+        price - цена за штуку
+        count - количество продаваемых стикеров*/
+        private static async Task<ResultCode> SellCard(UserEntity user, string stickerShortHashCode, int price, int count = 1)
         {
-            
-            var countStikers = user.Stickers[hashCode].Count;
-            var price = 0;//устанавливаем сумму за штуку 
-            var countForSell = 0;//тут как-то кнопками регулировать количество стикеров для продажи
-            if (countForSell > countStikers)
-            {
-                Debug.Fail("Ты шо, ебобо?");
-                //выводим сообщение о ошибке мол у тебя столько нету стикеров
-            }
-            else
-            {
-                var summa = price * countForSell;
-                //подтверждаем действие
-                user.Stickers[hashCode].Count -= countForSell;
-                user.Cash.Coins += summa;
-            }
+            if (count > user.Stickers[stickerShortHashCode].Count)
+                return ResultCode.NotEnoughStickers;
+            var summa = price * count;
+            //подтверждаем действие
+            user.Stickers[stickerShortHashCode].Count -= count;
+            user.Cash.Coins += summa;
+            return ResultCode.Ok;
         }
         
         private static async void BuyCard()

+ 22 - 23
CardCollector/Commands/CallbackQuery/CallbackQuery.cs

@@ -9,43 +9,42 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    using static Logs;
+    /* Родительский класс для входящих обновлений типа CallbackQuery (нажатие пользователем инлайн кнопки)
+     при наследовании укажите ключевое слово, содержащееся в запросе
+     для поля Command и определите логику действий в Execute
+     Также необходимо определить констуктор с параметрами UserEntity
+     и Update, наслеуемый от base(user, update)
+     И После реализации добавить команду в список List в этом классе
+     Для обработки команды определены следующие поля
+     User - пользователь, вызвавший команду
+     Update - обновление, полученное от сервера Телеграм */
     public abstract class CallbackQuery : UpdateModel
     {
-        
+        /* Список команд, распознаваемых ботом */
         private static readonly List<CallbackQuery> List = new()
         {
 
         };
 
+        /* Метод, создающий объекты команд исходя из полученного обновления */
         public static async Task<UpdateModel> Factory(Update update)
         {
-            try
-            {
-                // Текст команды
-                var command = update.CallbackQuery!.Data;
+            // Текст команды
+            var command = update.CallbackQuery!.Data;
 
-                // Объект пользователя
-                var user = await UserDao.GetUser(update.CallbackQuery.From);
+            // Объект пользователя
+            var user = await UserDao.GetUser(update.CallbackQuery.From);
 
-                // Добавляем сообщения пользователя в пул для удаления
-                MessageController.AddNewMessageToPool(user, update.CallbackQuery!.Message!.MessageId);
+            // Возвращаем объект, если команда совпала
+            foreach (var item in List.Where(item => item.IsMatches(command)))
+                if (Activator.CreateInstance(item.GetType(), user, update) is CallbackQuery executor && executor.IsMatches(command))
+                    return executor;
 
-                // Возвращаем объект, если команда совпала
-                foreach (var item in List.Where(item => item.IsMatches(command)))
-                    if (Activator.CreateInstance(item.GetType(), user, update) is CallbackQuery executor && executor.IsMatches(command))
-                        return executor;
-
-                // Возвращаем команда не найдена, если код дошел до сюда
-                return new CommandNotFound(user, update, command);
-            }
-            catch (Exception e)
-            {
-                LogOutError(e);
-                throw;
-            }
+            // Возвращаем команда не найдена, если код дошел до сюда
+            return new CommandNotFound(user, update, command);
         }
 
         protected CallbackQuery(UserEntity user, Update update) : base(user, update) { }
+        protected CallbackQuery() { }
     }
 }

+ 28 - 21
CardCollector/Commands/ChosenInlineResult/ChosenInlineResult.cs

@@ -8,39 +8,46 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.ChosenInlineResult
 {
+    /* Родительский класс для входящих обновлений типа ChosenInlineResult
+     (выбор пользователем инлайн команды)
+     при наследовании укажите ключевое слово, содержащееся в запросе
+     для поля Command и определите логику действий в Execute
+     Также необходимо определить констуктор с параметрами UserEntity,
+     Update и InlineResult, наслеуемый от base(user, update, inlineResult)
+     И После реализации добавить команду в список List в этом классе
+     Для обработки команды определены следующие поля
+     User - пользователь, вызвавший команду
+     Update - обновление, полученное от сервера Телеграм
+     InlineResult - результат входящего запроса */
     public abstract class ChosenInlineResult : UpdateModel
     {
+        /* Результат запроса (id выбранного пользователем элемента) */
         protected readonly string InlineResult = "";
         
+        /* Список команд */
         private static readonly List<ChosenInlineResult> List = new()
         {
+            // Обработка результата при отправке стикера
             new SendStickerResult(),
         };
         
+        /* Метод, создающий объекты команд исходя из полученного обновления */
         public static async Task<UpdateModel> Factory(Update update)
         {
-            try
-            {
-                // Текст команды
-                var command = update.ChosenInlineResult!.ResultId;
-                
-                // Объект пользователя
-                var user = await UserDao.GetUser(update.ChosenInlineResult!.From);
-                
-                // Возвращаем объект, если команда совпала
-                foreach (var item in List.Where(item => item.IsMatches(command)))
-                    if(Activator.CreateInstance(item.GetType(), 
-                        user, update, update.ChosenInlineResult.ResultId) is ChosenInlineResult executor)
-                        if (executor.IsMatches(command)) return executor;
+            // Текст команды
+            var command = update.ChosenInlineResult!.ResultId;
             
-                // Возвращаем команда не найдена, если код дошел до сюда
-                return new CommandNotFound(user, update, command);
-            }
-            catch (Exception e)
-            {
-                Logs.LogOutError(e);
-                throw;
-            }
+            // Объект пользователя
+            var user = await UserDao.GetUser(update.ChosenInlineResult!.From);
+            
+            // Возвращаем объект, если команда совпала
+            foreach (var item in List.Where(item => item.IsMatches(command)))
+                if(Activator.CreateInstance(item.GetType(), 
+                    user, update, update.ChosenInlineResult.ResultId) is ChosenInlineResult executor)
+                    if (executor.IsMatches(command)) return executor;
+        
+            // Возвращаем команда не найдена, если код дошел до сюда
+            return new CommandNotFound(user, update, command);
         }
 
         protected ChosenInlineResult(UserEntity user, Update update, string inlineResult)

+ 5 - 1
CardCollector/Commands/ChosenInlineResult/SendStickerResult.cs

@@ -4,19 +4,23 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.ChosenInlineResult
 {
+    /* Данная команда выполняется при отправке пользователем стикера */
     public class SendStickerResult : ChosenInlineResult
     {
+        /* Ключевое слово для данной команды send_sticker */
         protected override string Command => "send_sticker";
         public override Task Execute()
         {
+            // Получаем MD5 хеш из полученного запроса разделением по символу '='
             var shortHash = InlineResult.Split('=')[1];
+            // Вычитаем один стикер из общего их количества
             User.Stickers[shortHash].Count--;
+            // Возвращаем CompletedTask, означающий завершение данного метода
             return Task.CompletedTask;
         }
         
         public SendStickerResult(UserEntity user, Update update, string inlineResult)
             : base(user, update, inlineResult) { }
-
         public SendStickerResult() { }
     }
 }

+ 1 - 0
CardCollector/Commands/CommandNotFound.cs

@@ -5,6 +5,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands
 {
+    /* Данный класс реализует операцию "Команда не найдена" */
     public class CommandNotFound : UpdateModel
     {
         protected override string Command => "";

+ 1 - 0
CardCollector/Commands/IgnoreUpdate.cs

@@ -6,6 +6,7 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands
 {
+    /* Данный класс позволяет проигнорировать действие пользователя */
     public class IgnoreUpdate : UpdateModel
     {
         protected override string Command => "";

+ 27 - 21
CardCollector/Commands/InlineQuery/InlineQuery.cs

@@ -8,39 +8,45 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.InlineQuery
 {
+    /* Родительский класс для входящих обновлений типа InlineQuery (ввод пользователем @имя_бота)
+     при наследовании укажите ключевое слово, содержащееся в запросе
+     для поля Command и определите логику действий в Execute
+     Также необходимо определить констуктор с параметрами UserEntity,
+     Update и InlineQueryId, наслеуемый от base(user, update, inlineResult)
+     И После реализации добавить команду в список List в этом классе
+     Для обработки команды определены следующие поля
+     User - пользователь, вызвавший команду
+     Update - обновление, полученное от сервера Телеграм
+     InlineQueryId - id запроса для ответа на него */
     public abstract class InlineQuery : UpdateModel
     {
+        /* Id входящего запроса */
         protected readonly string InlineQueryId = "";
         
+        /* Список команд */
         private static readonly List<InlineQuery> List = new()
         {
+            // Показать стикеры в чатах для отправки (кроме личного чата с ботом)
             new ShowStickersInGroup(),
-            //new FilteredInlineQuery(),
         };
         
+        
+        /* Метод, создающий объекты команд исходя из полученного обновления */
         public static async Task<UpdateModel> Factory(Update update)
         {
-            try
-            {
-                // Текст команды
-                var command = $"{update.InlineQuery!.ChatType}={update.InlineQuery!.Query}";
-                
-                // Объект пользователя
-                var user = await UserDao.GetUser(update.InlineQuery!.From);
-                
-                // Возвращаем объект, если команда совпала
-                foreach (var item in List.Where(item => item.IsMatches(command)))
-                    if(Activator.CreateInstance(item.GetType(), user, update, update.InlineQuery.Id) is InlineQuery executor)
-                        if (executor.IsMatches(command)) return executor;
+            // Текст команды
+            var command = $"{update.InlineQuery!.ChatType}={update.InlineQuery!.Query}";
+            
+            // Объект пользователя
+            var user = await UserDao.GetUser(update.InlineQuery!.From);
             
-                // Возвращаем команда не найдена, если код дошел до сюда
-                return new CommandNotFound(user, update, command);
-            }
-            catch (Exception e)
-            {
-                Logs.LogOutError(e);
-                throw;
-            }
+            // Возвращаем объект, если команда совпала
+            foreach (var item in List.Where(item => item.IsMatches(command)))
+                if(Activator.CreateInstance(item.GetType(), user, update, update.InlineQuery.Id) is InlineQuery executor)
+                    if (executor.IsMatches(command)) return executor;
+        
+            // Возвращаем команда не найдена, если код дошел до сюда
+            return new CommandNotFound(user, update, command);
         }
 
         protected InlineQuery(UserEntity user, Update update, string inlineQueryId) : base(user, update)

+ 9 - 1
CardCollector/Commands/InlineQuery/ShowStickersInGroup.cs

@@ -5,17 +5,25 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.InlineQuery
 {
+    /* Отображение стикеров в чатах, кроме личной беседы с ботом */
     public class ShowStickersInGroup : InlineQuery
     {
+        /* Команда - пустая строка, поскольку пользователь может вводить любые слова
+         после @имя_бота, введенная фраза будет использоваться для фильтрации стикеров */
         protected override string Command => "";
         
         public override async Task Execute()
         {
+            // Фильтр - введенная пользователем фраза
             var filter = Update.InlineQuery!.Query;
+            // Получаем список стикеров
             var stickersList = await User.GetStickersList("send_sticker",filter);
-            await MessageController.AnswerInlineQuery(InlineQueryId, stickersList, "название");
+            // Посылаем пользователю ответ на его запрос
+            await MessageController.AnswerInlineQuery(InlineQueryId, stickersList);
         }
 
+        /* Команда пользователя удовлетворяет условию, если она вызвана
+         в беседе/канале/личных сообщениях (кроме личных сообщений с ботом) */
         protected override bool IsMatches(string command)
         {
             return command.Contains("Group") || command.Contains("Supergroup") || command.Contains("Private");

+ 30 - 26
CardCollector/Commands/Message/Message.cs

@@ -9,44 +9,48 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.Message
 {
-    using static Logs;
+    /* Родительский класс для входящих обновлений типа Message (обычное сообщение)
+     при наследовании укажите ключевое слово, содержащееся в тексте
+     для поля Command и определите логику действий в Execute
+     Также необходимо определить констуктор с параметрами UserEntity и
+     Update, наслеуемый от base(user, update)
+     И После реализации добавить команду в список List в этом классе
+     Для обработки команды определены следующие поля
+     User - пользователь, вызвавший команду
+     Update - обновление, полученное от сервера Телеграм */
     public abstract class Message : UpdateModel
     {
+        /* Список команд */
         private static readonly List<Message> List = new()
         {
+            // Команда "Профиль"
             new ProfileMessage(),
+            // Команда "/start"
             new StartMessage()
         };
 
+        /* Метод, создающий объекты команд исходя из полученного обновления */
         public static async Task<UpdateModel> Factory(Update update)
         {
-            try
-            {
-                // Объект пользователя
-                var user = await UserDao.GetUser(update.Message!.From);
-                
-                //Если сообщение не содержит текст
-                if (update.Message!.Text == null) return new IgnoreUpdate(user, update);
-                
-                // Текст команды
-                var command = update.Message!.Text;
+            // Объект пользователя
+            var user = await UserDao.GetUser(update.Message!.From);
             
-                // Добавляем сообщения пользователя в пул для удаления
-                MessageController.AddNewMessageToPool(user, update.Message.MessageId);
-                
-                // Возвращаем объект, если команда совпала
-                foreach (var item in List.Where(item => item.IsMatches(command)))
-                    if(Activator.CreateInstance(item.GetType(), user, update) is Message executor)
-                        if (executor.IsMatches(command)) return executor;
+            //Если сообщение не содержит текст
+            if (update.Message!.Text == null) return new IgnoreUpdate(user, update);
             
-                // Возвращаем команда не найдена, если код дошел до сюда
-                return new CommandNotFound(user, update, command);
-            }
-            catch (Exception e)
-            {
-                LogOutError(e);
-                throw;
-            }
+            // Текст команды
+            var command = update.Message!.Text;
+        
+            // Добавляем сообщения пользователя в пул для удаления
+            await MessageController.DeleteMessage(user, update.Message.MessageId);
+            
+            // Возвращаем объект, если команда совпала
+            foreach (var item in List.Where(item => item.IsMatches(command)))
+                if(Activator.CreateInstance(item.GetType(), user, update) is Message executor)
+                    if (executor.IsMatches(command)) return executor;
+        
+            // Возвращаем команда не найдена, если код дошел до сюда
+            return new CommandNotFound(user, update, command);
         }
 
         protected Message(UserEntity user, Update update) : base(user, update) { }

+ 13 - 12
CardCollector/Commands/Message/ProfileMessage.cs

@@ -1,27 +1,28 @@
 using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
 using Telegram.Bot.Types;
-using Telegram.Bot.Types.ReplyMarkups;
 
 namespace CardCollector.Commands.Message
 {
+    /* Команда "Профиль" Отображает профиль пользователя и его баланс */
     public class ProfileMessage : Message
     {
-        protected override string Command => "Профиль";
+        /* Для данной команды ключевое слово "Профиль" */
+        protected override string Command => MessageCommands.profile;
         public override async Task Execute()
         {
-            var keyboard = new InlineKeyboardMarkup(new[]
-                {
-                    InlineKeyboardButton.WithCallbackData("Собрать прибыль")
-                }
-            );
-            await MessageController.SendMessage(
-                User, 
+            /* Отправляем сообщение */
+            await MessageController.SendMessage(User, 
+                /* Имя пользователя */
                 $"{User.Username}\n" +
-                       $"Монеты: {User.Cash.Coins}\n" +
-                       $"Алмазы: {User.Cash.Gems}",
-                keyboard);
+                /* Количество монет */
+                $"Монеты: {User.Cash.Coins}\n" +
+                /* Количество алмазов */
+                $"Алмазы: {User.Cash.Gems}",
+                /* Клавиатура профиля */
+                Keyboard.ProfileKeyboard);
         }
         
         public ProfileMessage() { }

+ 6 - 8
CardCollector/Commands/Message/StartMessage.cs

@@ -1,23 +1,21 @@
 using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
 using Telegram.Bot.Types;
-using Telegram.Bot.Types.ReplyMarkups;
 
 namespace CardCollector.Commands.Message
 {
+    /* Обработка команды "/start" */
     public class StartMessage : Message
     {
-        protected override string Command => "/start";
+        /* */
+        protected override string Command => MessageCommands.start;
         
         public override async Task Execute()
         {
-            var keyboard = new ReplyKeyboardMarkup(new []
-            {
-                new KeyboardButton[] {"Профиль", "Коллекция"},
-                new KeyboardButton[] {"Магазин", "Аукцион"},
-            }) { ResizeKeyboard = true };
-            await MessageController.SendMessage(User,"Привет!", keyboard);
+            /* Отправляем пользователю сообщение со стандартной клавиатурой */
+            await MessageController.SendMessage(User,"Привет!", Keyboard.Menu);
         }
         
         public StartMessage(UserEntity user, Update update) : base(user, update) { }

+ 3 - 0
CardCollector/Commands/MyChatMember/MyChatMember.cs

@@ -6,6 +6,9 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.MyChatMember
 {
+    /* Родительский класс для входящих обновлений типа MyChatMember
+     (Добавление/Добавление в чаты/Добавление в каналы/Блокировки/Исключения бота)
+     Данный класс полностью реализован и не нуждается в наследовании */
     public class MyChatMember : UpdateModel
     {
         protected override string Command => "";

+ 7 - 0
CardCollector/Commands/UpdateModel.cs

@@ -4,6 +4,13 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands
 {
+    /* Реализация базового класса для обработки получаемых
+     с сервера Телеграм обновлений.
+     Поле User - пользователь, вызвавший команду 
+     Поле Update - полученное обновление
+     Поле Command - команда или же ключевое слово, по которому идентифицируется команда
+     Метод Execute - реализация логики команды во время ее выполнения 
+     Метод IsMatches - проверяет команду на сопадение по ключу */
     public abstract class UpdateModel
     {
         protected abstract string Command { get; }

+ 59 - 46
CardCollector/Controllers/MessageController.cs

@@ -22,35 +22,44 @@ namespace CardCollector.Controllers
 {
     using static Logs;
 
+    /* Данный класс управляет получением обновлений и отправкой сообщений */
     public static class MessageController
     {
-        private static readonly 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 Message.Factory(update),
+                    // Тип обновления - нажатие на инлайн кнопку
                     UpdateType.CallbackQuery => await CallBackQuery.Factory(update),
+                    // Тип обновления - блокировка/добавление бота
                     UpdateType.MyChatMember => await MyChatMember.Factory(update),
+                    // Тип обновления - вызов бота через @имя_бота
                     UpdateType.InlineQuery => await InlineQuery.Factory(update),
+                    // Тип обновления - выбор результата в инлайн меню
                     UpdateType.ChosenInlineResult => await ChosenInlineResult.Factory(update),
                     _ => throw new ArgumentOutOfRangeException()
                 };
+                // Обработать команду
                 await executor.Execute();
             }
             catch (Exception e)
             {
                 switch (e)
                 {
+                    // Случай, когда не определена обработка для данного типа обновленияот
                     case ArgumentOutOfRangeException:
                         LogOut(update.Type);
                         break;
+                    // Ошибка, полученная со стороны Telegram API
                     case ApiRequestException:
                         LogOutWarning(e.Message);
                         break;
+                    // Прочие ошибки
                     default:
                         LogOutError(e);
                         break;
@@ -58,6 +67,7 @@ namespace CardCollector.Controllers
             }
         }
 
+        // Обработка ошибок, полученных от сервера Телеграм
         public static Task HandleErrorAsync(ITelegramBotClient client, Exception e, CancellationToken ct)
         {
             switch (e)
@@ -72,34 +82,10 @@ namespace CardCollector.Controllers
             return Task.CompletedTask;
         }
 
-        public static void AddNewMessageToPool(UserEntity user, int messageId)
-        {
-            try
-            {
-                DeletingMessagePool[user.ChatId].Add(messageId);
-            }
-            catch (Exception)
-            {
-                DeletingMessagePool.Add(user.ChatId, new List<int>());
-                DeletingMessagePool[user.ChatId].Add(messageId);
-            }
-        }
-
-        public static async Task DeleteMessagesFromPool(UserEntity user)
-        {
-            try
-            {
-                foreach (var id in DeletingMessagePool[user.ChatId])
-                    await DeleteMessage(user, id);
-                DeletingMessagePool[user.ChatId].Clear();
-                DeletingMessagePool.Remove(user.ChatId);
-            }
-            catch (Exception)
-            {
-                /* ignore */
-            }
-        }
-
+        /* Метод для отправки сообщения
+         user - пользователь, которому необходимо отправить сообщение
+         message - текст сообщения
+         keyboard - клавиатура, которую надо добавить к сообщению */
         public static async Task<TgMessage> SendMessage(UserEntity user, string message, IReplyMarkup keyboard = null)
         {
             try
@@ -114,12 +100,16 @@ namespace CardCollector.Controllers
             return new TgMessage();
         }
         
-        public static async Task<TgMessage> SendTextWithHtml(UserEntity info, string message, IReplyMarkup keyboard = null)
+        /* Метод для отправки сообщения с разметкой html
+         user - пользователь, которому необходимо отправить сообщение
+         message - текст сообщения
+         keyboard - клавиатура, которую надо добавить к сообщению */
+        public static async Task<TgMessage> SendTextWithHtml(UserEntity user, string message, IReplyMarkup keyboard = null)
         {
             try
             {
-                if (!info.IsBlocked)
-                    return await Bot.Client.SendTextMessageAsync(info.ChatId, message, ParseMode.Html, replyMarkup: keyboard, disableNotification: true);
+                if (!user.IsBlocked)
+                    return await Bot.Client.SendTextMessageAsync(user.ChatId, message, ParseMode.Html, replyMarkup: keyboard, disableNotification: true);
             }
             catch (Exception e)
             {
@@ -128,12 +118,15 @@ namespace CardCollector.Controllers
             return new TgMessage();
         }
         
-        public static async Task<TgMessage> SendSticker(UserEntity info, string fileId)
+        /* Метод для отправки стикера
+         user - пользователь, которому необходимо отправить сообщение
+         fileId - id стикера, расположенного на серверах телеграм */
+        public static async Task<TgMessage> SendSticker(UserEntity user, string fileId)
         {
             try
             {
-                if (!info.IsBlocked)
-                    return await Bot.Client.SendStickerAsync(info.ChatId, fileId, true);
+                if (!user.IsBlocked)
+                    return await Bot.Client.SendStickerAsync(user.ChatId, fileId, true);
             }
             catch (Exception e)
             {
@@ -141,13 +134,18 @@ namespace CardCollector.Controllers
             }
             return new TgMessage();
         }
-
-        public static async Task<TgMessage> EditMessage(UserEntity info, int messageId, string message, InlineKeyboardMarkup keyboard = null)
+        
+        /* Метод для редактирования сообщения
+         user - пользователь, которому необходимо отредактировать сообщение
+         messageId - id сообщения
+         message - текст сообщения
+         keyboard - клавиатура, которую надо добавить к сообщению */
+        public static async Task<TgMessage> EditMessage(UserEntity user, int messageId, string message, InlineKeyboardMarkup keyboard = null)
         {
             try
             {
-                if (!info.IsBlocked)
-                    return await Bot.Client.EditMessageTextAsync(info.ChatId, messageId, message, replyMarkup: keyboard);
+                if (!user.IsBlocked)
+                    return await Bot.Client.EditMessageTextAsync(user.ChatId, messageId, message, replyMarkup: keyboard);
             }
             catch (Exception e)
             {
@@ -156,12 +154,16 @@ namespace CardCollector.Controllers
             return new TgMessage();
         }
 
-        public static async Task<TgMessage> EditReplyMarkup(UserEntity info, int messageId, InlineKeyboardMarkup keyboard)
+        /* Метод для редактирования клавиатуры под сообщением
+         user - пользователь, которому необходимо отредактировать сообщение
+         messageId - Id сообщения
+         keyboard - новая клавиатура, которую надо добавить к сообщению */
+        public static async Task<TgMessage> EditReplyMarkup(UserEntity user, int messageId, InlineKeyboardMarkup keyboard)
         {
             try
             {
-                if (!info.IsBlocked)
-                    return await Bot.Client.EditMessageReplyMarkupAsync(info.ChatId, messageId, keyboard);
+                if (!user.IsBlocked)
+                    return await Bot.Client.EditMessageReplyMarkupAsync(user.ChatId, messageId, keyboard);
             }
             catch (Exception e)
             {
@@ -170,6 +172,9 @@ namespace CardCollector.Controllers
             return new TgMessage();
         }
         
+        /* Метод для удаления сообщения
+         user - пользователь, которому необходимо удалить сообщение
+         messageId - Id сообщения */
         public static async Task DeleteMessage(UserEntity user, int messageId)
         {
             try
@@ -183,12 +188,17 @@ namespace CardCollector.Controllers
             }
         }
 
-        public static async Task<TgMessage> SendImage(UserEntity info, InputOnlineFile inputOnlineFile, string message = null, InlineKeyboardMarkup replyMarkup = null)
+        /* Метод для отправки изображения
+         user - пользователь, которому необходимо отправить сообщение
+         inputOnlineFile - фото, которое необходимо отправить
+         message - текст сообщения
+         keyboard - клавиатура, которую надо добавить к сообщению */
+        public static async Task<TgMessage> SendImage(UserEntity user, InputOnlineFile inputOnlineFile, string message = null, InlineKeyboardMarkup keyboard = null)
         {
             try
             {
-                if (!info.IsBlocked)
-                    return await Bot.Client.SendPhotoAsync(info.ChatId, inputOnlineFile, message, replyMarkup: replyMarkup, disableNotification: true);
+                if (!user.IsBlocked)
+                    return await Bot.Client.SendPhotoAsync(user.ChatId, inputOnlineFile, message, replyMarkup: keyboard, disableNotification: true);
             }
             catch (Exception e)
             {
@@ -197,6 +207,9 @@ namespace CardCollector.Controllers
             return new TgMessage();
         }
 
+        /* Метод для ответа на запрос @имя_бота
+         queryId - Id запроса
+         results - массив объектов InlineQueryResult */
         public static async Task AnswerInlineQuery(string queryId, IEnumerable<InlineQueryResult> results, string offset = null)
         {
             await Bot.Client.AnswerInlineQueryAsync(queryId, results, isPersonal: true, nextOffset: offset, cacheTime: Constants.INLINE_RESULTS_CACHE_TIME);

+ 9 - 2
CardCollector/DataBase/CardCollectorDatabase.cs

@@ -4,10 +4,17 @@ using Microsoft.EntityFrameworkCore;
 namespace CardCollector.DataBase
 {
     using static Resources.AppSettings;
+    
+    /* Предоставляет доступ к базе данных */
     public class CardCollectorDatabase : DbContext
     {
+        /* Скрываем конструктор, чтобы его нельзя было использовать извне */
         private CardCollectorDatabase() { }
+        
+        /* Объект базы данных */
         private static CardCollectorDatabase _instance;
+        
+        /* Предоставляет доступ к объекту */
         public static CardCollectorDatabase Instance
         {
             get
@@ -19,14 +26,14 @@ namespace CardCollector.DataBase
             }
         }
         
-        // Таблицы базы данных, представленные Entity объектами
+        /* Таблицы базы данных, представленные Entity объектами */
         public DbSet<UserEntity> Users { get; set; }
         public DbSet<CashEntity> CashTable { get; set; }
         public DbSet<UserStickerRelationEntity> UserStickerRelations { get; set; }
         public DbSet<StickerEntity> Stickers { get; set; }
 
         
-        // Конфигурация подключения к БД
+        /* Конфигурация подключения к БД */
         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
         {
             optionsBuilder.UseMySQL(

+ 7 - 0
CardCollector/DataBase/Entity/CashEntity.cs

@@ -3,12 +3,19 @@ using System.ComponentModel.DataAnnotations.Schema;
 
 namespace CardCollector.DataBase.Entity
 {
+    /* Объект таблицы cash (одна строка)
+     В таблице хранится Id пользователя, количество монет и алмазов*/
     [Table("cash")]
     public class CashEntity
     {
+        /* Id пользователя */
         [Key]
         [Column("user_id"), MaxLength(127)] public long UserId { get; set; }
+        
+        /* Количество монет */
         [Column("coins"), MaxLength(32)] public int Coins { get; set; } = 0;
+        
+        /* Количество алмазов */
         [Column("gems"), MaxLength(32)] public int Gems { get; set; } = 0;
     }
 }

+ 21 - 1
CardCollector/DataBase/Entity/StickerEntity.cs

@@ -3,17 +3,37 @@ using System.ComponentModel.DataAnnotations.Schema;
 
 namespace CardCollector.DataBase.Entity
 {
+    /* Объект таблицы stickers (одна строка)
+     Здесь хранится Id стикера с серверов Telegram
+     Наименование стикера, Автор стикера, Доход в монетах,
+     Доход в алмазах, Количество звезд, Эмоции связанные со стикером,
+     Описание стикера
+     */
     [Table("stickers")]
     public class StickerEntity
     {
+        /* Id стикера на сервере Телеграм */
         [Column("id"), MaxLength(127)] public string Id { get; set; }
+        
+        /* Название стикера */
         [Column("title"), MaxLength(256)] public string Title { get; set; }
+        
+        /* Автор стикера */
         [Column("author"), MaxLength(128)] public string Author { get; set; }
-
+        
+        /* Доход от стикера в монетах */
         [Column("income_coins"), MaxLength(32)] public int IncomeCoins { get; set; } = 0;
+        
+        /* Доход от стикера в алмазах */
         [Column("income_gems"), MaxLength(32)] public int IncomeGems { get; set; } = 0;
+        
+        /* Количество звезд стикера (редкость) */
         [Column("tier"), MaxLength(32)] public int Tier { get; set; } = 1;
+        
+        /* Эмоции, связанные со стикером */
         [Column("emoji"), MaxLength(127)] public string Emoji { get; set; } = "";
+        
+        /* Описание стикера */
         [Column("description"), MaxLength(1024)] public string Description { get; set; } = "";
     }
 }

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

@@ -8,18 +8,30 @@ using Telegram.Bot.Types.InlineQueryResults;
 
 namespace CardCollector.DataBase.Entity
 {
+    /* Этот класс представляет собой строку таблицы users и описывает объект пользователя */
     [Table("users")]
     public class UserEntity
     {
+        /* Id пользователя */
         [Key]
         [Column("id"), MaxLength(127)] public long Id { get; set; }
+        
+        /* Id чата */
         [Column("chat_id"), MaxLength(127)] public long ChatId { get; set; }
+        
+        /* Имя пользователя */
         [Column("username"), MaxLength(256)] public string Username { get; set; }
+        
+        /* Заблокирован ли пользователь */
         [Column("is_blocked"), MaxLength(11)] public bool IsBlocked { get; set; }
         
+        /* Счет пользователя */
         [NotMapped] public CashEntity Cash { get; set; }
+        
+        /* Стикеры пользователя */
         [NotMapped] public Dictionary<string, UserStickerRelationEntity> Stickers { get; set; }
         
+        /* Возвращает стикеры в виде объектов телеграм */
         public async Task<IEnumerable<InlineQueryResult>> GetStickersList(string command, string filter)
         {
             var result = new List<InlineQueryResult>();

+ 10 - 0
CardCollector/DataBase/Entity/UserStickerRelationEntity.cs

@@ -3,13 +3,23 @@ using System.ComponentModel.DataAnnotations.Schema;
 
 namespace CardCollector.DataBase.Entity
 {
+    /* Явялется расширенной связью многий ко многим между таблицами пользователей и стикеров */
     [Table("user_to_stickers_relations")]
     public class UserStickerRelationEntity
     {
+        /* Id записи в таблице, роли не играет */
         [Column("id"), MaxLength(127)] public long Id { get; set; }
+        
+        /* Id стикера на серверах Телеграм */
         [Column("sticker_id"), MaxLength(127)] public string StickerId { get; set; }
+        
+        /* Id пользователя */
         [Column("user_id"), MaxLength(127)] public long UserId { get; set; }
+        
+        /* Количество стикеров данного вида у пользователя */
         [Column("count"), MaxLength(32)] public int Count { get; set; }
+        
+        /* MD5 хеш id стикера и пользователя */
         [Column("short_hash"), MaxLength(40)] public string ShortHash { get; set; }
     }
 }

+ 4 - 0
CardCollector/DataBase/EntityDao/CashDao.cs

@@ -4,16 +4,20 @@ using Microsoft.EntityFrameworkCore;
 
 namespace CardCollector.DataBase.EntityDao
 {
+    /* Класс, позволяющий получить доступ к объектам таблицы Cash*/
     public static class CashDao
     {
+        /* Таблица cash в представлении EntityFramework */
         private static readonly DbSet<CashEntity> Table = CardCollectorDatabase.Instance.CashTable;
         
+        /* Получение объекта по Id */
         public static async Task<CashEntity> GetById(long userId)
         {
             var user = await Table.FindAsync(userId);
             return user ?? await AddNew(userId);
         }
 
+        /* Добавление нового объекта в систему */
         private static async Task<CashEntity> AddNew(long userId)
         {
             var cash = new CashEntity { UserId = userId };

+ 12 - 0
CardCollector/DataBase/EntityDao/StickerDao.cs

@@ -4,15 +4,27 @@ using Microsoft.EntityFrameworkCore;
 
 namespace CardCollector.DataBase.EntityDao
 {
+    /* Класс, предоставляющий доступ к объектам таблицы Stickers*/
     public static class StickerDao
     {
+        /* Таблица stickers в представлении Entity Framework */
         private static readonly DbSet<StickerEntity> Table = CardCollectorDatabase.Instance.Stickers;
         
+        /* Получение информации о стикере по его Id, возвращает Null, если стикера не существует */
         public static async Task<StickerEntity> GetStickerInfo(string stickerId)
         {
             return await Table.FindAsync(stickerId);
         }
 
+         /* Добавление новго стикера в систему
+         fileId - id стикера на сервере telegram
+         title - название стикера
+         author - автор
+         incomeCoins - прибыль стикера в монетах / минуту
+         incomeGems - прибыль стикера в алзмазах / минуту
+         tier - количество звезд стикера
+         emoji - эмоции, связанные со стикером
+         description - описание стикера */
         private static async Task<StickerEntity> AddNew(string fileId, string title, string author,
             int incomeCoins = 0, int incomeGems = 0, int tier = 1, string emoji = "", string description = "")
 

+ 13 - 3
CardCollector/DataBase/EntityDao/UserDao.cs

@@ -6,33 +6,43 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.DataBase.EntityDao
 {
+    /* Класс, предоставляющий доступ к объектам пользователей таблицы Users */
     public static class UserDao
     {
+        /* Таблица Users в представлении EntityFramework */
         private static readonly DbSet<UserEntity> Table = CardCollectorDatabase.Instance.Users;
+        
+        /* Активные пользователи в системе */
         private static readonly Dictionary<long, UserEntity> ActiveUsers = new();
 
+        /* Получение пользователя по представлению user из Базы данных */
         public static async Task<UserEntity> GetUser(User user)
         {
             UserEntity result;
             try
             {
+                /* Пытаемся получить пользователя из списка активных */
                 result = ActiveUsers[user.Id];
             }
             catch
             {
+                /* Ищем пользователя в базе данных или добавляем нового, если не найден*/
                 result = await Table.FindAsync(user.Id) ?? await AddNew(user);
                 
-                // Build user object
+                /* Собираем объект пользователя */
                 result.Cash = await CashDao.GetById(user.Id);
                 result.Stickers = await UserStickerRelationDao.GetListById(user.Id);
                 
-                // Add to avoid database fetching
+                /* Добавляем пользователя в список активных, чтобы не обращаться к бд лишний раз */
                 ActiveUsers.Add(user.Id, result);
             }
+            /* Обновляем имя пользователя, если он его сменил */
+            result.Username = user.Username;
             return result;
         }
 
-        public static async Task<UserEntity> AddNew(User user)
+        /* Добавление новго пользователя в систему */
+        private static async Task<UserEntity> AddNew(User user)
         {
             var userEntity = new UserEntity
             {

+ 4 - 5
CardCollector/DataBase/EntityDao/UserStickerRelationDao.cs

@@ -6,10 +6,13 @@ using Microsoft.EntityFrameworkCore;
 
 namespace CardCollector.DataBase.EntityDao
 {
+    /* Предоставляет доступ к соотношениям таблицы user_to_sticker_relation */
     public static class UserStickerRelationDao
     {
+        /* Таблица user_to_sticker_relation в представлении Entity Framework */
         private static readonly DbSet<UserStickerRelationEntity> Table = CardCollectorDatabase.Instance.UserStickerRelations;
         
+        /* Возвращает словарь стикеров по Id пользователя */
         public static async Task<Dictionary<string, UserStickerRelationEntity>> GetListById(long userId)
         {
             var result = await Table
@@ -18,11 +21,7 @@ namespace CardCollector.DataBase.EntityDao
             return result;
         }
 
-        public static async Task<UserStickerRelationEntity> GetByShortHash(string shortHash)
-        {
-            return await Table.FirstAsync(i => i.ShortHash == shortHash);
-        }
-
+        /* Добавляет новое отношение в таблицу */
         private static async Task<UserStickerRelationEntity> AddNew(long userId, string stickerId, int count)
         {
             var cash = new UserStickerRelationEntity

+ 9 - 0
CardCollector/Resources/CallbackQueryCommands.Designer.cs

@@ -59,5 +59,14 @@ namespace CardCollector.Resources {
                 resourceCulture = value;
             }
         }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Собрать прибыль.
+        /// </summary>
+        internal static string collect_income {
+            get {
+                return ResourceManager.GetString("collect_income", resourceCulture);
+            }
+        }
     }
 }

+ 3 - 0
CardCollector/Resources/CallbackQueryCommands.resx

@@ -18,4 +18,7 @@
     <resheader name="writer">
         <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     </resheader>
+    <data name="collect_income" xml:space="preserve">
+        <value>Собрать прибыль</value>
+    </data>
 </root>

+ 22 - 0
CardCollector/Resources/Keyboard.cs

@@ -0,0 +1,22 @@
+using Telegram.Bot.Types.ReplyMarkups;
+
+namespace CardCollector.Resources
+{
+    /* В данном классе содержатся все клавиатуры, используемые в проекте */
+    public static class Keyboard
+    {
+        /* Клавиатура, отображаемая вместе с сообщением профиля */
+        public static readonly InlineKeyboardMarkup ProfileKeyboard = new (new[]
+            {
+                InlineKeyboardButton.WithCallbackData(CallbackQueryCommands.collect_income)
+            }
+        );
+        
+        /* Клавиатура, отображаемая с первым сообщением пользователя */
+        public static readonly ReplyKeyboardMarkup Menu = new (new []
+        {
+            new KeyboardButton[] { MessageCommands.profile, MessageCommands.collection },
+            new KeyboardButton[] { MessageCommands.shop, MessageCommands.auction },
+        }) { ResizeKeyboard = true };
+    }
+}

+ 45 - 0
CardCollector/Resources/MessageCommands.Designer.cs

@@ -59,5 +59,50 @@ namespace CardCollector.Resources {
                 resourceCulture = value;
             }
         }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Аукцион.
+        /// </summary>
+        internal static string auction {
+            get {
+                return ResourceManager.GetString("auction", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Коллекция.
+        /// </summary>
+        internal static string collection {
+            get {
+                return ResourceManager.GetString("collection", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Профиль.
+        /// </summary>
+        internal static string profile {
+            get {
+                return ResourceManager.GetString("profile", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Магазин.
+        /// </summary>
+        internal static string shop {
+            get {
+                return ResourceManager.GetString("shop", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to /start.
+        /// </summary>
+        internal static string start {
+            get {
+                return ResourceManager.GetString("start", resourceCulture);
+            }
+        }
     }
 }

+ 15 - 0
CardCollector/Resources/MessageCommands.resx

@@ -18,4 +18,19 @@
     <resheader name="writer">
         <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
     </resheader>
+    <data name="profile" xml:space="preserve">
+        <value>Профиль</value>
+    </data>
+    <data name="collection" xml:space="preserve">
+        <value>Коллекция</value>
+    </data>
+    <data name="shop" xml:space="preserve">
+        <value>Магазин</value>
+    </data>
+    <data name="auction" xml:space="preserve">
+        <value>Аукцион</value>
+    </data>
+    <data name="start" xml:space="preserve">
+        <value>/start</value>
+    </data>
 </root>

+ 13 - 0
CardCollector/Resources/ResultCode.cs

@@ -0,0 +1,13 @@
+namespace CardCollector.Resources
+{
+    /*Перечисление всех кодов возможных ошибок,
+    Исходя из возвращаемого кода будет выведено соответствующее сообщение*/
+    public enum ResultCode
+    {
+        // Недостаточное количество стикеров
+        NotEnoughStickers,
+        
+        // Действие завершилось без ошибок
+        Ok,
+    }
+}