Browse Source

Refactore code. Split session to dynamic modules

Tigran 3 years ago
parent
commit
77dc0dd476
59 changed files with 749 additions and 759 deletions
  1. 5 1
      CardCollector.sln.DotSettings.user
  2. 3 2
      CardCollector/Commands/CallbackQuery/BackToCombine.cs
  3. 26 8
      CardCollector/Commands/CallbackQuery/BackToFiltersMenu.cs
  4. 0 21
      CardCollector/Commands/CallbackQuery/BackToStickerQuery.cs
  5. 18 52
      CardCollector/Commands/CallbackQuery/BuyStickerQuery.cs
  6. 0 2
      CardCollector/Commands/CallbackQuery/CallbackQuery.cs
  7. 22 2
      CardCollector/Commands/CallbackQuery/CancelCallback.cs
  8. 2 3
      CardCollector/Commands/CallbackQuery/CollectIncomeQuery.cs
  9. 20 11
      CardCollector/Commands/CallbackQuery/CombineCallback.cs
  10. 14 12
      CardCollector/Commands/CallbackQuery/CombineStickers.cs
  11. 5 9
      CardCollector/Commands/CallbackQuery/ConfirmBuyingQuery.cs
  12. 6 5
      CardCollector/Commands/CallbackQuery/ConfirmationSellingQuery.cs
  13. 32 15
      CardCollector/Commands/CallbackQuery/CountQuery.cs
  14. 7 5
      CardCollector/Commands/CallbackQuery/DeleteCombine.cs
  15. 2 1
      CardCollector/Commands/CallbackQuery/PriceCallback.cs
  16. 5 5
      CardCollector/Commands/CallbackQuery/PutForAuctionQuery.cs
  17. 2 1
      CardCollector/Commands/CallbackQuery/SetFilterCallback.cs
  18. 29 6
      CardCollector/Commands/ChosenInlineResult/SelectStickerInlineResult.cs
  19. 7 7
      CardCollector/Commands/ChosenInlineResult/SelectTraderResult.cs
  20. 10 5
      CardCollector/Commands/InlineQuery/ShowAuctionStickers.cs
  21. 4 3
      CardCollector/Commands/InlineQuery/ShowCollectionStickers.cs
  22. 5 4
      CardCollector/Commands/InlineQuery/ShowCombineStickers.cs
  23. 3 2
      CardCollector/Commands/InlineQuery/ShowShopStickers.cs
  24. 8 4
      CardCollector/Commands/InlineQuery/ShowTradersInBotChat.cs
  25. 2 4
      CardCollector/Commands/Message/DocumentMessage/UploadFileMessage.cs
  26. 2 4
      CardCollector/Commands/Message/Message.cs
  27. 0 56
      CardCollector/Commands/Message/TextMessage/EnterCoinsPriceMessage.cs
  28. 4 2
      CardCollector/Commands/Message/TextMessage/EnterEmojiMessage.cs
  29. 6 5
      CardCollector/Commands/Message/TextMessage/EnterGemsPriceMessage.cs
  30. 2 2
      CardCollector/Commands/Message/TextMessage/ProfileMessage.cs
  31. 1 2
      CardCollector/Commands/Message/TextMessage/ShopMessage.cs
  32. 25 3
      CardCollector/Commands/Message/TextMessage/ShowFiltersMenu.cs
  33. 11 41
      CardCollector/Controllers/AuctionController.cs
  34. 0 9
      CardCollector/Controllers/ShopController.cs
  35. 22 12
      CardCollector/DataBase/Entity/AuctionEntity.cs
  36. 40 1
      CardCollector/DataBase/Entity/CashEntity.cs
  37. 13 11
      CardCollector/DataBase/Entity/StickerEntity.cs
  38. 1 1
      CardCollector/DataBase/Entity/UserEntity.cs
  39. 2 0
      CardCollector/DataBase/Entity/UserStickerRelationEntity.cs
  40. 2 10
      CardCollector/DataBase/EntityDao/AuctionDao.cs
  41. 5 0
      CardCollector/DataBase/EntityDao/CashDao.cs
  42. 5 25
      CardCollector/DataBase/EntityDao/StickerDao.cs
  43. 3 0
      CardCollector/DataBase/EntityDao/UserDao.cs
  44. 20 94
      CardCollector/Extensions.cs
  45. 0 163
      CardCollector/Others/Session.cs
  46. 0 54
      CardCollector/Others/StickerInfo.cs
  47. 0 19
      CardCollector/Others/TraderInformation.cs
  48. 31 55
      CardCollector/Resources/Keyboard.cs
  49. 9 0
      CardCollector/Resources/Messages.Designer.cs
  50. 3 0
      CardCollector/Resources/Messages.resx
  51. 2 0
      CardCollector/Resources/UserState.cs
  52. 7 0
      CardCollector/Session/Module.cs
  53. 21 0
      CardCollector/Session/Modules/AuctionModule.cs
  54. 18 0
      CardCollector/Session/Modules/CollectionModule.cs
  55. 42 0
      CardCollector/Session/Modules/CombineModule.cs
  56. 13 0
      CardCollector/Session/Modules/DefaultModule.cs
  57. 110 0
      CardCollector/Session/Modules/FiltersModule.cs
  58. 13 0
      CardCollector/Session/Modules/ShopModule.cs
  59. 79 0
      CardCollector/Session/Session.cs

+ 5 - 1
CardCollector.sln.DotSettings.user

@@ -1,4 +1,8 @@
 <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:String x:Key="/Default/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">WARNING</s:String>
+	<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/FiltersState/=CodeStyle/@EntryIndexedValue">On</s:String>
+	<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/FiltersState/=NamingFilter/@EntryIndexedValue">On</s:String>
+	<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/FiltersState/=SpellingFilter/@EntryIndexedValue">On</s:String>
 	<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;&#xD;
   &lt;Assembly Path="C:\Users\DarkGolly\.nuget\packages\telegram.bot\17.0.0-alpha.3\lib\netcoreapp3.1\Telegram.Bot.dll" /&gt;&#xD;
 &lt;/AssemblyExplorer&gt;</s:String>
@@ -8,7 +12,7 @@
 	
 	
 	
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FMessages/@EntryIndexedValue">False</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FMessages/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FSortingTypes/@EntryIndexedValue">False</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FText/@EntryIndexedValue">True</s:Boolean>
 	

+ 3 - 2
CardCollector/Commands/CallbackQuery/BackToCombine.cs

@@ -2,6 +2,7 @@
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -12,8 +13,8 @@ namespace CardCollector.Commands.CallbackQuery
         public override async Task Execute()
         {
             await User.ClearChat();
-            var message = await MessageController.SendMessage(User, User.Session.GetCombineMessage(), 
-                Keyboard.GetCombineKeyboard(User.Session));
+            var combineModule = User.Session.GetModule<CombineModule>();
+            var message = await MessageController.SendMessage(User, combineModule.ToString(), Keyboard.GetCombineKeyboard(combineModule));
             User.Session.Messages.Add(message.MessageId);
         }
 

+ 26 - 8
CardCollector/Commands/CallbackQuery/BackToFiltersMenu.cs

@@ -3,6 +3,7 @@ using CardCollector.Commands.Message.TextMessage;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -14,16 +15,33 @@ namespace CardCollector.Commands.CallbackQuery
         {
             /* Удаляем пользователя из очереди */
             EnterEmojiMessage.RemoveFromQueue(User.Id);
-            User.Session.SelectedSticker = null;
-            User.Session.CombineList.Clear();
-            /* Очищаем чат, если был передан параметр очистки */
+            switch (User.Session.State)
+            {
+                case UserState.CombineMenu:
+                    User.Session.State = UserState.CollectionMenu;
+                    User.Session.DeleteModule<CombineModule>();
+                    break;
+                case UserState.CollectionMenu:
+                    User.Session.GetModule<CollectionModule>().Reset();
+                    break;
+                case UserState.ProductMenu:
+                    User.Session.State = UserState.AuctionMenu;
+                    User.Session.GetModule<AuctionModule>().Reset();
+                    break;
+                case UserState.ShopMenu:
+                    User.Session.GetModule<ShopModule>().Reset();
+                    break;
+                case UserState.AuctionMenu:
+                    User.Session.GetModule<AuctionModule>().Reset();
+                    break;
+            }
             await User.ClearChat();
             /* Формируем сообщение с имеющимися фильтрами у пользователя */
-            var text = User.Session.Filters.ToMessage(User.Session.State);
-            /* Редактируем сообщение */
-            var message = await MessageController.EditMessage(User, CallbackMessageId, 
-                    text, Keyboard.GetSortingMenu(User.Session.State));
-            if (!User.Session.Messages.Contains(message.MessageId)) User.Session.Messages.Add(message.MessageId);
+            var filtersModule = User.Session.GetModule<FiltersModule>();
+            var text = filtersModule.ToString(User.Session.State);
+            /* Отправляем сообщение */
+            var message = await MessageController.SendMessage(User, text, Keyboard.GetSortingMenu(User.Session.State));
+            User.Session.Messages.Add(message.MessageId);
         }
         
         public BackToFiltersMenu() { }

+ 0 - 21
CardCollector/Commands/CallbackQuery/BackToStickerQuery.cs

@@ -1,21 +0,0 @@
-using System.Threading.Tasks;
-using CardCollector.Controllers;
-using CardCollector.DataBase.Entity;
-using CardCollector.Resources;
-using Telegram.Bot.Types;
-
-namespace CardCollector.Commands.CallbackQuery
-{
-    public class BackToStickerQuery : CallbackQuery
-    {
-        protected override string CommandText => Command.back_to_sticker;
-        public override async Task Execute()
-        {
-            var sticker = User.Session.SelectedSticker;
-            await MessageController.EditMessage(User, CallbackMessageId, sticker.ToString(), Keyboard.GetStickerKeyboard(User.Session));
-        }
-
-        public BackToStickerQuery() { }
-        public BackToStickerQuery(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 18 - 52
CardCollector/Commands/CallbackQuery/BuyStickerQuery.cs

@@ -1,9 +1,9 @@
-using System;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -14,59 +14,25 @@ namespace CardCollector.Commands.CallbackQuery
 
         public override async Task Execute()
         {
-            try
+            var auctionModule = User.Session.GetModule<AuctionModule>();
+            if (auctionModule.Count > auctionModule.MaxCount)
             {
-                var selectedSticker = User.Session.SelectedSticker;
-                var count = User.Session.State switch
-                {
-                    UserState.AuctionMenu => await AuctionController.GetStickerCount(selectedSticker.TraderInfo.Id),
-                    UserState.ShopMenu => await ShopController.GetStickerCount(selectedSticker.Id),
-                    _ => 0
-                };
-                var coinsPrice = selectedSticker.GetCoinsPrice();
-                var gemsPrice = selectedSticker.GetGemsPrice();
-                if (count < selectedSticker.Count && count != -1)
-                {
-                    await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id,
-                        Messages.not_enougth_stickers);
-                    await new BackToFiltersMenu(User, Update).Execute();
-                }
-                else if (coinsPrice > User.Cash.Coins)
-                    await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id,
-                        Messages.not_enougth_coins);
-                else if (gemsPrice > User.Cash.Gems)
-                    await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id,
-                        Messages.not_enougth_gems);
-                else
-                {
-                    switch (User.Session.State)
-                    {
-                        case UserState.AuctionMenu:
-                            await AuctionController.BuyCard(selectedSticker);
-                            break;
-                        case UserState.ShopMenu:
-                            await ShopController.SoldCard(selectedSticker);
-                            break;
-                    }
-
-                    if (User.Stickers.ContainsKey(selectedSticker.Md5Hash))
-                    {
-                        await User.Session.PayOutOne(selectedSticker.Md5Hash);
-                        await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id,
-                            $"{Messages.you_collected} {User.Session.IncomeCoins}{Text.coin} / {User.Session.IncomeGems}{Text.gem}");
-                    }
-
-                    await UserStickerRelationDao.AddNew(User, selectedSticker, selectedSticker.Count);
-                    User.Cash.Coins -= coinsPrice;
-                    User.Cash.Gems -= gemsPrice;
-                    User.Session.SelectedSticker = null;
-                    await User.ClearChat();
-                }
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_stickers);
+                await new BackToFiltersMenu(User, Update).Execute();
             }
-            catch (Exception)
+            else if (auctionModule.Price * auctionModule.Count > User.Cash.Gems)
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_gems);
+            else
             {
-                await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id, Messages.not_enougth_stickers);
-                await new BackToFiltersMenu(User, Update).Execute();
+                await auctionModule.SelectedPosition.BuyCard(auctionModule.Count);
+                if (User.Stickers.ContainsKey(auctionModule.SelectedSticker.Md5Hash))
+                    await MessageController.AnswerCallbackQuery(User, CallbackQueryId, 
+                        $"{Messages.you_collected} {await User.Cash.Payout(User.Stickers[auctionModule.SelectedSticker.Md5Hash])}{Text.coin}");
+                else
+                    await UserStickerRelationDao.AddNew(User, auctionModule.SelectedSticker, auctionModule.Count);
+                User.Cash.Gems -= auctionModule.Price * auctionModule.Count;
+                User.Session.ResetModule<AuctionModule>();
+                await User.ClearChat();
             }
         }
 

+ 0 - 2
CardCollector/Commands/CallbackQuery/CallbackQuery.cs

@@ -55,8 +55,6 @@ namespace CardCollector.Commands.CallbackQuery
             new PutForAuctionQuery(),
             /* Команда для покупки стикера */
             new BuyStickerQuery(),
-            /* Команда возврата к стикеру */
-            new BackToStickerQuery(),
             /* Команда возврата к комбинированию */
             new BackToCombine(),
             /* Команда добавления к комбинироавнию */

+ 22 - 2
CardCollector/Commands/CallbackQuery/CancelCallback.cs

@@ -2,6 +2,7 @@
 using CardCollector.Commands.Message.TextMessage;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -11,10 +12,29 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.cancel;
         public override async Task Execute()
         {
+            switch (User.Session.State)
+            {
+                case UserState.CollectionMenu:
+                    User.Session.DeleteModule<CollectionModule>();
+                    break;
+                case UserState.ShopMenu:
+                    User.Session.DeleteModule<ShopModule>();
+                    break;
+                case UserState.AuctionMenu:
+                    User.Session.DeleteModule<AuctionModule>();
+                    break;
+                case UserState.CombineMenu:
+                    User.Session.DeleteModule<CombineModule>();
+                    break;
+                case UserState.ProductMenu:
+                    User.Session.DeleteModule<AuctionModule>();
+                    break;
+                case UserState.Default:
+                    User.Session.GetModule<DefaultModule>().Reset();
+                    break;
+            }
             User.Session.State = UserState.Default;
-            User.Session.SelectedSticker = null;
             EnterEmojiMessage.RemoveFromQueue(User.Id);
-            EnterCoinsPriceMessage.RemoveFromQueue(User.Id);
             EnterGemsPriceMessage.RemoveFromQueue(User.Id);
             await User.ClearChat();
         }

+ 2 - 3
CardCollector/Commands/CallbackQuery/CollectIncomeQuery.cs

@@ -11,11 +11,10 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.collect_income;
         public override async Task Execute()
         {
-            await User.Session.PayOut();
+            var result = await User.Cash.Payout(User.Stickers);
             await MessageController.AnswerCallbackQuery(User, CallbackQueryId, 
                 $"{Messages.you_collected}: " +
-                $"{User.Session.IncomeCoins}{Text.coin} " +
-                $"{User.Session.IncomeGems}{Text.gem}" +
+                $"{result}{Text.coin} " +
                 $"\n\n{Messages.your_cash}: " +
                 $"{User.Cash.Coins}{Text.coin} " +
                 $"{User.Cash.Gems}{Text.gem}", true);

+ 20 - 11
CardCollector/Commands/CallbackQuery/CombineCallback.cs

@@ -2,6 +2,7 @@
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -11,27 +12,35 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.combine;
         public override async Task Execute()
         {
-            var selectedSticker = User.Session.SelectedSticker;
-            var combineCount = User.Session.GetCombineCount();
+            User.Session.State = UserState.CombineMenu;
+            var combineModule = User.Session.GetModule<CombineModule>();
+            if (combineModule.SelectedSticker == null)
+            {
+                var collectionModule = User.Session.GetModule<CollectionModule>();
+                combineModule.SelectedSticker = collectionModule.SelectedSticker;
+                combineModule.Count = collectionModule.Count;
+            }
+            var selectedSticker = combineModule.SelectedSticker;
+            var combineCount = combineModule.GetCombineCount();
             if (combineCount == Constants.COMBINE_COUNT)
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.cant_combine, true);
             else
             {
-                if (combineCount + selectedSticker.Count > Constants.COMBINE_COUNT)
+                if (combineCount + combineModule.Count > Constants.COMBINE_COUNT)
                 {
-                    selectedSticker.Count = Constants.COMBINE_COUNT - combineCount;
+                    combineModule.Count = Constants.COMBINE_COUNT - combineCount;
                     await MessageController.AnswerCallbackQuery(User, CallbackQueryId, $"{Messages.combine_added_only} " +
-                        $"{selectedSticker.Count}{Text.items}", true);
+                        $"{combineModule.Count}{Text.items}", true);
                 }
-                if (User.Session.CombineList.ContainsKey(selectedSticker.Md5Hash))
+                if (combineModule.CombineList.ContainsKey(selectedSticker))
                 {
-                    var combineSticker = User.Session.CombineList[selectedSticker.Md5Hash];
-                    if (selectedSticker.MaxCount < combineSticker.Count + selectedSticker.Count)
-                        User.Session.CombineList[selectedSticker.Md5Hash].Count = selectedSticker.Count;
+                    var maxCount = User.Stickers[selectedSticker.Md5Hash].Count;
+                    if (maxCount < combineModule.CombineList[selectedSticker] + combineModule.Count)
+                        combineModule.CombineList[selectedSticker] = combineModule.Count;
                     else
-                        User.Session.CombineList[selectedSticker.Md5Hash].Count += selectedSticker.Count;
+                        combineModule.CombineList[selectedSticker] += combineModule.Count;
                 }
-                else User.Session.CombineList.Add(selectedSticker.Md5Hash, selectedSticker);
+                else combineModule.CombineList.Add(selectedSticker, combineModule.Count);
             }
             await new BackToCombine(User, Update).Execute();
         }

+ 14 - 12
CardCollector/Commands/CallbackQuery/CombineStickers.cs

@@ -5,6 +5,7 @@ using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -14,32 +15,33 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.combine_stickers;
         public override async Task Execute()
         {
-            if (User.Cash.Coins < User.Session.CombineCoinsPrice)
+            var combineModule = User.Session.GetModule<CombineModule>();
+            var price = combineModule.CalculateCombinePrice();
+            if (User.Cash.Coins < price)
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_coins);
-            else if (User.Cash.Gems < User.Session.CombineGemsPrice)
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_gems);
             else
             {
                 await User.ClearChat();
-                User.Cash.Coins -= User.Session.CombineCoinsPrice;
-                User.Cash.Gems -= User.Session.CombineGemsPrice;
-                foreach (var item in User.Session.CombineList.Values)
+                User.Cash.Coins -= price;
+                var result = 0;
+                foreach (var (item, count) in combineModule.CombineList)
                 {
-                    await User.Session.PayOutOne(item.Md5Hash);
-                    User.Stickers[item.Md5Hash].Count -= item.Count;
+                    result += await User.Cash.Payout(User.Stickers[item.Md5Hash]);
+                    User.Stickers[item.Md5Hash].Count -= count;
                 }
-                var authors = User.Session.CombineList.Values.Select(i => i.Author).ToList();
-                var tier = User.Session.CombineList.Values.First().Tier;
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, $"{Messages.you_collected} {result}{Text.coin}");
+                var authors = combineModule.CombineList.Select(i => i.Key.Author).ToList();
+                var tier = combineModule.Tier;
                 var rnd = new Random();
                 var author = authors[rnd.Next(authors.Count)];
                 var stickers = await StickerDao.GetListWhere(i => i.Author == author && i.Tier == tier + 1);
                 var sticker = stickers[rnd.Next(stickers.Count)];
-                if (User.Stickers.ContainsKey(sticker.Md5Hash))
-                    await User.Session.PayOutOne(sticker.Md5Hash);
+                if (User.Stickers.ContainsKey(sticker.Md5Hash)) await User.Cash.Payout(User.Stickers[sticker.Md5Hash]);
                 await UserStickerRelationDao.AddNew(User, sticker, 1);
                 var text = $"{Messages.combined_sticker}:\n" + sticker;
                 var stickerMessage = await MessageController.SendSticker(User, sticker.Id);
                 var message = await MessageController.SendMessage(User, text, Keyboard.BackToFilters(sticker.Title));
+                User.Session.DeleteModule<CombineModule>();
                 User.Session.Messages.Add(stickerMessage.MessageId);
                 User.Session.Messages.Add(message.MessageId);
             }

+ 5 - 9
CardCollector/Commands/CallbackQuery/ConfirmBuyingQuery.cs

@@ -2,6 +2,7 @@
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -11,18 +12,13 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.confirm_buying;
         public override async Task Execute()
         {
-            var coinsPrice = User.Session.SelectedSticker.GetCoinsPrice();
-            var gemsPrice = User.Session.SelectedSticker.GetGemsPrice();
-            if (coinsPrice > User.Cash.Coins)
-                await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id, Messages.not_enougth_coins);
-            else if (gemsPrice > User.Cash.Gems)
+            var auctionModule = User.Session.GetModule<AuctionModule>();
+            var price = auctionModule.Price * auctionModule.Count;
+            if (price > User.Cash.Gems)
                 await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id, Messages.not_enougth_gems);
             else
             {
-                var text = $"{Messages.confirm_buying}\n" +
-                           $"{User.Session.SelectedSticker.Count}{Text.items} {Text.per} " +
-                           $"{coinsPrice*User.Session.SelectedSticker.Count}{Text.coin} / {gemsPrice*User.Session.SelectedSticker.Count}{Text.gem}" +
-                           $"\n{Messages.are_you_sure}";
+                var text = $"{Messages.confirm_buying}\n{auctionModule.Count}{Text.items} {Text.per} {price}{Text.gem}\n{Messages.are_you_sure}";
                 await MessageController.EditMessage(User, CallbackMessageId, text, Keyboard.GetConfirmationKeyboard(Command.buy_sticker));
             }
         }

+ 6 - 5
CardCollector/Commands/CallbackQuery/ConfirmationSellingQuery.cs

@@ -3,6 +3,7 @@ using CardCollector.Commands.Message.TextMessage;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -12,12 +13,12 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.confirm_selling;
         public override async Task Execute()
         {
-            EnterCoinsPriceMessage.RemoveFromQueue(User.Id);
+            var collectionModule = User.Session.GetModule<CollectionModule>();
             EnterGemsPriceMessage.RemoveFromQueue(User.Id);
-            await User.Session.PayOutOne(User.Session.SelectedSticker.Md5Hash);
-            await MessageController.AnswerCallbackQuery(User, CallbackQueryId, 
-                $"{Messages.you_collected} {User.Session.IncomeCoins}{Text.coin} / {User.Session.IncomeGems}{Text.gem}");
-            AuctionController.SellCard(User, User.Session.CoinPrice, User.Session.GemPrice);
+            var income = await User.Cash.Payout(User.Stickers[collectionModule.SelectedSticker.Md5Hash]);
+            await MessageController.AnswerCallbackQuery(User, CallbackQueryId, $"{Messages.you_collected} {income}{Text.coin}");
+            User.Stickers[collectionModule.SelectedSticker.Md5Hash].Count -= collectionModule.Count;
+            AuctionController.SellCard(User.Id, collectionModule.SelectedSticker.Md5Hash, collectionModule.SellPrice, collectionModule.Count);
             await MessageController.EditMessage(User, CallbackMessageId, Messages.successfully_selling);
         }
         public ConfirmationSellingQuery(){}

+ 32 - 15
CardCollector/Commands/CallbackQuery/CountQuery.cs

@@ -2,6 +2,7 @@
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -11,25 +12,41 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.count;
         public override async Task Execute()
         {
-            var buyPositionCount = User.Session.SelectedSticker.Count;
-            if (CallbackData.Contains('+'))
+            var (stickerCount, maxCount) = User.Session.State switch
             {
-                if (buyPositionCount < User.Session.SelectedSticker.MaxCount || User.Session.SelectedSticker.MaxCount == -1)
-                {
-                    User.Session.SelectedSticker.Count++;
-                    await MessageController.EditReplyMarkup(User, CallbackMessageId, Keyboard.GetStickerKeyboard(User.Session));
-                }
-                else await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id, Messages.cant_increase);
+                UserState.CollectionMenu when User.Session.GetModule<CollectionModule>() is {} module => 
+                    (module.Count, User.Stickers[module.SelectedSticker.Md5Hash].Count),
+                UserState.ProductMenu when User.Session.GetModule<AuctionModule>() is {} module => 
+                    (module.Count, module.MaxCount),
+                UserState.CombineMenu when User.Session.GetModule<CombineModule>() is {} module => 
+                    (module.Count, User.Stickers[module.SelectedSticker.Md5Hash].Count),
+                _ => (0, 0)
+            };
+            var changed = false;
+            if (CallbackData.Contains('+') && (stickerCount < maxCount || maxCount == -1))
+            {
+                stickerCount++;
+                changed = true;
+            }
+            else if (CallbackData.Contains('-') && stickerCount > 1)
+            {
+                stickerCount--;
+                changed = true;
             }
-            else if (CallbackData.Contains('-'))
+            switch(User.Session.State)
             {
-                if (buyPositionCount > 1)
-                {
-                    User.Session.SelectedSticker.Count--;
-                    await MessageController.EditReplyMarkup(User, CallbackMessageId, Keyboard.GetStickerKeyboard(User.Session));
-                }
-                else await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id, Messages.cant_decrease);
+                case UserState.CollectionMenu:
+                    User.Session.GetModule<CollectionModule>().Count = stickerCount;
+                    break;
+                case UserState.ProductMenu: 
+                    User.Session.GetModule<AuctionModule>().Count = stickerCount;
+                    break;
+                case UserState.CombineMenu: 
+                    User.Session.GetModule<CombineModule>().Count = stickerCount;
+                    break;
             }
+            if (changed) await MessageController.EditReplyMarkup(User, CallbackMessageId, Keyboard.GetStickerKeyboard(User.Session));
+            else await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id, Messages.cant_change_count);
         }
 
         public CountQuery() { }

+ 7 - 5
CardCollector/Commands/CallbackQuery/DeleteCombine.cs

@@ -1,7 +1,9 @@
 using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -11,12 +13,12 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.delete_combine;
         public override async Task Execute()
         {
-            var hash = CallbackData.Split('=')[1];
-            User.Session.CombineList.Remove(hash);
-            if (User.Session.CombineList.Count == 0)
+            var sticker = await StickerDao.GetStickerByHash(CallbackData.Split('=')[1]);
+            var module = User.Session.GetModule<CombineModule>();
+            module.CombineList.Remove(sticker);
+            if (module.CombineList.Count == 0)
                 await new BackToFiltersMenu(User, Update).Execute();
-            else await MessageController.EditMessage(User, CallbackMessageId, 
-                User.Session.GetCombineMessage(), Keyboard.GetCombineKeyboard(User.Session));
+            else await MessageController.EditMessage(User, CallbackMessageId, module.ToString(), Keyboard.GetCombineKeyboard(module));
         }
 
         public DeleteCombine() { }

+ 2 - 1
CardCollector/Commands/CallbackQuery/PriceCallback.cs

@@ -11,7 +11,8 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.price;
         public override async Task Execute()
         {
-            await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_price, Keyboard.PriceOptions);
+            await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_price, 
+                User.Session.State == UserState.AuctionMenu ? Keyboard.GemsPriceOptions : Keyboard.CoinsPriceOptions);
         }
 
         public PriceCallback() { }

+ 5 - 5
CardCollector/Commands/CallbackQuery/PutForAuctionQuery.cs

@@ -3,6 +3,7 @@ using CardCollector.Commands.Message.TextMessage;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -14,12 +15,11 @@ namespace CardCollector.Commands.CallbackQuery
         public override async Task Execute()
         {
             await User.ClearChat();
-            User.Session.CoinPrice = User.Session.SelectedSticker.PriceCoins;
-            User.Session.GemPrice = User.Session.SelectedSticker.PriceGems;
+            var module = User.Session.GetModule<CollectionModule>();
             var message = await MessageController.SendMessage(User,
-                $"{Messages.current_price} {User.Session.CoinPrice}{Text.coin} / {User.Session.GemPrice}{Text.gem}" +
-                $"\n{Messages.enter_your_coins_price} {Text.coin}:", Keyboard.AuctionPutCancelKeyboard);
-            EnterCoinsPriceMessage.AddToQueue(User.Id, message.MessageId);
+                $"{Messages.current_price} {module.SellPrice}{Text.gem}" +
+                $"\n{Messages.enter_your_gems_price} {Text.gem}:", Keyboard.AuctionPutCancelKeyboard);
+            EnterGemsPriceMessage.AddToQueue(User.Id, message.MessageId);
             User.Session.Messages.Add(message.MessageId);
         }
 

+ 2 - 1
CardCollector/Commands/CallbackQuery/SetFilterCallback.cs

@@ -3,6 +3,7 @@ using System.Threading.Tasks;
 using CardCollector.Commands.Message.TextMessage;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
@@ -15,7 +16,7 @@ namespace CardCollector.Commands.CallbackQuery
         {
             EnterEmojiMessage.RemoveFromQueue(User.Id);
             var result = CallbackData.Split('=');
-            var filters = User.Session.Filters;
+            var filters = User.Session.GetModule<FiltersModule>().Filters;
             /* Команду set мы получаем в виде set=<key>=<value>, соответственно аргументы 1 и 2 это ключ и значение словаря */
             filters[result[1]] = Convert.ChangeType(result[2], filters[result[1]].GetType());
             /* Если левая граница стоимости алмазов или монет больше правой, то меняем правую на бесконечность */

+ 29 - 6
CardCollector/Commands/ChosenInlineResult/SelectStickerInlineResult.cs

@@ -2,8 +2,8 @@
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
-using CardCollector.Others;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.ChosenInlineResult
@@ -18,18 +18,41 @@ namespace CardCollector.Commands.ChosenInlineResult
             var sticker = await StickerDao.GetStickerByHash(hash);
             var stickerCount = User.Session.State switch
             {
-                UserState.AuctionMenu => await AuctionController.GetStickerCount(sticker.Id, User.Session.Filters),
-                UserState.ShopMenu => await ShopController.GetStickerCount(sticker.Id),
+                UserState.AuctionMenu => await AuctionController.GetStickerCount(sticker.Id, User.Session.GetModule<FiltersModule>()),
                 _ => User.Stickers[sticker.Md5Hash].Count
             };
-            var stickerInfo = new StickerInfo(sticker) {MaxCount = stickerCount};
-            User.Session.SelectedSticker = stickerInfo;
+            switch (User.Session.State)
+            {
+                case UserState.CollectionMenu:
+                    User.Session.GetModule<CollectionModule>().SelectedSticker = sticker;
+                    User.Session.GetModule<CollectionModule>().Count = 1;
+                    break;
+                case UserState.AuctionMenu:
+                    User.Session.GetModule<AuctionModule>().SelectedSticker = sticker;
+                    User.Session.GetModule<AuctionModule>().Count = 1;
+                    break;
+                case UserState.CombineMenu:
+                    User.Session.GetModule<CombineModule>().SelectedSticker = sticker;
+                    User.Session.GetModule<CombineModule>().Count = 1;
+                    break;
+                case UserState.Default:
+                    User.Session.GetModule<DefaultModule>().SelectedSticker = sticker;
+                    break;
+            }
             var stickerMessage = await MessageController.SendSticker(User, sticker.Id);
-            var infoMessage = await MessageController.SendMessage(User, stickerInfo.ToString(), Keyboard.GetStickerKeyboard(User.Session));
+            var infoMessage = await MessageController.SendMessage(User, sticker.ToString(stickerCount), Keyboard.GetStickerKeyboard(User.Session));
+            if (User.Session.State == UserState.AuctionMenu) User.Session.State = UserState.ProductMenu;
             User.Session.Messages.Add(stickerMessage.MessageId);
             User.Session.Messages.Add(infoMessage.MessageId);
         }
 
+        protected internal override bool IsMatches(string command)
+        {
+            return User == null 
+                ? base.IsMatches(command)
+                : User.Session.State is UserState.CollectionMenu or UserState.AuctionMenu or UserState.CombineMenu or UserState.Default;
+        }
+
         public SelectStickerInlineResult() { }
         public SelectStickerInlineResult(UserEntity user, Update update) : base(user, update) { }
     }

+ 7 - 7
CardCollector/Commands/ChosenInlineResult/SelectTraderResult.cs

@@ -3,6 +3,7 @@ using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.ChosenInlineResult
@@ -14,13 +15,12 @@ namespace CardCollector.Commands.ChosenInlineResult
         {
             await User.ClearChat();
             var productId = int.Parse(InlineResult.Split('=')[1]);
-            var trader = await AuctionDao.GetTraderInfo(productId);
-            if (User.Session.SelectedSticker == null) return;
-            var sticker = User.Session.SelectedSticker;
-            sticker.TraderInfo = trader;
-            sticker.MaxCount = trader.Quantity;
-            var messageSticker = await MessageController.SendSticker(User, User.Session.SelectedSticker.Id);
-            var message = await MessageController.SendMessage(User, sticker.ToString(), Keyboard.GetStickerKeyboard(User.Session));
+            var product = await AuctionDao.GetProduct(productId);
+            var module = User.Session.GetModule<AuctionModule>();
+            if (module.SelectedSticker is not {} sticker) return;
+            module.SelectedPosition = product;
+            var messageSticker = await MessageController.SendSticker(User, sticker.Id);
+            var message = await MessageController.SendMessage(User, sticker.ToString(module.MaxCount), Keyboard.GetStickerKeyboard(User.Session));
             User.Session.Messages.Add(messageSticker.MessageId);
             User.Session.Messages.Add(message.MessageId);
         }

+ 10 - 5
CardCollector/Commands/InlineQuery/ShowAuctionStickers.cs

@@ -1,7 +1,9 @@
-using System.Threading.Tasks;
+using System.Linq;
+using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.InlineQuery
@@ -14,9 +16,12 @@ namespace CardCollector.Commands.InlineQuery
             // Фильтр - введенная пользователем фраза
             var filter = Update.InlineQuery!.Query;
             // Получаем список стикеров
-            var stickersList = await AuctionController.GetStickers(filter);
-            var results = User.Session.Filters
-                .ApplyTo(stickersList, User.Session.State).ToTelegramResults(Command.select_sticker);
+            var stickersList = (await AuctionController.GetStickers(filter)).AsEnumerable();
+            stickersList = User.Session.GetModule<FiltersModule>()
+                .ApplyTo(stickersList);
+            var results = User.Session.GetModule<FiltersModule>()
+                .ApplyPriceTo(stickersList)
+                .ToTelegramResults(Command.select_sticker);
             // Посылаем пользователю ответ на его запрос
             await MessageController.AnswerInlineQuery(InlineQueryId, results);
         }
@@ -27,7 +32,7 @@ namespace CardCollector.Commands.InlineQuery
         {
             return User == null 
                 ? command.Contains("Sender")
-                : User.Session.State == UserState.AuctionMenu && User.Session.SelectedSticker == null;
+                : User.Session.State == UserState.AuctionMenu;
         }
 
         public ShowAuctionStickers() { }

+ 4 - 3
CardCollector/Commands/InlineQuery/ShowCollectionStickers.cs

@@ -3,6 +3,7 @@ using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.InlineQuery
@@ -16,8 +17,8 @@ namespace CardCollector.Commands.InlineQuery
             var filter = Update.InlineQuery!.Query;
             // Получаем список стикеров
             var stickersList = await User.GetStickersList(filter);
-            var results = User.Session.Filters
-                .ApplyTo(stickersList, User.Session.State).ToTelegramResults(Command.select_sticker);
+            var results = User.Session.GetModule<FiltersModule>()
+                .ApplyTo(stickersList).ToTelegramResults(Command.select_sticker);
             // Посылаем пользователю ответ на его запрос
             await MessageController.AnswerInlineQuery(InlineQueryId, results);
         }
@@ -28,7 +29,7 @@ namespace CardCollector.Commands.InlineQuery
         {
             return User == null 
                 ? command.Contains("Sender")
-                : User.Session.State == UserState.CollectionMenu && User.Session.CombineList.Count == 0;
+                : User.Session.State == UserState.CollectionMenu;
         }
 
         public ShowCollectionStickers() { }

+ 5 - 4
CardCollector/Commands/InlineQuery/ShowCombineStickers.cs

@@ -1,8 +1,8 @@
-using System.Linq;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.InlineQuery
@@ -14,8 +14,9 @@ namespace CardCollector.Commands.InlineQuery
         {
             // Фильтр - введенная пользователем фраза
             var filter = Update.InlineQuery!.Query;
+            var module = User.Session.GetModule<CombineModule>();
             // Получаем список стикеров
-            var stickersList = await User.GetStickersList(filter, User.Session.CombineList.First().Value.Tier);
+            var stickersList = await User.GetStickersList(filter, module.Tier);
             var results = stickersList.ToTelegramResults(Command.select_sticker);
             // Посылаем пользователю ответ на его запрос
             await MessageController.AnswerInlineQuery(InlineQueryId, results);
@@ -27,7 +28,7 @@ namespace CardCollector.Commands.InlineQuery
         {
             return User == null 
                 ? command.Contains("Sender")
-                : User.Session.State == UserState.CollectionMenu && User.Session.CombineList.Count > 0;
+                : User.Session.State == UserState.CombineMenu;
         }
 
         public ShowCombineStickers() { }

+ 3 - 2
CardCollector/Commands/InlineQuery/ShowShopStickers.cs

@@ -2,6 +2,7 @@
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.InlineQuery
@@ -15,8 +16,8 @@ namespace CardCollector.Commands.InlineQuery
             var filter = Update.InlineQuery!.Query;
             // Получаем список стикеров
             var stickersList = await ShopController.GetStickers(filter);
-            var results = User.Session.Filters
-                .ApplyTo(stickersList, User.Session.State).ToTelegramResults(Command.select_sticker);
+            var results = User.Session.GetModule<FiltersModule>()
+                .ApplyTo(stickersList).ToTelegramResults(Command.select_sticker);
             // Посылаем пользователю ответ на его запрос
             await MessageController.AnswerInlineQuery(InlineQueryId, results);
         }

+ 8 - 4
CardCollector/Commands/InlineQuery/ShowTradersInBotChat.cs

@@ -2,6 +2,7 @@
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.InlineQuery
@@ -13,11 +14,14 @@ namespace CardCollector.Commands.InlineQuery
         {
             // Фильтр - введенная пользователем фраза
             var filter = Update.InlineQuery!.Query;
+            var module = User.Session.GetModule<AuctionModule>();
             // Получаем список продавцов
-            var traders = await AuctionController.GetTradersList(filter, User.Session.SelectedSticker.Id);
-            var results = User.Session.Filters.ApplyTo(traders).ToTelegramResults(Command.buy_sticker);
+            var traders = await AuctionController.GetTradersList(filter, module.SelectedSticker.Id);
+            var results = User.Session.GetModule<FiltersModule>()
+                .ApplyPriceTo(traders)
+                .ToTelegramResults(Command.buy_sticker);
             // Посылаем пользователю ответ на его запрос
-            await MessageController.AnswerInlineQuery(InlineQueryId, results);
+            await MessageController.AnswerInlineQuery(InlineQueryId, await results);
         }
         
         /* Команда пользователя удовлетворяет условию, если она вызвана
@@ -26,7 +30,7 @@ namespace CardCollector.Commands.InlineQuery
         {
             return User == null 
                 ? command.Contains("Sender")
-                : User.Session.SelectedSticker != null && User.Session.State == UserState.AuctionMenu;
+                : User.Session.State == UserState.ProductMenu;
         }
 
         public ShowTradersInBotChat() { }

+ 2 - 4
CardCollector/Commands/Message/DocumentMessage/UploadFileMessage.cs

@@ -82,10 +82,8 @@ namespace CardCollector.Commands.Message.DocumentMessage
                     await CreateSticker(fileInfo, fields["Emoji"]);
                     var sticker = new StickerEntity
                     {
-                        Title = fields["Title"], Author = fields["Author"], 
-                        IncomeCoins = int.Parse(fields["IncomeCoins"]), IncomeGems = int.Parse(fields["IncomeGems"]), 
-                        IncomeTime = int.Parse(fields["IncomeTime"]), PriceCoins = int.Parse(fields["PriceCoins"]),
-                        PriceGems = int.Parse(fields["PriceGems"]), Tier = int.Parse(fields["Tier"]), 
+                        Title = fields["Title"], Author = fields["Author"], Income = int.Parse(fields["IncomeCoins"]), 
+                        IncomeTime = int.Parse(fields["IncomeTime"]), Tier = int.Parse(fields["Tier"]), 
                         Emoji = fields["Emoji"], Description = fields["Description"]
                     };
                     newStickers.Add(sticker);

+ 2 - 4
CardCollector/Commands/Message/Message.cs

@@ -37,17 +37,15 @@ namespace CardCollector.Commands.Message
                 // Команда "Коллекция"
                 new CollectionMessage(),
                 // Команда "Магазин"
-                new ShopMessage(),
+                // TODO переписать магазин
+                // new ShopMessage(),
                 // Команда "Аукцион"
                 new AuctionMessage(),
                 // Ожидание ввода эмоджи
                 new EnterEmojiMessage(),
-                //Очередь воода цены на аукцион
-                new EnterCoinsPriceMessage(),
                 // Загрузка стикерпака
                 new DownloadStickerPackMessage(),
                 //команда ввода цены
-                new EnterCoinsPriceMessage(),
                 new EnterGemsPriceMessage(),
                 // Команда "Показать пример"
                 new ShowSampleMessage(),

+ 0 - 56
CardCollector/Commands/Message/TextMessage/EnterCoinsPriceMessage.cs

@@ -1,56 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using CardCollector.Controllers;
-using CardCollector.DataBase.Entity;
-using CardCollector.Resources;
-using Telegram.Bot.Types;
-
-namespace CardCollector.Commands.Message.TextMessage
-{
-    public class EnterCoinsPriceMessage : Message
-    {
-        protected override string CommandText => "";
-
-        //Список пользователей, от которых ожидается ввод эмоджи ключ - id пользователя, значение - сообщение с меню #1#
-        private static readonly Dictionary<long, int> Queue = new ();
-
-        public override async Task Execute()
-        {
-            if (!int.TryParse(Update.Message!.Text, out var price) || price < 0)
-                await MessageController.EditMessage(User, Queue[User.Id], 
-                    $"{Messages.current_price} {User.Session.CoinPrice}{Text.coin} / {User.Session.GemPrice}{Text.gem}" +
-                    $"\n{Messages.please_enter_price}", Keyboard.AuctionPutCancelKeyboard);
-            else
-            {
-                User.Session.CoinPrice = price;
-                /* Редактируем сообщение */
-                await MessageController.EditMessage(User, Queue[User.Id], 
-                    $"{Messages.current_price} {User.Session.CoinPrice}{Text.coin} / {User.Session.GemPrice}{Text.gem}" +
-                    $"\n{Messages.enter_your_gems_price} {Text.gem}:", Keyboard.AuctionPutCancelKeyboard);
-                EnterGemsPriceMessage.AddToQueue(User.Id,Queue[User.Id]);
-                Queue.Remove(User.Id);
-            }
-        }
-
-        //Добавляем пользователя в очередь #1#
-        public static void AddToQueue(long userId, int messageId)
-        {
-            Queue.TryAdd(userId, messageId);
-        }
-
-        //Удаляем пользователя из очереди #1#
-        public static void RemoveFromQueue(long userId)
-        {
-            Queue.Remove(userId);
-        }
-
-        // Переопределяем метод, так как команда удовлетворяет условию, если пользователь находится в очереди #1#
-        protected internal override bool IsMatches(string command)
-        {
-            return User == null ? base.IsMatches(command) : Queue.ContainsKey(User.Id);
-        }
-
-        public EnterCoinsPriceMessage() { }
-        public EnterCoinsPriceMessage(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 4 - 2
CardCollector/Commands/Message/TextMessage/EnterEmojiMessage.cs

@@ -4,6 +4,7 @@ using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.Message.TextMessage
@@ -30,9 +31,10 @@ namespace CardCollector.Commands.Message.TextMessage
                 await MessageController.EditMessage(User, Queue[User.Id], Messages.enter_only_one_emoji, Keyboard.EmojiOptions);
             else
             {
-                User.Session.Filters[Command.emoji] = input;
+                var filtersModule = User.Session.GetModule<FiltersModule>();
+                filtersModule.Filters[Command.emoji] = input;
                 /* Формируем сообщение с имеющимися фильтрами у пользователя */
-                var text = User.Session.Filters.ToMessage(User.Session.State);
+                var text = filtersModule.ToString(User.Session.State);
                 /* Редактируем сообщение */
                 await MessageController.EditMessage(User, Queue[User.Id], text, Keyboard.GetSortingMenu(User.Session.State));
                 Queue.Remove(User.Id);

+ 6 - 5
CardCollector/Commands/Message/TextMessage/EnterGemsPriceMessage.cs

@@ -3,6 +3,7 @@ using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.Message.TextMessage
@@ -15,16 +16,16 @@ namespace CardCollector.Commands.Message.TextMessage
         public override async Task Execute()
         {
             /* если пользователь ввел что-то кроме эмодзи */
+            var module = User.Session.GetModule<CollectionModule>();
             if (!int.TryParse(Update.Message!.Text, out var price) || price < 0)
                 await MessageController.EditMessage(User, Queue[User.Id], 
-                    $"{Messages.current_price} {User.Session.CoinPrice}{Text.coin} / {User.Session.GemPrice}{Text.gem}" +
-                    $"\n{Messages.please_enter_price}", Keyboard.AuctionPutCancelKeyboard);
+                    $"{Messages.current_price} {module.SellPrice}{Text.gem}\n{Messages.please_enter_price}",
+                    Keyboard.AuctionPutCancelKeyboard);
             else
             {
-                User.Session.GemPrice = price;
+                module.SellPrice = price;
                 await MessageController.EditMessage(User, Queue[User.Id],
-                    $"{Messages.confirm_selling} {User.Session.CoinPrice}{Text.coin} / {User.Session.GemPrice}{Text.gem}:", 
-                    Keyboard.AuctionPutCancelKeyboard);
+                    $"{Messages.confirm_selling} {module.SellPrice}{Text.gem}:", Keyboard.AuctionPutCancelKeyboard);
                 Queue.Remove(User.Id);
             }
         }

+ 2 - 2
CardCollector/Commands/Message/TextMessage/ProfileMessage.cs

@@ -14,7 +14,7 @@ namespace CardCollector.Commands.Message.TextMessage
         public override async Task Execute()
         {
             /* Подсчитываем прибыль */
-            await User.Session.CalculateIncome();
+            var income = await User.Cash.CalculateIncome(User.Stickers);
             /* Отправляем сообщение */
             var message = await MessageController.SendMessage(User, 
                 /* Имя пользователя */
@@ -24,7 +24,7 @@ namespace CardCollector.Commands.Message.TextMessage
                 /* Количество алмазов */
                 $"{Messages.gems}: {User.Cash.Gems}{Text.gem}",
                 /* Клавиатура профиля */
-                Keyboard.GetProfileKeyboard(User));
+                Keyboard.GetProfileKeyboard(income));
             /* Записываем id нового сообщения */
             User.Session.Messages.Add(message.MessageId);
         }

+ 1 - 2
CardCollector/Commands/Message/TextMessage/ShopMessage.cs

@@ -15,8 +15,7 @@ namespace CardCollector.Commands.Message.TextMessage
             await User.ClearChat();
             /* Переводим состояние пользователя в меню магазина */
             User.Session.State = UserState.ShopMenu;
-            /* Отображаем сообщение с фильтрами */
-            await new ShowFiltersMenu(User, Update).Execute();
+            
         }
         
         public ShopMessage() { }

+ 25 - 3
CardCollector/Commands/Message/TextMessage/ShowFiltersMenu.cs

@@ -1,7 +1,9 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.Message.TextMessage
@@ -12,9 +14,29 @@ namespace CardCollector.Commands.Message.TextMessage
         protected override string CommandText => "Message";
         public override async Task Execute()
         {
-            User.Session.SelectedSticker = null;
+            switch (User.Session.State)
+            {
+                case UserState.CollectionMenu:
+                    User.Session.DeleteModule<CollectionModule>();
+                    break;
+                case UserState.ShopMenu:
+                    User.Session.DeleteModule<ShopModule>();
+                    break;
+                case UserState.AuctionMenu:
+                    User.Session.DeleteModule<AuctionModule>();
+                    break;
+                case UserState.CombineMenu:
+                    User.Session.DeleteModule<CombineModule>();
+                    break;
+                case UserState.ProductMenu:
+                    User.Session.DeleteModule<AuctionModule>();
+                    break;
+                case UserState.Default:
+                    User.Session.GetModule<DefaultModule>().Reset();
+                    break;
+            }
             /* Формируем сообщение с имеющимися фильтрами у пользователя */
-            var text = User.Session.Filters.ToMessage(User.Session.State);
+            var text = User.Session.GetModule<FiltersModule>().ToString(User.Session.State);
             /* Отправляем сообщение */
             var message = await MessageController.SendMessage(User, text, Keyboard.GetSortingMenu(User.Session.State));
             /* Добавляем это сообщение в список для удаления */

+ 11 - 41
CardCollector/Controllers/AuctionController.cs

@@ -3,8 +3,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
-using CardCollector.Others;
-using CardCollector.Resources;
+using CardCollector.Session.Modules;
 
 namespace CardCollector.Controllers 
 {
@@ -17,39 +16,18 @@ namespace CardCollector.Controllers
         stickerShortHashCode - MD5 хеш представляющий собой сумму id стикера и id пользователя, используется в словаре как ключ
         price - цена за штуку
         count - количество продаваемых стикеров*/
-        public static void SellCard(UserEntity user, int priceCoins, int priceGems)
+        public static void SellCard(long userId, string stickerId, int price, int count)
         {
-            //подтверждаем действие
-            var hash = user.Session.SelectedSticker.Md5Hash;
-            user.Stickers[hash].Count -= user.Session.SelectedSticker.Count;
-            /* user.Cash.Coins += price * count; */
             var product = new AuctionEntity
             {
-                PriceCoins = priceCoins,
-                PriceGems = priceGems,
-                Quantity = user.Session.SelectedSticker.Count,
-                StickerId = user.Session.SelectedSticker.Id,
-                Trader = user.Id
+                Trader = userId,
+                StickerId = stickerId,
+                Price = price,
+                Count = count
             };
             AuctionDao.AddNew(product);
         }
-        
-        //покупка стикера
-        public static async Task BuyCard(StickerInfo sticker)
-        {
-            var product = await AuctionDao.GetProduct(sticker.TraderInfo.Id);
-            product.Quantity -= sticker.Count;
-            var user = await UserDao.GetById(product.Trader);
-            var coinsSum = product.PriceCoins * sticker.Count;
-            var gemsSum = product.PriceGems * sticker.Count;
-            await MessageController.SendMessage(user, $"{Messages.you_sold} {sticker.Title} {sticker.Count}{Text.items}" +
-                                                      $"\n{Messages.you_collected} {coinsSum}{Text.coin} / {gemsSum}{Text.gem}");
-            user.Cash.Coins += coinsSum;
-            user.Cash.Gems += gemsSum;
-            if (product.Quantity == 0)
-                await AuctionDao.DeleteRow(sticker.TraderInfo.Id);
-        }
-        
+
         public static async Task<int> GetStickerCount(string stickerId)
         {
             return await AuctionDao.GetTotalQuantity(stickerId);
@@ -60,17 +38,9 @@ namespace CardCollector.Controllers
             return (await AuctionDao.GetStickers(filter)).ToList();
         }
 
-        public static async Task<IEnumerable<TraderInformation>> GetTradersList(string filter, string stickerId)
+        public static async Task<IEnumerable<AuctionEntity>> GetTradersList(string filter, string stickerId)
         {
-            var result = new List<TraderInformation>();
-            var products = await AuctionDao.GetProducts(stickerId);
-            var users = await UserDao.GetUsersList(filter);
-            foreach (var product in products)
-            {
-                if (users.FirstOrDefault(i => i.Id == product.Trader) is { } user)
-                    result.Add(new TraderInformation(product) {Username = user.Username});
-            }
-            return result;
+            return await AuctionDao.GetProducts(stickerId);
         }
 
         public static async Task<int> GetStickerCount(int productId)
@@ -78,10 +48,10 @@ namespace CardCollector.Controllers
             return await AuctionDao.GetQuantity(productId);
         }
 
-        public static async Task<int> GetStickerCount(string stickerId, Dictionary<string, object> sessionFilters)
+        public static async Task<int> GetStickerCount(string stickerId, FiltersModule sessionFilters)
         {
             var traders = await GetTradersList("", stickerId);
-            return sessionFilters.ApplyTo(traders).Sum(i => i.Quantity);
+            return sessionFilters.ApplyPriceTo(traders).Sum(i => i.Count);
         }
     }
 }

+ 0 - 9
CardCollector/Controllers/ShopController.cs

@@ -3,7 +3,6 @@ using System.Linq;
 using System.Threading.Tasks;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
-using CardCollector.Others;
 
 namespace CardCollector.Controllers
 {
@@ -20,13 +19,5 @@ namespace CardCollector.Controllers
             var result = await ShopDao.GetAllShopPositions(filter);
             return result.ToList();
         }
-
-        public static async Task SoldCard(StickerInfo selectedSticker)
-        {
-            var product = await ShopDao.GetSticker(selectedSticker.Id);
-            if (product.IsInfinite) return;
-            product.Count -= selectedSticker.Count;
-            if (product.Count == 0) ShopDao.DeleteRow(product.Id);
-        }
     }
 }

+ 22 - 12
CardCollector/DataBase/Entity/AuctionEntity.cs

@@ -1,31 +1,41 @@
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.EntityDao;
+using CardCollector.Resources;
 
 namespace CardCollector.DataBase.Entity
 {
     [Table("auction")]
     public class AuctionEntity
     {
-        /* добавил, так как один и тот же стикер может продаваться разными людьми,
-         следовательно - он не уникальный */
         /* id записи */
-        [Column("id"), MaxLength(32)] public int Id { get; set; }
+        [Key] [Column("id"), MaxLength(32)] public int Id { get; set; }
         
         /* id стикера */
         [Column("sticker_id"), MaxLength(127)] public string StickerId { get; set; }
-        
-        /* Разбил на 2 отдельных цены, так как я ранее говорил,
-         что можно будет продать стик за 2 валюты одновременно, поле валюты упразднил */
-        /* цена в монетах */
-        [Column("price_coins"), MaxLength(32)] public int PriceCoins { get; set; }
-        
-        /* цена в алмазах */
-        [Column("price_gems"), MaxLength(32)] public int PriceGems { get; set; }
+
+        /* цена (в алмазах) */
+        [Column("price"), MaxLength(32)] public int Price { get; set; }
         
         /* количество */
-        [Column("quantity"), MaxLength(32)] public int Quantity { get; set; }
+        [Column("count"), MaxLength(32)] public int Count { get; set; }
         
         /* продавец */
         [Column("trader"), MaxLength(127)] public long Trader { get; set; }
+
+        public async Task BuyCard(int count)
+        {
+            Count -= count;
+            var user = await UserDao.GetById(Trader);
+            var sticker = await StickerDao.GetById(StickerId);
+            var gemsSum = Price * count;
+            await MessageController.SendMessage(user, $"{Messages.you_sold} {sticker.Title} {count}{Text.items}" +
+                                                      $"\n{Messages.you_collected} {gemsSum}{Text.gem}");
+            user.Cash.Gems += gemsSum;
+            await CashDao.Save();
+            if (Count == 0) await AuctionDao.DeleteRow(Id);
+        }
     }
 }

+ 40 - 1
CardCollector/DataBase/Entity/CashEntity.cs

@@ -1,5 +1,9 @@
-using System.ComponentModel.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
+using System.Threading.Tasks;
+using CardCollector.DataBase.EntityDao;
 
 namespace CardCollector.DataBase.Entity
 {
@@ -17,5 +21,40 @@ namespace CardCollector.DataBase.Entity
         
         /* Количество алмазов */
         [Column("gems"), MaxLength(32)] public int Gems { get; set; } = 1000;
+        
+        [NotMapped] private DateTime LastPayout = DateTime.Now;
+        
+        public async Task<int> CalculateIncome(Dictionary<string, UserStickerRelationEntity> stickers)
+        {
+            LastPayout = DateTime.Now;
+            var result = 0;
+            foreach (var sticker in stickers.Values)
+            {
+                var stickerInfo = await StickerDao.GetStickerByHash(sticker.ShortHash);
+                var payoutInterval = LastPayout - sticker.Payout;
+                var payoutsCount = (int) (payoutInterval.TotalMinutes / stickerInfo.IncomeTime);
+                if (payoutsCount < 1) continue;
+                var multiplier = payoutsCount * sticker.Count;
+                result += stickerInfo.Income * multiplier;
+            }
+            return result;
+        }
+        
+        public async Task<int> Payout(Dictionary<string, UserStickerRelationEntity> stickers)
+        {
+            return await stickers.Values.SumAsync(async sticker => await Payout(sticker));
+        }
+        
+        public async Task<int> Payout(UserStickerRelationEntity relation)
+        {
+            var stickerInfo = await StickerDao.GetById(relation.StickerId);
+            var payoutInterval = DateTime.Now - relation.Payout;
+            var payoutsCount = (int) (payoutInterval.TotalMinutes / stickerInfo.IncomeTime);
+            if (payoutsCount < 1) return 0;
+            relation.Payout += new TimeSpan(0, stickerInfo.IncomeTime, 0) * payoutsCount;
+            var result = stickerInfo.Income * payoutsCount * relation.Count;
+            Coins += result;
+            return result;
+        }
     }
 }

+ 13 - 11
CardCollector/DataBase/Entity/StickerEntity.cs

@@ -24,20 +24,11 @@ namespace CardCollector.DataBase.Entity
         [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("income_coins"), MaxLength(32)] public int Income { get; set; } = 0;
         
         /* Время, необходимое для получения дохода */
         [Column("income_time"), MaxLength(32)] public int IncomeTime { get; set; } = 0;
         
-        /* Стоимость стикера в магазине (монеты) */
-        [Column("price_coins"), MaxLength(32)] public int PriceCoins { get; set; } = 0;
-        
-        /* Стоимость стикера в магазине (алмазы) */
-        [Column("price_gems"), MaxLength(32)] public int PriceGems { get; set; } = 0;
-        
         /* Количество звезд стикера (редкость) */
         [Column("tier"), MaxLength(32)] public int Tier { get; set; } = 1;
         
@@ -55,7 +46,18 @@ namespace CardCollector.DataBase.Entity
             var str = $"\n{Title} {string.Concat(Enumerable.Repeat(Text.star, Tier))}" +
                              $"\n{Text.emoji}: {Emoji}" +
                              $"\n{Text.author}: {Author}" +
-                             $"\n{IncomeCoins}{Text.coin} / {IncomeGems}{Text.gem} {IncomeTime}{Text.time}{Text.minutes}";
+                             $"\n{Income}{Text.coin} {IncomeTime}{Text.time}{Text.minutes}";
+            if (Description != "") str += $"\n\n{Text.description}: {Description}";
+            return str;
+        }
+        
+        public string ToString(int count)
+        {
+            var str = $"\n{Title} {string.Concat(Enumerable.Repeat(Text.star, Tier))}" +
+                      $"\n{Text.emoji}: {Emoji}" +
+                      $"\n{Text.author}: {Author}" +
+                      $"\n{Text.count}: {(count != -1 ? count : "∞")}" +
+                      $"\n{Income}{Text.coin} {IncomeTime}{Text.time}{Text.minutes}";
             if (Description != "") str += $"\n\n{Text.description}: {Description}";
             return str;
         }

+ 1 - 1
CardCollector/DataBase/Entity/UserEntity.cs

@@ -5,8 +5,8 @@ using System.ComponentModel.DataAnnotations.Schema;
 using System.Linq;
 using System.Threading.Tasks;
 using CardCollector.DataBase.EntityDao;
-using CardCollector.Others;
 using CardCollector.Resources;
+using CardCollector.Session;
 using Telegram.Bot.Types;
 
 namespace CardCollector.DataBase.Entity

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

@@ -1,6 +1,8 @@
 using System;
 using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
+using System.Threading.Tasks;
+using CardCollector.DataBase.EntityDao;
 
 namespace CardCollector.DataBase.Entity
 {

+ 2 - 10
CardCollector/DataBase/EntityDao/AuctionDao.cs

@@ -4,7 +4,6 @@ using System.Linq;
 using System.Linq.Expressions;
 using System.Threading.Tasks;
 using CardCollector.DataBase.Entity;
-using CardCollector.Others;
 using Microsoft.EntityFrameworkCore;
 
 namespace CardCollector.DataBase.EntityDao
@@ -22,18 +21,11 @@ namespace CardCollector.DataBase.EntityDao
             return (await Table.WhereAsync(e => Task.FromResult(e.StickerId == stickerId))).ToList();
         }
 
-        public static async Task<TraderInformation> GetTraderInfo(int productId)
-        {
-            var product = await Table.FirstAsync(item => item.Id == productId);
-            var trader = await UserDao.GetById(product.Trader);
-            return new TraderInformation(product) {Username = trader.Username};
-        }
-
         public static async Task<int> GetTotalQuantity(string stickerId)
         {
             /* Добавил метод, который считает общее количество данных стикеров на аукционе */
             var list = await GetProducts(stickerId);
-            return list.Sum(e => e.Quantity);
+            return list.Sum(e => e.Count);
         }
 
         public static async Task<IEnumerable<StickerEntity>> GetStickers(string filter)
@@ -67,7 +59,7 @@ namespace CardCollector.DataBase.EntityDao
 
         public static async Task<int> GetQuantity(int productId)
         {
-            return (await Table.FirstAsync(item => item.Id == productId)).Quantity;
+            return (await Table.FirstAsync(item => item.Id == productId)).Count;
         }
 
         public static async Task<AuctionEntity> GetProduct(int productId)

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

@@ -26,5 +26,10 @@ namespace CardCollector.DataBase.EntityDao
             await Instance.SaveChangesAsync();
             return result.Entity;
         }
+
+        public static async Task Save()
+        {
+            await Instance.SaveChangesAsync();
+        }
     }
 }

+ 5 - 25
CardCollector/DataBase/EntityDao/StickerDao.cs

@@ -20,31 +20,6 @@ namespace CardCollector.DataBase.EntityDao
             return await Table.FirstOrDefaultAsync(item => item.Md5Hash == hash);
         }
 
-         /* Добавление новго стикера в систему
-         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 = "")
-
-        {
-            var cash = new StickerEntity
-            {
-                Id = fileId, Title = title, Author = author,
-                IncomeCoins = incomeCoins, IncomeGems = incomeGems,
-                Tier = tier, Emoji = emoji, Description = description,
-                Md5Hash = Utilities.CreateMd5(fileId)
-            };
-            var result = await Table.AddAsync(cash);
-            await Instance.SaveChangesAsync();
-            return result.Entity;
-        }
-
         public static async Task<List<string>> GetAuthorsList()
         {
             return await Table
@@ -69,5 +44,10 @@ namespace CardCollector.DataBase.EntityDao
         {
             return (await Table.ToListAsync()).Where(func).ToList();
         }
+
+        public static async Task<StickerEntity> GetById(string id)
+        {
+            return await Table.FirstOrDefaultAsync(sticker => sticker.Id == id);
+        }
     }
 }

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

@@ -4,6 +4,7 @@ using System.Threading.Tasks;
 using System.Timers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Microsoft.EntityFrameworkCore;
 using Telegram.Bot.Types;
 
@@ -36,6 +37,8 @@ namespace CardCollector.DataBase.EntityDao
                 /* Собираем объект пользователя */
                 result.Cash = await CashDao.GetById(user.Id);
                 result.Stickers = await UserStickerRelationDao.GetListById(user.Id);
+                result.Session.InitNewModule<FiltersModule>();
+                result.Session.InitNewModule<DefaultModule>();
                 
                 /* Добавляем пользователя в список активных, чтобы не обращаться к бд лишний раз */
                 ActiveUsers.Add(user.Id, result);

+ 20 - 94
CardCollector/Extensions.cs

@@ -5,7 +5,6 @@ using System.Linq;
 using System.Threading.Tasks;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
-using CardCollector.Others;
 using CardCollector.Resources;
 using Telegram.Bot.Types.InlineQueryResults;
 
@@ -32,15 +31,16 @@ namespace CardCollector
             return result;
         }
         /* Преобразует список продавцов в список результатов для телеграм */
-        public static IEnumerable<InlineQueryResult> ToTelegramResults
-            (this IEnumerable<TraderInformation> list, string command)
+        public static async Task<IEnumerable<InlineQueryResult>> ToTelegramResults
+            (this IEnumerable<AuctionEntity> list, string command)
         {
             var result = new List<InlineQueryResult>();
             foreach (var item in list)
             {
+                var user = await UserDao.GetById(item.Trader);
                 result.Add(new InlineQueryResultArticle($"{command}={item.Id}",
-                    $"{item.Username} {item.Quantity}{Text.items}", new InputTextMessageContent(Text.buy))
-                { Description = $"{item.PriceCoins}{Text.coin}/{item.PriceGems}{Text.gem} {Text.per} 1{Text.items}" });
+                    $"{user.Username} {item.Count}{Text.items}", new InputTextMessageContent(Text.buy))
+                { Description = $"{item.Price}{Text.gem} {Text.per} 1{Text.items}" });
                 /* Ограничение Telegram API по количеству результатов в 50 шт. */
                 if (result.Count > 49) return result;
             }
@@ -60,95 +60,6 @@ namespace CardCollector
             return result;
         }
 
-        public static IEnumerable<StickerEntity> ApplyTo(this Dictionary<string, object> dict,
-            IEnumerable<StickerEntity> list, UserState state)
-        {
-            /* Фильтруем по автору */
-            if (dict[Command.author] is string author && author != "")
-                list = list.Where(item => item.Author.Contains(author));
-            /* Фильтруем по тиру */
-            if (dict[Command.tier] is int tier && tier != -1)
-                list = list.Where(item => item.Tier.Equals(tier));
-            /* Фильтруем по эмоции */
-            if (dict[Command.emoji] is string emoji && emoji != "")
-                list = list.Where(item => item.Emoji.Contains(emoji));
-            /* Если пользвователь не находится в меню коллекции, то фильтруем по цене */
-            if (state is not UserState.CollectionMenu)
-            {
-                /* Фильтруем по цене монет ОТ */
-                if (dict[Command.price_coins_from] is int PCF && PCF != 0)
-                {
-                    list = list.Where(item => state == UserState.AuctionMenu
-                        ? AuctionDao.HaveAny(item.Id, i => i.PriceCoins >= PCF)
-                        : item.PriceCoins >= PCF);
-                }
-                /* Фильтруем по цене монет ДО */
-                if (dict[Command.price_coins_to] is int PCT && PCT != 0)
-                    list = list.Where(item => state == UserState.AuctionMenu
-                        ? AuctionDao.HaveAny(item.Id, i => i.PriceCoins <= PCT)
-                        : item.PriceCoins <= PCT);
-                /* Фильтруем по цене алмазов ОТ */
-                if (dict[Command.price_gems_from] is int PGF && PGF != 0)
-                    list = list.Where(item => state == UserState.AuctionMenu
-                        ? AuctionDao.HaveAny(item.Id, i => i.PriceGems >= PGF)
-                        : item.PriceGems >= PGF);
-                /* Фильтруем по цене адмазов ДО */
-                if (dict[Command.price_gems_to] is int PGT && PGT != 0)
-                    list = list.Where(item => state == UserState.AuctionMenu
-                        ? AuctionDao.HaveAny(item.Id, i => i.PriceGems <= PGT)
-                        : item.PriceGems <= PGT);
-            }
-            /* Сортируем список, если тип сортировки установлен */
-            if (dict[Command.sort] is not string sort || sort == SortingTypes.None) return list;
-            {
-                /* Сортируем по автору */
-                if (sort== SortingTypes.ByAuthor)
-                    list = list.OrderBy(item => item.Author);
-                /* Сортируем по названию */
-                if (sort == SortingTypes.ByTitle)
-                    list = list.OrderBy(item => item.Title);
-                /* Сортируем по увеличению тира */
-                if (sort == SortingTypes.ByTierIncrease)
-                    list = list.OrderBy(item => item.Tier);
-                /* Сортируем по уменьшению тира */
-                if (sort == SortingTypes.ByTierDecrease)
-                    list = list.OrderByDescending(item => item.Tier);
-            }
-            return list;
-        }
-
-        public static IEnumerable<TraderInformation> ApplyTo(this Dictionary<string, object> dict, IEnumerable<TraderInformation> list)
-        {
-            /* Фильтруем по цене монет ОТ */
-            if (dict[Command.price_coins_from] is int PCF && PCF != 0)
-                list = list.Where(item => item.PriceCoins >= PCF);
-            /* Фильтруем по цене монет ДО */
-            if (dict[Command.price_coins_to] is int PCT && PCT != 0)
-                list = list.Where(item => item.PriceCoins <= PCT);
-            /* Фильтруем по цене алмазов ОТ */
-            if (dict[Command.price_gems_from] is int PGF && PGF != 0)
-                list = list.Where(item => item.PriceGems >= PGF);
-            /* Фильтруем по цене адмазов ДО */
-            if (dict[Command.price_gems_to] is int PGT && PGT != 0)
-                list = list.Where(item => item.PriceGems <= PGT);
-            return list;
-        }
-        
-        public static string ToMessage(this Dictionary<string, object> dict, UserState state)
-        {
-            var text = $"{Messages.current_filters}\n" +
-                       $"{Messages.author} {(dict[Command.author] is string author and not "" ? author : Messages.all)}\n" +
-                       $"{Messages.tier} {(dict[Command.tier] is int tier and not -1 ? new string('⭐', tier) : Messages.all)}\n" +
-                       $"{Messages.emoji} {(dict[Command.emoji] is string emoji and not "" ? emoji : Messages.all)}\n";
-            if (state != UserState.CollectionMenu) 
-                text += $"{Messages.price} 💰 {dict[Command.price_coins_from]} -" +
-                        $" {(dict[Command.price_coins_to] is int c and not 0 ? c : "∞")}\n" +
-                        $"{Messages.price} 💎 {dict[Command.price_gems_from]} -" +
-                        $" {(dict[Command.price_gems_to] is int g and not 0 ? g : "∞")}\n";
-            text += $"{Messages.sorting} {dict[Command.sort]}\n\n{Messages.select_filter}";
-            return text;
-        }
-        
         public static async Task<IEnumerable<T>> WhereAsync<T>(
             this IEnumerable<T> source, Func<T, Task<bool>> predicate)
         {
@@ -162,5 +73,20 @@ namespace CardCollector
             await Task.WhenAll(tasks);
             return results;
         }
+        
+        public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> source)
+        {
+            return source.Select((item, index) => (item, index));
+        }
+        
+        public static async Task<int> SumAsync<TSource>(this IEnumerable<TSource> source, Func<TSource, Task<int>> selector)
+        {
+            var sum = 0;
+            checked
+            {
+                foreach (var item in source) sum += await selector(item);
+            }
+            return sum;
+        }
     }
 }

+ 0 - 163
CardCollector/Others/Session.cs

@@ -1,163 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CardCollector.Controllers;
-using CardCollector.DataBase.Entity;
-using CardCollector.DataBase.EntityDao;
-using CardCollector.Resources;
-
-namespace CardCollector.Others
-{
-    public class UserSession
-    {
-        private DateTime _lastAccess = DateTime.Now;
-
-        private readonly UserEntity user;
-
-        public UserSession(UserEntity user)
-        {
-            this.user = user;
-        }
-
-        /* Текущее состояние пользователя */
-        public UserState State = UserState.Default;
-
-        /* Выбранный пользователем стикер для покупки (продажи, слияния) */
-        public StickerInfo SelectedSticker { get; set; }
-
-        /* Выбранные пользователем стикеры для слияния */
-        public Dictionary<string, StickerInfo> CombineList { get; set; } = new();
-
-
-        public void UpdateLastAccess()
-        {
-            _lastAccess = DateTime.Now;
-        }
-
-        public int GetLastAccessInterval()
-        {
-            return (int) (DateTime.Now - _lastAccess).TotalMinutes;
-        }
-
-        /* Фильтры, примененные пользователем в меню коллекции/магазина/аукциона */
-        public readonly Dictionary<string, object> Filters = new()
-        {
-            {Command.author, ""},
-            {Command.tier, -1},
-            {Command.emoji, ""},
-            {Command.price_coins_from, 0},
-            {Command.price_coins_to, 0},
-            {Command.price_gems_from, 0},
-            {Command.price_gems_to, 0},
-            {Command.sort, SortingTypes.None},
-        };
-
-        /* Сообщения в чате пользователя */
-        public readonly List<int> Messages = new();
-
-        /* Прибыль, которую может получить пользователь, подсчитывается во время команды профиля */
-        public int IncomeCoins;
-        public int IncomeGems;
-        private DateTime LastPayout;
-
-        public async Task ClearMessages()
-        {
-            foreach (var messageId in Messages)
-                await MessageController.DeleteMessage(user, messageId, false);
-            Messages.Clear();
-        }
-
-        public int CombineCoinsPrice;
-        public int CombineGemsPrice;
-
-        public int CoinPrice;
-        public int GemPrice;
-
-        public void CalculateCombinePrice()
-        {
-            var coinsSum = CombineList.Values.Sum(i => 1440 / i.IncomeTime * i.IncomeCoins * i.Count);
-            var gemsSum = CombineList.Values.Sum(i => 1440 / i.IncomeTime * i.IncomeGems * i.Count);
-            var multiplier = SelectedSticker.Tier * 0.25 + 1;
-            CombineCoinsPrice = (int)(coinsSum * multiplier);
-            CombineGemsPrice = (int)(gemsSum * multiplier);
-        }
-
-        public async Task CalculateIncome()
-        {
-            IncomeCoins = 0;
-            IncomeGems = 0;
-            LastPayout = DateTime.Now;
-            foreach (var sticker in user.Stickers.Values)
-            {
-                var stickerInfo = await StickerDao.GetStickerByHash(sticker.ShortHash);
-                var payoutInterval = LastPayout - sticker.Payout;
-                var payoutsCount = (int) (payoutInterval.TotalMinutes / stickerInfo.IncomeTime);
-                if (payoutsCount < 1) continue;
-                var multiplier = payoutsCount * sticker.Count;
-                IncomeCoins += stickerInfo.IncomeCoins * multiplier;
-                IncomeGems += stickerInfo.IncomeGems * multiplier;
-            }
-        }
-
-        public async Task PayOut()
-        {
-            IncomeCoins = 0;
-            IncomeGems = 0;
-            foreach (var sticker in user.Stickers.Values)
-            {
-                var stickerInfo = await StickerDao.GetStickerByHash(sticker.ShortHash);
-                var payoutInterval = LastPayout - sticker.Payout;
-                var payoutsCount = (int) (payoutInterval.TotalMinutes / stickerInfo.IncomeTime);
-                if (payoutsCount < 1) continue;
-                var multiplier = payoutsCount * sticker.Count;
-                sticker.Payout += new TimeSpan(0, stickerInfo.IncomeTime, 0) * payoutsCount;
-                IncomeCoins += stickerInfo.IncomeCoins * multiplier;
-                IncomeGems += stickerInfo.IncomeGems * multiplier;
-            }
-
-            user.Cash.Coins += IncomeCoins;
-            user.Cash.Gems += IncomeGems;
-        }
-
-        public async Task PayOutOne(string hash)
-        {
-            IncomeCoins = 0;
-            IncomeGems = 0;
-            var sticker = user.Stickers[hash];
-            var stickerInfo = await StickerDao.GetStickerByHash(hash);
-            var payoutInterval = DateTime.Now - sticker.Payout;
-            var payoutsCount = (int) (payoutInterval.TotalMinutes / stickerInfo.IncomeTime);
-            if (payoutsCount < 1) return;
-            var multiplier = payoutsCount * sticker.Count;
-            sticker.Payout += new TimeSpan(0, stickerInfo.IncomeTime, 0) * payoutsCount;
-            IncomeCoins += stickerInfo.IncomeCoins * multiplier;
-            IncomeGems += stickerInfo.IncomeGems * multiplier;
-            user.Cash.Coins += IncomeCoins;
-            user.Cash.Gems += IncomeGems;
-        }
-
-        public int GetCombineCount()
-        {
-            return CombineList.Values.Sum(e => e.Count);
-        }
-
-        public async void EndSession()
-        {
-            await ClearMessages();
-            SelectedSticker = null;
-            CombineList.Clear();
-        }
-
-        public string GetCombineMessage()
-        {
-            var message = $"{Text.added_stickers} {GetCombineCount()}/{Constants.COMBINE_COUNT}:";
-            var i = 0;
-            foreach (var sticker in CombineList.Values){
-                message += $"\n{Text.sticker} {i + 1}: {sticker.Title} {sticker.Count}{Text.items}";
-                ++i;
-            }
-            return message;
-        }
-    }
-}

+ 0 - 54
CardCollector/Others/StickerInfo.cs

@@ -1,54 +0,0 @@
-using System.Linq;
-using CardCollector.DataBase.Entity;
-using CardCollector.Resources;
-
-namespace CardCollector.Others
-{
-    /* Класс-расщирение, который может содержать дополнительную информацию о стикере */
-    public class StickerInfo : StickerEntity
-    {
-        public StickerInfo(StickerEntity entity)
-        {
-            Id = entity.Id;
-            Title = entity.Title;
-            Author = entity.Author;
-            IncomeCoins = entity.IncomeCoins;
-            IncomeGems = entity.IncomeGems;
-            IncomeTime = entity.IncomeTime;
-            PriceCoins = entity.PriceCoins;
-            PriceGems = entity.PriceGems;
-            Tier = entity.Tier;
-            Emoji = entity.Emoji;
-            Description = entity.Description;
-            Md5Hash = entity.Md5Hash;
-        }
-        
-        public int Count = 1;
-        public int MaxCount;
-        
-        
-        public TraderInformation TraderInfo = null;
-
-        public int GetCoinsPrice()
-        {
-            return Count * TraderInfo?.PriceCoins ?? PriceCoins;
-        }
-        
-        public int GetGemsPrice()
-        {
-            return Count * TraderInfo?.PriceGems ?? PriceGems;
-        }
-
-        public override string ToString()
-        {
-            var count = TraderInfo?.Quantity ?? MaxCount;
-            var str = $"\n{Title} {string.Concat(Enumerable.Repeat(Text.star, Tier))}" +
-                $"\n{Text.emoji}: {Emoji}" +
-                $"\n{Text.author}: {Author}" +
-                $"\n{Text.count}: {(count != -1 ? count : "∞")}" +
-                $"\n{IncomeCoins}{Text.coin} / {IncomeGems}{Text.gem} {IncomeTime}{Text.time}{Text.minutes}";
-            if (Description != "") str += $"\n\n{Text.description}: {Description}";
-            return str;
-        }
-    }
-}

+ 0 - 19
CardCollector/Others/TraderInformation.cs

@@ -1,19 +0,0 @@
-using CardCollector.DataBase.Entity;
-
-namespace CardCollector.Others
-{
-    public class TraderInformation : AuctionEntity
-    {
-        public TraderInformation(AuctionEntity entity)
-        {
-            Id = entity.Id;
-            StickerId = entity.StickerId;
-            PriceCoins = entity.PriceCoins;
-            PriceGems = entity.PriceGems;
-            Quantity = entity.Quantity;
-            Trader = entity.Trader;
-        }
-
-        public string Username;
-    }
-}

+ 31 - 55
CardCollector/Resources/Keyboard.cs

@@ -1,7 +1,8 @@
 using System.Collections.Generic;
 using System.Linq;
 using CardCollector.DataBase.Entity;
-using CardCollector.Others;
+using CardCollector.Session;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types.ReplyMarkups;
 
 namespace CardCollector.Resources
@@ -84,7 +85,7 @@ namespace CardCollector.Resources
         });
 
         /* Клавиатура меню выбора цен */
-        public static readonly InlineKeyboardMarkup PriceOptions = new (new[]
+        public static readonly InlineKeyboardMarkup CoinsPriceOptions = new (new[]
         {
             new[] {
                 InlineKeyboardButton.WithCallbackData($"💰 {Text.from} 0", $"{Command.set}={Command.price_coins_from}=0"),
@@ -102,6 +103,12 @@ namespace CardCollector.Resources
                 InlineKeyboardButton.WithCallbackData($"💰 {Text.from} 1000", $"{Command.set}={Command.price_coins_from}=1000"),
                 InlineKeyboardButton.WithCallbackData($"💰 {Text.to} ∞", $"{Command.set}={Command.price_coins_to}=0"),
             },
+            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
+        });
+
+        /* Клавиатура меню выбора цен */
+        public static readonly InlineKeyboardMarkup GemsPriceOptions = new (new[]
+        {
             new[] {
                 InlineKeyboardButton.WithCallbackData($"💎 {Text.from} 0", $"{Command.set}={Command.price_gems_from}=0"),
                 InlineKeyboardButton.WithCallbackData($"💎 {Text.to} 10", $"{Command.set}={Command.price_gems_to}=10"),
@@ -189,39 +196,21 @@ namespace CardCollector.Resources
             return new InlineKeyboardMarkup(keyboardList);
         }
 
-        public static InlineKeyboardMarkup GetShopStickerKeyboard(StickerInfo stickerInfo)
-        {
-            return new InlineKeyboardMarkup(new[] {
-                new[]
-                {
-                    InlineKeyboardButton.WithCallbackData(
-                        $"{Text.buy} ({stickerInfo.Count})" +
-                        $" {stickerInfo.PriceCoins * stickerInfo.Count}{Text.coin} / " +
-                        $"{stickerInfo.PriceGems * stickerInfo.Count}{Text.gem}",
-                        Command.confirm_buying),
-                },
-                new[]
-                {
-                    InlineKeyboardButton.WithCallbackData(Text.minus, $"{Command.count}-"),
-                    InlineKeyboardButton.WithCallbackData(Text.plus, $"{Command.count}+"),
-                },
-                new[] {InlineKeyboardButton.WithCallbackData(Text.back, $"{Command.back}={Command.clear_chat}")},
-            });
-        }
-
-        public static InlineKeyboardMarkup GetCollectionStickerKeyboard(StickerInfo stickerInfo)
+        public static InlineKeyboardMarkup GetCollectionStickerKeyboard(CollectionModule module)
         {
+            var sticker = module.SelectedSticker;
+            var count = module.Count;
             var keyboard = new List<InlineKeyboardButton[]>
             {
-                new[] {InlineKeyboardButton.WithSwitchInlineQuery(Text.send_sticker, stickerInfo.Title)},
-                new[] {InlineKeyboardButton.WithCallbackData($"{Text.sell_on_auction} ({stickerInfo.Count})", Command.sell_on_auction)},
+                new[] {InlineKeyboardButton.WithSwitchInlineQuery(Text.send_sticker, sticker.Title)},
+                new[] {InlineKeyboardButton.WithCallbackData($"{Text.sell_on_auction} ({count})", Command.sell_on_auction)},
                 new[]
                 {
                     InlineKeyboardButton.WithCallbackData(Text.minus, $"{Command.count}-"),
                     InlineKeyboardButton.WithCallbackData(Text.plus, $"{Command.count}+"),
                 }
             };
-            if (stickerInfo.Tier != 5) keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData($"{Text.combine} ({stickerInfo.Count})", Command.combine)});
+            if (sticker.Tier != 5) keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData($"{Text.combine} ({count})", Command.combine)});
             keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.back, $"{Command.back}={Command.clear_chat}")});
             return new InlineKeyboardMarkup(keyboard);
         }
@@ -234,17 +223,10 @@ namespace CardCollector.Resources
             });
         }
 
-        public static InlineKeyboardMarkup GetAuctionProductKeyboard(StickerInfo stickerInfo)
+        public static InlineKeyboardMarkup GetAuctionProductKeyboard(AuctionModule module)
         {
             return new InlineKeyboardMarkup(new[] {
-                new[]
-                {
-                    InlineKeyboardButton.WithCallbackData(
-                        $"{Text.buy} ({stickerInfo.Count})" +
-                        $" {stickerInfo.TraderInfo.PriceCoins * stickerInfo.Count}{Text.coin} / " +
-                        $"{stickerInfo.TraderInfo.PriceGems * stickerInfo.Count}{Text.gem}",
-                        Command.confirm_buying),
-                },
+                new[] {InlineKeyboardButton.WithCallbackData($"{Text.buy} ({module.Count}) {module.Price * module.Count}{Text.gem}", Command.confirm_buying)},
                 new[]
                 {
                     InlineKeyboardButton.WithCallbackData(Text.minus, $"{Command.count}-"),
@@ -254,7 +236,7 @@ namespace CardCollector.Resources
             });
         }
 
-        public static InlineKeyboardMarkup GetStickerKeyboard(StickerInfo stickerInfo)
+        public static InlineKeyboardMarkup GetStickerKeyboard(StickerEntity stickerInfo)
         {
             return new InlineKeyboardMarkup(new[] {
                 new[] {InlineKeyboardButton.WithSwitchInlineQuery(Text.send_sticker, stickerInfo.Title)},
@@ -277,19 +259,18 @@ namespace CardCollector.Resources
         {
             return session.State switch
             {
-                UserState.AuctionMenu when session.SelectedSticker.TraderInfo is not null => GetAuctionProductKeyboard(session.SelectedSticker),
+                UserState.ProductMenu => GetAuctionProductKeyboard(session.GetModule<AuctionModule>()),
                 UserState.AuctionMenu => GetAuctionStickerKeyboard(),
-                UserState.ShopMenu => GetShopStickerKeyboard(session.SelectedSticker),
-                UserState.CollectionMenu when session.CombineList.Count > 0 => GetCombineStickerKeyboard(session.SelectedSticker),
-                UserState.CollectionMenu => GetCollectionStickerKeyboard(session.SelectedSticker),
-                _ => GetStickerKeyboard(session.SelectedSticker)
+                UserState.CombineMenu => GetCombineStickerKeyboard(session.GetModule<CombineModule>()),
+                UserState.CollectionMenu => GetCollectionStickerKeyboard(session.GetModule<CollectionModule>()),
+                _ => GetStickerKeyboard(session.GetModule<DefaultModule>().SelectedSticker)
             };
         }
 
-        public static InlineKeyboardMarkup GetCombineStickerKeyboard(StickerInfo stickerInfo)
+        public static InlineKeyboardMarkup GetCombineStickerKeyboard(CombineModule module)
         {
             return new InlineKeyboardMarkup(new[] {
-                new[] {InlineKeyboardButton.WithCallbackData($"{Text.add} ({stickerInfo.Count})", Command.combine)},
+                new[] {InlineKeyboardButton.WithCallbackData($"{Text.add} ({module.Count})", Command.combine)},
                 new[]
                 {
                     InlineKeyboardButton.WithCallbackData(Text.minus, $"{Command.count}-"),
@@ -300,34 +281,29 @@ namespace CardCollector.Resources
             });
         }
 
-        public static InlineKeyboardMarkup GetCombineKeyboard(UserSession session)
+        public static InlineKeyboardMarkup GetCombineKeyboard(CombineModule module)
         {
             var keyboard = new List<InlineKeyboardButton[]>();
-            foreach (var (id, _) in session.CombineList)
+            foreach (var (sticker, _) in module.CombineList)
             {
                 keyboard.Add(new []{InlineKeyboardButton.WithCallbackData($"{Text.delete} {Text.sticker} {keyboard.Count + 1}",
-                    $"{Command.delete_combine}={id}")});
+                    $"{Command.delete_combine}={sticker.Md5Hash}")});
             }
             keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)});
-            if (session.GetCombineCount() == Constants.COMBINE_COUNT)
-            {
-                session.CalculateCombinePrice();
+            if (module.GetCombineCount() == Constants.COMBINE_COUNT)
                 keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(
-                    $"{Text.combine} {session.CombineCoinsPrice}{Text.coin}/{session.CombineGemsPrice}{Text.gem}", 
+                    $"{Text.combine} {module.CalculateCombinePrice()}{Text.coin}",
                     Command.combine_stickers)});
-            }
             else keyboard.Add(new[] {InlineKeyboardButton.WithSwitchInlineQueryCurrentChat(Text.add_sticker)});
             return new InlineKeyboardMarkup(keyboard);
         }
 
         /* Клавиатура, отображаемая вместе с сообщением профиля */
-        public static InlineKeyboardMarkup GetProfileKeyboard(UserEntity user)
+        public static InlineKeyboardMarkup GetProfileKeyboard(int income)
         {
             var keyboard = new List<InlineKeyboardButton[]>
             {
-                new[] {InlineKeyboardButton.WithCallbackData(
-                    $"{Text.collect} {user.Session.IncomeCoins}{Text.coin} {user.Session.IncomeGems}{Text.gem}", 
-                    Command.collect_income)},
+                new[] {InlineKeyboardButton.WithCallbackData($"{Text.collect} {income}{Text.coin}", Command.collect_income)},
             };
             
             return new InlineKeyboardMarkup(keyboard);

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

@@ -87,6 +87,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Невозможно изменить количество!.
+        /// </summary>
+        internal static string cant_change_count {
+            get {
+                return ResourceManager.GetString("cant_change_count", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Не удалось добавить стикеры. Необходимое количество уже собрано..
         /// </summary>

+ 3 - 0
CardCollector/Resources/Messages.resx

@@ -156,4 +156,7 @@
     <data name="successfully_selling" xml:space="preserve">
         <value>Товар успешно попал на аукцион</value>
     </data>
+    <data name="cant_change_count" xml:space="preserve">
+        <value>Невозможно изменить количество!</value>
+    </data>
 </root>

+ 2 - 0
CardCollector/Resources/UserState.cs

@@ -11,6 +11,8 @@
         AuctionMenu,
         /* Пользователь, загружающий файл */
         UploadFile,
+        CombineMenu,
+        ProductMenu,
         
         /* Пользователь в базовом состоянии (по умолчанию) */
         Default,

+ 7 - 0
CardCollector/Session/Module.cs

@@ -0,0 +1,7 @@
+namespace CardCollector.Session
+{
+    public interface Module
+    {
+        public void Reset();
+    }
+}

+ 21 - 0
CardCollector/Session/Modules/AuctionModule.cs

@@ -0,0 +1,21 @@
+using CardCollector.DataBase.Entity;
+
+namespace CardCollector.Session.Modules
+{
+    public class AuctionModule : Module
+    {
+        public StickerEntity SelectedSticker;
+        public AuctionEntity SelectedPosition;
+        public int Count = 1;
+        public int MaxCount => SelectedPosition.Count;
+        public int Price => SelectedPosition.Price;
+
+
+        public void Reset()
+        {
+            SelectedSticker = null;
+            SelectedPosition = null;
+            Count = 0;
+        }
+    }
+}

+ 18 - 0
CardCollector/Session/Modules/CollectionModule.cs

@@ -0,0 +1,18 @@
+using CardCollector.DataBase.Entity;
+
+namespace CardCollector.Session.Modules
+{
+    public class CollectionModule : Module
+    {
+        public StickerEntity SelectedSticker;
+        public int Count = 1;
+        public int SellPrice = 0;
+        
+        public void Reset()
+        {
+            SelectedSticker = null;
+            Count = 0;
+            SellPrice = 0;
+        }
+    }
+}

+ 42 - 0
CardCollector/Session/Modules/CombineModule.cs

@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.Linq;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+
+namespace CardCollector.Session.Modules
+{
+    public class CombineModule : Module
+    {
+        public StickerEntity SelectedSticker;
+        public int Count = 1;
+        public int Tier => SelectedSticker.Tier;
+        public Dictionary<StickerEntity, int> CombineList { get; } = new();
+        
+        public int CalculateCombinePrice()
+        {
+            var coinsSum = CombineList.Sum(pair => 1440 / pair.Key.IncomeTime * pair.Key.Income * pair.Value);
+            var multiplier = SelectedSticker.Tier * 0.25 + 1;
+            return (int)(coinsSum * multiplier);
+        }
+        
+        public int GetCombineCount()
+        {
+            return CombineList.Values.Sum();
+        }
+        
+        public void Reset()
+        {
+            SelectedSticker = null;
+            Count = 0;
+            CombineList.Clear();
+        }
+        
+        public override string ToString()
+        {
+            var message = $"{Text.added_stickers} {GetCombineCount()}/{Constants.COMBINE_COUNT}:";
+            foreach (var ((sticker, count), index) in CombineList.WithIndex())
+                message += $"\n{Text.sticker} {index + 1}: {sticker.Title} {count}{Text.items}";
+            return message;
+        }
+    }
+}

+ 13 - 0
CardCollector/Session/Modules/DefaultModule.cs

@@ -0,0 +1,13 @@
+using CardCollector.DataBase.Entity;
+
+namespace CardCollector.Session.Modules
+{
+    public class DefaultModule : Module
+    {
+        public StickerEntity SelectedSticker;
+        public void Reset()
+        {
+            SelectedSticker = null;
+        }
+    }
+}

+ 110 - 0
CardCollector/Session/Modules/FiltersModule.cs

@@ -0,0 +1,110 @@
+using System.Collections.Generic;
+using System.Linq;
+using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
+using CardCollector.Resources;
+
+namespace CardCollector.Session.Modules
+{
+    public class FiltersModule : Module
+    {
+        /* Фильтры, примененные пользователем в меню коллекции/магазина/аукциона */
+        public readonly Dictionary<string, object> Filters = new()
+        {
+            {Command.author, ""},
+            {Command.tier, -1},
+            {Command.emoji, ""},
+            {Command.price_coins_from, 0},
+            {Command.price_coins_to, 0},
+            {Command.price_gems_from, 0},
+            {Command.price_gems_to, 0},
+            {Command.sort, SortingTypes.None},
+        };
+
+        public string ToString(UserState state)
+        {
+            var text = $"{Messages.current_filters}\n" +
+                       $"{Messages.author} {(Filters[Command.author] is string author and not "" ? author : Messages.all)}\n" +
+                       $"{Messages.tier} {(Filters[Command.tier] is int tier and not -1 ? new string('⭐', tier) : Messages.all)}\n" +
+                       $"{Messages.emoji} {(Filters[Command.emoji] is string emoji and not "" ? emoji : Messages.all)}\n";
+            switch (state)
+            {
+                case UserState.AuctionMenu:
+                    text += $"{Messages.price} 💎 {Filters[Command.price_gems_from]} -" +
+                            $" {(Filters[Command.price_gems_to] is int g and not 0 ? g : "∞")}\n";
+                    break;
+                case UserState.ShopMenu:
+                    text += $"{Messages.price} 💰 {Filters[Command.price_coins_from]} -" +
+                            $" {(Filters[Command.price_coins_to] is int c and not 0 ? c : "∞")}\n";
+                    break;
+            }
+
+            text += $"{Messages.sorting} {Filters[Command.sort]}\n\n{Messages.select_filter}";
+            return text;
+        }
+        
+        public IEnumerable<StickerEntity> ApplyTo(IEnumerable<StickerEntity> list)
+        {
+            /* Фильтруем по автору */
+            if (Filters[Command.author] is string author && author != "")
+                list = list.Where(item => item.Author.Contains(author));
+            /* Фильтруем по тиру */
+            if (Filters[Command.tier] is int tier && tier != -1)
+                list = list.Where(item => item.Tier.Equals(tier));
+            /* Фильтруем по эмоции */
+            if (Filters[Command.emoji] is string emoji && emoji != "")
+                list = list.Where(item => item.Emoji.Contains(emoji));
+            /* Сортируем список, если тип сортировки установлен */
+            if (Filters[Command.sort] is not string sort || sort == SortingTypes.None) return list;
+            {
+                /* Сортируем по автору */
+                if (sort== SortingTypes.ByAuthor)
+                    list = list.OrderBy(item => item.Author);
+                /* Сортируем по названию */
+                if (sort == SortingTypes.ByTitle)
+                    list = list.OrderBy(item => item.Title);
+                /* Сортируем по увеличению тира */
+                if (sort == SortingTypes.ByTierIncrease)
+                    list = list.OrderBy(item => item.Tier);
+                /* Сортируем по уменьшению тира */
+                if (sort == SortingTypes.ByTierDecrease)
+                    list = list.OrderByDescending(item => item.Tier);
+            }
+            return list;
+        }
+        
+        public IEnumerable<StickerEntity> ApplyPriceTo(IEnumerable<StickerEntity> list)
+        {
+            /* Фильтруем по цене алмазов ОТ */
+            if (Filters[Command.price_gems_from] is int PGF && PGF != 0)
+                list = list.Where(item => AuctionDao.HaveAny(item.Id, i => i.Price >= PGF));
+            /* Фильтруем по цене адмазов ДО */
+            if (Filters[Command.price_gems_to] is int PGT && PGT != 0)
+                list = list.Where(item => AuctionDao.HaveAny(item.Id, i => i.Price <= PGT));
+            return list;
+        }
+        
+        public IEnumerable<AuctionEntity> ApplyPriceTo(IEnumerable<AuctionEntity> list)
+        {
+            /* Фильтруем по цене алмазов ОТ */
+            if (Filters[Command.price_gems_from] is int PGF && PGF != 0)
+                list = list.Where(item => item.Price >= PGF);
+            /* Фильтруем по цене адмазов ДО */
+            if (Filters[Command.price_gems_to] is int PGT && PGT != 0)
+                list = list.Where(item => item.Price <= PGT);
+            return list;
+        }
+        
+        public void Reset()
+        {
+            Filters[Command.author] = "";
+            Filters[Command.tier] = -1;
+            Filters[Command.emoji] = "";
+            Filters[Command.price_coins_from] = 0;
+            Filters[Command.price_coins_to] = 0;
+            Filters[Command.price_gems_from] = 0;
+            Filters[Command.price_gems_to] = 0;
+            Filters[Command.sort] = SortingTypes.None;
+        }
+    }
+}

+ 13 - 0
CardCollector/Session/Modules/ShopModule.cs

@@ -0,0 +1,13 @@
+using CardCollector.DataBase.Entity;
+
+namespace CardCollector.Session.Modules
+{
+    public class ShopModule : Module
+    {
+        public ShopEntity SelectedPosition;
+        public void Reset()
+        {
+            SelectedPosition = null;
+        }
+    }
+}

+ 79 - 0
CardCollector/Session/Session.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+
+namespace CardCollector.Session
+{
+    public class UserSession
+    {
+        /* Ссылка на пользователя */
+        private readonly UserEntity user;
+        /* Дата и время последней актвности пользователя */
+        private DateTime _lastAccess = DateTime.Now;
+        /* Текущее состояние пользователя */
+        public UserState State = UserState.Default;
+        /* Подключаемые модули */
+        private readonly Dictionary<Type, Module> Modules = new();
+        /* Сообщения в чате пользователя */
+        public readonly List<int> Messages = new();
+
+        public UserSession(UserEntity user)
+        {
+            this.user = user;
+        }
+
+        public T InitNewModule<T>() where T : Module
+        {
+            Modules.Add(typeof(T), Activator.CreateInstance<T>());
+            return (T) Modules[typeof(T)];
+        }
+
+        public T GetModule<T>() where T : Module
+        {
+            try {
+                return (T) Modules[typeof(T)];
+            } catch {
+                return InitNewModule<T>();
+            }
+        }
+
+        public void ResetModule<T>() where T : Module
+        {
+            Modules[typeof(T)].Reset();
+        }
+
+        public void DeleteModule<T>() where T : Module
+        {
+            Modules.Remove(typeof(T));
+        }
+        
+        public void UpdateLastAccess()
+        {
+            _lastAccess = DateTime.Now;
+        }
+
+        public int GetLastAccessInterval()
+        {
+            return (int) (DateTime.Now - _lastAccess).TotalMinutes;
+        }
+
+
+        public async Task ClearMessages()
+        {
+            foreach (var messageId in Messages)
+                await MessageController.DeleteMessage(user, messageId, false);
+            Messages.Clear();
+        }
+
+        public async void EndSession()
+        {
+            await ClearMessages();
+            State = UserState.Default;
+            foreach (var module in Modules.Values) module.Reset();
+            Modules.Clear();
+        }
+    }
+}