From 88677749026d601547e7580d7bd428c29aa19f77 Mon Sep 17 00:00:00 2001 From: Aleksey Usatov Date: Sun, 11 Jul 2021 22:53:01 +0300 Subject: [PATCH 1/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 986138c50..f0234c9e2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # .NET Client for Telegram Bot API [![package](https://img.shields.io/nuget/vpre/Telegram.Bot.svg?label=Telegram.Bot&style=flat-square)](https://www.nuget.org/packages/Telegram.Bot) -[![Bot API Version](https://img.shields.io/badge/Bot%20API-5.2%20(April%2026,%202021)-f36caf.svg?style=flat-square)](https://core.telegram.org/bots/api) +[![Bot API Version](https://img.shields.io/badge/Bot%20API-5.2%20(June%2025,%202021)-f36caf.svg?style=flat-square)](https://core.telegram.org/bots/api) [![documentations](https://img.shields.io/badge/Documentations-Book-orange.svg?style=flat-square)](https://telegrambots.github.io/book/) [![telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://t.me/joinchat/B35YY0QbLfd034CFnvCtCA) From ac9da5f5eb6e74f1be3dac8b3681f7dca64b1b1d Mon Sep 17 00:00:00 2001 From: Aleksey Usatov Date: Sun, 1 Aug 2021 16:31:49 +0300 Subject: [PATCH 2/5] Make exceptions parser public --- src/Telegram.Bot/Exceptions/ApiResponse.cs | 2 +- .../Exceptions/DefaultExceptionParser.cs | 12 ++++++++++++ src/Telegram.Bot/Exceptions/ExceptionParser.cs | 8 -------- src/Telegram.Bot/Exceptions/IExceptionParser.cs | 2 +- src/Telegram.Bot/ITelegramBotClient.cs | 8 ++++++++ src/Telegram.Bot/TelegramBotClient.cs | 9 +++++---- 6 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 src/Telegram.Bot/Exceptions/DefaultExceptionParser.cs delete mode 100644 src/Telegram.Bot/Exceptions/ExceptionParser.cs diff --git a/src/Telegram.Bot/Exceptions/ApiResponse.cs b/src/Telegram.Bot/Exceptions/ApiResponse.cs index 752978383..71dc6b6e5 100644 --- a/src/Telegram.Bot/Exceptions/ApiResponse.cs +++ b/src/Telegram.Bot/Exceptions/ApiResponse.cs @@ -8,7 +8,7 @@ namespace Telegram.Bot.Exceptions /// Represents failed API response /// [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - internal class ApiResponse + public class ApiResponse { /// /// Gets the error message. diff --git a/src/Telegram.Bot/Exceptions/DefaultExceptionParser.cs b/src/Telegram.Bot/Exceptions/DefaultExceptionParser.cs new file mode 100644 index 000000000..4352fb43b --- /dev/null +++ b/src/Telegram.Bot/Exceptions/DefaultExceptionParser.cs @@ -0,0 +1,12 @@ +namespace Telegram.Bot.Exceptions +{ + internal class DefaultExceptionParser : IExceptionParser + { + public ApiRequestException Parse(ApiResponse apiResponse) => + new( + message: apiResponse.Description, + errorCode: apiResponse.ErrorCode, + parameters: apiResponse.Parameters + ); + } +} diff --git a/src/Telegram.Bot/Exceptions/ExceptionParser.cs b/src/Telegram.Bot/Exceptions/ExceptionParser.cs deleted file mode 100644 index f664e77f9..000000000 --- a/src/Telegram.Bot/Exceptions/ExceptionParser.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Telegram.Bot.Exceptions -{ - internal class ExceptionParser : IExceptionParser - { - public ApiRequestException Parse(ApiResponse apiResponse) => - new(apiResponse.Description, apiResponse.ErrorCode, apiResponse.Parameters); - } -} diff --git a/src/Telegram.Bot/Exceptions/IExceptionParser.cs b/src/Telegram.Bot/Exceptions/IExceptionParser.cs index e980143a1..ba42cdeb6 100644 --- a/src/Telegram.Bot/Exceptions/IExceptionParser.cs +++ b/src/Telegram.Bot/Exceptions/IExceptionParser.cs @@ -3,7 +3,7 @@ namespace Telegram.Bot.Exceptions /// /// Parses unsuccessful responses from Telegram Bot API to make specific exceptions /// - internal interface IExceptionParser + public interface IExceptionParser { /// /// Parses HTTP response and constructs a specific exception out of it diff --git a/src/Telegram.Bot/ITelegramBotClient.cs b/src/Telegram.Bot/ITelegramBotClient.cs index 8c47d5bf9..86918dfb5 100644 --- a/src/Telegram.Bot/ITelegramBotClient.cs +++ b/src/Telegram.Bot/ITelegramBotClient.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Telegram.Bot.Args; +using Telegram.Bot.Exceptions; using Telegram.Bot.Requests.Abstractions; using File = Telegram.Bot.Types.File; @@ -26,6 +27,13 @@ public interface ITelegramBotClient /// TimeSpan Timeout { get; set; } + /// + /// Instance of to parse errors from Bot API into + /// + /// + /// This property is not thread safe + IExceptionParser ExceptionsParser { get; set; } + /// /// Occurs before sending a request to API /// diff --git a/src/Telegram.Bot/TelegramBotClient.cs b/src/Telegram.Bot/TelegramBotClient.cs index 78c1cfa48..8ddd966c7 100644 --- a/src/Telegram.Bot/TelegramBotClient.cs +++ b/src/Telegram.Bot/TelegramBotClient.cs @@ -22,8 +22,6 @@ public class TelegramBotClient : ITelegramBotClient { const string BaseTelegramUrl = "https://api.telegram.org"; - static readonly ExceptionParser ExceptionParser = new(); - readonly string _baseFileUrl; readonly string _baseRequestUrl; readonly bool _localBotServer; @@ -41,6 +39,9 @@ public TimeSpan Timeout set => _httpClient.Timeout = value; } + /// + public IExceptionParser ExceptionsParser { get; set; } = new DefaultExceptionParser(); + /// /// Occurs before sending a request to API /// @@ -156,7 +157,7 @@ response.Description is null ) .ConfigureAwait(false); - throw ExceptionParser.Parse(failedApiResponse); + throw ExceptionsParser.Parse(failedApiResponse); } var apiResponse = await httpResponse @@ -244,7 +245,7 @@ response.Description is null ) .ConfigureAwait(false); - throw ExceptionParser.Parse(failedApiResponse); + throw ExceptionsParser.Parse(failedApiResponse); } if (httpResponse.Content is null) From 9b60d2ad4e8515f3717af0077d52457cd01e7b4f Mon Sep 17 00:00:00 2001 From: Aleksey Usatov Date: Sun, 1 Aug 2021 16:32:33 +0300 Subject: [PATCH 3/5] Use named parameters for methods with more than one argument --- .../Exceptions/ApiRequestException.cs | 11 +- .../Exceptions/RequestException.cs | 8 +- src/Telegram.Bot/TelegramBotClient.cs | 121 +++++++++++------- 3 files changed, 82 insertions(+), 58 deletions(-) diff --git a/src/Telegram.Bot/Exceptions/ApiRequestException.cs b/src/Telegram.Bot/Exceptions/ApiRequestException.cs index d035bd949..507d01e23 100644 --- a/src/Telegram.Bot/Exceptions/ApiRequestException.cs +++ b/src/Telegram.Bot/Exceptions/ApiRequestException.cs @@ -35,10 +35,8 @@ public ApiRequestException(string message) /// The message. /// The error code. public ApiRequestException(string message, int errorCode) - : base(message) - { + : base(message) => ErrorCode = errorCode; - } /// /// Initializes a new instance of the class. @@ -50,8 +48,7 @@ public ApiRequestException(string message, int errorCode) /// public ApiRequestException(string message, Exception innerException) : base(message, innerException) - { - } + { } /// /// Initializes a new instance of the class. @@ -60,10 +57,8 @@ public ApiRequestException(string message, Exception innerException) /// The error code. /// The inner exception. public ApiRequestException(string message, int errorCode, Exception innerException) - : base(message, innerException) - { + : base(message, innerException) => ErrorCode = errorCode; - } /// /// Initializes a new instance of the class diff --git a/src/Telegram.Bot/Exceptions/RequestException.cs b/src/Telegram.Bot/Exceptions/RequestException.cs index fd0d53035..999ff1b78 100644 --- a/src/Telegram.Bot/Exceptions/RequestException.cs +++ b/src/Telegram.Bot/Exceptions/RequestException.cs @@ -45,10 +45,8 @@ public RequestException(string message, Exception innerException) /// of the received response /// public RequestException(string message, HttpStatusCode httpStatusCode) - : base(message) - { + : base(message) => HttpStatusCode = httpStatusCode; - } /// /// Initializes a new instance of the class. @@ -64,9 +62,7 @@ public RequestException(string message, HttpStatusCode httpStatusCode) /// (Nothing in Visual Basic) if no inner exception is specified. /// public RequestException(string message, HttpStatusCode httpStatusCode, Exception innerException) - : base(message, innerException) - { + : base(message, innerException) => HttpStatusCode = httpStatusCode; - } } } diff --git a/src/Telegram.Bot/TelegramBotClient.cs b/src/Telegram.Bot/TelegramBotClient.cs index 8ddd966c7..e524c12e6 100644 --- a/src/Telegram.Bot/TelegramBotClient.cs +++ b/src/Telegram.Bot/TelegramBotClient.cs @@ -72,7 +72,7 @@ public TelegramBotClient( HttpClient? httpClient = null, string? baseUrl = default) { - if (token is null) throw new ArgumentNullException(nameof(token)); + if (token is null) { throw new ArgumentNullException(nameof(token)); } BotId = GetIdFromToken(token); @@ -96,11 +96,11 @@ public TelegramBotClient( var botIdSpan = span[..index]; if (!long.TryParse(botIdSpan, out var botId)) { return null; } #else - var index = token.IndexOf(':'); + var index = token.IndexOf(value: ':'); if (index is < 1 or > 16) { return null; } - var botIdSpan = token.Substring(0, index); + var botIdSpan = token.Substring(startIndex: 0, length: index); if (!long.TryParse(botIdSpan, out var botId)) { return null; } #endif @@ -116,7 +116,7 @@ public async Task MakeRequestAsync( if (request is null) { throw new ArgumentNullException(nameof(request)); } var url = $"{_baseRequestUrl}/{request.MethodName}"; - var httpRequest = new HttpRequestMessage(request.Method, url) + var httpRequest = new HttpRequestMessage(method: request.Method, requestUri: url) { Content = request.ToHttpContent() }; @@ -124,25 +124,37 @@ public async Task MakeRequestAsync( if (OnMakingApiRequest is not null) { var requestEventArgs = new ApiRequestEventArgs( - request.MethodName, - httpRequest.Content + methodName: request.MethodName, + httpContent: httpRequest.Content + ); + await OnMakingApiRequest.Invoke( + botClient: this, + args: requestEventArgs, + cancellationToken: cancellationToken ); - await OnMakingApiRequest.Invoke(this, requestEventArgs, cancellationToken); } - using var httpResponse = await SendRequestAsync(_httpClient, httpRequest, cancellationToken).ConfigureAwait(false); + using var httpResponse = await SendRequestAsync( + httpClient: _httpClient, + httpRequest: httpRequest, + cancellationToken: cancellationToken + ).ConfigureAwait(false); if (OnApiResponseReceived is not null) { var requestEventArgs = new ApiRequestEventArgs( - request.MethodName, - httpRequest.Content + methodName: request.MethodName, + httpContent: httpRequest.Content ); var responseEventArgs = new ApiResponseEventArgs( - httpResponse, - requestEventArgs + responseMessage: httpResponse, + apiRequestEventArgs: requestEventArgs + ); + await OnApiResponseReceived.Invoke( + botClient: this, + args: responseEventArgs, + cancellationToken: cancellationToken ); - await OnApiResponseReceived.Invoke(this, responseEventArgs, cancellationToken); } if (httpResponse.StatusCode != HttpStatusCode.OK) @@ -169,14 +181,18 @@ response.Result is null return apiResponse.Result!; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static async Task SendRequestAsync(HttpClient httpClient, HttpRequestMessage httpRequest, CancellationToken cancellationToken) + [MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)] + static async Task SendRequestAsync( + HttpClient httpClient, + HttpRequestMessage httpRequest, + CancellationToken cancellationToken) { HttpResponseMessage? httpResponse; try { - httpResponse = await httpClient.SendAsync(httpRequest, cancellationToken) - .ConfigureAwait(false); + httpResponse = await httpClient + .SendAsync(request: httpRequest, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); } catch (TaskCanceledException exception) { @@ -185,11 +201,14 @@ static async Task SendRequestAsync(HttpClient httpClient, H throw; } - throw new RequestException("Request timed out", exception); + throw new RequestException(message: "Request timed out", innerException: exception); } catch (Exception exception) { - throw new RequestException("Exception during making request", exception); + throw new RequestException( + message: "Exception during making request", + innerException: exception + ); } return httpResponse; @@ -204,7 +223,8 @@ public async Task TestApiAsync(CancellationToken cancellationToken = defau { try { - await MakeRequestAsync(new GetMeRequest(), cancellationToken).ConfigureAwait(false); + await MakeRequestAsync(request: new GetMeRequest(), cancellationToken: cancellationToken) + .ConfigureAwait(false); return true; } catch (ApiRequestException e) @@ -222,16 +242,17 @@ public async Task DownloadFileAsync( { if (string.IsNullOrWhiteSpace(filePath) || filePath.Length < 2) { - throw new ArgumentException("Invalid file path", nameof(filePath)); + throw new ArgumentException(message: "Invalid file path", paramName: nameof(filePath)); } - if (destination is null) - { - throw new ArgumentNullException(nameof(destination)); - } + if (destination is null) { throw new ArgumentNullException(nameof(destination)); } var fileUri = $"{_baseFileUrl}/{filePath}"; - using HttpResponseMessage httpResponse = await GetResponseAsync(_httpClient, fileUri, cancellationToken).ConfigureAwait(false); + using HttpResponseMessage httpResponse = await GetResponseAsync( + httpClient: _httpClient, + fileUri: fileUri, + cancellationToken: cancellationToken + ).ConfigureAwait(false); if (!httpResponse.IsSuccessStatusCode) { @@ -249,10 +270,12 @@ response.Description is null } if (httpResponse.Content is null) + { throw new RequestException( - "Response doesn't contain any content", + message: "Response doesn't contain any content", httpResponse.StatusCode ); + } try { @@ -262,34 +285,44 @@ await httpResponse.Content.CopyToAsync(destination) catch (Exception exception) { throw new RequestException( - "Exception during file download", + message: "Exception during file download", httpResponse.StatusCode, exception ); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static async Task GetResponseAsync(HttpClient httpClient, string fileUri, CancellationToken cancellationToken) + [MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)] + static async Task GetResponseAsync( + HttpClient httpClient, + string fileUri, + CancellationToken cancellationToken) { HttpResponseMessage? httpResponse; try { httpResponse = await httpClient - .GetAsync(fileUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken) - .ConfigureAwait(false); + .GetAsync( + requestUri: fileUri, + completionOption: HttpCompletionOption.ResponseHeadersRead, + cancellationToken: cancellationToken + ) + .ConfigureAwait(continueOnCapturedContext: false); } catch (TaskCanceledException exception) { - if (cancellationToken.IsCancellationRequested) - { - throw; - } + if (cancellationToken.IsCancellationRequested) { throw; } - throw new RequestException("Request timed out", exception); + throw new RequestException( + message: "Request timed out", + innerException: exception + ); } catch (Exception exception) { - throw new RequestException("Exception during file download", exception); + throw new RequestException( + message: "Exception during file download", + innerException: exception + ); } return httpResponse; @@ -313,15 +346,15 @@ await DownloadFileAsync(file.FilePath!, destination, cancellationToken) static string ExtractBaseUrl(string? baseUrl) { - if (baseUrl is null) throw new ArgumentNullException(nameof(baseUrl)); + if (baseUrl is null) { throw new ArgumentNullException(paramName: nameof(baseUrl)); } - if (!Uri.TryCreate(baseUrl, UriKind.Absolute, out var baseUri) - || string.IsNullOrEmpty(baseUri.Scheme) - || string.IsNullOrEmpty(baseUri.Authority)) + if (!Uri.TryCreate(uriString: baseUrl, uriKind: UriKind.Absolute, out var baseUri) + || string.IsNullOrEmpty(value: baseUri.Scheme) + || string.IsNullOrEmpty(value: baseUri.Authority)) { throw new ArgumentException( - "Invalid format. A valid base url looks \"http://localhost:8081\" ", - nameof(baseUrl) + message: "Invalid format. A valid base url looks \"http://localhost:8081\" ", + paramName: nameof(baseUrl) ); } From 5006daf65fcf114d920ddfdb007479ca35a03f72 Mon Sep 17 00:00:00 2001 From: Aleksey Usatov Date: Sun, 1 Aug 2021 16:32:41 +0300 Subject: [PATCH 4/5] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea1ff14cc..1009d3724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/). +## [Unreleased] + +### Added +- Interface `IExceptionsParser` +- Type `ApiResponse` +- Property `ITelegramBotClient.ExceptionsParser` + +## [v.16.0.2] - 2021-08-16 +### Fixed +- Parameter name `ChatLocation.String` replaced with `ChatLocation.Address` + ## [v16.0.1] - 2021-07-10 ### Fixed From 2849ef29f24d14ee703a7bd61ad69ab9d0a81ad7 Mon Sep 17 00:00:00 2001 From: Aleksey Usatov Date: Sun, 1 Aug 2021 16:42:52 +0300 Subject: [PATCH 5/5] Bump package version --- .azure-pipelines/variables.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure-pipelines/variables.yml b/.azure-pipelines/variables.yml index 8c54e1c81..0df86e619 100644 --- a/.azure-pipelines/variables.yml +++ b/.azure-pipelines/variables.yml @@ -3,7 +3,7 @@ variables: - name: versionPrefix value: 17.0.0 - name: versionSuffix - value: "alpha.1" + value: "alpha.2" - name: ciVersionSuffix value: ci.$(Build.BuildId)+git.commit.$(Build.SourceVersion) - name: buildConfiguration