From 7cd1a295bbcbdb172cd8a8fff3a88219524e006f Mon Sep 17 00:00:00 2001 From: West14 <30056636+West14@users.noreply.github.com> Date: Fri, 21 Jan 2022 00:10:27 +0200 Subject: [PATCH] [quote] Crappy chat quote implementation. --- Kruzya.TelegramBot.sln | 35 +++++--- modules/ChatQuotes/ChatQuotes.cs | 32 ++++++++ modules/ChatQuotes/ChatQuotes.csproj | 22 +++++ modules/ChatQuotes/Handler/Quote.cs | 31 +++++++ .../Json/NullToEmptyObjectResolver.cs | 18 ++++ .../Json/NullToEmptyObjectValueProvider.cs | 26 ++++++ modules/ChatQuotes/Model/QuoteRequest.cs | 82 +++++++++++++++++++ modules/ChatQuotes/Model/QuoteResponse.cs | 35 ++++++++ modules/ChatQuotes/Model/User.cs | 22 +++++ modules/ChatQuotes/QuoteGeneratorService.cs | 38 +++++++++ 10 files changed, 331 insertions(+), 10 deletions(-) create mode 100644 modules/ChatQuotes/ChatQuotes.cs create mode 100644 modules/ChatQuotes/ChatQuotes.csproj create mode 100644 modules/ChatQuotes/Handler/Quote.cs create mode 100644 modules/ChatQuotes/Json/NullToEmptyObjectResolver.cs create mode 100644 modules/ChatQuotes/Json/NullToEmptyObjectValueProvider.cs create mode 100644 modules/ChatQuotes/Model/QuoteRequest.cs create mode 100644 modules/ChatQuotes/Model/QuoteResponse.cs create mode 100644 modules/ChatQuotes/Model/User.cs create mode 100644 modules/ChatQuotes/QuoteGeneratorService.cs diff --git a/Kruzya.TelegramBot.sln b/Kruzya.TelegramBot.sln index fe567b1..72016fc 100644 --- a/Kruzya.TelegramBot.sln +++ b/Kruzya.TelegramBot.sln @@ -1,25 +1,29 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{B41CB31A-641E-4079-87ED-5CE310B2D8C1}" +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{B41CB31A-641E-4079-87ED-5CE310B2D8C1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{C7821F15-DEDD-474F-A575-A296D4B58F10}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CombotAntiSpam", "modules\CombotAntiSpam\CombotAntiSpam.csproj", "{F1E1FBB7-ABA4-4304-9A42-D4975822B002}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CombotAntiSpam", "modules\CombotAntiSpam\CombotAntiSpam.csproj", "{F1E1FBB7-ABA4-4304-9A42-D4975822B002}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RichSiteSummary", "modules\RichSiteSummary\RichSiteSummary.csproj", "{C50D64C3-204B-4B58-927B-C8D759787252}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RichSiteSummary", "modules\RichSiteSummary\RichSiteSummary.csproj", "{C50D64C3-204B-4B58-927B-C8D759787252}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "tests\Tests.csproj", "{5C62CA70-F270-4029-953E-458623C31A3B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "tests\Tests.csproj", "{5C62CA70-F270-4029-953E-458623C31A3B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UrlLimitations", "modules\UrlLimitations\UrlLimitations.csproj", "{B82E3339-3B34-4600-8C59-C5A3640B1982}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UrlLimitations", "modules\UrlLimitations\UrlLimitations.csproj", "{B82E3339-3B34-4600-8C59-C5A3640B1982}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Destiny2.WhereIsXur", "modules\Destiny2.WhereIsXur\Destiny2.WhereIsXur.csproj", "{AE782132-BED8-4D1E-98FA-CB768171D332}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Destiny2.WhereIsXur", "modules\Destiny2.WhereIsXur\Destiny2.WhereIsXur.csproj", "{AE782132-BED8-4D1E-98FA-CB768171D332}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatManagement", "modules\ChatManagement\ChatManagement.csproj", "{55ACF7F7-409A-45EE-A41D-D8417DE7EAA7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatManagement", "modules\ChatManagement\ChatManagement.csproj", "{55ACF7F7-409A-45EE-A41D-D8417DE7EAA7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserGroupTag", "modules\UserGroupTag\UserGroupTag.csproj", "{5B89E22A-78CF-4037-BE0D-5F88EF47022B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UserGroupTag", "modules\UserGroupTag\UserGroupTag.csproj", "{5B89E22A-78CF-4037-BE0D-5F88EF47022B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entertainment", "modules\Entertainment\Entertainment.csproj", "{1D62101B-C2B1-4E79-8F7A-690E44E3EE81}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Entertainment", "modules\Entertainment\Entertainment.csproj", "{1D62101B-C2B1-4E79-8F7A-690E44E3EE81}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatQuotes", "modules\ChatQuotes\ChatQuotes.csproj", "{04F16325-8F44-4250-9B2E-0F77E8F65CBF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -63,6 +67,13 @@ Global {1D62101B-C2B1-4E79-8F7A-690E44E3EE81}.Debug|Any CPU.Build.0 = Debug|Any CPU {1D62101B-C2B1-4E79-8F7A-690E44E3EE81}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D62101B-C2B1-4E79-8F7A-690E44E3EE81}.Release|Any CPU.Build.0 = Release|Any CPU + {04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {F1E1FBB7-ABA4-4304-9A42-D4975822B002} = {C7821F15-DEDD-474F-A575-A296D4B58F10} @@ -72,5 +83,9 @@ Global {55ACF7F7-409A-45EE-A41D-D8417DE7EAA7} = {C7821F15-DEDD-474F-A575-A296D4B58F10} {5B89E22A-78CF-4037-BE0D-5F88EF47022B} = {C7821F15-DEDD-474F-A575-A296D4B58F10} {1D62101B-C2B1-4E79-8F7A-690E44E3EE81} = {C7821F15-DEDD-474F-A575-A296D4B58F10} + {04F16325-8F44-4250-9B2E-0F77E8F65CBF} = {C7821F15-DEDD-474F-A575-A296D4B58F10} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5BA73C3B-D5FC-4942-9091-504325CDC308} EndGlobalSection EndGlobal diff --git a/modules/ChatQuotes/ChatQuotes.cs b/modules/ChatQuotes/ChatQuotes.cs new file mode 100644 index 0000000..117d91f --- /dev/null +++ b/modules/ChatQuotes/ChatQuotes.cs @@ -0,0 +1,32 @@ +using System.Globalization; +using Kruzya.TelegramBot.Core; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using West.TelegramBot.ChatQuotes.Json; + +namespace West.TelegramBot.ChatQuotes +{ + public class ChatQuotes : Module + { + public static readonly JsonSerializerSettings SerializerSettings = new() + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + DateParseHandling = DateParseHandling.None, + Converters = + { + new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }, + new StringEnumConverter(new CamelCaseNamingStrategy()) + }, + ContractResolver = new NullToEmptyObjectResolver() + }; + + public ChatQuotes(Core core) : base(core) { } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + } + } +} \ No newline at end of file diff --git a/modules/ChatQuotes/ChatQuotes.csproj b/modules/ChatQuotes/ChatQuotes.csproj new file mode 100644 index 0000000..1a1653a --- /dev/null +++ b/modules/ChatQuotes/ChatQuotes.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + West.TelegramBot.ChatQuotes + TelegramBot.ChatQuotes + + + + + + + + + + + + + + diff --git a/modules/ChatQuotes/Handler/Quote.cs b/modules/ChatQuotes/Handler/Quote.cs new file mode 100644 index 0000000..0cbcdab --- /dev/null +++ b/modules/ChatQuotes/Handler/Quote.cs @@ -0,0 +1,31 @@ +using BotFramework; +using BotFramework.Attributes; +using BotFramework.Enums; +using Telegram.Bot; +using Telegram.Bot.Types.InputFiles; + +namespace West.TelegramBot.ChatQuotes.Handler; + +public class Quote : BotEventHandler +{ + private readonly QuoteGeneratorService _quoteGenerator; + + public Quote(QuoteGeneratorService quoteGenerator) + { + _quoteGenerator = quoteGenerator; + } + + [Command(InChat.Public, "q", CommandParseMode.Both)] + public async Task HandleQuote() + { + var msg = RawUpdate.Message; + var replyToMessage = msg?.ReplyToMessage; + if (msg == null || replyToMessage == null) + { + return; + } + + var file = new InputOnlineFile(await _quoteGenerator.GenerateQuoteImage(replyToMessage)); + await Bot.SendStickerAsync(Chat.Id, file, replyToMessageId: msg.MessageId); + } +} \ No newline at end of file diff --git a/modules/ChatQuotes/Json/NullToEmptyObjectResolver.cs b/modules/ChatQuotes/Json/NullToEmptyObjectResolver.cs new file mode 100644 index 0000000..be2d3c1 --- /dev/null +++ b/modules/ChatQuotes/Json/NullToEmptyObjectResolver.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace West.TelegramBot.ChatQuotes.Json +{ + class NullToEmptyObjectResolver : DefaultContractResolver + { + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + return type.GetProperties() + .Select(p => { + var jp = base.CreateProperty(p, memberSerialization); + jp.ValueProvider = new NullToEmptyStringValueProvider(p); + return jp; + }).ToList(); + } + } +} diff --git a/modules/ChatQuotes/Json/NullToEmptyObjectValueProvider.cs b/modules/ChatQuotes/Json/NullToEmptyObjectValueProvider.cs new file mode 100644 index 0000000..aad5bb1 --- /dev/null +++ b/modules/ChatQuotes/Json/NullToEmptyObjectValueProvider.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +namespace West.TelegramBot.ChatQuotes.Json; + +public class NullToEmptyStringValueProvider : IValueProvider +{ + private readonly PropertyInfo _memberInfo; + public NullToEmptyStringValueProvider(PropertyInfo memberInfo) + { + _memberInfo = memberInfo; + } + + public object? GetValue(object target) + { + var result = _memberInfo.GetValue(target); + if (_memberInfo.PropertyType == typeof(Message) && result == null) result = new object(); + return result; + } + + public void SetValue(object target, object value) + { + _memberInfo.SetValue(target, value); + } +} \ No newline at end of file diff --git a/modules/ChatQuotes/Model/QuoteRequest.cs b/modules/ChatQuotes/Model/QuoteRequest.cs new file mode 100644 index 0000000..bda9183 --- /dev/null +++ b/modules/ChatQuotes/Model/QuoteRequest.cs @@ -0,0 +1,82 @@ +#nullable enable + +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; +using File = Telegram.Bot.Types.File; + +namespace West.TelegramBot.ChatQuotes.Model; + +[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] +public class QuoteRequest +{ + public QuoteRequest(Message msg) + { + // TODO: move default values to config + Format = "webp"; + BackgroundColor = "#1b1429"; + Width = 512; + Height = 768; + Scale = 2; + + Messages = new List {QuoteMessage.FromMessage(msg)}; + } + + public QuoteType Type { get; set; } + public string Format { get; set; } + public string BackgroundColor { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public int Scale { get; set; } + public List? Messages { get; set; } + + public string ToJson() + { + return JsonConvert.SerializeObject(this, ChatQuotes.SerializerSettings); + } +} +[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] +public class QuoteMessage +{ + public List? Media { get; set; } + public QuoteMediaType? MediaType { get; set; } + public long ChatId { get; set; } + public bool Avatar { get; set; } = true; + public User? From { get; set; } + public string? Text { get; set; } + public Message? ReplyMessage { get; set; } + + public static QuoteMessage FromMessage(Message msg) + { + var quoteMessage = new QuoteMessage(); + + if (msg.Text != null) + { + quoteMessage.Text = msg.Text; + } + + var sticker = msg.Sticker; + if (sticker != null) + { + var file = new File + { + FileId = sticker.FileId, + FileSize = sticker.FileSize, + FileUniqueId = sticker.FileUniqueId + }; + quoteMessage.Media = new List {file}; + quoteMessage.MediaType = QuoteMediaType.Sticker; + } + + quoteMessage.ChatId = msg.Chat.Id; + quoteMessage.ReplyMessage = msg.ReplyToMessage; + quoteMessage.From = User.FromTgUser(msg.From!); + + return quoteMessage; + } +} + +public enum QuoteMediaType +{ + Sticker = 0 +} \ No newline at end of file diff --git a/modules/ChatQuotes/Model/QuoteResponse.cs b/modules/ChatQuotes/Model/QuoteResponse.cs new file mode 100644 index 0000000..85bb85e --- /dev/null +++ b/modules/ChatQuotes/Model/QuoteResponse.cs @@ -0,0 +1,35 @@ +#nullable disable + +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace West.TelegramBot.ChatQuotes.Model; + +[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] +public class QuoteResponse +{ + public bool Ok { get; set; } + + public Result Result { get; set; } + public static QuoteResponse FromJson(string json) + => JsonConvert.DeserializeObject(json, ChatQuotes.SerializerSettings); +} + +[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] +public class Result +{ + public string Image { get; set; } + + public QuoteType Type { get; set; } + + public long Width { get; set; } + + public long Height { get; set; } +} + +public enum QuoteType +{ + Quote = 0, + Image, + Null +} \ No newline at end of file diff --git a/modules/ChatQuotes/Model/User.cs b/modules/ChatQuotes/Model/User.cs new file mode 100644 index 0000000..455d297 --- /dev/null +++ b/modules/ChatQuotes/Model/User.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace West.TelegramBot.ChatQuotes.Model; + +public class User : Telegram.Bot.Types.User +{ + [JsonProperty] + public string Name => LastName != null ? $"{FirstName} {LastName}" : FirstName; + + public static User FromTgUser(Telegram.Bot.Types.User user) + { + return new User + { + Id = user.Id, + FirstName = user.FirstName, + LastName = user.LastName, + Username = user.Username, + LanguageCode = user.LanguageCode, + IsBot = user.IsBot + }; + } +} \ No newline at end of file diff --git a/modules/ChatQuotes/QuoteGeneratorService.cs b/modules/ChatQuotes/QuoteGeneratorService.cs new file mode 100644 index 0000000..9e407fc --- /dev/null +++ b/modules/ChatQuotes/QuoteGeneratorService.cs @@ -0,0 +1,38 @@ +using System.Net.Http.Headers; +using System.Text; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using Telegram.Bot.Types; +using West.TelegramBot.ChatQuotes.Model; + +namespace West.TelegramBot.ChatQuotes; + +public class QuoteGeneratorService +{ + private readonly HttpClient _httpClient; + + public QuoteGeneratorService(IConfiguration configuration) + { + _httpClient = new HttpClient + { + BaseAddress = new Uri(configuration.GetValue("QuoteApiUrl")) + }; + } + + public async Task GenerateQuoteImage(Message msg) + { + var request = new QuoteRequest(msg); + var content = new ByteArrayContent(Encoding.UTF8.GetBytes(request.ToJson().ToCharArray())) + { + Headers = + { + ContentType = MediaTypeHeaderValue.Parse("application/json") + } + }; + + var resp = await _httpClient.PostAsync("/generate", content); + var quoteResponse = JsonConvert.DeserializeObject(await resp.Content.ReadAsStringAsync()); + + return new MemoryStream(Convert.FromBase64String(quoteResponse.Result.Image)); + } +} \ No newline at end of file