Migration guide for version 17.0
There are several breaking changes in v17:
- New exceptions handling logic
- Removal of update and message events
- Removal of API methods from
ITelegramBotClient
interface and moving them into extension methods in the same namespace (that shouldn't break anyone's sources as long as they don't employ reflection or make their own interface implementations) - Working with default enum values
These are the most user facing breaking changes you should be aware of during migration.
Let's dive deep on the migrations.
New exceptions handling logic
v17 brings a new base type for exceptions: RequestException
. ApiRequestException
inherits from RequestException
and is thrown only when an actual error response with the correct body is received from the Bot API. In other situations RequestException
will be thrown instead containing actual exception as InnerException
if there is one, e.g. serialization or connection-related exceptions.
If you used ApiRequestException
and HttpRequestException
to handle most exception now you have to replace HttpRequestException
with RequestException
and look for the inner exception. All valid errors with JSON body from Telegram are now thrown as ApiRequestException
including 429: Too Many Requests
.
Since 5XX
responses don't usually include correct JSON body they are thrown as RequestException
with HttpRequestException
inside.
Look at the following example on how to handle different kinds of exceptions. You might not need to implement everything as you see, it's there only for demonstration purposes.
try
{
await bot.SendTextMessageAsync(chatId, "Hello");
}
catch (ApiRequestException exception)
{
switch (exception.StatusCode)
{
case 400:
// Handle incorrect requests exceptions
break;
case 401:
// Handle incorrect bot token exception (revoked tokens)
break;
case 403:
// Handle authorization exceptions (blocked users, unaccessible chats, etc)
break;
case 429:
// Handle rate limiting exception
break;
default:
// Handle other errors with valid json body: it includes status code and description of the error
break;
}
}
catch (RequestException exception)
{
if (exception.InnerException is HttpRequestException httpRequestException)
{
// Handle connection exceptions or 5XX exceptions from the Bot API
}
else if (exception.InnerException is JsonSerializationException serializationException)
{
// Handle serialization exception when a request or a response can't be serialized for some reason
}
else
{
// Handle all other exceptions
}
}
catch (OperationCancelledException exception)
{
// Handle cancellation exception, e.g. when CancellationToken is cancelled
}
Removal of events
In v17 we removed events and introduced a new way of getting updates with Telegram.Bot.Extensions.Polling package. You can find an example in First Chat Bot article.
Removal of API methods from ITelegramBotClient
interface
This change shouldn't affect most users, the methods are still there, but instead of being implementations of the interface they are now extension methods. It makes the interface leaner and easier to implement for custom clients and for decorators (e.g. rate limiters implemented as decorators). There isn't really a migration path for those who used these for some reason.
Working with default enum values
We changed how we work with enums. The most notable change is the default value: there is none, all our enums are now start with 1 (exception UpdateType
and MessageType
since they are not a part of the Bot API and we fully control these). 0
value is left unassigned for a purpose: if we encounter an unknown value in the response from the Bot API we assign 0
as its value.
Let's imagine that Telegram adds new MessageEntity
value. From now on all unknown values can be handled in the default
case of switch
statement.
MessageEntity entity = message.Entities.First();
switch (entity.Type)
{
case MessageEntityType.Username:
// ...
break;
case MessageEntityType.Command:
// ...
break;
default:
// All unknown values will go there
break;
}
Also some default enums values were removed, e.g. ParseMode.Default
since we started using nullable types for every optional value and ParseMode.Default
lost it's use. If a message doesn't have any markup you'll receive null
in places where ParseMode
type was used or if you want to explicitly indicate an absence of markup pass null
instead.
Other breaking changes
Constructor accepting IWebProxy
We removed constructor accepting IWebProxy
. Now you have to configure HttpClient yourself to use proxy. You can find examples in Working Behind a Proxy article.
InputMediaType
Property Type
in IInputMedia
was changed to an enum InputMediaType
for easier discoverability. So if you relied string values like photo
, video
, animation
and so on now you need to switch to using enums. As a result you'll get autocomplete in IDEs and more predictability of what types of input media there are.
EncryptedPassportElementType
Property Type
of EncryptedPassportElement
was replace with an enum for the same reason with EncryptedPassportElementType
enum.
ChatMember
As part of Bot API 5.3 implementation ChatMember
type was split into a hierarchy of types with a discriminator field Status
. If you need to access some data from the derived class you should use pattern matching or type casting like this:
ChatMember member = ... //;
if (chatMember is ChatMemberKicked kickedMember)
{
// now you can access properties of a kicked chat member
if (kickedMember.Until is not null)
{
// do something with the value of Until
}
}
ChatId
Fields Identifier
and Username
are now get-only properties. It shouldn't break most people's code as it's not a source breaking change. If you used reflection to find these fields you should to look for properties now.
InlineQueryResultBase
Type InlineQueryResultBase
is renamed to InlineQueryResult
to match Bot API type hierarchy.
Nullability
From now on all properties that are optional will use nullable types, e.g. int?
, string?
, because default values of such properties might be an actual values and isn't distinguishable from a lack of value. From now if a property is null
you can be sure that it's value was not present in a response from the Bot API.
ReplyKeyboardMarkup
Since ResizeKeyboard
and OneTimeKeyboard
are optional, we removed them from ReplyKeyboardMarkup
constructor. You have to use object initialization syntax to configure these properties:
var replyKeyboardMarkup = new ReplyKeyboardMarkup(
new KeyboardButton[][]
{
new KeyboardButton[] { "1.1", "1.2" },
new KeyboardButton[] { "2.1", "2.2" },
})
{
ResizeKeyboard = true
};