Browse Source

Refactore code. Exchange coins. Rework special offers.

Tigran 3 years ago
parent
commit
e98241bc69
82 changed files with 727 additions and 320 deletions
  1. 1 1
      CardCollector.sln.DotSettings.user
  2. 3 3
      CardCollector/Commands/CallbackQuery/AuthorMenu.cs
  3. 1 1
      CardCollector/Commands/CallbackQuery/BackToCombine.cs
  4. 2 2
      CardCollector/Commands/CallbackQuery/BackToFiltersMenu.cs
  5. 2 1
      CardCollector/Commands/CallbackQuery/BuyByCoins.cs
  6. 2 1
      CardCollector/Commands/CallbackQuery/BuyByGems.cs
  7. 30 0
      CardCollector/Commands/CallbackQuery/BuyCoins.cs
  8. 4 4
      CardCollector/Commands/CallbackQuery/BuyGems.cs
  9. 3 3
      CardCollector/Commands/CallbackQuery/BuyPack.cs
  10. 3 3
      CardCollector/Commands/CallbackQuery/BuySticker.cs
  11. 5 5
      CardCollector/Commands/CallbackQuery/Cancel.cs
  12. 1 1
      CardCollector/Commands/CallbackQuery/ClearChat.cs
  13. 3 3
      CardCollector/Commands/CallbackQuery/CollectIncome.cs
  14. 3 3
      CardCollector/Commands/CallbackQuery/Combine.cs
  15. 1 1
      CardCollector/Commands/CallbackQuery/CombineStickers.cs
  16. 3 3
      CardCollector/Commands/CallbackQuery/ConfirmBuying.cs
  17. 30 0
      CardCollector/Commands/CallbackQuery/ConfirmExchange.cs
  18. 4 4
      CardCollector/Commands/CallbackQuery/ConfirmationSelling.cs
  19. 3 3
      CardCollector/Commands/CallbackQuery/Count.cs
  20. 3 3
      CardCollector/Commands/CallbackQuery/DailyTasks.cs
  21. 1 1
      CardCollector/Commands/CallbackQuery/DeleteCombine.cs
  22. 3 3
      CardCollector/Commands/CallbackQuery/MyPacks.cs
  23. 3 3
      CardCollector/Commands/CallbackQuery/OpenPack.cs
  24. 3 3
      CardCollector/Commands/CallbackQuery/OpenSpecific.cs
  25. 1 1
      CardCollector/Commands/CallbackQuery/PackInfo.cs
  26. 4 4
      CardCollector/Commands/CallbackQuery/PutForAuction.cs
  27. 3 3
      CardCollector/Commands/CallbackQuery/SelectAuthor.cs
  28. 4 4
      CardCollector/Commands/CallbackQuery/SelectEmoji.cs
  29. 27 0
      CardCollector/Commands/CallbackQuery/SelectOffer.cs
  30. 0 45
      CardCollector/Commands/CallbackQuery/SelectOfferCallback.cs
  31. 3 3
      CardCollector/Commands/CallbackQuery/SelectPrice.cs
  32. 3 3
      CardCollector/Commands/CallbackQuery/SelectSort.cs
  33. 3 3
      CardCollector/Commands/CallbackQuery/SelectTier.cs
  34. 4 4
      CardCollector/Commands/CallbackQuery/SetFilter.cs
  35. 43 0
      CardCollector/Commands/CallbackQuery/ShowInfo.cs
  36. 28 0
      CardCollector/Commands/CallbackQuery/SpecialOffers.cs
  37. 32 27
      CardCollector/Commands/CallbackQueryCommand.cs
  38. 2 2
      CardCollector/Commands/ChosenInlineResult/GetUnlimitedStickerAndExecuteCommand.cs
  39. 3 3
      CardCollector/Commands/ChosenInlineResult/SelectStickerInline.cs
  40. 3 3
      CardCollector/Commands/ChosenInlineResult/SelectTrader.cs
  41. 1 1
      CardCollector/Commands/ChosenInlineResult/SendPrivateSticker.cs
  42. 3 3
      CardCollector/Commands/ChosenInlineResult/SendSticker.cs
  43. 10 9
      CardCollector/Commands/ChosenInlineResultCommand.cs
  44. 1 1
      CardCollector/Commands/InlineQuery/ShowAuctionStickers.cs
  45. 1 1
      CardCollector/Commands/InlineQuery/ShowCollectionStickers.cs
  46. 1 1
      CardCollector/Commands/InlineQuery/ShowCombineStickers.cs
  47. 1 1
      CardCollector/Commands/InlineQuery/ShowStickersInBotChat.cs
  48. 1 1
      CardCollector/Commands/InlineQuery/ShowStickersInGroup.cs
  49. 1 1
      CardCollector/Commands/InlineQuery/ShowStickersInPrivate.cs
  50. 1 1
      CardCollector/Commands/InlineQuery/ShowTradersInBotChat.cs
  51. 6 5
      CardCollector/Commands/InlineQueryCommand.cs
  52. 3 3
      CardCollector/Commands/Message/DocumentMessage/UploadFileMessageCommand.cs
  53. 3 3
      CardCollector/Commands/Message/TextMessage/Auction.cs
  54. 3 3
      CardCollector/Commands/Message/TextMessage/Collection.cs
  55. 3 3
      CardCollector/Commands/Message/TextMessage/DownloadStickerPack.cs
  56. 3 3
      CardCollector/Commands/Message/TextMessage/EnterEmoji.cs
  57. 62 0
      CardCollector/Commands/Message/TextMessage/EnterGemsExchange.cs
  58. 4 4
      CardCollector/Commands/Message/TextMessage/EnterGemsPrice.cs
  59. 3 3
      CardCollector/Commands/Message/TextMessage/Menu.cs
  60. 3 3
      CardCollector/Commands/Message/TextMessage/Profile.cs
  61. 6 6
      CardCollector/Commands/Message/TextMessage/Shop.cs
  62. 1 1
      CardCollector/Commands/Message/TextMessage/ShowFiltersMenu.cs
  63. 13 9
      CardCollector/Commands/Message/TextMessage/ShowSample.cs
  64. 3 3
      CardCollector/Commands/Message/TextMessage/Start.cs
  65. 1 1
      CardCollector/Commands/Message/TextMessage/StopBot.cs
  66. 22 21
      CardCollector/Commands/MessageCommand.cs
  67. 3 3
      CardCollector/Commands/MyChatMember/MyChatMemberCommand.cs
  68. 4 4
      CardCollector/Commands/PreCheckoutQuery/BuyGems.cs
  69. 3 3
      CardCollector/Commands/PreCheckoutQuery/Test.cs
  70. 8 7
      CardCollector/Commands/PreCheckoutQueryCommand.cs
  71. 24 30
      CardCollector/Controllers/MessageController.cs
  72. 2 0
      CardCollector/DataBase/Entity/ShopEntity.cs
  73. 2 2
      CardCollector/DataBase/Entity/UserEntity.cs
  74. 1 1
      CardCollector/Logs.cs
  75. 45 9
      CardCollector/Resources/Command.Designer.cs
  76. 13 1
      CardCollector/Resources/Command.resx
  77. 20 4
      CardCollector/Resources/Keyboard.cs
  78. 74 2
      CardCollector/Resources/Messages.Designer.cs
  79. 25 1
      CardCollector/Resources/Messages.resx
  80. 46 1
      CardCollector/Resources/Text.Designer.cs
  81. 16 1
      CardCollector/Resources/Text.resx
  82. 1 0
      CardCollector/Session/Modules/ShopModule.cs

+ 1 - 1
CardCollector.sln.DotSettings.user

@@ -19,6 +19,6 @@
 	<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">True</s:Boolean>
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FStickerEffects_002FEffectTranslations/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FStickerEffects_002FEffectTranslations/@EntryIndexedValue">False</s:Boolean>
 	
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>

+ 3 - 3
CardCollector/Commands/CallbackQuery/AuthorMenuQuery.cs → CardCollector/Commands/CallbackQuery/AuthorMenu.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class AuthorMenuQuery : CallbackQuery
+    public class AuthorMenu : CallbackQueryCommand
     {
         protected override string CommandText => Command.author_menu;
         public override async Task Execute()
@@ -31,7 +31,7 @@ namespace CardCollector.Commands.CallbackQuery
             }
         }
 
-        public AuthorMenuQuery() { }
-        public AuthorMenuQuery(UserEntity user, Update update) : base(user, update) { }
+        public AuthorMenu() { }
+        public AuthorMenu(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 1 - 1
CardCollector/Commands/CallbackQuery/BackToCombine.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class BackToCombine : CallbackQuery
+    public class BackToCombine : CallbackQueryCommand
     {
         protected override string CommandText => Command.back_to_combine;
         public override async Task Execute()

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

@@ -8,13 +8,13 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class BackToFiltersMenu : CallbackQuery
+    public class BackToFiltersMenu : CallbackQueryCommand
     {
         protected override string CommandText => Command.back;
         public override async Task Execute()
         {
             /* Удаляем пользователя из очереди */
-            EnterEmojiMessage.RemoveFromQueue(User.Id);
+            EnterEmoji.RemoveFromQueue(User.Id);
             switch (User.Session.State)
             {
                 case UserState.CombineMenu:

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

@@ -9,7 +9,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class BuyByCoins : CallbackQuery
+    public class BuyByCoins : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_by_coins;
         public override async Task Execute()
@@ -23,6 +23,7 @@ namespace CardCollector.Commands.CallbackQuery
                 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);

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

@@ -9,7 +9,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class BuyByGems : CallbackQuery
+    public class BuyByGems : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_by_gems;
         public override async Task Execute()
@@ -23,6 +23,7 @@ namespace CardCollector.Commands.CallbackQuery
                 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);

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

@@ -0,0 +1,30 @@
+using System.Threading.Tasks;
+using CardCollector.Commands.Message.TextMessage;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using CardCollector.Session.Modules;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    public class BuyCoins : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.buy_coins;
+
+        public override async Task Execute()
+        {
+            EnterGemsExchange.AddToQueue(User.Id, CallbackMessageId);
+            var module = User.Session.GetModule<ShopModule>();
+            await MessageController.EditMessage(User, CallbackMessageId,
+                $"{Messages.exchange_mesage}" +
+                $"\n{Messages.gems_exchange_count} {module.EnteredExchangeSum}{Text.gem}" +
+                $"\n{Messages.coins_exchange_count} {module.EnteredExchangeSum * 150}{Text.coin}" +
+                $"\n{Messages.enter_exchange_sum}",
+                Keyboard.BuyCoinsKeyboard);
+        }
+
+        public BuyCoins() { }
+        public BuyCoins(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 4 - 4
CardCollector/Commands/CallbackQuery/BuyGemsQuery.cs → CardCollector/Commands/CallbackQuery/BuyGems.cs

@@ -7,19 +7,19 @@ using Telegram.Bot.Types.Payments;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class BuyGemsQuery : CallbackQuery
+    public class BuyGems : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_gems;
         public override async Task Execute()
         {
             await User.ClearChat();
             var messages = await MessageController.SendInvoice(User, Text.gems_title, Text.gems_description, 
-                Command.gems50, new[] {new LabeledPrice(Text.gems_label50, 100)},
+                Command.buy_gems_item, new[] {new LabeledPrice(Text.gems_label50, 100)},
                 1000000, new [] {500, 1000, 2500, 5000});
             User.Session.Messages.Add(messages.MessageId);
         }
 
-        public BuyGemsQuery() { }
-        public BuyGemsQuery(UserEntity user, Update update) : base(user, update) { }
+        public BuyGems() { }
+        public BuyGems(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/BuyPackQuery.cs → CardCollector/Commands/CallbackQuery/BuyPack.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class BuyPackQuery : CallbackQuery
+    public class BuyPack : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_pack;
         public override async Task Execute()
@@ -15,7 +15,7 @@ namespace CardCollector.Commands.CallbackQuery
                 Keyboard.ShopPacksKeyboard);
         }
 
-        public BuyPackQuery() { }
-        public BuyPackQuery(UserEntity user, Update update) : base(user, update) { }
+        public BuyPack() { }
+        public BuyPack(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/BuyStickerQuery.cs → CardCollector/Commands/CallbackQuery/BuySticker.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class BuyStickerQuery : CallbackQuery
+    public class BuySticker : CallbackQueryCommand
     {
         protected override string CommandText => Command.buy_sticker;
 
@@ -36,7 +36,7 @@ namespace CardCollector.Commands.CallbackQuery
             }
         }
 
-        public BuyStickerQuery() { }
-        public BuyStickerQuery(UserEntity user, Update update) : base(user, update) { }
+        public BuySticker() { }
+        public BuySticker(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 5 - 5
CardCollector/Commands/CallbackQuery/CancelCallback.cs → CardCollector/Commands/CallbackQuery/Cancel.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class CancelCallback : CallbackQuery
+    public class Cancel : CallbackQueryCommand
     {
         protected override string CommandText => Command.cancel;
         public override async Task Execute()
@@ -34,12 +34,12 @@ namespace CardCollector.Commands.CallbackQuery
                     break;
             }
             User.Session.State = UserState.Default;
-            EnterEmojiMessage.RemoveFromQueue(User.Id);
-            EnterGemsPriceMessage.RemoveFromQueue(User.Id);
+            EnterEmoji.RemoveFromQueue(User.Id);
+            EnterGemsPrice.RemoveFromQueue(User.Id);
             await User.ClearChat();
         }
 
-        public CancelCallback() { }
-        public CancelCallback(UserEntity user, Update update) : base(user, update) { }
+        public Cancel() { }
+        public Cancel(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 1 - 1
CardCollector/Commands/CallbackQuery/ClearChat.cs

@@ -5,7 +5,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class ClearChat : CallbackQuery
+    public class ClearChat : CallbackQueryCommand
     {
         protected override string CommandText => Command.clear_chat;
         public override async Task Execute()

+ 3 - 3
CardCollector/Commands/CallbackQuery/CollectIncomeQuery.cs → CardCollector/Commands/CallbackQuery/CollectIncome.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class CollectIncomeQuery : CallbackQuery
+    public class CollectIncome : CallbackQueryCommand
     {
         protected override string CommandText => Command.collect_income;
         public override async Task Execute()
@@ -21,7 +21,7 @@ namespace CardCollector.Commands.CallbackQuery
             await MessageController.DeleteMessage(User, Update.CallbackQuery!.Message!.MessageId);
         }
 
-        public CollectIncomeQuery() { }
-        public CollectIncomeQuery(UserEntity user, Update update) : base(user, update) { }
+        public CollectIncome() { }
+        public CollectIncome(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/CombineCallback.cs → CardCollector/Commands/CallbackQuery/Combine.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class CombineCallback : CallbackQuery
+    public class Combine : CallbackQueryCommand
     {
         protected override string CommandText => Command.combine;
         public override async Task Execute()
@@ -45,7 +45,7 @@ namespace CardCollector.Commands.CallbackQuery
             await new BackToCombine(User, Update).Execute();
         }
 
-        public CombineCallback() { }
-        public CombineCallback(UserEntity user, Update update) : base(user, update) { }
+        public Combine() { }
+        public Combine(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 1 - 1
CardCollector/Commands/CallbackQuery/CombineStickers.cs

@@ -10,7 +10,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class CombineStickers : CallbackQuery
+    public class CombineStickers : CallbackQueryCommand
     {
         protected override string CommandText => Command.combine_stickers;
         public override async Task Execute()

+ 3 - 3
CardCollector/Commands/CallbackQuery/ConfirmBuyingQuery.cs → CardCollector/Commands/CallbackQuery/ConfirmBuying.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class ConfirmBuyingQuery : CallbackQuery
+    public class ConfirmBuying : CallbackQueryCommand
     {
         protected override string CommandText => Command.confirm_buying;
         public override async Task Execute()
@@ -23,7 +23,7 @@ namespace CardCollector.Commands.CallbackQuery
             }
         }
 
-        public ConfirmBuyingQuery() { }
-        public ConfirmBuyingQuery(UserEntity user, Update update) : base(user, update) { }
+        public ConfirmBuying() { }
+        public ConfirmBuying(UserEntity user, Update update) : base(user, update) { }
     }
 }

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

@@ -0,0 +1,30 @@
+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 ConfirmExchange : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.confirm_exchange;
+        public override async Task Execute()
+        {
+            var module = User.Session.GetModule<ShopModule>();
+            if (module.EnteredExchangeSum > User.Cash.Gems)
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.not_enougth_gems);
+            else
+            {
+                User.Cash.Gems -= module.EnteredExchangeSum;
+                User.Cash.Coins += module.EnteredExchangeSum * 150;
+                await MessageController.EditMessage(User, CallbackMessageId,
+                    $"{Messages.you_got} {module.EnteredExchangeSum * 150}{Text.coin} {Text.per} {module.EnteredExchangeSum}{Text.gem}");
+            }
+        }
+
+        public ConfirmExchange() { }
+        public ConfirmExchange(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 4 - 4
CardCollector/Commands/CallbackQuery/ConfirmationSellingQuery.cs → CardCollector/Commands/CallbackQuery/ConfirmationSelling.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class ConfirmationSellingQuery : CallbackQuery
+    public class ConfirmationSelling : CallbackQueryCommand
     {
         protected override string CommandText => Command.confirm_selling;
         public override async Task Execute()
@@ -18,7 +18,7 @@ namespace CardCollector.Commands.CallbackQuery
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.cant_sell_zero, true);
             else
             {
-                EnterGemsPriceMessage.RemoveFromQueue(User.Id);
+                EnterGemsPrice.RemoveFromQueue(User.Id);
                 User.Stickers[collectionModule.SelectedSticker.Md5Hash].Count -= collectionModule.Count;
                 AuctionController.SellCard(User.Id, collectionModule.SelectedSticker.Id, collectionModule.SellPrice,
                     collectionModule.Count);
@@ -26,8 +26,8 @@ namespace CardCollector.Commands.CallbackQuery
             }
         }
         
-        public ConfirmationSellingQuery(){}
-        public ConfirmationSellingQuery(UserEntity user, Update update) : base(user, update){}
+        public ConfirmationSelling(){}
+        public ConfirmationSelling(UserEntity user, Update update) : base(user, update){}
     }
     
     

+ 3 - 3
CardCollector/Commands/CallbackQuery/CountQuery.cs → CardCollector/Commands/CallbackQuery/Count.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class CountQuery : CallbackQuery
+    public class Count : CallbackQueryCommand
     {
         protected override string CommandText => Command.count;
         public override async Task Execute()
@@ -49,7 +49,7 @@ namespace CardCollector.Commands.CallbackQuery
             else await MessageController.AnswerCallbackQuery(User, Update.CallbackQuery!.Id, Messages.cant_change_count);
         }
 
-        public CountQuery() { }
-        public CountQuery(UserEntity user, Update update) : base(user, update) { }
+        public Count() { }
+        public Count(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/DailyTasksQuery.cs → CardCollector/Commands/CallbackQuery/DailyTasks.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class DailyTasksQuery : CallbackQuery
+    public class DailyTasks : CallbackQueryCommand
     {
         protected override string CommandText => Command.daily_tasks;
         
@@ -27,7 +27,7 @@ namespace CardCollector.Commands.CallbackQuery
             User.Session.Messages.Add(message.MessageId);
         }
 
-        public DailyTasksQuery() { }
-        public DailyTasksQuery(UserEntity user, Update update) : base(user, update) { }
+        public DailyTasks() { }
+        public DailyTasks(UserEntity user, Update update) : base(user, update) { }
     }
 }

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

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class DeleteCombine : CallbackQuery
+    public class DeleteCombine : CallbackQueryCommand
     {
         protected override string CommandText => Command.delete_combine;
         public override async Task Execute()

+ 3 - 3
CardCollector/Commands/CallbackQuery/MyPacksQuery.cs → CardCollector/Commands/CallbackQuery/MyPacks.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class MyPacksQuery : CallbackQuery
+    public class MyPacks : CallbackQueryCommand
     {
         protected override string CommandText => Command.my_packs;
         
@@ -25,7 +25,7 @@ namespace CardCollector.Commands.CallbackQuery
             User.Session.Messages.Add(message.MessageId);
         }
 
-        public MyPacksQuery() { }
-        public MyPacksQuery(UserEntity user, Update update) : base(user, update) { }
+        public MyPacks() { }
+        public MyPacks(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/OpenPackCallback.cs → CardCollector/Commands/CallbackQuery/OpenPack.cs

@@ -9,7 +9,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class OpenPackCallback : CallbackQuery
+    public class OpenPack : CallbackQueryCommand
     {
         protected override string CommandText => Command.open_pack;
         
@@ -76,7 +76,7 @@ namespace CardCollector.Commands.CallbackQuery
             };
         }
 
-        public OpenPackCallback() { }
-        public OpenPackCallback(UserEntity user, Update update) : base(user, update) { }
+        public OpenPack() { }
+        public OpenPack(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/OpenSpecificCallback.cs → CardCollector/Commands/CallbackQuery/OpenSpecific.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class OpenSpecificCallback : CallbackQuery
+    public class OpenSpecific : CallbackQueryCommand
     {
         protected override string CommandText => Command.open_specific;
         public override async Task Execute()
@@ -30,7 +30,7 @@ namespace CardCollector.Commands.CallbackQuery
             }
         }
 
-        public OpenSpecificCallback() { }
-        public OpenSpecificCallback(UserEntity user, Update update) : base(user, update) { }
+        public OpenSpecific() { }
+        public OpenSpecific(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 1 - 1
CardCollector/Commands/CallbackQuery/PackInfo.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class PackInfo : CallbackQuery
+    public class PackInfo : CallbackQueryCommand
     {
         protected override string CommandText => Command.pack_info;
         public override async Task Execute()

+ 4 - 4
CardCollector/Commands/CallbackQuery/PutForAuctionQuery.cs → CardCollector/Commands/CallbackQuery/PutForAuction.cs

@@ -9,7 +9,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class PutForAuctionQuery : CallbackQuery
+    public class PutForAuction : CallbackQueryCommand
     {
         protected override string CommandText => Command.sell_on_auction;
 
@@ -25,11 +25,11 @@ namespace CardCollector.Commands.CallbackQuery
                 $"{Messages.current_price} {module.SellPrice}{Text.gem}" +
                 $"\n{Messages.lower_price} {lowerPrice}{Text.gem}" +
                 $"\n{Messages.enter_your_gems_price} {Text.gem}:", Keyboard.AuctionPutCancelKeyboard);
-            EnterGemsPriceMessage.AddToQueue(User.Id, message.MessageId);
+            EnterGemsPrice.AddToQueue(User.Id, message.MessageId);
             User.Session.Messages.Add(message.MessageId);
         }
 
-        public PutForAuctionQuery() { }
-        public PutForAuctionQuery(UserEntity user, Update update) : base(user, update) { }
+        public PutForAuction() { }
+        public PutForAuction(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/AuthorCallback.cs → CardCollector/Commands/CallbackQuery/SelectAuthor.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.CallbackQuery
 {
     /* Реализует нажатие на кнопку "Автор" (открывается меню с выбором автора) */
-    public class AuthorCallback : CallbackQuery
+    public class SelectAuthor : CallbackQueryCommand
     {
         protected override string CommandText => Command.author;
 
@@ -24,7 +24,7 @@ namespace CardCollector.Commands.CallbackQuery
                 Messages.choose_author, Keyboard.GetAuthorsKeyboard(list, page));
         }
 
-        public AuthorCallback() { }
-        public AuthorCallback(UserEntity user, Update update) : base(user, update) { }
+        public SelectAuthor() { }
+        public SelectAuthor(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 4 - 4
CardCollector/Commands/CallbackQuery/EmojiCallback.cs → CardCollector/Commands/CallbackQuery/SelectEmoji.cs

@@ -7,16 +7,16 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class EmojiCallback : CallbackQuery
+    public class SelectEmoji : CallbackQueryCommand
     {
         protected override string CommandText => Command.emoji;
         public override async Task Execute()
         {
-            EnterEmojiMessage.AddToQueue(User.Id, CallbackMessageId);
+            EnterEmoji.AddToQueue(User.Id, CallbackMessageId);
             await MessageController.EditMessage(User, CallbackMessageId, Messages.enter_emoji, Keyboard.EmojiOptions);
         }
 
-        public EmojiCallback() { }
-        public EmojiCallback(UserEntity user, Update update) : base(user, update) { }
+        public SelectEmoji() { }
+        public SelectEmoji(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 27 - 0
CardCollector/Commands/CallbackQuery/SelectOffer.cs

@@ -0,0 +1,27 @@
+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 SelectOffer : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.select_offer;
+        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));
+            User.Session.Messages.Add(message.MessageId);
+        }
+
+        public SelectOffer() { }
+        public SelectOffer(UserEntity user, Update update) : base(user, update) { }
+    }
+}

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

@@ -1,45 +0,0 @@
-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 SelectOfferCallback : CallbackQuery
-    {
-        protected override string CommandText => Command.select_offer;
-        public override async Task Execute()
-        {
-            var offerId = int.Parse(CallbackData.Split('=')[1]);
-            var offerInfo = await ShopDao.GetById(offerId);
-            var packInfo = await PacksDao.GetById(offerInfo.PackId);
-            User.Session.GetModule<ShopModule>().SelectedPosition = offerInfo;
-            var message = $"{offerInfo.Title}" +
-                          $"\n{packInfo.Author}" +
-                          $"{(packInfo.Description != "" ? $"\n{packInfo.Description}" : "")}";
-            if (offerInfo.Discount > 0) message += $"\n{Text.discount}: {offerInfo.Discount}%";
-            if (offerInfo.Count > 1) message += $"\n{Text.count}: {offerInfo.Count}{Text.items}";
-            if (offerInfo.AdditionalPrize != "") 
-                message += $"\n{Text.prize}: {await PrizeToString(offerInfo.AdditionalPrize)}";
-            if (offerInfo.TimeLimited) message += $"\n{Text.time_limit}: {offerInfo.TimeLimit}";
-            await MessageController.EditMessage(User, CallbackMessageId, message, Keyboard.OfferKeyboard(offerInfo));
-        }
-
-        private async Task<string> PrizeToString(string prize)
-        {
-            var data = prize.Split('=');
-            return data[0] switch
-            {
-                "tier" => $"{Text.sticker} {data[1]} {Text.tier2}",
-                "sticker" => $"{Text.sticker} {(await StickerDao.GetStickerByHash(data[1])).Title}",
-                _ => ""
-            };
-        }
-
-        public SelectOfferCallback() { }
-        public SelectOfferCallback(UserEntity user, Update update) : base(user, update) { }
-    }
-}

+ 3 - 3
CardCollector/Commands/CallbackQuery/PriceCallback.cs → CardCollector/Commands/CallbackQuery/SelectPrice.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class PriceCallback : CallbackQuery
+    public class SelectPrice : CallbackQueryCommand
     {
         protected override string CommandText => Command.price;
         public override async Task Execute()
@@ -15,7 +15,7 @@ namespace CardCollector.Commands.CallbackQuery
                 User.Session.State == UserState.AuctionMenu ? Keyboard.GemsPriceOptions : Keyboard.CoinsPriceOptions);
         }
 
-        public PriceCallback() { }
-        public PriceCallback(UserEntity user, Update update) : base(user, update) { }
+        public SelectPrice() { }
+        public SelectPrice(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/SortCallback.cs → CardCollector/Commands/CallbackQuery/SelectSort.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class SortCallback : CallbackQuery
+    public class SelectSort : CallbackQueryCommand
     {
         protected override string CommandText => Command.sort;
         public override async Task Execute()
@@ -14,7 +14,7 @@ namespace CardCollector.Commands.CallbackQuery
             await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_sort, Keyboard.SortOptions);
         }
 
-        public SortCallback() { }
-        public SortCallback(UserEntity user, Update update) : base(user, update) { }
+        public SelectSort() { }
+        public SelectSort(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/CallbackQuery/TierCallback.cs → CardCollector/Commands/CallbackQuery/SelectTier.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.CallbackQuery
 {
-    public class TierCallback : CallbackQuery
+    public class SelectTier : CallbackQueryCommand
     {
         protected override string CommandText => Command.tier;
         public override async Task Execute()
@@ -14,7 +14,7 @@ namespace CardCollector.Commands.CallbackQuery
             await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_tier, Keyboard.TierOptions);
         }
         
-        public TierCallback() { }
-        public TierCallback(UserEntity user, Update update) : base(user, update) { }
+        public SelectTier() { }
+        public SelectTier(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 4 - 4
CardCollector/Commands/CallbackQuery/SetFilterCallback.cs → CardCollector/Commands/CallbackQuery/SetFilter.cs

@@ -9,12 +9,12 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.CallbackQuery
 {
     /* Реализует установку фильтров */
-    public class SetFilterCallback : CallbackQuery
+    public class SetFilter : CallbackQueryCommand
     {
         protected override string CommandText => Command.set;
         public override async Task Execute()
         {
-            EnterEmojiMessage.RemoveFromQueue(User.Id);
+            EnterEmoji.RemoveFromQueue(User.Id);
             var result = CallbackData.Split('=');
             var filters = User.Session.GetModule<FiltersModule>().Filters;
             /* Команду set мы получаем в виде set=<key>=<value>, соответственно аргументы 1 и 2 это ключ и значение словаря */
@@ -28,7 +28,7 @@ namespace CardCollector.Commands.CallbackQuery
             await new BackToFiltersMenu(User, Update).Execute();
         }
         
-        public SetFilterCallback() { }
-        public SetFilterCallback(UserEntity user, Update update) : base (user, update) { }
+        public SetFilter() { }
+        public SetFilter(UserEntity user, Update update) : base (user, update) { }
     }
 }

+ 43 - 0
CardCollector/Commands/CallbackQuery/ShowInfo.cs

@@ -0,0 +1,43 @@
+using System.Globalization;
+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 ShowInfo : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.show_offer_info;
+        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]
+                : Text.unexpired;
+            message += $"\n{Text.time_limit} {dateText}";
+            if (offerInfo.Description != "") message += $"\n{Text.description}: {offerInfo.Description}";
+            await MessageController.AnswerCallbackQuery(User, CallbackQueryId, message, true);
+        }
+        private async Task<string> PrizeToString(string prize)
+        {
+            var data = prize.Split('=');
+            return data[0] switch
+            {
+                "tier" => $"{Text.sticker} {data[1]} {Text.tier2}",
+                "sticker" => $"{Text.sticker} {(await StickerDao.GetStickerByHash(data[1])).Title}",
+                _ => ""
+            };
+        }
+
+        public ShowInfo() { }
+        public ShowInfo(UserEntity user, Update update) : base(user, update) { }
+    }
+}

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

@@ -0,0 +1,28 @@
+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 SpecialOffers : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.special_offers;
+        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)
+                await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.offers_not_found, true);
+            else
+                await MessageController.EditMessage(User, CallbackMessageId, Messages.available_offers,
+                    Keyboard.SpecialOffersKeyboard(specialOffers));
+        }
+
+        public SpecialOffers() { }
+        public SpecialOffers(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 32 - 27
CardCollector/Commands/CallbackQuery/CallbackQuery.cs → CardCollector/Commands/CallbackQueryCommand.cs

@@ -2,11 +2,12 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using CardCollector.Commands.CallbackQuery;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.CallbackQuery
+namespace CardCollector.Commands
 {
     /* Родительский класс для входящих обновлений типа CallbackQuery (нажатие пользователем инлайн кнопки)
      при наследовании укажите ключевое слово, содержащееся в запросе
@@ -17,7 +18,7 @@ namespace CardCollector.Commands.CallbackQuery
      Для обработки команды определены следующие поля
      User - пользователь, вызвавший команду
      Update - обновление, полученное от сервера Телеграм */
-    public abstract class CallbackQuery : UpdateModel
+    public abstract class CallbackQueryCommand : UpdateModel
     {
         /* Данные, поступившие после нажатия на кнокпку */
         protected string CallbackData;
@@ -29,38 +30,42 @@ namespace CardCollector.Commands.CallbackQuery
         protected string CallbackQueryId;
         
         /* Список команд, распознаваемых ботом */
-        private static readonly List<CallbackQuery> List = new()
+        private static readonly List<CallbackQueryCommand> List = new()
         {
-            new AuthorCallback(),
-            new AuthorMenuQuery(),
+            new SelectAuthor(),
+            new AuthorMenu(),
             new BackToCombine(),
             new BackToFiltersMenu(),
             new BuyByCoins(),
             new BuyByGems(),
-            new BuyGemsQuery(),
-            new BuyPackQuery(),
-            new BuyStickerQuery(),
-            new CancelCallback(),
+            new BuyCoins(),
+            new BuyGems(),
+            new BuyPack(),
+            new BuySticker(),
+            new Cancel(),
             new ClearChat(),
-            new CollectIncomeQuery(),
-            new CombineCallback(),
+            new CollectIncome(),
+            new Combine(),
             new CombineStickers(),
-            new ConfirmationSellingQuery(),
-            new ConfirmBuyingQuery(),
-            new CountQuery(),
-            new DailyTasksQuery(),
+            new ConfirmationSelling(),
+            new ConfirmBuying(),
+            new ConfirmExchange(),
+            new Count(),
+            new CallbackQuery.DailyTasks(),
             new DeleteCombine(),
-            new EmojiCallback(),
-            new MyPacksQuery(),
-            new OpenPackCallback(),
-            new OpenSpecificCallback(),
+            new SelectEmoji(),
+            new MyPacks(),
+            new OpenPack(),
+            new OpenSpecific(),
             new PackInfo(),
-            new PriceCallback(),
-            new PutForAuctionQuery(),
-            new SelectOfferCallback(),
-            new SetFilterCallback(),
-            new SortCallback(),
-            new TierCallback(),
+            new SelectPrice(),
+            new PutForAuction(),
+            new SelectOffer(),
+            new SetFilter(),
+            new SelectSort(),
+            new ShowInfo(),
+            new SpecialOffers(),
+            new SelectTier(),
         };
 
         /* Метод, создающий объекты команд исходя из полученного обновления */
@@ -84,8 +89,8 @@ namespace CardCollector.Commands.CallbackQuery
             return query == CommandText;
         }
 
-        protected CallbackQuery() { }
-        protected CallbackQuery(UserEntity user, Update update) : base(user, update)
+        protected CallbackQueryCommand() { }
+        protected CallbackQueryCommand(UserEntity user, Update update) : base(user, update)
         {
             CallbackData = update.CallbackQuery!.Data;
             CallbackMessageId = update.CallbackQuery!.Message!.MessageId;

+ 2 - 2
CardCollector/Commands/ChosenInlineResult/GetUnlimitedStickerAndExecuteCommand.cs

@@ -9,7 +9,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.ChosenInlineResult
 {
-    public class GetUnlimitedStickerAndExecuteCommand : ChosenInlineResult
+    public class GetUnlimitedStickerAndExecuteCommand : ChosenInlineResultCommand
     {
         protected override string CommandText => Command.unlimited_stickers;
         
@@ -29,7 +29,7 @@ namespace CardCollector.Commands.ChosenInlineResult
         public GetUnlimitedStickerAndExecuteCommand(UserEntity user, Update update) : base(user, update) { }
         
         /* Список команд, аналогичный родительскому, только не включает эту команду (unlimited) */
-        private static readonly List<ChosenInlineResult> PrivateList = List.GetRange(1, List.Count - 1);
+        private static readonly List<ChosenInlineResultCommand> PrivateList = List.GetRange(1, List.Count - 1);
         
         /* Метод, создающий объекты команд исходя из полученного обновления */
         private static UpdateModel PrivateFactory(Update update, UserEntity user)

+ 3 - 3
CardCollector/Commands/ChosenInlineResult/SelectStickerInlineResult.cs → CardCollector/Commands/ChosenInlineResult/SelectStickerInline.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.ChosenInlineResult
 {
-    public class SelectStickerInlineResult : ChosenInlineResult
+    public class SelectStickerInline : ChosenInlineResultCommand
     {
         protected override string CommandText => Command.select_sticker;
         public override async Task Execute()
@@ -52,7 +52,7 @@ namespace CardCollector.Commands.ChosenInlineResult
                    user.Session.State is UserState.CollectionMenu or UserState.AuctionMenu or UserState.CombineMenu or UserState.Default;
         }
 
-        public SelectStickerInlineResult() { }
-        public SelectStickerInlineResult(UserEntity user, Update update) : base(user, update) { }
+        public SelectStickerInline() { }
+        public SelectStickerInline(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/ChosenInlineResult/SelectTraderResult.cs → CardCollector/Commands/ChosenInlineResult/SelectTrader.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.ChosenInlineResult
 {
-    public class SelectTraderResult : ChosenInlineResult
+    public class SelectTrader : ChosenInlineResultCommand
     {
         protected override string CommandText => Command.buy_sticker;
         public override async Task Execute()
@@ -27,7 +27,7 @@ namespace CardCollector.Commands.ChosenInlineResult
             User.Session.Messages.Add(message.MessageId);
         }
 
-        public SelectTraderResult() { }
-        public SelectTraderResult(UserEntity user, Update update) : base(user, update) { }
+        public SelectTrader() { }
+        public SelectTrader(UserEntity user, Update update) : base(user, update) { }
     }
 }

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

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.ChosenInlineResult
 {
-    public class SendPrivateSticker : ChosenInlineResult
+    public class SendPrivateSticker : ChosenInlineResultCommand
     {
         protected override string CommandText => Command.send_private_sticker;
         

+ 3 - 3
CardCollector/Commands/ChosenInlineResult/SendStickerResult.cs → CardCollector/Commands/ChosenInlineResult/SendSticker.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.ChosenInlineResult
 {
     /* Данная команда выполняется при отправке пользователем стикера */
-    public class SendStickerResult : ChosenInlineResult
+    public class SendSticker : ChosenInlineResultCommand
     {
         /* Ключевое слово для данной команды send_sticker */
         protected override string CommandText => Command.send_sticker;
@@ -15,7 +15,7 @@ namespace CardCollector.Commands.ChosenInlineResult
             return Task.CompletedTask;
         }
         
-        public SendStickerResult() { }
-        public SendStickerResult(UserEntity user, Update update) : base(user, update) { }
+        public SendSticker() { }
+        public SendSticker(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 10 - 9
CardCollector/Commands/ChosenInlineResult/ChosenInlineResult.cs → CardCollector/Commands/ChosenInlineResultCommand.cs

@@ -2,11 +2,12 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using CardCollector.Commands.ChosenInlineResult;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.ChosenInlineResult
+namespace CardCollector.Commands
 {
     /* Родительский класс для входящих обновлений типа ChosenInlineResult
      (выбор пользователем инлайн команды)
@@ -19,7 +20,7 @@ namespace CardCollector.Commands.ChosenInlineResult
      User - пользователь, вызвавший команду
      Update - обновление, полученное от сервера Телеграм
      InlineResult - результат входящего запроса */
-    public abstract class ChosenInlineResult : UpdateModel
+    public abstract class ChosenInlineResultCommand : UpdateModel
     {
         /* Результат запроса (id выбранного пользователем элемента) */
         protected readonly string InlineResult;
@@ -28,19 +29,19 @@ namespace CardCollector.Commands.ChosenInlineResult
         protected readonly string InlineQuery;
 
         /* Список команд */
-        protected static readonly List<ChosenInlineResult> List = new()
+        protected static readonly List<ChosenInlineResultCommand> List = new()
         {
             /* Этот объект должен быть всегда в начале списка, так как он должен быть вызван
              вперед других, если в коде включен режим бесконечных стикеров */
             new GetUnlimitedStickerAndExecuteCommand(),
 
             // Обработка результата при отправке стикера
-            new SendStickerResult(),
+            new SendSticker(),
             new SendPrivateSticker(),
             // Обработка результата при выборе продавца
-            new SelectTraderResult(),
-
-            new SelectStickerInlineResult(),
+            new SelectTrader(),
+            
+            new SelectStickerInline(),
         };
 
         /* Метод, создающий объекты команд исходя из полученного обновления */
@@ -64,8 +65,8 @@ namespace CardCollector.Commands.ChosenInlineResult
             return CommandText == query;
         }
 
-        protected ChosenInlineResult() { }
-        protected ChosenInlineResult(UserEntity user, Update update) : base(user, update)
+        protected ChosenInlineResultCommand() { }
+        protected ChosenInlineResultCommand(UserEntity user, Update update) : base(user, update)
         {
             InlineResult = update.ChosenInlineResult!.ResultId;
             InlineQuery = update.ChosenInlineResult.Query;

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

@@ -9,7 +9,7 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.InlineQuery
 {
-    public class ShowAuctionStickers : InlineQuery
+    public class ShowAuctionStickers : InlineQueryCommand
     {
         protected override string CommandText => "";
         

+ 1 - 1
CardCollector/Commands/InlineQuery/ShowCollectionStickers.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.InlineQuery
 {
-    public class ShowCollectionStickers : InlineQuery
+    public class ShowCollectionStickers : InlineQueryCommand
     {
         protected override string CommandText => "";
         public override async Task Execute()

+ 1 - 1
CardCollector/Commands/InlineQuery/ShowCombineStickers.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.InlineQuery
 {
-    public class ShowCombineStickers : InlineQuery
+    public class ShowCombineStickers : InlineQueryCommand
     {
         protected override string CommandText => "";
         public override async Task Execute()

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

@@ -8,7 +8,7 @@ using Telegram.Bot.Types.Enums;
 namespace CardCollector.Commands.InlineQuery
 {
     /* Отображение стикеров в личной беседt с ботом */
-    public class ShowStickersInBotChat : InlineQuery
+    public class ShowStickersInBotChat : InlineQueryCommand
     {
         /* Команда - пустая строка, поскольку пользователь может вводить любые слова
          после @имя_бота, введенная фраза будет использоваться для фильтрации стикеров */

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

@@ -8,7 +8,7 @@ using Telegram.Bot.Types.Enums;
 namespace CardCollector.Commands.InlineQuery
 {
     /* Отображение стикеров в чатах, кроме личной беседы с ботом */
-    public class ShowStickersInGroup : InlineQuery
+    public class ShowStickersInGroup : InlineQueryCommand
     {
         /* Команда - пустая строка, поскольку пользователь может вводить любые слова
          после @имя_бота, введенная фраза будет использоваться для фильтрации стикеров */

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

@@ -7,7 +7,7 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.InlineQuery
 {
-    public class ShowStickersInPrivate : InlineQuery
+    public class ShowStickersInPrivate : InlineQueryCommand
     {
         /* Команда - пустая строка, поскольку пользователь может вводить любые слова
          после @имя_бота, введенная фраза будет использоваться для фильтрации стикеров */

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

@@ -8,7 +8,7 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.InlineQuery
 {
-    public class ShowTradersInBotChat : InlineQuery
+    public class ShowTradersInBotChat : InlineQueryCommand
     {
         protected override string CommandText => "";
         

+ 6 - 5
CardCollector/Commands/InlineQuery/InlineQuery.cs → CardCollector/Commands/InlineQueryCommand.cs

@@ -2,11 +2,12 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using CardCollector.Commands.InlineQuery;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.InlineQuery
+namespace CardCollector.Commands
 {
     /* Родительский класс для входящих обновлений типа InlineQuery (ввод пользователем @имя_бота)
      при наследовании укажите ключевое слово, содержащееся в запросе
@@ -18,7 +19,7 @@ namespace CardCollector.Commands.InlineQuery
      User - пользователь, вызвавший команду
      Update - обновление, полученное от сервера Телеграм
      InlineQueryId - id запроса для ответа на него */
-    public abstract class InlineQuery : UpdateModel
+    public abstract class InlineQueryCommand : UpdateModel
     {
         /* Id входящего запроса */
         protected readonly string InlineQueryId;
@@ -26,7 +27,7 @@ namespace CardCollector.Commands.InlineQuery
         protected readonly string Query;
         
         /* Список команд */
-        private static readonly List<InlineQuery> List = new()
+        private static readonly List<InlineQueryCommand> List = new()
         {
             // Показать стикеры в чатах для отправки (кроме личного чата с ботом)
             new ShowStickersInGroup(),
@@ -59,12 +60,12 @@ namespace CardCollector.Commands.InlineQuery
                 : new CommandNotFound(user, update, $"{update.InlineQuery!.ChatType}={update.InlineQuery!.Query}");
         }
 
-        protected InlineQuery(UserEntity user, Update update) : base(user, update)
+        protected InlineQueryCommand(UserEntity user, Update update) : base(user, update)
         {
             InlineQueryId = update.InlineQuery!.Id;
             Query = update.InlineQuery!.Query;
         }
         
-        protected InlineQuery() { }
+        protected InlineQueryCommand() { }
     }
 }

+ 3 - 3
CardCollector/Commands/Message/DocumentMessage/UploadFileMessage.cs → CardCollector/Commands/Message/DocumentMessage/UploadFileMessageCommand.cs

@@ -17,7 +17,7 @@ using File = System.IO.File;
 
 namespace CardCollector.Commands.Message.DocumentMessage
 {
-    public class UploadFileMessage : Message
+    public class UploadFileMessageCommand : MessageCommand
     {
         protected override string CommandText => "";
         public override async Task Execute()
@@ -180,7 +180,7 @@ namespace CardCollector.Commands.Message.DocumentMessage
             return user.Session.State == UserState.UploadFile;
         }
 
-        public UploadFileMessage() { }
-        public UploadFileMessage(UserEntity user, Update update) : base(user, update) { }
+        public UploadFileMessageCommand() { }
+        public UploadFileMessageCommand(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/Message/TextMessage/AuctionMessage.cs → CardCollector/Commands/Message/TextMessage/Auction.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.Message.TextMessage
 {
     /* Реалищует команду "Аукцион" */
-    public class AuctionMessage : Message
+    public class Auction : MessageCommand
     {
         protected override string CommandText => Text.auction;
         public override async Task Execute()
@@ -21,7 +21,7 @@ namespace CardCollector.Commands.Message.TextMessage
             await new ShowFiltersMenu(User, Update).Execute();
         }
         
-        public AuctionMessage() { }
-        public AuctionMessage(UserEntity user, Update update) : base(user, update) { }
+        public Auction() { }
+        public Auction(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/Message/TextMessage/CollectionMessage.cs → CardCollector/Commands/Message/TextMessage/Collection.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.Message.TextMessage
 {
     /* Реализует команду "Коллекция" */
-    public class CollectionMessage : Message
+    public class Collection : MessageCommand
     {
         protected override string CommandText => Text.collection;
         public override async Task Execute()
@@ -21,7 +21,7 @@ namespace CardCollector.Commands.Message.TextMessage
             await new ShowFiltersMenu(User, Update).Execute();
         }
         
-        public CollectionMessage() { }
-        public CollectionMessage(UserEntity user, Update update) : base(user, update) { }
+        public Collection() { }
+        public Collection(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/Message/TextMessage/DownloadStickerPackMessage.cs → CardCollector/Commands/Message/TextMessage/DownloadStickerPack.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.Message.TextMessage
 {
-    public class DownloadStickerPackMessage : Message
+    public class DownloadStickerPack : MessageCommand
     {
         protected override string CommandText => Text.download_stickerpack;
         public override async Task Execute()
@@ -22,7 +22,7 @@ namespace CardCollector.Commands.Message.TextMessage
             return base.IsMatches(user, update) && user.PrivilegeLevel >= Constants.ARTIST_PRIVILEGE_LEVEL;
         }
 
-        public DownloadStickerPackMessage() { }
-        public DownloadStickerPackMessage(UserEntity user, Update update) : base(user, update) { }
+        public DownloadStickerPack() { }
+        public DownloadStickerPack(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/Message/TextMessage/EnterEmojiMessage.cs → CardCollector/Commands/Message/TextMessage/EnterEmoji.cs

@@ -10,7 +10,7 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.Message.TextMessage
 {
-    public class EnterEmojiMessage : Message
+    public class EnterEmoji : MessageCommand
     {
         protected override string CommandText => "";
 
@@ -59,7 +59,7 @@ namespace CardCollector.Commands.Message.TextMessage
             return Queue.ContainsKey(user.Id) && update.Message!.Type == MessageType.Text;
         }
 
-        public EnterEmojiMessage() { }
-        public EnterEmojiMessage(UserEntity user, Update update) : base(user, update) { }
+        public EnterEmoji() { }
+        public EnterEmoji(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 62 - 0
CardCollector/Commands/Message/TextMessage/EnterGemsExchange.cs

@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using CardCollector.Session.Modules;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+
+namespace CardCollector.Commands.Message.TextMessage
+{
+    public class EnterGemsExchange : MessageCommand
+    {
+        protected override string CommandText => "";
+        
+        private static readonly Dictionary<long, int> Queue = new ();
+        public override async Task Execute()
+        {
+            var module = User.Session.GetModule<ShopModule>();
+            if (!int.TryParse(Update.Message!.Text, out var sum) || sum < 0)
+                await MessageController.EditMessage(User, Queue[User.Id], 
+                    $"{Messages.exchange_mesage}" +
+                    $"\n{Messages.gems_exchange_count} {module.EnteredExchangeSum}{Text.gem}" +
+                    $"\n{Messages.coins_exchange_count} {module.EnteredExchangeSum * 150}{Text.coin}" +
+                    $"\n{Messages.please_enter_integer}",
+                    Keyboard.BuyCoinsKeyboard);
+            else
+            {
+                module.EnteredExchangeSum = sum;
+                await MessageController.EditMessage(User, Queue[User.Id], 
+                    $"{Messages.exchange_mesage}" +
+                    $"\n{Messages.gems_exchange_count} {module.EnteredExchangeSum}{Text.gem}" +
+                    $"\n{Messages.coins_exchange_count} {module.EnteredExchangeSum * 150}{Text.coin}" +
+                    $"\n{Messages.confirm_exchange}",
+                    Keyboard.BuyCoinsKeyboard);
+                Queue.Remove(User.Id);
+            }
+        }
+
+        //Добавляем пользователя в очередь #1#
+        public static void AddToQueue(long userId, int messageId)
+        {
+            Queue.TryAdd(userId, messageId);
+        }
+
+        //Удаляем пользователя из очереди #1#
+        public static void RemoveFromQueue(long userId)
+        {
+            Queue.Remove(userId);
+        }
+
+        // Переопределяем метод, так как команда удовлетворяет условию, если пользователь находится в очереди #1#
+        protected internal override bool IsMatches(UserEntity user, Update update)
+        {
+            return Queue.ContainsKey(user.Id) && update.Message!.Type == MessageType.Text;
+        }
+
+        public EnterGemsExchange() { }
+
+        public EnterGemsExchange(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 4 - 4
CardCollector/Commands/Message/TextMessage/EnterGemsPriceMessage.cs → CardCollector/Commands/Message/TextMessage/EnterGemsPrice.cs

@@ -9,7 +9,7 @@ using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.Message.TextMessage
 {
-    public class EnterGemsPriceMessage : Message
+    public class EnterGemsPrice : MessageCommand
     {
         protected override string CommandText => "";
         
@@ -20,7 +20,7 @@ namespace CardCollector.Commands.Message.TextMessage
             var module = User.Session.GetModule<CollectionModule>();
             if (!int.TryParse(Update.Message!.Text, out var price) || price < 0)
                 await MessageController.EditMessage(User, Queue[User.Id], 
-                    $"{Messages.current_price} {module.SellPrice}{Text.gem}\n{Messages.please_enter_price}",
+                    $"{Messages.current_price} {module.SellPrice}{Text.gem}\n{Messages.please_enter_integer}",
                     Keyboard.AuctionPutCancelKeyboard);
             else
             {
@@ -49,8 +49,8 @@ namespace CardCollector.Commands.Message.TextMessage
             return Queue.ContainsKey(user.Id) && update.Message!.Type == MessageType.Text;
         }
 
-        public EnterGemsPriceMessage() { }
+        public EnterGemsPrice() { }
 
-        public EnterGemsPriceMessage(UserEntity user, Update update) : base(user, update) { }
+        public EnterGemsPrice(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/Message/TextMessage/MenuMessage.cs → CardCollector/Commands/Message/TextMessage/Menu.cs

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.Message.TextMessage
 {
-    public class MenuMessage : Message
+    public class Menu : MessageCommand
     {
         protected override string CommandText => Text.menu;
         public override async Task Execute()
@@ -16,7 +16,7 @@ namespace CardCollector.Commands.Message.TextMessage
             User.Session.Messages.Add(message.MessageId);
         }
 
-        public MenuMessage() { }
-        public MenuMessage(UserEntity user, Update update) : base(user, update) { }
+        public Menu() { }
+        public Menu(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/Message/TextMessage/ProfileMessage.cs → CardCollector/Commands/Message/TextMessage/Profile.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.Message.TextMessage
 {
     /* Команда "Профиль" Отображает профиль пользователя и его баланс */
-    public class ProfileMessage : Message
+    public class Profile : MessageCommand
     {
         /* Для данной команды ключевое слово "Профиль" */
         protected override string CommandText => Text.profile;
@@ -29,7 +29,7 @@ namespace CardCollector.Commands.Message.TextMessage
             User.Session.Messages.Add(message.MessageId);
         }
         
-        public ProfileMessage() { }
-        public ProfileMessage(UserEntity user, Update update) : base(user, update) { }
+        public Profile() { }
+        public Profile(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 6 - 6
CardCollector/Commands/Message/TextMessage/ShopMessage.cs → CardCollector/Commands/Message/TextMessage/Shop.cs

@@ -9,7 +9,7 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.Message.TextMessage
 {
     /* Реализует команду "Магазин" */
-    public class ShopMessage : Message
+    public class Shop : MessageCommand
     {
         protected override string CommandText => Text.shop;
         public override async Task Execute()
@@ -19,13 +19,13 @@ namespace CardCollector.Commands.Message.TextMessage
             /* Переводим состояние пользователя в меню магазина */
             User.Session.State = UserState.ShopMenu;
             User.Session.InitNewModule<ShopModule>();
-            var specialOffers = await ShopDao.GetSpecialPositions();
-            var message = await MessageController.SendMessage(User, Messages.shop_message, 
-                Keyboard.ShopKeyboard(specialOffers));
+            var haveSpecialOffers = await (await ShopDao.GetSpecialPositions())
+                .AnyAsync(async offer => offer.IsInfinite || !await SpecialOfferUsersDao.NowUsed(User.Id, offer.Id));
+            var message = await MessageController.SendMessage(User, Messages.shop_message, Keyboard.ShopKeyboard(haveSpecialOffers));
             User.Session.Messages.Add(message.MessageId);
         }
         
-        public ShopMessage() { }
-        public ShopMessage(UserEntity user, Update update) : base(user, update) { }
+        public Shop() { }
+        public Shop(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 1 - 1
CardCollector/Commands/Message/TextMessage/ShowFiltersMenu.cs

@@ -8,7 +8,7 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.Message.TextMessage
 {
     /* Этот класс реализует отправку нового сообщения с фильтрами пользователя */
-    public class ShowFiltersMenu : Message
+    public class ShowFiltersMenu : MessageCommand
     {
         protected override string CommandText => "Message";
         public override async Task Execute()

+ 13 - 9
CardCollector/Commands/Message/TextMessage/ShowSampleMessage.cs → CardCollector/Commands/Message/TextMessage/ShowSample.cs

@@ -3,23 +3,27 @@ using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot;
 using Telegram.Bot.Types;
-using Telegram.Bot.Types.Payments;
+using Telegram.Bot.Types.ReplyMarkups;
 
 namespace CardCollector.Commands.Message.TextMessage
 {
     /* Этот класс можно использовать для тестирования или наброски эскизов
      Команда "Показать пример" доступна только пользователям с уровнем доступа "Разработчик" и выше
-     PrivilegeLevel = 5 */
-    public class ShowSampleMessage : Message
+     PrivilegeLevel = 7 */
+    public class ShowSample : MessageCommand
     {
         protected override string CommandText => Text.show_sample;
         public override async Task Execute()
         {
-            await Bot.Client.SendInvoiceAsync(User.ChatId, "test", "test", "test", AppSettings.PAYMENT_PROVIDER,
-                "USD", new []
+            var loginUrl = new LoginUrl
+            {
+                Url = "http://127.0.0.1:8081/login"
+            };
+            await Bot.Client.SendTextMessageAsync(User.ChatId, "Test", replyMarkup: new InlineKeyboardMarkup(
+                new []
                 {
-                    new LabeledPrice("text", 100)
-                });
+                    InlineKeyboardButton.WithLoginUrl("test", loginUrl), 
+                }));
         }
 
         /* Нужно помимо совпадения текста проверить пользователя на уровень привилегий */
@@ -28,7 +32,7 @@ namespace CardCollector.Commands.Message.TextMessage
             return base.IsMatches(user, update) && user.PrivilegeLevel >= Constants.PROGRAMMER_PRIVILEGE_LEVEL;
         }
         
-        public ShowSampleMessage(UserEntity user, Update update) : base(user, update) { }
-        public ShowSampleMessage() { }
+        public ShowSample(UserEntity user, Update update) : base(user, update) { }
+        public ShowSample() { }
     }
 }

+ 3 - 3
CardCollector/Commands/Message/TextMessage/StartMessage.cs → CardCollector/Commands/Message/TextMessage/Start.cs

@@ -7,7 +7,7 @@ using Telegram.Bot.Types;
 namespace CardCollector.Commands.Message.TextMessage
 {
     /* Обработка команды "/start" */
-    public class StartMessage : Message
+    public class Start : MessageCommand
     {
         /* */
         protected override string CommandText => Text.start;
@@ -18,7 +18,7 @@ namespace CardCollector.Commands.Message.TextMessage
             await MessageController.SendMessage(User, Messages.start_message, Keyboard.Menu);
         }
         
-        public StartMessage() { }
-        public StartMessage(UserEntity user, Update update) : base(user, update) { }
+        public Start() { }
+        public Start(UserEntity user, Update update) : base(user, update) { }
     }
 }

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

@@ -6,7 +6,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.Message.TextMessage
 {
-    public class StopBot : Message
+    public class StopBot : MessageCommand
     {
         protected override string CommandText => Text.stop_bot;
         

+ 22 - 21
CardCollector/Commands/Message/Message.cs → CardCollector/Commands/MessageCommand.cs

@@ -1,10 +1,10 @@
-using System;
-using System.Threading.Tasks;
-using CardCollector.Controllers;
+using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Threading.Tasks;
 using CardCollector.Commands.Message.DocumentMessage;
 using CardCollector.Commands.Message.TextMessage;
+using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
@@ -12,7 +12,7 @@ using Telegram.Bot;
 using Telegram.Bot.Types;
 using Telegram.Bot.Types.Enums;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands
 {
     /* Родительский класс для входящих обновлений типа Message (обычное сообщение)
      при наследовании укажите ключевое слово, содержащееся в тексте
@@ -23,37 +23,38 @@ namespace CardCollector.Commands.Message
      Для обработки команды определены следующие поля
      User - пользователь, вызвавший команду
      Update - обновление, полученное от сервера Телеграм */
-    public abstract class Message : UpdateModel
+    public abstract class MessageCommand : UpdateModel
     {
         /* Список команд */
-        private static readonly List<Message>
+        private static readonly List<MessageCommand>
             TextCommandsList = new() {
                 // Команда "Профиль"
-                new ProfileMessage(),
+                new Profile(),
                 // Команда "/start"
-                new StartMessage(),
+                new Start(),
                 // Команда "/menu"
-                new MenuMessage(),
+                new Menu(),
                 // Команда "Коллекция"
-                new CollectionMessage(),
+                new Collection(),
                 // Команда "Магазин"
-                new ShopMessage(),
+                new Shop(),
                 // Команда "Аукцион"
-                new AuctionMessage(),
+                new Auction(),
                 // Ожидание ввода эмоджи
-                new EnterEmojiMessage(),
+                new EnterEmoji(),
+                new EnterGemsExchange(),
                 // Загрузка стикерпака
-                new DownloadStickerPackMessage(),
+                new DownloadStickerPack(),
                 //команда ввода цены
-                new EnterGemsPriceMessage(),
+                new EnterGemsPrice(),
                 // Команда "Показать пример"
-                new ShowSampleMessage(),
+                new ShowSample(),
                 // Команда "Остановить"
                 new StopBot()
             },
             FileCommandsList = new() {
                 /* Выгрузка файлов к боту */
-                new UploadFileMessage(),
+                new UploadFileMessageCommand(),
             };
 
         /* Метод, создающий объекты команд исходя из полученного обновления */
@@ -76,7 +77,7 @@ namespace CardCollector.Commands.Message
             {
                 MessageType.Text => TextCommandsList,
                 MessageType.Document => FileCommandsList,
-                _ => new List<Message>()
+                _ => new List<MessageCommand>()
             };
             
             // Объект пользователя
@@ -98,7 +99,7 @@ namespace CardCollector.Commands.Message
                 : new CommandNotFound(user, update, update.Message.Type switch {
                     MessageType.Text => update.Message.Text,
                     MessageType.Document => update.Message.Document!.FileId,
-                    _ => "Unknown"
+                    _ => Utilities.ToJson(update.Message)
                 });
         }
 
@@ -107,7 +108,7 @@ namespace CardCollector.Commands.Message
             return update.Message!.Text == CommandText;
         }
 
-        protected Message(UserEntity user, Update update) : base(user, update) { }
-        protected Message() { }
+        protected MessageCommand(UserEntity user, Update update) : base(user, update) { }
+        protected MessageCommand() { }
     }
 }

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

@@ -9,7 +9,7 @@ namespace CardCollector.Commands.MyChatMember
     /* Родительский класс для входящих обновлений типа MyChatMember
      (Добавление/Добавление в чаты/Добавление в каналы/Блокировки/Исключения бота)
      Данный класс полностью реализован и не нуждается в наследовании */
-    public class MyChatMember : UpdateModel
+    public class MyChatMemberCommand : UpdateModel
     {
         protected override string CommandText => "";
         private readonly ChatMemberStatus _status;
@@ -38,7 +38,7 @@ namespace CardCollector.Commands.MyChatMember
         {
             // Объект пользователя
             var user = await UserDao.GetUser(update.MyChatMember!.From);
-            return new MyChatMember(user, update);
+            return new MyChatMemberCommand(user, update);
         }
 
         /*private static User ChatToUser(Chat chat)
@@ -53,7 +53,7 @@ namespace CardCollector.Commands.MyChatMember
             };
         }*/
 
-        private MyChatMember(UserEntity user, Update update) : base(user, update)
+        private MyChatMemberCommand(UserEntity user, Update update) : base(user, update)
         {
             _status = update.MyChatMember!.NewChatMember.Status;
         }

+ 4 - 4
CardCollector/Commands/PreCheckoutQuery/Gems50.cs → CardCollector/Commands/PreCheckoutQuery/BuyGems.cs

@@ -7,9 +7,9 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.PreCheckoutQuery
 {
-    public class Gems50 : PreCheckoutQuery
+    public class BuyGems : PreCheckoutQueryCommand
     {
-        protected override string CommandText => Command.gems50;
+        protected override string CommandText => Command.buy_gems_item;
         public override async Task Execute()
         {
             await Bot.Client.AnswerPreCheckoutQueryAsync(PreCheckoutQueryId);
@@ -19,7 +19,7 @@ namespace CardCollector.Commands.PreCheckoutQuery
             User.Session.Messages.Add(message.MessageId);
         }
 
-        public Gems50() { }
-        public Gems50(UserEntity user, Update update) : base(user, update) { }
+        public BuyGems() { }
+        public BuyGems(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 3 - 3
CardCollector/Commands/PreCheckoutQuery/TestPreCheckoutQuery.cs → CardCollector/Commands/PreCheckoutQuery/Test.cs

@@ -5,7 +5,7 @@ using Telegram.Bot.Types;
 
 namespace CardCollector.Commands.PreCheckoutQuery
 {
-    public class TestPreCheckoutQuery : PreCheckoutQuery
+    public class Test : PreCheckoutQueryCommand
     {
         protected override string CommandText => "test";
 
@@ -14,7 +14,7 @@ namespace CardCollector.Commands.PreCheckoutQuery
             await Bot.Client.AnswerPreCheckoutQueryAsync(PreCheckoutQueryId);
         }
 
-        public TestPreCheckoutQuery() { }
-        public TestPreCheckoutQuery(UserEntity user, Update update) : base(user, update) { }
+        public Test() { }
+        public Test(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 8 - 7
CardCollector/Commands/PreCheckoutQuery/PreCheckoutQuery.cs → CardCollector/Commands/PreCheckoutQueryCommand.cs

@@ -2,23 +2,24 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using CardCollector.Commands.PreCheckoutQuery;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.PreCheckoutQuery
+namespace CardCollector.Commands
 {
-    public abstract class PreCheckoutQuery : UpdateModel
+    public abstract class PreCheckoutQueryCommand : UpdateModel
     {
         protected readonly string PreCheckoutQueryId;
         protected readonly int Amount;
         
-        private static readonly List<PreCheckoutQuery> List = new()
+        private static readonly List<PreCheckoutQueryCommand> List = new()
             {
                 // Тестовая покупка
-                new TestPreCheckoutQuery(),
+                new Test(),
                 // Покупка 50 алмазов
-                new Gems50(),
+                new BuyGems(),
             };
 
         /* Метод, создающий объекты команд исходя из полученного обновления */
@@ -41,8 +42,8 @@ namespace CardCollector.Commands.PreCheckoutQuery
             return CommandText == update.PreCheckoutQuery!.InvoicePayload;
         }
 
-        protected PreCheckoutQuery() { }
-        protected PreCheckoutQuery(UserEntity user, Update update) : base(user, update)
+        protected PreCheckoutQueryCommand() { }
+        protected PreCheckoutQueryCommand(UserEntity user, Update update) : base(user, update)
         {
             PreCheckoutQueryId = update.PreCheckoutQuery!.Id;
             Amount = update.PreCheckoutQuery.TotalAmount;

+ 24 - 30
CardCollector/Controllers/MessageController.cs

@@ -2,6 +2,8 @@ using System;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using CardCollector.Commands;
+using CardCollector.Commands.MyChatMember;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot;
@@ -9,16 +11,8 @@ using Telegram.Bot.Exceptions;
 using Telegram.Bot.Types;
 using Telegram.Bot.Types.Enums;
 using Telegram.Bot.Types.InlineQueryResults;
-using Telegram.Bot.Types.InputFiles;
 using Telegram.Bot.Types.Payments;
 using Telegram.Bot.Types.ReplyMarkups;
-using Message = CardCollector.Commands.Message.Message;
-using CallBackQuery = CardCollector.Commands.CallbackQuery.CallbackQuery;
-using MyChatMember = CardCollector.Commands.MyChatMember.MyChatMember;
-using InlineQuery = CardCollector.Commands.InlineQuery.InlineQuery;
-using ChosenInlineResult = CardCollector.Commands.ChosenInlineResult.ChosenInlineResult;
-using PreCheckoutQuery = CardCollector.Commands.PreCheckoutQuery.PreCheckoutQuery;
-using TgMessage = Telegram.Bot.Types.Message;
 
 namespace CardCollector.Controllers
 {
@@ -35,17 +29,17 @@ namespace CardCollector.Controllers
                 var executor = update.Type switch
                 {
                     // Тип обновления - сообщение
-                    UpdateType.Message => await Message.Factory(update),
+                    UpdateType.Message => await MessageCommand.Factory(update),
                     // Тип обновления - нажатие на инлайн кнопку
-                    UpdateType.CallbackQuery => await CallBackQuery.Factory(update),
+                    UpdateType.CallbackQuery => await CallbackQueryCommand.Factory(update),
                     // Тип обновления - блокировка/добавление бота
-                    UpdateType.MyChatMember => await MyChatMember.Factory(update),
+                    UpdateType.MyChatMember => await MyChatMemberCommand.Factory(update),
                     // Тип обновления - вызов бота через @имя_бота
-                    UpdateType.InlineQuery => await InlineQuery.Factory(update),
+                    UpdateType.InlineQuery => await InlineQueryCommand.Factory(update),
                     // Тип обновления - выбор результата в инлайн меню
-                    UpdateType.ChosenInlineResult => await ChosenInlineResult.Factory(update),
+                    UpdateType.ChosenInlineResult => await ChosenInlineResultCommand.Factory(update),
                     // Тип обновления - платеж
-                    UpdateType.PreCheckoutQuery => await PreCheckoutQuery.Factory(update),
+                    UpdateType.PreCheckoutQuery => await PreCheckoutQueryCommand.Factory(update),
                     _ => throw new ArgumentOutOfRangeException()
                 };
                 // Обработать команду
@@ -90,7 +84,7 @@ namespace CardCollector.Controllers
          user - пользователь, которому необходимо отправить сообщение
          message - текст сообщения
          keyboard - клавиатура, которую надо добавить к сообщению */
-        public static async Task<TgMessage> SendMessage(UserEntity user, string message, IReplyMarkup keyboard = null)
+        public static async Task<Message> SendMessage(UserEntity user, string message, IReplyMarkup keyboard = null)
         {
             try
             {
@@ -101,14 +95,14 @@ namespace CardCollector.Controllers
             {
                 LogOutWarning("Can't send text message " + e.Message);
             }
-            return new TgMessage();
+            return new Message();
         }
         
         /* Метод для отправки сообщения с разметкой html
          user - пользователь, которому необходимо отправить сообщение
          message - текст сообщения
          keyboard - клавиатура, которую надо добавить к сообщению */
-        public static async Task<TgMessage> SendTextWithHtml(UserEntity user, string message, IReplyMarkup keyboard = null)
+        public static async Task<Message> SendTextWithHtml(UserEntity user, string message, IReplyMarkup keyboard = null)
         {
             try
             {
@@ -119,24 +113,24 @@ namespace CardCollector.Controllers
             {
                 LogOutWarning("Can't send text message with html " + e.Message);
             }
-            return new TgMessage();
+            return new Message();
         }
         
         /* Метод для отправки стикера
          user - пользователь, которому необходимо отправить сообщение
          fileId - id стикера, расположенного на серверах телеграм */
-        public static async Task<TgMessage> SendSticker(UserEntity user, string fileId)
+        public static async Task<Message> SendSticker(UserEntity user, string fileId, IReplyMarkup keyboard = null)
         {
             try
             {
                 if (!user.IsBlocked)
-                    return await Bot.Client.SendStickerAsync(user.ChatId, fileId, true);
+                    return await Bot.Client.SendStickerAsync(user.ChatId, fileId, true, replyMarkup: keyboard);
             }
             catch (Exception e)
             {
                 LogOutWarning("Can't send sticker " + e.Message);
             }
-            return new TgMessage();
+            return new Message();
         }
         
         /* Метод для редактирования сообщения
@@ -144,7 +138,7 @@ namespace CardCollector.Controllers
          messageId - id сообщения
          message - текст сообщения
          keyboard - клавиатура, которую надо добавить к сообщению */
-        public static async Task<TgMessage> EditMessage(UserEntity user, int messageId, string message, InlineKeyboardMarkup keyboard = null)
+        public static async Task<Message> EditMessage(UserEntity user, int messageId, string message, InlineKeyboardMarkup keyboard = null)
         {
             try
             {
@@ -155,14 +149,14 @@ namespace CardCollector.Controllers
             {
                 return await SendMessage(user, message, keyboard);
             }
-            return new TgMessage();
+            return new Message();
         }
 
         /* Метод для редактирования клавиатуры под сообщением
          user - пользователь, которому необходимо отредактировать сообщение
          messageId - Id сообщения
          keyboard - новая клавиатура, которую надо добавить к сообщению */
-        public static async Task<TgMessage> EditReplyMarkup(UserEntity user, int messageId, InlineKeyboardMarkup keyboard)
+        public static async Task<Message> EditReplyMarkup(UserEntity user, int messageId, InlineKeyboardMarkup keyboard)
         {
             try
             {
@@ -173,7 +167,7 @@ namespace CardCollector.Controllers
             {
                 LogOutWarning("Can't edit reply markup " + e.Message);
             }
-            return new TgMessage();
+            return new Message();
         }
         
         public static async Task AnswerCallbackQuery(UserEntity user, string callbackQueryId, string text, bool showAlert = false)
@@ -211,18 +205,18 @@ namespace CardCollector.Controllers
          inputOnlineFile - фото, которое необходимо отправить
          message - текст сообщения
          keyboard - клавиатура, которую надо добавить к сообщению */
-        public static async Task<TgMessage> SendImage(UserEntity user, InputOnlineFile inputOnlineFile, string message = null, InlineKeyboardMarkup keyboard = null)
+        public static async Task<Message> SendImage(UserEntity user, string fileId, string message = null, InlineKeyboardMarkup keyboard = null)
         {
             try
             {
                 if (!user.IsBlocked)
-                    return await Bot.Client.SendPhotoAsync(user.ChatId, inputOnlineFile, message, replyMarkup: keyboard, disableNotification: true);
+                    return await Bot.Client.SendPhotoAsync(user.ChatId, fileId, message, replyMarkup: keyboard, disableNotification: true);
             }
             catch (Exception e)
             {
                 LogOutWarning("Can't send photo " + e.Message);
             }
-            return new TgMessage();
+            return new Message();
         }
 
         /* Метод для ответа на запрос @имя_бота
@@ -233,7 +227,7 @@ namespace CardCollector.Controllers
             await Bot.Client.AnswerInlineQueryAsync(queryId, results, isPersonal: true, nextOffset: offset, cacheTime: Constants.INLINE_RESULTS_CACHE_TIME);
         }
 
-        public static async Task<TgMessage> SendInvoice(UserEntity user, string title, string description, 
+        public static async Task<Message> SendInvoice(UserEntity user, string title, string description, 
             string payload, IEnumerable<LabeledPrice> prices, int maxTip = 0, IEnumerable<int> tips = null, Currency currency = Currency.USD)
         {
             try
@@ -246,7 +240,7 @@ namespace CardCollector.Controllers
             {
                 LogOutWarning("Can't send photo " + e.Message);
             }
-            return new TgMessage();
+            return new Message();
         }
     }
 }

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

@@ -18,6 +18,8 @@ namespace CardCollector.DataBase.Entity
         [Column("time_limited"), MaxLength(32)] public bool TimeLimited { get; set; } = false;
         [Column("time_limit"), MaxLength(32)] public DateTime TimeLimit { get; set; } = DateTime.Now;
         [Column("additional_prize"), MaxLength(256)] public string AdditionalPrize { get; set; } = "";
+        [Column("image_id"), MaxLength(127)] public string ImageId { get; set; } = "";
+        [Column("description"), MaxLength(256)] public string Description { get; set; } = "";
 
         [NotMapped] public bool IsSpecial => Count > 1 || Discount > 0 || TimeLimited ||
                                              AdditionalPrize != "" || PackId is not 1 and not 2;

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

@@ -23,10 +23,10 @@ namespace CardCollector.DataBase.Entity
         [Column("chat_id"), MaxLength(127)] public long ChatId { get; set; }
         
         /* Имя пользователя */
-        [Column("username"), MaxLength(256)] public string Username { get; set; }
+        [Column("username"), MaxLength(256)] public string Username { get; set; } = "";
         
         /* Заблокирован ли пользователь */
-        [Column("is_blocked"), MaxLength(11)] public bool IsBlocked { get; set; }
+        [Column("is_blocked"), MaxLength(11)] public bool IsBlocked { get; set; } = false;
 
         /* Уровень привилегий пользователя */
         [Column("privilege_level"), MaxLength(32)] public int PrivilegeLevel { get; set; } = 0;

+ 1 - 1
CardCollector/Logs.cs

@@ -14,7 +14,7 @@ namespace CardCollector
 
         static Logs()
         {
-            path = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) + @"\Logs";
+            path = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) + "/Logs";
             if (!Directory.Exists(path))
                 Directory.CreateDirectory(path);
         }

+ 45 - 9
CardCollector/Resources/Command.Designer.cs

@@ -114,6 +114,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABQ.
+        /// </summary>
+        internal static string buy_coins {
+            get {
+                return ResourceManager.GetString("buy_coins", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to ABH.
         /// </summary>
@@ -123,6 +132,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABN.
+        /// </summary>
+        internal static string buy_gems_item {
+            get {
+                return ResourceManager.GetString("buy_gems_item", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to ABK.
         /// </summary>
@@ -204,6 +222,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABR.
+        /// </summary>
+        internal static string confirm_exchange {
+            get {
+                return ResourceManager.GetString("confirm_exchange", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to AAM.
         /// </summary>
@@ -258,15 +285,6 @@ namespace CardCollector.Resources {
             }
         }
         
-        /// <summary>
-        ///   Looks up a localized string similar to ABN.
-        /// </summary>
-        internal static string gems50 {
-            get {
-                return ResourceManager.GetString("gems50", resourceCulture);
-            }
-        }
-        
         /// <summary>
         ///   Looks up a localized string similar to AAS.
         /// </summary>
@@ -411,6 +429,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABS.
+        /// </summary>
+        internal static string show_offer_info {
+            get {
+                return ResourceManager.GetString("show_offer_info", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to ABA.
         /// </summary>
@@ -420,6 +447,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABP.
+        /// </summary>
+        internal static string special_offers {
+            get {
+                return ResourceManager.GetString("special_offers", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to ABC.
         /// </summary>

+ 13 - 1
CardCollector/Resources/Command.resx

@@ -138,10 +138,22 @@
     <data name="pack_info" xml:space="preserve">
         <value>ABM</value>
     </data>
-    <data name="gems50" xml:space="preserve">
+    <data name="buy_gems_item" xml:space="preserve">
         <value>ABN</value>
     </data>
     <data name="open_specific" xml:space="preserve">
         <value>ABO</value>
     </data>
+    <data name="special_offers" xml:space="preserve">
+        <value>ABP</value>
+    </data>
+    <data name="buy_coins" xml:space="preserve">
+        <value>ABQ</value>
+    </data>
+    <data name="confirm_exchange" xml:space="preserve">
+        <value>ABR</value>
+    </data>
+    <data name="show_offer_info" xml:space="preserve">
+        <value>ABS</value>
+    </data>
 </root>

+ 20 - 4
CardCollector/Resources/Keyboard.cs

@@ -28,6 +28,12 @@ namespace CardCollector.Resources
             new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
         });
 
+        public static InlineKeyboardMarkup BuyCoinsKeyboard = new(new[]
+        {
+            new[] {InlineKeyboardButton.WithCallbackData(Text.confirm_exchange, Command.confirm_exchange)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+        });
+
         public static InlineKeyboardMarkup BackToFilters(string stickerTitle)
         {
             return new InlineKeyboardMarkup(new[]
@@ -376,15 +382,24 @@ namespace CardCollector.Resources
             return new InlineKeyboardMarkup(keyboard);
         }
 
-        public static InlineKeyboardMarkup ShopKeyboard(IEnumerable<ShopEntity> specialOffers)
+        public static InlineKeyboardMarkup ShopKeyboard(bool haveOffers)
+        {
+            return new InlineKeyboardMarkup(new[] {
+                new[] {InlineKeyboardButton.WithCallbackData(Text.special_offers + (haveOffers ? Text.gift : ""), Command.special_offers)},
+                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)},
+            });
+        }
+
+        public static InlineKeyboardMarkup SpecialOffersKeyboard(IEnumerable<ShopEntity> specialOffers)
         {
             var keyboard = new List<InlineKeyboardButton[]>();
             foreach (var offer in specialOffers)
                 keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(offer.Title,
                         $"{Command.select_offer}={offer.Id}")
                 });
-            keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.buy_pack, Command.buy_pack)});
-            keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.buy_gems, Command.buy_gems)});
             keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)});
             return new InlineKeyboardMarkup(keyboard);
         }
@@ -399,7 +414,7 @@ namespace CardCollector.Resources
 
         public static InlineKeyboardMarkup OfferKeyboard(ShopEntity offerInfo)
         {
-            var keyboard = new List<InlineKeyboardButton[]>();
+            var keyboard = new List<InlineKeyboardButton[]> ();
             if (offerInfo.PriceCoins >= 0)
                 keyboard.Add(new [] {InlineKeyboardButton.WithCallbackData(
                     $"{offerInfo.ResultPriceCoins}{Text.coin}", Command.buy_by_coins)
@@ -411,6 +426,7 @@ namespace CardCollector.Resources
                 else keyboard.Add(new [] {InlineKeyboardButton.WithCallbackData(
                     $"{offerInfo.ResultPriceGems}{Text.gem}", Command.buy_by_gems)
                 });
+            keyboard.Add(new [] {InlineKeyboardButton.WithCallbackData(Text.info, Command.show_offer_info)});
             keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)});
             return new InlineKeyboardMarkup(keyboard);
         }

+ 74 - 2
CardCollector/Resources/Messages.Designer.cs

@@ -96,6 +96,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Список доступных специальных предложений:.
+        /// </summary>
+        internal static string available_offers {
+            get {
+                return ResourceManager.GetString("available_offers", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Бот уходит на технический перерыв....
         /// </summary>
@@ -186,6 +195,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Вы получите монет:.
+        /// </summary>
+        internal static string coins_exchange_count {
+            get {
+                return ResourceManager.GetString("coins_exchange_count", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Не удалось добавить все стикеры. Было добавлено только.
         /// </summary>
@@ -222,6 +240,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Подтвердите обмен:.
+        /// </summary>
+        internal static string confirm_exchange {
+            get {
+                return ResourceManager.GetString("confirm_exchange", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Подтвердите выставление за.
         /// </summary>
@@ -348,6 +375,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Введите количество алмазов для обмена:.
+        /// </summary>
+        internal static string enter_exchange_sum {
+            get {
+                return ResourceManager.GetString("enter_exchange_sum", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Введите только одну эмоцию!.
         /// </summary>
@@ -366,6 +402,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Покупка монет производится за алмазы, 1💎 = 150💰..
+        /// </summary>
+        internal static string exchange_mesage {
+            get {
+                return ResourceManager.GetString("exchange_mesage", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Алмазы.
         /// </summary>
@@ -375,6 +420,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Вы платите алмазов:.
+        /// </summary>
+        internal static string gems_exchange_count {
+            get {
+                return ResourceManager.GetString("gems_exchange_count", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Наименьшая цена:.
         /// </summary>
@@ -429,6 +483,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Нет доступных специальных предложений! Возвращайтесь позже..
+        /// </summary>
+        internal static string offers_not_found {
+            get {
+                return ResourceManager.GetString("offers_not_found", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to При покупке &quot;Случайного пака&quot; Вам может выпасть ЛЮБОЙ стикер от ЛЮБОГО художника
         ///
@@ -485,9 +548,9 @@ namespace CardCollector.Resources {
         /// <summary>
         ///   Looks up a localized string similar to Пожалуйста, введите положительное целое число.
         /// </summary>
-        internal static string please_enter_price {
+        internal static string please_enter_integer {
             get {
-                return ResourceManager.GetString("please_enter_price", resourceCulture);
+                return ResourceManager.GetString("please_enter_integer", resourceCulture);
             }
         }
         
@@ -644,6 +707,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Вы получили.
+        /// </summary>
+        internal static string you_got {
+            get {
+                return ResourceManager.GetString("you_got", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Вы продали.
         /// </summary>

+ 25 - 1
CardCollector/Resources/Messages.resx

@@ -129,7 +129,7 @@
     <data name="combined_sticker" xml:space="preserve">
         <value>Поздравляем! Вы получили стикер</value>
     </data>
-    <data name="please_enter_price" xml:space="preserve">
+    <data name="please_enter_integer" xml:space="preserve">
         <value>Пожалуйста, введите положительное целое число</value>
     </data>
     <data name="current_price" xml:space="preserve">
@@ -230,4 +230,28 @@
     <data name="cant_sell_zero" xml:space="preserve">
         <value>Нельзя продать товар за 0!</value>
     </data>
+    <data name="offers_not_found" xml:space="preserve">
+        <value>Нет доступных специальных предложений! Возвращайтесь позже.</value>
+    </data>
+    <data name="available_offers" xml:space="preserve">
+        <value>Список доступных специальных предложений:</value>
+    </data>
+    <data name="exchange_mesage" xml:space="preserve">
+        <value>Покупка монет производится за алмазы, 1💎 = 150💰.</value>
+    </data>
+    <data name="confirm_exchange" xml:space="preserve">
+        <value>Подтвердите обмен:</value>
+    </data>
+    <data name="enter_exchange_sum" xml:space="preserve">
+        <value>Введите количество алмазов для обмена:</value>
+    </data>
+    <data name="gems_exchange_count" xml:space="preserve">
+        <value>Вы платите алмазов:</value>
+    </data>
+    <data name="coins_exchange_count" xml:space="preserve">
+        <value>Вы получите монет:</value>
+    </data>
+    <data name="you_got" xml:space="preserve">
+        <value>Вы получили</value>
+    </data>
 </root>

+ 46 - 1
CardCollector/Resources/Text.Designer.cs

@@ -141,6 +141,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Купить монеты.
+        /// </summary>
+        internal static string buy_coins {
+            get {
+                return ResourceManager.GetString("buy_coins", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Купить алмазы.
         /// </summary>
@@ -213,6 +222,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Совершить обмен.
+        /// </summary>
+        internal static string confirm_exchange {
+            get {
+                return ResourceManager.GetString("confirm_exchange", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Панель управления.
         /// </summary>
@@ -348,6 +366,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to 🎁.
+        /// </summary>
+        internal static string gift {
+            get {
+                return ResourceManager.GetString("gift", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to /help.
         /// </summary>
@@ -591,6 +618,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Специальные предложения .
+        /// </summary>
+        internal static string special_offers {
+            get {
+                return ResourceManager.GetString("special_offers", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to ⭐.
         /// </summary>
@@ -655,7 +691,7 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Акция заканчивается.
+        ///   Looks up a localized string similar to Дата окончания акции:.
         /// </summary>
         internal static string time_limit {
             get {
@@ -681,6 +717,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Бессрочно.
+        /// </summary>
+        internal static string unexpired {
+            get {
+                return ResourceManager.GetString("unexpired", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Да.
         /// </summary>

+ 16 - 1
CardCollector/Resources/Text.resx

@@ -196,7 +196,7 @@
         <value>Тира</value>
     </data>
     <data name="time_limit" xml:space="preserve">
-        <value>Акция заканчивается</value>
+        <value>Дата окончания акции:</value>
     </data>
     <data name="discount" xml:space="preserve">
         <value>Скидка</value>
@@ -228,4 +228,19 @@
     <data name="effect" xml:space="preserve">
         <value>Эффект</value>
     </data>
+    <data name="special_offers" xml:space="preserve">
+        <value>Специальные предложения </value>
+    </data>
+    <data name="buy_coins" xml:space="preserve">
+        <value>Купить монеты</value>
+    </data>
+    <data name="confirm_exchange" xml:space="preserve">
+        <value>Совершить обмен</value>
+    </data>
+    <data name="unexpired" xml:space="preserve">
+        <value>Бессрочно</value>
+    </data>
+    <data name="gift" xml:space="preserve">
+        <value>🎁</value>
+    </data>
 </root>

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

@@ -5,6 +5,7 @@ namespace CardCollector.Session.Modules
     public class ShopModule : Module
     {
         public ShopEntity SelectedPosition;
+        public int EnteredExchangeSum = 1;
         public void Reset()
         {
             SelectedPosition = null;