Explorar el Código

Loading via bot stickerpacks and deleting it from tg. I hate stickers.

Tigran hace 4 años
padre
commit
5caeecebd5
Se han modificado 26 ficheros con 426 adiciones y 43 borrados
  1. 2 2
      CardCollector.sln.DotSettings.user
  2. 8 1
      CardCollector/Bot.cs
  3. 1 0
      CardCollector/CardCollector.csproj
  4. 1 0
      CardCollector/Commands/CallbackQuery/BackToFiltersMenu.cs
  5. 1 0
      CardCollector/Commands/CallbackQuery/EmojiCallback.cs
  6. 1 0
      CardCollector/Commands/CallbackQuery/SetFilterCallback.cs
  7. 194 0
      CardCollector/Commands/Message/DocumentMessage/UploadFileMessage.cs
  8. 48 26
      CardCollector/Commands/Message/Message.cs
  9. 1 1
      CardCollector/Commands/Message/TextMessage/AuctionMessage.cs
  10. 1 2
      CardCollector/Commands/Message/TextMessage/CollectionMessage.cs
  11. 30 0
      CardCollector/Commands/Message/TextMessage/DownloadStickerPackMessage.cs
  12. 1 1
      CardCollector/Commands/Message/TextMessage/EnterEmojiMessage.cs
  13. 1 1
      CardCollector/Commands/Message/TextMessage/ProfileMessage.cs
  14. 1 2
      CardCollector/Commands/Message/TextMessage/ShopMessage.cs
  15. 1 1
      CardCollector/Commands/Message/TextMessage/ShowFiltersMenu.cs
  16. 1 1
      CardCollector/Commands/Message/TextMessage/ShowSampleMessage.cs
  17. 1 1
      CardCollector/Commands/Message/TextMessage/StartMessage.cs
  18. 1 2
      CardCollector/DataBase/Entity/UserEntity.cs
  19. 5 0
      CardCollector/DataBase/EntityDao/StickerDao.cs
  20. 6 0
      CardCollector/Resources/Keyboard.cs
  21. 63 0
      CardCollector/Resources/Messages.Designer.cs
  22. 21 0
      CardCollector/Resources/Messages.resx
  23. 9 0
      CardCollector/Resources/Text.Designer.cs
  24. 3 0
      CardCollector/Resources/Text.resx
  25. 4 1
      CardCollector/Resources/UserState.cs
  26. 20 1
      CardCollector/Utilities.cs

+ 2 - 2
CardCollector.sln.DotSettings.user

@@ -1,11 +1,11 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
 	
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FCommand/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FCommand/@EntryIndexedValue">False</s:Boolean>
 	
 	
 	
 	
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_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">True</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FText/@EntryIndexedValue">False</s:Boolean>
 	

+ 8 - 1
CardCollector/Bot.cs

@@ -39,7 +39,14 @@ namespace CardCollector
 
         private static async void SavingChanges(object o, ElapsedEventArgs e)
         {
-            await CardCollectorDatabase.Instance.SaveChangesAsync();
+            try
+            {
+                await CardCollectorDatabase.Instance.SaveChangesAsync();
+            }
+            catch (Exception)
+            {
+                // ignored
+            }
         }
     }
 }

+ 1 - 0
CardCollector/CardCollector.csproj

@@ -6,6 +6,7 @@
     </PropertyGroup>
 
     <ItemGroup>
+        <PackageReference Include="EPPlus" Version="5.7.4" />
         <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.7" />
         <PackageReference Include="MySql.Data" Version="8.0.25" />
         <PackageReference Include="MySql.EntityFrameworkCore" Version="5.0.3.1" />

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

@@ -1,5 +1,6 @@
 using System.Threading.Tasks;
 using CardCollector.Commands.Message;
+using CardCollector.Commands.Message.TextMessage;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;

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

@@ -1,5 +1,6 @@
 using System.Threading.Tasks;
 using CardCollector.Commands.Message;
+using CardCollector.Commands.Message.TextMessage;
 using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;

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

@@ -1,6 +1,7 @@
 using System;
 using System.Threading.Tasks;
 using CardCollector.Commands.Message;
+using CardCollector.Commands.Message.TextMessage;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;

+ 194 - 0
CardCollector/Commands/Message/DocumentMessage/UploadFileMessage.cs

@@ -0,0 +1,194 @@
+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 UploadFileMessage : Message
+    {
+        protected override string CommandText => "";
+        public override async Task Execute()
+        {
+            /* Очищаем чат */
+            await User.ClearChat();
+            User.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);
+                /* Сообщаем пользователю, что список стикеров обновится через 1.5 часа */
+                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"], 
+                        IncomeCoins = int.Parse(fields["IncomeCoins"]), IncomeGems = int.Parse(fields["IncomeGems"]), 
+                        IncomeTime = int.Parse(fields["IncomeTime"]), PriceCoins = int.Parse(fields["PriceCoins"]),
+                        PriceGems = int.Parse(fields["PriceGems"]), Tier = int.Parse(fields["Tier"]), 
+                        Emoji = fields["Emoji"], Description = fields["Description"]
+                    };
+                    newStickers.Add(sticker);
+                }
+
+                var timer = new Timer
+                {
+                    Interval = 10 * 60 * 1000,
+                    Enabled = true,
+                    AutoReset = false,
+                };
+
+                async void SavingStickersToDatabase(object sender, ElapsedEventArgs args)
+                {
+                    var stickerSet = (await Bot.Client.GetStickerSetAsync(
+                        $"{(isAnimated ? "animated" : "static")}_by_{AppSettings.NAME}")).Stickers.ToList();
+                    Logs.LogOut("Saving stickers " + 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]);
+                    }
+                    while (stickerSet.Count > 0)
+                    {
+                        var stickerId = stickerSet[0].FileId;
+                        try
+                        {
+                            await Bot.Client.DeleteStickerFromSetAsync(stickerId);
+                            stickerSet.RemoveAt(0);
+                        }
+                        catch (Exception)
+                        {
+                            Logs.LogOut("Cant delete sticker " + stickerId);
+                            Logs.LogOut(stickerSet.Count);
+                            Logs.LogOutJson(stickerSet[0]);
+                        }
+                    }
+                }
+                
+                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, $"animated_by_{AppSettings.NAME}",
+                    new InputFileStream(fileStream), emoji);
+                else
+                {
+                    var onlineFile = await Bot.Client.UploadStickerFileAsync(User.Id, fileInfo.OpenRead());
+                    await Bot.Client.AddStickerToSetAsync(User.Id, $"static_by_{AppSettings.NAME}",
+                        new InputMedia(onlineFile.FileId), emoji);
+                }
+            }
+            catch (Exception)
+            {
+                fileStream.Close();
+                fileStream = fileInfo.OpenRead();
+                if (isAnimated) 
+                    await Bot.Client.CreateNewAnimatedStickerSetAsync(User.Id, $"animated_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, $"static_by_{AppSettings.NAME}",
+                        "static", new InputMedia(onlineFile.FileId), emoji);
+                }
+            }
+            fileStream.Close();
+        }
+
+        protected internal override bool IsMatches(string command)
+        {
+            return User != null 
+                ? User.State == UserState.UploadFile
+                : base.IsMatches(command);
+        }
+
+        public UploadFileMessage() { }
+        public UploadFileMessage(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 48 - 26
CardCollector/Commands/Message/Message.cs

@@ -3,11 +3,14 @@ using System.Threading.Tasks;
 using CardCollector.Controllers;
 using System.Collections.Generic;
 using System.Linq;
+using CardCollector.Commands.Message.DocumentMessage;
+using CardCollector.Commands.Message.TextMessage;
 using CardCollector.DataBase.Entity;
 using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
 using Telegram.Bot;
 using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
 
 namespace CardCollector.Commands.Message
 {
@@ -23,24 +26,30 @@ namespace CardCollector.Commands.Message
     public abstract class Message : UpdateModel
     {
         /* Список команд */
-        private static readonly List<Message> List = new()
-        {
-            // Команда "Профиль"
-            new ProfileMessage(),
-            // Команда "/start"
-            new StartMessage(),
-            // Команда "Коллекция"
-            new CollectionMessage(),
-            // Команда "Магазин"
-            new ShopMessage(),
-            // Команда "Аукцион"
-            new AuctionMessage(),
-            // Ожидание ввода эмоджи
-            new EnterEmojiMessage(),
-            
-            // Команда "Показать пример"
-            new ShowSampleMessage()
-        };
+        private static readonly List<Message>
+            TextCommandsList = new() {
+                // Команда "Профиль"
+                new ProfileMessage(),
+                // Команда "/start"
+                new StartMessage(),
+                // Команда "Коллекция"
+                new CollectionMessage(),
+                // Команда "Магазин"
+                new ShopMessage(),
+                // Команда "Аукцион"
+                new AuctionMessage(),
+                // Ожидание ввода эмоджи
+                new EnterEmojiMessage(),
+                // Загрузка стикерпака
+                new DownloadStickerPackMessage(),
+
+                // Команда "Показать пример"
+                new ShowSampleMessage()
+            },
+            FileCommandsList = new() {
+                /* Выгрузка файлов к боту */
+                new UploadFileMessage(),
+            };
 
         /* Метод, создающий объекты команд исходя из полученного обновления */
         public static async Task<UpdateModel> Factory(Update update)
@@ -51,26 +60,39 @@ namespace CardCollector.Commands.Message
                 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<Message>()
+            };
+            
+            /* Данные определяем исходя из типа сообщения */
+            var data = update.Message.Type switch
+            {
+                MessageType.Text => update.Message.Text,
+                MessageType.Document => update.Message.Document.FileId,
+                _ => ""
+            };
             
             // Объект пользователя
             var user = await UserDao.GetUser(update.Message!.From);
             
-            // Если пользователь заблокирован или сообщение не содержит текст или пользователь - бот
-            if (user.IsBlocked || update.Message!.Text == null || update.Message!.From!.IsBot) return new IgnoreUpdate();
-
-            // Текст команды
-            var command = update.Message!.Text;
+            // Если пользователь заблокирован или пользователь - бот, то мы игнорируем дальнейшие действия
+            if (user.IsBlocked || update.Message!.From!.IsBot) return new IgnoreUpdate();
         
             // Удаляем сообщение пользователя, оно нам больше не нужно
             await MessageController.DeleteMessage(user, update.Message.MessageId);
             
             // Возвращаем объект, если команда совпала
-            foreach (var item in List.Where(item => item.IsMatches(command)))
+            foreach (var item in list.Where(item => item.IsMatches(data)))
                 if(Activator.CreateInstance(item.GetType(), user, update) is Message executor)
-                    if (executor.IsMatches(command)) return executor;
+                    if (executor.IsMatches(data)) return executor;
         
             // Возвращаем команда не найдена, если код дошел до сюда
-            return new CommandNotFound(user, update, command);
+            return new CommandNotFound(user, update, data);
         }
 
         protected Message(UserEntity user, Update update) : base(user, update) { }

+ 1 - 1
CardCollector/Commands/Message/AuctionMessage.cs → CardCollector/Commands/Message/TextMessage/AuctionMessage.cs

@@ -3,7 +3,7 @@ using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands.Message.TextMessage
 {
     /* Реалищует команду "Аукцион" */
     public class AuctionMessage : Message

+ 1 - 2
CardCollector/Commands/Message/CollectionMessage.cs → CardCollector/Commands/Message/TextMessage/CollectionMessage.cs

@@ -1,10 +1,9 @@
 using System.Threading.Tasks;
-using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands.Message.TextMessage
 {
     /* Реализует команду "Коллекция" */
     public class CollectionMessage : Message

+ 30 - 0
CardCollector/Commands/Message/TextMessage/DownloadStickerPackMessage.cs

@@ -0,0 +1,30 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.Message.TextMessage
+{
+    public class DownloadStickerPackMessage : Message
+    {
+        protected override string CommandText => Text.download_stickerpack;
+        public override async Task Execute()
+        {
+            await User.ClearChat();
+            User.State = UserState.UploadFile;
+            var result = await MessageController.SendMessage(User, Messages.upload_file, Keyboard.CancelKeyboard);
+            User.Messages.Add(result.MessageId);
+        }
+
+        protected internal override bool IsMatches(string command)
+        {
+            return User != null 
+                ? base.IsMatches(command) && User.PrivilegeLevel >= Constants.ARTIST_PRIVILEGE_LEVEL
+                : base.IsMatches(command);
+        }
+
+        public DownloadStickerPackMessage() { }
+        public DownloadStickerPackMessage(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 1 - 1
CardCollector/Commands/Message/EnterEmojiMessage.cs → CardCollector/Commands/Message/TextMessage/EnterEmojiMessage.cs

@@ -6,7 +6,7 @@ using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands.Message.TextMessage
 {
     public class EnterEmojiMessage : Message
     {

+ 1 - 1
CardCollector/Commands/Message/ProfileMessage.cs → CardCollector/Commands/Message/TextMessage/ProfileMessage.cs

@@ -5,7 +5,7 @@ using CardCollector.Resources;
 using Telegram.Bot;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands.Message.TextMessage
 {
     /* Команда "Профиль" Отображает профиль пользователя и его баланс */
     public class ProfileMessage : Message

+ 1 - 2
CardCollector/Commands/Message/ShopMessage.cs → CardCollector/Commands/Message/TextMessage/ShopMessage.cs

@@ -1,10 +1,9 @@
 using System.Threading.Tasks;
-using CardCollector.Controllers;
 using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands.Message.TextMessage
 {
     /* Реализует команду "Магазин" */
     public class ShopMessage : Message

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

@@ -4,7 +4,7 @@ using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands.Message.TextMessage
 {
     /* Этот класс реализует отправку нового сообщения с фильтрами пользователя */
     public class ShowFiltersMenu : Message

+ 1 - 1
CardCollector/Commands/Message/ShowSampleMessage.cs → CardCollector/Commands/Message/TextMessage/ShowSampleMessage.cs

@@ -5,7 +5,7 @@ using CardCollector.Resources;
 using Telegram.Bot.Types;
 using Telegram.Bot.Types.ReplyMarkups;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands.Message.TextMessage
 {
     /* Этот класс можно использовать для тестирования или наброски эскизов
      Команда "Показать пример" доступна только пользователям с уровнем доступа "Разработчик" и выше

+ 1 - 1
CardCollector/Commands/Message/StartMessage.cs → CardCollector/Commands/Message/TextMessage/StartMessage.cs

@@ -4,7 +4,7 @@ using CardCollector.DataBase.Entity;
 using CardCollector.Resources;
 using Telegram.Bot.Types;
 
-namespace CardCollector.Commands.Message
+namespace CardCollector.Commands.Message.TextMessage
 {
     /* Обработка команды "/start" */
     public class StartMessage : Message

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

@@ -158,8 +158,7 @@ namespace CardCollector.DataBase.Entity
             var result = new List<InlineQueryResult>();
             foreach (var item in list)
             {
-                result.Add(new 
-                    InlineQueryResultCachedSticker(
+                result.Add(new InlineQueryResultCachedSticker(
                         $"{(Constants.UNLIMITED_ALL_STICKERS ? Command.unlimited_stickers : "")}{command}={item.Md5Hash}",
                         item.Id));
                 /* Ограничение Telegram API по количеству результатов в 50 шт. */

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

@@ -54,5 +54,10 @@ namespace CardCollector.DataBase.EntityDao
         {
             return await Table.ToListAsync();
         }
+
+        public static async Task AddNew(StickerEntity sticker)
+        {
+            await Table.AddAsync(sticker);
+        }
     }
 }

+ 6 - 0
CardCollector/Resources/Keyboard.cs

@@ -65,6 +65,12 @@ namespace CardCollector.Resources
             new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.back)},
         });
 
+        /* Клавиатура с одной кнопкой отмены */
+        public static readonly InlineKeyboardMarkup CancelKeyboard = new (new[]
+        {
+            new[] {InlineKeyboardButton.WithCallbackData(Text.cancel, Command.cancel)},
+        });
+
         /* Клавиатура меню выбора цен */
         public static readonly InlineKeyboardMarkup PriceOptions = new (new[]
         {

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

@@ -123,6 +123,24 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Удаляю файлы....
+        /// </summary>
+        internal static string deleting_files {
+            get {
+                return ResourceManager.GetString("deleting_files", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Загружаю файл....
+        /// </summary>
+        internal static string downloading_file {
+            get {
+                return ResourceManager.GetString("downloading_file", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Эмоция:.
         /// </summary>
@@ -168,6 +186,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Читаю документ....
+        /// </summary>
+        internal static string reading_document {
+            get {
+                return ResourceManager.GetString("reading_document", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Установите фильтры кнопками ниже:.
         /// </summary>
@@ -195,6 +222,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Стикеры будут обновлены через 10 минут.
+        /// </summary>
+        internal static string stickers_will_be_updated {
+            get {
+                return ResourceManager.GetString("stickers_will_be_updated", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Тир:.
         /// </summary>
@@ -203,5 +239,32 @@ namespace CardCollector.Resources {
                 return ResourceManager.GetString("tier", resourceCulture);
             }
         }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Произошла неожиданная ошибка, попробуйте еще раз..
+        /// </summary>
+        internal static string unexpected_exception {
+            get {
+                return ResourceManager.GetString("unexpected_exception", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Распаковываю архив.....
+        /// </summary>
+        internal static string unzipping_file {
+            get {
+                return ResourceManager.GetString("unzipping_file", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Загрузите архив со стикерами и таблицей в формате zip.
+        /// </summary>
+        internal static string upload_file {
+            get {
+                return ResourceManager.GetString("upload_file", resourceCulture);
+            }
+        }
     }
 }

+ 21 - 0
CardCollector/Resources/Messages.resx

@@ -66,4 +66,25 @@
     <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>Стикеры будут обновлены через 10 минут</value>
+    </data>
+    <data name="unexpected_exception" xml:space="preserve">
+        <value>Произошла неожиданная ошибка, попробуйте еще раз.</value>
+    </data>
 </root>

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

@@ -114,6 +114,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Загрузить стикерпак.
+        /// </summary>
+        internal static string download_stickerpack {
+            get {
+                return ResourceManager.GetString("download_stickerpack", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Эмоция.
         /// </summary>

+ 3 - 0
CardCollector/Resources/Text.resx

@@ -78,4 +78,7 @@
     <data name="no" xml:space="preserve">
         <value>Нет</value>
     </data>
+    <data name="download_stickerpack" xml:space="preserve">
+        <value>Загрузить стикерпак</value>
+    </data>
 </root>

+ 4 - 1
CardCollector/Resources/UserState.cs

@@ -9,8 +9,11 @@
         ShopMenu,
         /* Пользователь в меню аукциона */
         AuctionMenu,
+        /* Пользователь, загружающий файл */
+        UploadFile,
         
         /* Пользователь в базовом состоянии (по умолчанию) */
-        Default
+        Default,
+        
     }
 }

+ 20 - 1
CardCollector/Utilities.cs

@@ -1,4 +1,9 @@
-using System.Text;
+using System;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using CardCollector.Resources;
+using Telegram.Bot;
 
 namespace CardCollector
 {
@@ -21,5 +26,19 @@ namespace CardCollector
                 sb.Append(t.ToString("X2"));
             return sb.ToString();
         }
+
+        public static async Task DownloadFile(string fileId)
+        {
+            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");
+            });
+        }
     }
 }