Browse Source

Change loading stickers algorythm

Tigran 3 years ago
parent
commit
2cfdcb96a9
38 changed files with 454 additions and 283 deletions
  1. 2 2
      CardCollector.sln.DotSettings.user
  2. 24 0
      CardCollector/Commands/CallbackQuery/EndUploadStickers.cs
  3. 1 0
      CardCollector/Commands/CallbackQueryCommand.cs
  4. 1 2
      CardCollector/Commands/InlineQueryCommand.cs
  5. 0 0
      CardCollector/Commands/Message/Auction.cs
  6. 0 0
      CardCollector/Commands/Message/Collection.cs
  7. 43 0
      CardCollector/Commands/Message/CreateToken.cs
  8. 0 186
      CardCollector/Commands/Message/DocumentMessage/UploadFileMessageCommand.cs
  9. 5 3
      CardCollector/Commands/Message/DownloadStickerPack.cs
  10. 0 0
      CardCollector/Commands/Message/EnterEmoji.cs
  11. 0 0
      CardCollector/Commands/Message/EnterGemsExchange.cs
  12. 0 0
      CardCollector/Commands/Message/EnterGemsPrice.cs
  13. 0 0
      CardCollector/Commands/Message/Menu.cs
  14. 0 0
      CardCollector/Commands/Message/Profile.cs
  15. 0 0
      CardCollector/Commands/Message/Shop.cs
  16. 0 0
      CardCollector/Commands/Message/ShowFiltersMenu.cs
  17. 0 0
      CardCollector/Commands/Message/ShowSample.cs
  18. 0 0
      CardCollector/Commands/Message/Start.cs
  19. 0 0
      CardCollector/Commands/Message/StopBot.cs
  20. 96 0
      CardCollector/Commands/Message/UploadFile.cs
  21. 34 0
      CardCollector/Commands/Message/UploadSticker.cs
  22. 36 47
      CardCollector/Commands/MessageCommand.cs
  23. 6 1
      CardCollector/Controllers/MessageController.cs
  24. 1 0
      CardCollector/DataBase/CardCollectorDatabase.cs
  25. 12 0
      CardCollector/DataBase/Entity/SessionToken.cs
  26. 2 0
      CardCollector/DataBase/Entity/StickerEntity.cs
  27. 21 0
      CardCollector/DataBase/EntityDao/SessionTokenDao.cs
  28. 10 0
      CardCollector/DataBase/EntityDao/StickerDao.cs
  29. 9 0
      CardCollector/Resources/Command.Designer.cs
  30. 3 0
      CardCollector/Resources/Command.resx
  31. 12 1
      CardCollector/Resources/Keyboard.cs
  32. 47 15
      CardCollector/Resources/Messages.Designer.cs
  33. 33 14
      CardCollector/Resources/Messages.resx
  34. 18 0
      CardCollector/Resources/Text.Designer.cs
  35. 6 0
      CardCollector/Resources/Text.resx
  36. 2 1
      CardCollector/Resources/UserState.cs
  37. 18 0
      CardCollector/Session/Modules/UploadedStickersModule.cs
  38. 12 11
      CardCollector/Utilities.cs

+ 2 - 2
CardCollector.sln.DotSettings.user

@@ -16,9 +16,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>

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

@@ -0,0 +1,24 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using CardCollector.Session.Modules;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.CallbackQuery
+{
+    public class EndUploadStickers : CallbackQueryCommand
+    {
+        protected override string CommandText => Command.end_sticker_upload;
+        public override async Task Execute()
+        {
+            User.Session.State = UserState.UploadFile;
+            var module = User.Session.GetModule<UploadedStickersModule>();
+            await MessageController.EditMessage(User, module.MessageId, Messages.upload_your_file, 
+                Keyboard.CancelKeyboard);
+        }
+
+        public EndUploadStickers() { }
+        public EndUploadStickers(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 1 - 0
CardCollector/Commands/CallbackQueryCommand.cs

@@ -53,6 +53,7 @@ namespace CardCollector.Commands
             new Count(),
             new CallbackQuery.DailyTasks(),
             new DeleteCombine(),
+            new EndUploadStickers(),
             new SelectEmoji(),
             new MyPacks(),
             new OpenPack(),

+ 1 - 2
CardCollector/Commands/InlineQueryCommand.cs

@@ -60,12 +60,11 @@ namespace CardCollector.Commands
                 : new CommandNotFound(user, update, $"{update.InlineQuery!.ChatType}={update.InlineQuery!.Query}");
         }
 
+        protected InlineQueryCommand() { }
         protected InlineQueryCommand(UserEntity user, Update update) : base(user, update)
         {
             InlineQueryId = update.InlineQuery!.Id;
             Query = update.InlineQuery!.Query;
         }
-        
-        protected InlineQueryCommand() { }
     }
 }

+ 0 - 0
CardCollector/Commands/Message/TextMessage/Auction.cs → CardCollector/Commands/Message/Auction.cs


+ 0 - 0
CardCollector/Commands/Message/TextMessage/Collection.cs → CardCollector/Commands/Message/Collection.cs


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

@@ -0,0 +1,43 @@
+using System.Linq;
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+
+namespace CardCollector.Commands.Message.TextMessage
+{
+    public class CreateToken : MessageCommand
+    {
+        protected override string CommandText => "create_token";
+        private const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_";
+        private const string site = "http://127.0.0.1:8080/";
+        
+        public override async Task Execute()
+        {
+            var token = GenerateNewToken();
+            await SessionTokenDao.AddNew(User.Id, token);
+            var loginLink = $"{site}login?token={token}";
+            var message = await MessageController.SendTextWithHtml(User,
+                $"<a href=\"{loginLink}\">{Messages.your_login_link}</a>", Keyboard.LoginKeyboard(loginLink));
+            User.Session.Messages.Add(message.MessageId);
+        }
+
+        private string GenerateNewToken()
+        {
+            return new string(Enumerable.Repeat(chars, 64).Select(s => s[Utilities.rnd.Next(s.Length)]).ToArray());
+        }
+
+        protected internal override bool IsMatches(UserEntity user, Update update)
+        {
+            if (update.Message!.Type != MessageType.Text) return false;
+            var data = update.Message.Text!.Split(' ');
+            return data.Length > 1 && data[1] == CommandText;
+        }
+
+        public CreateToken() { }
+        public CreateToken(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 0 - 186
CardCollector/Commands/Message/DocumentMessage/UploadFileMessageCommand.cs

@@ -1,186 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Timers;
-using CardCollector.Controllers;
-using CardCollector.Resources;
-using CardCollector.DataBase.Entity;
-using CardCollector.DataBase.EntityDao;
-using OfficeOpenXml;
-using Telegram.Bot;
-using Telegram.Bot.Types;
-using Telegram.Bot.Types.InputFiles;
-using File = System.IO.File;
-
-namespace CardCollector.Commands.Message.DocumentMessage
-{
-    public class UploadFileMessageCommand : MessageCommand
-    {
-        protected override string CommandText => "";
-        public override async Task Execute()
-        {
-            /* Очищаем чат */
-            await User.ClearChat();
-            User.Session.State = UserState.Default;
-            /* Соообщаем, что начали загрузку файла */
-            var message = await MessageController.SendMessage(User, Messages.downloading_file);
-            /* Загружаем файл */
-            await Utilities.DownloadFile(Update.Message!.Document!.FileId);
-            /* Сообщаем пользователю, что начали распаковку */
-            await MessageController.EditMessage(User, message.MessageId, Messages.unzipping_file);
-            /* Извлекаем из архива файлы */
-            await Task.Run(() => ZipFile.ExtractToDirectory("pack.zip", "pack", true));
-            /* Сообщаем пользователю, что читаем документ */
-            await MessageController.EditMessage(User, message.MessageId, Messages.reading_document);
-            /* Парсим файл */
-            try
-            {
-                await ParseExcelFile();
-                /* Сообщаем пользователю, что удаляем данные */
-                await MessageController.EditMessage(User, message.MessageId, Messages.deleting_files);
-                File.Delete("pack.zip");
-                Directory.Delete("pack", true);
-                /* Сообщаем пользователю, что список стикеров обновится через 15 минут */
-                await MessageController.EditMessage(User, message.MessageId, Messages.stickers_will_be_updated);
-            }
-            catch (Exception)
-            {
-                /* Сообщаем пользователю, что произошла ошибка */
-                await MessageController.EditMessage(User, message.MessageId, Messages.unexpected_exception);
-            }
-            var timer = new Timer
-            {
-                Interval = 60 * 1000,
-                Enabled = true,
-                AutoReset = false
-            };
-
-            async void DeleteMessage(object sender, ElapsedEventArgs args) => await MessageController.DeleteMessage(User, message.MessageId);
-
-            timer.Elapsed += DeleteMessage;
-        }
-        
-        private async Task ParseExcelFile()
-        {
-            await Task.Run(async () =>
-            {
-                using var xlPackage = new ExcelPackage(new FileInfo("pack/table.xlsx"));
-                var myWorksheet = xlPackage.Workbook.Worksheets.First(); //select sheet here
-                var dirInfo = new DirectoryInfo("pack");
-                var newStickers = new List<StickerEntity>();
-                var firstStickerName = myWorksheet.Cells[2, 1].Value.ToString();
-                var firstStickerInfo = dirInfo.GetFiles($@"{firstStickerName}.*")[0];
-                var isAnimated = firstStickerInfo.Extension == ".tgs";
-                for (var rowNum = 2; myWorksheet.Cells[rowNum, 1].Value is string; rowNum++) //select starting row here
-                {
-                    var stickerName = myWorksheet.Cells[rowNum, 1].Value.ToString();
-                    var fileInfo = dirInfo.GetFiles($@"{stickerName}.*")[0];
-                    var fields = ParseRow(myWorksheet.Cells, rowNum);
-                    await CreateSticker(fileInfo, fields["Emoji"]);
-                    var sticker = new StickerEntity
-                    {
-                        Title = fields["Title"], Author = fields["Author"], Income = int.Parse(fields["IncomeCoins"]), 
-                        IncomeTime = int.Parse(fields["IncomeTime"]), Tier = int.Parse(fields["Tier"]), 
-                        Emoji = fields["Emoji"], Description = fields["Description"]
-                    };
-                    newStickers.Add(sticker);
-                }
-
-                var timer = new Timer
-                {
-                    Interval = 15 * 60 * 1000,
-                    Enabled = true,
-                    AutoReset = false,
-                };
-
-                async void SavingStickersToDatabase(object sender, ElapsedEventArgs args)
-                {
-                    var stickerSet = (await Bot.Client.GetStickerSetAsync(
-                        $"{(isAnimated ? "a" : "s")}{User.Id}_by_{AppSettings.NAME}")).Stickers.ToList();
-                    Logs.LogOut("Saving " + newStickers.Count + " stickers from " + stickerSet.Count);
-                    for (var i = 0; i < newStickers.Count; i++)
-                    {
-                        var stickerId = stickerSet[i].FileId;
-                        newStickers[i].Id = stickerId;
-                        newStickers[i].Md5Hash = Utilities.CreateMd5(stickerId);
-                        await StickerDao.AddNew(newStickers[i]);
-                    }
-                    foreach (var sticker in newStickers)
-                    {
-                        try
-                        {
-                            await Bot.Client.DeleteStickerFromSetAsync(sticker.Id);
-                        }
-                        catch (Exception)
-                        {
-                            Logs.LogOut("Cant delete sticker " + sticker.Title);
-                        }
-                    }
-                }
-                
-                timer.Elapsed += SavingStickersToDatabase;
-            });
-        }
-
-        private static Dictionary<string, string> ParseRow(ExcelRange cells, int rowNum)
-        {
-            return new Dictionary<string, string>
-            {
-                {"Title", cells[rowNum, 2].Value.ToString()},
-                {"Author", cells[rowNum, 3].Value.ToString()},
-                {"IncomeCoins", cells[rowNum, 4].Value.ToString()},
-                {"IncomeGems", cells[rowNum, 5].Value.ToString()},
-                {"IncomeTime", cells[rowNum, 6].Value.ToString()},
-                {"PriceCoins", cells[rowNum, 7].Value.ToString()},
-                {"PriceGems", cells[rowNum, 8].Value.ToString()},
-                {"Tier", cells[rowNum, 9].Value.ToString()},
-                {"Emoji", cells[rowNum, 10].Value.ToString()},
-                {"Description", cells[rowNum, 11].Value is string s ? s : ""},
-            };
-        }
-
-        private async Task CreateSticker(FileInfo fileInfo, string emoji)
-        {
-            var isAnimated = fileInfo.Extension == ".tgs";
-            var fileStream = fileInfo.OpenRead();
-            try
-            {
-                if (isAnimated)
-                    await Bot.Client.AddAnimatedStickerToSetAsync(User.Id, $"a{User.Id}_by_{AppSettings.NAME}",
-                    new InputFileStream(fileStream), emoji);
-                else
-                {
-                    var onlineFile = await Bot.Client.UploadStickerFileAsync(User.Id, fileInfo.OpenRead());
-                    await Bot.Client.AddStickerToSetAsync(User.Id, $"s{User.Id}_by_{AppSettings.NAME}",
-                        new InputMedia(onlineFile.FileId), emoji);
-                }
-            }
-            catch (Exception)
-            {
-                fileStream.Close();
-                fileStream = fileInfo.OpenRead();
-                if (isAnimated) 
-                    await Bot.Client.CreateNewAnimatedStickerSetAsync(User.Id, $"a{User.Id}_by_{AppSettings.NAME}",
-                    "animated", new InputFileStream(fileStream), emoji);
-                else
-                {
-                    var onlineFile = await Bot.Client.UploadStickerFileAsync(User.Id, fileInfo.OpenRead());
-                    await Bot.Client.CreateNewStickerSetAsync(User.Id, $"s{User.Id}_by_{AppSettings.NAME}",
-                        "static", new InputMedia(onlineFile.FileId), emoji);
-                }
-            }
-            fileStream.Close();
-        }
-
-        protected internal override bool IsMatches(UserEntity user, Update update)
-        {
-            return user.Session.State == UserState.UploadFile;
-        }
-
-        public UploadFileMessageCommand() { }
-        public UploadFileMessageCommand(UserEntity user, Update update) : base(user, update) { }
-    }
-}

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

@@ -2,9 +2,10 @@
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
+using CardCollector.Session.Modules;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Message.TextMessage
+namespace CardCollector.Commands.Message
 {
     public class DownloadStickerPack : MessageCommand
     {
@@ -12,9 +13,10 @@ namespace CardCollector.Commands.Message.TextMessage
         public override async Task Execute()
         {
             await User.ClearChat();
-            User.Session.State = UserState.UploadFile;
-            var result = await MessageController.SendMessage(User, Messages.upload_file, Keyboard.CancelKeyboard);
+            User.Session.State = UserState.UploadSticker;
+            var result = await MessageController.SendMessage(User, Messages.upload_your_stickers, Keyboard.CancelKeyboard);
             User.Session.Messages.Add(result.MessageId);
+            User.Session.GetModule<UploadedStickersModule>().MessageId = result.MessageId;
         }
 
         protected internal override bool IsMatches(UserEntity user, Update update)

+ 0 - 0
CardCollector/Commands/Message/TextMessage/EnterEmoji.cs → CardCollector/Commands/Message/EnterEmoji.cs


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


+ 0 - 0
CardCollector/Commands/Message/TextMessage/EnterGemsPrice.cs → CardCollector/Commands/Message/EnterGemsPrice.cs


+ 0 - 0
CardCollector/Commands/Message/TextMessage/Menu.cs → CardCollector/Commands/Message/Menu.cs


+ 0 - 0
CardCollector/Commands/Message/TextMessage/Profile.cs → CardCollector/Commands/Message/Profile.cs


+ 0 - 0
CardCollector/Commands/Message/TextMessage/Shop.cs → CardCollector/Commands/Message/Shop.cs


+ 0 - 0
CardCollector/Commands/Message/TextMessage/ShowFiltersMenu.cs → CardCollector/Commands/Message/ShowFiltersMenu.cs


+ 0 - 0
CardCollector/Commands/Message/TextMessage/ShowSample.cs → CardCollector/Commands/Message/ShowSample.cs


+ 0 - 0
CardCollector/Commands/Message/TextMessage/Start.cs → CardCollector/Commands/Message/Start.cs


+ 0 - 0
CardCollector/Commands/Message/TextMessage/StopBot.cs → CardCollector/Commands/Message/StopBot.cs


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

@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.DataBase.EntityDao;
+using CardCollector.Resources;
+using CardCollector.Session.Modules;
+using OfficeOpenXml;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+using File = System.IO.File;
+
+namespace CardCollector.Commands.Message
+{
+    public class UploadFile : MessageCommand
+    {
+        protected override string CommandText => "";
+        public override async Task Execute()
+        {
+            User.Session.State = UserState.Default;
+            var module = User.Session.GetModule<UploadedStickersModule>();
+            try
+            {
+                /* Соообщаем, что начали загрузку файла */
+                await MessageController.EditMessage(User, module.MessageId, Messages.downloading_file);
+                /* Загружаем файл */
+                var fileName = await Utilities.DownloadFile(Update.Message?.Document);
+                /* Сообщаем пользователю, что читаем документ */
+                await MessageController.EditMessage(User, module.MessageId, Messages.reading_document);
+                /* Парсим файл */
+                var stickersList = await ParseExcelFile(fileName, module.StickersList);
+                /* Сообщаем пользователю, что удаляем файлы */
+                await MessageController.EditMessage(User, module.MessageId, Messages.deleting_files);
+                File.Delete(fileName);
+                /* Сообщаем пользователю, что загружаем стикеры */
+                await MessageController.EditMessage(User, module.MessageId, Messages.uploading_stickers);
+                var packInfo = await PacksDao.AddNew(stickersList.First().Author);
+                await StickerDao.AddRange(stickersList, packInfo.Id);
+                /* Сообщаем пользователю, что стикеры загружены */
+                await MessageController.EditMessage(User, module.MessageId, Messages.stickers_succesfully_uploaded);
+            }
+            catch (Exception e)
+            {
+                /* Сообщаем пользователю, что произошла ошибка */
+                await MessageController.EditMessage(User, module.MessageId, $"{Messages.unexpected_exception}: {e.Message}");
+            }
+        }
+        
+        private async Task<List<StickerEntity>> ParseExcelFile(string fileName, List<StickerEntity> stickers)
+        {
+            return await Task.Run(() =>
+            {
+                using var xlPackage = new ExcelPackage(new FileInfo(fileName));
+                var myWorksheet = xlPackage.Workbook.Worksheets.First(); //select sheet here
+                for (var rowNum = 2; myWorksheet.Cells[rowNum, 1].Value is not null; rowNum++) //select starting row here
+                {
+                    var fields = ParseRow(myWorksheet.Cells, rowNum);
+                    stickers[rowNum - 2].Title = fields["Title"];
+                    stickers[rowNum - 2].Author = fields["Author"];
+                    stickers[rowNum - 2].Tier = int.Parse(fields["Tier"]);
+                    stickers[rowNum - 2].Emoji = fields["Emoji"];
+                    stickers[rowNum - 2].Effect = int.Parse(fields["Effect"]);
+                    stickers[rowNum - 2].Description = fields["Description"];
+                    stickers[rowNum - 2].IncomeTime = 60;
+                    stickers[rowNum - 2].Income = (int) Math.Pow(5, stickers[rowNum - 2].Tier - 1);
+                    stickers[rowNum - 2].Md5Hash = Utilities.CreateMd5(stickers[rowNum - 2].Title);
+                }
+                return stickers;
+            });
+        }
+
+        private static Dictionary<string, string> ParseRow(ExcelRange cells, int rowNum)
+        {
+            return new Dictionary<string, string>
+            {
+                {"Title", cells[rowNum, 1].Value.ToString()},
+                {"Author", cells[rowNum, 2].Value.ToString()},
+                {"Tier", cells[rowNum, 3].Value.ToString()},
+                {"Emoji", cells[rowNum, 4].Value.ToString()},
+                {"Effect", cells[rowNum, 5].Value is int e ? e.ToString() : "0"},
+                {"Description", cells[rowNum, 6].Value is string s ? s : ""}
+            };
+        }
+
+        protected internal override bool IsMatches(UserEntity user, Update update)
+        {
+            return user.Session.State == UserState.UploadFile && update.Message?.Type == MessageType.Document;
+        }
+
+        public UploadFile() { }
+        public UploadFile(UserEntity user, Update update) : base(user, update) { }
+    }
+}

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

@@ -0,0 +1,34 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using CardCollector.Session.Modules;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+
+namespace CardCollector.Commands.Message
+{
+    public class UploadSticker : MessageCommand
+    {
+        protected override string CommandText => "";
+        public override async Task Execute()
+        {
+            var stickerId = Update.Message?.Sticker?.FileId;
+            var module = User.Session.GetModule<UploadedStickersModule>();
+            module.StickersList.Add(new StickerEntity {Id = stickerId});
+            var message = $"{Messages.upload_your_stickers}" +
+                          $"\n{Messages.uploaded_count} {module.Count}";
+            foreach (var (stickerEntity, i) in module.StickersList.WithIndex())
+                message += $"\n{Text.sticker} {i + 1}: {stickerEntity.Id}";
+            await MessageController.EditMessage(User, module.MessageId, message, Keyboard.EndStickerUpload);
+        }
+
+        protected internal override bool IsMatches(UserEntity user, Update update)
+        {
+            return user.Session.State == UserState.UploadSticker && update.Message?.Type == MessageType.Sticker;
+        }
+
+        public UploadSticker() { }
+        public UploadSticker(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 36 - 47
CardCollector/Commands/MessageCommand.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
-using CardCollector.Commands.Message.DocumentMessage;
+using CardCollector.Commands.Message;
 using CardCollector.Commands.Message.TextMessage;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
@@ -26,36 +26,35 @@ namespace CardCollector.Commands
     public abstract class MessageCommand : UpdateModel
     {
         /* Список команд */
-        private static readonly List<MessageCommand>
-            TextCommandsList = new() {
-                // Команда "Профиль"
-                new Profile(),
-                // Команда "/start"
-                new Start(),
-                // Команда "/menu"
-                new Menu(),
-                // Команда "Коллекция"
-                new Collection(),
-                // Команда "Магазин"
-                new Shop(),
-                // Команда "Аукцион"
-                new Auction(),
-                // Ожидание ввода эмоджи
-                new EnterEmoji(),
-                new EnterGemsExchange(),
-                // Загрузка стикерпака
-                new DownloadStickerPack(),
-                //команда ввода цены
-                new EnterGemsPrice(),
-                // Команда "Показать пример"
-                new ShowSample(),
-                // Команда "Остановить"
-                new StopBot()
-            },
-            FileCommandsList = new() {
-                /* Выгрузка файлов к боту */
-                new UploadFileMessageCommand(),
-            };
+        private static readonly List<MessageCommand> List = new() {
+            // Команда "Профиль"
+            new Profile(),
+            // Команда "/start"
+            new Start(),
+            // Команда "/menu"
+            new Menu(),
+            // Команда "Коллекция"
+            new Collection(),
+            // Команда "Магазин"
+            new Shop(),
+            // Команда "Аукцион"
+            new Auction(),
+            // Ожидание ввода эмоджи
+            new EnterEmoji(),
+            new EnterGemsExchange(),
+            // Загрузка стикерпака
+            new DownloadStickerPack(),
+            //команда ввода цены
+            new EnterGemsPrice(),
+            new CreateToken(),
+            // Команда "Показать пример"
+            new ShowSample(),
+            // Команда "Остановить"
+            new StopBot(),
+            /* Выгрузка файлов к боту */
+            new UploadFile(),
+            new UploadSticker(),
+        };
 
         /* Метод, создающий объекты команд исходя из полученного обновления */
         public static async Task<UpdateModel> Factory(Update update)
@@ -71,14 +70,6 @@ namespace CardCollector.Commands
                     await Bot.Client.DeleteMessageAsync(update.Message.Chat.Id, update.Message.MessageId);
                 return new IgnoreUpdate();
             }
-
-            /* Список команд определяем исходя из типа сообщения */
-            var list = update.Message.Type switch
-            {
-                MessageType.Text => TextCommandsList,
-                MessageType.Document => FileCommandsList,
-                _ => new List<MessageCommand>()
-            };
             
             // Объект пользователя
             var user = await UserDao.GetUser(update.Message!.From);
@@ -94,21 +85,19 @@ namespace CardCollector.Commands
             
             // Возвращаем объект, если команда совпала
             /* Возвращаем первую подходящую команду */
-            return list.FirstOrDefault(item => item.IsMatches(user, update)) is { } executor
+            return List.FirstOrDefault(item => item.IsMatches(user, update)) is { } executor
                 ? (UpdateModel) Activator.CreateInstance(executor.GetType(), user, update)
-                : new CommandNotFound(user, update, update.Message.Type switch {
-                    MessageType.Text => update.Message.Text,
-                    MessageType.Document => update.Message.Document!.FileId,
-                    _ => Utilities.ToJson(update.Message)
-                });
+                : new CommandNotFound(user, update, update.Message.Type == MessageType.Text 
+                    ? update.Message.Text 
+                    : Utilities.ToJson(update.Message));
         }
 
         protected internal override bool IsMatches(UserEntity user, Update update)
         {
-            return update.Message!.Text == CommandText;
+            return update.Message?.Type == MessageType.Text && update.Message?.Text == CommandText;
         }
 
-        protected MessageCommand(UserEntity user, Update update) : base(user, update) { }
         protected MessageCommand() { }
+        protected MessageCommand(UserEntity user, Update update) : base(user, update) { }
     }
 }

+ 6 - 1
CardCollector/Controllers/MessageController.cs

@@ -147,7 +147,12 @@ namespace CardCollector.Controllers
             }
             catch (ApiRequestException e)
             {
-                if (e.ErrorCode != 400) return await SendMessage(user, message, keyboard);
+                if (e.ErrorCode != 400)
+                {
+                    var msg = await SendMessage(user, message, keyboard);
+                    user.Session.Messages.Add(msg.MessageId);
+                    return msg;
+                }
             }
             return new Message();
         }

+ 1 - 0
CardCollector/DataBase/CardCollectorDatabase.cs

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

+ 12 - 0
CardCollector/DataBase/Entity/SessionToken.cs

@@ -0,0 +1,12 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace CardCollector.DataBase.Entity
+{
+    [Table("session_tokens")]
+    public class SessionToken
+    {
+        [Key, Column("user_id"), MaxLength(127)] public long UserId { get; set; }
+        [Column("token"), MaxLength(256)] public string Token { get; set; }
+    }
+}

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

@@ -43,6 +43,8 @@ namespace CardCollector.DataBase.Entity
         [Column("md5hash"), MaxLength(40)] public string Md5Hash { get; set; }
         
         [Column("effect"), MaxLength(32)] public int Effect { get; set; }
+        
+        [Column("pack_id"), MaxLength(32)] public int PackId { get; set; }
 
         public override string ToString()
         {

+ 21 - 0
CardCollector/DataBase/EntityDao/SessionTokenDao.cs

@@ -0,0 +1,21 @@
+using System.Threading.Tasks;
+using CardCollector.DataBase.Entity;
+using Microsoft.EntityFrameworkCore;
+
+namespace CardCollector.DataBase.EntityDao
+{
+    public static class SessionTokenDao
+    {
+        private static readonly CardCollectorDatabase Instance = CardCollectorDatabase.GetSpecificInstance(typeof(SessionTokenDao));
+        /* Таблица auction в представлении Entity Framework */
+        private static readonly DbSet<SessionToken> Table = Instance.SessionTokens;
+
+        public static async Task AddNew(long userId, string token)
+        {
+            var result = await Table.AddAsync(new SessionToken {UserId = userId, Token = token});
+            await Instance.SaveChangesAsync();
+            Table.Attach(result.Entity);
+            Instance.ChangeTracker.Clear();
+        }
+    }
+}

+ 10 - 0
CardCollector/DataBase/EntityDao/StickerDao.cs

@@ -38,6 +38,16 @@ namespace CardCollector.DataBase.EntityDao
         public static async Task AddNew(StickerEntity sticker)
         {
             await Table.AddAsync(sticker);
+            await Instance.SaveChangesAsync();
+        }
+
+        public static async Task AddRange(IEnumerable<StickerEntity> stickers, int packId)
+        {
+            await Table.AddRangeAsync(stickers.Select(item => {
+                item.PackId = packId;
+                return item;
+            }));
+            await Instance.SaveChangesAsync();
         }
 
         public static async Task<List<StickerEntity>> GetListWhere(Func<StickerEntity, bool> func)

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

@@ -285,6 +285,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABT.
+        /// </summary>
+        internal static string end_sticker_upload {
+            get {
+                return ResourceManager.GetString("end_sticker_upload", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to AAS.
         /// </summary>

+ 3 - 0
CardCollector/Resources/Command.resx

@@ -156,4 +156,7 @@
     <data name="show_offer_info" xml:space="preserve">
         <value>ABS</value>
     </data>
+    <data name="end_sticker_upload" xml:space="preserve">
+        <value>ABT</value>
+    </data>
 </root>

+ 12 - 1
CardCollector/Resources/Keyboard.cs

@@ -28,12 +28,18 @@ namespace CardCollector.Resources
             new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
         });
 
-        public static InlineKeyboardMarkup BuyCoinsKeyboard = new(new[]
+        public static readonly InlineKeyboardMarkup BuyCoinsKeyboard = new(new[]
         {
             new[] {InlineKeyboardButton.WithCallbackData(Text.confirm_exchange, Command.confirm_exchange)},
             new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
         });
 
+        public static readonly InlineKeyboardMarkup EndStickerUpload = new(new[]
+        {
+            new[] {InlineKeyboardButton.WithCallbackData(Text.end_sticker_upload, Command.end_sticker_upload)},
+            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+        });
+
         public static InlineKeyboardMarkup BackToFilters(string stickerTitle)
         {
             return new InlineKeyboardMarkup(new[]
@@ -430,5 +436,10 @@ namespace CardCollector.Resources
             keyboard.Add(new []{InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)});
             return new InlineKeyboardMarkup(keyboard);
         }
+
+        public static InlineKeyboardMarkup LoginKeyboard(string loginLink)
+        {
+            return new InlineKeyboardMarkup(InlineKeyboardButton.WithUrl(Text.login, loginLink));
+        }
     }
 }

+ 47 - 15
CardCollector/Resources/Messages.Designer.cs

@@ -495,13 +495,13 @@ namespace CardCollector.Resources {
         /// <summary>
         ///   Looks up a localized string similar to При покупке &quot;Случайного пака&quot; Вам может выпасть ЛЮБОЙ стикер от ЛЮБОГО художника
         ///
-        ///При покупке &quot;Пака художника&quot; Вам может выпасть ЛЮБОЙ стикер от выбранного Вами художника художника
+        ///        При покупке &quot;Пака художника&quot; Вам может выпасть ЛЮБОЙ стикер от выбранного Вами художника художника
         ///
-        ///Вероятности выпадения стикера с тиром:
-        ///1 - 80%
-        ///2 - 16%
-        ///3 - 3.3%
-        ///4 - 0.7%.
+        ///        Вероятности выпадения стикера с тиром:
+        ///        1 - 80%
+        ///        2 - 16%
+        ///        3 - 3.3%
+        ///        4 - 0.7%.
         /// </summary>
         internal static string pack_info {
             get {
@@ -627,11 +627,11 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Стикеры будут обновлены через 15 минут.
+        ///   Looks up a localized string similar to Стикеры успешно загружены!.
         /// </summary>
-        internal static string stickers_will_be_updated {
+        internal static string stickers_succesfully_uploaded {
             get {
-                return ResourceManager.GetString("stickers_will_be_updated", resourceCulture);
+                return ResourceManager.GetString("stickers_succesfully_uploaded", resourceCulture);
             }
         }
         
@@ -672,20 +672,43 @@ namespace CardCollector.Resources {
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Распаковываю архив.....
+        ///   Looks up a localized string similar to Пожалуйста, загрузите ваш xlsx файл с описанием стикеров, по одной строке на стикер. Соблюдайте порядок, в котором вы загружали стикеры.
+        ///        Структура таблицы должна сожержать столбцы в следующем порядке: Название, Автор, Тир, Эмоции, Эффект, Описание.
+        ///        Оставьте первую строку пустой или заполните ее этими заголовками, значения начинайте вписывать со !второй! строки.
+        ///        Поле &quot;Эффект&quot; Может содержать целочисленное значение. Вот описания этих значений:
+        ///        0 - Нет эффекта
+        ///        1 - У [rest of string was truncated]&quot;;.
         /// </summary>
-        internal static string unzipping_file {
+        internal static string upload_your_file {
             get {
-                return ResourceManager.GetString("unzipping_file", resourceCulture);
+                return ResourceManager.GetString("upload_your_file", resourceCulture);
             }
         }
         
         /// <summary>
-        ///   Looks up a localized string similar to Загрузите архив со стикерами и таблицей в формате zip.
+        ///   Looks up a localized string similar to Загрузите свои стикеры в формате .webp или .tgs в том порядке, в котором вы описываете их в таблице Excel. Когда завершите загрузку, нажмите на &quot;Завершить загрузку стикеров&quot;.
         /// </summary>
-        internal static string upload_file {
+        internal static string upload_your_stickers {
             get {
-                return ResourceManager.GetString("upload_file", resourceCulture);
+                return ResourceManager.GetString("upload_your_stickers", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Количество загруженных стикеров:.
+        /// </summary>
+        internal static string uploaded_count {
+            get {
+                return ResourceManager.GetString("uploaded_count", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Загружаю стикеры....
+        /// </summary>
+        internal static string uploading_stickers {
+            get {
+                return ResourceManager.GetString("uploading_stickers", resourceCulture);
             }
         }
         
@@ -743,6 +766,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Ваша ссылка для входа.
+        /// </summary>
+        internal static string your_login_link {
+            get {
+                return ResourceManager.GetString("your_login_link", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to У вас сейчас:.
         /// </summary>

+ 33 - 14
CardCollector/Resources/Messages.resx

@@ -66,23 +66,17 @@
     <data name="choose_sort" xml:space="preserve">
         <value>Выберите сортировку из списка ниже:</value>
     </data>
-    <data name="upload_file" xml:space="preserve">
-        <value>Загрузите архив со стикерами и таблицей в формате zip</value>
-    </data>
     <data name="downloading_file" xml:space="preserve">
         <value>Загружаю файл...</value>
     </data>
-    <data name="unzipping_file" xml:space="preserve">
-        <value>Распаковываю архив....</value>
-    </data>
     <data name="reading_document" xml:space="preserve">
         <value>Читаю документ...</value>
     </data>
     <data name="deleting_files" xml:space="preserve">
         <value>Удаляю файлы...</value>
     </data>
-    <data name="stickers_will_be_updated" xml:space="preserve">
-        <value>Стикеры будут обновлены через 15 минут</value>
+    <data name="stickers_succesfully_uploaded" xml:space="preserve">
+        <value>Стикеры успешно загружены!</value>
     </data>
     <data name="unexpected_exception" xml:space="preserve">
         <value>Произошла неожиданная ошибка, попробуйте еще раз.</value>
@@ -192,13 +186,13 @@
     <data name="pack_info" xml:space="preserve">
         <value>При покупке "Случайного пака" Вам может выпасть ЛЮБОЙ стикер от ЛЮБОГО художника
 
-При покупке "Пака художника" Вам может выпасть ЛЮБОЙ стикер от выбранного Вами художника художника
+        При покупке "Пака художника" Вам может выпасть ЛЮБОЙ стикер от выбранного Вами художника художника
 
-Вероятности выпадения стикера с тиром:
-1 - 80%
-2 - 16%
-3 - 3.3%
-4 - 0.7%</value>
+        Вероятности выпадения стикера с тиром:
+        1 - 80%
+        2 - 16%
+        3 - 3.3%
+        4 - 0.7%</value>
     </data>
     <data name="pack_prize" xml:space="preserve">
         <value>Поздравляем! Вы получили случайный пак! Открыть его можно во вкладке профиля.</value>
@@ -254,4 +248,29 @@
     <data name="you_got" xml:space="preserve">
         <value>Вы получили</value>
     </data>
+    <data name="your_login_link" xml:space="preserve">
+        <value>Ваша ссылка для входа</value>
+    </data>
+    <data name="upload_your_stickers" xml:space="preserve">
+        <value>Загрузите свои стикеры в формате .webp или .tgs в том порядке, в котором вы описываете их в таблице Excel. Когда завершите загрузку, нажмите на "Завершить загрузку стикеров"</value>
+    </data>
+    <data name="uploaded_count" xml:space="preserve">
+        <value>Количество загруженных стикеров:</value>
+    </data>
+    <data name="uploading_stickers" xml:space="preserve">
+        <value>Загружаю стикеры...</value>
+    </data>
+    <data name="upload_your_file" xml:space="preserve">
+        <value>Пожалуйста, загрузите ваш xlsx файл с описанием стикеров, по одной строке на стикер. Соблюдайте порядок, в котором вы загружали стикеры.
+        Структура таблицы должна сожержать столбцы в следующем порядке: Название, Автор, Тир, Эмоции, Эффект, Описание.
+        Оставьте первую строку пустой или заполните ее этими заголовками, значения начинайте вписывать со !второй! строки.
+        Поле "Эффект" Может содержать целочисленное значение. Вот описания этих значений:
+        0 - Нет эффекта
+        1 - Увеличивает копилку на 200 монет
+        2 - Пользователь получает 25% алмазов к своему кошельку
+        3 - Скидка на аукционе 5%
+        4 - Случайный пак раз в 5 дней
+        5 - Случайный стикер 2 тира раз в 3 дня
+        6 - Случайный стикер 1 тира раз в 3 дня</value>
+    </data>
 </root>

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

@@ -312,6 +312,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Завершить загрузку стикеров.
+        /// </summary>
+        internal static string end_sticker_upload {
+            get {
+                return ResourceManager.GetString("end_sticker_upload", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to /error.
         /// </summary>
@@ -402,6 +411,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Вход на сайт.
+        /// </summary>
+        internal static string login {
+            get {
+                return ResourceManager.GetString("login", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to /menu.
         /// </summary>

+ 6 - 0
CardCollector/Resources/Text.resx

@@ -243,4 +243,10 @@
     <data name="gift" xml:space="preserve">
         <value>🎁</value>
     </data>
+    <data name="login" xml:space="preserve">
+        <value>Вход на сайт</value>
+    </data>
+    <data name="end_sticker_upload" xml:space="preserve">
+        <value>Завершить загрузку стикеров</value>
+    </data>
 </root>

+ 2 - 1
CardCollector/Resources/UserState.cs

@@ -16,6 +16,7 @@
         
         /* Пользователь в базовом состоянии (по умолчанию) */
         Default,
-        
+
+        UploadSticker
     }
 }

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

@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using CardCollector.DataBase.Entity;
+
+namespace CardCollector.Session.Modules
+{
+    public class UploadedStickersModule : Module
+    {
+        public List<StickerEntity> StickersList = new();
+        public int MessageId = 0;
+        public int Count => StickersList.Count;
+        
+        public void Reset()
+        {
+            StickersList.Clear();
+            MessageId = 0;
+        }
+    }
+}

+ 12 - 11
CardCollector/Utilities.cs

@@ -5,11 +5,14 @@ using System.Threading.Tasks;
 using System.Timers;
 using CardCollector.Resources;
 using Telegram.Bot;
+using Telegram.Bot.Types;
 
 namespace CardCollector
 {
     public static class Utilities
     {
+        public static readonly Random rnd = new Random();
+        
         public static string ToJson(object obj)
         {
             return Newtonsoft.Json.JsonConvert.SerializeObject(obj);
@@ -28,18 +31,16 @@ namespace CardCollector
             return sb.ToString();
         }
 
-        public static async Task DownloadFile(string fileId)
+        public static async Task<string> DownloadFile(Document file)
         {
-            await Task.Run(async () =>
-            {
-                /* Получаем информацию о файле */
-                var fileInfo = await Bot.Client.GetFileAsync(fileId);
-                /* Собираем ссылку на файл */
-                var fileUri = $"https://api.telegram.org/file/bot{AppSettings.TOKEN}/{fileInfo.FilePath}";
-                /* Загружаем файл */
-                var client = new WebClient();
-                client.DownloadFile(new Uri(fileUri), "pack.zip");
-            });
+            /* Получаем информацию о файле */
+            var fileInfo = await Bot.Client.GetFileAsync(file.FileId);
+            /* Собираем ссылку на файл */
+            var fileUri = $"https://api.telegram.org/file/bot{AppSettings.TOKEN}/{fileInfo.FilePath}";
+            /* Загружаем файл */
+            var client = new WebClient();
+            client.DownloadFile(new Uri(fileUri), file.FileName ?? "file");
+            return file.FileName ?? "file";
         }
         
         public static void SetUpTimer(TimeSpan timeToRun, ElapsedEventHandler callback)