Browse Source

Complete filtering and sorting commands

Tigran 4 năm trước cách đây
mục cha
commit
df103eb712

+ 2 - 2
CardCollector.sln.DotSettings.user

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

+ 10 - 7
CardCollector/Commands/CallbackQuery/BackToFiltersMenu.cs

@@ -1,4 +1,5 @@
 using System.Threading.Tasks;
+using CardCollector.Commands.Message;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
@@ -11,16 +12,18 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.back;
         public override async Task Execute()
         {
+            /* Удаляем пользователя из очереди */
+            EnterEmojiMessage.RemoveFromQueue(User.Id);
             /* Формируем сообщение с имеющимися фильтрами у пользователя */
             var text = $"{Messages.current_filters}\n" +
-                       $"{Messages.author} {(User.Filters["author"].Equals("") ? Messages.all : User.Filters["author"])}\n" +
-                       $"{Messages.tier} {(User.Filters["tier"].Equals(-1) ? Messages.all : new string('⭐', (int)User.Filters["tier"]))}\n" +
-                       $"{Messages.emoji} {(User.Filters["emoji"].Equals("") ? Messages.all : User.Filters["emoji"])}\n" +
-                       $"{Messages.price} {User.Filters["price"]}-∞\n" +
-                       $"{Messages.sorting} {User.Filters["sorting"]}\n" +
-                       $"\n{Messages.select_filter}";
+                       $"{Messages.author} {(User.Filters[Command.author].Equals("") ? Messages.all : User.Filters[Command.author])}\n" +
+                       $"{Messages.tier} {(User.Filters[Command.tier].Equals(-1) ? Messages.all : new string('⭐', (int) User.Filters[Command.tier]))}\n" +
+                       $"{Messages.emoji} {(User.Filters[Command.emoji].Equals("") ? Messages.all : User.Filters[Command.emoji])}\n";
+            if (User.State != UserState.CollectionMenu) text += $"{Messages.price} {User.Filters[Command.price]} -" +
+                                                                $" {(User.Filters[Command.price_to] is int p && p != 0 ? p : "∞")}\n";
+            text += $"{Messages.sorting} {User.Filters[Command.sort]}\n\n{Messages.select_filter}";
             /* Редактируем сообщение */
-            await MessageController.EditMessage(User, CallbackMessageId, text, Keyboard.SortingOptions);
+            await MessageController.EditMessage(User, CallbackMessageId, text, Keyboard.GetSortingMenu(User.State));
         }
         
         public BackToFiltersMenu() { }

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

@@ -32,8 +32,16 @@ namespace CardCollector.Commands.CallbackQuery
             new AuthorCallback(),
             /* Кнопка "Тир" */
             new TierCallback(),
+            /* Кнопка "Эмоция" */
+            new EmojiCallback(),
+            /* Кнопка "Цена" */
+            new PriceCallback(),
+            /* Кнопка "Сортировка" */
+            new SortCallback(),
             /* Установка фильтра */
             new SetFilterCallback(),
+            /* Команда отмены */
+            new CancelCallback(),
             
             /* Отмена в момент выбора "значения фильтра", не в самом меню */
             new BackToFiltersMenu(),

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

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

+ 22 - 0
CardCollector/Commands/CallbackQuery/EmojiCallback.cs

@@ -0,0 +1,22 @@
+using System.Threading.Tasks;
+using CardCollector.Commands.Message;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    public class EmojiCallback : CallbackQuery
+    {
+        protected override string CommandText => Command.emoji;
+        public override async Task Execute()
+        {
+            EnterEmojiMessage.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) { }
+    }
+}

+ 20 - 0
CardCollector/Commands/CallbackQuery/PriceCallback.cs

@@ -0,0 +1,20 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    public class PriceCallback : CallbackQuery
+    {
+        protected override string CommandText => Command.price;
+        public override async Task Execute()
+        {
+            await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_price, Keyboard.PriceOptions);
+        }
+
+        public PriceCallback() { }
+        public PriceCallback(UserEntity user, Update update) : base(user, update) { }
+    }
+}

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

@@ -1,5 +1,6 @@
 using System;
 using System.Threading.Tasks;
+using CardCollector.Commands.Message;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
@@ -12,6 +13,7 @@ namespace CardCollector.Commands.CallbackQuery
         protected override string CommandText => Command.set;
         public override async Task Execute()
         {
+            EnterEmojiMessage.RemoveFromQueue(User.Id);
             var result = CallbackData.Split('=');
             /* Команду set мы получаем в виде set=<key>=<value>, соответственно аргументы 1 и 2 это ключ и значение словаря */
             User.Filters[result[1]] = Convert.ChangeType(result[2], User.Filters[result[1]].GetType());

+ 20 - 0
CardCollector/Commands/CallbackQuery/SortCallback.cs

@@ -0,0 +1,20 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    public class SortCallback : CallbackQuery
+    {
+        protected override string CommandText => Command.sort;
+        public override async Task Execute()
+        {
+            await MessageController.EditMessage(User, CallbackMessageId, Messages.choose_sort, Keyboard.SortOptions);
+        }
+
+        public SortCallback() { }
+        public SortCallback(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 0 - 1
CardCollector/Commands/Message/AuctionMessage.cs

@@ -1,5 +1,4 @@
 using System.Threading.Tasks;
-using CardCollector.Commands.Custom;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;

+ 2 - 11
CardCollector/Commands/Message/CollectionMessage.cs

@@ -16,17 +16,8 @@ namespace CardCollector.Commands.Message
             await User.ClearChat();
             /* Переводим состояние пользователя в меню коллекции */
             User.State = UserState.CollectionMenu;
-            /* Формируем сообщение с имеющимися фильтрами у пользователя */
-            var text = $"{Messages.current_filters}\n" +
-                       $"{Messages.author} {(User.Filters["author"].Equals("") ? Messages.all : User.Filters["author"])}\n" +
-                       $"{Messages.tier} {(User.Filters["tier"].Equals(-1) ? Messages.all : new string('⭐', (int)User.Filters["tier"]))}\n" +
-                       $"{Messages.emoji} {(User.Filters["emoji"].Equals("") ? Messages.all : User.Filters["emoji"])}\n" +
-                       $"{Messages.sorting} {User.Filters["sorting"]}\n" +
-                       $"\n{Messages.select_filter}";
-            /* Отправляем сообщение */
-            var message = await MessageController.SendMessage(User, text, Keyboard.SortingOptions);
-            /* Добавляем это сообщение в список для удаления */
-            User.Messages.Add(message.MessageId);
+            /* Отображаем сообщение с фильтрами */
+            await new ShowFiltersMenu(User, Update).Execute();
         }
         
         public CollectionMessage() { }

+ 69 - 0
CardCollector/Commands/Message/EnterEmojiMessage.cs

@@ -0,0 +1,69 @@
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.Message
+{
+    public class EnterEmojiMessage : Message
+    {
+        protected override string CommandText => "";
+
+        private const string oneEmojiPattern =
+            "^\\u00a9$|^\\u00ae$|^[\\u2000-\\u3300]$|^\\ud83c[\\ud000-\\udfff]$|^\\ud83d[\\ud000-\\udfff]$|^\\ud83e[\\ud000-\\udfff]$";
+        private const string onlyEmojiPattern =
+            "\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff]";
+        
+        /* Список пользователей, от которых ожидается ввод эмоджи ключ - id пользователя, значение - сообщение с меню */
+        private static readonly Dictionary<long, int> Queue = new ();
+        public override async Task Execute()
+        {
+            var input = "" + Update.Message!.Text; // это чтоб варн не показывлся, тут никогда не null, так как в Factory все уже проверено
+            /* если пользователь ввел что-то кроме эмодзи */
+            if (!Regex.IsMatch(input, onlyEmojiPattern))
+                await MessageController.EditMessage(User, Queue[User.Id], Messages.please_enter_emoji, Keyboard.EmojiOptions);
+            /* если пользователь ввел несколько эмодзи или эмодзи и текст */
+            else if (!Regex.IsMatch(input, oneEmojiPattern))
+                await MessageController.EditMessage(User, Queue[User.Id], Messages.enter_only_one_emoji, Keyboard.EmojiOptions);
+            else
+            {
+                User.Filters[Command.emoji] = input;
+                /* Формируем сообщение с имеющимися фильтрами у пользователя */
+                var text = $"{Messages.current_filters}\n" +
+                           $"{Messages.author} {(User.Filters[Command.author].Equals("") ? Messages.all : User.Filters[Command.author])}\n" +
+                           $"{Messages.tier} {(User.Filters[Command.tier].Equals(-1) ? Messages.all : new string('⭐', (int) User.Filters[Command.tier]))}\n" +
+                           $"{Messages.emoji} {(User.Filters[Command.emoji].Equals("") ? Messages.all : User.Filters[Command.emoji])}\n";
+                if (User.State != UserState.CollectionMenu) text += $"{Messages.price} {User.Filters[Command.price]} -" +
+                                                                    $" {(User.Filters[Command.price_to] is int p && p != 0 ? p : "∞")}\n";
+                text += $"{Messages.sorting} {User.Filters[Command.sort]}\n\n{Messages.select_filter}";
+                /* Редактируем сообщение */
+                await MessageController.EditMessage(User, Queue[User.Id], text, Keyboard.GetSortingMenu(User.State));
+                Queue.Remove(User.Id);
+            }
+        }
+
+        /* Добавляем пользователя в очередь */
+        public static void AddToQueue(long userId, int messageId)
+        {
+            Queue.TryAdd(userId, messageId);
+        }
+
+        /* Удаляем пользователя из очереди */
+        public static void RemoveFromQueue(long userId)
+        {
+            Queue.Remove(userId);
+        }
+
+        /* Переопределяем метод, так как команда удовлетворяет условию, если пользователь находится в очереди */
+        protected internal override bool IsMatches(string command)
+        {
+            return User == null ? base.IsMatches(command) : Queue.ContainsKey(User.Id);
+        }
+
+        public EnterEmojiMessage() { }
+        public EnterEmojiMessage(UserEntity user, Update update) : base(user, update) { }
+    }
+}

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

@@ -35,6 +35,8 @@ namespace CardCollector.Commands.Message
             new ShopMessage(),
             // Команда "Аукцион"
             new AuctionMessage(),
+            // Ожидание ввода эмоджи
+            new EnterEmojiMessage(),
             
             // Команда "Показать пример"
             new ShowSampleMessage()

+ 2 - 12
CardCollector/Commands/Message/ShopMessage.cs

@@ -16,18 +16,8 @@ namespace CardCollector.Commands.Message
             await User.ClearChat();
             /* Переводим состояние пользователя в меню магазина */
             User.State = UserState.ShopMenu;
-            /* Формируем сообщение с имеющимися фильтрами у пользователя */
-            var text = $"{Messages.current_filters}\n" +
-                       $"{Messages.author} {(User.Filters["author"].Equals("") ? Messages.all : User.Filters["author"])}\n" +
-                       $"{Messages.tier} {(User.Filters["tier"].Equals(-1) ? Messages.all : new string('⭐', (int)User.Filters["tier"]))}\n" +
-                       $"{Messages.emoji} {(User.Filters["emoji"].Equals("") ? Messages.all : User.Filters["emoji"])}\n" +
-                       $"{Messages.price} {User.Filters["price"]}-∞\n" +
-                       $"{Messages.sorting} {User.Filters["sorting"]}\n" +
-                       $"\n{Messages.select_filter}";
-            /* Отправляем сообщение */
-            var message = await MessageController.SendMessage(User, text, Keyboard.SortingOptions);
-            /* Добавляем это сообщение в список для удаления */
-            User.Messages.Add(message.MessageId);
+            /* Отображаем сообщение с фильтрами */
+            await new ShowFiltersMenu(User, Update).Execute();
         }
         
         public ShopMessage() { }

+ 9 - 9
CardCollector/Commands/Custom/ShowFiltersMenu.cs → CardCollector/Commands/Message/ShowFiltersMenu.cs

@@ -4,24 +4,24 @@ using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Custom
+namespace CardCollector.Commands.Message
 {
     /* Этот класс реализует отправку нового сообщения с фильтрами пользователя */
-    public class ShowFiltersMenu : Message.Message
+    public class ShowFiltersMenu : Message
     {
         protected override string CommandText => "Message";
         public override async Task Execute()
         {
             /* Формируем сообщение с имеющимися фильтрами у пользователя */
             var text = $"{Messages.current_filters}\n" +
-                       $"{Messages.author} {(User.Filters["author"].Equals("") ? Messages.all : User.Filters["author"])}\n" +
-                       $"{Messages.tier} {(User.Filters["tier"].Equals(-1) ? Messages.all : new string('⭐', (int)User.Filters["tier"]))}\n" +
-                       $"{Messages.emoji} {(User.Filters["emoji"].Equals("") ? Messages.all : User.Filters["emoji"])}\n" +
-                       $"{Messages.price} {User.Filters["price"]}-∞\n" +
-                       $"{Messages.sorting} {User.Filters["sorting"]}\n" +
-                       $"\n{Messages.select_filter}";
+                       $"{Messages.author} {(User.Filters[Command.author].Equals("") ? Messages.all : User.Filters[Command.author])}\n" +
+                       $"{Messages.tier} {(User.Filters[Command.tier].Equals(-1) ? Messages.all : new string('⭐', (int) User.Filters[Command.tier]))}\n" +
+                       $"{Messages.emoji} {(User.Filters[Command.emoji].Equals("") ? Messages.all : User.Filters[Command.emoji])}\n";
+            if (User.State != UserState.CollectionMenu) text += $"{Messages.price} {User.Filters[Command.price]} -" +
+                                                                $" {(User.Filters[Command.price_to] is int p && p != 0 ? p : "∞")}\n";
+            text += $"{Messages.sorting} {User.Filters[Command.sort]}\n\n{Messages.select_filter}";
             /* Отправляем сообщение */
-            var message = await MessageController.SendMessage(User, text, Keyboard.SortingOptions);
+            var message = await MessageController.SendMessage(User, text, Keyboard.GetSortingMenu(User.State));
             /* Добавляем это сообщение в список для удаления */
             User.Messages.Add(message.MessageId);
         }

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

@@ -1,4 +1,5 @@
-using System.ComponentModel.DataAnnotations;
+using System;
+using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
 
 namespace CardCollector.DataBase.Entity
@@ -27,6 +28,12 @@ namespace CardCollector.DataBase.Entity
         /* Доход от стикера в алмазах */
         [Column("income_gems"), MaxLength(32)] public int IncomeGems { get; set; } = 0;
         
+        /* Время, необходимое для получения дохода */
+        [Column("income_time"), MaxLength(32)] public int IncomeTime { get; set; } = 0;
+        
+        /* Стоимость стикера в магазине */
+        [Column("income_time"), MaxLength(32)] public int Price { get; set; } = 0;
+        
         /* Количество звезд стикера (редкость) */
         [Column("tier"), MaxLength(32)] public int Tier { get; set; } = 1;
         

+ 43 - 20
CardCollector/DataBase/Entity/UserEntity.cs

@@ -44,11 +44,12 @@ namespace CardCollector.DataBase.Entity
         /* Фильтры, примененные пользователем в меню коллекции/магазина/аукциона */
         [NotMapped] public readonly Dictionary<string, object> Filters = new()
         {
-            {"author", ""},
-            {"tier", -1},
-            {"emoji", ""},
-            {"sorting", SortingTypes.None},
-            {"price", 0},
+            {Command.author, ""},
+            {Command.tier, -1},
+            {Command.emoji, ""},
+            {Command.price, 0},
+            {Command.price_to, 0},
+            {Command.sort, SortingTypes.None},
         };
 
         /* Сообщения в чате пользователя */
@@ -67,41 +68,63 @@ namespace CardCollector.DataBase.Entity
         {
             /* Получаем список стикеров исходя из того, нужно ли для отладки получить бесконечные стикеры */
             var stickersList = Constants.UNLIMITED_ALL_STICKERS
-                ? await GetUnlimitedStickers(filter) : await GetUserStickers(filter);
+                ? await GetUnlimitedStickers(filter)
+                : State switch
+                {
+                    UserState.CollectionMenu => await GetUserStickers(filter),
+                    UserState.AuctionMenu => await GetAuctionStickers(filter),
+                    UserState.ShopMenu => await GetShopStickers(filter),
+                    _ => await GetUserStickers(filter)
+                };
             /* Если пользовательская сортировка не применена, то возвращаем реультат */
             if (!userFilterEnabled) return ToTelegramResults(stickersList, command);
             /* Фильтруем по автору */
-            if (Filters["author"] is not "")
-                stickersList = stickersList.Where(item => item.Author.Contains((string) Filters["author"]));
+            if (Filters[Command.author] is not "")
+                stickersList = stickersList.Where(item => item.Author.Contains((string) Filters[Command.author]));
             /* Фильтруем по тиру */
-            if (Filters["tier"] is not -1)
-                stickersList = stickersList.Where(item => item.Tier.Equals((int) Filters["tier"]));
+            if (Filters[Command.tier] is not -1)
+                stickersList = stickersList.Where(item => item.Tier.Equals((int) Filters[Command.tier]));
             /* Фильтруем по эмоции */
-            if (Filters["emoji"] is not "")
-                stickersList = stickersList.Where(item => item.Emoji.Contains((string) Filters["emoji"]));
-            /* Фильтруем по цене если пользователь не в меню коллекции */
-            if (Filters["emoji"] is not "" && State is not UserState.CollectionMenu)
-                stickersList = stickersList.Where(item => item.Emoji.Contains((string) Filters["emoji"]));
+            if (Filters[Command.emoji] is not "")
+                stickersList = stickersList.Where(item => item.Emoji.Contains((string) Filters[Command.emoji]));
+            /* Фильтруем по цене ОТ если пользователь не в меню коллекции */
+            if (Filters[Command.price] is not 0 && State is not UserState.CollectionMenu)
+                stickersList = stickersList.Where(item => item.Price >= (int)Filters[Command.price]);
+            /* Фильтруем по цене ДО если пользователь не в меню коллекции */
+            if (Filters[Command.price_to] is not 0 && State is not UserState.CollectionMenu)
+                stickersList = stickersList.Where(item => item.Price <= (int)Filters[Command.price_to]);
             /* Если не установлена сортировка, возвращаем результат */
-            if ((string) Filters["sorting"] == SortingTypes.None) return ToTelegramResults(stickersList, command);
+            if ((string) Filters[Command.sort] == SortingTypes.None) return ToTelegramResults(stickersList, command);
             
             /* Сортируем список, если тип сортировки установлен */
             /* Сортируем по автору */
-            if ((string) Filters["sorting"] == SortingTypes.ByAuthor)
+            if ((string) Filters[Command.sort] == SortingTypes.ByAuthor)
                 stickersList = stickersList.OrderBy(item => item.Author);
             /* Сортируем по названию */
-            if ((string) Filters["sorting"] == SortingTypes.ByTitle)
+            if ((string) Filters[Command.sort] == SortingTypes.ByTitle)
                 stickersList = stickersList.OrderBy(item => item.Title);
             /* Сортируем по увеличению тира */
-            if ((string) Filters["sorting"] == SortingTypes.ByTierIncrease)
+            if ((string) Filters[Command.sort] == SortingTypes.ByTierIncrease)
                 stickersList = stickersList.OrderBy(item => item.Tier);
             /* Сортируем по уменьшению тира */
-            if ((string) Filters["sorting"] == SortingTypes.ByTierDecrease)
+            if ((string) Filters[Command.sort] == SortingTypes.ByTierDecrease)
                 stickersList = stickersList.OrderByDescending(item => item.Tier);
             
             return ToTelegramResults(stickersList, command);
         }
 
+        private static async Task<IEnumerable<StickerEntity>> GetShopStickers(string filter)
+        {
+            // TODO return shop stickers
+            return await GetUnlimitedStickers(filter);
+        }
+
+        private static async Task<IEnumerable<StickerEntity>> GetAuctionStickers(string filter)
+        {
+            // TODO return auction stickers
+            return await GetUnlimitedStickers(filter);
+        }
+
         /* Возвращает все стикеры системы */
         private static async Task<IEnumerable<StickerEntity>> GetUnlimitedStickers(string filter)
         {

+ 5 - 1
CardCollector/DataBase/Entity/UserStickerRelationEntity.cs

@@ -1,4 +1,5 @@
-using System.ComponentModel.DataAnnotations;
+using System;
+using System.ComponentModel.DataAnnotations;
 using System.ComponentModel.DataAnnotations.Schema;
 
 namespace CardCollector.DataBase.Entity
@@ -19,6 +20,9 @@ namespace CardCollector.DataBase.Entity
         /* Количество стикеров данного вида у пользователя */
         [Column("count"), MaxLength(32)] public int Count { get; set; }
         
+        /* Последняя выплата по данному стикеру */
+        [Column("payout"), MaxLength(32)] public DateTime Payout { get; set; } = DateTime.Now;
+        
         /* MD5 хеш id стикера */
         [Column("short_hash"), MaxLength(40)] public string ShortHash { get; set; }
     }

+ 8 - 3
CardCollector/Logs.cs

@@ -8,15 +8,20 @@ namespace CardCollector
     {
         public static void LogOut(object message)
         {
-            Console.WriteLine($"[INFO] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {(message is Exception e ? e : Utilities.ToJson(message))}");
+            Console.WriteLine($"[INFO] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {message}");
         }
         public static void LogOutWarning(object message)
         {
-            Console.WriteLine($"[WARNING] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {(message is Exception e ? e : Utilities.ToJson(message))}");
+            Console.WriteLine($"[WARNING] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {message}");
         }
         public static void LogOutError(object message)
         {
-            Console.WriteLine($"[ERROR] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {(message is Exception e ? e : Utilities.ToJson(message))}");
+            Console.WriteLine($"[ERROR] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {message}");
+        }
+
+        public static void LogOutJson(object message)
+        {
+            Console.WriteLine($"[JSON] [{DateTime.Now.ToString(CultureInfo.CurrentCulture)}] {Utilities.ToJson(message)}");
         }
     }
 }

+ 18 - 0
CardCollector/Resources/Command.Designer.cs

@@ -114,6 +114,24 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to price.
+        /// </summary>
+        internal static string price {
+            get {
+                return ResourceManager.GetString("price", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to price_to.
+        /// </summary>
+        internal static string price_to {
+            get {
+                return ResourceManager.GetString("price_to", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to select.
         /// </summary>

+ 6 - 0
CardCollector/Resources/Command.resx

@@ -54,4 +54,10 @@
     <data name="select_sticker" xml:space="preserve">
         <value>select</value>
     </data>
+    <data name="price" xml:space="preserve">
+        <value>price</value>
+    </data>
+    <data name="price_to" xml:space="preserve">
+        <value>price_to</value>
+    </data>
 </root>

+ 50 - 7
CardCollector/Resources/Keyboard.cs

@@ -21,15 +21,29 @@ namespace CardCollector.Resources
             new KeyboardButton[] {Text.shop, Text.auction},
         }) {ResizeKeyboard = true};
 
+        public static InlineKeyboardMarkup GetSortingMenu(UserState state)
+        {
+            var keyboard = new List<InlineKeyboardButton[]>
+            {
+                new[] {InlineKeyboardButton.WithCallbackData(Text.author, Command.author)},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.tier, Command.tier)},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.emoji, Command.emoji)}
+            };
+            if (state != UserState.CollectionMenu) keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.price, Command.price)});
+            keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.sort, Command.sort)});
+            keyboard.Add(new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)});
+            keyboard.Add(new[] {InlineKeyboardButton.WithSwitchInlineQueryCurrentChat(Text.show_stickers)});
+            return new InlineKeyboardMarkup(keyboard);
+        }
         /* Клавиатура меню сортировки */
-        public static readonly InlineKeyboardMarkup SortingOptions = new(new[]
+        public static readonly InlineKeyboardMarkup SortOptions = new(new[]
         {
-            new[] {InlineKeyboardButton.WithCallbackData(Text.author, Command.author)},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.tier, Command.tier)},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.emoji, Command.emoji)},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.sort, Command.sort)},
-            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
-            new[] {InlineKeyboardButton.WithSwitchInlineQueryCurrentChat(Text.show_stickers)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.no, $"{Command.set}={Command.sort}={SortingTypes.None}")},
+            new[] {InlineKeyboardButton.WithCallbackData(SortingTypes.ByTierIncrease, $"{Command.set}={Command.sort}={SortingTypes.ByTierIncrease}")},
+            new[] {InlineKeyboardButton.WithCallbackData(SortingTypes.ByTierDecrease, $"{Command.set}={Command.sort}={SortingTypes.ByTierDecrease}")},
+            new[] {InlineKeyboardButton.WithCallbackData(SortingTypes.ByAuthor, $"{Command.set}={Command.sort}={SortingTypes.ByAuthor}")},
+            new[] {InlineKeyboardButton.WithCallbackData(SortingTypes.ByTitle, $"{Command.set}={Command.sort}={SortingTypes.ByTitle}")},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
         });
 
         /* Клавиатура меню выбора тира */
@@ -44,6 +58,35 @@ namespace CardCollector.Resources
             new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
         });
 
+        /* Клавиатура меню ввода эмоджи */
+        public static readonly InlineKeyboardMarkup EmojiOptions = new (new[]
+        {
+            new[] {InlineKeyboardButton.WithCallbackData(Text.all, $"{Command.set}={Command.emoji}=")},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
+        });
+
+        /* Клавиатура меню выбора цен */
+        public static readonly InlineKeyboardMarkup PriceOptions = new (new[]
+        {
+            new[] {
+                InlineKeyboardButton.WithCallbackData($"{Text.from} 0", $"{Command.set}={Command.price}=0"),
+                InlineKeyboardButton.WithCallbackData($"{Text.to} 100", $"{Command.set}={Command.price_to}=100"),
+            },
+            new[] {
+                InlineKeyboardButton.WithCallbackData($"{Text.from} 100", $"{Command.set}={Command.price}=100"),
+                InlineKeyboardButton.WithCallbackData($"{Text.to} 500", $"{Command.set}={Command.price_to}=500"),
+            },
+            new[] {
+                InlineKeyboardButton.WithCallbackData($"{Text.from} 500", $"{Command.set}={Command.price}=500"),
+                InlineKeyboardButton.WithCallbackData($"{Text.to} 1000", $"{Command.set}={Command.price_to}=1000"),
+            },
+            new[] {
+                InlineKeyboardButton.WithCallbackData($"{Text.from} 1000", $"{Command.set}={Command.price}=1000"),
+                InlineKeyboardButton.WithCallbackData($"{Text.to} ∞", $"{Command.set}={Command.price_to}=0"),
+            },
+            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
+        });
+
         /* Возвращает клавиатуру со списоком авторов */
         public static InlineKeyboardMarkup GetAuthorsKeyboard(List<string> list, int page = 1)
         {

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

@@ -87,6 +87,24 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Выберите диапазон цен из списка ниже:.
+        /// </summary>
+        internal static string choose_price {
+            get {
+                return ResourceManager.GetString("choose_price", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Выберите сортировку из списка ниже:.
+        /// </summary>
+        internal static string choose_sort {
+            get {
+                return ResourceManager.GetString("choose_sort", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Выберите тир из списка ниже:.
         /// </summary>
@@ -114,6 +132,33 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Введите эмоцию.
+        /// </summary>
+        internal static string enter_emoji {
+            get {
+                return ResourceManager.GetString("enter_emoji", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Введите только одну эмоцию!.
+        /// </summary>
+        internal static string enter_only_one_emoji {
+            get {
+                return ResourceManager.GetString("enter_only_one_emoji", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Пожалуйста, введите только эмоцию.
+        /// </summary>
+        internal static string please_enter_emoji {
+            get {
+                return ResourceManager.GetString("please_enter_emoji", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Стоимость:.
         /// </summary>

+ 15 - 0
CardCollector/Resources/Messages.resx

@@ -51,4 +51,19 @@
     <data name="choose_tier" xml:space="preserve">
         <value>Выберите тир из списка ниже:</value>
     </data>
+    <data name="enter_emoji" xml:space="preserve">
+        <value>Введите эмоцию</value>
+    </data>
+    <data name="enter_only_one_emoji" xml:space="preserve">
+        <value>Введите только одну эмоцию!</value>
+    </data>
+    <data name="please_enter_emoji" xml:space="preserve">
+        <value>Пожалуйста, введите только эмоцию</value>
+    </data>
+    <data name="choose_price" xml:space="preserve">
+        <value>Выберите диапазон цен из списка ниже:</value>
+    </data>
+    <data name="choose_sort" xml:space="preserve">
+        <value>Выберите сортировку из списка ниже:</value>
+    </data>
 </root>

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

@@ -123,6 +123,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to От.
+        /// </summary>
+        internal static string from {
+            get {
+                return ResourceManager.GetString("from", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to →.
         /// </summary>
@@ -132,6 +141,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Нет.
+        /// </summary>
+        internal static string no {
+            get {
+                return ResourceManager.GetString("no", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to ←.
         /// </summary>
@@ -141,6 +159,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Цена.
+        /// </summary>
+        internal static string price {
+            get {
+                return ResourceManager.GetString("price", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Профиль.
         /// </summary>
@@ -203,5 +230,14 @@ namespace CardCollector.Resources {
                 return ResourceManager.GetString("tier", resourceCulture);
             }
         }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to До.
+        /// </summary>
+        internal static string to {
+            get {
+                return ResourceManager.GetString("to", resourceCulture);
+            }
+        }
     }
 }

+ 12 - 0
CardCollector/Resources/Text.resx

@@ -66,4 +66,16 @@
     <data name="start" xml:space="preserve">
         <value>/start</value>
     </data>
+    <data name="from" xml:space="preserve">
+        <value>От</value>
+    </data>
+    <data name="to" xml:space="preserve">
+        <value>До</value>
+    </data>
+    <data name="price" xml:space="preserve">
+        <value>Цена</value>
+    </data>
+    <data name="no" xml:space="preserve">
+        <value>Нет</value>
+    </data>
 </root>