Browse Source

Menu stack-back system. Some changes on shop

Tigran 3 years ago
parent
commit
b3a8ae1d79
100 changed files with 760 additions and 867 deletions
  1. 2 2
      CardCollector.sln.DotSettings.user
  2. 0 1
      CardCollector/Bot.cs
  3. 0 37
      CardCollector/Commands/CallbackQuery/AuthorMenu.cs
  4. 38 0
      CardCollector/Commands/CallbackQuery/AuthorsMenu.cs
  5. 25 0
      CardCollector/Commands/CallbackQuery/Back.cs
  6. 0 24
      CardCollector/Commands/CallbackQuery/BackToCombine.cs
  7. 0 50
      CardCollector/Commands/CallbackQuery/BackToFiltersMenu.cs
  8. 35 0
      CardCollector/Commands/CallbackQuery/BuyAuthorPackMenu.cs
  9. 0 81
      CardCollector/Commands/CallbackQuery/BuyByCoins.cs
  10. 0 81
      CardCollector/Commands/CallbackQuery/BuyByGems.cs
  11. 2 0
      CardCollector/Commands/CallbackQuery/BuyCoins.cs
  12. 3 0
      CardCollector/Commands/CallbackQuery/BuyGems.cs
  13. 3 0
      CardCollector/Commands/CallbackQuery/BuyPack.cs
  14. 91 0
      CardCollector/Commands/CallbackQuery/BuyShopItem.cs
  15. 3 1
      CardCollector/Commands/CallbackQuery/BuySticker.cs
  16. 0 45
      CardCollector/Commands/CallbackQuery/Cancel.cs
  17. 0 19
      CardCollector/Commands/CallbackQuery/ClearChat.cs
  18. 3 0
      CardCollector/Commands/CallbackQuery/CollectIncome.cs
  19. 4 1
      CardCollector/Commands/CallbackQuery/Combine.cs
  20. 28 0
      CardCollector/Commands/CallbackQuery/CombineMenu.cs
  21. 3 0
      CardCollector/Commands/CallbackQuery/CombineStickers.cs
  22. 3 0
      CardCollector/Commands/CallbackQuery/ConfirmBuying.cs
  23. 3 0
      CardCollector/Commands/CallbackQuery/ConfirmExchange.cs
  24. 3 0
      CardCollector/Commands/CallbackQuery/ConfirmationSelling.cs
  25. 3 0
      CardCollector/Commands/CallbackQuery/Count.cs
  26. 3 1
      CardCollector/Commands/CallbackQuery/DailyTasks.cs
  27. 4 1
      CardCollector/Commands/CallbackQuery/DeleteCombine.cs
  28. 4 1
      CardCollector/Commands/CallbackQuery/EndUploadStickers.cs
  29. 9 7
      CardCollector/Commands/CallbackQuery/MyPacks.cs
  30. 45 0
      CardCollector/Commands/CallbackQuery/OpenAuthorPackMenu.cs
  31. 15 40
      CardCollector/Commands/CallbackQuery/OpenPack.cs
  32. 0 36
      CardCollector/Commands/CallbackQuery/OpenSpecific.cs
  33. 3 0
      CardCollector/Commands/CallbackQuery/PackInfo.cs
  34. 3 2
      CardCollector/Commands/CallbackQuery/PutForAuction.cs
  35. 0 30
      CardCollector/Commands/CallbackQuery/SelectAuthor.cs
  36. 3 0
      CardCollector/Commands/CallbackQuery/SelectEmoji.cs
  37. 6 2
      CardCollector/Commands/CallbackQuery/SelectOffer.cs
  38. 4 1
      CardCollector/Commands/CallbackQuery/SelectPrice.cs
  39. 33 0
      CardCollector/Commands/CallbackQuery/SelectShopPack.cs
  40. 3 0
      CardCollector/Commands/CallbackQuery/SelectSort.cs
  41. 3 0
      CardCollector/Commands/CallbackQuery/SelectTier.cs
  42. 4 1
      CardCollector/Commands/CallbackQuery/SetFilter.cs
  43. 14 9
      CardCollector/Commands/CallbackQuery/ShowInfo.cs
  44. 9 2
      CardCollector/Commands/CallbackQuery/SpecialOffers.cs
  45. 8 13
      CardCollector/Commands/CallbackQueryCommand.cs
  46. 3 1
      CardCollector/Commands/ChosenInlineResult/GetUnlimitedStickerAndExecuteCommand.cs
  47. 3 0
      CardCollector/Commands/ChosenInlineResult/SelectStickerInline.cs
  48. 3 0
      CardCollector/Commands/ChosenInlineResult/SelectTrader.cs
  49. 3 1
      CardCollector/Commands/ChosenInlineResult/SendPrivateSticker.cs
  50. 3 0
      CardCollector/Commands/ChosenInlineResult/SendSticker.cs
  51. 3 0
      CardCollector/Commands/CommandNotFound.cs
  52. 3 0
      CardCollector/Commands/IgnoreUpdate.cs
  53. 3 1
      CardCollector/Commands/InlineQuery/ShowAuctionStickers.cs
  54. 3 0
      CardCollector/Commands/InlineQuery/ShowCollectionStickers.cs
  55. 3 0
      CardCollector/Commands/InlineQuery/ShowCombineStickers.cs
  56. 3 1
      CardCollector/Commands/InlineQuery/ShowStickersInBotChat.cs
  57. 3 1
      CardCollector/Commands/InlineQuery/ShowStickersInGroup.cs
  58. 3 1
      CardCollector/Commands/InlineQuery/ShowStickersInPrivate.cs
  59. 3 1
      CardCollector/Commands/InlineQuery/ShowTradersInBotChat.cs
  60. 4 1
      CardCollector/Commands/Message/Auction.cs
  61. 4 1
      CardCollector/Commands/Message/Collection.cs
  62. 3 0
      CardCollector/Commands/Message/CreateToken.cs
  63. 4 1
      CardCollector/Commands/Message/DownloadStickerPack.cs
  64. 2 0
      CardCollector/Commands/Message/EnterEmoji.cs
  65. 3 1
      CardCollector/Commands/Message/EnterGemsExchange.cs
  66. 3 1
      CardCollector/Commands/Message/EnterGemsPrice.cs
  67. 3 0
      CardCollector/Commands/Message/Menu.cs
  68. 4 0
      CardCollector/Commands/Message/Profile.cs
  69. 3 0
      CardCollector/Commands/Message/Shop.cs
  70. 4 22
      CardCollector/Commands/Message/ShowFiltersMenu.cs
  71. 3 0
      CardCollector/Commands/Message/ShowSample.cs
  72. 3 1
      CardCollector/Commands/Message/Start.cs
  73. 3 1
      CardCollector/Commands/Message/StopBot.cs
  74. 3 0
      CardCollector/Commands/Message/UploadFile.cs
  75. 3 0
      CardCollector/Commands/Message/UploadSticker.cs
  76. 3 0
      CardCollector/Commands/MyChatMember/MyChatMemberCommand.cs
  77. 3 0
      CardCollector/Commands/PreCheckoutQuery/BuyGems.cs
  78. 2 0
      CardCollector/Commands/PreCheckoutQuery/Test.cs
  79. 10 0
      CardCollector/Commands/UpdateModel.cs
  80. 6 8
      CardCollector/Controllers/MessageController.cs
  81. 2 2
      CardCollector/DailyTasks/CustomTasks/SendStickers.cs
  82. 0 1
      CardCollector/DailyTasks/DailyTask.cs
  83. 1 2
      CardCollector/DataBase/CardCollectorDatabase.cs
  84. 3 0
      CardCollector/DataBase/Entity/PackEntity.cs
  85. 1 1
      CardCollector/DataBase/Entity/UserEntity.cs
  86. 5 4
      CardCollector/DataBase/Entity/UserPacks.cs
  87. 0 16
      CardCollector/DataBase/Entity/UsersPacksEntity.cs
  88. 1 1
      CardCollector/DataBase/EntityDao/PacksDao.cs
  89. 0 42
      CardCollector/DataBase/EntityDao/SpecificPackDao.cs
  90. 3 4
      CardCollector/DataBase/EntityDao/StickerDao.cs
  91. 32 0
      CardCollector/DataBase/EntityDao/UserPacksDao.cs
  92. 0 25
      CardCollector/DataBase/EntityDao/UsersPacksDao.cs
  93. 35 80
      CardCollector/Resources/Command.Designer.cs
  94. 8 23
      CardCollector/Resources/Command.resx
  95. 79 129
      CardCollector/Resources/Keyboard.cs
  96. 3 3
      CardCollector/Resources/Messages.Designer.cs
  97. 2 2
      CardCollector/Resources/Messages.resx
  98. 9 0
      CardCollector/Resources/Text.Designer.cs
  99. 3 0
      CardCollector/Resources/Text.resx
  100. 30 0
      CardCollector/Session/MenuInformation.cs

+ 2 - 2
CardCollector.sln.DotSettings.user

@@ -11,12 +11,12 @@
 	
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FDailyTasks_002FTitles/@EntryIndexedValue">False</s:Boolean>
 	
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FCommand/@EntryIndexedValue">False</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FCommand/@EntryIndexedValue">True</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_002FMessages/@EntryIndexedValue">False</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">False</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FStickerEffects_002FEffectTranslations/@EntryIndexedValue">False</s:Boolean>

+ 0 - 1
CardCollector/Bot.cs

@@ -81,7 +81,6 @@ namespace CardCollector
             var users = await UserDao.GetAllWhere(user => Task.FromResult(!user.IsBlocked));
             foreach (var user in users)
                 await SendMessage(user, Messages.daily_task_alertation, Keyboard.Menu);
-            Utilities.SetUpTimer(Constants.DailyTaskAlert, DailyTaskAlert);
         }
     }
 }

+ 0 - 37
CardCollector/Commands/CallbackQuery/AuthorMenu.cs

@@ -1,37 +0,0 @@
-using System.Linq;
-using System.Threading.Tasks;
-using CardCollector.Controllers;
-using CardCollector.DataBase.Entity;
-using CardCollector.DataBase.EntityDao;
-using CardCollector.Resources;
-using Telegram.Bot.Types;
-
-namespace CardCollector.Commands.CallbackQuery
-{
-    public class AuthorMenu : CallbackQueryCommand
-    {
-        protected override string CommandText => Command.author_menu;
-        public override async Task Execute()
-        {
-            var packs = await UsersPacksDao.GetUserPacks(User.Id);
-            if (packs.AuthorCount == 0)
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.packs_count_zero, true);
-            else
-            {
-                var page = int.Parse(CallbackData.Split('=')[1]);
-                var low = page * 10 - 10;
-                var up = page * 10;
-                var authors = await PacksDao.GetAll();
-                authors = authors.Where((_, index) => index >= low && index < up).ToList();
-                if (authors.Count == 0)
-                    await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.page_not_found);
-                else
-                    await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_author,
-                        Keyboard.GetAuthorsKeyboard(authors, page));
-            }
-        }
-
-        public AuthorMenu() { }
-        public AuthorMenu(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 38 - 0
CardCollector/Commands/CallbackQuery/AuthorsMenu.cs

@@ -0,0 +1,38 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    /* Реализует нажатие на кнопку "Автор" (открывается меню с выбором автора) */
+    public class AuthorsMenu : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.authors_menu;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => true;
+
+        public override async Task Execute()
+        {
+            var page = int.Parse(CallbackData.Split('=')[1]);
+            /* Получаем из бд список всех авторов */
+            var list = await StickerDao.GetAuthorsList();
+            var totalCount = list.Count;
+            list = list.GetRange((page - 1) * 10, list.Count >= page * 10 ? 10 : list.Count % 10);
+            if (list.Count == 0)
+            {
+                User.Session.PopLast();
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.page_not_found);
+            }
+            /* Заменяем сообщение меню на сообщение со списком */
+            else
+                await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_author,
+                    Keyboard.GetAuthorsKeyboard(list, Keyboard.GetPagePanel(page, totalCount, CommandText)));
+        }
+
+        public AuthorsMenu() { }
+        public AuthorsMenu(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 25 - 0
CardCollector/Commands/CallbackQuery/Back.cs

@@ -0,0 +1,25 @@
+using System.Threading.Tasks;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    public class Back : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.back;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+        
+        public override async Task Execute()
+        {
+            await User.ClearChat();
+            if (User.Session.TryGetPreviousMenu(out var menu))
+                await menu.BackToThis(User.Session);
+            else await User.Session.EndSession();
+        }
+        
+        public Back() { }
+        public Back(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 0 - 24
CardCollector/Commands/CallbackQuery/BackToCombine.cs

@@ -1,24 +0,0 @@
-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.CallbackQuery
-{
-    public class BackToCombine : CallbackQueryCommand
-    {
-        protected override string CommandText => Command.back_to_combine;
-        public override async Task Execute()
-        {
-            await User.ClearChat();
-            var combineModule = User.Session.GetModule<CombineModule>();
-            var message = await MessageController.SendMessage(User, combineModule.ToString(), Keyboard.GetCombineKeyboard(combineModule));
-            User.Session.Messages.Add(message.MessageId);
-        }
-
-        public BackToCombine() { }
-        public BackToCombine(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 0 - 50
CardCollector/Commands/CallbackQuery/BackToFiltersMenu.cs

@@ -1,50 +0,0 @@
-using System.Threading.Tasks;
-using CardCollector.Commands.Message;
-using CardCollector.Controllers;
-using CardCollector.DataBase.Entity;
-using CardCollector.Resources;
-using CardCollector.Session.Modules;
-using Telegram.Bot.Types;
-
-namespace CardCollector.Commands.CallbackQuery
-{
-    public class BackToFiltersMenu : CallbackQueryCommand
-    {
-        protected override string CommandText => Command.back;
-        public override async Task Execute()
-        {
-            /* Удаляем пользователя из очереди */
-            EnterEmoji.RemoveFromQueue(User.Id);
-            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 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() { }
-        public BackToFiltersMenu(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 35 - 0
CardCollector/Commands/CallbackQuery/BuyAuthorPackMenu.cs

@@ -0,0 +1,35 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    public class BuyAuthorPackMenu : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.buy_author_pack_menu;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => true;
+        
+        public override async Task Execute()
+        {
+            var page = int.Parse(CallbackData.Split('=')[1]);
+            var packs = await PacksDao.GetAll();
+            var totalCount = packs.Count;
+            packs = packs.GetRange((page - 1) * 10, packs.Count >= page * 10 ? 10 : packs.Count % 10);
+            if (packs.Count == 0)
+            {
+                User.Session.PopLast();
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.page_not_found);
+            }
+            else
+                await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_author,
+                    Keyboard.GetShopPacksKeyboard(packs, Keyboard.GetPagePanel(page, totalCount, CommandText)));
+        }
+
+        public BuyAuthorPackMenu() { }
+        public BuyAuthorPackMenu(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 0 - 81
CardCollector/Commands/CallbackQuery/BuyByCoins.cs

@@ -1,81 +0,0 @@
-using System;
-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
-{
-    public class BuyByCoins : CallbackQueryCommand
-    {
-        protected override string CommandText => Command.buy_by_coins;
-        public override async Task Execute()
-        {
-            var offerInfo = User.Session.GetModule<ShopModule>().SelectedPosition;
-            if (User.Cash.Coins < offerInfo.ResultPriceCoins)
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_coins);
-            else if (offerInfo.Expired)
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.offer_expired);
-            else if (offerInfo.IsSpecial && !offerInfo.IsInfinite && await SpecialOfferUsersDao.NowUsed(User.Id, offerInfo.Id))
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.you_already_use_this_offer);
-            else
-            {
-                await User.ClearChat();
-                User.Cash.Coins -= offerInfo.ResultPriceCoins;
-                if (offerInfo.IsSpecial && !offerInfo.IsInfinite)
-                    await SpecialOfferUsersDao.AddNew(User.Id, offerInfo.Id);
-                var userPacks = await UsersPacksDao.GetUserPacks(User.Id);
-                switch (offerInfo.PackId)
-                {
-                    case 1:
-                        userPacks.RandomCount += offerInfo.Count;
-                        break;
-                    case 2:
-                        userPacks.AuthorCount += offerInfo.Count;
-                        break;
-                    default:
-                        var info = await SpecificPackDao.GetInfo(User.Id, offerInfo.PackId);
-                        info.Count += offerInfo.Count;
-                        break;
-                }
-                await MessageController.EditMessage(User, CallbackMessageId, Messages.thanks_for_buying);
-                if (offerInfo.AdditionalPrize != "") await GivePrize(offerInfo.AdditionalPrize);
-            }
-        }
-
-        private async Task GivePrize(string prizeInfo)
-        {
-            var data = prizeInfo.Split('=');
-            StickerEntity sticker = null;
-            switch (data[0])
-            {
-                case "tier":
-                    var tier = int.Parse(data[1]);
-                    var stickers = await StickerDao.GetListWhere(item => item.Tier == tier);
-                    var rnd = new Random();
-                    sticker = stickers[rnd.Next(stickers.Count)];
-                    break;
-                case "sticker":
-                    sticker = await StickerDao.GetStickerByHash(data[1]);
-                    break;
-            }
-            if (sticker != null)
-            {
-                if (!User.Stickers.ContainsKey(sticker.Md5Hash))
-                    await UserStickerRelationDao.AddNew(User, sticker, 1);
-                else
-                    User.Stickers[sticker.Md5Hash].Count ++;
-                var stickerMessage = await MessageController.SendSticker(User, sticker.Id);
-                var message = await MessageController.SendMessage(User, $"{Messages.congratulation}\n{sticker}");
-                User.Session.Messages.Add(stickerMessage.MessageId);
-                User.Session.Messages.Add(message.MessageId);
-            }
-        }
-
-        public BuyByCoins() { }
-        public BuyByCoins(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 0 - 81
CardCollector/Commands/CallbackQuery/BuyByGems.cs

@@ -1,81 +0,0 @@
-using System;
-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
-{
-    public class BuyByGems : CallbackQueryCommand
-    {
-        protected override string CommandText => Command.buy_by_gems;
-        public override async Task Execute()
-        {
-            var offerInfo = User.Session.GetModule<ShopModule>().SelectedPosition;
-            if (User.Cash.Gems < offerInfo.ResultPriceGems)
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_gems);
-            else if (offerInfo.Expired)
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.offer_expired);
-            else if (offerInfo.IsSpecial && !offerInfo.IsInfinite && await SpecialOfferUsersDao.NowUsed(User.Id, offerInfo.Id))
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.you_already_use_this_offer);
-            else
-            {
-                await User.ClearChat();
-                User.Cash.Gems -= offerInfo.ResultPriceGems;
-                if (offerInfo.IsSpecial && !offerInfo.IsInfinite)
-                    await SpecialOfferUsersDao.AddNew(User.Id, offerInfo.Id);
-                var userPacks = await UsersPacksDao.GetUserPacks(User.Id);
-                switch (offerInfo.PackId)
-                {
-                    case 1:
-                        userPacks.RandomCount += offerInfo.Count;
-                        break;
-                    case 2:
-                        userPacks.AuthorCount += offerInfo.Count;
-                        break;
-                    default:
-                        var info = await SpecificPackDao.GetInfo(User.Id, offerInfo.PackId);
-                        info.Count += offerInfo.Count;
-                        break;
-                }
-                await MessageController.EditMessage(User, CallbackMessageId, Messages.thanks_for_buying);
-                if (offerInfo.AdditionalPrize != "") await GivePrize(offerInfo.AdditionalPrize);
-            }
-        }
-
-        private async Task GivePrize(string prizeInfo)
-        {
-            var data = prizeInfo.Split('=');
-            StickerEntity sticker = null;
-            switch (data[0])
-            {
-                case "tier":
-                    var tier = int.Parse(data[1]);
-                    var stickers = await StickerDao.GetListWhere(item => item.Tier == tier);
-                    var rnd = new Random();
-                    sticker = stickers[rnd.Next(stickers.Count)];
-                    break;
-                case "sticker":
-                    sticker = await StickerDao.GetStickerByHash(data[1]);
-                    break;
-            }
-            if (sticker != null)
-            {
-                if (!User.Stickers.ContainsKey(sticker.Md5Hash))
-                    await UserStickerRelationDao.AddNew(User, sticker, 1);
-                else
-                    User.Stickers[sticker.Md5Hash].Count ++;
-                var stickerMessage = await MessageController.SendSticker(User, sticker.Id);
-                var message = await MessageController.SendMessage(User, $"{Messages.congratulation}\n{sticker}");
-                User.Session.Messages.Add(stickerMessage.MessageId);
-                User.Session.Messages.Add(message.MessageId);
-            }
-        }
-
-        public BuyByGems() { }
-        public BuyByGems(UserEntity user, Update update) : base(user, update) { }
-    }
-}

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

@@ -11,6 +11,8 @@ namespace CardCollector.Commands.CallbackQuery
     public class BuyCoins : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_coins;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
 
         public override async Task Execute()
         {

+ 3 - 0
CardCollector/Commands/CallbackQuery/BuyGems.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class BuyGems : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_gems;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+        
         public override async Task Execute()
         {
             await User.ClearChat();

+ 3 - 0
CardCollector/Commands/CallbackQuery/BuyPack.cs

@@ -9,6 +9,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class BuyPack : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_pack;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => true;
+        
         public override async Task Execute()
         {
             await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_option,

+ 91 - 0
CardCollector/Commands/CallbackQuery/BuyShopItem.cs

@@ -0,0 +1,91 @@
+using System;
+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
+{
+    public class BuyShopItem : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.buy_shop_item;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
+        public override async Task Execute()
+        {
+            var module = User.Session.GetModule<ShopModule>();
+            var resultPriceCoins = module.SelectedPosition?.ResultPriceCoins 
+                                   ?? module.SelectedPack?.PriceCoins * module.Count ?? -1; 
+            var resultPriceGems = module.SelectedPosition?.ResultPriceGems 
+                                  ?? module.SelectedPack?.PriceGems * module.Count ?? -1;
+            var offerExpired = module.SelectedPosition?.Expired ?? false;
+            var offerSpecial = module.SelectedPosition?.IsSpecial ?? false;
+            var offerInfinite = module.SelectedPosition?.IsInfinite ?? true;
+            var offerUsed = module.SelectedPosition != null 
+                ? await SpecialOfferUsersDao.NowUsed(User.Id, module.SelectedPosition.Id)
+                : false;
+            var currency = CallbackData.Split('=')[1];
+            
+            if (currency == "coins" && User.Cash.Coins < resultPriceCoins)
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_coins);
+            else if (currency == "gems" && User.Cash.Gems < resultPriceGems)
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_gems);
+            else if (offerExpired)
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.offer_expired);
+            else if (offerSpecial && !offerInfinite && offerUsed)
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.you_already_use_this_offer);
+            else
+            {
+                await User.ClearChat();
+                if (currency == "coins") User.Cash.Coins -= resultPriceCoins;
+                else if (currency == "gems") User.Cash.Gems -= resultPriceGems;
+                
+                if (offerSpecial && !offerInfinite)
+                    await SpecialOfferUsersDao.AddNew(User.Id, module.SelectedPosition.Id);
+                var packId = module.SelectedPosition?.PackId ?? module.SelectedPack?.Id ?? 1;
+                var userPack = await UserPacksDao.GetOne(User.Id, packId);
+                userPack.Count += module.SelectedPosition?.Count ?? module.Count;
+                if (module.SelectedPosition?.AdditionalPrize != "") await GivePrize(module.SelectedPosition?.AdditionalPrize);
+                var message = await MessageController.SendMessage(User, Messages.thanks_for_buying);
+                User.Session.Messages.Add(message.MessageId);
+            }
+        }
+
+        private async Task GivePrize(string prizeInfo)
+        {
+            if (prizeInfo == null) return;
+            var data = prizeInfo.Split('=');
+            StickerEntity sticker = null;
+            switch (data[0])
+            {
+                case "tier":
+                    var tier = int.Parse(data[1]);
+                    var stickers = await StickerDao.GetListWhere(item => item.Tier == tier);
+                    var rnd = new Random();
+                    sticker = stickers[rnd.Next(stickers.Count)];
+                    break;
+                case "sticker":
+                    sticker = await StickerDao.GetStickerByHash(data[1]);
+                    break;
+            }
+            if (sticker != null)
+            {
+                if (!User.Stickers.ContainsKey(sticker.Md5Hash))
+                    await UserStickerRelationDao.AddNew(User, sticker, 1);
+                else
+                    User.Stickers[sticker.Md5Hash].Count ++;
+                var stickerMessage = await MessageController.SendSticker(User, sticker.Id);
+                var message = await MessageController.SendMessage(User, $"{Messages.congratulation}\n{sticker}");
+                User.Session.Messages.Add(stickerMessage.MessageId);
+                User.Session.Messages.Add(message.MessageId);
+            }
+        }
+
+        public BuyShopItem() { }
+        public BuyShopItem(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 3 - 1
CardCollector/Commands/CallbackQuery/BuySticker.cs

@@ -11,6 +11,8 @@ namespace CardCollector.Commands.CallbackQuery
     public class BuySticker : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_sticker;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
 
         public override async Task Execute()
         {
@@ -18,7 +20,7 @@ namespace CardCollector.Commands.CallbackQuery
             if (auctionModule.Count > auctionModule.MaxCount)
             {
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_stickers);
-                await new BackToFiltersMenu(User, Update).Execute();
+                await new Back(User, Update).PrepareAndExecute();
             }
             else if (auctionModule.Price * auctionModule.Count > User.Cash.Gems)
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_gems);

+ 0 - 45
CardCollector/Commands/CallbackQuery/Cancel.cs

@@ -1,45 +0,0 @@
-using System.Threading.Tasks;
-using CardCollector.Commands.Message;
-using CardCollector.DataBase.Entity;
-using CardCollector.Resources;
-using CardCollector.Session.Modules;
-using Telegram.Bot.Types;
-
-namespace CardCollector.Commands.CallbackQuery
-{
-    public class Cancel : CallbackQueryCommand
-    {
-        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;
-            EnterEmoji.RemoveFromQueue(User.Id);
-            EnterGemsPrice.RemoveFromQueue(User.Id);
-            await User.ClearChat();
-        }
-
-        public Cancel() { }
-        public Cancel(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 0 - 19
CardCollector/Commands/CallbackQuery/ClearChat.cs

@@ -1,19 +0,0 @@
-using System.Threading.Tasks;
-using CardCollector.DataBase.Entity;
-using CardCollector.Resources;
-using Telegram.Bot.Types;
-
-namespace CardCollector.Commands.CallbackQuery
-{
-    public class ClearChat : CallbackQueryCommand
-    {
-        protected override string CommandText => Command.clear_chat;
-        public override async Task Execute()
-        {
-            await User.ClearChat();
-        }
-
-        public ClearChat() { }
-        public ClearChat(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 3 - 0
CardCollector/Commands/CallbackQuery/CollectIncome.cs

@@ -9,6 +9,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class CollectIncome : CallbackQueryCommand
     {
         protected override string CommandText => Command.collect_income;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var result = await User.Cash.Payout(User.Stickers);

+ 4 - 1
CardCollector/Commands/CallbackQuery/Combine.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class Combine : CallbackQueryCommand
     {
         protected override string CommandText => Command.combine;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             User.Session.State = UserState.CombineMenu;
@@ -42,7 +45,7 @@ namespace CardCollector.Commands.CallbackQuery
                 }
                 else combineModule.CombineList.Add(selectedSticker, combineModule.Count);
             }
-            await new BackToCombine(User, Update).Execute();
+            await new CombineMenu(User, Update).PrepareAndExecute();
         }
 
         public Combine() { }

+ 28 - 0
CardCollector/Commands/CallbackQuery/CombineMenu.cs

@@ -0,0 +1,28 @@
+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.CallbackQuery
+{
+    public class CombineMenu : CallbackQueryCommand
+    {
+        protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => true;
+
+        public override async Task Execute()
+        {
+            var combineModule = User.Session.GetModule<CombineModule>();
+            await User.ClearChat();
+            await MessageController.EditMessage(User, CallbackMessageId, combineModule.ToString(), 
+                Keyboard.GetCombineKeyboard(combineModule));
+        }
+
+        protected internal override bool IsMatches(UserEntity user, Update update) => false;
+
+        public CombineMenu(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 3 - 0
CardCollector/Commands/CallbackQuery/CombineStickers.cs

@@ -13,6 +13,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class CombineStickers : CallbackQueryCommand
     {
         protected override string CommandText => Command.combine_stickers;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var combineModule = User.Session.GetModule<CombineModule>();

+ 3 - 0
CardCollector/Commands/CallbackQuery/ConfirmBuying.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class ConfirmBuying : CallbackQueryCommand
     {
         protected override string CommandText => Command.confirm_buying;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var auctionModule = User.Session.GetModule<AuctionModule>();

+ 3 - 0
CardCollector/Commands/CallbackQuery/ConfirmExchange.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class ConfirmExchange : CallbackQueryCommand
     {
         protected override string CommandText => Command.confirm_exchange;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var module = User.Session.GetModule<ShopModule>();

+ 3 - 0
CardCollector/Commands/CallbackQuery/ConfirmationSelling.cs

@@ -11,6 +11,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class ConfirmationSelling : CallbackQueryCommand
     {
         protected override string CommandText => Command.confirm_selling;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var collectionModule = User.Session.GetModule<CollectionModule>();

+ 3 - 0
CardCollector/Commands/CallbackQuery/Count.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class Count : CallbackQueryCommand
     {
         protected override string CommandText => Command.count;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var (stickerCount, maxCount) = User.Session.State switch

+ 3 - 1
CardCollector/Commands/CallbackQuery/DailyTasks.cs

@@ -11,7 +11,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class DailyTasks : CallbackQueryCommand
     {
         protected override string CommandText => Command.daily_tasks;
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await User.ClearChat();

+ 4 - 1
CardCollector/Commands/CallbackQuery/DeleteCombine.cs

@@ -11,13 +11,16 @@ namespace CardCollector.Commands.CallbackQuery
     public class DeleteCombine : CallbackQueryCommand
     {
         protected override string CommandText => Command.delete_combine;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             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();
+                await new Back(User, Update).PrepareAndExecute();
             else await MessageController.EditMessage(User, CallbackMessageId, module.ToString(), Keyboard.GetCombineKeyboard(module));
         }
 

+ 4 - 1
CardCollector/Commands/CallbackQuery/EndUploadStickers.cs

@@ -10,12 +10,15 @@ namespace CardCollector.Commands.CallbackQuery
     public class EndUploadStickers : CallbackQueryCommand
     {
         protected override string CommandText => Command.end_sticker_upload;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             User.Session.State = UserState.UploadFile;
             var module = User.Session.GetModule<UploadedStickersModule>();
             await MessageController.EditMessage(User, module.MessageId, Messages.upload_your_file, 
-                Keyboard.CancelKeyboard);
+                Keyboard.BackKeyboard);
         }
 
         public EndUploadStickers() { }

+ 9 - 7
CardCollector/Commands/CallbackQuery/MyPacks.cs

@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System.Linq;
+using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
@@ -10,17 +11,18 @@ namespace CardCollector.Commands.CallbackQuery
     public class MyPacks : CallbackQueryCommand
     {
         protected override string CommandText => Command.my_packs;
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => true;
+
         public override async Task Execute()
         {
             await User.ClearChat();
-            var userPack = await UsersPacksDao.GetUserPacks(User.Id);
-            var specificCount = await SpecificPackDao.GetCount(User.Id);
+            var random = await UserPacksDao.GetOne(User.Id, 1);
+            var authorCount = (await UserPacksDao.GetUserPacks(User.Id)).Sum(item => item.PackId != 1 ? item.Count : 0);
             var message = await MessageController.SendMessage(User, 
                 $"{Messages.your_packs}" +
-                $"\n{Messages.random_packs}: {userPack.RandomCount}{Text.items}" +
-                $"\n{Messages.author_packs}: {userPack.AuthorCount}{Text.items}" +
-                $"\n{Messages.specific_packs} {specificCount}{Text.items}",
+                $"\n{Messages.random_packs}: {random.Count}{Text.items}" +
+                $"\n{Messages.author_pack}: {authorCount}{Text.items}",
                 Keyboard.PackMenu);
             User.Session.Messages.Add(message.MessageId);
         }

+ 45 - 0
CardCollector/Commands/CallbackQuery/OpenAuthorPackMenu.cs

@@ -0,0 +1,45 @@
+using System.Linq;
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    public class OpenAuthorPackMenu : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.open_author_pack_menu;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => true;
+
+        public override async Task Execute()
+        {
+            var packs = (await UserPacksDao.GetUserPacks(User.Id))
+                .Where(item => item.Count > 0 && item.PackId != 1).ToList();
+            if (packs.Count == 0)
+            {
+                User.Session.PopLast();
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.packs_count_zero, true);
+            }
+            else
+            {
+                var page = int.Parse(CallbackData.Split('=')[1]);
+                var totalCount = packs.Count;
+                packs = packs.GetRange((page - 1) * 10, packs.Count >= page * 10 ? 10 : packs.Count % 10);
+                if (packs.Count == 0)
+                {
+                    User.Session.PopLast();
+                    await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.page_not_found);
+                }
+                else
+                    await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_author,
+                        await Keyboard.GetUserPacksKeyboard(packs, Keyboard.GetPagePanel(page, totalCount, CommandText)));
+            }
+        }
+
+        public OpenAuthorPackMenu() { }
+        public OpenAuthorPackMenu(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 15 - 40
CardCollector/Commands/CallbackQuery/OpenPack.cs

@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
@@ -12,35 +10,25 @@ namespace CardCollector.Commands.CallbackQuery
     public class OpenPack : CallbackQueryCommand
     {
         protected override string CommandText => Command.open_pack;
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var packId = int.Parse(CallbackData.Split("=")[1]);
-            var userPack = await UsersPacksDao.GetUserPacks(User.Id);
-            var rnd = new Random();
-            var packInfo = await PacksDao.GetById(packId);
-            var tier = GetTier(rnd.NextDouble() * 100);
-            switch (packId)
-            {
-                case 1 when userPack.RandomCount < 1:
-                    await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.packs_count_zero, true);
-                    break;
-                case 1:
-                    userPack.RandomCount--;
-                    await OpenPack(await StickerDao.GetListWhere(item => item.Tier == tier));
-                    break;
-                default:
-                    if (!await TryOpen()) 
-                        await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.packs_count_zero, true);
-                    await OpenPack(await StickerDao.GetListWhere(item => item.Tier == tier && item.Author == packInfo.Author));
-                    break;
-            }
-
-            async Task OpenPack(List<StickerEntity> stickers)
+            var userPack = await UserPacksDao.GetOne(User.Id, packId);
+            if (userPack.Count < 1)
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.packs_count_zero, true);
+            else
             {
-                packInfo.OpenedCount++;
                 await User.ClearChat();
-                var sticker = stickers[rnd.Next(stickers.Count)];
+                var packInfo = await PacksDao.GetById(packId);
+                packInfo.OpenedCount++;
+                userPack.Count--;
+                var tier = GetTier(Utilities.rnd.NextDouble() * 100);
+                var stickers = await StickerDao.GetListWhere(item =>
+                    item.Tier == tier && (packId == 1 || item.PackId == packId));
+                var sticker = stickers[Utilities.rnd.Next(stickers.Count)];
                 if (User.Stickers.ContainsKey(sticker.Md5Hash))
                     User.Stickers[sticker.Md5Hash].Count++;
                 else
@@ -50,19 +38,6 @@ namespace CardCollector.Commands.CallbackQuery
                 User.Session.Messages.Add(stickerMessage.MessageId);
                 User.Session.Messages.Add(message.MessageId);
             }
-            
-            async Task<bool> TryOpen()
-            {
-                var info = await SpecificPackDao.GetInfo(User.Id, packId);
-                if (info.Count < 1)
-                {
-                    if (userPack.AuthorCount < 1) return false;
-                    userPack.AuthorCount--;
-                    return true;
-                }
-                info.Count--;
-                return true;
-            }
         }
 
         private int GetTier(double chance)

+ 0 - 36
CardCollector/Commands/CallbackQuery/OpenSpecific.cs

@@ -1,36 +0,0 @@
-using System.Linq;
-using System.Threading.Tasks;
-using CardCollector.Controllers;
-using CardCollector.DataBase.Entity;
-using CardCollector.DataBase.EntityDao;
-using CardCollector.Resources;
-using Telegram.Bot.Types;
-
-namespace CardCollector.Commands.CallbackQuery
-{
-    public class OpenSpecific : CallbackQueryCommand
-    {
-        protected override string CommandText => Command.open_specific;
-        public override async Task Execute()
-        {
-            var packs = await SpecificPackDao.GetUserPacks(User.Id);
-            if (packs.Count == 0)
-                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.packs_count_zero, true);
-            else
-            {
-                var page = int.Parse(CallbackData.Split('=')[1]);
-                var low = page * 10 - 10;
-                var up = page * 10;
-                packs = packs.Where((_, index) => index >= low && index < up).ToList();
-                if (packs.Count == 0)
-                    await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.page_not_found);
-                else
-                    await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_author,
-                        await Keyboard.GetAuthorsKeyboard(packs, page));
-            }
-        }
-
-        public OpenSpecific() { }
-        public OpenSpecific(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 3 - 0
CardCollector/Commands/CallbackQuery/PackInfo.cs

@@ -9,6 +9,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class PackInfo : CallbackQueryCommand
     {
         protected override string CommandText => Command.pack_info;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await MessageController.EditMessage(User, CallbackMessageId, Messages.pack_info);

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

@@ -12,11 +12,12 @@ namespace CardCollector.Commands.CallbackQuery
     public class PutForAuction : CallbackQueryCommand
     {
         protected override string CommandText => Command.sell_on_auction;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
 
         public override async Task Execute()
         {
-            await MessageController.AnswerCallbackQuery(User, CallbackQueryId,
-                Messages.comission_warning, true);
+            await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.comission_warning, true);
             await User.ClearChat();
             var module = User.Session.GetModule<CollectionModule>();
             var priceList = (await AuctionController.GetPriceList(module.SelectedSticker.Id)).ToList();

+ 0 - 30
CardCollector/Commands/CallbackQuery/SelectAuthor.cs

@@ -1,30 +0,0 @@
-using System.Threading.Tasks;
-using CardCollector.Controllers;
-using CardCollector.DataBase.Entity;
-using CardCollector.DataBase.EntityDao;
-using CardCollector.Resources;
-using Telegram.Bot.Types;
-
-namespace CardCollector.Commands.CallbackQuery
-{
-    /* Реализует нажатие на кнопку "Автор" (открывается меню с выбором автора) */
-    public class SelectAuthor : CallbackQueryCommand
-    {
-        protected override string CommandText => Command.author;
-
-        public override async Task Execute()
-        {
-            var page = int.Parse(CallbackData.Split('=')[1]);
-            /* Получаем из бд список всех авторов */
-            var list = await StickerDao.GetAuthorsList();
-            /* Сортируем по алфавиту */
-            list.Sort();
-            /* Заменяем сообщение меню на сообщение со списком */
-            await MessageController.EditMessage(User, CallbackMessageId,
-                Messages.choose_author, Keyboard.GetAuthorsKeyboard(list, page));
-        }
-
-        public SelectAuthor() { }
-        public SelectAuthor(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 3 - 0
CardCollector/Commands/CallbackQuery/SelectEmoji.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class SelectEmoji : CallbackQueryCommand
     {
         protected override string CommandText => Command.emoji;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             EnterEmoji.AddToQueue(User.Id, CallbackMessageId);

+ 6 - 2
CardCollector/Commands/CallbackQuery/SelectOffer.cs

@@ -11,13 +11,17 @@ namespace CardCollector.Commands.CallbackQuery
     public class SelectOffer : CallbackQueryCommand
     {
         protected override string CommandText => Command.select_offer;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await User.ClearChat();
             var offerId = int.Parse(CallbackData.Split('=')[1]);
             var offerInfo = await ShopDao.GetById(offerId);
-            User.Session.GetModule<ShopModule>().SelectedPosition = offerInfo;
-            var message = await MessageController.SendSticker(User, offerInfo.ImageId, Keyboard.OfferKeyboard(offerInfo));
+            var module = User.Session.GetModule<ShopModule>();
+            module.SelectedPosition = offerInfo;
+            var message = await MessageController.SendSticker(User, offerInfo.ImageId, Keyboard.OfferKeyboard(module));
             User.Session.Messages.Add(message.MessageId);
         }
 

+ 4 - 1
CardCollector/Commands/CallbackQuery/SelectPrice.cs

@@ -8,7 +8,10 @@ namespace CardCollector.Commands.CallbackQuery
 {
     public class SelectPrice : CallbackQueryCommand
     {
-        protected override string CommandText => Command.price;
+        protected override string CommandText => Command.select_price;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_price, 

+ 33 - 0
CardCollector/Commands/CallbackQuery/SelectShopPack.cs

@@ -0,0 +1,33 @@
+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
+{
+    public class SelectShopPack : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.select_shop_pack;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
+        public override async  Task Execute()
+        {
+            await User.ClearChat();
+            var packId = int.Parse(CallbackData.Split('=')[1]);
+            var packInfo = await PacksDao.GetById(packId);
+            var module = User.Session.GetModule<ShopModule>();
+            module.SelectedPack = packInfo;
+            var stickers = await StickerDao.GetListWhere(item => packId == 1 || item.PackId == packId);
+            var sticker = stickers[Utilities.rnd.Next(stickers.Count)];
+            var message = await MessageController.SendSticker(User, sticker.Id, Keyboard.OfferKeyboard(module));
+            User.Session.Messages.Add(message.MessageId);
+        }
+
+        public SelectShopPack() { }
+        public SelectShopPack(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 3 - 0
CardCollector/Commands/CallbackQuery/SelectSort.cs

@@ -9,6 +9,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class SelectSort : CallbackQueryCommand
     {
         protected override string CommandText => Command.sort;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_sort, Keyboard.SortOptions);

+ 3 - 0
CardCollector/Commands/CallbackQuery/SelectTier.cs

@@ -9,6 +9,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class SelectTier : CallbackQueryCommand
     {
         protected override string CommandText => Command.tier;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_tier, Keyboard.TierOptions);

+ 4 - 1
CardCollector/Commands/CallbackQuery/SetFilter.cs

@@ -12,6 +12,9 @@ namespace CardCollector.Commands.CallbackQuery
     public class SetFilter : CallbackQueryCommand
     {
         protected override string CommandText => Command.set;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             EnterEmoji.RemoveFromQueue(User.Id);
@@ -25,7 +28,7 @@ namespace CardCollector.Commands.CallbackQuery
             if (filters[Command.price_gems_to] is int g && g <= (int) filters[Command.price_gems_from])
                 filters[Command.price_gems_to] = 0;
             /* Возвращаемся в меню фильтров */
-            await new BackToFiltersMenu(User, Update).Execute();
+            await new Back(User, Update).PrepareAndExecute();
         }
         
         public SetFilter() { }

+ 14 - 9
CardCollector/Commands/CallbackQuery/ShowInfo.cs

@@ -12,18 +12,23 @@ namespace CardCollector.Commands.CallbackQuery
     public class ShowInfo : CallbackQueryCommand
     {
         protected override string CommandText => Command.show_offer_info;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
-            var offerInfo = User.Session.GetModule<ShopModule>().SelectedPosition;
-            var message = $"{offerInfo.Title}";
-            if (offerInfo.Discount > 0) message += $"\n{Text.discount}: {offerInfo.Discount}%";
-            if (offerInfo.AdditionalPrize != "") 
-                message += $"\n{Text.prize}: {await PrizeToString(offerInfo.AdditionalPrize)}";
-            var dateText = offerInfo.TimeLimited
-                ? offerInfo.TimeLimit.ToString(CultureInfo.CurrentCulture).Split(' ')[0]
+            var module = User.Session.GetModule<ShopModule>();
+            var description = module.SelectedPosition?.Description ?? module.SelectedPack?.Description ?? "";
+            var message = module.SelectedPosition?.Title ?? $"{Messages.author_pack}: {module.SelectedPack?.Author}";
+            if (module.SelectedPosition?.Discount > 0) message += $"\n{Text.discount}: {module.SelectedPosition?.Discount}%";
+            if (module.SelectedPosition != null && module.SelectedPosition?.AdditionalPrize != "") 
+                message += $"\n{Text.prize}: {await PrizeToString(module.SelectedPosition?.AdditionalPrize)}";
+            var dateText = module.SelectedPosition?.TimeLimited ?? false
+                ? module.SelectedPosition?.TimeLimit.ToString(CultureInfo.CurrentCulture).Split(' ')[0]
                 : Text.unexpired;
-            message += $"\n{Text.time_limit} {dateText}";
-            if (offerInfo.Description != "") message += $"\n{Text.description}: {offerInfo.Description}";
+            if (module.SelectedPosition != null) message += $"\n{Text.time_limit} {dateText}";
+            if (module.SelectedPack != null) message += $"\n{Text.opened_count} {module.SelectedPack.OpenedCount}";
+            if (description != "") message += $"\n{Text.description}: {description}";
             await MessageController.AnswerCallbackQuery(User, CallbackQueryId, message, true);
         }
         private async Task<string> PrizeToString(string prize)

+ 9 - 2
CardCollector/Commands/CallbackQuery/SpecialOffers.cs

@@ -11,15 +11,22 @@ namespace CardCollector.Commands.CallbackQuery
     public class SpecialOffers : CallbackQueryCommand
     {
         protected override string CommandText => Command.special_offers;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => true;
+
         public override async Task Execute()
         {
             var specialOffers = await (await ShopDao.GetSpecialPositions())
                 .WhereAsync(async offer => offer.IsInfinite || !await SpecialOfferUsersDao.NowUsed(User.Id, offer.Id));
-            if (specialOffers.Count() < 1)
+            var shopEntities = specialOffers.ToList();
+            if (shopEntities.Count < 1)
+            {
+                User.Session.PopLast();
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.offers_not_found, true);
+            }
             else
                 await MessageController.EditMessage(User, CallbackMessageId, Messages.available_offers,
-                    Keyboard.SpecialOffersKeyboard(specialOffers));
+                    Keyboard.SpecialOffersKeyboard(shopEntities));
         }
 
         public SpecialOffers() { }

+ 8 - 13
CardCollector/Commands/CallbackQueryCommand.cs

@@ -22,28 +22,22 @@ namespace CardCollector.Commands
     {
         /* Данные, поступившие после нажатия на кнокпку */
         protected string CallbackData;
-        
         /* Id сообщения, под которым нажали на кнопку */
         protected int CallbackMessageId;
-        
         /* Id запроса */
         protected string CallbackQueryId;
-        
+
         /* Список команд, распознаваемых ботом */
         private static readonly List<CallbackQueryCommand> List = new()
         {
-            new SelectAuthor(),
-            new AuthorMenu(),
-            new BackToCombine(),
-            new BackToFiltersMenu(),
-            new BuyByCoins(),
-            new BuyByGems(),
+            new AuthorsMenu(),
+            new BuyAuthorPackMenu(),
+            new Back(),
+            new BuyShopItem(),
             new BuyCoins(),
             new BuyGems(),
             new BuyPack(),
             new BuySticker(),
-            new Cancel(),
-            new ClearChat(),
             new CollectIncome(),
             new Combine(),
             new CombineStickers(),
@@ -57,7 +51,7 @@ namespace CardCollector.Commands
             new SelectEmoji(),
             new MyPacks(),
             new OpenPack(),
-            new OpenSpecific(),
+            new OpenAuthorPackMenu(),
             new PackInfo(),
             new SelectPrice(),
             new PutForAuction(),
@@ -67,6 +61,7 @@ namespace CardCollector.Commands
             new ShowInfo(),
             new SpecialOffers(),
             new SelectTier(),
+            new SelectShopPack(),
         };
 
         /* Метод, создающий объекты команд исходя из полученного обновления */
@@ -94,7 +89,7 @@ namespace CardCollector.Commands
         protected CallbackQueryCommand(UserEntity user, Update update) : base(user, update)
         {
             CallbackData = update.CallbackQuery!.Data;
-            CallbackMessageId = update.CallbackQuery!.Message!.MessageId;
+            CallbackMessageId = user.Session.Messages.LastOrDefault();
             CallbackQueryId = update.CallbackQuery!.Id;
         }
     }

+ 3 - 1
CardCollector/Commands/ChosenInlineResult/GetUnlimitedStickerAndExecuteCommand.cs

@@ -12,7 +12,9 @@ namespace CardCollector.Commands.ChosenInlineResult
     public class GetUnlimitedStickerAndExecuteCommand : ChosenInlineResultCommand
     {
         protected override string CommandText => Command.unlimited_stickers;
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             /* Получаем хеш стикера */

+ 3 - 0
CardCollector/Commands/ChosenInlineResult/SelectStickerInline.cs

@@ -11,6 +11,9 @@ namespace CardCollector.Commands.ChosenInlineResult
     public class SelectStickerInline : ChosenInlineResultCommand
     {
         protected override string CommandText => Command.select_sticker;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await User.ClearChat();

+ 3 - 0
CardCollector/Commands/ChosenInlineResult/SelectTrader.cs

@@ -11,6 +11,9 @@ namespace CardCollector.Commands.ChosenInlineResult
     public class SelectTrader : ChosenInlineResultCommand
     {
         protected override string CommandText => Command.buy_sticker;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await User.ClearChat();

+ 3 - 1
CardCollector/Commands/ChosenInlineResult/SendPrivateSticker.cs

@@ -10,7 +10,9 @@ namespace CardCollector.Commands.ChosenInlineResult
     public class SendPrivateSticker : ChosenInlineResultCommand
     {
         protected override string CommandText => Command.send_private_sticker;
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var dailyTask = DailyTask.List[DailyTaskKeys.SendStickersToUsers];

+ 3 - 0
CardCollector/Commands/ChosenInlineResult/SendSticker.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.ChosenInlineResult
     {
         /* Ключевое слово для данной команды send_sticker */
         protected override string CommandText => Command.send_sticker;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override Task Execute()
         {
             return Task.CompletedTask;

+ 3 - 0
CardCollector/Commands/CommandNotFound.cs

@@ -9,6 +9,9 @@ namespace CardCollector.Commands
     public class CommandNotFound : UpdateModel
     {
         protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         private readonly string _command;
 
         public override async Task Execute()

+ 3 - 0
CardCollector/Commands/IgnoreUpdate.cs

@@ -8,6 +8,9 @@ namespace CardCollector.Commands
     public class IgnoreUpdate : UpdateModel
     {
         protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override Task Execute() { return  Task.CompletedTask; }
         protected internal override bool IsMatches(UserEntity user, Update update) => true;
     }

+ 3 - 1
CardCollector/Commands/InlineQuery/ShowAuctionStickers.cs

@@ -12,7 +12,9 @@ namespace CardCollector.Commands.InlineQuery
     public class ShowAuctionStickers : InlineQueryCommand
     {
         protected override string CommandText => "";
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             // Получаем список стикеров

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

@@ -11,6 +11,9 @@ namespace CardCollector.Commands.InlineQuery
     public class ShowCollectionStickers : InlineQueryCommand
     {
         protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             // Получаем список стикеров

+ 3 - 0
CardCollector/Commands/InlineQuery/ShowCombineStickers.cs

@@ -11,6 +11,9 @@ namespace CardCollector.Commands.InlineQuery
     public class ShowCombineStickers : InlineQueryCommand
     {
         protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var module = User.Session.GetModule<CombineModule>();

+ 3 - 1
CardCollector/Commands/InlineQuery/ShowStickersInBotChat.cs

@@ -13,7 +13,9 @@ namespace CardCollector.Commands.InlineQuery
         /* Команда - пустая строка, поскольку пользователь может вводить любые слова
          после @имя_бота, введенная фраза будет использоваться для фильтрации стикеров */
         protected override string CommandText => "";
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             // Получаем список стикеров

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

@@ -13,7 +13,9 @@ namespace CardCollector.Commands.InlineQuery
         /* Команда - пустая строка, поскольку пользователь может вводить любые слова
          после @имя_бота, введенная фраза будет использоваться для фильтрации стикеров */
         protected override string CommandText => "";
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             // Получаем список стикеров

+ 3 - 1
CardCollector/Commands/InlineQuery/ShowStickersInPrivate.cs

@@ -12,7 +12,9 @@ namespace CardCollector.Commands.InlineQuery
         /* Команда - пустая строка, поскольку пользователь может вводить любые слова
          после @имя_бота, введенная фраза будет использоваться для фильтрации стикеров */
         protected override string CommandText => "";
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             // Получаем список стикеров

+ 3 - 1
CardCollector/Commands/InlineQuery/ShowTradersInBotChat.cs

@@ -11,7 +11,9 @@ namespace CardCollector.Commands.InlineQuery
     public class ShowTradersInBotChat : InlineQueryCommand
     {
         protected override string CommandText => "";
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var module = User.Session.GetModule<AuctionModule>();

+ 4 - 1
CardCollector/Commands/Message/Auction.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.Message
     public class Auction : MessageCommand
     {
         protected override string CommandText => Text.auction;
+        protected override bool ClearMenu => true;
+        protected override bool AddToStack => true;
+
         public override async Task Execute()
         {
             /* Очищаем чат с пользователем */
@@ -18,7 +21,7 @@ namespace CardCollector.Commands.Message
             User.Session.State = UserState.AuctionMenu;
             User.Session.InitNewModule<AuctionModule>();
             /* Отображаем сообщение с фильтрами */
-            await new ShowFiltersMenu(User, Update).Execute();
+            await new ShowFiltersMenu(User, Update).PrepareAndExecute();
         }
         
         public Auction() { }

+ 4 - 1
CardCollector/Commands/Message/Collection.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.Message
     public class Collection : MessageCommand
     {
         protected override string CommandText => Text.collection;
+        protected override bool ClearMenu => true;
+        protected override bool AddToStack => true;
+
         public override async Task Execute()
         {
             /* Очищаем чат с пользователем */
@@ -18,7 +21,7 @@ namespace CardCollector.Commands.Message
             User.Session.State = UserState.CollectionMenu;
             User.Session.InitNewModule<CollectionModule>();
             /* Отображаем сообщение с фильтрами */
-            await new ShowFiltersMenu(User, Update).Execute();
+            await new ShowFiltersMenu(User, Update).PrepareAndExecute();
         }
         
         public Collection() { }

+ 3 - 0
CardCollector/Commands/Message/CreateToken.cs

@@ -12,6 +12,9 @@ namespace CardCollector.Commands.Message
     public class CreateToken : MessageCommand
     {
         protected override string CommandText => "create_token";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_";
         private const string site = "http://127.0.0.1:8080/";
         

+ 4 - 1
CardCollector/Commands/Message/DownloadStickerPack.cs

@@ -10,11 +10,14 @@ namespace CardCollector.Commands.Message
     public class DownloadStickerPack : MessageCommand
     {
         protected override string CommandText => Text.download_stickerpack;
+        protected override bool ClearMenu => true;
+        protected override bool AddToStack => true;
+
         public override async Task Execute()
         {
             await User.ClearChat();
             User.Session.State = UserState.UploadSticker;
-            var result = await MessageController.SendMessage(User, Messages.upload_your_stickers, Keyboard.CancelKeyboard);
+            var result = await MessageController.SendMessage(User, Messages.upload_your_stickers, Keyboard.BackKeyboard);
             User.Session.Messages.Add(result.MessageId);
             User.Session.GetModule<UploadedStickersModule>().MessageId = result.MessageId;
         }

+ 2 - 0
CardCollector/Commands/Message/EnterEmoji.cs

@@ -13,6 +13,8 @@ namespace CardCollector.Commands.Message
     public class EnterEmoji : MessageCommand
     {
         protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
 
         private const string oneEmojiPattern =
             "^\\u00a9$|^\\u00ae$|^[\\u2000-\\u3300]$|^\\ud83c[\\ud000-\\udfff]$|^\\ud83d[\\ud000-\\udfff]$|^\\ud83e[\\ud000-\\udfff]$";

+ 3 - 1
CardCollector/Commands/Message/EnterGemsExchange.cs

@@ -12,7 +12,9 @@ namespace CardCollector.Commands.Message
     public class EnterGemsExchange : MessageCommand
     {
         protected override string CommandText => "";
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         private static readonly Dictionary<long, int> Queue = new ();
         public override async Task Execute()
         {

+ 3 - 1
CardCollector/Commands/Message/EnterGemsPrice.cs

@@ -12,7 +12,9 @@ namespace CardCollector.Commands.Message
     public class EnterGemsPrice : MessageCommand
     {
         protected override string CommandText => "";
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         private static readonly Dictionary<long, int> Queue = new ();
         public override async Task Execute()
         {

+ 3 - 0
CardCollector/Commands/Message/Menu.cs

@@ -9,6 +9,9 @@ namespace CardCollector.Commands.Message
     public class Menu : MessageCommand
     {
         protected override string CommandText => Text.menu;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             /* Отправляем пользователю сообщение со стандартной клавиатурой */

+ 4 - 0
CardCollector/Commands/Message/Profile.cs

@@ -11,8 +11,12 @@ namespace CardCollector.Commands.Message
     {
         /* Для данной команды ключевое слово "Профиль" */
         protected override string CommandText => Text.profile;
+        protected override bool ClearMenu => true;
+        protected override bool AddToStack => true;
+
         public override async Task Execute()
         {
+            await User.ClearChat();
             /* Подсчитываем прибыль */
             var income = await User.Cash.CalculateIncome(User.Stickers);
             /* Отправляем сообщение */

+ 3 - 0
CardCollector/Commands/Message/Shop.cs

@@ -12,6 +12,9 @@ namespace CardCollector.Commands.Message
     public class Shop : MessageCommand
     {
         protected override string CommandText => Text.shop;
+        protected override bool ClearMenu => true;
+        protected override bool AddToStack => true;
+
         public override async Task Execute()
         {
             /* Очищаем чат с пользователем */

+ 4 - 22
CardCollector/Commands/Message/ShowFiltersMenu.cs

@@ -10,30 +10,12 @@ namespace CardCollector.Commands.Message
     /* Этот класс реализует отправку нового сообщения с фильтрами пользователя */
     public class ShowFiltersMenu : MessageCommand
     {
-        protected override string CommandText => "Message";
+        protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         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;
-            }
             /* Формируем сообщение с имеющимися фильтрами у пользователя */
             var text = User.Session.GetModule<FiltersModule>().ToString(User.Session.State);
             /* Отправляем сообщение */

+ 3 - 0
CardCollector/Commands/Message/ShowSample.cs

@@ -13,6 +13,9 @@ namespace CardCollector.Commands.Message
     public class ShowSample : MessageCommand
     {
         protected override string CommandText => Text.show_sample;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var loginUrl = new LoginUrl

+ 3 - 1
CardCollector/Commands/Message/Start.cs

@@ -11,7 +11,9 @@ namespace CardCollector.Commands.Message
     {
         /* */
         protected override string CommandText => Text.start;
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             /* Отправляем пользователю сообщение со стандартной клавиатурой */

+ 3 - 1
CardCollector/Commands/Message/StopBot.cs

@@ -9,7 +9,9 @@ namespace CardCollector.Commands.Message
     public class StopBot : MessageCommand
     {
         protected override string CommandText => Text.stop_bot;
-        
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var message = await MessageController.SendMessage(User, "Stopping bot");

+ 3 - 0
CardCollector/Commands/Message/UploadFile.cs

@@ -18,6 +18,9 @@ namespace CardCollector.Commands.Message
     public class UploadFile : MessageCommand
     {
         protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             User.Session.State = UserState.Default;

+ 3 - 0
CardCollector/Commands/Message/UploadSticker.cs

@@ -11,6 +11,9 @@ namespace CardCollector.Commands.Message
     public class UploadSticker : MessageCommand
     {
         protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             var stickerId = Update.Message?.Sticker?.FileId;

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

@@ -12,6 +12,9 @@ namespace CardCollector.Commands.MyChatMember
     public class MyChatMemberCommand : UpdateModel
     {
         protected override string CommandText => "";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         private readonly ChatMemberStatus _status;
         public override Task Execute()
         {

+ 3 - 0
CardCollector/Commands/PreCheckoutQuery/BuyGems.cs

@@ -10,6 +10,9 @@ namespace CardCollector.Commands.PreCheckoutQuery
     public class BuyGems : PreCheckoutQueryCommand
     {
         protected override string CommandText => Command.buy_gems_item;
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
+
         public override async Task Execute()
         {
             await Bot.Client.AnswerPreCheckoutQueryAsync(PreCheckoutQueryId);

+ 2 - 0
CardCollector/Commands/PreCheckoutQuery/Test.cs

@@ -8,6 +8,8 @@ namespace CardCollector.Commands.PreCheckoutQuery
     public class Test : PreCheckoutQueryCommand
     {
         protected override string CommandText => "test";
+        protected override bool ClearMenu => false;
+        protected override bool AddToStack => false;
 
         public override async Task Execute()
         {

+ 10 - 0
CardCollector/Commands/UpdateModel.cs

@@ -14,11 +14,21 @@ namespace CardCollector.Commands
     public abstract class UpdateModel
     {
         protected abstract string CommandText { get; }
+        protected abstract bool ClearMenu { get; }
+        protected abstract bool AddToStack { get; }
         protected UserEntity User;
         protected Update Update;
 
         public abstract Task Execute();
 
+        public async Task PrepareAndExecute()
+        {
+            User.Session.SetCurrentCommand(GetType());
+            if (ClearMenu) User.Session.ClearMenuStack();
+            if (AddToStack) User.Session.AddMenuToStack(this);
+            await Execute();
+        }
+
         protected internal abstract bool IsMatches(UserEntity user, Update update);
 
         protected UpdateModel()

+ 6 - 8
CardCollector/Controllers/MessageController.cs

@@ -43,7 +43,7 @@ namespace CardCollector.Controllers
                     _ => throw new ArgumentOutOfRangeException()
                 };
                 // Обработать команду
-                await executor.Execute();
+                await executor.PrepareAndExecute();
             }
             catch (Exception e)
             {
@@ -145,14 +145,12 @@ namespace CardCollector.Controllers
                 if (!user.IsBlocked)
                     return await Bot.Client.EditMessageTextAsync(user.ChatId, messageId, message, replyMarkup: keyboard);
             }
-            catch (ApiRequestException e)
+            catch (Exception)
             {
-                if (e.ErrorCode != 400)
-                {
-                    var msg = await SendMessage(user, message, keyboard);
-                    user.Session.Messages.Add(msg.MessageId);
-                    return msg;
-                }
+                await user.ClearChat();
+                var msg = await SendMessage(user, message, keyboard);
+                user.Session.Messages.Add(msg.MessageId);
+                return msg;
             }
             return new Message();
         }

+ 2 - 2
CardCollector/DailyTasks/CustomTasks/SendStickers.cs

@@ -20,8 +20,8 @@ namespace CardCollector.DailyTasks.CustomTasks
 
         public override async Task GiveReward(long userId, object[] args = null)
         {
-            var userPacks = await UsersPacksDao.GetUserPacks(userId);
-            userPacks.RandomCount++;
+            var userPacks = await UserPacksDao.GetOne(userId, 1);
+            userPacks.Count++;
         }
     }
 }

+ 0 - 1
CardCollector/DailyTasks/DailyTask.cs

@@ -31,7 +31,6 @@ namespace CardCollector.DailyTasks
         {
             await foreach (var item in DailyTaskDao.GetAll())
                 item.Progress = List[(DailyTaskKeys) item.TaskId].Goal;
-            Utilities.SetUpTimer(Constants.DailyTaskReset, ResetTasks);
         }
     }
 }

+ 1 - 2
CardCollector/DataBase/CardCollectorDatabase.cs

@@ -64,10 +64,9 @@ namespace CardCollector.DataBase
         public DbSet<AuctionEntity> Auction { get; set; }
         public DbSet<ShopEntity> Shop { get; set; }
         public DbSet<DailyTaskEntity> DailyTasks { get; set; }
-        public DbSet<UsersPacksEntity> UsersPacks { get; set; }
+        public DbSet<UserPacks> UsersPacks { get; set; }
         public DbSet<PackEntity> Packs { get; set; }
         public DbSet<SpecialOfferUsers> SpecialOfferUsers { get; set; }
-        public DbSet<SpecificPacksEntity> SpecificPacks { get; set; }
         public DbSet<SessionToken> SessionTokens { get; set; }
 
 

+ 3 - 0
CardCollector/DataBase/Entity/PackEntity.cs

@@ -17,5 +17,8 @@ namespace CardCollector.DataBase.Entity
         
         /* Количество открытий */
         [Column("opened_count"), MaxLength(127)] public long OpenedCount { get; set; } = 0;
+
+        [NotMapped] public int PriceCoins => Id == 1 ? 1000 : -1;
+        [NotMapped] public int PriceGems => Id == 1 ? 80 : 100;
     }
 }

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

@@ -38,7 +38,7 @@ namespace CardCollector.DataBase.Entity
         [NotMapped] public Dictionary<string, UserStickerRelationEntity> Stickers { get; set; }
         
         /* Данные, хранящиеся в рамках одной сессии */
-        [NotMapped] public readonly UserSession Session;
+        [NotMapped] public UserSession Session;
 
         /* Удаляет из чата все сообщения, добавленные в список выше */
         public async Task ClearChat()

+ 5 - 4
CardCollector/DataBase/Entity/SpecificPacksEntity.cs → CardCollector/DataBase/Entity/UserPacks.cs

@@ -3,12 +3,13 @@ using System.ComponentModel.DataAnnotations.Schema;
 
 namespace CardCollector.DataBase.Entity
 {
-    [Table("specific_packs")]
-    public class SpecificPacksEntity
+    [Table("users_packs")]
+    public class UserPacks
     {
-        [Key][Column("id"), MaxLength(127)] public long Id { get; set; }
+        /* Id пользователя */
+        [Key] [Column("id"), MaxLength(127)] public long Id { get; set; }
         [Column("user_id"), MaxLength(127)] public long UserId { get; set; }
         [Column("pack_id"), MaxLength(32)] public int PackId { get; set; }
-        [Column("count"), MaxLength(32)] public int Count { get; set; }
+        [Column("count"), MaxLength(32)] public int Count { get; set; } = 0;
     }
 }

+ 0 - 16
CardCollector/DataBase/Entity/UsersPacksEntity.cs

@@ -1,16 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-namespace CardCollector.DataBase.Entity
-{
-    [Table("users_packs")]
-    public class UsersPacksEntity
-    {
-        /* Id пользователя */
-        [Key] [Column("user_id"), MaxLength(127)] public long UserId { get; set; }
-        
-        /* Количество паков у пользователя */
-        [Column("random_count"), MaxLength(32)] public int RandomCount { get; set; } = 0;
-        [Column("author_count"), MaxLength(32)] public int AuthorCount { get; set; } = 0;
-    }
-}

+ 1 - 1
CardCollector/DataBase/EntityDao/PacksDao.cs

@@ -29,7 +29,7 @@ namespace CardCollector.DataBase.EntityDao
 
         public static async Task<List<PackEntity>> GetAll()
         {
-            return (await Table.WhereAsync(item => item.Id is not 1 or 2)).ToList();
+            return (await Table.WhereAsync(item => item.Id is not 1)).ToList();
         }
     }
 }

+ 0 - 42
CardCollector/DataBase/EntityDao/SpecificPackDao.cs

@@ -1,42 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using CardCollector.DataBase.Entity;
-using Microsoft.EntityFrameworkCore;
-
-namespace CardCollector.DataBase.EntityDao
-{
-    public static class SpecificPackDao
-    {
-        private static readonly CardCollectorDatabase Instance = CardCollectorDatabase.GetSpecificInstance(typeof(SpecificPackDao));
-        private static readonly DbSet<SpecificPacksEntity> Table = Instance.SpecificPacks;
-
-        public static async Task<SpecificPacksEntity> GetInfo(long userId, int packId)
-        {
-            return await Table.FirstOrDefaultAsync(item => item.PackId == packId && item.UserId == userId)
-                   ?? await AddNew(userId, packId, 0);
-        }
-
-        public static async Task<SpecificPacksEntity> AddNew(long userId, int packId, int count)
-        {
-            var pack = await Table.FirstOrDefaultAsync(item => item.UserId == userId && item.PackId == packId) ??
-            (await Table.AddAsync(new SpecificPacksEntity
-            {
-                UserId = userId,
-                PackId = packId,
-                Count = count
-            })).Entity;
-            return pack;
-        }
-
-        public static async Task<int> GetCount(long userId)
-        {
-            return (await Table.WhereAsync(item => item.UserId == userId)).Sum(item => item.Count);
-        }
-
-        public static async Task<List<SpecificPacksEntity>> GetUserPacks(long userId)
-        {
-            return (await Table.WhereAsync(item => item.UserId == userId && item.Count > 0)).ToList();
-        }
-    }
-}

+ 3 - 4
CardCollector/DataBase/EntityDao/StickerDao.cs

@@ -22,10 +22,9 @@ namespace CardCollector.DataBase.EntityDao
 
         public static async Task<List<string>> GetAuthorsList()
         {
-            return await Table
-                .Select(item => item.Author)
-                .Distinct()
-                .ToListAsync();
+            var list = (await Table.ToListAsync()).Select(item => item.Author).Distinct().ToList();
+            list.Sort();
+            return list;
         }
 
         public static async Task<List<StickerEntity>> GetAll(string filter = "")

+ 32 - 0
CardCollector/DataBase/EntityDao/UserPacksDao.cs

@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using CardCollector.DataBase.Entity;
+using Microsoft.EntityFrameworkCore;
+
+namespace CardCollector.DataBase.EntityDao
+{
+    public static class UserPacksDao
+    {
+        private static readonly CardCollectorDatabase Instance = CardCollectorDatabase.GetSpecificInstance(typeof(UserPacksDao));
+        private static readonly DbSet<UserPacks> Table = Instance.UsersPacks;
+
+        public static async Task<List<UserPacks>> GetUserPacks(long userId)
+        {
+            return (await Table.WhereAsync(item => item.UserId == userId)).ToList();
+        }
+
+        public static async Task<UserPacks> AddNew(long userId, int packId)
+        {
+            var newPack = new UserPacks() { UserId = userId, PackId = packId };
+            var result = await Table.AddAsync(newPack);
+            return result.Entity;
+        }
+
+        public static async Task<UserPacks> GetOne(long userId, int packId)
+        {
+            return await Table.FirstOrDefaultAsync(item => item.UserId == userId && item.PackId == packId)
+                   ?? await AddNew(userId, packId);
+        }
+    }
+}

+ 0 - 25
CardCollector/DataBase/EntityDao/UsersPacksDao.cs

@@ -1,25 +0,0 @@
-using System.Threading.Tasks;
-using CardCollector.DataBase.Entity;
-using Microsoft.EntityFrameworkCore;
-
-namespace CardCollector.DataBase.EntityDao
-{
-    public static class UsersPacksDao
-    {
-        private static readonly CardCollectorDatabase Instance = CardCollectorDatabase.GetSpecificInstance(typeof(UsersPacksDao));
-        private static readonly DbSet<UsersPacksEntity> Table = Instance.UsersPacks;
-
-        public static async Task<UsersPacksEntity> GetUserPacks(long userId)
-        {
-            return await Table.FirstOrDefaultAsync(item => item.UserId == userId) ?? await AddNew(userId);
-        }
-
-        public static async Task<UsersPacksEntity> AddNew(long userId)
-        {
-            var newPack = new UsersPacksEntity(){UserId = userId};
-            var result = await Table.AddAsync(newPack);
-            await Instance.SaveChangesAsync();
-            return result.Entity;
-        }
-    }
-}

+ 35 - 80
CardCollector/Resources/Command.Designer.cs

@@ -63,18 +63,9 @@ namespace CardCollector.Resources {
         /// <summary>
         ///   Looks up a localized string similar to AAA.
         /// </summary>
-        internal static string author {
+        internal static string authors_menu {
             get {
-                return ResourceManager.GetString("author", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to AAB.
-        /// </summary>
-        internal static string author_menu {
-            get {
-                return ResourceManager.GetString("author_menu", resourceCulture);
+                return ResourceManager.GetString("authors_menu", resourceCulture);
             }
         }
         
@@ -88,29 +79,11 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to AAD.
-        /// </summary>
-        internal static string back_to_combine {
-            get {
-                return ResourceManager.GetString("back_to_combine", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to ABI.
-        /// </summary>
-        internal static string buy_by_coins {
-            get {
-                return ResourceManager.GetString("buy_by_coins", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to ABJ.
+        ///   Looks up a localized string similar to AAB.
         /// </summary>
-        internal static string buy_by_gems {
+        internal static string buy_author_pack_menu {
             get {
-                return ResourceManager.GetString("buy_by_gems", resourceCulture);
+                return ResourceManager.GetString("buy_author_pack_menu", resourceCulture);
             }
         }
         
@@ -141,15 +114,6 @@ namespace CardCollector.Resources {
             }
         }
         
-        /// <summary>
-        ///   Looks up a localized string similar to ABK.
-        /// </summary>
-        internal static string buy_offer {
-            get {
-                return ResourceManager.GetString("buy_offer", resourceCulture);
-            }
-        }
-        
         /// <summary>
         ///   Looks up a localized string similar to ABG.
         /// </summary>
@@ -160,29 +124,20 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to AAF.
-        /// </summary>
-        internal static string buy_sticker {
-            get {
-                return ResourceManager.GetString("buy_sticker", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to AAG.
+        ///   Looks up a localized string similar to ABI.
         /// </summary>
-        internal static string cancel {
+        internal static string buy_shop_item {
             get {
-                return ResourceManager.GetString("cancel", resourceCulture);
+                return ResourceManager.GetString("buy_shop_item", resourceCulture);
             }
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to AAH.
+        ///   Looks up a localized string similar to AAF.
         /// </summary>
-        internal static string clear_chat {
+        internal static string buy_sticker {
             get {
-                return ResourceManager.GetString("clear_chat", resourceCulture);
+                return ResourceManager.GetString("buy_sticker", resourceCulture);
             }
         }
         
@@ -304,20 +259,20 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to ABE.
+        ///   Looks up a localized string similar to ABO.
         /// </summary>
-        internal static string open_pack {
+        internal static string open_author_pack_menu {
             get {
-                return ResourceManager.GetString("open_pack", resourceCulture);
+                return ResourceManager.GetString("open_author_pack_menu", resourceCulture);
             }
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to ABO.
+        ///   Looks up a localized string similar to ABE.
         /// </summary>
-        internal static string open_specific {
+        internal static string open_pack {
             get {
-                return ResourceManager.GetString("open_specific", resourceCulture);
+                return ResourceManager.GetString("open_pack", resourceCulture);
             }
         }
         
@@ -330,15 +285,6 @@ namespace CardCollector.Resources {
             }
         }
         
-        /// <summary>
-        ///   Looks up a localized string similar to AAU.
-        /// </summary>
-        internal static string price {
-            get {
-                return ResourceManager.GetString("price", resourceCulture);
-            }
-        }
-        
         /// <summary>
         ///   Looks up a localized string similar to AAV.
         /// </summary>
@@ -384,6 +330,24 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to AAU.
+        /// </summary>
+        internal static string select_price {
+            get {
+                return ResourceManager.GetString("select_price", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to ABU.
+        /// </summary>
+        internal static string select_shop_pack {
+            get {
+                return ResourceManager.GetString("select_shop_pack", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to AAV.
         /// </summary>
@@ -429,15 +393,6 @@ namespace CardCollector.Resources {
             }
         }
         
-        /// <summary>
-        ///   Looks up a localized string similar to ABL.
-        /// </summary>
-        internal static string shop_authors {
-            get {
-                return ResourceManager.GetString("shop_authors", resourceCulture);
-            }
-        }
-        
         /// <summary>
         ///   Looks up a localized string similar to ABS.
         /// </summary>

+ 8 - 23
CardCollector/Resources/Command.resx

@@ -21,7 +21,7 @@
     <data name="collect_income" xml:space="preserve">
         <value>AAI</value>
     </data>
-    <data name="author" xml:space="preserve">
+    <data name="authors_menu" xml:space="preserve">
         <value>AAA</value>
     </data>
     <data name="tier" xml:space="preserve">
@@ -33,9 +33,6 @@
     <data name="sort" xml:space="preserve">
         <value>ABA</value>
     </data>
-    <data name="cancel" xml:space="preserve">
-        <value>AAG</value>
-    </data>
     <data name="set" xml:space="preserve">
         <value>AAZ</value>
     </data>
@@ -63,15 +60,12 @@
     <data name="price_gems_to" xml:space="preserve">
         <value>AAY</value>
     </data>
-    <data name="price" xml:space="preserve">
+    <data name="select_price" xml:space="preserve">
         <value>AAU</value>
     </data>
     <data name="buy_sticker" xml:space="preserve">
         <value>AAF</value>
     </data>
-    <data name="clear_chat" xml:space="preserve">
-        <value>AAH</value>
-    </data>
     <data name="combine" xml:space="preserve">
         <value>AAJ</value>
     </data>
@@ -87,9 +81,6 @@
     <data name="confirm_selling" xml:space="preserve">
         <value>AAM</value>
     </data>
-    <data name="back_to_combine" xml:space="preserve">
-        <value>AAD</value>
-    </data>
     <data name="delete_combine" xml:space="preserve">
         <value>AAQ</value>
     </data>
@@ -108,7 +99,7 @@
     <data name="send_private_sticker" xml:space="preserve">
         <value>AAX</value>
     </data>
-    <data name="author_menu" xml:space="preserve">
+    <data name="buy_author_pack_menu" xml:space="preserve">
         <value>AAB</value>
     </data>
     <data name="open_pack" xml:space="preserve">
@@ -123,25 +114,16 @@
     <data name="buy_gems" xml:space="preserve">
         <value>ABH</value>
     </data>
-    <data name="buy_by_coins" xml:space="preserve">
+    <data name="buy_shop_item" xml:space="preserve">
         <value>ABI</value>
     </data>
-    <data name="buy_by_gems" xml:space="preserve">
-        <value>ABJ</value>
-    </data>
-    <data name="buy_offer" xml:space="preserve">
-        <value>ABK</value>
-    </data>
-    <data name="shop_authors" xml:space="preserve">
-        <value>ABL</value>
-    </data>
     <data name="pack_info" xml:space="preserve">
         <value>ABM</value>
     </data>
     <data name="buy_gems_item" xml:space="preserve">
         <value>ABN</value>
     </data>
-    <data name="open_specific" xml:space="preserve">
+    <data name="open_author_pack_menu" xml:space="preserve">
         <value>ABO</value>
     </data>
     <data name="special_offers" xml:space="preserve">
@@ -159,4 +141,7 @@
     <data name="end_sticker_upload" xml:space="preserve">
         <value>ABT</value>
     </data>
+    <data name="select_shop_pack" xml:space="preserve">
+        <value>ABU</value>
+    </data>
 </root>

+ 79 - 129
CardCollector/Resources/Keyboard.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using System.Linq;
 using System.Threading.Tasks;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
@@ -23,21 +22,20 @@ namespace CardCollector.Resources
         public static readonly InlineKeyboardMarkup PackMenu = new(new[]
         {
             new[] {InlineKeyboardButton.WithCallbackData(Text.open_random, $"{Command.open_pack}=1")},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.open_author, $"{Command.author_menu}=1")},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.open_specific, $"{Command.open_specific}=1")},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.open_author, $"{Command.open_author_pack_menu}=1")},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         public static readonly InlineKeyboardMarkup BuyCoinsKeyboard = new(new[]
         {
             new[] {InlineKeyboardButton.WithCallbackData(Text.confirm_exchange, Command.confirm_exchange)},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         public static readonly InlineKeyboardMarkup EndStickerUpload = new(new[]
         {
             new[] {InlineKeyboardButton.WithCallbackData(Text.end_sticker_upload, Command.end_sticker_upload)},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         public static InlineKeyboardMarkup BackToFilters(string stickerTitle)
@@ -45,7 +43,7 @@ namespace CardCollector.Resources
             return new InlineKeyboardMarkup(new[]
             {
                 new[] {InlineKeyboardButton.WithSwitchInlineQuery(Text.send_sticker, stickerTitle)},
-                new[] {InlineKeyboardButton.WithCallbackData(Text.back, $"{Command.back}={Command.clear_chat}")}
+                new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)}
             });
         }
 
@@ -53,13 +51,13 @@ namespace CardCollector.Resources
         {
             var keyboard = new List<InlineKeyboardButton[]>
             {
-                new[] {InlineKeyboardButton.WithCallbackData(Text.author, $"{Command.author}=1")},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.author, $"{Command.authors_menu}=1")},
                 new[] {InlineKeyboardButton.WithCallbackData(Text.tier, Command.tier)},
                 new[] {InlineKeyboardButton.WithCallbackData(Text.emoji, Command.emoji)}
             };
-            if (state != UserState.CollectionMenu) keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.price, Command.price)});
+            if (state != UserState.CollectionMenu) keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.price, Command.select_price)});
             keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.sort, Command.sort)});
-            keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)});
+            keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)});
             keyboard.Add(new[] {InlineKeyboardButton.WithSwitchInlineQueryCurrentChat(Text.show_stickers)});
             return new InlineKeyboardMarkup(keyboard);
         }
@@ -71,7 +69,7 @@ namespace CardCollector.Resources
             new[] {InlineKeyboardButton.WithCallbackData(SortingTypes.ByTierDecrease, $"{Command.set}={Command.sort}={SortingTypes.ByTierDecrease}")},
             new[] {InlineKeyboardButton.WithCallbackData(SortingTypes.ByAuthor, $"{Command.set}={Command.sort}={SortingTypes.ByAuthor}")},
             new[] {InlineKeyboardButton.WithCallbackData(SortingTypes.ByTitle, $"{Command.set}={Command.sort}={SortingTypes.ByTitle}")},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         /* Клавиатура меню выбора тира */
@@ -82,27 +80,27 @@ namespace CardCollector.Resources
             new[] {InlineKeyboardButton.WithCallbackData("2", $"{Command.set}={Command.tier}=2")},
             new[] {InlineKeyboardButton.WithCallbackData("3", $"{Command.set}={Command.tier}=3")},
             new[] {InlineKeyboardButton.WithCallbackData("4", $"{Command.set}={Command.tier}=4")},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         /* Клавиатура меню ввода эмоджи */
         public static readonly InlineKeyboardMarkup EmojiOptions = new (new[]
         {
             new[] {InlineKeyboardButton.WithCallbackData(Text.all, $"{Command.set}={Command.emoji}=")},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         /* Клавиатура с одной кнопкой отмены */
-        public static readonly InlineKeyboardMarkup CancelKeyboard = new (new[]
+        public static readonly InlineKeyboardMarkup BackKeyboard = new (new[]
         {
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         /* Клавиатура с отменой и выставлением */
         public static readonly InlineKeyboardMarkup AuctionPutCancelKeyboard = new (new[]
         {
             new[] {InlineKeyboardButton.WithCallbackData(Text.sell_on_auction, Command.confirm_selling)},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         /* Клавиатура меню выбора цен */
@@ -124,7 +122,7 @@ 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)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
         /* Клавиатура меню выбора цен */
@@ -146,129 +144,74 @@ namespace CardCollector.Resources
                 InlineKeyboardButton.WithCallbackData($"💎 {Text.from} 100", $"{Command.set}={Command.price_gems_from}=100"),
                 InlineKeyboardButton.WithCallbackData($"💎 {Text.to} ∞", $"{Command.set}={Command.price_gems_to}=0"),
             },
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
-        /* Возвращает клавиатуру со списоком авторов */
-        public static InlineKeyboardMarkup GetAuthorsKeyboard(List<string> list, int page = 1)
+        public static InlineKeyboardMarkup GetAuthorsKeyboard(List<string> list, InlineKeyboardButton[] pagePanel)
         {
-            /* Список авторов, отображаемый на текущей странице */
-            var sublist = list.GetRange((page - 1) * 10,
-                list.Count >= page * 10 ? 10 : list.Count % 10);
-            /* Список кнопок на клавиатуре */
-            var keyboardList = new List<InlineKeyboardButton[]>
-            {
-                new[]
-                {
-                    /* Добавляем в список кнопку "Все" */
-                    InlineKeyboardButton.WithCallbackData(Text.all, 
-                        $"{Command.set}={Command.author}=")
-                }
+            var keyboardList = new List<InlineKeyboardButton[]> {
+                /* Добавляем в список кнопку "Все" */
+                new[] {InlineKeyboardButton.WithCallbackData(Text.all, $"{Command.set}={Command.authors_menu}=")}
             };
-            /* Копируем список */
-            var copyList = sublist.ToList();
-            while (copyList.Count > 0)
+            foreach (var (author, i) in list.WithIndex())
             {
-                /* Берем первый элемент и запихиваем его в строку */
-                var author = copyList[0];
-                copyList.RemoveAt(0);
-                var keyRow = new List<InlineKeyboardButton>
-                {
-                    InlineKeyboardButton.WithCallbackData(author,
-                            $"{Command.set}={Command.author}={author}")
+                if (i % 2 == 0) keyboardList.Add(new [] {
+                    InlineKeyboardButton.WithCallbackData(author, $"{Command.set}={Command.authors_menu}={author}")
+                });
+                else keyboardList[keyboardList.Count - 1] = new [] {
+                    keyboardList[keyboardList.Count - 1][0],
+                    InlineKeyboardButton.WithCallbackData(author, $"{Command.set}={Command.authors_menu}={author}")
                 };
-                /* Если есть еще элементы, то добавляем в строку вторую кнопку */
-                if (copyList.Count > 0)
-                {
-                    author = copyList[0];
-                    copyList.RemoveAt(0);
-                    keyRow.Add(InlineKeyboardButton.WithCallbackData(author,
-                            $"{Command.set}={Command.author}={author}"));
-                }
-                /* Добавляем строку кнопок в клавиатуру */
-                keyboardList.Add(keyRow.ToArray());
             }
-
-            /* Если всего авторов больше 10, то добавляем стрелочки */
-            if (list.Count > 10)
-                keyboardList.Add(
-                    sublist.Count switch
-                    {
-                        <10 => new[]
-                        {
-                            InlineKeyboardButton.WithCallbackData(Text.previous, $"{Command.author}={page - 1}")
-                        },
-                        >=10 when page == 1 => new[]
-                        {
-                            InlineKeyboardButton.WithCallbackData(Text.next, $"{Command.author}={page + 1}")
-                        },
-                        _ => new[]
-                        {
-                            InlineKeyboardButton.WithCallbackData(Text.previous, $"{Command.author}={page - 1}"),
-                            InlineKeyboardButton.WithCallbackData(Text.next, $"{Command.author}={page + 1}")
-                        }
-                    }
-                );
-            /* Добавляем кнопку отмены */
-            keyboardList.Add(new[] {
-                InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)
-            });
-            /* Вовзращаем клавиатуру */
+            keyboardList.Add(pagePanel);
             return new InlineKeyboardMarkup(keyboardList);
         }
 
-        /* Возвращает клавиатуру со списоком авторов */
-        public static InlineKeyboardMarkup GetAuthorsKeyboard(List<PackEntity> infoList, int page)
+        public static InlineKeyboardButton[] GetPagePanel(int page, int totalCount, string callback)
+        {
+            var arrows = new List<InlineKeyboardButton>();
+            if (page > 1) arrows.Add(InlineKeyboardButton
+                .WithCallbackData(Text.previous, $"{callback}={page - 1}"));
+            arrows.Add(InlineKeyboardButton.WithCallbackData(Text.back, Command.back));
+            if (totalCount > page * 10) arrows.Add(InlineKeyboardButton
+                .WithCallbackData(Text.next, $"{callback}={page + 1}"));
+            return arrows.ToArray();
+        }
+
+        public static InlineKeyboardMarkup GetShopPacksKeyboard(List<PackEntity> infoList, InlineKeyboardButton[] pagePanel)
         {
-            /* Список кнопок на клавиатуре */
             var keyboardList = new List<InlineKeyboardButton[]>();
-            /* Копируем список */
             foreach (var (item, i) in infoList.WithIndex())
             {
                 if (i % 2 == 0) keyboardList.Add(new [] {
-                    InlineKeyboardButton.WithCallbackData(item.Author, $"{Command.open_pack}={item.Id}")
+                    InlineKeyboardButton.WithCallbackData(item.Author, $"{Command.select_shop_pack}={item.Id}")
                 });
                 else keyboardList[keyboardList.Count - 1] = new [] {
                     keyboardList[keyboardList.Count - 1][0],
-                    InlineKeyboardButton.WithCallbackData(item.Author, $"{Command.open_pack}={item.Id}")
+                    InlineKeyboardButton.WithCallbackData(item.Author, $"{Command.select_shop_pack}={item.Id}")
                 };
             }
-            keyboardList.Add(new[] {
-                InlineKeyboardButton.WithCallbackData(Text.previous, $"{Command.author_menu}={page - 1}"),
-                InlineKeyboardButton.WithCallbackData(Text.next, $"{Command.author_menu}={page + 1}")
-            });
-            keyboardList.Add(new[] {
-                InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)
-            });
-            /* Вовзращаем клавиатуру */
+            keyboardList.Add(pagePanel);
             return new InlineKeyboardMarkup(keyboardList);
         }
 
-        /* Возвращает клавиатуру со списоком авторов */
-        public static async Task<InlineKeyboardMarkup> GetAuthorsKeyboard(List<SpecificPacksEntity> infoList, int page)
+        public static async Task<InlineKeyboardMarkup> GetUserPacksKeyboard(List<UserPacks> infoList, InlineKeyboardButton[] pagePanel)
         {
-            /* Список кнопок на клавиатуре */
             var keyboardList = new List<InlineKeyboardButton[]>();
-            /* Копируем список */
             foreach (var (item, i) in infoList.WithIndex())
             {
                 var author = await PacksDao.GetById(item.PackId);
                 if (i % 2 == 0) keyboardList.Add(new [] {
-                    InlineKeyboardButton.WithCallbackData($"{author.Author} {item.Count}", $"{Command.open_pack}={item.PackId}")
+                    InlineKeyboardButton.WithCallbackData($"{author.Author} ({item.Count}{Text.items})", 
+                        $"{Command.open_pack}={item.PackId}")
                 });
                 else keyboardList[keyboardList.Count - 1] = new [] {
                     keyboardList[keyboardList.Count - 1][0],
-                    InlineKeyboardButton.WithCallbackData($"{author.Author} {item.Count}", $"{Command.open_pack}={item.PackId}")
+                    InlineKeyboardButton.WithCallbackData($"{author.Author} ({item.Count}{Text.items})",
+                        $"{Command.open_pack}={item.PackId}")
                 };
             }
-            keyboardList.Add(new[] {
-                InlineKeyboardButton.WithCallbackData(Text.previous, $"{Command.open_specific}={page - 1}"),
-                InlineKeyboardButton.WithCallbackData(Text.next, $"{Command.open_specific}={page + 1}")
-            });
-            keyboardList.Add(new[] {
-                InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)
-            });
-            /* Вовзращаем клавиатуру */
+            keyboardList.Add(pagePanel);
             return new InlineKeyboardMarkup(keyboardList);
         }
 
@@ -287,7 +230,7 @@ namespace CardCollector.Resources
                 }
             };
             if (sticker.Tier != 4) keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData($"{Text.combine} ({count})", Command.combine)});
-            keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.back, $"{Command.back}={Command.clear_chat}")});
+            keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)});
             return new InlineKeyboardMarkup(keyboard);
         }
 
@@ -295,7 +238,7 @@ namespace CardCollector.Resources
         {
             return new InlineKeyboardMarkup(new[] {
                 new[] {InlineKeyboardButton.WithSwitchInlineQueryCurrentChat(Text.show_traders)},
-                new[] {InlineKeyboardButton.WithCallbackData(Text.back, $"{Command.back}={Command.clear_chat}")},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
             });
         }
 
@@ -309,7 +252,7 @@ namespace CardCollector.Resources
                     InlineKeyboardButton.WithCallbackData(Text.minus, $"{Command.count}={Text.minus}"),
                     InlineKeyboardButton.WithCallbackData(Text.plus, $"{Command.count}={Text.plus}"),
                 },
-                new[] {InlineKeyboardButton.WithCallbackData(Text.back, $"{Command.back}={Command.clear_chat}")},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
             });
         }
 
@@ -317,7 +260,7 @@ namespace CardCollector.Resources
         {
             return new InlineKeyboardMarkup(new[] {
                 new[] {InlineKeyboardButton.WithSwitchInlineQuery(Text.send_sticker, stickerInfo.Title)},
-                new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.clear_chat)},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
             });
         }
 
@@ -353,7 +296,7 @@ namespace CardCollector.Resources
                     InlineKeyboardButton.WithCallbackData(Text.minus, $"{Command.count}={Text.minus}"),
                     InlineKeyboardButton.WithCallbackData(Text.plus, $"{Command.count}={Text.plus}"),
                 },
-                new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back_to_combine)},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
                 new[] {InlineKeyboardButton.WithSwitchInlineQueryCurrentChat(Text.select_another)},
             });
         }
@@ -366,12 +309,12 @@ namespace CardCollector.Resources
                 keyboard.Add(new []{InlineKeyboardButton.WithCallbackData($"{Text.delete} {Text.sticker} {keyboard.Count + 1}",
                     $"{Command.delete_combine}={sticker.Md5Hash}")});
             }
-            keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)});
+            keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.back, Command.back)});
             if (module.GetCombineCount() == Constants.COMBINE_COUNT)
                 keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(
-                    $"{Text.combine} {module.CalculateCombinePrice()}{Text.coin}",
-                    Command.combine_stickers)});
+                    $"{Text.combine} {module.CalculateCombinePrice()}{Text.coin}", Command.combine_stickers)});
             else keyboard.Add(new[] {InlineKeyboardButton.WithSwitchInlineQueryCurrentChat(Text.add_sticker)});
+            keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)});
             return new InlineKeyboardMarkup(keyboard);
         }
 
@@ -385,6 +328,7 @@ namespace CardCollector.Resources
             };
             if (privilegeLevel > 2) keyboard.Add(
                 new[] {InlineKeyboardButton.WithCallbackData(Text.control_panel, Command.control_panel)});
+            keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)});
             return new InlineKeyboardMarkup(keyboard);
         }
 
@@ -395,7 +339,7 @@ namespace CardCollector.Resources
                 new[] {InlineKeyboardButton.WithCallbackData(Text.buy_pack, Command.buy_pack)},
                 new[] {InlineKeyboardButton.WithCallbackData(Text.buy_coins, Command.buy_coins)},
                 new[] {InlineKeyboardButton.WithCallbackData(Text.buy_gems, Command.buy_gems)},
-                new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
             });
         }
 
@@ -406,34 +350,40 @@ namespace CardCollector.Resources
                 keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(offer.Title,
                         $"{Command.select_offer}={offer.Id}")
                 });
-            keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)});
+            keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.back, Command.back)});
             return new InlineKeyboardMarkup(keyboard);
         }
 
         public static InlineKeyboardMarkup ShopPacksKeyboard = new (new[]
         {
-            new[] {InlineKeyboardButton.WithCallbackData(Text.buy_random, $"{Command.select_offer}=1")},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.buy_author, $"{Command.select_offer}=2")},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.buy_random, $"{Command.select_shop_pack}=1")},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.buy_author, $"{Command.buy_author_pack_menu}=1")},
             new[] {InlineKeyboardButton.WithCallbackData(Text.info, Command.pack_info)},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.back, Command.back)},
         });
 
-        public static InlineKeyboardMarkup OfferKeyboard(ShopEntity offerInfo)
+        public static InlineKeyboardMarkup OfferKeyboard(ShopModule module)
         {
-            var keyboard = new List<InlineKeyboardButton[]> ();
-            if (offerInfo.PriceCoins >= 0)
+            var resultPriceCoins = module.SelectedPosition?.ResultPriceCoins 
+                                   ?? module.SelectedPack?.PriceCoins * module.Count ?? -1; 
+            var resultPriceGems = module.SelectedPosition?.ResultPriceGems 
+                                  ?? module.SelectedPack?.PriceGems * module.Count ?? -1; 
+            var keyboard = new List<InlineKeyboardButton[]>();
+            if (resultPriceCoins >= 0)
                 keyboard.Add(new [] {InlineKeyboardButton.WithCallbackData(
-                    $"{offerInfo.ResultPriceCoins}{Text.coin}", Command.buy_by_coins)
+                    $"{resultPriceCoins}{Text.coin}", $"{Command.buy_shop_item}=coins")
                 });
-            if (offerInfo.PriceGems >= 0)
-                if (keyboard.Count > 0) keyboard[0] = new [] {keyboard[0][0], InlineKeyboardButton.WithCallbackData(
-                    $"{offerInfo.ResultPriceGems}{Text.gem}", Command.buy_by_gems)
+            if (resultPriceGems >= 0)
+                if (keyboard.Count > 0) keyboard[0] = new [] {
+                    keyboard[0][0], 
+                    InlineKeyboardButton.WithCallbackData($"{resultPriceGems}{Text.gem}",
+                        $"{Command.buy_shop_item}=gems")
                 };
-                else keyboard.Add(new [] {InlineKeyboardButton.WithCallbackData(
-                    $"{offerInfo.ResultPriceGems}{Text.gem}", Command.buy_by_gems)
+                else keyboard.Add(new [] {InlineKeyboardButton.WithCallbackData($"{resultPriceGems}{Text.gem}",
+                    $"{Command.buy_shop_item}=gems")
                 });
             keyboard.Add(new [] {InlineKeyboardButton.WithCallbackData(Text.info, Command.show_offer_info)});
-            keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)});
+            keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.back, Command.back)});
             return new InlineKeyboardMarkup(keyboard);
         }
 

+ 3 - 3
CardCollector/Resources/Messages.Designer.cs

@@ -90,9 +90,9 @@ namespace CardCollector.Resources {
         /// <summary>
         ///   Looks up a localized string similar to Пак художника.
         /// </summary>
-        internal static string author_packs {
+        internal static string author_pack {
             get {
-                return ResourceManager.GetString("author_packs", resourceCulture);
+                return ResourceManager.GetString("author_pack", resourceCulture);
             }
         }
         
@@ -495,7 +495,7 @@ namespace CardCollector.Resources {
         /// <summary>
         ///   Looks up a localized string similar to При покупке &quot;Случайного пака&quot; Вам может выпасть ЛЮБОЙ стикер от ЛЮБОГО художника
         ///
-        ///        При покупке &quot;Пака художника&quot; Вам может выпасть ЛЮБОЙ стикер от выбранного Вами художника художника
+        ///        При покупке &quot;Пака художника&quot; Вам может выпасть ЛЮБОЙ стикер от выбранного Вами художника
         ///
         ///        Вероятности выпадения стикера с тиром:
         ///        1 - 80%

+ 2 - 2
CardCollector/Resources/Messages.resx

@@ -156,7 +156,7 @@
     <data name="random_packs" xml:space="preserve">
         <value>Случайный пак</value>
     </data>
-    <data name="author_packs" xml:space="preserve">
+    <data name="author_pack" xml:space="preserve">
         <value>Пак художника</value>
     </data>
     <data name="comission_warning" xml:space="preserve">
@@ -186,7 +186,7 @@
     <data name="pack_info" xml:space="preserve">
         <value>При покупке "Случайного пака" Вам может выпасть ЛЮБОЙ стикер от ЛЮБОГО художника
 
-        При покупке "Пака художника" Вам может выпасть ЛЮБОЙ стикер от выбранного Вами художника художника
+        При покупке "Пака художника" Вам может выпасть ЛЮБОЙ стикер от выбранного Вами художника
 
         Вероятности выпадения стикера с тиром:
         1 - 80%

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

@@ -501,6 +501,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Количество открытых:.
+        /// </summary>
+        internal static string opened_count {
+            get {
+                return ResourceManager.GetString("opened_count", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to за.
         /// </summary>

+ 3 - 0
CardCollector/Resources/Text.resx

@@ -249,4 +249,7 @@
     <data name="end_sticker_upload" xml:space="preserve">
         <value>Завершить загрузку стикеров</value>
     </data>
+    <data name="opened_count" xml:space="preserve">
+        <value>Количество открытых:</value>
+    </data>
 </root>

+ 30 - 0
CardCollector/Session/MenuInformation.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Threading.Tasks;
+using CardCollector.Commands;
+using CardCollector.Resources;
+
+namespace CardCollector.Session
+{
+    public class MenuInformation
+    {
+        private UpdateModel Parent;
+        private UserState State;
+
+        public async Task BackToThis(UserSession session)
+        {
+            session.State = State;
+            await Parent.PrepareAndExecute();
+        }
+
+        public Type GetMenuType()
+        {
+            return Parent.GetType();
+        }
+
+        public MenuInformation(UpdateModel parent, UserState state)
+        {
+            Parent = parent;
+            State = state;
+        }
+    }
+}

Some files were not shown because too many files changed in this diff