From 8de8eac95944b3d1e927f792633fbad9029e1897 Mon Sep 17 00:00:00 2001 From: Andriy <30056636+West14@users.noreply.github.com> Date: Thu, 20 Jul 2023 20:24:16 +0300 Subject: [PATCH 1/7] initial chat options implementation --- Core/Core.csproj | 2 +- Core/Data/ChatOptionValue.cs | 38 ++++++++ Core/Data/CoreContext.cs | 2 + .../RepositoryExtension.ChatOption.cs | 26 +++++ .../20230720171645_AddChatOptions.Designer.cs | 95 +++++++++++++++++++ .../20230720171645_AddChatOptions.cs | 58 +++++++++++ Core/Migrations/CoreContextModelSnapshot.cs | 27 +++++- Core/Options/AbstractOption.cs | 25 +++++ Core/Options/DbOptionProvider.cs | 59 ++++++++++++ Core/Options/IOption.cs | 10 ++ Core/Options/IOptionProvider.cs | 9 ++ Core/Options/OptionAttribute.cs | 17 ++++ Core/Startup.cs | 3 + Core/Test.cs | 20 ++++ modules/Entertainment/Entertainment.cs | 3 + modules/Entertainment/Handler/Common.cs | 20 +++- modules/Entertainment/Options/NsfwOption.cs | 11 +++ 17 files changed, 420 insertions(+), 5 deletions(-) create mode 100644 Core/Data/ChatOptionValue.cs create mode 100644 Core/Extensions/RepositoryExtension.ChatOption.cs create mode 100644 Core/Migrations/20230720171645_AddChatOptions.Designer.cs create mode 100644 Core/Migrations/20230720171645_AddChatOptions.cs create mode 100644 Core/Options/AbstractOption.cs create mode 100644 Core/Options/DbOptionProvider.cs create mode 100644 Core/Options/IOption.cs create mode 100644 Core/Options/IOptionProvider.cs create mode 100644 Core/Options/OptionAttribute.cs create mode 100644 Core/Test.cs create mode 100644 modules/Entertainment/Options/NsfwOption.cs diff --git a/Core/Core.csproj b/Core/Core.csproj index cadaad8..f28bf9a 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -1,4 +1,4 @@ - + net6.0 diff --git a/Core/Data/ChatOptionValue.cs b/Core/Data/ChatOptionValue.cs new file mode 100644 index 0000000..3e3f010 --- /dev/null +++ b/Core/Data/ChatOptionValue.cs @@ -0,0 +1,38 @@ +#nullable enable + +using Microsoft.EntityFrameworkCore; +using System.Text.Json.Serialization; +using System.Text.Json; +using System; + +namespace Kruzya.TelegramBot.Core.Data; + +[PrimaryKey(nameof(ChatId), nameof(OptionId))] +public class ChatOptionValue +{ + public long ChatId { get; set; } + public string OptionId { get; set; } + public byte[] Value { get; set; } + + + public T GetValue(T? defVal = default) + { + try + { + return JsonSerializer.Deserialize(new ReadOnlySpan(Value)); + } + catch (Exception) + { + return defVal; + } + } + + public void SetValue(T value) + { + Value = JsonSerializer.SerializeToUtf8Bytes(value, new JsonSerializerOptions + { + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }); + } +} \ No newline at end of file diff --git a/Core/Data/CoreContext.cs b/Core/Data/CoreContext.cs index 286e98c..6843a11 100644 --- a/Core/Data/CoreContext.cs +++ b/Core/Data/CoreContext.cs @@ -15,6 +15,8 @@ namespace Kruzya.TelegramBot.Core.Data /// public DbSet UserValues { get; set; } + public DbSet ChatOptionValues { get; set; } + public CoreContext(DbContextOptions ctx) : base(ctx) { Database.Migrate(); diff --git a/Core/Extensions/RepositoryExtension.ChatOption.cs b/Core/Extensions/RepositoryExtension.ChatOption.cs new file mode 100644 index 0000000..c45a733 --- /dev/null +++ b/Core/Extensions/RepositoryExtension.ChatOption.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using Kruzya.TelegramBot.Core.Data; +using Microsoft.EntityFrameworkCore; + +namespace Kruzya.TelegramBot.Core.Extensions; + +public partial class RepositoryExtension +{ + public static async Task FindOption(this DbSet repository, long chatId, + string optionId) + { + return await repository.SingleOrDefaultAsync(x => x.ChatId == chatId && x.OptionId == optionId); + } + + public static async Task FindOrCreateOption(this DbSet repository, long chatId, + string optionId) + { + var opt = await repository.FindOption(chatId, optionId) ?? new ChatOptionValue + { + ChatId = chatId, + OptionId = optionId + }; + + return opt; + } +} \ No newline at end of file diff --git a/Core/Migrations/20230720171645_AddChatOptions.Designer.cs b/Core/Migrations/20230720171645_AddChatOptions.Designer.cs new file mode 100644 index 0000000..751cbe8 --- /dev/null +++ b/Core/Migrations/20230720171645_AddChatOptions.Designer.cs @@ -0,0 +1,95 @@ +// +using System; +using Kruzya.TelegramBot.Core.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Kruzya.TelegramBot.Core.Migrations +{ + [DbContext(typeof(CoreContext))] + [Migration("20230720171645_AddChatOptions")] + partial class AddChatOptions + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUser", b => + { + b.Property("BotUserId") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ChatId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("BotUserId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUserValue", b => + { + b.Property("BotUserValueId") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("BotUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("Value") + .HasColumnType("longblob"); + + b.Property("ValueType") + .HasColumnType("longtext"); + + b.HasKey("BotUserValueId"); + + b.HasIndex("BotUserId"); + + b.ToTable("UserValues"); + }); + + modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.ChatOptionValue", b => + { + b.Property("ChatId") + .HasColumnType("bigint"); + + b.Property("OptionId") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("ChatId", "OptionId"); + + b.ToTable("ChatOptionValues"); + }); + + modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUserValue", b => + { + b.HasOne("Kruzya.TelegramBot.Core.Data.BotUser", "BotUser") + .WithMany() + .HasForeignKey("BotUserId"); + + b.Navigation("BotUser"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Core/Migrations/20230720171645_AddChatOptions.cs b/Core/Migrations/20230720171645_AddChatOptions.cs new file mode 100644 index 0000000..29c59d0 --- /dev/null +++ b/Core/Migrations/20230720171645_AddChatOptions.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Kruzya.TelegramBot.Core.Migrations +{ + /// + public partial class AddChatOptions : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ChatOptionValues", + columns: table => new + { + ChatId = table.Column(type: "bigint", nullable: false), + OptionId = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longblob", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChatOptionValues", x => new { x.ChatId, x.OptionId }); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ChatOptionValues"); + + migrationBuilder.AlterColumn( + name: "ValueType", + table: "UserValues", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "UserValues", + type: "longtext CHARACTER SET utf8mb4", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/Core/Migrations/CoreContextModelSnapshot.cs b/Core/Migrations/CoreContextModelSnapshot.cs index 8545ed9..971a65a 100644 --- a/Core/Migrations/CoreContextModelSnapshot.cs +++ b/Core/Migrations/CoreContextModelSnapshot.cs @@ -5,6 +5,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +#nullable disable + namespace Kruzya.TelegramBot.Core.Migrations { [DbContext(typeof(CoreContext))] @@ -14,7 +16,7 @@ namespace Kruzya.TelegramBot.Core.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.2") + .HasAnnotation("ProductVersion", "7.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUser", b => @@ -44,13 +46,13 @@ namespace Kruzya.TelegramBot.Core.Migrations .HasColumnType("char(36)"); b.Property("Name") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.Property("Value") .HasColumnType("longblob"); b.Property("ValueType") - .HasColumnType("longtext CHARACTER SET utf8mb4"); + .HasColumnType("longtext"); b.HasKey("BotUserValueId"); @@ -59,11 +61,30 @@ namespace Kruzya.TelegramBot.Core.Migrations b.ToTable("UserValues"); }); + modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.ChatOptionValue", b => + { + b.Property("ChatId") + .HasColumnType("bigint"); + + b.Property("OptionId") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("ChatId", "OptionId"); + + b.ToTable("ChatOptionValues"); + }); + modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUserValue", b => { b.HasOne("Kruzya.TelegramBot.Core.Data.BotUser", "BotUser") .WithMany() .HasForeignKey("BotUserId"); + + b.Navigation("BotUser"); }); #pragma warning restore 612, 618 } diff --git a/Core/Options/AbstractOption.cs b/Core/Options/AbstractOption.cs new file mode 100644 index 0000000..067e923 --- /dev/null +++ b/Core/Options/AbstractOption.cs @@ -0,0 +1,25 @@ +using System.Runtime.Serialization; +using System.Threading.Tasks; + +namespace Kruzya.TelegramBot.Core.Options; + +public abstract class AbstractOption : IOption + where TOption: AbstractOption +{ + private readonly IOptionProvider _optionProvider; + + protected AbstractOption(IOptionProvider optionProvider) + { + _optionProvider = optionProvider; + } + + public async Task GetValueAsync(long chatId) + { + return await _optionProvider.GetValueAsync(chatId); + } + + public async Task SetValueAsync(long chatId, TValue value) + { + await _optionProvider.SetValueAsync(chatId, value); + } +} \ No newline at end of file diff --git a/Core/Options/DbOptionProvider.cs b/Core/Options/DbOptionProvider.cs new file mode 100644 index 0000000..a67fc0b --- /dev/null +++ b/Core/Options/DbOptionProvider.cs @@ -0,0 +1,59 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; +using Kruzya.TelegramBot.Core.Data; +using Kruzya.TelegramBot.Core.Extensions; + +namespace Kruzya.TelegramBot.Core.Options; + +public class DbOptionProvider : IOptionProvider +{ + private readonly CoreContext _db; + + public DbOptionProvider(CoreContext db) + { + _db = db; + } + + public async Task GetValueAsync(long chatId) + { + return await GetValueAsync( + chatId, + GetOptionId(), + GetDefaultValue() + ); + } + + public async Task GetValueAsync(long chatId, string optionId, TValue defVal) + { + return (await _db.ChatOptionValues.FindOrCreateOption(chatId, optionId)).GetValue(defVal); + } + + public async Task SetValueAsync(long chatId, TValue value) + { + var chatOption = await _db.ChatOptionValues.FindOrCreateOption(chatId, GetOptionId()); + chatOption.SetValue(value); + _db.AddOrUpdate(chatOption); + } + + protected OptionAttribute GetOptionAttribute() + { + return typeof(TOption).GetCustomAttribute(); + } + + protected string GetOptionId() + { + var optionId = GetOptionAttribute()?.OptionId; + if (optionId == null) + { + throw new ArgumentException("Option must implement OptionAttribute"); + } + + return optionId; + } + + protected TValue GetDefaultValue() + { + return (TValue) GetOptionAttribute().DefaultValue; + } +} \ No newline at end of file diff --git a/Core/Options/IOption.cs b/Core/Options/IOption.cs new file mode 100644 index 0000000..04fe8a9 --- /dev/null +++ b/Core/Options/IOption.cs @@ -0,0 +1,10 @@ +using System.Runtime.Serialization; +using System.Threading.Tasks; + +namespace Kruzya.TelegramBot.Core.Options; + +public interface IOption +{ + public Task GetValueAsync(long chatId); + public Task SetValueAsync(long chatId, TValue value); +} \ No newline at end of file diff --git a/Core/Options/IOptionProvider.cs b/Core/Options/IOptionProvider.cs new file mode 100644 index 0000000..f09e690 --- /dev/null +++ b/Core/Options/IOptionProvider.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Kruzya.TelegramBot.Core.Options; + +public interface IOptionProvider +{ + public Task GetValueAsync(long chatId); + public Task SetValueAsync(long chatId, TValue value); +} \ No newline at end of file diff --git a/Core/Options/OptionAttribute.cs b/Core/Options/OptionAttribute.cs new file mode 100644 index 0000000..7de5136 --- /dev/null +++ b/Core/Options/OptionAttribute.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.Serialization; + +namespace Kruzya.TelegramBot.Core.Options; + +[AttributeUsage(AttributeTargets.Class)] +public class OptionAttribute : Attribute +{ + public string OptionId { get; } + public object DefaultValue { get; } + + public OptionAttribute(string optionId, object defaultValue = default) + { + OptionId = optionId; + DefaultValue = defaultValue; + } +} \ No newline at end of file diff --git a/Core/Startup.cs b/Core/Startup.cs index b5e18f4..ca3f127 100644 --- a/Core/Startup.cs +++ b/Core/Startup.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; +using Kruzya.TelegramBot.Core.Options; using Telegram.Bot.Types; namespace Kruzya.TelegramBot.Core @@ -53,6 +54,8 @@ namespace Kruzya.TelegramBot.Core services.AddSingleton>(); services.AddHostedService(); + services.AddScoped(); + // Trigger module handlers. _core.Modules.ConfigureServices(services); } diff --git a/Core/Test.cs b/Core/Test.cs new file mode 100644 index 0000000..aadf958 --- /dev/null +++ b/Core/Test.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using BotFramework; +using BotFramework.Attributes; +using BotFramework.Enums; +using Telegram.Bot; + +namespace Kruzya.TelegramBot.Core; + +public class Test : BotEventHandler +{ + public uint Counter { get; set; } = 5; + + + [Command(InChat.All, "test_state")] + public async Task TestState() + { + Counter++; + await Bot.SendTextMessageAsync(Chat.Id, Counter.ToString()); + } +} \ No newline at end of file diff --git a/modules/Entertainment/Entertainment.cs b/modules/Entertainment/Entertainment.cs index d184b4b..8e419f3 100644 --- a/modules/Entertainment/Entertainment.cs +++ b/modules/Entertainment/Entertainment.cs @@ -3,7 +3,9 @@ global using GuessCache = Kruzya.TelegramBot.Core.Cache.MemoryCache(); + services.AddScoped(); } } } \ No newline at end of file diff --git a/modules/Entertainment/Handler/Common.cs b/modules/Entertainment/Handler/Common.cs index b3bf726..2788fac 100644 --- a/modules/Entertainment/Handler/Common.cs +++ b/modules/Entertainment/Handler/Common.cs @@ -12,20 +12,23 @@ using Kruzya.TelegramBot.Core.Service; using Microsoft.Extensions.Logging; using Telegram.Bot; using Telegram.Bot.Types.Enums; +using West.Entertainment.Options; namespace West.Entertainment.Handler { public class Common : BotEventHandler { private readonly UserService _userService; + private readonly NsfwOption _nsfwOption; private readonly ILogger _logger; private readonly CoreContext _db; - public Common(CoreContext db, ILogger logger, UserService userService) + public Common(CoreContext db, ILogger logger, UserService userService, NsfwOption nsfwOption) { _db = db; _logger = logger; _userService = userService; + _nsfwOption = nsfwOption; } [Command(InChat.Public, "cooldowns", CommandParseMode.Both)] @@ -59,5 +62,20 @@ namespace West.Entertainment.Handler await Bot.SendTextMessageAsync(Chat.Id, msg.ToString(), parseMode: ParseMode.Html); } + + [Command("test_option_get")] + public async Task HandleTestOptionGet() + { + await Bot.SendTextMessageAsync(Chat.Id, $"nsfw option is: {await _nsfwOption.GetValueAsync(Chat.Id)}"); + } + + [Command("test_option_set")] + public async Task HandleTestOptionSet() + { + var value = await _nsfwOption.GetValueAsync(Chat.Id); + await _nsfwOption.SetValueAsync(Chat.Id, !value); + + await Bot.SendTextMessageAsync(Chat.Id, $"nsfw option is: {await _nsfwOption.GetValueAsync(Chat.Id)}"); + } } } \ No newline at end of file diff --git a/modules/Entertainment/Options/NsfwOption.cs b/modules/Entertainment/Options/NsfwOption.cs new file mode 100644 index 0000000..2307941 --- /dev/null +++ b/modules/Entertainment/Options/NsfwOption.cs @@ -0,0 +1,11 @@ +using Kruzya.TelegramBot.Core.Options; + +namespace West.Entertainment.Options; + +[Option("allowNSFW", false)] +public class NsfwOption : AbstractOption +{ + public NsfwOption(IOptionProvider optionProvider) : base(optionProvider) + { + } +} \ No newline at end of file From 467ccd4fdee8a59390df4231add65448a7779adc Mon Sep 17 00:00:00 2001 From: Andriy <30056636+West14@users.noreply.github.com> Date: Thu, 20 Jul 2023 21:22:35 +0300 Subject: [PATCH 2/7] [core] move core handlers into subfolder --- Core/{ => Handler}/CommonHandler.cs | 10 +++++----- Core/{ => Handler}/GeneralHandler.cs | 8 +++----- modules/ChatManagement/Handler/Ban.cs | 2 +- modules/ChatManagement/Handler/BanStickerSet.cs | 4 ++-- modules/ChatManagement/Handler/Rules.cs | 2 +- modules/ChatManagement/Handler/Warn.cs | 2 +- modules/ChatManagement/Handler/WhoIs.cs | 2 +- modules/Entertainment/Handler/Guess.cs | 1 + modules/Entertainment/Handler/Roll.cs | 1 + modules/Reputation/Handler/Reputation.cs | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) rename Core/{ => Handler}/CommonHandler.cs (95%) rename Core/{ => Handler}/GeneralHandler.cs (88%) diff --git a/Core/CommonHandler.cs b/Core/Handler/CommonHandler.cs similarity index 95% rename from Core/CommonHandler.cs rename to Core/Handler/CommonHandler.cs index 142c0bc..b6758e9 100644 --- a/Core/CommonHandler.cs +++ b/Core/Handler/CommonHandler.cs @@ -9,7 +9,7 @@ using Telegram.Bot; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; -namespace Kruzya.TelegramBot.Core +namespace Kruzya.TelegramBot.Core.Handler { public abstract class CommonHandler : BotEventHandler { @@ -18,7 +18,7 @@ namespace Kruzya.TelegramBot.Core protected Message? Message => RawUpdate.Message; protected User? RepliedUser => Message?.ReplyToMessage?.From; - + protected CommonHandler(CoreContext db) { Db = db; @@ -34,8 +34,8 @@ namespace Kruzya.TelegramBot.Core { return RepliedUser != null && await Bot.CanPunishMember(Chat, From, RepliedUser); } - - + + protected async Task BanMember(long userId, DateTime banEnd) { await Bot.RestrictChatMemberAsync( @@ -48,7 +48,7 @@ namespace Kruzya.TelegramBot.Core banEnd ); } - + protected async Task GetWarnOption(User user) { return await Db.UserValues.FindOrCreateOption(Chat.Id, user.Id, "warnCount"); diff --git a/Core/GeneralHandler.cs b/Core/Handler/GeneralHandler.cs similarity index 88% rename from Core/GeneralHandler.cs rename to Core/Handler/GeneralHandler.cs index 9dc2227..15a76e0 100644 --- a/Core/GeneralHandler.cs +++ b/Core/Handler/GeneralHandler.cs @@ -1,14 +1,12 @@ using System; -using System.Linq; using System.Threading.Tasks; using BotFramework; using BotFramework.Attributes; using Kruzya.TelegramBot.Core.Data; -using Kruzya.TelegramBot.Core.Extensions; using Microsoft.EntityFrameworkCore; using Telegram.Bot.Types; -namespace Kruzya.TelegramBot.Core +namespace Kruzya.TelegramBot.Core.Handler { public class GeneralHandler : BotEventHandler { @@ -22,7 +20,7 @@ namespace Kruzya.TelegramBot.Core } protected readonly CoreContext databaseContext; - + public GeneralHandler(CoreContext dbCtx) { databaseContext = dbCtx; @@ -32,7 +30,7 @@ namespace Kruzya.TelegramBot.Core [Priority(short.MaxValue - 10)] public async Task Listener() { - foreach (var message in new Message[] {RawUpdate.Message, RawUpdate.EditedMessage, RawUpdate.ChannelPost, RawUpdate.EditedChannelPost}) + foreach (var message in new Message[] { RawUpdate.Message, RawUpdate.EditedMessage, RawUpdate.ChannelPost, RawUpdate.EditedChannelPost }) { if (message == null) { diff --git a/modules/ChatManagement/Handler/Ban.cs b/modules/ChatManagement/Handler/Ban.cs index 7067b30..927ab32 100644 --- a/modules/ChatManagement/Handler/Ban.cs +++ b/modules/ChatManagement/Handler/Ban.cs @@ -4,9 +4,9 @@ using System; using System.Threading.Tasks; using BotFramework.Attributes; using BotFramework.Enums; -using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Handler; using Telegram.Bot; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; diff --git a/modules/ChatManagement/Handler/BanStickerSet.cs b/modules/ChatManagement/Handler/BanStickerSet.cs index c179c1f..ad8b00d 100644 --- a/modules/ChatManagement/Handler/BanStickerSet.cs +++ b/modules/ChatManagement/Handler/BanStickerSet.cs @@ -1,5 +1,5 @@ -using Kruzya.TelegramBot.Core; -using Kruzya.TelegramBot.Core.Data; +using Kruzya.TelegramBot.Core.Data; +using Kruzya.TelegramBot.Core.Handler; namespace West.TelegramBot.ChatManagement.Handler; diff --git a/modules/ChatManagement/Handler/Rules.cs b/modules/ChatManagement/Handler/Rules.cs index a794e2d..1c73e14 100644 --- a/modules/ChatManagement/Handler/Rules.cs +++ b/modules/ChatManagement/Handler/Rules.cs @@ -1,9 +1,9 @@ using System.Threading.Tasks; using BotFramework.Attributes; using BotFramework.Enums; -using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Handler; using Telegram.Bot; using Telegram.Bot.Exceptions; using West.TelegramBot.ChatManagement.Service; diff --git a/modules/ChatManagement/Handler/Warn.cs b/modules/ChatManagement/Handler/Warn.cs index b828052..d8694ab 100644 --- a/modules/ChatManagement/Handler/Warn.cs +++ b/modules/ChatManagement/Handler/Warn.cs @@ -3,8 +3,8 @@ using System.Threading.Tasks; using BotFramework.Attributes; using BotFramework.Enums; -using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core.Data; +using Kruzya.TelegramBot.Core.Handler; using West.TelegramBot.ChatManagement.Service; namespace West.TelegramBot.ChatManagement.Handler; diff --git a/modules/ChatManagement/Handler/WhoIs.cs b/modules/ChatManagement/Handler/WhoIs.cs index 2b38905..4ff9ba3 100644 --- a/modules/ChatManagement/Handler/WhoIs.cs +++ b/modules/ChatManagement/Handler/WhoIs.cs @@ -3,9 +3,9 @@ using System; using System.Threading.Tasks; using BotFramework.Attributes; using BotFramework.Enums; -using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Handler; using Kruzya.TelegramBot.Core.Service; using Microsoft.Extensions.DependencyInjection; using Telegram.Bot; diff --git a/modules/Entertainment/Handler/Guess.cs b/modules/Entertainment/Handler/Guess.cs index c701460..15af1ce 100644 --- a/modules/Entertainment/Handler/Guess.cs +++ b/modules/Entertainment/Handler/Guess.cs @@ -9,6 +9,7 @@ using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core.AutoDelete; using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Handler; using Kruzya.TelegramBot.Core.Service; using Telegram.Bot; using Telegram.Bot.Types; diff --git a/modules/Entertainment/Handler/Roll.cs b/modules/Entertainment/Handler/Roll.cs index 2fef7f9..8b82025 100644 --- a/modules/Entertainment/Handler/Roll.cs +++ b/modules/Entertainment/Handler/Roll.cs @@ -15,6 +15,7 @@ using System.Collections.Concurrent; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types; using Microsoft.AspNetCore.Components.Forms; +using Kruzya.TelegramBot.Core.Handler; namespace West.Entertainment.Handler; diff --git a/modules/Reputation/Handler/Reputation.cs b/modules/Reputation/Handler/Reputation.cs index a64bc3a..6adb558 100644 --- a/modules/Reputation/Handler/Reputation.cs +++ b/modules/Reputation/Handler/Reputation.cs @@ -3,9 +3,9 @@ using BotFramework.Attributes; using BotFramework.Enums; using BotFramework.Utils; -using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Handler; using Kruzya.TelegramBot.Core.Service; using Microsoft.Extensions.Logging; using Telegram.Bot; From 27ddfd39ccc1d4fd5854732b18d3e15343868f4f Mon Sep 17 00:00:00 2001 From: Andriy <30056636+West14@users.noreply.github.com> Date: Fri, 21 Jul 2023 17:14:11 +0300 Subject: [PATCH 3/7] [core] Massive setting system update - Get rid of attribute crap. Now only interface is required - Rename canUse to isAdmin in `Bot.IsUserAdminAsync` to match the actual meaning of variable - Made `AnswerQuery` reusable by moving it from ContentStore to Core's CommonHandler - Chat Settings edit frontend (inline keyboard) - Rename NsfwOption to AllowNsfw and move it to ContentStore module - Remove option backend testing commands - Add KarmaMode option as an example of Select option type --- Core/Data/ChatOptionValue.cs | 6 +- Core/Extensions/BotExtension.cs | 6 +- Core/Handler/CommonHandler.cs | 5 + Core/Handler/SettingsHandler.cs | 184 ++++++++++++++++++++ Core/Options/AbstractOption.cs | 12 +- Core/Options/DbOptionProvider.cs | 40 +---- Core/Options/IOption.cs | 14 +- Core/Options/IOptionProvider.cs | 4 +- Core/Options/OptionAttribute.cs | 17 -- Core/Options/OptionType.cs | 7 + Core/Options/SelectChoice.cs | 29 +++ modules/ContentStore/ContentStore.cs | 4 + modules/ContentStore/ContentStore.csproj | 2 +- modules/ContentStore/Handler/Store.cs | 13 +- modules/ContentStore/Option/AllowNsfw.cs | 15 ++ modules/Entertainment/Entertainment.cs | 2 - modules/Entertainment/Handler/Common.cs | 20 +-- modules/Entertainment/Options/NsfwOption.cs | 11 -- modules/Reputation/Options/KarmaMode.cs | 21 +++ modules/Reputation/Reputation.cs | 3 + modules/Reputation/Reputation.csproj | 2 +- 21 files changed, 307 insertions(+), 110 deletions(-) create mode 100644 Core/Handler/SettingsHandler.cs delete mode 100644 Core/Options/OptionAttribute.cs create mode 100644 Core/Options/OptionType.cs create mode 100644 Core/Options/SelectChoice.cs create mode 100644 modules/ContentStore/Option/AllowNsfw.cs delete mode 100644 modules/Entertainment/Options/NsfwOption.cs create mode 100644 modules/Reputation/Options/KarmaMode.cs diff --git a/Core/Data/ChatOptionValue.cs b/Core/Data/ChatOptionValue.cs index 3e3f010..9fc4027 100644 --- a/Core/Data/ChatOptionValue.cs +++ b/Core/Data/ChatOptionValue.cs @@ -1,6 +1,4 @@ -#nullable enable - -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using System.Text.Json.Serialization; using System.Text.Json; using System; @@ -15,7 +13,7 @@ public class ChatOptionValue public byte[] Value { get; set; } - public T GetValue(T? defVal = default) + public T GetValue(T defVal = default) { try { diff --git a/Core/Extensions/BotExtension.cs b/Core/Extensions/BotExtension.cs index 0ca594a..60fffaf 100644 --- a/Core/Extensions/BotExtension.cs +++ b/Core/Extensions/BotExtension.cs @@ -22,14 +22,14 @@ namespace Kruzya.TelegramBot.Core.Extensions public static async Task IsUserAdminAsync(this ITelegramBotClient bot, Chat chat, User user) { var callerMember = await bot.GetChatMemberAsync(chat, user.Id); - var canUse = callerMember is ChatMemberOwner; + var isAdmin = callerMember is ChatMemberOwner; if (callerMember is ChatMemberAdministrator callerAdmin) { - canUse = callerAdmin.CanRestrictMembers; + isAdmin = callerAdmin.CanRestrictMembers; } - return canUse; + return isAdmin; } public static async Task CanDeleteMessagesAsync(this ITelegramBotClient bot, Chat chat) diff --git a/Core/Handler/CommonHandler.cs b/Core/Handler/CommonHandler.cs index b6758e9..136a1ee 100644 --- a/Core/Handler/CommonHandler.cs +++ b/Core/Handler/CommonHandler.cs @@ -53,5 +53,10 @@ namespace Kruzya.TelegramBot.Core.Handler { return await Db.UserValues.FindOrCreateOption(Chat.Id, user.Id, "warnCount"); } + + protected async Task AnswerQuery(string id, string? message = null) + { + await Bot.AnswerCallbackQueryAsync(id, message); + } } } \ No newline at end of file diff --git a/Core/Handler/SettingsHandler.cs b/Core/Handler/SettingsHandler.cs new file mode 100644 index 0000000..bd50c70 --- /dev/null +++ b/Core/Handler/SettingsHandler.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BotFramework.Attributes; +using BotFramework.Enums; +using Kruzya.TelegramBot.Core.Data; +using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Telegram.Bot; +using Telegram.Bot.Types; +using Telegram.Bot.Types.ReplyMarkups; + +namespace Kruzya.TelegramBot.Core.Handler; + +public class SettingsHandler : CommonHandler +{ + private readonly IEnumerable _options; + + public SettingsHandler(CoreContext db, IServiceProvider serviceProvider) : base(db) + { + _options = serviceProvider.GetServices(); + } + + [Command(InChat.Public, "settings", CommandParseMode.WithUsername)] + public async Task HandleSettings() + { + if (!await Bot.IsUserAdminAsync(Chat, From)) + { + return; + } + + await Bot.SendTextMessageAsync( + Chat.Id, + "What do you want to change?", + replyMarkup: GetSettingsMarkup() + ); + } + + [Update(InChat.Public, UpdateFlag.CallbackQuery)] + public async Task HandleCallback() + { + var query = CallbackQuery; + var queryData = query.Data; + + if (queryData == null || !queryData.StartsWith("option") || !await Bot.IsUserAdminAsync(Chat, From)) + { + return; + } + + var paramList = queryData.Split('|'); + if (!long.TryParse(paramList[1], out var senderId)) + { + return; + } + + if (senderId != From.Id) + { + await AnswerQuery(query.Id, "Keyboard called by another user"); + return; + } + + if (paramList[2] == "main_menu") + { + await AnswerQuery(query.Id); + await Bot.EditMessageTextAsync(Chat.Id, query.Message!.MessageId, "What do you want to change?", + replyMarkup: GetSettingsMarkup()); + return; + } + + if (paramList[2] == "edit" || paramList[2] == "select") + { + var option = GetOptionById(paramList[3]); + if (option == null) + { + await AnswerQuery(query.Id, "Invalid option ID"); + return; + } + + switch (paramList[2]) + { + case "edit": + await HandleOptionEdit(option, paramList, query); + break; + case "select": + await HandleOptionSelect(option, paramList, query); + break; + } + } + } + + private async Task HandleOptionEdit(IOption option, string[] paramList, CallbackQuery query) + { + var chatId = Chat.Id; + var messageId = query.Message!.MessageId; + switch (option.OptionType) + { + case OptionType.OnOff: + var onOffOpt = (IOption) option; + await onOffOpt.SetValueAsync(chatId, !await onOffOpt.GetValueAsync(chatId)); + await AnswerQuery(query.Id); + + await Bot.EditMessageReplyMarkupAsync(chatId, messageId, GetSettingsMarkup()); + break; + + case OptionType.Select: + var selectOpt = (IOption) option; + await AnswerQuery(query.Id); + + await Bot.EditMessageTextAsync(Chat.Id, messageId, "Here's what you can choose:", + replyMarkup: await GetChoiceSelectKeyboard(selectOpt)); + break; + + default: + await AnswerQuery(query.Id, "Not implemented"); + break; + } + } + + private async Task HandleOptionSelect(IOption option, string[] paramList, CallbackQuery query) + { + var choice = option.ChoiceList.SingleOrDefault(x => x.Id == paramList[4]); + if (choice == null) + { + await AnswerQuery(query.Id, "Invalid option choice"); + return; + } + + var selectOption = (IOption) option; + await selectOption.SetValueAsync(Chat.Id, choice.Id); + await AnswerQuery(query.Id); + + await Bot.EditMessageReplyMarkupAsync(Chat.Id, query.Message!.MessageId, await GetChoiceSelectKeyboard(selectOption)); + } + + private async Task GetKeyboardButton(IOption option) + { + var button = new InlineKeyboardButton(option.OptionName) + { + CallbackData = $"option|{From.Id}|edit|{option.OptionId}" + }; + + if (option.OptionType == OptionType.OnOff) + { + var isEnabled = await ((IOption) option).GetValueAsync(Chat.Id); + button.Text += isEnabled ? " 🟢" : " 🔴"; + } + + return button; + } + + private async Task GetChoiceSelectKeyboard(IOption option) + { + var value = await option.GetValueAsync(Chat.Id); + var choices = option.ChoiceList.Select(x => x.ToButton(From.Id, option.OptionId, x.Id == value)) + .ToList(); + + choices.Add(new [] + { + new InlineKeyboardButton("Main menu") + { + CallbackData = $"option|{From.Id}|main_menu" + } + }); + + return new InlineKeyboardMarkup(choices); + } + + private InlineKeyboardMarkup GetSettingsMarkup() + { + var keyboardLayout = _options.ToAsyncEnumerable() + .SelectAwait(async x => await GetKeyboardButton(x)) + .ToEnumerable() + .Chunk(3); + + return new InlineKeyboardMarkup(keyboardLayout); + } + + private IOption GetOptionById(string optionId) + { + return _options.SingleOrDefault(x => x.OptionId == optionId); + } +} \ No newline at end of file diff --git a/Core/Options/AbstractOption.cs b/Core/Options/AbstractOption.cs index 067e923..bc8dd22 100644 --- a/Core/Options/AbstractOption.cs +++ b/Core/Options/AbstractOption.cs @@ -1,4 +1,4 @@ -using System.Runtime.Serialization; +using System.Collections.Generic; using System.Threading.Tasks; namespace Kruzya.TelegramBot.Core.Options; @@ -6,6 +6,12 @@ namespace Kruzya.TelegramBot.Core.Options; public abstract class AbstractOption : IOption where TOption: AbstractOption { + public abstract string OptionId { get; } + public abstract string OptionName { get; } + public abstract OptionType OptionType { get; } + public abstract TValue DefaultValue { get; } + public List ChoiceList { get; } = new(); + private readonly IOptionProvider _optionProvider; protected AbstractOption(IOptionProvider optionProvider) @@ -15,11 +21,11 @@ public abstract class AbstractOption : IOption public async Task GetValueAsync(long chatId) { - return await _optionProvider.GetValueAsync(chatId); + return await _optionProvider.GetValueAsync(chatId, OptionId, DefaultValue); } public async Task SetValueAsync(long chatId, TValue value) { - await _optionProvider.SetValueAsync(chatId, value); + await _optionProvider.SetValueAsync(chatId, OptionId, value); } } \ No newline at end of file diff --git a/Core/Options/DbOptionProvider.cs b/Core/Options/DbOptionProvider.cs index a67fc0b..4c66ecf 100644 --- a/Core/Options/DbOptionProvider.cs +++ b/Core/Options/DbOptionProvider.cs @@ -1,6 +1,4 @@ -using System; -using System.Reflection; -using System.Threading.Tasks; +using System.Threading.Tasks; using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; @@ -14,46 +12,16 @@ public class DbOptionProvider : IOptionProvider { _db = db; } - - public async Task GetValueAsync(long chatId) - { - return await GetValueAsync( - chatId, - GetOptionId(), - GetDefaultValue() - ); - } - + public async Task GetValueAsync(long chatId, string optionId, TValue defVal) { return (await _db.ChatOptionValues.FindOrCreateOption(chatId, optionId)).GetValue(defVal); } - public async Task SetValueAsync(long chatId, TValue value) + public async Task SetValueAsync(long chatId, string optionId, TValue value) { - var chatOption = await _db.ChatOptionValues.FindOrCreateOption(chatId, GetOptionId()); + var chatOption = await _db.ChatOptionValues.FindOrCreateOption(chatId, optionId); chatOption.SetValue(value); _db.AddOrUpdate(chatOption); } - - protected OptionAttribute GetOptionAttribute() - { - return typeof(TOption).GetCustomAttribute(); - } - - protected string GetOptionId() - { - var optionId = GetOptionAttribute()?.OptionId; - if (optionId == null) - { - throw new ArgumentException("Option must implement OptionAttribute"); - } - - return optionId; - } - - protected TValue GetDefaultValue() - { - return (TValue) GetOptionAttribute().DefaultValue; - } } \ No newline at end of file diff --git a/Core/Options/IOption.cs b/Core/Options/IOption.cs index 04fe8a9..8a37b24 100644 --- a/Core/Options/IOption.cs +++ b/Core/Options/IOption.cs @@ -1,10 +1,20 @@ -using System.Runtime.Serialization; +using System.Collections.Generic; using System.Threading.Tasks; namespace Kruzya.TelegramBot.Core.Options; -public interface IOption +public interface IOption { + public string OptionId { get; } + public string OptionName { get; } + public OptionType OptionType { get; } + public List ChoiceList { get; } +} + +public interface IOption : IOption +{ + public TValue DefaultValue { get; } + public Task GetValueAsync(long chatId); public Task SetValueAsync(long chatId, TValue value); } \ No newline at end of file diff --git a/Core/Options/IOptionProvider.cs b/Core/Options/IOptionProvider.cs index f09e690..ba771ed 100644 --- a/Core/Options/IOptionProvider.cs +++ b/Core/Options/IOptionProvider.cs @@ -4,6 +4,6 @@ namespace Kruzya.TelegramBot.Core.Options; public interface IOptionProvider { - public Task GetValueAsync(long chatId); - public Task SetValueAsync(long chatId, TValue value); + public Task GetValueAsync(long chatId, string optionId, TValue defVal); + public Task SetValueAsync(long chatId, string optionId, TValue value); } \ No newline at end of file diff --git a/Core/Options/OptionAttribute.cs b/Core/Options/OptionAttribute.cs deleted file mode 100644 index 7de5136..0000000 --- a/Core/Options/OptionAttribute.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Kruzya.TelegramBot.Core.Options; - -[AttributeUsage(AttributeTargets.Class)] -public class OptionAttribute : Attribute -{ - public string OptionId { get; } - public object DefaultValue { get; } - - public OptionAttribute(string optionId, object defaultValue = default) - { - OptionId = optionId; - DefaultValue = defaultValue; - } -} \ No newline at end of file diff --git a/Core/Options/OptionType.cs b/Core/Options/OptionType.cs new file mode 100644 index 0000000..6c65c56 --- /dev/null +++ b/Core/Options/OptionType.cs @@ -0,0 +1,7 @@ +namespace Kruzya.TelegramBot.Core.Options; + +public enum OptionType +{ + OnOff, + Select +} \ No newline at end of file diff --git a/Core/Options/SelectChoice.cs b/Core/Options/SelectChoice.cs new file mode 100644 index 0000000..3dc3900 --- /dev/null +++ b/Core/Options/SelectChoice.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Telegram.Bot.Types.ReplyMarkups; + +namespace Kruzya.TelegramBot.Core.Options; + +public class SelectChoice +{ + public string Id { get; } + public string FullName { get; } + public string ShortName { get; } + + public SelectChoice(string id, string fullName, string shortName) + { + Id = id; + FullName = fullName; + ShortName = shortName; + } + + public IEnumerable ToButton(long userId, string optionId, bool isActive = false) + { + return new[] + { + new InlineKeyboardButton((isActive ? "🟢 " : "🔴 ") + FullName) + { + CallbackData = $"option|{userId}|select|{optionId}|{Id}" + } + }; + } +} diff --git a/modules/ContentStore/ContentStore.cs b/modules/ContentStore/ContentStore.cs index 7710d77..294f28a 100644 --- a/modules/ContentStore/ContentStore.cs +++ b/modules/ContentStore/ContentStore.cs @@ -1,5 +1,7 @@ using Kruzya.TelegramBot.Core; +using Kruzya.TelegramBot.Core.Options; using Microsoft.Extensions.DependencyInjection; +using West.TelegramBot.ContentStore.Option; using West.TelegramBot.ContentStore.Service; namespace West.TelegramBot.ContentStore; @@ -22,6 +24,8 @@ public class ContentStore : Module public override void ConfigureServices(IServiceCollection services) { + services.AddScoped(); + foreach (var serviceType in MediaServiceTypes) { services.AddScoped(typeof(IRandomMediaService), serviceType); diff --git a/modules/ContentStore/ContentStore.csproj b/modules/ContentStore/ContentStore.csproj index d2e1a4f..c520860 100644 --- a/modules/ContentStore/ContentStore.csproj +++ b/modules/ContentStore/ContentStore.csproj @@ -1,4 +1,4 @@ - + net6.0 diff --git a/modules/ContentStore/Handler/Store.cs b/modules/ContentStore/Handler/Store.cs index a524a56..6ad2819 100644 --- a/modules/ContentStore/Handler/Store.cs +++ b/modules/ContentStore/Handler/Store.cs @@ -1,26 +1,26 @@ #nullable enable -using BotFramework; using BotFramework.Attributes; using BotFramework.Enums; +using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Extensions; +using Kruzya.TelegramBot.Core.Handler; using Kruzya.TelegramBot.Core.Service; using Microsoft.Extensions.DependencyInjection; using Telegram.Bot; -using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; using West.TelegramBot.ContentStore.Service; namespace West.TelegramBot.ContentStore.Handler; -public class Store : BotEventHandler +public class Store : CommonHandler { private readonly IReputation? _reputation; private readonly IServiceProvider _provider; private readonly ContentStore _store; - public Store(IServiceProvider provider, ContentStore store) + public Store(CoreContext db, IServiceProvider provider, ContentStore store) : base(db) { _reputation = provider.GetService(); _provider = provider; @@ -164,9 +164,4 @@ public class Store : BotEventHandler return mediaService != null; } - - private async Task AnswerQuery(string id, string? message = null) - { - await Bot.AnswerCallbackQueryAsync(id, message); - } } \ No newline at end of file diff --git a/modules/ContentStore/Option/AllowNsfw.cs b/modules/ContentStore/Option/AllowNsfw.cs new file mode 100644 index 0000000..11f047d --- /dev/null +++ b/modules/ContentStore/Option/AllowNsfw.cs @@ -0,0 +1,15 @@ +using Kruzya.TelegramBot.Core.Options; + +namespace West.TelegramBot.ContentStore.Option; + +public class AllowNsfw : AbstractOption +{ + public override string OptionId => "allowNSFW"; + public override string OptionName => "NSFW"; + public override OptionType OptionType => OptionType.OnOff; + public override bool DefaultValue => false; + + public AllowNsfw(IOptionProvider optionProvider) : base(optionProvider) + { + } +} \ No newline at end of file diff --git a/modules/Entertainment/Entertainment.cs b/modules/Entertainment/Entertainment.cs index 8e419f3..0203fdd 100644 --- a/modules/Entertainment/Entertainment.cs +++ b/modules/Entertainment/Entertainment.cs @@ -5,7 +5,6 @@ global using GuessCache = Kruzya.TelegramBot.Core.Cache.MemoryCache(); - services.AddScoped(); } } } \ No newline at end of file diff --git a/modules/Entertainment/Handler/Common.cs b/modules/Entertainment/Handler/Common.cs index 2788fac..b3bf726 100644 --- a/modules/Entertainment/Handler/Common.cs +++ b/modules/Entertainment/Handler/Common.cs @@ -12,23 +12,20 @@ using Kruzya.TelegramBot.Core.Service; using Microsoft.Extensions.Logging; using Telegram.Bot; using Telegram.Bot.Types.Enums; -using West.Entertainment.Options; namespace West.Entertainment.Handler { public class Common : BotEventHandler { private readonly UserService _userService; - private readonly NsfwOption _nsfwOption; private readonly ILogger _logger; private readonly CoreContext _db; - public Common(CoreContext db, ILogger logger, UserService userService, NsfwOption nsfwOption) + public Common(CoreContext db, ILogger logger, UserService userService) { _db = db; _logger = logger; _userService = userService; - _nsfwOption = nsfwOption; } [Command(InChat.Public, "cooldowns", CommandParseMode.Both)] @@ -62,20 +59,5 @@ namespace West.Entertainment.Handler await Bot.SendTextMessageAsync(Chat.Id, msg.ToString(), parseMode: ParseMode.Html); } - - [Command("test_option_get")] - public async Task HandleTestOptionGet() - { - await Bot.SendTextMessageAsync(Chat.Id, $"nsfw option is: {await _nsfwOption.GetValueAsync(Chat.Id)}"); - } - - [Command("test_option_set")] - public async Task HandleTestOptionSet() - { - var value = await _nsfwOption.GetValueAsync(Chat.Id); - await _nsfwOption.SetValueAsync(Chat.Id, !value); - - await Bot.SendTextMessageAsync(Chat.Id, $"nsfw option is: {await _nsfwOption.GetValueAsync(Chat.Id)}"); - } } } \ No newline at end of file diff --git a/modules/Entertainment/Options/NsfwOption.cs b/modules/Entertainment/Options/NsfwOption.cs deleted file mode 100644 index 2307941..0000000 --- a/modules/Entertainment/Options/NsfwOption.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Kruzya.TelegramBot.Core.Options; - -namespace West.Entertainment.Options; - -[Option("allowNSFW", false)] -public class NsfwOption : AbstractOption -{ - public NsfwOption(IOptionProvider optionProvider) : base(optionProvider) - { - } -} \ No newline at end of file diff --git a/modules/Reputation/Options/KarmaMode.cs b/modules/Reputation/Options/KarmaMode.cs new file mode 100644 index 0000000..404ae0f --- /dev/null +++ b/modules/Reputation/Options/KarmaMode.cs @@ -0,0 +1,21 @@ +using Kruzya.TelegramBot.Core.Options; + +namespace West.TelegramBot.Reputation.Options; + +public class KarmaMode : AbstractOption +{ + public override string OptionId => "karmaMode"; + public override string OptionName => "Karma Mode"; + public override OptionType OptionType => OptionType.Select; + public override string DefaultValue => "geometric"; + + public KarmaMode(IOptionProvider optionProvider) : base(optionProvider) + { + ChoiceList.AddRange(new [] + { + new SelectChoice("geometric", "Geometric", "G"), + new SelectChoice("linear", "Linear", "L") + }); + } +} + diff --git a/modules/Reputation/Reputation.cs b/modules/Reputation/Reputation.cs index ff524b8..5ef8769 100644 --- a/modules/Reputation/Reputation.cs +++ b/modules/Reputation/Reputation.cs @@ -1,9 +1,11 @@ using Kruzya.TelegramBot.Core; +using Kruzya.TelegramBot.Core.Options; using Kruzya.TelegramBot.Core.Service; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using West.TelegramBot.Reputation.Data; +using West.TelegramBot.Reputation.Options; using West.TelegramBot.Reputation.Service; namespace West.TelegramBot.Reputation; @@ -20,5 +22,6 @@ public class Reputation : Module services.AddDbContext(options => options.UseMySql(dsn, ServerVersion.AutoDetect(dsn))); services.AddScoped(); + services.AddScoped(); } } \ No newline at end of file diff --git a/modules/Reputation/Reputation.csproj b/modules/Reputation/Reputation.csproj index 7d3549f..28d1b18 100644 --- a/modules/Reputation/Reputation.csproj +++ b/modules/Reputation/Reputation.csproj @@ -1,4 +1,4 @@ - + net6.0 From 864f6d0f57ed623ed443e0186aff6e4e279e9abd Mon Sep 17 00:00:00 2001 From: Andriy <30056636+West14@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:38:34 +0300 Subject: [PATCH 4/7] [core] add IServiceCollection extension for option registration --- Core/Extensions/StartupExtension.cs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Core/Extensions/StartupExtension.cs b/Core/Extensions/StartupExtension.cs index 1cd4c46..fa40220 100644 --- a/Core/Extensions/StartupExtension.cs +++ b/Core/Extensions/StartupExtension.cs @@ -1,15 +1,20 @@ using System.Collections.Concurrent; using Kruzya.TelegramBot.Core.Cache; +using Kruzya.TelegramBot.Core.Options; using Microsoft.Extensions.DependencyInjection; -namespace Kruzya.TelegramBot.Core.Extensions -{ - public static class StartupExtension - { - public static IServiceCollection AddQueue(this IServiceCollection collection) - => collection.AddSingleton>(); +namespace Kruzya.TelegramBot.Core.Extensions; + +public static class StartupExtension +{ + public static IServiceCollection AddQueue(this IServiceCollection collection) + => collection.AddSingleton>(); + + public static IServiceCollection AddMemoryCache(this IServiceCollection collection) + => collection.AddSingleton>(); + + public static IServiceCollection AddOption(this IServiceCollection collection) where T : class, IOption + => collection.AddScoped() + .AddScoped(s => s.GetService()); - public static IServiceCollection AddMemoryCache(this IServiceCollection collection) - => collection.AddSingleton>(); - } } \ No newline at end of file From 613ac5ae2218a5caf763995f7998fd78865b8d1e Mon Sep 17 00:00:00 2001 From: Andriy <30056636+West14@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:39:38 +0300 Subject: [PATCH 5/7] [reputation] KarmaMode now actually changes smth --- modules/Reputation/Reputation.cs | 4 ++-- .../Reputation/Service/ReputationService.cs | 22 +++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/modules/Reputation/Reputation.cs b/modules/Reputation/Reputation.cs index 5ef8769..c2ad89b 100644 --- a/modules/Reputation/Reputation.cs +++ b/modules/Reputation/Reputation.cs @@ -1,5 +1,5 @@ using Kruzya.TelegramBot.Core; -using Kruzya.TelegramBot.Core.Options; +using Kruzya.TelegramBot.Core.Extensions; using Kruzya.TelegramBot.Core.Service; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -21,7 +21,7 @@ public class Reputation : Module var dsn = Configuration.GetConnectionString("Reputation"); services.AddDbContext(options => options.UseMySql(dsn, ServerVersion.AutoDetect(dsn))); + services.AddOption(); services.AddScoped(); - services.AddScoped(); } } \ No newline at end of file diff --git a/modules/Reputation/Service/ReputationService.cs b/modules/Reputation/Service/ReputationService.cs index 2a3c667..1169cf0 100644 --- a/modules/Reputation/Service/ReputationService.cs +++ b/modules/Reputation/Service/ReputationService.cs @@ -4,16 +4,19 @@ using Kruzya.TelegramBot.Core.Service; using Microsoft.EntityFrameworkCore; using Telegram.Bot.Types; using West.TelegramBot.Reputation.Data; +using West.TelegramBot.Reputation.Options; namespace West.TelegramBot.Reputation.Service; public class ReputationService : IReputation { private readonly ReputationContext _reputationContext; + private readonly KarmaMode _karmaMode; - public ReputationService(ReputationContext reputationContext) + public ReputationService(ReputationContext reputationContext, KarmaMode karmaMode) { _reputationContext = reputationContext; + _karmaMode = karmaMode; } public async Task GetUserReputationAsync(Chat chat, User user) @@ -45,14 +48,25 @@ public class ReputationService : IReputation public async Task GetReputationDiffAsync(Chat chat, User sender, User receiver) { var senderRep = await FindUserReputation(chat, sender); - if (senderRep == null) { return 1; } - + + var karmaMode = await _karmaMode.GetValueAsync(chat.Id); var senderValue = senderRep.Value; - return Math.Round(senderValue == 0 ? 1 : Math.Sqrt(senderValue), 2); + + double diff; + if (senderValue == 0 || karmaMode == "linear") + { + diff = 1; + } + else + { + diff = Math.Round(Math.Sqrt(senderValue), 2); + } + + return diff; } public async Task> GetChatRatingAsync(Chat chat, int limit = 10, int offset = 0) From 4893c1240d6a9a1097b7c3aa4deca3798425bb59 Mon Sep 17 00:00:00 2001 From: Andriy <30056636+West14@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:40:24 +0300 Subject: [PATCH 6/7] [store] Make store use AllowNSFW option --- modules/ContentStore/ContentStore.cs | 3 ++- modules/ContentStore/Handler/Store.cs | 2 +- .../Service/AbstractKittiesService.cs | 13 ++++------ .../Service/AbstractNsfwService.cs | 19 +++++++++++++++ .../ContentStore/Service/AnimBoobsService.cs | 24 ++++++------------- .../ContentStore/Service/CapyApiService.cs | 4 ++-- .../ContentStore/Service/GayKittiesService.cs | 3 ++- .../Service/IRandomMediaService.cs | 2 +- modules/ContentStore/Service/OBoobsService.cs | 18 ++++---------- .../Service/SomeRandomApiAbstractService.cs | 2 +- .../Service/StraightKittiesService.cs | 3 ++- 11 files changed, 47 insertions(+), 46 deletions(-) create mode 100644 modules/ContentStore/Service/AbstractNsfwService.cs diff --git a/modules/ContentStore/ContentStore.cs b/modules/ContentStore/ContentStore.cs index 294f28a..62416b9 100644 --- a/modules/ContentStore/ContentStore.cs +++ b/modules/ContentStore/ContentStore.cs @@ -1,4 +1,5 @@ using Kruzya.TelegramBot.Core; +using Kruzya.TelegramBot.Core.Extensions; using Kruzya.TelegramBot.Core.Options; using Microsoft.Extensions.DependencyInjection; using West.TelegramBot.ContentStore.Option; @@ -24,7 +25,7 @@ public class ContentStore : Module public override void ConfigureServices(IServiceCollection services) { - services.AddScoped(); + services.AddOption(); foreach (var serviceType in MediaServiceTypes) { diff --git a/modules/ContentStore/Handler/Store.cs b/modules/ContentStore/Handler/Store.cs index 6ad2819..21737fa 100644 --- a/modules/ContentStore/Handler/Store.cs +++ b/modules/ContentStore/Handler/Store.cs @@ -110,7 +110,7 @@ public class Store : CommonHandler { await PerformPurchase(mediaService, messageId); } - catch (Exception e) + catch (Exception) { answer = "Товар недоступен"; await Bot.EditMessageTextAsync(Chat.Id, diff --git a/modules/ContentStore/Service/AbstractKittiesService.cs b/modules/ContentStore/Service/AbstractKittiesService.cs index 6f53ba8..133f252 100644 --- a/modules/ContentStore/Service/AbstractKittiesService.cs +++ b/modules/ContentStore/Service/AbstractKittiesService.cs @@ -1,25 +1,23 @@ using System.Net.Http.Headers; using System.Text; using BotFramework.Utils; -using Kruzya.TelegramBot.Core.Data; -using Kruzya.TelegramBot.Core.Extensions; using Microsoft.Extensions.Configuration; using Telegram.Bot.Types; using West.TelegramBot.ContentStore.Model; +using West.TelegramBot.ContentStore.Option; namespace West.TelegramBot.ContentStore.Service; public abstract class AbstractKittiesService : IRandomMediaService { - private readonly CoreContext _db; + private readonly AllowNsfw _allowNsfw; protected abstract string VideoType { get; } public abstract StoreItem StoreItem { get; } private readonly HttpClient _httpClient; - protected AbstractKittiesService(IConfiguration configuration, CoreContext db) + protected AbstractKittiesService(IConfiguration configuration, AllowNsfw allowNsfw) { - _db = db; var section = configuration.GetSection("KittiesService"); var uri = section.GetValue("Uri"); @@ -33,6 +31,7 @@ public abstract class AbstractKittiesService : IRandomMediaService Authorization = new AuthenticationHeaderValue("Bearer", token) } }; + _allowNsfw = allowNsfw; } public async Task GetRandomMediaAsync() @@ -52,8 +51,6 @@ public abstract class AbstractKittiesService : IRandomMediaService public async Task CanView(Chat chat, User user) { - var option = await _db.UserValues.FindOrCreateOption(chat.Id, "AllowNSFW"); - - return option.GetValue(false); + return await _allowNsfw.GetValueAsync(chat.Id); } } \ No newline at end of file diff --git a/modules/ContentStore/Service/AbstractNsfwService.cs b/modules/ContentStore/Service/AbstractNsfwService.cs new file mode 100644 index 0000000..bb96667 --- /dev/null +++ b/modules/ContentStore/Service/AbstractNsfwService.cs @@ -0,0 +1,19 @@ +using Telegram.Bot.Types; +using West.TelegramBot.ContentStore.Model; +using West.TelegramBot.ContentStore.Option; + +namespace West.TelegramBot.ContentStore.Service; + +public abstract class AbstractNsfwService : IRandomMediaService +{ + private readonly AllowNsfw _allowNsfw; + public abstract StoreItem StoreItem { get; } + public abstract Task GetRandomMediaAsync(); + + protected AbstractNsfwService(AllowNsfw allowNsfw) + { + _allowNsfw = allowNsfw; + } + + public async Task CanView(Chat chat, User user) => await _allowNsfw.GetValueAsync(chat.Id); +} \ No newline at end of file diff --git a/modules/ContentStore/Service/AnimBoobsService.cs b/modules/ContentStore/Service/AnimBoobsService.cs index 6c50d58..78083d1 100644 --- a/modules/ContentStore/Service/AnimBoobsService.cs +++ b/modules/ContentStore/Service/AnimBoobsService.cs @@ -1,27 +1,24 @@ -using Kruzya.TelegramBot.Core.Data; -using Kruzya.TelegramBot.Core.Extensions; -using Telegram.Bot.Types; +using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using West.TelegramBot.ContentStore.Model; +using West.TelegramBot.ContentStore.Option; namespace West.TelegramBot.ContentStore.Service; -public class AnimBoobsService : IRandomMediaService +public class AnimBoobsService : AbstractNsfwService { - private readonly CoreContext _db; private readonly HttpClient _httpClient; - public StoreItem StoreItem { get; } = new("anim_boobs", "Сиськи движущиеся", 120, 40); + public override StoreItem StoreItem { get; } = new("anim_boobs", "Сиськи движущиеся", 120, 40); - public AnimBoobsService(CoreContext db) + public AnimBoobsService(AllowNsfw allowNsfw) : base(allowNsfw) { - _db = db; - _httpClient = new HttpClient() + _httpClient = new HttpClient { BaseAddress = new Uri("https://westdev.me/_boobs/") }; } - public async Task GetRandomMediaAsync() + public override async Task GetRandomMediaAsync() { var fileName = await _httpClient.GetStringAsync("index.php"); // https://bugs.telegram.org/c/23674 workaround @@ -33,11 +30,4 @@ public class AnimBoobsService : IRandomMediaService Nsfw = true }; } - - public async Task CanView(Chat chat, User user) - { - var option = await _db.UserValues.FindOrCreateOption(chat.Id, "AllowNSFW"); - - return option.GetValue(false); - } } \ No newline at end of file diff --git a/modules/ContentStore/Service/CapyApiService.cs b/modules/ContentStore/Service/CapyApiService.cs index 46a454c..a1f932c 100644 --- a/modules/ContentStore/Service/CapyApiService.cs +++ b/modules/ContentStore/Service/CapyApiService.cs @@ -6,8 +6,8 @@ namespace West.TelegramBot.ContentStore.Service; public class CapyApiService : IRandomMediaService { public StoreItem StoreItem { get; } = new("capybara", "Капибара", 60, 25); - public async Task GetRandomMediaAsync() + public Task GetRandomMediaAsync() { - return new RandomMedia(new InputFileUrl($"https://api.capy.lol/v1/capybara?{new Random().Next(0, 10000)}")); + return Task.FromResult(new RandomMedia(new InputFileUrl($"https://api.capy.lol/v1/capybara?{new Random().Next(0, 10000)}"))); } } \ No newline at end of file diff --git a/modules/ContentStore/Service/GayKittiesService.cs b/modules/ContentStore/Service/GayKittiesService.cs index 16f393b..e01a701 100644 --- a/modules/ContentStore/Service/GayKittiesService.cs +++ b/modules/ContentStore/Service/GayKittiesService.cs @@ -1,12 +1,13 @@ using Kruzya.TelegramBot.Core.Data; using Microsoft.Extensions.Configuration; using West.TelegramBot.ContentStore.Model; +using West.TelegramBot.ContentStore.Option; namespace West.TelegramBot.ContentStore.Service; public class GayKittiesService : AbstractKittiesService { - public GayKittiesService(IConfiguration configuration, CoreContext db) : base(configuration, db) + public GayKittiesService(IConfiguration configuration, AllowNsfw allowNsfw) : base(configuration, allowNsfw) { } diff --git a/modules/ContentStore/Service/IRandomMediaService.cs b/modules/ContentStore/Service/IRandomMediaService.cs index 2d6060e..9b29447 100644 --- a/modules/ContentStore/Service/IRandomMediaService.cs +++ b/modules/ContentStore/Service/IRandomMediaService.cs @@ -8,5 +8,5 @@ public interface IRandomMediaService public StoreItem StoreItem { get; } public Task GetRandomMediaAsync(); - public async Task CanView(Chat chat, User user) => true; + public Task CanView(Chat chat, User user) => Task.FromResult(true); } \ No newline at end of file diff --git a/modules/ContentStore/Service/OBoobsService.cs b/modules/ContentStore/Service/OBoobsService.cs index 40f697f..a60b896 100644 --- a/modules/ContentStore/Service/OBoobsService.cs +++ b/modules/ContentStore/Service/OBoobsService.cs @@ -3,25 +3,24 @@ using Kruzya.TelegramBot.Core.Extensions; using Newtonsoft.Json; using Telegram.Bot.Types; using West.TelegramBot.ContentStore.Model; +using West.TelegramBot.ContentStore.Option; namespace West.TelegramBot.ContentStore.Service; -public class OBoobsService : IRandomMediaService +public class OBoobsService : AbstractNsfwService { - private readonly CoreContext _db; private readonly HttpClient _httpClient; - public StoreItem StoreItem { get; } = new("boobs", "Сиськи", 60, 30); + public override StoreItem StoreItem { get; } = new("boobs", "Сиськи", 60, 30); - public OBoobsService(CoreContext db) + public OBoobsService(AllowNsfw allowNsfw) : base(allowNsfw) { - _db = db; _httpClient = new HttpClient { BaseAddress = new Uri("http://api.oboobs.ru") }; } - public async Task GetRandomMediaAsync() + public override async Task GetRandomMediaAsync() { // "/boobs/{start=0; sql offset}/{count=1; sql limit}/{order=-id;[id,rank,-rank,interest,-interest,random]}/ var httpResp = await _httpClient.GetAsync("boobs/1/1/random"); @@ -31,11 +30,4 @@ public class OBoobsService : IRandomMediaService return new RandomMedia(new InputFileUrl($"https://media.oboobs.ru/boobs/{boobsId}.jpg"), boobsItem.Model, true); } - - public async Task CanView(Chat chat, User user) - { - var option = await _db.UserValues.FindOrCreateOption(chat.Id, "AllowNSFW"); - - return option.GetValue(false); - } } \ No newline at end of file diff --git a/modules/ContentStore/Service/SomeRandomApiAbstractService.cs b/modules/ContentStore/Service/SomeRandomApiAbstractService.cs index 788b733..9c82aa1 100644 --- a/modules/ContentStore/Service/SomeRandomApiAbstractService.cs +++ b/modules/ContentStore/Service/SomeRandomApiAbstractService.cs @@ -22,7 +22,7 @@ public abstract class SomeRandomApiAbstractService : IRandomMediaService public async Task GetRandomMediaAsync() { var response = await _httpClient.GetStringAsync($"https://some-random-api.com/animal/{ApiId}"); - var image = JsonConvert.DeserializeObject(response); + var image = JsonConvert.DeserializeObject(response)!; return new RandomMedia(new InputFileUrl(image.Image), image.Fact); } diff --git a/modules/ContentStore/Service/StraightKittiesService.cs b/modules/ContentStore/Service/StraightKittiesService.cs index f614455..e346906 100644 --- a/modules/ContentStore/Service/StraightKittiesService.cs +++ b/modules/ContentStore/Service/StraightKittiesService.cs @@ -1,6 +1,7 @@ using Kruzya.TelegramBot.Core.Data; using Microsoft.Extensions.Configuration; using West.TelegramBot.ContentStore.Model; +using West.TelegramBot.ContentStore.Option; namespace West.TelegramBot.ContentStore.Service; @@ -9,7 +10,7 @@ public class StraightKittiesService : AbstractKittiesService protected override string VideoType => "straight"; public override StoreItem StoreItem { get; } = new("kitties_straight", "Прон", 40, 100); - public StraightKittiesService(IConfiguration configuration, CoreContext db) : base(configuration, db) + public StraightKittiesService(IConfiguration configuration, AllowNsfw allowNsfw) : base(configuration, allowNsfw) { } } \ No newline at end of file From d3b6da0ebdf974b05111cfc3596fbf511d132d09 Mon Sep 17 00:00:00 2001 From: Andriy <30056636+West14@users.noreply.github.com> Date: Fri, 21 Jul 2023 19:27:58 +0300 Subject: [PATCH 7/7] [core] this class shouldn't be here --- Core/Test.cs | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 Core/Test.cs diff --git a/Core/Test.cs b/Core/Test.cs deleted file mode 100644 index aadf958..0000000 --- a/Core/Test.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Threading.Tasks; -using BotFramework; -using BotFramework.Attributes; -using BotFramework.Enums; -using Telegram.Bot; - -namespace Kruzya.TelegramBot.Core; - -public class Test : BotEventHandler -{ - public uint Counter { get; set; } = 5; - - - [Command(InChat.All, "test_state")] - public async Task TestState() - { - Counter++; - await Bot.SendTextMessageAsync(Chat.Id, Counter.ToString()); - } -} \ No newline at end of file