Browse Source

daily task system

Tigran 3 years ago
parent
commit
a0b462b549

+ 7 - 3
CardCollector.sln.DotSettings.user

@@ -7,13 +7,17 @@
   <Assembly Path="C:\Users\DarkGolly\.nuget\packages\telegram.bot\17.0.0-alpha.3\lib\netcoreapp3.1\Telegram.Bot.dll" />
 &lt;/AssemblyExplorer&gt;</s:String>
 	
-	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FCommand/@EntryIndexedValue">False</s:Boolean>
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FDailyTasks_002FDescriptions/@EntryIndexedValue">False</s:Boolean>
 	
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FDailyTasks_002FTitles/@EntryIndexedValue">False</s:Boolean>
 	
+	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=CardCollector_002FResources_002FCommand/@EntryIndexedValue">True</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_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_002FText/@EntryIndexedValue">False</s:Boolean>
 	
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>

+ 3 - 0
CardCollector/Bot.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Timers;
+using CardCollector.DailyTasks;
 using CardCollector.DataBase;
 using CardCollector.DataBase.EntityDao;
 using CardCollector.Resources;
@@ -44,6 +45,8 @@ namespace CardCollector
             
             /* Запускаем механизм уведомления */
             Utilities.SetUpTimer(Constants.DailyTaskAlert, DailyTaskAlert);
+            /* Запускаем сброс ежедневных заданий */
+            Utilities.SetUpTimer(Constants.DailyTaskReset, DailyTask.ResetTasks);
             
             _end.WaitOne();
             Logs.LogOut("Stopping program");

+ 18 - 0
CardCollector/CardCollector.csproj

@@ -35,6 +35,14 @@
         <Generator>ResXFileCodeGenerator</Generator>
         <LastGenOutput>Command.Designer.cs</LastGenOutput>
       </EmbeddedResource>
+      <EmbeddedResource Update="DailyTasks\Titles.resx">
+        <Generator>ResXFileCodeGenerator</Generator>
+        <LastGenOutput>Titles.Designer.cs</LastGenOutput>
+      </EmbeddedResource>
+      <EmbeddedResource Update="DailyTasks\Descriptions.resx">
+        <Generator>ResXFileCodeGenerator</Generator>
+        <LastGenOutput>Descriptions.Designer.cs</LastGenOutput>
+      </EmbeddedResource>
     </ItemGroup>
 
     <ItemGroup>
@@ -63,6 +71,16 @@
         <AutoGen>True</AutoGen>
         <DependentUpon>Command.resx</DependentUpon>
       </Compile>
+      <Compile Update="DailyTasks\Titles.Designer.cs">
+        <DesignTime>True</DesignTime>
+        <AutoGen>True</AutoGen>
+        <DependentUpon>Title.resx</DependentUpon>
+      </Compile>
+      <Compile Update="DailyTasks\Descriptions.Designer.cs">
+        <DesignTime>True</DesignTime>
+        <AutoGen>True</AutoGen>
+        <DependentUpon>Description.resx</DependentUpon>
+      </Compile>
     </ItemGroup>
 
 </Project>

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

@@ -33,6 +33,7 @@ namespace CardCollector.Commands.ChosenInlineResult
         
             // Обработка результата при отправке стикера
             new SendStickerResult(),
+            new SendPrivateSticker(),
             // Обработка результата при выборе продавца
             new SelectTraderResult(),
             

+ 25 - 0
CardCollector/Commands/ChosenInlineResult/SendPrivateSticker.cs

@@ -0,0 +1,25 @@
+using System.Threading.Tasks;
+using CardCollector.DailyTasks;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.ChosenInlineResult
+{
+    public class SendPrivateSticker : ChosenInlineResult
+    {
+        protected override string CommandText => Command.send_private_sticker;
+        
+        public override async Task Execute()
+        {
+            var dailyTask = DailyTask.List[DailyTaskKeys.SendStickersToUsers];
+            if (await dailyTask.Execute(User.Id))
+            {
+                // TODO give reward
+            }
+        }
+
+        public SendPrivateSticker() { }
+        public SendPrivateSticker(UserEntity user, Update update) : base(user, update) { }
+    }
+}

+ 0 - 5
CardCollector/Commands/ChosenInlineResult/SendStickerResult.cs

@@ -12,11 +12,6 @@ namespace CardCollector.Commands.ChosenInlineResult
         protected override string CommandText => Command.send_sticker;
         public override Task Execute()
         {
-            /*// Получаем MD5 хеш из полученного запроса разделением по символу '='
-            var shortHash = InlineResult.Split('=')[1];
-            // Вычитаем один стикер из общего их количества
-            User.Stickers[shortHash].Count--;*/
-            // Возвращаем CompletedTask, означающий завершение данного метода
             return Task.CompletedTask;
         }
         

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

@@ -40,6 +40,7 @@ namespace CardCollector.Commands.InlineQuery
             new ShowTradersInBotChat(),
             // Показать стикеры в личных сообщениях с ботом для выбора или просмотра информации
             new ShowStickersInBotChat(),
+            new ShowStickersInPrivate(),
         };
         
         

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

@@ -25,10 +25,10 @@ namespace CardCollector.Commands.InlineQuery
         }
 
         /* Команда пользователя удовлетворяет условию, если она вызвана
-         в беседе/канале/личных сообщениях (кроме личных сообщений с ботом) */
+         в беседе/канале */
         protected internal override bool IsMatches(string command)
         {
-            return command.Contains("Group") || command.Contains("Supergroup") || command.Contains("Private");
+            return command.Contains("Group") || command.Contains("Supergroup");
         }
 
         public ShowStickersInGroup() { }

+ 36 - 0
CardCollector/Commands/InlineQuery/ShowStickersInPrivate.cs

@@ -0,0 +1,36 @@
+using System.Threading.Tasks;
+using CardCollector.Controllers;
+using CardCollector.DataBase.Entity;
+using CardCollector.Resources;
+using Telegram.Bot.Types;
+
+namespace CardCollector.Commands.InlineQuery
+{
+    public class ShowStickersInPrivate : InlineQuery
+    {
+        /* Команда - пустая строка, поскольку пользователь может вводить любые слова
+         после @имя_бота, введенная фраза будет использоваться для фильтрации стикеров */
+        protected override string CommandText => "";
+        
+        public override async Task Execute()
+        {
+            // Фильтр - введенная пользователем фраза
+            var filter = Update.InlineQuery!.Query;
+            // Получаем список стикеров
+            var stickersList = await User.GetStickersList(filter);
+            var results = stickersList.ToTelegramResults(Command.send_private_sticker, false);
+            // Посылаем пользователю ответ на его запрос
+            await MessageController.AnswerInlineQuery(InlineQueryId, results);
+        }
+
+        /* Команда пользователя удовлетворяет условию, если она вызвана
+         в личных сообщениях (кроме личных сообщений с ботом) */
+        protected internal override bool IsMatches(string command)
+        {
+            return command.Contains("Private");
+        }
+
+        public ShowStickersInPrivate() { }
+        public ShowStickersInPrivate(UserEntity user, Update update) : base(user, update) { }
+    }
+}

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

@@ -24,7 +24,7 @@ namespace CardCollector.Commands.Message.TextMessage
                 /* Количество алмазов */
                 $"{Messages.gems}: {User.Cash.Gems}{Text.gem}",
                 /* Клавиатура профиля */
-                Keyboard.GetProfileKeyboard(income));
+                Keyboard.GetProfileKeyboard(income, User.PrivilegeLevel));
             /* Записываем id нового сообщения */
             User.Session.Messages.Add(message.MessageId);
         }

+ 21 - 0
CardCollector/DailyTasks/CustomTasks/SendStickers.cs

@@ -0,0 +1,21 @@
+using System.Threading.Tasks;
+using CardCollector.DataBase.EntityDao;
+
+namespace CardCollector.DailyTasks.CustomTasks
+{
+    public class SendStickers : DailyTask
+    {
+        public override int Id => (int)DailyTaskKeys.SendStickersToUsers;
+        public override int Goal => 10;
+        public override string Title => Titles.send_stickers;
+        public override string Description => Descriptions.send_stickers;
+
+        public override async Task<bool> Execute(long userId, object[] args = null)
+        {
+            var task = await DailyTaskDao.GetTaskInfo(userId, Id);
+            if (task.Progress == 0) return true;
+            task.Progress--;
+            return task.Progress == 0;
+        }
+    }
+}

+ 34 - 0
CardCollector/DailyTasks/DailyTask.cs

@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Timers;
+using CardCollector.DailyTasks.CustomTasks;
+using CardCollector.DataBase.EntityDao;
+
+namespace CardCollector.DailyTasks
+{
+    public enum DailyTaskKeys
+    {
+        SendStickersToUsers = 1,
+    }
+
+    public abstract class DailyTask
+    {
+        public static Dictionary<DailyTaskKeys, DailyTask> List = new()
+        {
+            {DailyTaskKeys.SendStickersToUsers, new SendStickers()}
+        };
+        
+        public abstract int Id { get; }
+        public abstract int Goal { get; }
+        public abstract string Title { get; }
+        public abstract string Description { get; }
+
+        public abstract Task<bool> Execute(long userId, object[] args = null);
+
+        public static async void ResetTasks(object o, ElapsedEventArgs e)
+        {
+            await foreach (var item in DailyTaskDao.GetAll())
+                item.Progress = List[(DailyTaskKeys) item.TaskId].Goal;
+        }
+    }
+}

+ 72 - 0
CardCollector/DailyTasks/Descriptions.Designer.cs

@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace CardCollector.DailyTasks {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Descriptions {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Descriptions() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CardCollector.DailyTasks.Descriptions", typeof(Descriptions).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Необходимо отправить стикеры в личные сообщения десяти пользователей используя бота..
+        /// </summary>
+        internal static string send_stickers {
+            get {
+                return ResourceManager.GetString("send_stickers", resourceCulture);
+            }
+        }
+    }
+}

+ 24 - 0
CardCollector/DailyTasks/Descriptions.resx

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<root>
+    <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+        <xsd:element name="root" msdata:IsDataSet="true">
+            
+        </xsd:element>
+    </xsd:schema>
+    <resheader name="resmimetype">
+        <value>text/microsoft-resx</value>
+    </resheader>
+    <resheader name="version">
+        <value>1.3</value>
+    </resheader>
+    <resheader name="reader">
+        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    </resheader>
+    <resheader name="writer">
+        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    </resheader>
+    <data name="send_stickers" xml:space="preserve">
+        <value>Необходимо отправить стикеры в личные сообщения десяти пользователей используя бота.</value>
+    </data>
+</root>

+ 72 - 0
CardCollector/DailyTasks/Titles.Designer.cs

@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace CardCollector.DailyTasks {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Titles {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Titles() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CardCollector.DailyTasks.Titles", typeof(Titles).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Отправка стикеров 10-и пользователям.
+        /// </summary>
+        internal static string send_stickers {
+            get {
+                return ResourceManager.GetString("send_stickers", resourceCulture);
+            }
+        }
+    }
+}

+ 24 - 0
CardCollector/DailyTasks/Titles.resx

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<root>
+    <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+        <xsd:element name="root" msdata:IsDataSet="true">
+            
+        </xsd:element>
+    </xsd:schema>
+    <resheader name="resmimetype">
+        <value>text/microsoft-resx</value>
+    </resheader>
+    <resheader name="version">
+        <value>1.3</value>
+    </resheader>
+    <resheader name="reader">
+        <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    </resheader>
+    <resheader name="writer">
+        <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    </resheader>
+    <data name="send_stickers" xml:space="preserve">
+        <value>Отправка стикеров 10-и пользователям</value>
+    </data>
+</root>

+ 1 - 0
CardCollector/DataBase/CardCollectorDatabase.cs

@@ -63,6 +63,7 @@ namespace CardCollector.DataBase
         public DbSet<StickerEntity> Stickers { get; set; }
         public DbSet<AuctionEntity> Auction { get; set; }
         public DbSet<ShopEntity> Shop { get; set; }
+        public DbSet<DailyTaskEntity> DailyTasks { get; set; }
 
         
         /* Конфигурация подключения к БД */

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

@@ -0,0 +1,21 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace CardCollector.DataBase.Entity
+{
+    [Table("daily_task")]
+    public class DailyTaskEntity
+    {
+        /* Id записи в таблице, роли не играет */
+        [Column("id"), MaxLength(127)] public long Id { get; set; }
+        
+        /* Id задачи */
+        [Column("task_id"), MaxLength(32)] public int TaskId { get; set; }
+        
+        /* Id пользователя */
+        [Column("user_id"), MaxLength(127)] public long UserId { get; set; }
+        
+        /* Прогресс пользователя по задаче */
+        [Column("progress"), MaxLength(32)] public int Progress { get; set; }
+    }
+}

+ 42 - 0
CardCollector/DataBase/EntityDao/DailyTaskDao.cs

@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using CardCollector.DailyTasks;
+using CardCollector.DataBase.Entity;
+using Microsoft.EntityFrameworkCore;
+
+namespace CardCollector.DataBase.EntityDao
+{
+    public static class DailyTaskDao
+    {
+        private static readonly CardCollectorDatabase Instance = CardCollectorDatabase.GetSpecificInstance(typeof(DailyTaskDao));
+        private static readonly DbSet<DailyTaskEntity> Table = Instance.DailyTasks;
+        
+        public static IAsyncEnumerable<DailyTaskEntity> GetAll()
+        {
+            return Table;
+        }
+
+        /* Добавляет новое отношение в таблицу */
+        public static async Task<DailyTaskEntity> AddNew(long userId, int taskId)
+        {
+            if (await Table.FirstOrDefaultAsync(item => item.UserId == userId && item.TaskId == taskId) is { } obj)
+                return obj;
+            var newTask = new DailyTaskEntity()
+            {
+                TaskId = taskId,
+                UserId = userId,
+                Progress = DailyTask.List[(DailyTaskKeys)taskId].Goal
+            };
+            var result = await Table.AddAsync(newTask);
+            await Instance.SaveChangesAsync();
+            return result.Entity;
+        }
+
+        public static async Task<DailyTaskEntity> GetTaskInfo(long userId, int taskId)
+        {
+            return await Table.FirstOrDefaultAsync(item => item.UserId == userId && item.TaskId == taskId) 
+                   ?? await AddNew(userId, taskId);
+        }
+    }
+}

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

@@ -168,6 +168,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABD.
+        /// </summary>
+        internal static string control_panel {
+            get {
+                return ResourceManager.GetString("control_panel", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to AAN.
         /// </summary>
@@ -177,6 +186,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABE.
+        /// </summary>
+        internal static string daily_tasks {
+            get {
+                return ResourceManager.GetString("daily_tasks", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to AAO.
         /// </summary>
@@ -195,6 +213,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABF.
+        /// </summary>
+        internal static string my_opencases {
+            get {
+                return ResourceManager.GetString("my_opencases", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to AAQ.
         /// </summary>
@@ -258,6 +285,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to ABG.
+        /// </summary>
+        internal static string send_private_sticker {
+            get {
+                return ResourceManager.GetString("send_private_sticker", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to AAX.
         /// </summary>

+ 12 - 0
CardCollector/Resources/Command.resx

@@ -102,4 +102,16 @@
     <data name="stop_bot" xml:space="preserve">
         <value>Остановить</value>
     </data>
+    <data name="control_panel" xml:space="preserve">
+        <value>ABD</value>
+    </data>
+    <data name="my_opencases" xml:space="preserve">
+        <value>ABF</value>
+    </data>
+    <data name="daily_tasks" xml:space="preserve">
+        <value>ABE</value>
+    </data>
+    <data name="send_private_sticker" xml:space="preserve">
+        <value>ABG</value>
+    </data>
 </root>

+ 6 - 1
CardCollector/Resources/Constants.cs

@@ -1,4 +1,6 @@
 using System;
+// ReSharper disable ConditionIsAlwaysTrueOrFalse
+#pragma warning disable 162
 
 namespace CardCollector.Resources
 {
@@ -22,6 +24,9 @@ namespace CardCollector.Resources
         public const int ARTIST_PRIVILEGE_LEVEL = 4;
         public const int SESSION_ACTIVE_PERIOD = DEBUG ? 1 : 60;
         public const int COMBINE_COUNT = 5;
-        public static readonly TimeSpan DailyTaskAlert = new(12, 0, 0);
+        
+        /* Время оповещения и сброса ежедневных заданий */
+        public static readonly TimeSpan DailyTaskAlert = DEBUG ? new TimeSpan(21, 0, 0) : new TimeSpan(12, 0, 0);
+        public static readonly TimeSpan DailyTaskReset = DEBUG ? new TimeSpan(21, 0, 0) : new TimeSpan(0, 0, 0);
     }
 }

+ 5 - 2
CardCollector/Resources/Keyboard.cs

@@ -299,13 +299,16 @@ namespace CardCollector.Resources
         }
 
         /* Клавиатура, отображаемая вместе с сообщением профиля */
-        public static InlineKeyboardMarkup GetProfileKeyboard(int income)
+        public static InlineKeyboardMarkup GetProfileKeyboard(int income, int privilegeLevel)
         {
             var keyboard = new List<InlineKeyboardButton[]>
             {
                 new[] {InlineKeyboardButton.WithCallbackData($"{Text.collect} {income}{Text.coin}", Command.collect_income)},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.daily_tasks, Command.daily_tasks)},
+                new[] {InlineKeyboardButton.WithCallbackData(Text.my_opencases, Command.my_opencases)},
             };
-            
+            if (privilegeLevel > 2) keyboard.Add(
+                new[] {InlineKeyboardButton.WithCallbackData(Text.control_panel, Command.control_panel)});
             return new InlineKeyboardMarkup(keyboard);
         }
     }

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

@@ -186,6 +186,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Панель управления.
+        /// </summary>
+        internal static string control_panel {
+            get {
+                return ResourceManager.GetString("control_panel", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Количество.
         /// </summary>
@@ -195,6 +204,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Ежедневные задания.
+        /// </summary>
+        internal static string daily_tasks {
+            get {
+                return ResourceManager.GetString("daily_tasks", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Удалить.
         /// </summary>
@@ -303,6 +321,15 @@ namespace CardCollector.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Мои паки.
+        /// </summary>
+        internal static string my_opencases {
+            get {
+                return ResourceManager.GetString("my_opencases", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to →.
         /// </summary>

+ 9 - 0
CardCollector/Resources/Text.resx

@@ -171,4 +171,13 @@
     <data name="total" xml:space="preserve">
         <value>Всего</value>
     </data>
+    <data name="control_panel" xml:space="preserve">
+        <value>Панель управления</value>
+    </data>
+    <data name="daily_tasks" xml:space="preserve">
+        <value>Ежедневные задания</value>
+    </data>
+    <data name="my_opencases" xml:space="preserve">
+        <value>Мои паки</value>
+    </data>
 </root>