diff --git a/Core/Service/UserService.cs b/Core/Service/UserService.cs new file mode 100644 index 0000000..0a8e15b --- /dev/null +++ b/Core/Service/UserService.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Telegram.Bot.Types; + +namespace Kruzya.TelegramBot.Core.Service +{ + public class UserService + { + private List _superUsers; + + public UserService(IConfiguration configuration) + { + _superUsers = configuration.GetSection("SuperUsers") + .GetChildren() + .Select(q => Convert.ToInt64(q.Value)) + .ToList(); + } + + public bool IsUserSuper(User user) + { + return IsUserSuper(user.Id); + } + + public bool IsUserSuper(long userId) + { + return _superUsers.Contains(userId); + } + } +} \ No newline at end of file diff --git a/Core/Startup.cs b/Core/Startup.cs index e53c10a..361929c 100644 --- a/Core/Startup.cs +++ b/Core/Startup.cs @@ -2,6 +2,7 @@ using BotFramework; using Kruzya.TelegramBot.Core.Cache; using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Service; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -41,6 +42,8 @@ namespace Kruzya.TelegramBot.Core // Cache for users. services.AddSingleton, MemoryCache>(); + services.AddSingleton(); + // Trigger module handlers. _core.Modules.ConfigureServices(services); } diff --git a/modules/ChatManagement/Handler/Reputation.cs b/modules/ChatManagement/Handler/Reputation.cs index e9d34a6..a419a04 100644 --- a/modules/ChatManagement/Handler/Reputation.cs +++ b/modules/ChatManagement/Handler/Reputation.cs @@ -4,14 +4,22 @@ using System.Collections.Generic; using System.Threading.Tasks; using BotFramework.Attributes; using BotFramework.Enums; +using BotFramework.Utils; using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Service; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Telegram.Bot; using Telegram.Bot.Types; namespace West.TelegramBot.ChatManagement.Handler { public class Reputation : ManagementHandler { + private readonly UserService _userService; + private readonly ILogger _logger; + private static readonly List RepUpChars = new() { "+", @@ -22,7 +30,6 @@ namespace West.TelegramBot.ChatManagement.Handler "👍🏿", "👍🏻" }; - private static readonly List RepDownChars = new() { "-", @@ -34,7 +41,11 @@ namespace West.TelegramBot.ChatManagement.Handler "👎🏻" }; - public Reputation(CoreContext db) : base(db) { } + public Reputation(CoreContext db, UserService userService, ILogger logger) : base(db) + { + _userService = userService; + _logger = logger; + } [Command("whois", CommandParseMode.Both)] public async Task HandleWhois() @@ -96,7 +107,35 @@ namespace West.TelegramBot.ChatManagement.Handler $"{action} репутацию {receiver.ToHtml()}({repCount})"); } } + + [ParametrizedCommand(InChat.Public, "setrep", CommandParseMode.Both)] + public async Task HandleSetRep(int reputation) + { + if (!_userService.IsUserSuper(From)) + { + _logger.LogDebug("{User} attempted to call /setrep in {Chat}", From, Chat.Title); + return; + } + + if (RepliedUser == null || RepliedUser.IsBot) + { + return; + } + var opt = await GetReputationOption(RepliedUser); + opt.SetValue(reputation); + Db.MarkAsModified(opt); + + await Reply( + new HtmlString() + .Code("Пользователю ") + .UserMention(RepliedUser) + .Code($" установлена репутация: {reputation}") + .ToString() + ); + } + + private async Task GetUserReputation(User user) { return (await GetReputationOption(user)).GetValue(); diff --git a/modules/Entertainment/Entertainment.csproj b/modules/Entertainment/Entertainment.csproj index b24581b..609decc 100644 --- a/modules/Entertainment/Entertainment.csproj +++ b/modules/Entertainment/Entertainment.csproj @@ -10,10 +10,6 @@ - - - - diff --git a/modules/Entertainment/Handler/Common.cs b/modules/Entertainment/Handler/Common.cs new file mode 100644 index 0000000..ffdb496 --- /dev/null +++ b/modules/Entertainment/Handler/Common.cs @@ -0,0 +1,60 @@ +#nullable enable + +using System; +using System.Threading.Tasks; +using BotFramework; +using BotFramework.Attributes; +using BotFramework.Enums; +using BotFramework.Utils; +using Kruzya.TelegramBot.Core.Data; +using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Service; +using Microsoft.Extensions.Logging; +using Telegram.Bot; +using Telegram.Bot.Types.Enums; + +namespace West.Entertainment.Handler +{ + public class Common : BotEventHandler + { + private readonly UserService _userService; + private readonly ILogger _logger; + private readonly CoreContext _db; + + public Common(CoreContext db, ILogger logger, UserService userService) + { + _db = db; + _logger = logger; + _userService = userService; + } + + [Command(InChat.Public, "cooldowns", CommandParseMode.Both)] + public async Task HandleCooldowns() + { + if (!_userService.IsUserSuper(From)) + { + _logger.LogDebug("{User} attempted to call /cooldowns in \"{Chat}\"", From, Chat.Title); + return; + } + + var cds = new[] { "python", "javascript" }; + + var msg = new HtmlString().Bold("Кулдауны:").Br(); + foreach (var cd in cds) + { + var opt = await _db.UserValues.FindOption(Chat.Id, $"{cd}NextUse"); + var val = opt.GetValue(); + if (val == default) + { + continue; + } + + msg.Text($"{cd}: ") + .Code((val - DateTime.Now).Duration().ToString()) + .Br(); + } + + await Bot.SendTextMessageAsync(Chat.Id, msg.ToString(), ParseMode.Html); + } + } +} \ No newline at end of file diff --git a/modules/Entertainment/Handler/Fun/AbstractAnimationReply.cs b/modules/Entertainment/Handler/Fun/AbstractAnimationReply.cs index e120ce7..12fea45 100644 --- a/modules/Entertainment/Handler/Fun/AbstractAnimationReply.cs +++ b/modules/Entertainment/Handler/Fun/AbstractAnimationReply.cs @@ -19,7 +19,7 @@ namespace West.Entertainment.Handler.Fun private readonly ILogger _logger; - private static TimeSpan Cooldown => TimeSpan.FromMinutes(new Random().Next(10, 1337)); + protected virtual TimeSpan Cooldown => TimeSpan.FromMinutes(new Random().Next(10, 1337)); protected Message? Message => RawUpdate.Message; @@ -57,14 +57,14 @@ namespace West.Entertainment.Handler.Fun if (!AnimationMatch.IsMatch(text.ToLower()) || text.StartsWith("/set") || !CanHandle()) return false; var animationFileId = (await GetAnimationFileOption()).GetValue(string.Empty); - var lastUseOption = await GetLastUseOption(); + var nextUseOption = await GetNextUseOption(); _logger.LogDebug("Animation: {Text}, ToLower: {ToLowerText}", Message!.Text, Message!.Text!.ToLower()); if (animationFileId.IsNullOrEmpty()) return false; - var nextUseDt = lastUseOption.GetValue(); + var nextUseDt = nextUseOption.GetValue(); if (nextUseDt > DateTime.Now) { _logger.LogInformation("{Name} is on cooldown for chat \"{ChatTitle}\". Will be available at {Time}", @@ -76,8 +76,8 @@ namespace West.Entertainment.Handler.Fun await Bot.SendAnimationAsync(Chat.Id, animationFileId, replyToMessageId: GetMessageIdToReply() ?? Message?.MessageId); - lastUseOption.SetValue(DateTime.Now + Cooldown); - _db.MarkAsModified(lastUseOption); + nextUseOption.SetValue(DateTime.Now + Cooldown); + _db.MarkAsModified(nextUseOption); return true; } @@ -87,7 +87,7 @@ namespace West.Entertainment.Handler.Fun return await _db.UserValues.FindOrCreateOption(Chat.Id, $"{GetAnimationName()}File"); } - private async Task GetLastUseOption() + private async Task GetNextUseOption() { return await _db.UserValues.FindOrCreateOption(Chat.Id, $"{GetAnimationName()}NextUse"); } diff --git a/modules/Entertainment/Handler/Fun/Bayan.cs b/modules/Entertainment/Handler/Fun/Bayan.cs index e3cc91a..777252a 100644 --- a/modules/Entertainment/Handler/Fun/Bayan.cs +++ b/modules/Entertainment/Handler/Fun/Bayan.cs @@ -1,5 +1,6 @@ #nullable enable +using System; using System.Text.RegularExpressions; using System.Threading.Tasks; using BotFramework.Attributes; @@ -12,6 +13,8 @@ namespace West.Entertainment.Handler.Fun { public class Bayan : AbstractAnimationReply { + protected override TimeSpan Cooldown => TimeSpan.Zero; + public Bayan(CoreContext db, ILogger logger) : base(db, logger) { } protected override Regex AnimationMatch