diff --git a/src/RxTelegram.Bot/Api/BaseTelegramBot.cs b/src/RxTelegram.Bot/Api/BaseTelegramBot.cs
index a61e34f..1baaa7c 100644
--- a/src/RxTelegram.Bot/Api/BaseTelegramBot.cs
+++ b/src/RxTelegram.Bot/Api/BaseTelegramBot.cs
@@ -10,6 +10,7 @@
using RxTelegram.Bot.Exceptions;
using RxTelegram.Bot.Interface.Validation;
using RxTelegram.Bot.Utils;
+using RxTelegram.Bot.Utils.Converter;
namespace RxTelegram.Bot.Api;
@@ -46,7 +47,7 @@ public static JsonSerializerSettings JsonSerializerSettings
new InputFileConverter(),
new ChatIdConverter(),
new ChatBoostSourceConverter(),
- new StringEnumConverter(new SnakeCaseNamingStrategy())
+ new UnknownStringEnumConverter(new SnakeCaseNamingStrategy())
};
return _jsonSerializerSettings = new JsonSerializerSettings
{
diff --git a/src/RxTelegram.Bot/Interface/BaseTypes/Enums/MessageEntityType.cs b/src/RxTelegram.Bot/Interface/BaseTypes/Enums/MessageEntityType.cs
index 87729b7..51872a4 100644
--- a/src/RxTelegram.Bot/Interface/BaseTypes/Enums/MessageEntityType.cs
+++ b/src/RxTelegram.Bot/Interface/BaseTypes/Enums/MessageEntityType.cs
@@ -91,4 +91,9 @@ public enum MessageEntityType
/// Inline custom emoji stickers
///
CustomEmoji,
+
+ ///
+ /// block quotation
+ ///
+ Blockquote
}
diff --git a/src/RxTelegram.Bot/Utils/ChatIdConverter.cs b/src/RxTelegram.Bot/Utils/Converter/ChatIdConverter.cs
similarity index 96%
rename from src/RxTelegram.Bot/Utils/ChatIdConverter.cs
rename to src/RxTelegram.Bot/Utils/Converter/ChatIdConverter.cs
index c6d4c2a..b616451 100644
--- a/src/RxTelegram.Bot/Utils/ChatIdConverter.cs
+++ b/src/RxTelegram.Bot/Utils/Converter/ChatIdConverter.cs
@@ -3,7 +3,7 @@
using Newtonsoft.Json.Linq;
using RxTelegram.Bot.Interface.BaseTypes;
-namespace RxTelegram.Bot.Utils;
+namespace RxTelegram.Bot.Utils.Converter;
public class ChatIdConverter : JsonConverter
{
diff --git a/src/RxTelegram.Bot/Utils/InputFileConverter.cs b/src/RxTelegram.Bot/Utils/Converter/InputFileConverter.cs
similarity index 95%
rename from src/RxTelegram.Bot/Utils/InputFileConverter.cs
rename to src/RxTelegram.Bot/Utils/Converter/InputFileConverter.cs
index 8513840..dbae295 100644
--- a/src/RxTelegram.Bot/Utils/InputFileConverter.cs
+++ b/src/RxTelegram.Bot/Utils/Converter/InputFileConverter.cs
@@ -2,7 +2,7 @@
using Newtonsoft.Json;
using RxTelegram.Bot.Interface.BaseTypes.Requests.Attachments;
-namespace RxTelegram.Bot.Utils;
+namespace RxTelegram.Bot.Utils.Converter;
internal class InputFileConverter : JsonConverter
{
diff --git a/src/RxTelegram.Bot/Utils/Converter/UnknownStringEnumConverter.cs b/src/RxTelegram.Bot/Utils/Converter/UnknownStringEnumConverter.cs
new file mode 100644
index 0000000..27dac60
--- /dev/null
+++ b/src/RxTelegram.Bot/Utils/Converter/UnknownStringEnumConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Serialization;
+
+namespace RxTelegram.Bot.Utils.Converter;
+
+public class UnknownStringEnumConverter(NamingStrategy namingStrategy) : StringEnumConverter(namingStrategy)
+{
+ public override object ReadJson(JsonReader reader, Type enumType, object existingValue, JsonSerializer serializer)
+ {
+ try
+ {
+ return base.ReadJson(reader, enumType, existingValue, serializer);
+ }
+ catch (Exception) when (enumType.IsEnum ||
+ enumType.IsGenericType &&
+ enumType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
+ enumType.GenericTypeArguments[0].IsEnum)
+ {
+ // Return null if it is a nullable enum and -1 if it is an enum to ensure that new values do not brake the bot
+ return reader.TokenType switch
+ {
+ JsonToken.Integer => Enum.ToObject(enumType, -1),
+ JsonToken.Null => null,
+ JsonToken.None => null,
+ JsonToken.String when enumType.IsGenericType && enumType.GetGenericTypeDefinition() == typeof(Nullable<>) => null,
+ JsonToken.String => Enum.ToObject(enumType, -1),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ }
+ }
+}
diff --git a/src/UnitTests/JsonConverters/UnknownStringEnumConverterTest.cs b/src/UnitTests/JsonConverters/UnknownStringEnumConverterTest.cs
new file mode 100644
index 0000000..e09d88e
--- /dev/null
+++ b/src/UnitTests/JsonConverters/UnknownStringEnumConverterTest.cs
@@ -0,0 +1,75 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using NUnit.Framework;
+using RxTelegram.Bot.Interface.BaseTypes.Enums;
+using RxTelegram.Bot.Utils.Converter;
+
+namespace RxTelegram.Bot.UnitTests.JsonConverters;
+
+[TestFixture]
+public class UnknownStringEnumConverterTest
+{
+ private readonly JsonSerializerSettings _jsonSettings = new()
+ {
+ Converters =
+ {
+ new
+ UnknownStringEnumConverter(new
+ SnakeCaseNamingStrategy())
+ }
+ };
+
+ [Test]
+ [TestCase(@"""blockquote""", MessageEntityType.Blockquote)]
+ [TestCase(@"""Pre""", MessageEntityType.Pre)]
+ [TestCase(@"""code""", MessageEntityType.Code)]
+ [TestCase(@"""mention""", MessageEntityType.Mention)]
+ [TestCase(@"""url""", MessageEntityType.Url)]
+ [TestCase(@"""email""", MessageEntityType.Email)]
+ [TestCase(@"""bold""", MessageEntityType.Bold)]
+ [TestCase(@"""italic""", MessageEntityType.Italic)]
+ [TestCase(@"""spoiler""", MessageEntityType.Spoiler)]
+ [TestCase(@"""hashtag""", MessageEntityType.Hashtag)]
+ [TestCase(@"""bot_command""", MessageEntityType.BotCommand)]
+ [TestCase(@"""custom_emoji""", MessageEntityType.CustomEmoji)]
+ [TestCase(@"""text_link""", MessageEntityType.TextLink)]
+ [TestCase(@"""text_mention""", MessageEntityType.TextMention)]
+ [TestCase(@"""unknown""", MessageEntityType.Unknown)]
+ [TestCase(@"""doesnt_exist""", null)]
+ [TestCase("", null)]
+ [TestCase(" ", null)]
+ public void ReadJson_WithUnknownStringEnum_ReturnsEnumValueOrNull(string json, MessageEntityType? expected)
+ {
+ // Act
+ var result = JsonConvert.DeserializeObject(json, _jsonSettings);
+
+ // Assert
+ Assert.That(result, Is.EqualTo(expected));
+ }
+
+ [Test]
+ [TestCase(@"""blockquote""", (int)MessageEntityType.Blockquote)]
+ [TestCase(@"""Pre""", (int)MessageEntityType.Pre)]
+ [TestCase(@"""code""", (int)MessageEntityType.Code)]
+ [TestCase(@"""mention""", (int)MessageEntityType.Mention)]
+ [TestCase(@"""url""", (int)MessageEntityType.Url)]
+ [TestCase(@"""email""", (int)MessageEntityType.Email)]
+ [TestCase(@"""bold""", (int)MessageEntityType.Bold)]
+ [TestCase(@"""italic""", (int)MessageEntityType.Italic)]
+ [TestCase(@"""spoiler""", (int)MessageEntityType.Spoiler)]
+ [TestCase(@"""hashtag""", (int)MessageEntityType.Hashtag)]
+ [TestCase(@"""bot_command""", (int)MessageEntityType.BotCommand)]
+ [TestCase(@"""custom_emoji""", (int)MessageEntityType.CustomEmoji)]
+ [TestCase(@"""text_link""", (int)MessageEntityType.TextLink)]
+ [TestCase(@"""text_mention""", (int)MessageEntityType.TextMention)]
+ [TestCase(@"""unknown""", (int)MessageEntityType.Unknown)]
+ [TestCase(@"""doesnt_exist""", -1)]
+ public void ReadJson_WithUnknownStringEnum_ReturnsEnumValueOrNegativeValue(string json, int expected)
+ {
+ // Act
+ var result = JsonConvert.DeserializeObject(json, _jsonSettings);
+
+ // Assert
+ Assert.That((int)result, Is.EqualTo(expected));
+ }
+}