Browse Source

User level system

Tigran 3 years ago
parent
commit
658f74ae44
75 changed files with 632 additions and 231 deletions
  1. 2 2
      CardCollector.sln.DotSettings.user
  2. 4 0
      CardCollector/Bot.cs
  3. 1 1
      CardCollector/Commands/CallbackQuery/AuthorsMenu.cs
  4. 1 1
      CardCollector/Commands/CallbackQuery/BuyAuthorPackMenu.cs
  5. 1 1
      CardCollector/Commands/CallbackQuery/BuyCoins.cs
  6. 1 1
      CardCollector/Commands/CallbackQuery/BuyGems.cs
  7. 1 1
      CardCollector/Commands/CallbackQuery/BuyPack.cs
  8. 3 6
      CardCollector/Commands/CallbackQuery/BuyShopItem.cs
  9. 1 4
      CardCollector/Commands/CallbackQuery/BuySticker.cs
  10. 1 1
      CardCollector/Commands/CallbackQuery/CombineMenu.cs
  11. 2 2
      CardCollector/Commands/CallbackQuery/CombineStickers.cs
  12. 1 1
      CardCollector/Commands/CallbackQuery/ConfirmBuying.cs
  13. 1 1
      CardCollector/Commands/CallbackQuery/ConfirmExchange.cs
  14. 1 1
      CardCollector/Commands/CallbackQuery/ConfirmationSelling.cs
  15. 1 1
      CardCollector/Commands/CallbackQuery/DailyTasks.cs
  16. 1 1
      CardCollector/Commands/CallbackQuery/DeleteCombine.cs
  17. 1 1
      CardCollector/Commands/CallbackQuery/EndUploadStickers.cs
  18. 1 1
      CardCollector/Commands/CallbackQuery/MyPacks.cs
  19. 1 1
      CardCollector/Commands/CallbackQuery/OpenAuthorPackMenu.cs
  20. 2 5
      CardCollector/Commands/CallbackQuery/OpenPack.cs
  21. 1 1
      CardCollector/Commands/CallbackQuery/PackInfo.cs
  22. 1 1
      CardCollector/Commands/CallbackQuery/PutForAuction.cs
  23. 1 1
      CardCollector/Commands/CallbackQuery/SelectEmoji.cs
  24. 1 1
      CardCollector/Commands/CallbackQuery/SelectPrice.cs
  25. 1 1
      CardCollector/Commands/CallbackQuery/SelectSort.cs
  26. 1 1
      CardCollector/Commands/CallbackQuery/SelectTier.cs
  27. 1 1
      CardCollector/Commands/CallbackQuery/SpecialOffers.cs
  28. 1 1
      CardCollector/Commands/ChosenInlineResult/GetUnlimitedStickerAndExecuteCommand.cs
  29. 29 0
      CardCollector/Commands/ChosenInlineResult/GiveExp.cs
  30. 1 1
      CardCollector/Commands/ChosenInlineResult/SelectStickerInline.cs
  31. 1 1
      CardCollector/Commands/ChosenInlineResult/SelectTrader.cs
  32. 1 1
      CardCollector/Commands/ChosenInlineResult/SendPrivateSticker.cs
  33. 0 22
      CardCollector/Commands/ChosenInlineResult/SendSticker.cs
  34. 1 1
      CardCollector/Commands/ChosenInlineResult/StickerInfo.cs
  35. 1 1
      CardCollector/Commands/ChosenInlineResultCommand.cs
  36. 1 1
      CardCollector/Commands/CommandNotFound.cs
  37. 1 1
      CardCollector/Commands/InlineQuery/ShowStickersInGroup.cs
  38. 1 1
      CardCollector/Commands/Message/CreateToken.cs
  39. 1 1
      CardCollector/Commands/Message/DownloadStickerPack.cs
  40. 3 3
      CardCollector/Commands/Message/EnterEmoji.cs
  41. 2 2
      CardCollector/Commands/Message/EnterGemsExchange.cs
  42. 2 2
      CardCollector/Commands/Message/EnterGemsPrice.cs
  43. 55 0
      CardCollector/Commands/Message/GiveExp.cs
  44. 1 2
      CardCollector/Commands/Message/Menu.cs
  45. 9 8
      CardCollector/Commands/Message/Profile.cs
  46. 1 1
      CardCollector/Commands/Message/Shop.cs
  47. 1 1
      CardCollector/Commands/Message/ShowFiltersMenu.cs
  48. 1 1
      CardCollector/Commands/Message/Start.cs
  49. 1 1
      CardCollector/Commands/Message/StopBot.cs
  50. 6 6
      CardCollector/Commands/Message/UploadFile.cs
  51. 1 1
      CardCollector/Commands/Message/UploadSticker.cs
  52. 10 10
      CardCollector/Commands/MessageCommand.cs
  53. 6 2
      CardCollector/Commands/PreCheckoutQuery/BuyGems.cs
  54. 61 73
      CardCollector/Controllers/MessageController.cs
  55. 2 0
      CardCollector/DataBase/CardCollectorDatabase.cs
  56. 1 1
      CardCollector/DataBase/Entity/AuctionEntity.cs
  57. 60 0
      CardCollector/DataBase/Entity/Level.cs
  58. 21 0
      CardCollector/DataBase/Entity/UserEntity.cs
  59. 14 0
      CardCollector/DataBase/Entity/UserLevel.cs
  60. 34 0
      CardCollector/DataBase/EntityDao/LevelDao.cs
  61. 15 2
      CardCollector/DataBase/EntityDao/PacksDao.cs
  62. 2 1
      CardCollector/DataBase/EntityDao/UserDao.cs
  63. 34 0
      CardCollector/DataBase/EntityDao/UserLevelDao.cs
  64. 3 3
      CardCollector/DataBase/EntityDao/UserStickerRelationDao.cs
  65. 9 9
      CardCollector/Resources/Command.Designer.cs
  66. 1 1
      CardCollector/Resources/Command.resx
  67. 2 0
      CardCollector/Resources/Constants.cs
  68. 97 7
      CardCollector/Resources/Messages.Designer.cs
  69. 37 7
      CardCollector/Resources/Messages.resx
  70. 36 0
      CardCollector/Resources/Text.Designer.cs
  71. 12 0
      CardCollector/Resources/Text.resx
  72. 7 7
      CardCollector/Session/Modules/FiltersModule.cs
  73. 5 6
      CardCollector/Session/Session.cs
  74. 2 2
      CardCollector/StickerEffects/EffectFunctions.cs
  75. 4 0
      CardCollector/Utilities.cs

+ 2 - 2
CardCollector.sln.DotSettings.user

@@ -17,9 +17,9 @@
 	
 	
 	
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FMessages/@EntryIndexedValue">False</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FMessages/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FSortingTypes/@EntryIndexedValue">False</s:Boolean>
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FText/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FText/@EntryIndexedValue">False</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FStickerEffects_002FEffectTranslations/@EntryIndexedValue">False</s:Boolean>
 	
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>

+ 4 - 0
CardCollector/Bot.cs

@@ -3,6 +3,8 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Timers;
+using CardCollector.Commands;
+using CardCollector.Commands.Message;
 using CardCollector.DailyTasks;
 using CardCollector.DataBase;
 using CardCollector.DataBase.EntityDao;
@@ -51,6 +53,8 @@ namespace CardCollector
             Utilities.SetUpTimer(Constants.DailyTaskReset, DailyTask.ResetTasks);
             /* Запускаем таймер с эффектами стикеров */
             Utilities.SetUpTimer(Constants.DailyStickerRewardCheck, EffectFunctions.RunAll);
+            /* Запускаем таймер сброса отправленных стикеров */
+            Utilities.SetUpTimer(Constants.ResetGroupStickersExp, GiveExp.ResetStickersExp);
             
             _end.WaitOne();
             Logs.LogOut("Stopping program");

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

@@ -24,7 +24,7 @@ namespace CardCollector.Commands.CallbackQuery
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.page_not_found);
             /* Заменяем сообщение меню на сообщение со списком */
             else
-                await MessageController.SendMessage(User, Messages.choose_author,
+                await MessageController.EditMessage(User, Messages.choose_author,
                     Keyboard.GetAuthorsKeyboard(list, Keyboard.GetPagePanel(page, totalCount, CommandText)));
         }
 

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

@@ -22,7 +22,7 @@ namespace CardCollector.Commands.CallbackQuery
             if (packs.Count == 0)
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.page_not_found);
             else
-                await MessageController.SendMessage(User, Messages.choose_author,
+                await MessageController.EditMessage(User, Messages.choose_author,
                     Keyboard.GetShopPacksKeyboard(packs, Keyboard.GetPagePanel(page, totalCount, CommandText)));
         }
 

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

@@ -16,7 +16,7 @@ namespace CardCollector.Commands.CallbackQuery
         {
             EnterGemsExchange.AddToQueue(User.Id);
             var module = User.Session.GetModule<ShopModule>();
-            await MessageController.SendMessage(User,
+            await MessageController.EditMessage(User,
                 $"{Messages.exchange_mesage}" +
                 $"\n{Messages.gems_exchange_count} {module.EnteredExchangeSum}{Text.gem}" +
                 $"\n{Messages.coins_exchange_count} {module.EnteredExchangeSum * 150}{Text.coin}" +

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

@@ -15,7 +15,7 @@ namespace CardCollector.Commands.CallbackQuery
         {
             await MessageController.SendInvoice(User, Text.gems_title, Text.gems_description, 
                 Command.buy_gems_item, new[] {new LabeledPrice(Text.gems_label50, 100)},
-                1000000, new [] {500, 1000, 2500, 5000}, Keyboard.BuyGemsKeyboard);
+                900000, new [] {400, 900, 2400, 4900}, Keyboard.BuyGemsKeyboard);
         }
 
         public BuyGems() { }

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

@@ -13,7 +13,7 @@ namespace CardCollector.Commands.CallbackQuery
         
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, Messages.choose_option, Keyboard.ShopPacksKeyboard);
+            await MessageController.EditMessage(User, Messages.choose_option, Keyboard.ShopPacksKeyboard);
         }
 
         public BuyPack() { }

+ 3 - 6
CardCollector/Commands/CallbackQuery/BuyShopItem.cs

@@ -49,7 +49,7 @@ namespace CardCollector.Commands.CallbackQuery
                 var userPack = await UserPacksDao.GetOne(User.Id, packId);
                 userPack.Count += module.SelectedPosition?.Count ?? module.Count;
                 if (module.SelectedPosition?.AdditionalPrize != "") await GivePrize(module.SelectedPosition?.AdditionalPrize);
-                await MessageController.SendMessage(User, $"{Messages.thanks_for_buying} {userPack.Count}", 
+                await MessageController.EditMessage(User, $"{Messages.thanks_for_buying} {userPack.Count}", 
                     offerSpecial && !offerInfinite || (module.SelectedPosition?.Expired ?? false) || !canBuy
                     ? Keyboard.BackKeyboard
                     : Keyboard.RepeatCommand(Text.buy_more, CallbackData));
@@ -75,12 +75,9 @@ namespace CardCollector.Commands.CallbackQuery
             }
             if (sticker != null)
             {
-                if (!User.Stickers.ContainsKey(sticker.Md5Hash))
-                    await UserStickerRelationDao.AddNew(User, sticker, 1);
-                else
-                    User.Stickers[sticker.Md5Hash].Count ++;
+                await UserStickerRelationDao.AddSticker(User, sticker);
                 await MessageController.SendSticker(User, sticker.Id);
-                await MessageController.SendMessage(User, $"{Messages.congratulation}\n{sticker}");
+                await MessageController.EditMessage(User, $"{Messages.congratulation}\n{sticker}");
             }
         }
 

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

@@ -28,10 +28,7 @@ namespace CardCollector.Commands.CallbackQuery
                 await auctionModule.SelectedPosition.BuyCard(auctionModule.Count);
                 var discount = 1.0 - await User.AuctionDiscount() / 100.0;
                 User.Cash.Gems -= (int)(auctionModule.Price * auctionModule.Count * discount);
-                if (!User.Stickers.ContainsKey(auctionModule.SelectedSticker.Md5Hash))
-                    await UserStickerRelationDao.AddNew(User, auctionModule.SelectedSticker, auctionModule.Count);
-                else
-                    User.Stickers[auctionModule.SelectedSticker.Md5Hash].Count += auctionModule.Count;
+                await UserStickerRelationDao.AddSticker(User, auctionModule.SelectedSticker, auctionModule.Count);
                 User.Session.ResetModule<AuctionModule>();
             }
         }

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

@@ -19,7 +19,7 @@ namespace CardCollector.Commands.CallbackQuery
             if (combineModule.CombineCount == 0)
                 await new Back(User, Update).Execute();
             else 
-                await MessageController.SendMessage(User, combineModule.ToString(), Keyboard.GetCombineKeyboard(combineModule));
+                await MessageController.EditMessage(User, combineModule.ToString(), Keyboard.GetCombineKeyboard(combineModule));
         }
 
         protected internal override bool IsMatches(UserEntity user, Update update) => false;

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

@@ -31,10 +31,10 @@ namespace CardCollector.Commands.CallbackQuery
                 var author = authors[rnd.Next(authors.Count)];
                 var stickers = await StickerDao.GetListWhere(i => i.Author == author && i.Tier == tier + 1);
                 var sticker = stickers[rnd.Next(stickers.Count)];
-                await UserStickerRelationDao.AddNew(User, sticker, 1);
+                await UserStickerRelationDao.AddSticker(User, sticker);
                 var text = $"{Messages.combined_sticker}:\n" + sticker;
                 await MessageController.SendSticker(User, sticker.Id);
-                await MessageController.SendMessage(User, text, Keyboard.BackToFilters(sticker.Title));
+                await MessageController.EditMessage(User, text, Keyboard.BackToFilters(sticker.Title));
                 User.Session.DeleteModule<CombineModule>();
             }
         }

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

@@ -22,7 +22,7 @@ namespace CardCollector.Commands.CallbackQuery
                 var text = $"{Messages.confirm_buying}" +
                            $"\n{auctionModule.Count}{Text.items} {Text.per} {price}{Text.gem}" +
                            $"\n{Messages.are_you_sure}";
-                await MessageController.SendMessage(User, text, Keyboard.GetConfirmationKeyboard(Command.buy_sticker));
+                await MessageController.EditMessage(User, text, Keyboard.GetConfirmationKeyboard(Command.buy_sticker));
             }
         }
 

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

@@ -20,7 +20,7 @@ namespace CardCollector.Commands.CallbackQuery
             {
                 User.Cash.Gems -= module.EnteredExchangeSum;
                 User.Cash.Coins += module.EnteredExchangeSum * 150;
-                await MessageController.SendMessage(User,
+                await MessageController.EditMessage(User,
                     $"{Messages.you_got} {module.EnteredExchangeSum * 150}{Text.coin} {Text.per} {module.EnteredExchangeSum}{Text.gem}");
             }
         }

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

@@ -24,7 +24,7 @@ namespace CardCollector.Commands.CallbackQuery
                 User.Stickers[collectionModule.SelectedSticker.Md5Hash].Count -= collectionModule.Count;
                 AuctionController.SellCard(User.Id, collectionModule.SelectedSticker.Id, collectionModule.SellPrice,
                     collectionModule.Count);
-                await MessageController.SendMessage(User, Messages.successfully_selling);
+                await MessageController.EditMessage(User, Messages.successfully_selling);
             }
         }
         

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

@@ -22,7 +22,7 @@ namespace CardCollector.Commands.CallbackQuery
                     userTasks.Add((int)key, await DailyTaskDao.AddNew(User.Id, (int)key));
                 text += $"\n{task.Title} ({task.Goal - userTasks[(int) key].Progress}/{task.Goal})";
             }
-            await MessageController.SendMessage(User, text, Keyboard.BackKeyboard);
+            await MessageController.EditMessage(User, text, Keyboard.BackKeyboard);
         }
 
         public DailyTasks() { }

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

@@ -20,7 +20,7 @@ namespace CardCollector.Commands.CallbackQuery
             module.CombineList.Remove(sticker);
             if (module.CombineList.Count == 0)
                 await new Back(User, Update).PrepareAndExecute();
-            else await MessageController.SendMessage(User, module.ToString(), Keyboard.GetCombineKeyboard(module));
+            else await MessageController.EditMessage(User, module.ToString(), Keyboard.GetCombineKeyboard(module));
         }
 
         public DeleteCombine() { }

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

@@ -12,7 +12,7 @@ namespace CardCollector.Commands.CallbackQuery
 
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, Messages.upload_your_file, Keyboard.BackKeyboard);
+            await MessageController.EditMessage(User, Messages.upload_your_file, Keyboard.BackKeyboard);
         }
 
         public EndUploadStickers() { }

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

@@ -18,7 +18,7 @@ namespace CardCollector.Commands.CallbackQuery
         {
             var random = await UserPacksDao.GetOne(User.Id, 1);
             var authorCount = (await UserPacksDao.GetUserPacks(User.Id)).Sum(item => item.PackId != 1 ? item.Count : 0);
-            await MessageController.SendMessage(User, 
+            await MessageController.EditMessage(User, 
                 $"{Messages.your_packs}" +
                 $"\n{Messages.random_packs}: {random.Count}{Text.items}" +
                 $"\n{Messages.author_pack}: {authorCount}{Text.items}",

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

@@ -35,7 +35,7 @@ namespace CardCollector.Commands.CallbackQuery
                 if (packs.Count == 0)
                     await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.page_not_found);
                 else
-                    await MessageController.SendMessage(User, Messages.choose_author,
+                    await MessageController.EditMessage(User, Messages.choose_author,
                         await Keyboard.GetUserPacksKeyboard(packs, Keyboard.GetPagePanel(page, totalCount, CommandText)));
             }
         }

+ 2 - 5
CardCollector/Commands/CallbackQuery/OpenPack.cs

@@ -26,12 +26,9 @@ namespace CardCollector.Commands.CallbackQuery
                 var stickers = await StickerDao.GetListWhere(item =>
                     item.Tier == tier && (packId == 1 || item.PackId == packId));
                 var sticker = stickers[Utilities.rnd.Next(stickers.Count)];
-                if (User.Stickers.ContainsKey(sticker.Md5Hash))
-                    User.Stickers[sticker.Md5Hash].Count++;
-                else
-                    await UserStickerRelationDao.AddNew(User, sticker, 1);
+                await UserStickerRelationDao.AddSticker(User, sticker);
                 await MessageController.SendSticker(User, sticker.Id);
-                await MessageController.SendMessage(User, $"{Messages.congratulation}\n{sticker}",
+                await MessageController.EditMessage(User, $"{Messages.congratulation}\n{sticker}",
                     userPack.Count > 0
                         ? Keyboard.RepeatCommand(Text.open_more, CallbackData)
                         : Keyboard.BackKeyboard);

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

@@ -12,7 +12,7 @@ namespace CardCollector.Commands.CallbackQuery
 
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, Messages.pack_info, Keyboard.BackKeyboard);
+            await MessageController.EditMessage(User, Messages.pack_info, Keyboard.BackKeyboard);
         }
 
         public PackInfo() { }

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

@@ -19,7 +19,7 @@ namespace CardCollector.Commands.CallbackQuery
             var module = User.Session.GetModule<CollectionModule>();
             var priceList = (await AuctionController.GetPriceList(module.SelectedSticker.Id)).ToList();
             var lowerPrice = priceList.Count > 0 ? priceList.Min() : 0;
-            await MessageController.SendMessage(User,
+            await MessageController.EditMessage(User,
                 $"{Messages.current_price} {module.SellPrice}{Text.gem}" +
                 $"\n{Messages.lower_price} {lowerPrice}{Text.gem}" +
                 $"\n{Messages.enter_your_gems_price} {Text.gem}:", Keyboard.BackKeyboard);

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

@@ -14,7 +14,7 @@ namespace CardCollector.Commands.CallbackQuery
         public override async Task Execute()
         {
             EnterEmoji.AddToQueue(User.Id);
-            await MessageController.SendMessage(User, Messages.enter_emoji, Keyboard.EmojiOptions);
+            await MessageController.EditMessage(User, Messages.enter_emoji, Keyboard.EmojiOptions);
         }
 
         public SelectEmoji() { }

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

@@ -12,7 +12,7 @@ namespace CardCollector.Commands.CallbackQuery
 
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, Messages.choose_price, 
+            await MessageController.EditMessage(User, Messages.choose_price, 
                 User.Session.State == UserState.AuctionMenu ? Keyboard.GemsPriceOptions : Keyboard.CoinsPriceOptions);
         }
 

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

@@ -12,7 +12,7 @@ namespace CardCollector.Commands.CallbackQuery
 
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, Messages.choose_sort, Keyboard.SortOptions);
+            await MessageController.EditMessage(User, Messages.choose_sort, Keyboard.SortOptions);
         }
 
         public SelectSort() { }

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

@@ -12,7 +12,7 @@ namespace CardCollector.Commands.CallbackQuery
 
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, Messages.choose_tier, Keyboard.TierOptions);
+            await MessageController.EditMessage(User, Messages.choose_tier, Keyboard.TierOptions);
         }
         
         public SelectTier() { }

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

@@ -21,7 +21,7 @@ namespace CardCollector.Commands.CallbackQuery
             if (shopEntities.Count < 1)
                 await MessageController.AnswerCallbackQuery(User, CallbackQueryId, Messages.offers_not_found, true);
             else
-                await MessageController.SendMessage(User, Messages.available_offers, Keyboard.SpecialOffersKeyboard(shopEntities));
+                await MessageController.EditMessage(User, Messages.available_offers, Keyboard.SpecialOffersKeyboard(shopEntities));
         }
 
         public SpecialOffers() { }

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

@@ -20,7 +20,7 @@ namespace CardCollector.Commands.ChosenInlineResult
             /* Получаем объект стикера */
             var sticker = await StickerDao.GetByHash(hash);
             /* Выдаем пользователю 1 стикер */
-            await UserStickerRelationDao.AddNew(User, sticker, 1);
+            await UserStickerRelationDao.AddSticker(User, sticker);
             /* Выполняем стандартный сценарий команды */
             await PrivateFactory(Update, User).PrepareAndExecute();
         }

+ 29 - 0
CardCollector/Commands/ChosenInlineResult/GiveExp.cs

@@ -0,0 +1,29 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.ChosenInlineResult
+{
+    /* Данная команда выполняется при отправке пользователем стикера */
+    public class GiveExp : ChosenInlineResultCommand
+    {
+        /* Ключевое слово для данной команды send_sticker */
+        protected override string CommandText => Command.give_exp;
+
+        public override async Task Execute()
+        {
+            if (!User.Session.ChosenResultWithMessage)
+            {
+                await MessageController.SendMessage(User, $"{Messages.you_gained} 1 {Text.exp} {Messages.send_sticker}" +
+                                                          $"\n{Messages.you_can_add_bot_to_conversation}", addToList: true);
+                await User.GiveExp(1);
+            }
+            User.Session.ChosenResultWithMessage = false;
+        }
+        
+        public GiveExp() { }
+        public GiveExp(UserEntity user, Update update) : base(user, update) { }
+    }
+}

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

@@ -40,7 +40,7 @@ namespace CardCollector.Commands.ChosenInlineResult
                     break;
             }
             await MessageController.SendSticker(User, sticker.Id);
-            await MessageController.SendMessage(User, sticker.ToString(stickerCount), Keyboard.GetStickerKeyboard(User.Session));
+            await MessageController.EditMessage(User, sticker.ToString(stickerCount), Keyboard.GetStickerKeyboard(User.Session));
             if (User.Session.State == UserState.AuctionMenu) User.Session.State = UserState.ProductMenu;
         }
 

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

@@ -20,7 +20,7 @@ namespace CardCollector.Commands.ChosenInlineResult
             if (module.SelectedSticker is not {} sticker) return;
             module.SelectedPosition = product;
             var discount = 1.0 - await User.AuctionDiscount() / 100.0;
-            await MessageController.SendMessage(User, sticker.ToString(module.MaxCount), 
+            await MessageController.EditMessage(User, sticker.ToString(module.MaxCount), 
                 Keyboard.GetStickerKeyboard(User.Session, discount));
         }
 

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

@@ -17,7 +17,7 @@ namespace CardCollector.Commands.ChosenInlineResult
             if (await dailyTask.Execute(User.Id))
             {
                 await dailyTask.GiveReward(User.Id);
-                await MessageController.SendMessage(User, Messages.pack_prize);
+                await MessageController.EditMessage(User, Messages.pack_prize);
             }
         }
 

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

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

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

@@ -16,7 +16,7 @@ namespace CardCollector.Commands.ChosenInlineResult
             var hash = InlineResult.Split('=')[1];
             var sticker = await StickerDao.GetByHash(hash);
             await MessageController.SendSticker(User, sticker.Id);
-            await MessageController.SendMessage(User, sticker.ToString(), Keyboard.StickerInfoKeyboard);
+            await MessageController.EditMessage(User, sticker.ToString(), Keyboard.StickerInfoKeyboard);
         }
 
         public StickerInfo() { }

+ 1 - 1
CardCollector/Commands/ChosenInlineResultCommand.cs

@@ -36,7 +36,7 @@ namespace CardCollector.Commands
             new GetUnlimitedStickerAndExecuteCommand(),
 
             // Обработка результата при отправке стикера
-            new SendSticker(),
+            new GiveExp(),
             new SendPrivateSticker(),
             // Обработка результата при выборе продавца
             new SelectTrader(),

+ 1 - 1
CardCollector/Commands/CommandNotFound.cs

@@ -14,7 +14,7 @@ namespace CardCollector.Commands
 
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, "Команда не найдена " + _command);
+            await MessageController.EditMessage(User, "Команда не найдена " + _command);
         }
 
         protected internal override bool IsMatches(UserEntity user, Update update) => true;

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

@@ -18,7 +18,7 @@ namespace CardCollector.Commands.InlineQuery
         {
             // Получаем список стикеров
             var stickersList = await User.GetStickersList(Query);
-            var results = stickersList.ToTelegramResults(Command.send_sticker, false);
+            var results = stickersList.ToTelegramResults(Command.give_exp, false);
             // Посылаем пользователю ответ на его запрос
             await MessageController.AnswerInlineQuery(InlineQueryId, results);
         }

+ 1 - 1
CardCollector/Commands/Message/CreateToken.cs

@@ -21,7 +21,7 @@ namespace CardCollector.Commands.Message
             var token = GenerateNewToken();
             await SessionTokenDao.AddNew(User.Id, token);
             var loginLink = $"{site}login?token={token}";
-            await MessageController.SendMessage(User, $"<a href=\"{loginLink}\">{Messages.your_login_link}</a>",
+            await MessageController.EditMessage(User, $"<a href=\"{loginLink}\">{Messages.your_login_link}</a>",
                 Keyboard.LoginKeyboard(loginLink), ParseMode.Html);
         }
 

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

@@ -15,7 +15,7 @@ namespace CardCollector.Commands.Message
 
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, Messages.upload_your_stickers, Keyboard.BackKeyboard);
+            await MessageController.EditMessage(User, Messages.upload_your_stickers, Keyboard.BackKeyboard);
         }
 
         protected internal override bool IsMatches(UserEntity user, Update update)

+ 3 - 3
CardCollector/Commands/Message/EnterEmoji.cs

@@ -25,10 +25,10 @@ namespace CardCollector.Commands.Message
             var input = Update.Message!.Text;
             /* если пользователь ввел что-то кроме эмодзи */
             if (!Regex.IsMatch(input!, onlyEmojiPattern))
-                await MessageController.SendMessage(User, Messages.please_enter_emoji, Keyboard.EmojiOptions);
+                await MessageController.EditMessage(User, Messages.please_enter_emoji, Keyboard.EmojiOptions);
             /* если пользователь ввел несколько эмодзи или эмодзи и текст */
             else if (!Regex.IsMatch(input, oneEmojiPattern))
-                await MessageController.SendMessage(User, Messages.enter_only_one_emoji,
+                await MessageController.EditMessage(User, Messages.enter_only_one_emoji,
                     Keyboard.EmojiOptions);
             else
             {
@@ -37,7 +37,7 @@ namespace CardCollector.Commands.Message
                 /* Формируем сообщение с имеющимися фильтрами у пользователя */
                 var text = filtersModule.ToString(User.Session.State);
                 /* Редактируем сообщение */
-                await MessageController.SendMessage(User, text, Keyboard.GetSortingMenu(User.Session.State));
+                await MessageController.EditMessage(User, text, Keyboard.GetSortingMenu(User.Session.State));
                 Queue.Remove(User.Id);
             }
         }

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

@@ -18,7 +18,7 @@ namespace CardCollector.Commands.Message
         {
             var module = User.Session.GetModule<ShopModule>();
             if (!int.TryParse(Update.Message!.Text, out var sum) || sum < 0)
-                await MessageController.SendMessage(User,
+                await MessageController.EditMessage(User,
                     $"{Messages.exchange_mesage}" +
                     $"\n{Messages.gems_exchange_count} {module.EnteredExchangeSum}{Text.gem}" +
                     $"\n{Messages.coins_exchange_count} {module.EnteredExchangeSum * 150}{Text.coin}" +
@@ -27,7 +27,7 @@ namespace CardCollector.Commands.Message
             else
             {
                 module.EnteredExchangeSum = sum; 
-                await MessageController.SendMessage(User,
+                await MessageController.EditMessage(User,
                     $"{Messages.exchange_mesage}" +
                     $"\n{Messages.gems_exchange_count} {module.EnteredExchangeSum}{Text.gem}" +
                     $"\n{Messages.coins_exchange_count} {module.EnteredExchangeSum * 150}{Text.coin}" +

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

@@ -20,14 +20,14 @@ namespace CardCollector.Commands.Message
             var module = User.Session.GetModule<CollectionModule>();
             if (!int.TryParse(Update.Message!.Text, out var price) || price < 0)
             {
-                await MessageController.SendMessage(User,
+                await MessageController.EditMessage(User,
                     $"{Messages.current_price} {module.SellPrice}{Text.gem}\n{Messages.please_enter_integer}",
                     Keyboard.AuctionPutCancelKeyboard);
             }
             else
             {
                 module.SellPrice = price;
-                await MessageController.SendMessage(User, $"{Messages.confirm_selling} {module.SellPrice}{Text.gem}:" +
+                await MessageController.EditMessage(User, $"{Messages.confirm_selling} {module.SellPrice}{Text.gem}:" +
                                                           $"\n{Messages.or_enter_another_sum}", Keyboard.AuctionPutCancelKeyboard);
             }
         }

+ 55 - 0
CardCollector/Commands/Message/GiveExp.cs

@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Timers;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+
+namespace CardCollector.Commands.Message
+{
+    public class GiveExp : MessageCommand
+    {
+        protected override string CommandText => "";
+
+        private static readonly Dictionary<long, Dictionary<long, int>> GroupStickersExp = new();
+
+        public override async Task Execute()
+        {
+            if (Update.Message == null) return;
+            User.Session.ChosenResultWithMessage = true;
+            var chatId = Update.Message.Chat.Id;
+            if (!GroupStickersExp.ContainsKey(chatId))
+                GroupStickersExp.Add(chatId, new Dictionary<long, int>());
+            if (!GroupStickersExp[chatId].ContainsKey(User.Id))
+                GroupStickersExp[chatId].Add(User.Id, 0);
+            if (GroupStickersExp[chatId][User.Id] < 5)
+            {
+                GroupStickersExp[chatId][User.Id]++;
+                var membersCount = await Bot.Client.GetChatMemberCountAsync(chatId) - 1;
+                await User.GiveExp(membersCount < 21 ? membersCount : 20);
+                await MessageController.SendMessage(User, 
+                    $"{Messages.you_gained} {(membersCount < 21 ? membersCount : 20)} {Text.exp} {Messages.send_sticker}" +
+                    $"\n{Messages.count_sends_per_day} \"{Update.Message.Chat.Title}\" {GroupStickersExp[chatId][User.Id]} / 5",
+                    addToList: true);
+            }
+        }
+        
+        public static void ResetStickersExp(object o, ElapsedEventArgs e)
+        {
+            GroupStickersExp.Clear();
+        }
+
+        protected internal override bool IsMatches(UserEntity user, Update update)
+        {
+            return update.Message?.ViaBot is { } bot && bot.Username == AppSettings.NAME &&
+                   update.Message.Type is MessageType.Sticker &&
+                   update.Message.Chat.Type is ChatType.Group or ChatType.Supergroup;
+        }
+
+        public GiveExp() { }
+        public GiveExp(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 1 - 2
CardCollector/Commands/Message/Menu.cs

@@ -13,8 +13,7 @@ namespace CardCollector.Commands.Message
         public override async Task Execute()
         {
             /* Отправляем пользователю сообщение со стандартной клавиатурой */
-            var message = await MessageController.SendMessage(User, Messages.menu_message, Keyboard.Menu);
-            User.Session.Messages.Remove(message.MessageId);
+            await MessageController.SendMessage(User, Messages.menu_message, Keyboard.Menu);
         }
 
         public Menu() { }

+ 9 - 8
CardCollector/Commands/Message/Profile.cs

@@ -1,6 +1,7 @@
 using System.Threading.Tasks;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
 
@@ -19,15 +20,15 @@ namespace CardCollector.Commands.Message
         {
             /* Подсчитываем прибыль */
             var income = await User.Cash.CalculateIncome(User.Stickers);
+            var expGoal = (await LevelDao.GetLevel(User.CurrentLevel.Level + 1))?.LevelExpGoal.ToString() ?? "∞";
             /* Отправляем сообщение */
-            await MessageController.SendMessage(User, 
-                /* Имя пользователя */
-                $"{User.Username}\n" +
-                /* Количество монет */
-                $"{Messages.coins}: {User.Cash.Coins}{Text.coin}\n" +
-                /* Количество алмазов */
-                $"{Messages.gems}: {User.Cash.Gems}{Text.gem}",
-                /* Клавиатура профиля */
+            await MessageController.EditMessage(User, 
+                $"{User.Username}" +
+                $"\n{Messages.coins}: {User.Cash.Coins}{Text.coin}" +
+                $"\n{Messages.gems}: {User.Cash.Gems}{Text.gem}" +
+                $"\n{Messages.level}: {User.CurrentLevel.Level}" +
+                $"\n{Messages.current_exp}: {User.CurrentLevel.CurrentExp} / {expGoal}" +
+                $"\n{Messages.cash_capacity}: {User.Cash.MaxCapacity}{Text.coin}",
                 Keyboard.GetProfileKeyboard(income, User.PrivilegeLevel));
         }
         

+ 1 - 1
CardCollector/Commands/Message/Shop.cs

@@ -21,7 +21,7 @@ namespace CardCollector.Commands.Message
             User.Session.InitNewModule<ShopModule>();
             var haveSpecialOffers = await (await ShopDao.GetSpecialPositions())
                 .AnyAsync(async offer => offer.IsInfinite || !await SpecialOfferUsersDao.NowUsed(User.Id, offer.Id));
-            await MessageController.SendMessage(User, Messages.shop_message, Keyboard.ShopKeyboard(haveSpecialOffers));
+            await MessageController.EditMessage(User, Messages.shop_message, Keyboard.ShopKeyboard(haveSpecialOffers));
         }
         
         public Shop() { }

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

@@ -17,7 +17,7 @@ namespace CardCollector.Commands.Message
             /* Формируем сообщение с имеющимися фильтрами у пользователя */
             var text = User.Session.GetModule<FiltersModule>().ToString(User.Session.State);
             /* Отправляем сообщение */
-            await MessageController.SendMessage(User, text, Keyboard.GetSortingMenu(User.Session.State));
+            await MessageController.EditMessage(User, text, Keyboard.GetSortingMenu(User.Session.State));
         }
         
         public ShowFiltersMenu(UserEntity user, Update update) : base(user, update) { }

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

@@ -15,7 +15,7 @@ namespace CardCollector.Commands.Message
         public override async Task Execute()
         {
             /* Отправляем пользователю сообщение со стандартной клавиатурой */
-            await MessageController.SendMessage(User, Messages.start_message, Keyboard.Menu);
+            await MessageController.EditMessage(User, Messages.start_message, Keyboard.Menu);
         }
         
         public Start() { }

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

@@ -12,7 +12,7 @@ namespace CardCollector.Commands.Message
 
         public override async Task Execute()
         {
-            await MessageController.SendMessage(User, "Stopping bot");
+            await MessageController.EditMessage(User, "Stopping bot");
             await Bot.StopProgram();
         }
 

+ 6 - 6
CardCollector/Commands/Message/UploadFile.cs

@@ -25,27 +25,27 @@ namespace CardCollector.Commands.Message
             try
             {
                 /* Соообщаем, что начали загрузку файла */
-                await MessageController.SendMessage(User, Messages.downloading_file);
+                await MessageController.EditMessage(User, Messages.downloading_file);
                 /* Загружаем файл */
                 var fileName = await Utilities.DownloadFile(Update.Message?.Document);
                 /* Сообщаем пользователю, что читаем документ */
-                await MessageController.SendMessage(User, Messages.reading_document);
+                await MessageController.EditMessage(User, Messages.reading_document);
                 /* Парсим файл */
                 var stickersList = await ParseExcelFile(fileName, module.StickersList);
                 /* Сообщаем пользователю, что удаляем файлы */
-                await MessageController.SendMessage(User, Messages.deleting_files);
+                await MessageController.EditMessage(User, Messages.deleting_files);
                 File.Delete(fileName);
                 /* Сообщаем пользователю, что загружаем стикеры */
-                await MessageController.SendMessage(User, Messages.uploading_stickers);
+                await MessageController.EditMessage(User, Messages.uploading_stickers);
                 var packInfo = await PacksDao.AddNew(stickersList.First().Author);
                 await StickerDao.AddRange(stickersList, packInfo.Id);
                 /* Сообщаем пользователю, что стикеры загружены */
-                await MessageController.SendMessage(User, Messages.stickers_succesfully_uploaded);
+                await MessageController.EditMessage(User, Messages.stickers_succesfully_uploaded);
             }
             catch (Exception e)
             {
                 /* Сообщаем пользователю, что произошла ошибка */
-                await MessageController.SendMessage(User, $"{Messages.unexpected_exception}: {e.Message}");
+                await MessageController.EditMessage(User, $"{Messages.unexpected_exception}: {e.Message}");
             }
         }
         

+ 1 - 1
CardCollector/Commands/Message/UploadSticker.cs

@@ -21,7 +21,7 @@ namespace CardCollector.Commands.Message
                           $"\n{Messages.uploaded_count} {module.Count}";
             foreach (var (stickerEntity, i) in module.StickersList.WithIndex())
                 message += $"\n{Text.sticker} {i + 1}: {stickerEntity.Id}";
-            await MessageController.SendMessage(User, message, Keyboard.EndStickerUpload);
+            await MessageController.EditMessage(User, message, Keyboard.EndStickerUpload);
         }
 
         protected internal override bool IsMatches(UserEntity user, Update update)

+ 10 - 10
CardCollector/Commands/MessageCommand.cs

@@ -53,6 +53,7 @@ namespace CardCollector.Commands
             /* Выгрузка файлов к боту */
             new UploadFile(),
             new UploadSticker(),
+            new GiveExp(),
         };
 
         /* Метод, создающий объекты команд исходя из полученного обновления */
@@ -72,23 +73,22 @@ namespace CardCollector.Commands
             
             // Объект пользователя
             var user = await UserDao.GetUser(update.Message!.From);
-        
-            // Удаляем сообщение пользователя в лс, оно нам больше не нужно
-            await MessageController.DeleteMessage(user, update.Message.MessageId);
             
-            // Если сообщение - это команда, полученная от бота, то мы игнорируем, так как получим ее через ChosenInlineResult
-            if (update.Message.ViaBot is { }) return new IgnoreUpdate();
+            // Удаляем сообщение пользователя в лс, оно нам больше не нужно
+            if (update.Message.Chat.Type is ChatType.Private)
+                await MessageController.DeleteMessage(user, update.Message.MessageId);
             
-            // Если пользователь заблокирован или сообщение где-то в другом канале, привате - игонрируем
-            if (user.IsBlocked || update.Message.Chat.Id != user.ChatId) return new IgnoreUpdate();
+            // Если пользователь заблокирован
+            if (user.IsBlocked) return new IgnoreUpdate();
             
             // Возвращаем объект, если команда совпала
             /* Возвращаем первую подходящую команду */
             return List.FirstOrDefault(item => item.IsMatches(user, update)) is { } executor
                 ? (UpdateModel) Activator.CreateInstance(executor.GetType(), user, update)
-                : new CommandNotFound(user, update, update.Message.Type == MessageType.Text 
-                    ? update.Message.Text 
-                    : Utilities.ToJson(update.Message));
+                : new IgnoreUpdate();
+            /*CommandNotFound(user, update, update.Message.Type == MessageType.Text 
+            ? update.Message.Text 
+            : Utilities.ToJson(update.Message));*/
         }
 
         protected internal override bool IsMatches(UserEntity user, Update update)

+ 6 - 2
CardCollector/Commands/PreCheckoutQuery/BuyGems.cs

@@ -14,8 +14,12 @@ namespace CardCollector.Commands.PreCheckoutQuery
         public override async Task Execute()
         {
             await Bot.Client.AnswerPreCheckoutQueryAsync(PreCheckoutQueryId);
-            User.Cash.Gems += 50 * Amount / 100;
-            await MessageController.SendMessage(User, Messages.thanks_for_buying);
+            var gemsCount = 50 * Amount / 100;
+            User.Cash.Gems += gemsCount;
+            await MessageController.EditMessage(User, Messages.thanks_for_buying_gems);
+            await MessageController.SendMessage(User, 
+                $"{Messages.you_gained} {gemsCount * 2} {Text.exp} {Messages.buy_gems}", addToList: true);
+            await User.GiveExp(gemsCount * 2);
         }
 
         public BuyGems() { }

+ 61 - 73
CardCollector/Controllers/MessageController.cs

@@ -85,57 +85,69 @@ namespace CardCollector.Controllers
          user - пользователь, которому необходимо отправить сообщение
          message - текст сообщения
          keyboard - клавиатура, которую надо добавить к сообщению */
-        public static async Task<Message> SendMessage(UserEntity user, string message, IReplyMarkup keyboard = null,
+        public static async Task<Message> EditMessage(UserEntity user, string message, IReplyMarkup keyboard = null,
             ParseMode? parseMode = null)
         {
-            try
-            {
-                if (!user.IsBlocked)
+        
+            if (!user.IsBlocked)
+                try
                 {
-                    if (user.Session.Messages.Count > 0)
-                        return await Bot.Client.EditMessageTextAsync(user.ChatId, user.Session.Messages.Last(),
-                            message, parseMode, 
-                            replyMarkup: (InlineKeyboardMarkup)keyboard ?? InlineKeyboardMarkup.Empty());
-                    var result = await Bot.Client.SendTextMessageAsync(user.ChatId, message, parseMode,
-                        replyMarkup: keyboard, disableNotification: true);
-                    user.Session?.Messages.Add(result.MessageId);
-                    return result;
+                        if (user.Session.Messages.Count > 0)
+                            return await Bot.Client.EditMessageTextAsync(user.ChatId, user.Session.Messages.Last(),
+                                message, parseMode, 
+                                replyMarkup: (InlineKeyboardMarkup)keyboard ?? InlineKeyboardMarkup.Empty());
+                        var result = await Bot.Client.SendTextMessageAsync(user.ChatId, message, parseMode,
+                            replyMarkup: keyboard, disableNotification: true);
+                        user.Session?.Messages.Add(result.MessageId);
+                        return result;
                 }
-            }
-            catch (Exception e)
-            {
-                LogOutWarning("Can't send text message " + e.Message);
-                if (!user.IsBlocked)
+                catch (Exception e)
                 {
-                    await user.ClearChat();
-                    var result = await Bot.Client.SendTextMessageAsync(user.ChatId, message, parseMode,
-                        replyMarkup: keyboard, disableNotification: true);
-                    user.Session?.Messages.Add(result.MessageId);
-                    return result;
+                    try
+                    {
+                        await user.ClearChat();
+                        var result = await Bot.Client.SendTextMessageAsync(user.ChatId, message, parseMode,
+                            replyMarkup: keyboard, disableNotification: true);
+                        user.Session?.Messages.Add(result.MessageId);
+                        return result;
+                    }
+                    catch (Exception)
+                    {
+                        LogOutWarning("Can't edit text message " + e.Message);
+                    }
                 }
-            }
             return new Message();
         }
+
+        public static async Task SendMessage(UserEntity user, string message, IReplyMarkup keyboard = null,
+            ParseMode? parseMode = null, bool addToList = false)
+        {
+            if (!user.IsBlocked)
+                try
+                {
+                    var result = await Bot.Client.SendTextMessageAsync(user.ChatId, message, parseMode,
+                        replyMarkup: keyboard, disableNotification: true);
+                    if (addToList) user.Session.Messages.Add(result.MessageId);
+                } catch (Exception) { /**/ }
+        }
         
         /* Метод для отправки стикера
          user - пользователь, которому необходимо отправить сообщение
          fileId - id стикера, расположенного на серверах телеграм */
         public static async Task<Message> SendSticker(UserEntity user, string fileId, IReplyMarkup keyboard = null)
         {
-            try
-            {
-                if (!user.IsBlocked)
+            if (!user.IsBlocked)
+                try
                 {
-                    await user.ClearChat();
-                    var result = await Bot.Client.SendStickerAsync(user.ChatId, fileId, true, replyMarkup: keyboard);
-                    user.Session.StickerMessages.Add(result.MessageId);
-                    return result;
+                        await user.ClearChat();
+                        var result = await Bot.Client.SendStickerAsync(user.ChatId, fileId, true, replyMarkup: keyboard);
+                        user.Session.StickerMessages.Add(result.MessageId);
+                        return result;
+                }
+                catch (Exception e)
+                {
+                    LogOutWarning("Can't send sticker " + e.Message);
                 }
-            }
-            catch (Exception e)
-            {
-                LogOutWarning("Can't send sticker " + e.Message);
-            }
             return new Message();
         }
 
@@ -145,18 +157,16 @@ namespace CardCollector.Controllers
          keyboard - новая клавиатура, которую надо добавить к сообщению */
         public static async Task<Message> EditReplyMarkup(UserEntity user, InlineKeyboardMarkup keyboard, int messageId = -1)
         {
-            try
-            {
-                if (!user.IsBlocked)
+            if (!user.IsBlocked)
+                try
                 {
                     var msgId = messageId != -1 ? messageId : user.Session.Messages.Last();
                     return await Bot.Client.EditMessageReplyMarkupAsync(user.ChatId, msgId, keyboard);
                 }
-            }
-            catch (Exception e)
-            {
-                LogOutWarning("Can't edit reply markup " + e.Message);
-            }
+                catch (Exception e)
+                {
+                    LogOutWarning("Can't edit reply markup " + e.Message);
+                }
             return new Message();
         }
         
@@ -177,11 +187,11 @@ namespace CardCollector.Controllers
         /* Метод для удаления сообщения
          user - пользователь, которому необходимо удалить сообщение
          messageId - Id сообщения */
-        public static async Task DeleteMessage(UserEntity user, int messageId, bool deleteFromList = true)
+        public static async Task DeleteMessage(UserEntity user, int messageId)
         {
             try
             {
-                if (deleteFromList) user.Session.Messages.Remove(messageId);
+                user.Session.Messages.Remove(messageId);
                 if (!user.IsBlocked)
                     await Bot.Client.DeleteMessageAsync(user.ChatId, messageId);
             }
@@ -191,26 +201,6 @@ namespace CardCollector.Controllers
             }
         }
 
-        /* Метод для отправки изображения
-         user - пользователь, которому необходимо отправить сообщение
-         inputOnlineFile - фото, которое необходимо отправить
-         message - текст сообщения
-         keyboard - клавиатура, которую надо добавить к сообщению */
-        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, fileId, message, replyMarkup: keyboard, disableNotification: true);
-            }
-            catch (Exception e)
-            {
-                LogOutWarning("Can't send photo " + e.Message);
-            }
-            return new Message();
-        }
-
         /* Метод для ответа на запрос @имя_бота
          queryId - Id запроса
          results - массив объектов InlineQueryResult */
@@ -225,9 +215,8 @@ namespace CardCollector.Controllers
             string payload, IEnumerable<LabeledPrice> prices, int maxTip = 0, IEnumerable<int> tips = null,
             InlineKeyboardMarkup keyboard = null, Currency currency = Currency.USD)
         {
-            try
-            {
-                if (!user.IsBlocked)
+            if (!user.IsBlocked)
+                try
                 {
                     await user.ClearChat();
                     var result = await Bot.Client.SendInvoiceAsync(user.ChatId, title, description, payload,
@@ -236,11 +225,10 @@ namespace CardCollector.Controllers
                     user.Session.Messages.Add(result.MessageId);
                     return result;
                 }
-            }
-            catch (Exception e)
-            {
-                LogOutWarning("Can't send photo " + e.Message);
-            }
+                catch (Exception e)
+                {
+                    LogOutWarning("Can't send photo " + e.Message);
+                }
             return new Message();
         }
     }

+ 2 - 0
CardCollector/DataBase/CardCollectorDatabase.cs

@@ -68,6 +68,8 @@ namespace CardCollector.DataBase
         public DbSet<PackEntity> Packs { get; set; }
         public DbSet<SpecialOfferUsers> SpecialOfferUsers { get; set; }
         public DbSet<SessionToken> SessionTokens { get; set; }
+        public DbSet<UserLevel> UserLevel { get; set; }
+        public DbSet<Level> Levels { get; set; }
 
 
         /* Конфигурация подключения к БД */

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

@@ -31,7 +31,7 @@ namespace CardCollector.DataBase.Entity
             var user = await UserDao.GetById(Trader);
             var sticker = await StickerDao.GetById(StickerId);
             var gemsSum = (int)(Price * count * 0.7);
-            await MessageController.SendMessage(user, $"{Messages.you_sold} {sticker.Title} {count}{Text.items}" +
+            await MessageController.EditMessage(user, $"{Messages.you_sold} {sticker.Title} {count}{Text.items}" +
                                                       $"\n{Messages.you_collected} {gemsSum}{Text.gem}");
             user.Cash.Gems += gemsSum;
             await CashDao.Save();

+ 60 - 0
CardCollector/DataBase/Entity/Level.cs

@@ -0,0 +1,60 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Threading.Tasks;
+using CardCollector.DataBase.EntityDao;
+using CardCollector.Resources;
+
+namespace CardCollector.DataBase.Entity
+{
+    [Table("levels")]
+    public class Level
+    {
+        [Key] [Column("id"), MaxLength(32)] public int Id { get; set; }
+        [Column("level_value"), MaxLength(32)] public int LevelValue { get; set; } = 0;
+        [Column("level_exp_goal"), MaxLength(127)] public long LevelExpGoal { get; set; } = 0;
+        [Column("level_reward"), MaxLength(512)] public string JSONLevelReward { get; set; } = "{}";
+
+        public LevelReward GetRewardInstance()
+        {
+            try {
+                return Utilities.FromJson<LevelReward>(JSONLevelReward);
+            } catch (Exception e) {
+                Logs.LogOutError(e);
+                return null;
+            }
+        }
+
+        [NotMapped]
+        public class LevelReward
+        {
+            public int? CashCapacity = null;
+            public int? RandomPacks = null;
+            public int? RandomStickerTier = null;
+
+            public async Task<string> GetReward(UserEntity user)
+            {
+                var rewardText = "";
+                if (CashCapacity is { } capacity)
+                {
+                    user.Cash.MaxCapacity += capacity;
+                    rewardText += $"\n{Text.cash_capacity_increased} +{capacity}{Text.coin}";
+                }
+                if (RandomPacks is { } packs)
+                {
+                    var userPack = await UserPacksDao.GetOne(user.Id, 1);
+                    userPack.Count += packs;
+                    rewardText += $"\n{Text.random_packs_added} {packs}{Text.items}";
+                }
+                if (RandomStickerTier is { } tier)
+                {
+                    var stickers = await StickerDao.GetListWhere(item => item.Tier == tier);
+                    var sticker = stickers[Utilities.rnd.Next(stickers.Count)];
+                    await UserStickerRelationDao.AddSticker(user, sticker);
+                    rewardText += $"\n{Text.random_sticker_added}\n{sticker}";
+                }
+                return rewardText;
+            }
+        }
+    }
+}

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

@@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
 using System.Linq;
 using System.Threading.Tasks;
+using CardCollector.Controllers;
 using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
 using CardCollector.Session;
@@ -32,6 +33,8 @@ namespace CardCollector.DataBase.Entity
         
         /* Счет пользователя */
         [NotMapped] public CashEntity Cash { get; set; }
+        /*Уровень пользователя*/
+        [NotMapped] public UserLevel CurrentLevel { get; set; }
         
         /* Стикеры пользователя */
         [NotMapped] public Dictionary<string, UserStickerRelationEntity> Stickers { get; set; }
@@ -60,6 +63,24 @@ namespace CardCollector.DataBase.Entity
             return await AuctionDiscount5.IsApplied(Stickers) ? 5 : 0;
         }
 
+        public async Task GiveExp(long count)
+        {
+            CurrentLevel.CurrentExp += count;
+            CurrentLevel.TotalExp += count;
+            var levelInfo = await LevelDao.GetLevel(CurrentLevel.Level + 1);
+            if (levelInfo?.LevelExpGoal <= CurrentLevel.CurrentExp) await ClearChat();
+            while (levelInfo?.LevelExpGoal <= CurrentLevel.CurrentExp)
+            {
+                CurrentLevel.CurrentExp -= levelInfo.LevelExpGoal;
+                CurrentLevel.Level++;
+                var levelReward = levelInfo.GetRewardInstance();
+                var message = $"{Messages.congratulation_new_level} {CurrentLevel.Level}" +
+                              $"\n{await levelReward.GetReward(this)}";
+                await MessageController.SendMessage(this, message);
+                levelInfo = await LevelDao.GetLevel(CurrentLevel.Level + 1);
+            }
+        }
+
         public UserEntity()
         {
             Session = new UserSession(this);

+ 14 - 0
CardCollector/DataBase/Entity/UserLevel.cs

@@ -0,0 +1,14 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace CardCollector.DataBase.Entity
+{
+    [Table("user_level")]
+    public class UserLevel
+    {
+        [Key] [Column("id"), MaxLength(127)] public long UserId { get; set; }
+        [Column("level"), MaxLength(32)] public int Level { get; set; } = 0;
+        [Column("current_exp"), MaxLength(127)] public long CurrentExp { get; set; } = 0;
+        [Column("total_exp"), MaxLength(127)] public long TotalExp { get; set; } = 0;
+    }
+}

+ 34 - 0
CardCollector/DataBase/EntityDao/LevelDao.cs

@@ -0,0 +1,34 @@
+#nullable enable
+using System.Threading.Tasks;
+using CardCollector.DataBase.Entity;
+using Microsoft.EntityFrameworkCore;
+
+namespace CardCollector.DataBase.EntityDao
+{
+    public class LevelDao
+    {
+        private static readonly CardCollectorDatabase Instance = CardCollectorDatabase.GetSpecificInstance(typeof(UserLevelDao));
+        /* Таблица cash в представлении EntityFramework */
+        private static readonly DbSet<Level> Table = Instance.Levels;
+        
+        /* Получение объекта по Id */
+        public static async Task<Level?> GetLevel(int level)
+        {
+            return await Table.FirstOrDefaultAsync(item => item.LevelValue == level);
+        }
+
+        /* Добавление нового объекта в систему */
+        /*private static async Task<Level> AddNew(long userId)
+        {
+            var level = new Level();
+            var result = await Table.AddAsync(level);
+            await Instance.SaveChangesAsync();
+            return result.Entity;
+        }*/
+
+        public static async Task Save()
+        {
+            await Instance.SaveChangesAsync();
+        }
+    }
+}

+ 15 - 2
CardCollector/DataBase/EntityDao/PacksDao.cs

@@ -1,4 +1,6 @@
-using System.Collections.Generic;
+#nullable enable
+using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
 using CardCollector.DataBase.Entity;
@@ -29,7 +31,18 @@ namespace CardCollector.DataBase.EntityDao
 
         public static async Task<List<PackEntity>> GetAll()
         {
-            return (await Table.WhereAsync(item => item.Id is not 1)).ToList();
+            var list = (await Table.WhereAsync(item => item.Id is not 1)).ToList();
+            list.Sort(new AuthorComparer());
+            return list;
+        }
+
+        private class AuthorComparer : IComparer<PackEntity>
+        {
+            public int Compare(PackEntity? x, PackEntity? y)
+            {
+                if (x is null || y is null) return 0;
+                return string.Compare(x.Author, y.Author, StringComparison.CurrentCultureIgnoreCase);
+            }
         }
     }
 }

+ 2 - 1
CardCollector/DataBase/EntityDao/UserDao.cs

@@ -39,6 +39,7 @@ namespace CardCollector.DataBase.EntityDao
                 /* Собираем объект пользователя */
                 result.Cash = await CashDao.GetById(user.Id);
                 result.Stickers = await UserStickerRelationDao.GetListById(user.Id);
+                result.CurrentLevel = await UserLevelDao.GetById(user.Id);
                 result.Session.InitNewModule<FiltersModule>();
                 result.Session.InitNewModule<DefaultModule>();
                 
@@ -95,7 +96,7 @@ namespace CardCollector.DataBase.EntityDao
             {
                 await user.Session.EndSession();
                 ActiveUsers.Remove(id);
-                await MessageController.SendMessage(user, Messages.bot_turning_off);
+                await MessageController.EditMessage(user, Messages.bot_turning_off);
             }
         }
 

+ 34 - 0
CardCollector/DataBase/EntityDao/UserLevelDao.cs

@@ -0,0 +1,34 @@
+using System.Threading.Tasks;
+using CardCollector.DataBase.Entity;
+using Microsoft.EntityFrameworkCore;
+
+namespace CardCollector.DataBase.EntityDao
+{
+    public class UserLevelDao
+    {
+        private static readonly CardCollectorDatabase Instance = CardCollectorDatabase.GetSpecificInstance(typeof(UserLevelDao));
+        /* Таблица cash в представлении EntityFramework */
+        private static readonly DbSet<UserLevel> Table = Instance.UserLevel;
+        
+        /* Получение объекта по Id */
+        public static async Task<UserLevel> GetById(long userId)
+        {
+            var user = await Table.FirstOrDefaultAsync(item => item.UserId == userId);
+            return user ?? await AddNew(userId);
+        }
+
+        /* Добавление нового объекта в систему */
+        private static async Task<UserLevel> AddNew(long userId)
+        {
+            var userLevel = new UserLevel { UserId = userId };
+            var result = await Table.AddAsync(userLevel);
+            await Instance.SaveChangesAsync();
+            return result.Entity;
+        }
+
+        public static async Task Save()
+        {
+            await Instance.SaveChangesAsync();
+        }
+    }
+}

+ 3 - 3
CardCollector/DataBase/EntityDao/UserStickerRelationDao.cs

@@ -28,7 +28,7 @@ namespace CardCollector.DataBase.EntityDao
         }
 
         /* Добавляет новое отношение в таблицу */
-        public static async Task<UserStickerRelationEntity> AddNew(UserEntity user, StickerEntity sticker, int count)
+        public static async Task<UserStickerRelationEntity> AddSticker(UserEntity user, StickerEntity sticker, int count = 1)
         {
             if (user.Stickers.ContainsKey(sticker.Md5Hash))
             {
@@ -46,11 +46,11 @@ namespace CardCollector.DataBase.EntityDao
             {
                 case Effect.PiggyBank200:
                     user.Cash.MaxCapacity += 200;
-                    await MessageController.SendMessage(user, Messages.effect_PiggyBank200);
+                    await MessageController.EditMessage(user, Messages.effect_PiggyBank200);
                     break;
                 case Effect.Diamonds25Percent:
                     user.Cash.Gems += (int)(user.Cash.Gems * 0.25);
-                    await MessageController.SendMessage(user, Messages.effect_Diamonds25Percent);
+                    await MessageController.EditMessage(user, Messages.effect_Diamonds25Percent);
                     break;
                 case Effect.Random1Pack5Day:
                     relation.AdditionalData = DateTime.Today.ToString(CultureInfo.CurrentCulture);

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

@@ -249,6 +249,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to AAY.
+        /// </summary>
+        internal static string give_exp {
+            get {
+                return ResourceManager.GetString("give_exp", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to AAS.
         /// </summary>
@@ -375,15 +384,6 @@ namespace CardCollector.Resources {
             }
         }
         
-        /// <summary>
-        ///   Looks up a localized string similar to AAY.
-        /// </summary>
-        internal static string send_sticker {
-            get {
-                return ResourceManager.GetString("send_sticker", resourceCulture);
-            }
-        }
-        
         /// <summary>
         ///   Looks up a localized string similar to AAZ.
         /// </summary>

+ 1 - 1
CardCollector/Resources/Command.resx

@@ -42,7 +42,7 @@
     <data name="unlimited_stickers" xml:space="preserve">
         <value>ABD</value>
     </data>
-    <data name="send_sticker" xml:space="preserve">
+    <data name="give_exp" xml:space="preserve">
         <value>AAY</value>
     </data>
     <data name="select_sticker" xml:space="preserve">

+ 2 - 0
CardCollector/Resources/Constants.cs

@@ -32,5 +32,7 @@ namespace CardCollector.Resources
         public static readonly TimeSpan DailyTaskReset = DEBUG ? new TimeSpan(20, 30, 0) : new TimeSpan(10, 0, 0);
         /* Время выдачи наград за пассивные эффекты стикеров */
         public static readonly TimeSpan DailyStickerRewardCheck = DEBUG ? new TimeSpan(20, 35, 0) : new TimeSpan(11, 0, 0);
+        /* Сброс отправленных в беседы стикеров */
+        public static readonly TimeSpan ResetGroupStickersExp = DEBUG ? new TimeSpan(20, 35, 0) : new TimeSpan(10, 0, 0);
     }
 }

+ 97 - 7
CardCollector/Resources/Messages.Designer.cs

@@ -79,7 +79,7 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Автор:.
+        ///   Looks up a localized string similar to Автор.
         /// </summary>
         internal static string author {
             get {
@@ -114,6 +114,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to за покупку алмазов в магазине.
+        /// </summary>
+        internal static string buy_gems {
+            get {
+                return ResourceManager.GetString("buy_gems", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Невозможно изменить количество!.
         /// </summary>
@@ -141,6 +150,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Размер копилки.
+        /// </summary>
+        internal static string cash_capacity {
+            get {
+                return ResourceManager.GetString("cash_capacity", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Выберите автора из списка ниже:.
         /// </summary>
@@ -268,7 +286,34 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Текущие примененные фильтры:.
+        ///   Looks up a localized string similar to Поздравляем! Вы достигли уровня.
+        /// </summary>
+        internal static string congratulation_new_level {
+            get {
+                return ResourceManager.GetString("congratulation_new_level", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Количество отправлений за этот день в беседу.
+        /// </summary>
+        internal static string count_sends_per_day {
+            get {
+                return ResourceManager.GetString("count_sends_per_day", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Опыт.
+        /// </summary>
+        internal static string current_exp {
+            get {
+                return ResourceManager.GetString("current_exp", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Текущие примененные фильтры.
         /// </summary>
         internal static string current_filters {
             get {
@@ -358,7 +403,7 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Эмоция:.
+        ///   Looks up a localized string similar to Эмоция.
         /// </summary>
         internal static string emoji {
             get {
@@ -429,6 +474,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Уровень.
+        /// </summary>
+        internal static string level {
+            get {
+                return ResourceManager.GetString("level", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Наименьшая цена:.
         /// </summary>
@@ -564,7 +618,7 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Стоимость:.
+        ///   Looks up a localized string similar to Стоимость.
         /// </summary>
         internal static string price {
             get {
@@ -591,7 +645,7 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Установите фильтры кнопками ниже:.
+        ///   Looks up a localized string similar to Установите фильтры кнопками ниже.
         /// </summary>
         internal static string select_filter {
             get {
@@ -599,6 +653,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to за отправку стикера в беседу..
+        /// </summary>
+        internal static string send_sticker {
+            get {
+                return ResourceManager.GetString("send_sticker", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Выберите одну из опций ниже:.
         /// </summary>
@@ -609,7 +672,7 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Сортировка:.
+        ///   Looks up a localized string similar to Сортировка.
         /// </summary>
         internal static string sorting {
             get {
@@ -663,7 +726,16 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Тир:.
+        ///   Looks up a localized string similar to Спасибо за покупку алмазов!.
+        /// </summary>
+        internal static string thanks_for_buying_gems {
+            get {
+                return ResourceManager.GetString("thanks_for_buying_gems", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Тир.
         /// </summary>
         internal static string tier {
             get {
@@ -731,6 +803,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Чтобы увеличить получаемый опыт, Вы можете добавить бота в беседу..
+        /// </summary>
+        internal static string you_can_add_bot_to_conversation {
+            get {
+                return ResourceManager.GetString("you_can_add_bot_to_conversation", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Получено прибыли со стикеров:.
         /// </summary>
@@ -740,6 +821,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Вы получили.
+        /// </summary>
+        internal static string you_gained {
+            get {
+                return ResourceManager.GetString("you_gained", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Вы получили.
         /// </summary>

+ 37 - 7
CardCollector/Resources/Messages.resx

@@ -22,28 +22,28 @@
         <value>Добро пожаловать! Этот бот позволяет вам коллекционировать стикеры. Вы можете выбрать одну из опций ниже. Напишите /help, чтобы получить подробную справку.</value>
     </data>
     <data name="current_filters" xml:space="preserve">
-        <value>Текущие примененные фильтры:</value>
+        <value>Текущие примененные фильтры</value>
     </data>
     <data name="select_filter" xml:space="preserve">
-        <value>Установите фильтры кнопками ниже:</value>
+        <value>Установите фильтры кнопками ниже</value>
     </data>
     <data name="author" xml:space="preserve">
-        <value>Автор:</value>
+        <value>Автор</value>
     </data>
     <data name="tier" xml:space="preserve">
-        <value>Тир:</value>
+        <value>Тир</value>
     </data>
     <data name="emoji" xml:space="preserve">
-        <value>Эмоция:</value>
+        <value>Эмоция</value>
     </data>
     <data name="sorting" xml:space="preserve">
-        <value>Сортировка:</value>
+        <value>Сортировка</value>
     </data>
     <data name="all" xml:space="preserve">
         <value>(все)</value>
     </data>
     <data name="price" xml:space="preserve">
-        <value>Стоимость:</value>
+        <value>Стоимость</value>
     </data>
     <data name="choose_author" xml:space="preserve">
         <value>Выберите автора из списка ниже:</value>
@@ -276,4 +276,34 @@
     <data name="or_enter_another_sum" xml:space="preserve">
         <value>Или введите другую сумму:</value>
     </data>
+    <data name="congratulation_new_level" xml:space="preserve">
+        <value>Поздравляем! Вы достигли уровня</value>
+    </data>
+    <data name="level" xml:space="preserve">
+        <value>Уровень</value>
+    </data>
+    <data name="current_exp" xml:space="preserve">
+        <value>Опыт</value>
+    </data>
+    <data name="cash_capacity" xml:space="preserve">
+        <value>Размер копилки</value>
+    </data>
+    <data name="you_gained" xml:space="preserve">
+        <value>Вы получили</value>
+    </data>
+    <data name="send_sticker" xml:space="preserve">
+        <value>за отправку стикера в беседу.</value>
+    </data>
+    <data name="you_can_add_bot_to_conversation" xml:space="preserve">
+        <value>Чтобы увеличить получаемый опыт, Вы можете добавить бота в беседу.</value>
+    </data>
+    <data name="buy_gems" xml:space="preserve">
+        <value>за покупку алмазов в магазине</value>
+    </data>
+    <data name="count_sends_per_day" xml:space="preserve">
+        <value>Количество отправлений за этот день в беседу</value>
+    </data>
+    <data name="thanks_for_buying_gems" xml:space="preserve">
+        <value>Спасибо за покупку алмазов!</value>
+    </data>
 </root>

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

@@ -204,6 +204,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Размер копилки увеличен:.
+        /// </summary>
+        internal static string cash_capacity_increased {
+            get {
+                return ResourceManager.GetString("cash_capacity_increased", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to 💰.
         /// </summary>
@@ -348,6 +357,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to опыта.
+        /// </summary>
+        internal static string exp {
+            get {
+                return ResourceManager.GetString("exp", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to От.
         /// </summary>
@@ -591,6 +609,24 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Получено случайных паков:.
+        /// </summary>
+        internal static string random_packs_added {
+            get {
+                return ResourceManager.GetString("random_packs_added", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Получен стикер:.
+        /// </summary>
+        internal static string random_sticker_added {
+            get {
+                return ResourceManager.GetString("random_sticker_added", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Выбрать.
         /// </summary>

+ 12 - 0
CardCollector/Resources/Text.resx

@@ -261,4 +261,16 @@
     <data name="open_more" xml:space="preserve">
         <value>Открыть ещё</value>
     </data>
+    <data name="cash_capacity_increased" xml:space="preserve">
+        <value>Размер копилки увеличен:</value>
+    </data>
+    <data name="random_packs_added" xml:space="preserve">
+        <value>Получено случайных паков:</value>
+    </data>
+    <data name="random_sticker_added" xml:space="preserve">
+        <value>Получен стикер:</value>
+    </data>
+    <data name="exp" xml:space="preserve">
+        <value>опыта</value>
+    </data>
 </root>

+ 7 - 7
CardCollector/Session/Modules/FiltersModule.cs

@@ -23,23 +23,23 @@ namespace CardCollector.Session.Modules
 
         public string ToString(UserState state)
         {
-            var text = $"{Messages.current_filters}\n" +
-                       $"{Messages.author} {(Filters[Command.authors_menu] is string author and not "" ? author : Messages.all)}\n" +
-                       $"{Messages.tier} {(Filters[Command.tier] is int tier and not -1 ? new string('⭐', tier) : Messages.all)}\n" +
-                       $"{Messages.emoji} {(Filters[Command.emoji] is string emoji and not "" ? emoji : Messages.all)}\n";
+            var text = $"{Messages.current_filters}:\n" +
+                       $"{Messages.author}: {(Filters[Command.authors_menu] is string author and not "" ? author : Messages.all)}\n" +
+                       $"{Messages.tier}: {(Filters[Command.tier] is int tier and not -1 ? new string('⭐', tier) : Messages.all)}\n" +
+                       $"{Messages.emoji}: {(Filters[Command.emoji] is string emoji and not "" ? emoji : Messages.all)}\n";
             switch (state)
             {
                 case UserState.AuctionMenu:
-                    text += $"{Messages.price} 💎 {Filters[Command.price_gems_from]} -" +
+                    text += $"{Messages.price}: 💎 {Filters[Command.price_gems_from]} -" +
                             $" {(Filters[Command.price_gems_to] is int g and not 0 ? g : "∞")}\n";
                     break;
                 case UserState.ShopMenu:
-                    text += $"{Messages.price} 💰 {Filters[Command.price_coins_from]} -" +
+                    text += $"{Messages.price}: 💰 {Filters[Command.price_coins_from]} -" +
                             $" {(Filters[Command.price_coins_to] is int c and not 0 ? c : "∞")}\n";
                     break;
             }
 
-            text += $"{Messages.sorting} {Filters[Command.sort]}\n\n{Messages.select_filter}";
+            text += $"{Messages.sorting} {Filters[Command.sort]}\n\n{Messages.select_filter}:";
             return text;
         }
         

+ 5 - 6
CardCollector/Session/Session.cs

@@ -34,6 +34,8 @@ namespace CardCollector.Session
             this.user = user;
         }
 
+        public bool ChosenResultWithMessage = false;
+
         public T InitNewModule<T>() where T : Module
         {
             if (!Modules.ContainsKey(typeof(T))) Modules.Add(typeof(T), Activator.CreateInstance<T>());
@@ -73,18 +75,15 @@ namespace CardCollector.Session
         public async Task ClearMessages()
         {
             foreach (var messageId in Messages.ToList())
-                await MessageController.DeleteMessage(user, messageId, false);
+                await MessageController.DeleteMessage(user, messageId);
             foreach (var messageId in StickerMessages.ToList())
-                await MessageController.DeleteMessage(user, messageId, false);
-            StickerMessages.Clear();
-            Messages.Clear();
+                await MessageController.DeleteMessage(user, messageId);
         }
 
         public async Task ClearStickers()
         {
             foreach (var messageId in StickerMessages.ToList())
-                await MessageController.DeleteMessage(user, messageId, false);
-            StickerMessages.Clear();
+                await MessageController.DeleteMessage(user, messageId);
         }
 
         public async Task EndSession()

+ 2 - 2
CardCollector/StickerEffects/EffectFunctions.cs

@@ -46,7 +46,7 @@ namespace CardCollector.StickerEffects
                     foreach (var (sticker, count) in stickerList)
                     {
                         generatedMessage += $"\n{sticker.Title} {count}{Text.items}";
-                        await UserStickerRelationDao.AddNew(user, sticker, count);
+                        await UserStickerRelationDao.AddSticker(user, sticker, count);
                     }
                     await MessageController.SendMessage(user,
                         $"{Messages.effect_RandomSticker2Tier3Day}{generatedMessage}");
@@ -68,7 +68,7 @@ namespace CardCollector.StickerEffects
                     foreach (var (sticker, count) in stickerList)
                     {
                         generatedMessage += $"\n{sticker.Title} {count}{Text.items}";
-                        await UserStickerRelationDao.AddNew(user, sticker, count);
+                        await UserStickerRelationDao.AddSticker(user, sticker, count);
                     }
                     await MessageController.SendMessage(user,
                         $"{Messages.effect_RandomSticker1Tier3Day}{generatedMessage}");

+ 4 - 0
CardCollector/Utilities.cs

@@ -17,6 +17,10 @@ namespace CardCollector
         {
             return Newtonsoft.Json.JsonConvert.SerializeObject(obj);
         }
+        public static T FromJson<T>(string json)
+        {
+            return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
+        }
         
         public static string CreateMd5(string input)
         {