From c6e2da64f8913ff248b86ad2c3718dd80220c2c9 Mon Sep 17 00:00:00 2001 From: karb0f0s <17474471+karb0f0s@users.noreply.github.com> Date: Fri, 29 Apr 2022 11:56:07 +0300 Subject: [PATCH 1/7] Align Passport project with Telegram.Bot repo --- .editorconfig | 10 +++-- .gitmodules | 3 -- Telegram.Bot.Extensions.Passport.sln | 17 --------- deps/Telegram.Bot | 1 - .../Telegram.Bot.Extensions.Passport.csproj | 38 ++++++++++++++----- 5 files changed, 35 insertions(+), 34 deletions(-) delete mode 100644 .gitmodules delete mode 160000 deps/Telegram.Bot diff --git a/.editorconfig b/.editorconfig index b86a352..b69f6a0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ # http://EditorConfig.org -# https://www.jetbrains.com/help/rider/EditorConfig_Properties.html # This file is the top-most EditorConfig file root = true +# All Files [*] charset = utf-8 end_of_line = lf @@ -11,22 +11,23 @@ indent_style = space indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true +max_line_length = 120 +# C# Files [*.cs] csharp_align_multiline_parameter = true csharp_align_multiline_extends_list = true csharp_align_linq_query = true csharp_place_attribute_on_same_line = false -csharp_empty_block_style = together_same_line -csharp_space_within_single_line_array_initializer_braces = true +csharp_empty_block_style = together +# Solution Files [*.sln] indent_style = tab # XML Project Files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 -xml_wrap_tags_and_pi = false # Configuration Files [*.{json,xml,yml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}] @@ -35,3 +36,4 @@ indent_size = 2 # Markdown Files [*.md] trim_trailing_whitespace = false +indent_size = 2 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 7d1f89c..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "deps/Telegram.Bot"] - path = deps/Telegram.Bot - url = https://github.com/TelegramBots/Telegram.Bot.git diff --git a/Telegram.Bot.Extensions.Passport.sln b/Telegram.Bot.Extensions.Passport.sln index 6294a03..8db0448 100644 --- a/Telegram.Bot.Extensions.Passport.sln +++ b/Telegram.Bot.Extensions.Passport.sln @@ -3,10 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{2CAB4FDA-D625-40D2-BC54-68FE375D821B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Telegram.Bot", "deps\Telegram.Bot\src\Telegram.Bot\Telegram.Bot.csproj", "{EC89AC57-7C57-4612-97BA-23FB5E7532EF}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{219FA372-F501-4189-A114-9F4BC224374A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Telegram.Bot.Extensions.Passport", "src\Telegram.Bot.Extensions.Passport\Telegram.Bot.Extensions.Passport.csproj", "{D6753021-F292-4465-9E8B-7D0CE7A4D504}" @@ -32,25 +28,12 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {EC89AC57-7C57-4612-97BA-23FB5E7532EF} = {2CAB4FDA-D625-40D2-BC54-68FE375D821B} {D6753021-F292-4465-9E8B-7D0CE7A4D504} = {219FA372-F501-4189-A114-9F4BC224374A} {8B4D1075-75E4-49D1-8AB7-19EC01DF3F17} = {8804BAE3-C00C-4618-91B3-65A38CEF34F0} {8CF594BF-BA8C-43C2-B7C4-69F7155A48F0} = {8804BAE3-C00C-4618-91B3-65A38CEF34F0} {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8} = {219FA372-F501-4189-A114-9F4BC224374A} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Debug|x64.ActiveCfg = Debug|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Debug|x64.Build.0 = Debug|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Debug|x86.ActiveCfg = Debug|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Debug|x86.Build.0 = Debug|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Release|Any CPU.Build.0 = Release|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Release|x64.ActiveCfg = Release|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Release|x64.Build.0 = Release|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Release|x86.ActiveCfg = Release|Any CPU - {EC89AC57-7C57-4612-97BA-23FB5E7532EF}.Release|x86.Build.0 = Release|Any CPU {D6753021-F292-4465-9E8B-7D0CE7A4D504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D6753021-F292-4465-9E8B-7D0CE7A4D504}.Debug|Any CPU.Build.0 = Debug|Any CPU {D6753021-F292-4465-9E8B-7D0CE7A4D504}.Debug|x64.ActiveCfg = Debug|Any CPU diff --git a/deps/Telegram.Bot b/deps/Telegram.Bot deleted file mode 160000 index 18e2876..0000000 --- a/deps/Telegram.Bot +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 18e2876039b8e72b818b530ccc06da64c1cea84a diff --git a/src/Telegram.Bot.Extensions.Passport/Telegram.Bot.Extensions.Passport.csproj b/src/Telegram.Bot.Extensions.Passport/Telegram.Bot.Extensions.Passport.csproj index 85b97cf..3814782 100644 --- a/src/Telegram.Bot.Extensions.Passport/Telegram.Bot.Extensions.Passport.csproj +++ b/src/Telegram.Bot.Extensions.Passport/Telegram.Bot.Extensions.Passport.csproj @@ -1,11 +1,18 @@ - + - netstandard2.0;net461 + netstandard2.0;netcoreapp3.1 1.0.0 - - latest + 10 + enable + true True True + true + true + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + AllEnabledByDefault Telegram Passport Extension for Telegram Bot API Client Telegram Passport is a unified authorization method for services that require personal identification. @@ -20,10 +27,23 @@ https://raw.githubusercontent.com/TelegramBots/Telegram.Bot.Extensions.Passport/master/package-icon.gif Telegram;Bot;Api;Passport;Telegram Passport - - - - - + + + $(NoWarn);CA1040 + $(NoWarn);CA1822 + + + + + + true + true + + + + + + + From aec3ded610e9256619fc2c0fa4f24793f028ae99 Mon Sep 17 00:00:00 2001 From: karb0f0s <17474471+karb0f0s@users.noreply.github.com> Date: Fri, 29 Apr 2022 11:57:54 +0300 Subject: [PATCH 2/7] Convert to file scoped namespace --- .../Decryption/Decrypter.cs | 489 +++++++++--------- .../Decryption/IDecrypter.cs | 6 +- .../PassportDataDecryptionException.cs | 41 +- .../Enums/Gender.cs | 29 +- .../Enums/PassportEnums.cs | 15 +- .../Enums/Scope.cs | 163 +++--- .../Errors/PassportElementError.cs | 68 ++- .../Errors/PassportElementErrorDataField.cs | 76 ++- .../Errors/PassportElementErrorFile.cs | 60 ++- .../Errors/PassportElementErrorFiles.cs | 61 ++- .../Errors/PassportElementErrorFrontSide.cs | 60 ++- .../Errors/PassportElementErrorReverseSide.cs | 58 +-- .../Errors/PassportElementErrorSelfie.cs | 60 ++- .../PassportElementErrorTranslationFile.cs | 62 ++- .../PassportElementErrorTranslationFiles.cs | 63 ++- .../Errors/PassportElementErrorUnspecified.cs | 56 +- .../Request/AuthorizationRequestParameters.cs | 155 +++--- .../Request/IPassportScopeElement.cs | 26 +- .../Request/PassportScope.cs | 61 ++- .../Request/PassportScopeElementOne.cs | 80 ++- .../PassportScopeElementOneOfSeveral.cs | 77 ++- .../SetPassportDataErrorsRequest.cs | 69 ++- .../TelegramBotClientPassportExtensions.cs | 146 +++--- .../Types/Credentials.cs | 31 +- .../Types/DataCredentials.cs | 31 +- .../Types/FileCredentials.cs | 33 +- .../Types/IDecryptedValue.cs | 12 +- .../Types/IdDocumentData.cs | 60 ++- .../Types/PersonalDetails.cs | 122 +++-- .../Types/ResidentialAddress.cs | 71 ++- .../Types/SecureData.cs | 123 +++-- .../Types/SecureValue.cs | 87 ++-- 32 files changed, 1239 insertions(+), 1312 deletions(-) diff --git a/src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs b/src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs index 292d81b..0e41d38 100644 --- a/src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs +++ b/src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs @@ -1,167 +1,221 @@ -using System; -using System.IO; +using Newtonsoft.Json; using System.Security.Cryptography; using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; using Telegram.Bot.Exceptions; using Telegram.Bot.Types.Passport; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport +namespace Telegram.Bot.Passport; + +/// +public class Decrypter : IDecrypter { /// - public class Decrypter : IDecrypter + public Credentials DecryptCredentials( + EncryptedCredentials encryptedCredentials, + RSA key + ) { - /// - public Credentials DecryptCredentials( - EncryptedCredentials encryptedCredentials, - RSA key - ) - { - if (encryptedCredentials is null) - throw new ArgumentNullException(nameof(encryptedCredentials)); - if (key is null) - throw new ArgumentNullException(nameof(key)); - if (encryptedCredentials.Data is null) - throw new ArgumentNullException(nameof(encryptedCredentials.Data)); - if (encryptedCredentials.Secret is null) - throw new ArgumentNullException(nameof(encryptedCredentials.Secret)); - if (encryptedCredentials.Hash is null) - throw new ArgumentNullException(nameof(encryptedCredentials.Hash)); - - byte[] data = Convert.FromBase64String(encryptedCredentials.Data); - if (data.Length == 0) - throw new ArgumentException("Data is empty.", nameof(encryptedCredentials.Data)); - if (data.Length % 16 != 0) - throw new PassportDataDecryptionException - ($"Data length is not divisible by 16: {data.Length}."); - - byte[] encryptedSecret = Convert.FromBase64String(encryptedCredentials.Secret); - - byte[] hash = Convert.FromBase64String(encryptedCredentials.Hash); - if (hash.Length != 32) - throw new PassportDataDecryptionException($"Hash length is not 32: {hash.Length}."); - - byte[] secret = key.Decrypt(encryptedSecret, RSAEncryptionPadding.OaepSHA1); - - byte[] decryptedData = DecryptDataBytes(data, secret, hash); - string json = Encoding.UTF8.GetString(decryptedData); - Credentials credentials = JsonConvert.DeserializeObject(json); - - return credentials; - } + if (encryptedCredentials is null) + throw new ArgumentNullException(nameof(encryptedCredentials)); + if (key is null) + throw new ArgumentNullException(nameof(key)); + if (encryptedCredentials.Data is null) + throw new ArgumentNullException(nameof(encryptedCredentials.Data)); + if (encryptedCredentials.Secret is null) + throw new ArgumentNullException(nameof(encryptedCredentials.Secret)); + if (encryptedCredentials.Hash is null) + throw new ArgumentNullException(nameof(encryptedCredentials.Hash)); + + byte[] data = Convert.FromBase64String(encryptedCredentials.Data); + if (data.Length == 0) + throw new ArgumentException("Data is empty.", nameof(encryptedCredentials.Data)); + if (data.Length % 16 != 0) + throw new PassportDataDecryptionException + ($"Data length is not divisible by 16: {data.Length}."); + + byte[] encryptedSecret = Convert.FromBase64String(encryptedCredentials.Secret); + + byte[] hash = Convert.FromBase64String(encryptedCredentials.Hash); + if (hash.Length != 32) + throw new PassportDataDecryptionException($"Hash length is not 32: {hash.Length}."); + + byte[] secret = key.Decrypt(encryptedSecret, RSAEncryptionPadding.OaepSHA1); + + byte[] decryptedData = DecryptDataBytes(data, secret, hash); + string json = Encoding.UTF8.GetString(decryptedData); + Credentials credentials = JsonConvert.DeserializeObject(json); + + return credentials; + } - /// - public TValue DecryptData( - string encryptedData, - DataCredentials dataCredentials - ) - where TValue : class, IDecryptedValue - { - if (encryptedData is null) - throw new ArgumentNullException(nameof(encryptedData)); - if (dataCredentials is null) - throw new ArgumentNullException(nameof(dataCredentials)); - if (dataCredentials.Secret is null) - throw new ArgumentNullException(nameof(dataCredentials.Secret)); - if (dataCredentials.DataHash is null) - throw new ArgumentNullException(nameof(dataCredentials.DataHash)); - - byte[] data = Convert.FromBase64String(encryptedData); - if (data.Length == 0) - throw new ArgumentException("Data is empty.", nameof(encryptedData)); - if (data.Length % 16 != 0) - throw new PassportDataDecryptionException - ($"Data length is not divisible by 16: {data.Length}."); - - byte[] dataSecret = Convert.FromBase64String(dataCredentials.Secret); - - byte[] dataHash = Convert.FromBase64String(dataCredentials.DataHash); - if (dataHash.Length != 32) - throw new PassportDataDecryptionException($"Hash length is not 32: {dataHash.Length}."); - - byte[] decryptedData = DecryptDataBytes(data, dataSecret, dataHash); - string content = Encoding.UTF8.GetString(decryptedData); - - return JsonConvert.DeserializeObject(content); - } + /// + public TValue DecryptData( + string encryptedData, + DataCredentials dataCredentials + ) + where TValue : class, IDecryptedValue + { + if (encryptedData is null) + throw new ArgumentNullException(nameof(encryptedData)); + if (dataCredentials is null) + throw new ArgumentNullException(nameof(dataCredentials)); + if (dataCredentials.Secret is null) + throw new ArgumentNullException(nameof(dataCredentials.Secret)); + if (dataCredentials.DataHash is null) + throw new ArgumentNullException(nameof(dataCredentials.DataHash)); + + byte[] data = Convert.FromBase64String(encryptedData); + if (data.Length == 0) + throw new ArgumentException("Data is empty.", nameof(encryptedData)); + if (data.Length % 16 != 0) + throw new PassportDataDecryptionException + ($"Data length is not divisible by 16: {data.Length}."); + + byte[] dataSecret = Convert.FromBase64String(dataCredentials.Secret); + + byte[] dataHash = Convert.FromBase64String(dataCredentials.DataHash); + if (dataHash.Length != 32) + throw new PassportDataDecryptionException($"Hash length is not 32: {dataHash.Length}."); + + byte[] decryptedData = DecryptDataBytes(data, dataSecret, dataHash); + string content = Encoding.UTF8.GetString(decryptedData); + + return JsonConvert.DeserializeObject(content); + } - /// - public byte[] DecryptFile( - byte[] encryptedContent, - FileCredentials fileCredentials - ) - { - if (encryptedContent is null) - throw new ArgumentNullException(nameof(encryptedContent)); - if (fileCredentials is null) - throw new ArgumentNullException(nameof(fileCredentials)); - if (fileCredentials.Secret is null) - throw new ArgumentNullException(nameof(fileCredentials.Secret)); - if (fileCredentials.FileHash is null) - throw new ArgumentNullException(nameof(fileCredentials.FileHash)); - if (encryptedContent.Length == 0) - throw new ArgumentException("Data array is empty.", nameof(encryptedContent)); - if (encryptedContent.Length % 16 != 0) - throw new PassportDataDecryptionException - ($"Data length is not divisible by 16: {encryptedContent.Length}."); - - byte[] dataSecret = Convert.FromBase64String(fileCredentials.Secret); - byte[] dataHash = Convert.FromBase64String(fileCredentials.FileHash); - if (dataHash.Length != 32) - throw new PassportDataDecryptionException($"Hash length is not 32: {dataHash.Length}."); - - return DecryptDataBytes(encryptedContent, dataSecret, dataHash); - } + /// + public byte[] DecryptFile( + byte[] encryptedContent, + FileCredentials fileCredentials + ) + { + if (encryptedContent is null) + throw new ArgumentNullException(nameof(encryptedContent)); + if (fileCredentials is null) + throw new ArgumentNullException(nameof(fileCredentials)); + if (fileCredentials.Secret is null) + throw new ArgumentNullException(nameof(fileCredentials.Secret)); + if (fileCredentials.FileHash is null) + throw new ArgumentNullException(nameof(fileCredentials.FileHash)); + if (encryptedContent.Length == 0) + throw new ArgumentException("Data array is empty.", nameof(encryptedContent)); + if (encryptedContent.Length % 16 != 0) + throw new PassportDataDecryptionException + ($"Data length is not divisible by 16: {encryptedContent.Length}."); + + byte[] dataSecret = Convert.FromBase64String(fileCredentials.Secret); + byte[] dataHash = Convert.FromBase64String(fileCredentials.FileHash); + if (dataHash.Length != 32) + throw new PassportDataDecryptionException($"Hash length is not 32: {dataHash.Length}."); + + return DecryptDataBytes(encryptedContent, dataSecret, dataHash); + } + + /// + public Task DecryptFileAsync( + Stream encryptedContent, + FileCredentials fileCredentials, + Stream destination, + CancellationToken cancellationToken = default + ) + { + if (encryptedContent is null) + throw new ArgumentNullException(nameof(encryptedContent)); + if (fileCredentials is null) + throw new ArgumentNullException(nameof(fileCredentials)); + if (fileCredentials.Secret is null) + throw new ArgumentNullException(nameof(fileCredentials.Secret)); + if (fileCredentials.FileHash is null) + throw new ArgumentNullException(nameof(fileCredentials.FileHash)); + if (destination is null) + throw new ArgumentNullException(nameof(destination)); + if (!encryptedContent.CanRead) + throw new ArgumentException("Stream does not support reading.", nameof(encryptedContent)); + if (encryptedContent.CanSeek && encryptedContent.Length == 0) + throw new ArgumentException("Stream is empty.", nameof(encryptedContent)); + if (encryptedContent.CanSeek && encryptedContent.Length % 16 != 0) + throw new PassportDataDecryptionException("Data length is not divisible by 16: " + + $"{encryptedContent.Length}."); + if (!destination.CanWrite) + throw new ArgumentException("Stream does not support writing.", nameof(destination)); + + byte[] dataSecret = Convert.FromBase64String(fileCredentials.Secret); + byte[] dataHash = Convert.FromBase64String(fileCredentials.FileHash); + if (dataHash.Length != 32) + throw new PassportDataDecryptionException($"Hash length is not 32: {dataHash.Length}."); + + return DecryptDataStreamAsync(encryptedContent, dataSecret, dataHash, destination, cancellationToken); + } - /// - public Task DecryptFileAsync( - Stream encryptedContent, - FileCredentials fileCredentials, - Stream destination, - CancellationToken cancellationToken = default - ) + private static async Task DecryptDataStreamAsync( + Stream data, + byte[] secret, + byte[] hash, + Stream destination, + CancellationToken cancellationToken + ) + { + FindDataKeyAndIv(secret, hash, out byte[] dataKey, out byte[] dataIv); + + using (var aes = Aes.Create()) { - if (encryptedContent is null) - throw new ArgumentNullException(nameof(encryptedContent)); - if (fileCredentials is null) - throw new ArgumentNullException(nameof(fileCredentials)); - if (fileCredentials.Secret is null) - throw new ArgumentNullException(nameof(fileCredentials.Secret)); - if (fileCredentials.FileHash is null) - throw new ArgumentNullException(nameof(fileCredentials.FileHash)); - if (destination is null) - throw new ArgumentNullException(nameof(destination)); - if (!encryptedContent.CanRead) - throw new ArgumentException("Stream does not support reading.", nameof(encryptedContent)); - if (encryptedContent.CanSeek && encryptedContent.Length == 0) - throw new ArgumentException("Stream is empty.", nameof(encryptedContent)); - if (encryptedContent.CanSeek && encryptedContent.Length % 16 != 0) - throw new PassportDataDecryptionException("Data length is not divisible by 16: " + - $"{encryptedContent.Length}."); - if (!destination.CanWrite) - throw new ArgumentException("Stream does not support writing.", nameof(destination)); - - byte[] dataSecret = Convert.FromBase64String(fileCredentials.Secret); - byte[] dataHash = Convert.FromBase64String(fileCredentials.FileHash); - if (dataHash.Length != 32) - throw new PassportDataDecryptionException($"Hash length is not 32: {dataHash.Length}."); - - return DecryptDataStreamAsync(encryptedContent, dataSecret, dataHash, destination, cancellationToken); + // ReSharper disable once PossibleNullReferenceException + aes.KeySize = 256; + aes.Mode = CipherMode.CBC; + aes.Key = dataKey; + aes.IV = dataIv; + aes.Padding = PaddingMode.None; + + using (var decrypter = aes.CreateDecryptor()) + using (CryptoStream aesStream = new CryptoStream(data, decrypter, CryptoStreamMode.Read)) + using (var sha256 = SHA256.Create()) + using (CryptoStream shaStream = new CryptoStream(aesStream, sha256, CryptoStreamMode.Read)) + { + byte[] paddingBuffer = new byte[256]; + int read = await shaStream.ReadAsync(paddingBuffer, 0, 256, cancellationToken) + .ConfigureAwait(false); + + byte paddingLength = paddingBuffer[0]; + if (paddingLength < 32) + throw new PassportDataDecryptionException($"Data padding length is invalid: {paddingLength}."); + + int actualDataLength = read - paddingLength; + if (actualDataLength < 1) + throw new PassportDataDecryptionException($"Data length is invalid: {actualDataLength}."); + + await destination.WriteAsync(paddingBuffer, paddingLength, actualDataLength, cancellationToken) + .ConfigureAwait(false); + + // 81920 is the default Stream.CopyTo buffer size + // The overload without the buffer size does not accept a cancellation token + const int defaultBufferSize = 81920; + await shaStream.CopyToAsync(destination, defaultBufferSize, cancellationToken) + .ConfigureAwait(false); + + byte[] paddedDataHash = sha256.Hash; + for (int i = 0; i < hash.Length; i++) + { + if (hash[i] != paddedDataHash[i]) + throw new PassportDataDecryptionException($"Data hash mismatch at position {i}."); + } + } } + } - private static async Task DecryptDataStreamAsync( - Stream data, - byte[] secret, - byte[] hash, - Stream destination, - CancellationToken cancellationToken - ) - { - FindDataKeyAndIv(secret, hash, out byte[] dataKey, out byte[] dataIv); + private static byte[] DecryptDataBytes(byte[] data, byte[] secret, byte[] hash) + { + #region Step 1: find data Key & IV + + FindDataKeyAndIv(secret, hash, out byte[] dataKey, out byte[] dataIv); + + #endregion + + byte[] dataWithPadding; + + #region Step 2.1: decrypt data to get "data with random padding" + { using (var aes = Aes.Create()) { // ReSharper disable once PossibleNullReferenceException @@ -170,129 +224,70 @@ CancellationToken cancellationToken aes.Key = dataKey; aes.IV = dataIv; aes.Padding = PaddingMode.None; - using (var decrypter = aes.CreateDecryptor()) - using (CryptoStream aesStream = new CryptoStream(data, decrypter, CryptoStreamMode.Read)) - using (var sha256 = SHA256.Create()) - using (CryptoStream shaStream = new CryptoStream(aesStream, sha256, CryptoStreamMode.Read)) { - byte[] paddingBuffer = new byte[256]; - int read = await shaStream.ReadAsync(paddingBuffer, 0, 256, cancellationToken) - .ConfigureAwait(false); - - byte paddingLength = paddingBuffer[0]; - if (paddingLength < 32) - throw new PassportDataDecryptionException($"Data padding length is invalid: {paddingLength}."); - - int actualDataLength = read - paddingLength; - if (actualDataLength < 1) - throw new PassportDataDecryptionException($"Data length is invalid: {actualDataLength}."); - - await destination.WriteAsync(paddingBuffer, paddingLength, actualDataLength, cancellationToken) - .ConfigureAwait(false); - - // 81920 is the default Stream.CopyTo buffer size - // The overload without the buffer size does not accept a cancellation token - const int defaultBufferSize = 81920; - await shaStream.CopyToAsync(destination, defaultBufferSize, cancellationToken) - .ConfigureAwait(false); - - byte[] paddedDataHash = sha256.Hash; - for (int i = 0; i < hash.Length; i++) - { - if (hash[i] != paddedDataHash[i]) - throw new PassportDataDecryptionException($"Data hash mismatch at position {i}."); - } + dataWithPadding = decrypter.TransformFinalBlock(data, 0, data.Length); } } } - private static byte[] DecryptDataBytes(byte[] data, byte[] secret, byte[] hash) - { - #region Step 1: find data Key & IV - - FindDataKeyAndIv(secret, hash, out byte[] dataKey, out byte[] dataIv); - - #endregion + #endregion - byte[] dataWithPadding; - - #region Step 2.1: decrypt data to get "data with random padding" + #region Step 2.2: verify "data_hash" and hash of "data with padding" are the same + { + byte[] paddedDataHash; + using (var sha256 = SHA256.Create()) { - using (var aes = Aes.Create()) - { - // ReSharper disable once PossibleNullReferenceException - aes.KeySize = 256; - aes.Mode = CipherMode.CBC; - aes.Key = dataKey; - aes.IV = dataIv; - aes.Padding = PaddingMode.None; - using (var decrypter = aes.CreateDecryptor()) - { - dataWithPadding = decrypter.TransformFinalBlock(data, 0, data.Length); - } - } + paddedDataHash = sha256.ComputeHash(dataWithPadding); } - #endregion - - #region Step 2.2: verify "data_hash" and hash of "data with padding" are the same - + for (int i = 0; i < hash.Length; i++) { - byte[] paddedDataHash; - using (var sha256 = SHA256.Create()) - { - paddedDataHash = sha256.ComputeHash(dataWithPadding); - } - - for (int i = 0; i < hash.Length; i++) - { - if (hash[i] != paddedDataHash[i]) - throw new PassportDataDecryptionException($"Data hash mismatch at position {i}."); - } + if (hash[i] != paddedDataHash[i]) + throw new PassportDataDecryptionException($"Data hash mismatch at position {i}."); } + } - #endregion + #endregion - byte[] decryptedData; + byte[] decryptedData; - #region Step 3: remove padding to get the actual data + #region Step 3: remove padding to get the actual data - { - byte paddingLength = dataWithPadding[0]; - if (paddingLength < 32) - throw new PassportDataDecryptionException($"Data padding length is invalid: {paddingLength}."); + { + byte paddingLength = dataWithPadding[0]; + if (paddingLength < 32) + throw new PassportDataDecryptionException($"Data padding length is invalid: {paddingLength}."); - int actualDataLength = dataWithPadding.Length - paddingLength; - if (actualDataLength < 1) - throw new PassportDataDecryptionException($"Data length is invalid: {actualDataLength}."); + int actualDataLength = dataWithPadding.Length - paddingLength; + if (actualDataLength < 1) + throw new PassportDataDecryptionException($"Data length is invalid: {actualDataLength}."); - decryptedData = new byte[actualDataLength]; - Array.Copy(dataWithPadding, paddingLength, decryptedData, 0, actualDataLength); - } + decryptedData = new byte[actualDataLength]; + Array.Copy(dataWithPadding, paddingLength, decryptedData, 0, actualDataLength); + } - #endregion + #endregion - return decryptedData; - } + return decryptedData; + } - private static void FindDataKeyAndIv(byte[] secret, byte[] hash, out byte[] dataKey, out byte[] dataIv) + private static void FindDataKeyAndIv(byte[] secret, byte[] hash, out byte[] dataKey, out byte[] dataIv) + { + byte[] dataSecretHash; + using (var sha512 = SHA512.Create()) { - byte[] dataSecretHash; - using (var sha512 = SHA512.Create()) - { - byte[] secretAndHashBytes = new byte[secret.Length + hash.Length]; - Array.Copy(secret, 0, secretAndHashBytes, 0, secret.Length); - Array.Copy(hash, 0, secretAndHashBytes, secret.Length, hash.Length); - dataSecretHash = sha512.ComputeHash(secretAndHashBytes); - } + byte[] secretAndHashBytes = new byte[secret.Length + hash.Length]; + Array.Copy(secret, 0, secretAndHashBytes, 0, secret.Length); + Array.Copy(hash, 0, secretAndHashBytes, secret.Length, hash.Length); + dataSecretHash = sha512.ComputeHash(secretAndHashBytes); + } - dataKey = new byte[32]; - Array.Copy(dataSecretHash, 0, dataKey, 0, 32); + dataKey = new byte[32]; + Array.Copy(dataSecretHash, 0, dataKey, 0, 32); - dataIv = new byte[16]; - Array.Copy(dataSecretHash, 32, dataIv, 0, 16); - } + dataIv = new byte[16]; + Array.Copy(dataSecretHash, 32, dataIv, 0, 16); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Decryption/IDecrypter.cs b/src/Telegram.Bot.Extensions.Passport/Decryption/IDecrypter.cs index 6200b01..1d4e93c 100644 --- a/src/Telegram.Bot.Extensions.Passport/Decryption/IDecrypter.cs +++ b/src/Telegram.Bot.Extensions.Passport/Decryption/IDecrypter.cs @@ -1,9 +1,5 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; using Newtonsoft.Json; +using System.Security.Cryptography; using Telegram.Bot.Exceptions; using Telegram.Bot.Types.Passport; diff --git a/src/Telegram.Bot.Extensions.Passport/Decryption/PassportDataDecryptionException.cs b/src/Telegram.Bot.Extensions.Passport/Decryption/PassportDataDecryptionException.cs index 266b30a..c5d76f2 100644 --- a/src/Telegram.Bot.Extensions.Passport/Decryption/PassportDataDecryptionException.cs +++ b/src/Telegram.Bot.Extensions.Passport/Decryption/PassportDataDecryptionException.cs @@ -1,30 +1,27 @@ -using System; - // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Exceptions +namespace Telegram.Bot.Exceptions; + +/// +/// Represents a fatal error in decryption of Telegram Passport Data +/// +public class PassportDataDecryptionException : Exception { /// - /// Represents a fatal error in decryption of Telegram Passport Data + /// Initializes a new instance of /// - public class PassportDataDecryptionException : Exception + /// Error description + public PassportDataDecryptionException(string message) + : base(message) { - /// - /// Initializes a new instance of - /// - /// Error description - public PassportDataDecryptionException(string message) - : base(message) - { - } + } - /// - /// Initializes a new instance of - /// - /// Error description - /// Root cause of the error - public PassportDataDecryptionException(string message, Exception innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of + /// + /// Error description + /// Root cause of the error + public PassportDataDecryptionException(string message, Exception innerException) + : base(message, innerException) + { } } diff --git a/src/Telegram.Bot.Extensions.Passport/Enums/Gender.cs b/src/Telegram.Bot.Extensions.Passport/Enums/Gender.cs index fa2b793..e5f9a60 100644 --- a/src/Telegram.Bot.Extensions.Passport/Enums/Gender.cs +++ b/src/Telegram.Bot.Extensions.Passport/Enums/Gender.cs @@ -1,23 +1,22 @@ -// ReSharper disable once CheckNamespace +// ReSharper disable once CheckNamespace -namespace Telegram.Bot +namespace Telegram.Bot; + +public static partial class PassportEnums { - public static partial class PassportEnums + /// + /// Gender, male or female + /// + public static class Gender { /// - /// Gender, male or female + /// Male /// - public static class Gender - { - /// - /// Male - /// - public const string Male = "male"; + public const string Male = "male"; - /// - /// Female - /// - public const string Female = "female"; - } + /// + /// Female + /// + public const string Female = "female"; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Enums/PassportEnums.cs b/src/Telegram.Bot.Extensions.Passport/Enums/PassportEnums.cs index 635a95e..1317455 100644 --- a/src/Telegram.Bot.Extensions.Passport/Enums/PassportEnums.cs +++ b/src/Telegram.Bot.Extensions.Passport/Enums/PassportEnums.cs @@ -1,11 +1,10 @@ -// ReSharper disable once CheckNamespace +// ReSharper disable once CheckNamespace -namespace Telegram.Bot +namespace Telegram.Bot; + +/// +/// Provides constant values for Passport feature +/// +public static partial class PassportEnums { - /// - /// Provides constant values for Passport feature - /// - public static partial class PassportEnums - { - } } diff --git a/src/Telegram.Bot.Extensions.Passport/Enums/Scope.cs b/src/Telegram.Bot.Extensions.Passport/Enums/Scope.cs index 004af3c..375ed44 100644 --- a/src/Telegram.Bot.Extensions.Passport/Enums/Scope.cs +++ b/src/Telegram.Bot.Extensions.Passport/Enums/Scope.cs @@ -1,89 +1,88 @@ -// ReSharper disable once CheckNamespace +// ReSharper disable once CheckNamespace -namespace Telegram.Bot +namespace Telegram.Bot; + +public static partial class PassportEnums { - public static partial class PassportEnums + /// + /// Provides scope names that a bot can request for + /// + public static class Scope { /// - /// Provides scope names that a bot can request for + /// Personal details + /// + public const string PersonalDetails = "personal_details"; + + /// + /// Passport + /// + public const string Passport = "passport"; + + /// + /// Driver license + /// + public const string DriverLicense = "driver_license"; + + /// + /// Identity card + /// + public const string IdentityCard = "identity_card"; + + /// + /// Internal passport + /// + public const string InternalPassport = "internal_passport"; + + /// + /// Residential address + /// + public const string Address = "address"; + + /// + /// Utility bill + /// + public const string UtilityBill = "utility_bill"; + + /// + /// Bank statement + /// + public const string BankStatement = "bank_statement"; + + /// + /// Rental agreement + /// + public const string RentalAgreement = "rental_agreement"; + + /// + /// Passport registration + /// + public const string PassportRegistration = "passport_registration"; + + /// + /// Temporary registration + /// + public const string TemporaryRegistration = "temporary_registration"; + + /// + /// Phone number + /// + public const string PhoneNumber = "phone_number"; + + /// + /// Email + /// + public const string Email = "email"; + + /// + /// Special type "id_document" is an alias for one of "passport", "driver_license", or "identity_card" + /// + public const string IdDocument = "id_document"; + + /// + /// Special type "address_document" is an alias for one of "utility_bill", "bank_statement", or + /// "rental_agreement" /// - public static class Scope - { - /// - /// Personal details - /// - public const string PersonalDetails = "personal_details"; - - /// - /// Passport - /// - public const string Passport = "passport"; - - /// - /// Driver license - /// - public const string DriverLicense = "driver_license"; - - /// - /// Identity card - /// - public const string IdentityCard = "identity_card"; - - /// - /// Internal passport - /// - public const string InternalPassport = "internal_passport"; - - /// - /// Residential address - /// - public const string Address = "address"; - - /// - /// Utility bill - /// - public const string UtilityBill = "utility_bill"; - - /// - /// Bank statement - /// - public const string BankStatement = "bank_statement"; - - /// - /// Rental agreement - /// - public const string RentalAgreement = "rental_agreement"; - - /// - /// Passport registration - /// - public const string PassportRegistration = "passport_registration"; - - /// - /// Temporary registration - /// - public const string TemporaryRegistration = "temporary_registration"; - - /// - /// Phone number - /// - public const string PhoneNumber = "phone_number"; - - /// - /// Email - /// - public const string Email = "email"; - - /// - /// Special type "id_document" is an alias for one of "passport", "driver_license", or "identity_card" - /// - public const string IdDocument = "id_document"; - - /// - /// Special type "address_document" is an alias for one of "utility_bill", "bank_statement", or - /// "rental_agreement" - /// - public const string AddressDocument = "address_document"; - } + public const string AddressDocument = "address_document"; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementError.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementError.cs index d8ebea1..b8facd2 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementError.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementError.cs @@ -1,47 +1,45 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// This object represents an error in the Telegram Passport element which was submitted that should be resolved +/// by the user. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public abstract class PassportElementError { /// - /// This object represents an error in the Telegram Passport element which was submitted that should be resolved - /// by the user. + /// Error source. /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public abstract class PassportElementError - { - /// - /// Error source. - /// - [JsonProperty(Required = Required.Always)] - public string Type { get; } + [JsonProperty(Required = Required.Always)] + public string Type { get; } - /// - /// The section of the user's Telegram Passport which has the error. - /// - [JsonProperty(Required = Required.Always)] - public string Source { get; } + /// + /// The section of the user's Telegram Passport which has the error. + /// + [JsonProperty(Required = Required.Always)] + public string Source { get; } - /// - /// Error message - /// - [JsonProperty(Required = Required.Always)] - public string Message { get; } + /// + /// Error message + /// + [JsonProperty(Required = Required.Always)] + public string Message { get; } - /// - /// Initializes a new passport element error instance with required parameters - /// - /// Error source - /// The section of the user's Telegram Passport which has the issue - /// Error message - /// if any argument is null - protected PassportElementError(string source, string type, string message) - { - Type = type ?? throw new ArgumentNullException(nameof(type)); - Source = source ?? throw new ArgumentNullException(nameof(source)); - Message = message ?? throw new ArgumentNullException(nameof(message)); - } + /// + /// Initializes a new passport element error instance with required parameters + /// + /// Error source + /// The section of the user's Telegram Passport which has the issue + /// Error message + /// if any argument is null + protected PassportElementError(string source, string type, string message) + { + Type = type ?? throw new ArgumentNullException(nameof(type)); + Source = source ?? throw new ArgumentNullException(nameof(source)); + Message = message ?? throw new ArgumentNullException(nameof(message)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorDataField.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorDataField.cs index 9077921..4e92529 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorDataField.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorDataField.cs @@ -1,50 +1,48 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue in one of the data fields that was provided by the user. The error is considered +/// resolved when the field's value changes. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorDataField : PassportElementError { /// - /// Represents an issue in one of the data fields that was provided by the user. The error is considered - /// resolved when the field's value changes. + /// Name of the data field which has the error. /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorDataField : PassportElementError - { - /// - /// Name of the data field which has the error. - /// - [JsonProperty(Required = Required.Always)] - public string FieldName { get; } + [JsonProperty(Required = Required.Always)] + public string FieldName { get; } - /// - /// Base64-encoded data hash. - /// - [JsonProperty(Required = Required.Always)] - public string DataHash { get; } + /// + /// Base64-encoded data hash. + /// + [JsonProperty(Required = Required.Always)] + public string DataHash { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// The section of the user's Telegram Passport which has the error, one of "personal_details", "passport", - /// "driver_license", "identity_card", "internal_passport", "address" - /// - /// Name of the data field which has the error - /// Base64-encoded data hash - /// Error message - /// if any argument is null - public PassportElementErrorDataField( - string type, - string fieldName, - string dataHash, - string message - ) - : base("data", type, message) - { - FieldName = fieldName ?? throw new ArgumentNullException(nameof(fieldName)); - DataHash = dataHash ?? throw new ArgumentNullException(nameof(dataHash)); - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// The section of the user's Telegram Passport which has the error, one of "personal_details", "passport", + /// "driver_license", "identity_card", "internal_passport", "address" + /// + /// Name of the data field which has the error + /// Base64-encoded data hash + /// Error message + /// if any argument is null + public PassportElementErrorDataField( + string type, + string fieldName, + string dataHash, + string message + ) + : base("data", type, message) + { + FieldName = fieldName ?? throw new ArgumentNullException(nameof(fieldName)); + DataHash = dataHash ?? throw new ArgumentNullException(nameof(dataHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFile.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFile.cs index fc6881f..2eb1ae9 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFile.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFile.cs @@ -1,41 +1,39 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue with a document scan. The error is considered resolved when the file with the document +/// scan changes. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorFile : PassportElementError { /// - /// Represents an issue with a document scan. The error is considered resolved when the file with the document - /// scan changes. + /// Base64-encoded hash of the file with the front side of the document. /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorFile : PassportElementError - { - /// - /// Base64-encoded hash of the file with the front side of the document. - /// - [JsonProperty(Required = Required.Always)] - public string FileHash { get; } + [JsonProperty(Required = Required.Always)] + public string FileHash { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// The section of the user's Telegram Passport which has the issue, one of "utility_bill", - /// "bank_statement", "rental_agreement", "passport_registration", "temporary_registration" - /// - /// Base64-encoded file hash - /// Error message - /// if any argument is null - public PassportElementErrorFile( - string type, - string fileHash, - string message - ) - : base("file", type, message) - { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// The section of the user's Telegram Passport which has the issue, one of "utility_bill", + /// "bank_statement", "rental_agreement", "passport_registration", "temporary_registration" + /// + /// Base64-encoded file hash + /// Error message + /// if any argument is null + public PassportElementErrorFile( + string type, + string fileHash, + string message + ) + : base("file", type, message) + { + FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFiles.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFiles.cs index dcb0168..0896358 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFiles.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFiles.cs @@ -1,42 +1,39 @@ -using System; -using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue with a list of scans. The error is considered resolved when the list of files containing +/// the scans changes. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorFiles : PassportElementError { /// - /// Represents an issue with a list of scans. The error is considered resolved when the list of files containing - /// the scans changes. + /// Base64-encoded hash of the file with the front side of the document. /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorFiles : PassportElementError - { - /// - /// Base64-encoded hash of the file with the front side of the document. - /// - [JsonProperty(Required = Required.Always)] - public IEnumerable FileHashes { get; } + [JsonProperty(Required = Required.Always)] + public IEnumerable FileHashes { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// The section of the user's Telegram Passport which has the issue, one of "utility_bill", "bank_statement", - /// "rental_agreement", "passport_registration", "temporary_registration" - /// - /// List of base64-encoded file hashes - /// Error message - /// if any argument is null - public PassportElementErrorFiles( - string type, - IEnumerable fileHashes, - string message - ) - : base("files", type, message) - { - FileHashes = fileHashes ?? throw new ArgumentNullException(nameof(fileHashes)); - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// The section of the user's Telegram Passport which has the issue, one of "utility_bill", "bank_statement", + /// "rental_agreement", "passport_registration", "temporary_registration" + /// + /// List of base64-encoded file hashes + /// Error message + /// if any argument is null + public PassportElementErrorFiles( + string type, + IEnumerable fileHashes, + string message + ) + : base("files", type, message) + { + FileHashes = fileHashes ?? throw new ArgumentNullException(nameof(fileHashes)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFrontSide.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFrontSide.cs index 0d95831..4a1426b 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFrontSide.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFrontSide.cs @@ -1,41 +1,39 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue with the front side of a document. The error is considered resolved when the file with +/// the front side of the document changes. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorFrontSide : PassportElementError { /// - /// Represents an issue with the front side of a document. The error is considered resolved when the file with - /// the front side of the document changes. + /// Base64-encoded hash of the file with the front side of the document /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorFrontSide : PassportElementError - { - /// - /// Base64-encoded hash of the file with the front side of the document - /// - [JsonProperty(Required = Required.Always)] - public string FileHash { get; } + [JsonProperty(Required = Required.Always)] + public string FileHash { get; } - /// - /// Initialize a new instance of with required parameters - /// - /// - /// The section of the user's Telegram Passport which has the issue, one of "passport", "driver_license", - /// "identity_card", "internal_passport" - /// - /// Base64-encoded hash of the file with the front side of the document - /// Error message - /// if any argument is null - public PassportElementErrorFrontSide( - string type, - string fileHash, - string message - ) - : base("front_side", type, message) - { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); - } + /// + /// Initialize a new instance of with required parameters + /// + /// + /// The section of the user's Telegram Passport which has the issue, one of "passport", "driver_license", + /// "identity_card", "internal_passport" + /// + /// Base64-encoded hash of the file with the front side of the document + /// Error message + /// if any argument is null + public PassportElementErrorFrontSide( + string type, + string fileHash, + string message + ) + : base("front_side", type, message) + { + FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorReverseSide.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorReverseSide.cs index d036ee4..ccae1a0 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorReverseSide.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorReverseSide.cs @@ -1,40 +1,38 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue with the front side of a document. The error is considered resolved when the file with +/// the reverse side of the document changes. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorReverseSide : PassportElementError { /// - /// Represents an issue with the front side of a document. The error is considered resolved when the file with - /// the reverse side of the document changes. + /// Base64-encoded hash of the file with the reverse side of the document /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorReverseSide : PassportElementError - { - /// - /// Base64-encoded hash of the file with the reverse side of the document - /// - [JsonProperty(Required = Required.Always)] - public string FileHash { get; } + [JsonProperty(Required = Required.Always)] + public string FileHash { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// The section of the user's Telegram Passport which has the issue, one of "driver_license", "identity_card" - /// - /// Base64-encoded hash of the file with the reverse side of the document - /// Error message - /// if any argument is null - public PassportElementErrorReverseSide( - string type, - string fileHash, - string message - ) - : base("reverse_side", type, message) - { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// The section of the user's Telegram Passport which has the issue, one of "driver_license", "identity_card" + /// + /// Base64-encoded hash of the file with the reverse side of the document + /// Error message + /// if any argument is null + public PassportElementErrorReverseSide( + string type, + string fileHash, + string message + ) + : base("reverse_side", type, message) + { + FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorSelfie.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorSelfie.cs index 4710d69..db2ad8d 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorSelfie.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorSelfie.cs @@ -1,41 +1,39 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue with the selfie with a document. The error is considered resolved when the file with +/// the selfie changes. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorSelfie : PassportElementError { /// - /// Represents an issue with the selfie with a document. The error is considered resolved when the file with - /// the selfie changes. + /// Base64-encoded hash of the file with the selfie /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorSelfie : PassportElementError - { - /// - /// Base64-encoded hash of the file with the selfie - /// - [JsonProperty(Required = Required.Always)] - public string FileHash { get; } + [JsonProperty(Required = Required.Always)] + public string FileHash { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// The section of the user's Telegram Passport which has the issue, one of "passport", "driver_license", - /// "identity_card", "internal_passport" - /// - /// Base64-encoded hash of the file with the selfie - /// Error message - /// if any argument is null - public PassportElementErrorSelfie( - string type, - string fileHash, - string message - ) - : base("selfie", type, message) - { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// The section of the user's Telegram Passport which has the issue, one of "passport", "driver_license", + /// "identity_card", "internal_passport" + /// + /// Base64-encoded hash of the file with the selfie + /// Error message + /// if any argument is null + public PassportElementErrorSelfie( + string type, + string fileHash, + string message + ) + : base("selfie", type, message) + { + FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFile.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFile.cs index ea75ad4..f3aa59b 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFile.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFile.cs @@ -1,42 +1,40 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue with one of the files that constitute the translation of a document. The error is +/// considered resolved when the file changes. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorTranslationFile : PassportElementError { /// - /// Represents an issue with one of the files that constitute the translation of a document. The error is - /// considered resolved when the file changes. + /// Base64-encoded file hash /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorTranslationFile : PassportElementError - { - /// - /// Base64-encoded file hash - /// - [JsonProperty(Required = Required.Always)] - public string FileHash { get; } + [JsonProperty(Required = Required.Always)] + public string FileHash { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// Type of element of the user's Telegram Passport which has the issue, one of "passport", - /// "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", - /// "rental_agreement", "passport_registration", "temporary_registration" - /// - /// Base64-encoded file hash - /// Error message - /// if any argument is null - public PassportElementErrorTranslationFile( - string type, - string fileHash, - string message - ) - : base("translation_file", type, message) - { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// Type of element of the user's Telegram Passport which has the issue, one of "passport", + /// "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", + /// "rental_agreement", "passport_registration", "temporary_registration" + /// + /// Base64-encoded file hash + /// Error message + /// if any argument is null + public PassportElementErrorTranslationFile( + string type, + string fileHash, + string message + ) + : base("translation_file", type, message) + { + FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFiles.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFiles.cs index 224f84f..2bdc0bc 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFiles.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFiles.cs @@ -1,43 +1,40 @@ -using System; -using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue with the translated version of a document. The error is considered resolved when a +/// file with the document translation change. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorTranslationFiles : PassportElementError { /// - /// Represents an issue with the translated version of a document. The error is considered resolved when a - /// file with the document translation change. + /// List of base64-encoded file hashes /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorTranslationFiles : PassportElementError - { - /// - /// List of base64-encoded file hashes - /// - [JsonProperty(Required = Required.Always)] - public IEnumerable FileHashes { get; } + [JsonProperty(Required = Required.Always)] + public IEnumerable FileHashes { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// Type of element of the user's Telegram Passport which has the issue, one of "passport", "driver_license", - /// "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", - /// "passport_registration", "temporary_registration" - /// - /// List of base64-encoded file hashes - /// Error message - /// if any argument is null - public PassportElementErrorTranslationFiles( - string type, - IEnumerable fileHashes, - string message - ) - : base("translation_files", type, message) - { - FileHashes = fileHashes ?? throw new ArgumentNullException(nameof(fileHashes)); - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// Type of element of the user's Telegram Passport which has the issue, one of "passport", "driver_license", + /// "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", + /// "passport_registration", "temporary_registration" + /// + /// List of base64-encoded file hashes + /// Error message + /// if any argument is null + public PassportElementErrorTranslationFiles( + string type, + IEnumerable fileHashes, + string message + ) + : base("translation_files", type, message) + { + FileHashes = fileHashes ?? throw new ArgumentNullException(nameof(fileHashes)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorUnspecified.cs b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorUnspecified.cs index e04d6ba..c6257e8 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorUnspecified.cs +++ b/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorUnspecified.cs @@ -1,39 +1,37 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Represents an issue in an unspecified place. The error is considered resolved when new data is added. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportElementErrorUnspecified : PassportElementError { /// - /// Represents an issue in an unspecified place. The error is considered resolved when new data is added. + /// Base64-encoded element hash /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportElementErrorUnspecified : PassportElementError - { - /// - /// Base64-encoded element hash - /// - [JsonProperty(Required = Required.Always)] - public string ElementHash { get; } + [JsonProperty(Required = Required.Always)] + public string ElementHash { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// Type of element of the user's Telegram Passport which has the issue - /// - /// Base64-encoded element hash - /// Error message - /// if any argument is null - public PassportElementErrorUnspecified( - string type, - string elementHash, - string message - ) - : base("unspecified", type, message) - { - ElementHash = elementHash ?? throw new ArgumentNullException(nameof(elementHash)); - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// Type of element of the user's Telegram Passport which has the issue + /// + /// Base64-encoded element hash + /// Error message + /// if any argument is null + public PassportElementErrorUnspecified( + string type, + string elementHash, + string message + ) + : base("unspecified", type, message) + { + ElementHash = elementHash ?? throw new ArgumentNullException(nameof(elementHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/AuthorizationRequestParameters.cs b/src/Telegram.Bot.Extensions.Passport/Request/AuthorizationRequestParameters.cs index 82cc890..860604d 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/AuthorizationRequestParameters.cs +++ b/src/Telegram.Bot.Extensions.Passport/Request/AuthorizationRequestParameters.cs @@ -1,96 +1,91 @@ -// ReSharper disable CommentTypo -// ReSharper disable CheckNamespace -// ReSharper disable StringLiteralTypo - -using System; using Newtonsoft.Json; -namespace Telegram.Bot.Passport.Request +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Passport.Request; + +/// +/// Parameters for making a Telegram Passport authorization request +/// +public class AuthorizationRequestParameters { /// - /// Parameters for making a Telegram Passport authorization request + /// Unique identifier for the bot. You can get it from bot token. For example, for the bot token + /// "1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy", the bot id is 1234567. /// - public class AuthorizationRequestParameters - { - /// - /// Unique identifier for the bot. You can get it from bot token. For example, for the bot token - /// "1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy", the bot id is 1234567. - /// - public int BotId { get; } + public int BotId { get; } - /// - /// Public key of the bot - /// - public string PublicKey { get; } - - /// - /// Bot-specified nonce. - /// Important: For security purposes it should be a cryptographically secure unique identifier of the request. - /// In particular, it should be long enough and it should be generated using a cryptographically secure - /// pseudorandom number generator. You should never accept credentials with the same nonce twice. - /// - public string Nonce { get; } + /// + /// Public key of the bot + /// + public string PublicKey { get; } - /// - /// Description of the data you want to request - /// - public PassportScope PassportScope { get; } + /// + /// Bot-specified nonce. + /// Important: For security purposes it should be a cryptographically secure unique identifier of the request. + /// In particular, it should be long enough and it should be generated using a cryptographically secure + /// pseudorandom number generator. You should never accept credentials with the same nonce twice. + /// + public string Nonce { get; } - /// - /// Query string part of the URI generated from the parameters - /// - public string Query { get; } + /// + /// Description of the data you want to request + /// + public PassportScope PassportScope { get; } - /// - /// Authorization request URI - /// - public string Uri => "tg://resolve?" + Query; + /// + /// Query string part of the URI generated from the parameters + /// + public string Query { get; } - /// - /// Authorization request URI for Android devices - /// - public string AndroidUri => "tg:resolve?" + Query; + /// + /// Authorization request URI + /// + public string Uri => "tg://resolve?" + Query; - /// - /// Initializes a new instance of - /// - /// - /// Unique identifier for the bot. You can get it from bot token. For example, for the bot token - /// "1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy", the bot id is 1234567. - /// - /// Public key of the bot - /// - /// Bot-specified nonce. - /// Important: For security purposes it should be a cryptographically secure unique identifier of the request. - /// In particular, it should be long enough and it should be generated using a cryptographically secure - /// pseudorandom number generator. You should never accept credentials with the same nonce twice. - /// - /// Description of the data you want to request - public AuthorizationRequestParameters( - int botId, - string publicKey, - string nonce, - PassportScope scope - ) - { - BotId = botId; - PublicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); - Nonce = nonce ?? throw new ArgumentNullException(nameof(nonce)); - PassportScope = scope ?? throw new ArgumentNullException(nameof(PassportScope)); + /// + /// Authorization request URI for Android devices + /// + public string AndroidUri => "tg:resolve?" + Query; - var scopeJson = JsonConvert.SerializeObject(scope); + /// + /// Initializes a new instance of + /// + /// + /// Unique identifier for the bot. You can get it from bot token. For example, for the bot token + /// "1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy", the bot id is 1234567. + /// + /// Public key of the bot + /// + /// Bot-specified nonce. + /// Important: For security purposes it should be a cryptographically secure unique identifier of the request. + /// In particular, it should be long enough and it should be generated using a cryptographically secure + /// pseudorandom number generator. You should never accept credentials with the same nonce twice. + /// + /// Description of the data you want to request + public AuthorizationRequestParameters( + int botId, + string publicKey, + string nonce, + PassportScope scope + ) + { + BotId = botId; + PublicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); + Nonce = nonce ?? throw new ArgumentNullException(nameof(nonce)); + PassportScope = scope ?? throw new ArgumentNullException(nameof(PassportScope)); - Query = "domain=telegrampassport" + - $"&bot_id={System.Uri.EscapeDataString(botId + "")}" + - $"&scope={System.Uri.EscapeDataString(scopeJson)}" + - $"&public_key={System.Uri.EscapeDataString(publicKey)}" + - $"&nonce={System.Uri.EscapeDataString(nonce)}"; - } + var scopeJson = JsonConvert.SerializeObject(scope); - /// - /// Converts the parameters to their "tg://" URI string representation - /// - /// URI representation of this request - public override string ToString() => Uri; + Query = "domain=telegrampassport" + + $"&bot_id={System.Uri.EscapeDataString(botId + "")}" + + $"&scope={System.Uri.EscapeDataString(scopeJson)}" + + $"&public_key={System.Uri.EscapeDataString(publicKey)}" + + $"&nonce={System.Uri.EscapeDataString(nonce)}"; } + + /// + /// Converts the parameters to their "tg://" URI string representation + /// + /// URI representation of this request + public override string ToString() => Uri; } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/IPassportScopeElement.cs b/src/Telegram.Bot.Extensions.Passport/Request/IPassportScopeElement.cs index 9930ea4..e3559f4 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/IPassportScopeElement.cs +++ b/src/Telegram.Bot.Extensions.Passport/Request/IPassportScopeElement.cs @@ -1,20 +1,18 @@ -// ReSharper disable once CheckNamespace +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Passport.Request; -namespace Telegram.Bot.Passport.Request +/// +/// A marker interface for object represents a requested element +/// +public interface IPassportScopeElement { /// - /// A marker interface for object represents a requested element + /// Optional. Use this parameter if you want to request a selfie with the document. /// - public interface IPassportScopeElement - { - /// - /// Optional. Use this parameter if you want to request a selfie with the document. - /// - bool? Selfie { get; } + bool? Selfie { get; } - /// - /// Optional. Use this parameter if you want to request a translation of the document. - /// - bool? Translation { get; } - } + /// + /// Optional. Use this parameter if you want to request a translation of the document. + /// + bool? Translation { get; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/PassportScope.cs b/src/Telegram.Bot.Extensions.Passport/Request/PassportScope.cs index f0e2be6..e8b0a66 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/PassportScope.cs +++ b/src/Telegram.Bot.Extensions.Passport/Request/PassportScope.cs @@ -1,43 +1,40 @@ -using System; -using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport.Request +namespace Telegram.Bot.Passport.Request; + +/// +/// This object represents the data to be requested. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportScope { /// - /// This object represents the data to be requested. + /// List of requested elements, each type may be used only once in the entire array of + /// objects /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportScope - { - /// - /// List of requested elements, each type may be used only once in the entire array of - /// objects - /// - [JsonProperty(Required = Required.Always)] - public IEnumerable Data { get; } + [JsonProperty(Required = Required.Always)] + public IEnumerable Data { get; } - /// - /// Scope version - /// - [JsonProperty(Required = Required.Always)] - public int V { get; } + /// + /// Scope version + /// + [JsonProperty(Required = Required.Always)] + public int V { get; } - /// - /// Initializes a new instance of with required parameters - /// - /// - /// List of requested elements, each type may be used only once in the entire array of - /// objects - /// - /// Scope version. Defaults to 1. - /// - public PassportScope(IEnumerable data, int v = 1) - { - Data = data ?? throw new ArgumentNullException(nameof(data)); - V = v; - } + /// + /// Initializes a new instance of with required parameters + /// + /// + /// List of requested elements, each type may be used only once in the entire array of + /// objects + /// + /// Scope version. Defaults to 1. + /// + public PassportScope(IEnumerable data, int v = 1) + { + Data = data ?? throw new ArgumentNullException(nameof(data)); + V = v; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOne.cs b/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOne.cs index a524fb9..454a894 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOne.cs +++ b/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOne.cs @@ -1,54 +1,52 @@ -using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport.Request +namespace Telegram.Bot.Passport.Request; + +/// +/// This object represents one particular element that must be provided. If no options are needed, String +/// can be used instead of this object to specify the type of the element. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportScopeElementOne : IPassportScopeElement { /// - /// This object represents one particular element that must be provided. If no options are needed, String - /// can be used instead of this object to specify the type of the element. + /// Element type. One of /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportScopeElementOne : IPassportScopeElement - { - /// - /// Element type. One of - /// - [JsonProperty(Required = Required.Always)] - public string Type { get; } + [JsonProperty(Required = Required.Always)] + public string Type { get; } - /// - /// Optional. Use this parameter if you want to request a selfie with the document as well. - /// Available for "passport", "driver_license", "identity_card" and "internal_passport" - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? Selfie { get; set; } + /// + /// Optional. Use this parameter if you want to request a selfie with the document as well. + /// Available for "passport", "driver_license", "identity_card" and "internal_passport" + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? Selfie { get; set; } - /// - /// Optional. Use this parameter if you want to request a translation of the document as well. - /// Available for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", - /// "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration". - /// Note: We suggest to only request translations after you have received a valid document that requires one. - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? Translation { get; set; } + /// + /// Optional. Use this parameter if you want to request a translation of the document as well. + /// Available for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", + /// "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration". + /// Note: We suggest to only request translations after you have received a valid document that requires one. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? Translation { get; set; } - /// - /// Optional. Use this parameter to request the first, last and middle name of the user in the language - /// of the user's country of residence. Available for "personal_details" - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? NativeNames { get; set; } + /// + /// Optional. Use this parameter to request the first, last and middle name of the user in the language + /// of the user's country of residence. Available for "personal_details" + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NativeNames { get; set; } - /// - /// Initializes a new instance of with required parameter - /// - /// Element type. One of - /// - public PassportScopeElementOne(string type) - { - Type = type ?? throw new ArgumentNullException(nameof(type)); - } + /// + /// Initializes a new instance of with required parameter + /// + /// Element type. One of + /// + public PassportScopeElementOne(string type) + { + Type = type ?? throw new ArgumentNullException(nameof(type)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOneOfSeveral.cs b/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOneOfSeveral.cs index 47c8c55..e12c62e 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOneOfSeveral.cs +++ b/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOneOfSeveral.cs @@ -1,52 +1,49 @@ -using System; -using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport.Request +namespace Telegram.Bot.Passport.Request; + +/// +/// This object represents several elements one of which must be provided. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PassportScopeElementOneOfSeveral : IPassportScopeElement { /// - /// This object represents several elements one of which must be provided. + /// List of elements one of which must be provided; must contain either several of "passport", + /// "driver_license", "identity_card", "internal_passport" or several of "utility_bill", "bank_statement", + /// "rental_agreement", "passport_registration", "temporary_registration" /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PassportScopeElementOneOfSeveral : IPassportScopeElement - { - /// - /// List of elements one of which must be provided; must contain either several of "passport", - /// "driver_license", "identity_card", "internal_passport" or several of "utility_bill", "bank_statement", - /// "rental_agreement", "passport_registration", "temporary_registration" - /// - [JsonProperty(Required = Required.Always)] - public IEnumerable OneOf { get; } + [JsonProperty(Required = Required.Always)] + public IEnumerable OneOf { get; } - /// - /// Optional. Use this parameter if you want to request a selfie with the document from this list that - /// the user chooses to upload. - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? Selfie { get; set; } + /// + /// Optional. Use this parameter if you want to request a selfie with the document from this list that + /// the user chooses to upload. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? Selfie { get; set; } - /// - /// Optional. Use this parameter if you want to request a translation of the document from this list - /// that the user chooses to upload. - /// Note: We suggest to only request translations after you have received a valid document that requires one. - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? Translation { get; set; } + /// + /// Optional. Use this parameter if you want to request a translation of the document from this list + /// that the user chooses to upload. + /// Note: We suggest to only request translations after you have received a valid document that requires one. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? Translation { get; set; } - /// - /// Initializes a new instance of with required parameter - /// - /// - /// List of elements one of which must be provided; must contain either several of "passport", - /// "driver_license", "identity_card", "internal_passport" or several of "utility_bill", "bank_statement", - /// "rental_agreement", "passport_registration", "temporary_registration" - /// - /// - public PassportScopeElementOneOfSeveral(IEnumerable oneOf) - { - OneOf = oneOf ?? throw new ArgumentNullException(nameof(oneOf)); - } + /// + /// Initializes a new instance of with required parameter + /// + /// + /// List of elements one of which must be provided; must contain either several of "passport", + /// "driver_license", "identity_card", "internal_passport" or several of "utility_bill", "bank_statement", + /// "rental_agreement", "passport_registration", "temporary_registration" + /// + /// + public PassportScopeElementOneOfSeveral(IEnumerable oneOf) + { + OneOf = oneOf ?? throw new ArgumentNullException(nameof(oneOf)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/SetPassportDataErrorsRequest.cs b/src/Telegram.Bot.Extensions.Passport/SetPassportDataErrorsRequest.cs index 2902581..7b43f2b 100644 --- a/src/Telegram.Bot.Extensions.Passport/SetPassportDataErrorsRequest.cs +++ b/src/Telegram.Bot.Extensions.Passport/SetPassportDataErrorsRequest.cs @@ -1,48 +1,45 @@ -using System; -using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Telegram.Bot.Types.Passport; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Requests +namespace Telegram.Bot.Requests; + +/// +/// Informs a user that some of the Telegram Passport elements they provided contains errors. +/// The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of +/// the field for which you returned the error must change). Returns True on success. +/// Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. +/// For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of +/// tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class SetPassportDataErrorsRequest : RequestBase { /// - /// Informs a user that some of the Telegram Passport elements they provided contains errors. - /// The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of - /// the field for which you returned the error must change). Returns True on success. - /// Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. - /// For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of - /// tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues. + /// User identifier /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class SetPassportDataErrorsRequest : RequestBase - { - /// - /// User identifier - /// - [JsonProperty(Required = Required.Always)] - public int UserId { get; } + [JsonProperty(Required = Required.Always)] + public int UserId { get; } - /// - /// Descriptions of the errors - /// - [JsonProperty(Required = Required.Always)] - public IEnumerable Errors { get; } + /// + /// Descriptions of the errors + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable Errors { get; } - /// - /// Initializes a new request with required parameters - /// - /// User identifier - /// Descriptions of the errors - /// - /// If is null - /// - public SetPassportDataErrorsRequest(int userId, IEnumerable errors) - : base("setPassportDataErrors") - { - UserId = userId; - Errors = errors ?? throw new ArgumentNullException(nameof(errors)); - } + /// + /// Initializes a new request with required parameters + /// + /// User identifier + /// Descriptions of the errors + /// + /// If is null + /// + public SetPassportDataErrorsRequest(int userId, IEnumerable errors) + : base("setPassportDataErrors") + { + UserId = userId; + Errors = errors ?? throw new ArgumentNullException(nameof(errors)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/TelegramBotClientPassportExtensions.cs b/src/Telegram.Bot.Extensions.Passport/TelegramBotClientPassportExtensions.cs index 1194af7..4a68771 100644 --- a/src/Telegram.Bot.Extensions.Passport/TelegramBotClientPassportExtensions.cs +++ b/src/Telegram.Bot.Extensions.Passport/TelegramBotClientPassportExtensions.cs @@ -1,93 +1,87 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Telegram.Bot.Passport; using Telegram.Bot.Requests; -using Telegram.Bot.Types; using Telegram.Bot.Types.Passport; // ReSharper disable once CheckNamespace -namespace Telegram.Bot +namespace Telegram.Bot; + +/// +/// Contains extension methods for instances +/// +public static class TelegramBotClientPassportExtensions { /// - /// Contains extension methods for instances + /// Informs a user that some of the Telegram Passport elements they provided contains errors. The user will + /// not be able to re-submit their Passport to you until the errors are fixed (the contents of the field for + /// which you returned the error must change). Returns True on success. + /// Use this if the data submitted by the user doesn't satisfy the standards your service requires for + /// any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows + /// evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to + /// correct the issues. /// - public static class TelegramBotClientPassportExtensions - { - /// - /// Informs a user that some of the Telegram Passport elements they provided contains errors. The user will - /// not be able to re-submit their Passport to you until the errors are fixed (the contents of the field for - /// which you returned the error must change). Returns True on success. - /// Use this if the data submitted by the user doesn't satisfy the standards your service requires for - /// any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows - /// evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to - /// correct the issues. - /// - /// Instance of bot client - /// User identifier - /// Descriptions of the errors - /// The cancellation token to cancel operation. - /// - public static Task SetPassportDataErrorsAsync( - this ITelegramBotClient botClient, - int userId, - IEnumerable errors, - CancellationToken cancellationToken = default - ) => - botClient.MakeRequestAsync(new SetPassportDataErrorsRequest(userId, errors), cancellationToken); + /// Instance of bot client + /// User identifier + /// Descriptions of the errors + /// The cancellation token to cancel operation. + /// + public static Task SetPassportDataErrorsAsync( + this ITelegramBotClient botClient, + int userId, + IEnumerable errors, + CancellationToken cancellationToken = default + ) => + botClient.MakeRequestAsync(new SetPassportDataErrorsRequest(userId, errors), cancellationToken); - /// - /// Downloads an encrypted Passport file, decrypts it, and writes the content to - /// stream - /// - /// Instance of bot client - /// - /// - /// - /// The cancellation token to cancel operation. - /// File information of the encrypted Passport file on Telegram servers. - /// - public static async Task DownloadAndDecryptPassportFileAsync( - this ITelegramBotClient botClient, - PassportFile passportFile, - FileCredentials fileCredentials, - System.IO.Stream destination, - CancellationToken cancellationToken = default - ) - { - if (passportFile == null) - throw new ArgumentNullException(nameof(passportFile)); - if (fileCredentials == null) - throw new ArgumentNullException(nameof(fileCredentials)); - if (destination == null) - throw new ArgumentNullException(nameof(destination)); - - File fileInfo; + /// + /// Downloads an encrypted Passport file, decrypts it, and writes the content to + /// stream + /// + /// Instance of bot client + /// + /// + /// + /// The cancellation token to cancel operation. + /// File information of the encrypted Passport file on Telegram servers. + /// + public static async Task DownloadAndDecryptPassportFileAsync( + this ITelegramBotClient botClient, + PassportFile passportFile, + FileCredentials fileCredentials, + System.IO.Stream destination, + CancellationToken cancellationToken = default + ) + { + if (passportFile == null) + throw new ArgumentNullException(nameof(passportFile)); + if (fileCredentials == null) + throw new ArgumentNullException(nameof(fileCredentials)); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); - var encryptedContentStream = passportFile.FileSize > 0 - ? new System.IO.MemoryStream(passportFile.FileSize) - : new System.IO.MemoryStream(); + Types.File fileInfo; - using (encryptedContentStream) - { - fileInfo = await botClient.GetInfoAndDownloadFileAsync( - passportFile.FileId, - encryptedContentStream, - cancellationToken - ).ConfigureAwait(false); + var encryptedContentStream = passportFile.FileSize > 0 + ? new System.IO.MemoryStream(passportFile.FileSize) + : new System.IO.MemoryStream(); - encryptedContentStream.Position = 0; + using (encryptedContentStream) + { + fileInfo = await botClient.GetInfoAndDownloadFileAsync( + passportFile.FileId, + encryptedContentStream, + cancellationToken + ).ConfigureAwait(false); - await new Decrypter().DecryptFileAsync( - encryptedContentStream, - fileCredentials, - destination, - cancellationToken - ).ConfigureAwait(false); - } + encryptedContentStream.Position = 0; - return fileInfo; + await new Decrypter().DecryptFileAsync( + encryptedContentStream, + fileCredentials, + destination, + cancellationToken + ).ConfigureAwait(false); } + + return fileInfo; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/Credentials.cs b/src/Telegram.Bot.Extensions.Passport/Types/Credentials.cs index 8416ef7..0493645 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/Credentials.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Credentials.cs @@ -2,24 +2,23 @@ using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// Credentials is a JSON-serialized object. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class Credentials : IDecryptedValue { /// - /// Credentials is a JSON-serialized object. + /// Credentials for encrypted data /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class Credentials : IDecryptedValue - { - /// - /// Credentials for encrypted data - /// - [JsonProperty(Required = Required.Always)] - public SecureData SecureData { get; set; } + [JsonProperty(Required = Required.Always)] + public SecureData SecureData { get; set; } - /// - /// Bot-specified nonce. Make sure that the payload is the same as was passed in the request. - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string Nonce { get; set; } - } + /// + /// Bot-specified nonce. Make sure that the payload is the same as was passed in the request. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Nonce { get; set; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/DataCredentials.cs b/src/Telegram.Bot.Extensions.Passport/Types/DataCredentials.cs index 16cbe41..7499acc 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/DataCredentials.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/DataCredentials.cs @@ -2,24 +2,23 @@ using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// These credentials can be used to decrypt encrypted data from the data field in . +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class DataCredentials { /// - /// These credentials can be used to decrypt encrypted data from the data field in . + /// Checksum of encrypted data /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class DataCredentials - { - /// - /// Checksum of encrypted data - /// - [JsonProperty(Required = Required.Always)] - public string DataHash { get; set; } + [JsonProperty(Required = Required.Always)] + public string DataHash { get; set; } - /// - /// Secret of encrypted data - /// - [JsonProperty(Required = Required.Always)] - public string Secret { get; set; } - } + /// + /// Secret of encrypted data + /// + [JsonProperty(Required = Required.Always)] + public string Secret { get; set; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/FileCredentials.cs b/src/Telegram.Bot.Extensions.Passport/Types/FileCredentials.cs index 78fb3b0..4b4b018 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/FileCredentials.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/FileCredentials.cs @@ -2,25 +2,24 @@ using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// These credentials can be used to decrypt encrypted files from the front_side, reverse_side, selfie and +/// files fields in EncryptedPassportData. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class FileCredentials { /// - /// These credentials can be used to decrypt encrypted files from the front_side, reverse_side, selfie and - /// files fields in EncryptedPassportData. + /// Checksum of encrypted file /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class FileCredentials - { - /// - /// Checksum of encrypted file - /// - [JsonProperty(Required = Required.Always)] - public string FileHash { get; set; } + [JsonProperty(Required = Required.Always)] + public string FileHash { get; set; } - /// - /// Secret of encrypted file - /// - [JsonProperty(Required = Required.Always)] - public string Secret { get; set; } - } + /// + /// Secret of encrypted file + /// + [JsonProperty(Required = Required.Always)] + public string Secret { get; set; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/IDecryptedValue.cs b/src/Telegram.Bot.Extensions.Passport/Types/IDecryptedValue.cs index 908c1c4..1be508a 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/IDecryptedValue.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/IDecryptedValue.cs @@ -1,11 +1,9 @@ // ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.Passport; -namespace Telegram.Bot.Types.Passport +/// +/// Marker interface for type of data in property +/// +public interface IDecryptedValue { - /// - /// Marker interface for type of data in property - /// - public interface IDecryptedValue - { - } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/IdDocumentData.cs b/src/Telegram.Bot.Extensions.Passport/Types/IdDocumentData.cs index 647ddb9..ec11428 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/IdDocumentData.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/IdDocumentData.cs @@ -1,46 +1,44 @@ -using System; -using System.Globalization; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using System.Globalization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// This object represents the data of an identity document. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class IdDocumentData : IDecryptedValue { /// - /// This object represents the data of an identity document. + /// Document number /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class IdDocumentData : IDecryptedValue - { - /// - /// Document number - /// - [JsonProperty(Required = Required.Always)] - public string DocumentNo { get; set; } + [JsonProperty(Required = Required.Always)] + public string DocumentNo { get; set; } - /// - /// Optional. Date of expiry, in DD.MM.YYYY format - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string ExpiryDate { get; set; } + /// + /// Optional. Date of expiry, in DD.MM.YYYY format + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string ExpiryDate { get; set; } - /// - /// Date of expiry if available - /// - public DateTime? Expiry + /// + /// Date of expiry if available + /// + public DateTime? Expiry + { + get { - get + if ( + !string.IsNullOrWhiteSpace(ExpiryDate) && + DateTime.TryParseExact(ExpiryDate, "dd.MM.yyyy", null, DateTimeStyles.None, out var result) + ) { - if ( - !string.IsNullOrWhiteSpace(ExpiryDate) && - DateTime.TryParseExact(ExpiryDate, "dd.MM.yyyy", null, DateTimeStyles.None, out var result) - ) - { - return result; - } - - return null; + return result; } + + return null; } } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/PersonalDetails.cs b/src/Telegram.Bot.Extensions.Passport/Types/PersonalDetails.cs index edcefa2..c31945d 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/PersonalDetails.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/PersonalDetails.cs @@ -1,81 +1,79 @@ -using System; using System.Globalization; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// This object represents personal details. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class PersonalDetails : IDecryptedValue { /// - /// This object represents personal details. + /// First Name /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class PersonalDetails : IDecryptedValue - { - /// - /// First Name - /// - [JsonProperty(Required = Required.Always)] - public string FirstName { get; set; } + [JsonProperty(Required = Required.Always)] + public string FirstName { get; set; } - /// - /// Last Name - /// - [JsonProperty(Required = Required.Always)] - public string LastName { get; set; } + /// + /// Last Name + /// + [JsonProperty(Required = Required.Always)] + public string LastName { get; set; } - /// - /// Optional. Middle Name - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string MiddleName { get; set; } + /// + /// Optional. Middle Name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string MiddleName { get; set; } - /// - /// Date of birth in DD.MM.YYYY format - /// - [JsonProperty(Required = Required.Always)] - public string BirthDate { get; set; } + /// + /// Date of birth in DD.MM.YYYY format + /// + [JsonProperty(Required = Required.Always)] + public string BirthDate { get; set; } - /// - /// Gender, male or female - /// - [JsonProperty(Required = Required.Always)] - public string Gender { get; set; } + /// + /// Gender, male or female + /// + [JsonProperty(Required = Required.Always)] + public string Gender { get; set; } - /// - /// Citizenship (ISO 3166-1 alpha-2 country code) - /// - [JsonProperty(Required = Required.Always)] - public string CountryCode { get; set; } + /// + /// Citizenship (ISO 3166-1 alpha-2 country code) + /// + [JsonProperty(Required = Required.Always)] + public string CountryCode { get; set; } - /// - /// Country of residence (ISO 3166-1 alpha-2 country code) - /// - [JsonProperty(Required = Required.Always)] - public string ResidenceCountryCode { get; set; } + /// + /// Country of residence (ISO 3166-1 alpha-2 country code) + /// + [JsonProperty(Required = Required.Always)] + public string ResidenceCountryCode { get; set; } - /// - /// First Name in the language of the user's country of residence - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string FirstNameNative { get; set; } + /// + /// First Name in the language of the user's country of residence + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string FirstNameNative { get; set; } - /// - /// Last Name in the language of the user's country of residence - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string LastNameNative { get; set; } + /// + /// Last Name in the language of the user's country of residence + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string LastNameNative { get; set; } - /// - /// Optional. Middle Name in the language of the user's country of residence - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string MiddleNameNative { get; set; } + /// + /// Optional. Middle Name in the language of the user's country of residence + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string MiddleNameNative { get; set; } - /// - /// Date of birth - /// - public DateTime Birthdate => - DateTime.ParseExact(BirthDate, "dd.MM.yyyy", null, DateTimeStyles.None); - } + /// + /// Date of birth + /// + public DateTime Birthdate => + DateTime.ParseExact(BirthDate, "dd.MM.yyyy", null, DateTimeStyles.None); } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/ResidentialAddress.cs b/src/Telegram.Bot.Extensions.Passport/Types/ResidentialAddress.cs index d18a415..7c77f01 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/ResidentialAddress.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/ResidentialAddress.cs @@ -2,48 +2,47 @@ using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// This object represents a residential address. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class ResidentialAddress : IDecryptedValue { /// - /// This object represents a residential address. + /// First line for the address /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class ResidentialAddress : IDecryptedValue - { - /// - /// First line for the address - /// - [JsonProperty(Required = Required.Always)] - public string StreetLine1 { get; set; } + [JsonProperty(Required = Required.Always)] + public string StreetLine1 { get; set; } - /// - /// Optional. Second line for the address - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string StreetLine2 { get; set; } + /// + /// Optional. Second line for the address + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string StreetLine2 { get; set; } - /// - /// City - /// - [JsonProperty(Required = Required.Always)] - public string City { get; set; } + /// + /// City + /// + [JsonProperty(Required = Required.Always)] + public string City { get; set; } - /// - /// Optional. State - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string State { get; set; } + /// + /// Optional. State + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string State { get; set; } - /// - /// ISO 3166-1 alpha-2 country code - /// - [JsonProperty(Required = Required.Always)] - public string CountryCode { get; set; } + /// + /// ISO 3166-1 alpha-2 country code + /// + [JsonProperty(Required = Required.Always)] + public string CountryCode { get; set; } - /// - /// Address post code - /// - [JsonProperty(Required = Required.Always)] - public string PostCode { get; set; } - } + /// + /// Address post code + /// + [JsonProperty(Required = Required.Always)] + public string PostCode { get; set; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/SecureData.cs b/src/Telegram.Bot.Extensions.Passport/Types/SecureData.cs index 59f728a..f67bf3b 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/SecureData.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/SecureData.cs @@ -2,79 +2,78 @@ using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// This object represents the credentials required to decrypt encrypted data. All fields are optional and depend +/// on fields that were requested. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class SecureData { /// - /// This object represents the credentials required to decrypt encrypted data. All fields are optional and depend - /// on fields that were requested. + /// Optional. Credentials for encrypted personal details /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class SecureData - { - /// - /// Optional. Credentials for encrypted personal details - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue PersonalDetails { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue PersonalDetails { get; set; } - /// - /// Optional. Credentials for encrypted passport - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue Passport { get; set; } + /// + /// Optional. Credentials for encrypted passport + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue Passport { get; set; } - /// - /// Optional. Credentials for encrypted internal passport - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue InternalPassport { get; set; } + /// + /// Optional. Credentials for encrypted internal passport + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue InternalPassport { get; set; } - /// - /// Optional. Credentials for encrypted driver license - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue DriverLicense { get; set; } + /// + /// Optional. Credentials for encrypted driver license + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue DriverLicense { get; set; } - /// - /// Optional. Credentials for encrypted ID card - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue IdentityCard { get; set; } + /// + /// Optional. Credentials for encrypted ID card + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue IdentityCard { get; set; } - /// - /// Optional. Credentials for encrypted residential address - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue Address { get; set; } + /// + /// Optional. Credentials for encrypted residential address + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue Address { get; set; } - /// - /// Optional. Credentials for encrypted utility bill - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue UtilityBill { get; set; } + /// + /// Optional. Credentials for encrypted utility bill + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue UtilityBill { get; set; } - /// - /// Optional. Credentials for encrypted bank statement - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue BankStatement { get; set; } + /// + /// Optional. Credentials for encrypted bank statement + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue BankStatement { get; set; } - /// - /// Optional. Credentials for encrypted rental agreement - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue RentalAgreement { get; set; } + /// + /// Optional. Credentials for encrypted rental agreement + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue RentalAgreement { get; set; } - /// - /// Optional. Credentials for encrypted registration from internal passport - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue PassportRegistration { get; set; } + /// + /// Optional. Credentials for encrypted registration from internal passport + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue PassportRegistration { get; set; } - /// - /// Optional. Credentials for encrypted temporary registration - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue TemporaryRegistration { get; set; } - } + /// + /// Optional. Credentials for encrypted temporary registration + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SecureValue TemporaryRegistration { get; set; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/SecureValue.cs b/src/Telegram.Bot.Extensions.Passport/Types/SecureValue.cs index 63714b0..e8c2daf 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/SecureValue.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/SecureValue.cs @@ -2,56 +2,55 @@ using Newtonsoft.Json.Serialization; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Types.Passport +namespace Telegram.Bot.Types.Passport; + +/// +/// This object represents the credentials required to decrypt encrypted value. All fields are optional and +/// depend on the type of field. +/// +[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] +public class SecureValue { /// - /// This object represents the credentials required to decrypt encrypted value. All fields are optional and - /// depend on the type of field. + /// Optional. Credentials for encrypted Telegram Passport data. Available for "personal_details", "passport", + /// "driver_license", "identity_card", "identity_passport" and "address" types. /// - [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - public class SecureValue - { - /// - /// Optional. Credentials for encrypted Telegram Passport data. Available for "personal_details", "passport", - /// "driver_license", "identity_card", "identity_passport" and "address" types. - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public DataCredentials Data { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DataCredentials Data { get; set; } - /// - /// Optional. Credentials for encrypted document's front side. Available for "passport", "driver_license", - /// "identity_card" and "internal_passport". - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials FrontSide { get; set; } + /// + /// Optional. Credentials for encrypted document's front side. Available for "passport", "driver_license", + /// "identity_card" and "internal_passport". + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public FileCredentials FrontSide { get; set; } - /// - /// Optional. Credentials for encrypted document's reverse side. Available for "driver_license" and - /// "identity_card". - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials ReverseSide { get; set; } + /// + /// Optional. Credentials for encrypted document's reverse side. Available for "driver_license" and + /// "identity_card". + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public FileCredentials ReverseSide { get; set; } - /// - /// Optional. Credentials for encrypted selfie of the user with a document. Can be available for "passport", - /// "driver_license", "identity_card" and "internal_passport". - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials Selfie { get; set; } + /// + /// Optional. Credentials for encrypted selfie of the user with a document. Can be available for "passport", + /// "driver_license", "identity_card" and "internal_passport". + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public FileCredentials Selfie { get; set; } - /// - /// Optional. Credentials for an encrypted translation of the document. Available for "passport", - /// "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", - /// "rental_agreement", "passport_registration" and "temporary_registration". - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials[] Translation { get; set; } + /// + /// Optional. Credentials for an encrypted translation of the document. Available for "passport", + /// "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", + /// "rental_agreement", "passport_registration" and "temporary_registration". + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public FileCredentials[] Translation { get; set; } - /// - /// Optional. Credentials for encrypted files. Available for "utility_bill", "bank_statement", - /// "rental_agreement", "passport_registration" and "temporary_registration" types. - /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials[] Files { get; set; } - } + /// + /// Optional. Credentials for encrypted files. Available for "utility_bill", "bank_statement", + /// "rental_agreement", "passport_registration" and "temporary_registration" types. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public FileCredentials[] Files { get; set; } } From 38299adab42fba688ed8173275f0a507e08351f3 Mon Sep 17 00:00:00 2001 From: karb0f0s <17474471+karb0f0s@users.noreply.github.com> Date: Fri, 29 Apr 2022 16:24:15 +0300 Subject: [PATCH 3/7] Refactor Passport project --- .../Converters/DateTimeConverter.cs | 11 +++++++ .../Decryption/Decrypter.cs | 12 +++---- .../Enums/Scope.cs | 1 - .../PassportDataDecryptionException.cs | 4 ++- .../Extensions/Extensions.cs | 13 ++++++++ .../Abstractions}/IPassportScopeElement.cs | 10 ++++-- .../AuthorizationRequestParameters.cs | 23 ++++++------- .../PassportErrors}/PassportElementError.cs | 17 +++++----- .../PassportElementErrorDataField.cs | 7 ++-- .../PassportElementErrorFile.cs | 5 +-- .../PassportElementErrorFiles.cs | 5 +-- .../PassportElementErrorFrontSide.cs | 5 +-- .../PassportElementErrorReverseSide.cs | 5 +-- .../PassportElementErrorSelfie.cs | 5 +-- .../PassportElementErrorTranslationFile.cs | 5 +-- .../PassportElementErrorTranslationFiles.cs | 5 +-- .../PassportElementErrorUnspecified.cs | 5 +-- .../Passport}/PassportScope.cs | 10 +++--- .../Passport}/PassportScopeElementOne.cs | 6 ++-- .../PassportScopeElementOneOfSeveral.cs | 6 ++-- .../Passport}/SetPassportDataErrorsRequest.cs | 13 ++++---- .../Telegram.Bot.Extensions.Passport.csproj | 6 +++- .../TelegramBotClientPassportExtensions.cs | 22 ++++++++----- .../Types/{ => Passport}/Credentials.cs | 6 ++-- .../Types/{ => Passport}/DataCredentials.cs | 4 +-- .../Types/{ => Passport}/FileCredentials.cs | 4 +-- .../Types/{ => Passport}/IDecryptedValue.cs | 0 .../Types/{ => Passport}/IdDocumentData.cs | 25 +++----------- .../Types/{ => Passport}/PersonalDetails.cs | 33 ++++++++----------- .../{ => Passport}/ResidentialAddress.cs | 12 +++---- .../Types/{ => Passport}/SecureData.cs | 22 ++++++------- .../Types/{ => Passport}/SecureValue.cs | 12 +++---- 32 files changed, 176 insertions(+), 143 deletions(-) create mode 100644 src/Telegram.Bot.Extensions.Passport/Converters/DateTimeConverter.cs rename src/Telegram.Bot.Extensions.Passport/{Decryption => Exceptions}/PassportDataDecryptionException.cs (76%) create mode 100644 src/Telegram.Bot.Extensions.Passport/Extensions/Extensions.cs rename src/Telegram.Bot.Extensions.Passport/{Request => Requests/Abstractions}/IPassportScopeElement.cs (57%) rename src/Telegram.Bot.Extensions.Passport/{Request => Requests/Passport}/AuthorizationRequestParameters.cs (88%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementError.cs (83%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorDataField.cs (88%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorFile.cs (90%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorFiles.cs (90%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorFrontSide.cs (91%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorReverseSide.cs (90%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorSelfie.cs (90%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorTranslationFile.cs (91%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorTranslationFiles.cs (91%) rename src/Telegram.Bot.Extensions.Passport/{Errors => Requests/Passport/PassportErrors}/PassportElementErrorUnspecified.cs (89%) rename src/Telegram.Bot.Extensions.Passport/{Request => Requests/Passport}/PassportScope.cs (83%) rename src/Telegram.Bot.Extensions.Passport/{Request => Requests/Passport}/PassportScopeElementOne.cs (93%) rename src/Telegram.Bot.Extensions.Passport/{Request => Requests/Passport}/PassportScopeElementOneOfSeveral.cs (93%) rename src/Telegram.Bot.Extensions.Passport/{ => Requests/Passport}/SetPassportDataErrorsRequest.cs (82%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/Credentials.cs (79%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/DataCredentials.cs (86%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/FileCredentials.cs (86%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/IDecryptedValue.cs (100%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/IdDocumentData.cs (56%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/PersonalDetails.cs (67%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/ResidentialAddress.cs (78%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/SecureData.cs (79%) rename src/Telegram.Bot.Extensions.Passport/Types/{ => Passport}/SecureValue.cs (87%) diff --git a/src/Telegram.Bot.Extensions.Passport/Converters/DateTimeConverter.cs b/src/Telegram.Bot.Extensions.Passport/Converters/DateTimeConverter.cs new file mode 100644 index 0000000..1f741e6 --- /dev/null +++ b/src/Telegram.Bot.Extensions.Passport/Converters/DateTimeConverter.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json.Converters; + +namespace Telegram.Bot.Converters; + +internal class CustomDateTimeConverter : IsoDateTimeConverter +{ + public CustomDateTimeConverter() + { + base.DateTimeFormat = "dd.MM.yyyy"; + } +} diff --git a/src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs b/src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs index 0e41d38..430cd73 100644 --- a/src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs +++ b/src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs @@ -20,12 +20,12 @@ RSA key throw new ArgumentNullException(nameof(encryptedCredentials)); if (key is null) throw new ArgumentNullException(nameof(key)); - if (encryptedCredentials.Data is null) - throw new ArgumentNullException(nameof(encryptedCredentials.Data)); - if (encryptedCredentials.Secret is null) - throw new ArgumentNullException(nameof(encryptedCredentials.Secret)); - if (encryptedCredentials.Hash is null) - throw new ArgumentNullException(nameof(encryptedCredentials.Hash)); + //if (encryptedCredentials.Data is null) + // throw new ArgumentNullException(nameof(encryptedCredentials.Data)); + //if (encryptedCredentials.Secret is null) + // throw new ArgumentNullException(nameof(encryptedCredentials.Secret)); + //if (encryptedCredentials.Hash is null) + // throw new ArgumentNullException(nameof(encryptedCredentials.Hash)); byte[] data = Convert.FromBase64String(encryptedCredentials.Data); if (data.Length == 0) diff --git a/src/Telegram.Bot.Extensions.Passport/Enums/Scope.cs b/src/Telegram.Bot.Extensions.Passport/Enums/Scope.cs index 375ed44..9869cdb 100644 --- a/src/Telegram.Bot.Extensions.Passport/Enums/Scope.cs +++ b/src/Telegram.Bot.Extensions.Passport/Enums/Scope.cs @@ -1,5 +1,4 @@ // ReSharper disable once CheckNamespace - namespace Telegram.Bot; public static partial class PassportEnums diff --git a/src/Telegram.Bot.Extensions.Passport/Decryption/PassportDataDecryptionException.cs b/src/Telegram.Bot.Extensions.Passport/Exceptions/PassportDataDecryptionException.cs similarity index 76% rename from src/Telegram.Bot.Extensions.Passport/Decryption/PassportDataDecryptionException.cs rename to src/Telegram.Bot.Extensions.Passport/Exceptions/PassportDataDecryptionException.cs index c5d76f2..3f0ffb9 100644 --- a/src/Telegram.Bot.Extensions.Passport/Decryption/PassportDataDecryptionException.cs +++ b/src/Telegram.Bot.Extensions.Passport/Exceptions/PassportDataDecryptionException.cs @@ -4,12 +4,14 @@ namespace Telegram.Bot.Exceptions; /// /// Represents a fatal error in decryption of Telegram Passport Data /// +#pragma warning disable CA1032 // Implement standard exception constructors public class PassportDataDecryptionException : Exception +#pragma warning restore CA1032 // Implement standard exception constructors { /// /// Initializes a new instance of /// - /// Error description + /// The error message that explains the reason for the exception. public PassportDataDecryptionException(string message) : base(message) { diff --git a/src/Telegram.Bot.Extensions.Passport/Extensions/Extensions.cs b/src/Telegram.Bot.Extensions.Passport/Extensions/Extensions.cs new file mode 100644 index 0000000..b1fd30b --- /dev/null +++ b/src/Telegram.Bot.Extensions.Passport/Extensions/Extensions.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; + +namespace Telegram.Bot.Extensions; + +/// +/// Extension Methods +/// +internal static class ObjectExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static T ThrowIfNull(this T? value, string parameterName) => + value ?? throw new ArgumentNullException(parameterName); +} diff --git a/src/Telegram.Bot.Extensions.Passport/Request/IPassportScopeElement.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Abstractions/IPassportScopeElement.cs similarity index 57% rename from src/Telegram.Bot.Extensions.Passport/Request/IPassportScopeElement.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Abstractions/IPassportScopeElement.cs index e3559f4..a19cc50 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/IPassportScopeElement.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Abstractions/IPassportScopeElement.cs @@ -1,5 +1,6 @@ // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport.Request; + +namespace Telegram.Bot.Requests.Abstractions; /// /// A marker interface for object represents a requested element @@ -7,12 +8,15 @@ namespace Telegram.Bot.Passport.Request; public interface IPassportScopeElement { /// - /// Optional. Use this parameter if you want to request a selfie with the document. + /// Optional. Use this parameter if you want to request a selfie with the document + /// from this list that the user chooses to upload. /// bool? Selfie { get; } /// - /// Optional. Use this parameter if you want to request a translation of the document. + /// Optional. Use this parameter if you want to request a translation of the document + /// from this list that the user chooses to upload. Note: We suggest to only request + /// translations after you have received a valid document that requires one. /// bool? Translation { get; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/AuthorizationRequestParameters.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/AuthorizationRequestParameters.cs similarity index 88% rename from src/Telegram.Bot.Extensions.Passport/Request/AuthorizationRequestParameters.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/AuthorizationRequestParameters.cs index 860604d..220987c 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/AuthorizationRequestParameters.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/AuthorizationRequestParameters.cs @@ -1,7 +1,8 @@ using Newtonsoft.Json; +using Telegram.Bot.Extensions; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport.Request; +namespace Telegram.Bot.Requests; /// /// Parameters for making a Telegram Passport authorization request @@ -12,7 +13,12 @@ public class AuthorizationRequestParameters /// Unique identifier for the bot. You can get it from bot token. For example, for the bot token /// "1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy", the bot id is 1234567. /// - public int BotId { get; } + public long BotId { get; } + + /// + /// Description of the data you want to request + /// + public PassportScope Scope { get; } /// /// Public key of the bot @@ -27,11 +33,6 @@ public class AuthorizationRequestParameters /// public string Nonce { get; } - /// - /// Description of the data you want to request - /// - public PassportScope PassportScope { get; } - /// /// Query string part of the URI generated from the parameters /// @@ -63,16 +64,16 @@ public class AuthorizationRequestParameters /// /// Description of the data you want to request public AuthorizationRequestParameters( - int botId, + long botId, string publicKey, string nonce, PassportScope scope ) { BotId = botId; - PublicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); - Nonce = nonce ?? throw new ArgumentNullException(nameof(nonce)); - PassportScope = scope ?? throw new ArgumentNullException(nameof(PassportScope)); + PublicKey = publicKey.ThrowIfNull(nameof(publicKey)); + Nonce = nonce.ThrowIfNull(nameof(nonce)); + Scope = scope.ThrowIfNull(nameof(scope)); var scopeJson = JsonConvert.SerializeObject(scope); diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementError.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementError.cs similarity index 83% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementError.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementError.cs index b8facd2..e681378 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementError.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementError.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// This object represents an error in the Telegram Passport element which was submitted that should be resolved @@ -12,16 +13,16 @@ namespace Telegram.Bot.Types.Passport; public abstract class PassportElementError { /// - /// Error source. + /// The section of the user's Telegram Passport which has the error. /// [JsonProperty(Required = Required.Always)] - public string Type { get; } + public string Source { get; } /// - /// The section of the user's Telegram Passport which has the error. + /// Error source. /// [JsonProperty(Required = Required.Always)] - public string Source { get; } + public string Type { get; } /// /// Error message @@ -38,8 +39,8 @@ public abstract class PassportElementError /// if any argument is null protected PassportElementError(string source, string type, string message) { - Type = type ?? throw new ArgumentNullException(nameof(type)); - Source = source ?? throw new ArgumentNullException(nameof(source)); - Message = message ?? throw new ArgumentNullException(nameof(message)); + Type = type.ThrowIfNull(nameof(type)); + Source = source.ThrowIfNull(nameof(source)); + Message = message.ThrowIfNull(nameof(message)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorDataField.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorDataField.cs similarity index 88% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorDataField.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorDataField.cs index 4e92529..aa7ab0a 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorDataField.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorDataField.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue in one of the data fields that was provided by the user. The error is considered @@ -42,7 +43,7 @@ string message ) : base("data", type, message) { - FieldName = fieldName ?? throw new ArgumentNullException(nameof(fieldName)); - DataHash = dataHash ?? throw new ArgumentNullException(nameof(dataHash)); + FieldName = fieldName.ThrowIfNull(nameof(fieldName)); + DataHash = dataHash.ThrowIfNull(nameof(dataHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFile.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFile.cs similarity index 90% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFile.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFile.cs index 2eb1ae9..7d666f6 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFile.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFile.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue with a document scan. The error is considered resolved when the file with the document @@ -34,6 +35,6 @@ string message ) : base("file", type, message) { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); + FileHash = fileHash.ThrowIfNull(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFiles.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFiles.cs similarity index 90% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFiles.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFiles.cs index 0896358..4941723 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFiles.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFiles.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue with a list of scans. The error is considered resolved when the list of files containing @@ -34,6 +35,6 @@ string message ) : base("files", type, message) { - FileHashes = fileHashes ?? throw new ArgumentNullException(nameof(fileHashes)); + FileHashes = fileHashes.ThrowIfNull(nameof(fileHashes)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFrontSide.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFrontSide.cs similarity index 91% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFrontSide.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFrontSide.cs index 4a1426b..4aa6f90 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorFrontSide.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorFrontSide.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue with the front side of a document. The error is considered resolved when the file with @@ -34,6 +35,6 @@ string message ) : base("front_side", type, message) { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); + FileHash = fileHash.ThrowIfNull(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorReverseSide.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorReverseSide.cs similarity index 90% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorReverseSide.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorReverseSide.cs index ccae1a0..44e3a6f 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorReverseSide.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorReverseSide.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue with the front side of a document. The error is considered resolved when the file with @@ -33,6 +34,6 @@ string message ) : base("reverse_side", type, message) { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); + FileHash = fileHash.ThrowIfNull(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorSelfie.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorSelfie.cs similarity index 90% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorSelfie.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorSelfie.cs index db2ad8d..2e97350 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorSelfie.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorSelfie.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue with the selfie with a document. The error is considered resolved when the file with @@ -34,6 +35,6 @@ string message ) : base("selfie", type, message) { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); + FileHash = fileHash.ThrowIfNull(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFile.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorTranslationFile.cs similarity index 91% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFile.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorTranslationFile.cs index f3aa59b..9b5ead0 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFile.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorTranslationFile.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue with one of the files that constitute the translation of a document. The error is @@ -35,6 +36,6 @@ string message ) : base("translation_file", type, message) { - FileHash = fileHash ?? throw new ArgumentNullException(nameof(fileHash)); + FileHash = fileHash.ThrowIfNull(nameof(fileHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFiles.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorTranslationFiles.cs similarity index 91% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFiles.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorTranslationFiles.cs index 2bdc0bc..8a33dd4 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorTranslationFiles.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorTranslationFiles.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue with the translated version of a document. The error is considered resolved when a @@ -35,6 +36,6 @@ string message ) : base("translation_files", type, message) { - FileHashes = fileHashes ?? throw new ArgumentNullException(nameof(fileHashes)); + FileHashes = fileHashes.ThrowIfNull(nameof(fileHashes)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorUnspecified.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorUnspecified.cs similarity index 89% rename from src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorUnspecified.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorUnspecified.cs index c6257e8..e631f25 100644 --- a/src/Telegram.Bot.Extensions.Passport/Errors/PassportElementErrorUnspecified.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportErrors/PassportElementErrorUnspecified.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; // ReSharper disable CheckNamespace -namespace Telegram.Bot.Types.Passport; +namespace Telegram.Bot.Requests.PassportErrors; /// /// Represents an issue in an unspecified place. The error is considered resolved when new data is added. @@ -32,6 +33,6 @@ string message ) : base("unspecified", type, message) { - ElementHash = elementHash ?? throw new ArgumentNullException(nameof(elementHash)); + ElementHash = elementHash.ThrowIfNull(nameof(elementHash)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/PassportScope.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScope.cs similarity index 83% rename from src/Telegram.Bot.Extensions.Passport/Request/PassportScope.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScope.cs index e8b0a66..c841eb2 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/PassportScope.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScope.cs @@ -1,8 +1,10 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; +using Telegram.Bot.Requests.Abstractions; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport.Request; +namespace Telegram.Bot.Requests; /// /// This object represents the data to be requested. @@ -18,7 +20,7 @@ public class PassportScope public IEnumerable Data { get; } /// - /// Scope version + /// Scope version, must be 1 /// [JsonProperty(Required = Required.Always)] public int V { get; } @@ -30,11 +32,11 @@ public class PassportScope /// List of requested elements, each type may be used only once in the entire array of /// objects /// - /// Scope version. Defaults to 1. + /// Scope version, must be 1 /// public PassportScope(IEnumerable data, int v = 1) { - Data = data ?? throw new ArgumentNullException(nameof(data)); + Data = data.ThrowIfNull(nameof(data)); V = v; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOne.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScopeElementOne.cs similarity index 93% rename from src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOne.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScopeElementOne.cs index 454a894..d816024 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOne.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScopeElementOne.cs @@ -1,8 +1,10 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; +using Telegram.Bot.Requests.Abstractions; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport.Request; +namespace Telegram.Bot.Requests; /// /// This object represents one particular element that must be provided. If no options are needed, String @@ -47,6 +49,6 @@ public class PassportScopeElementOne : IPassportScopeElement /// public PassportScopeElementOne(string type) { - Type = type ?? throw new ArgumentNullException(nameof(type)); + Type = type.ThrowIfNull(nameof(type)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOneOfSeveral.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScopeElementOneOfSeveral.cs similarity index 93% rename from src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOneOfSeveral.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScopeElementOneOfSeveral.cs index e12c62e..27da826 100644 --- a/src/Telegram.Bot.Extensions.Passport/Request/PassportScopeElementOneOfSeveral.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/PassportScopeElementOneOfSeveral.cs @@ -1,8 +1,10 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; +using Telegram.Bot.Requests.Abstractions; // ReSharper disable once CheckNamespace -namespace Telegram.Bot.Passport.Request; +namespace Telegram.Bot.Requests; /// /// This object represents several elements one of which must be provided. @@ -44,6 +46,6 @@ public class PassportScopeElementOneOfSeveral : IPassportScopeElement /// public PassportScopeElementOneOfSeveral(IEnumerable oneOf) { - OneOf = oneOf ?? throw new ArgumentNullException(nameof(oneOf)); + OneOf = oneOf.ThrowIfNull(nameof(oneOf)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/SetPassportDataErrorsRequest.cs b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/SetPassportDataErrorsRequest.cs similarity index 82% rename from src/Telegram.Bot.Extensions.Passport/SetPassportDataErrorsRequest.cs rename to src/Telegram.Bot.Extensions.Passport/Requests/Passport/SetPassportDataErrorsRequest.cs index 7b43f2b..8b85d39 100644 --- a/src/Telegram.Bot.Extensions.Passport/SetPassportDataErrorsRequest.cs +++ b/src/Telegram.Bot.Extensions.Passport/Requests/Passport/SetPassportDataErrorsRequest.cs @@ -1,12 +1,13 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -using Telegram.Bot.Types.Passport; +using Telegram.Bot.Extensions; +using Telegram.Bot.Requests.PassportErrors; -// ReSharper disable CheckNamespace +// ReSharper disable once CheckNamespace namespace Telegram.Bot.Requests; /// -/// Informs a user that some of the Telegram Passport elements they provided contains errors. +/// Informs a user that some of the Telegram Passport elements they provided contain errors. /// The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of /// the field for which you returned the error must change). Returns True on success. /// Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. @@ -20,7 +21,7 @@ public class SetPassportDataErrorsRequest : RequestBase /// User identifier /// [JsonProperty(Required = Required.Always)] - public int UserId { get; } + public long UserId { get; } /// /// Descriptions of the errors @@ -36,10 +37,10 @@ public class SetPassportDataErrorsRequest : RequestBase /// /// If is null /// - public SetPassportDataErrorsRequest(int userId, IEnumerable errors) + public SetPassportDataErrorsRequest(long userId, IEnumerable errors) : base("setPassportDataErrors") { UserId = userId; - Errors = errors ?? throw new ArgumentNullException(nameof(errors)); + Errors = errors.ThrowIfNull(nameof(errors)); } } diff --git a/src/Telegram.Bot.Extensions.Passport/Telegram.Bot.Extensions.Passport.csproj b/src/Telegram.Bot.Extensions.Passport/Telegram.Bot.Extensions.Passport.csproj index 3814782..3c8d439 100644 --- a/src/Telegram.Bot.Extensions.Passport/Telegram.Bot.Extensions.Passport.csproj +++ b/src/Telegram.Bot.Extensions.Passport/Telegram.Bot.Extensions.Passport.csproj @@ -29,8 +29,12 @@ + $(NoWarn);CA1014 $(NoWarn);CA1040 + $(NoWarn);CA1056 + $(NoWarn);CA1819 $(NoWarn);CA1822 + $(NoWarn);CA2208 @@ -44,6 +48,6 @@ - + diff --git a/src/Telegram.Bot.Extensions.Passport/TelegramBotClientPassportExtensions.cs b/src/Telegram.Bot.Extensions.Passport/TelegramBotClientPassportExtensions.cs index 4a68771..86b1047 100644 --- a/src/Telegram.Bot.Extensions.Passport/TelegramBotClientPassportExtensions.cs +++ b/src/Telegram.Bot.Extensions.Passport/TelegramBotClientPassportExtensions.cs @@ -1,5 +1,7 @@ +using Telegram.Bot.Extensions; using Telegram.Bot.Passport; using Telegram.Bot.Requests; +using Telegram.Bot.Requests.PassportErrors; using Telegram.Bot.Types.Passport; // ReSharper disable once CheckNamespace @@ -24,13 +26,15 @@ public static class TelegramBotClientPassportExtensions /// Descriptions of the errors /// The cancellation token to cancel operation. /// - public static Task SetPassportDataErrorsAsync( + public static async Task SetPassportDataErrorsAsync( this ITelegramBotClient botClient, - int userId, + long userId, IEnumerable errors, CancellationToken cancellationToken = default ) => - botClient.MakeRequestAsync(new SetPassportDataErrorsRequest(userId, errors), cancellationToken); + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(new SetPassportDataErrorsRequest(userId, errors), cancellationToken) + .ConfigureAwait(false); /// /// Downloads an encrypted Passport file, decrypts it, and writes the content to @@ -51,18 +55,18 @@ public static Task SetPassportDataErrorsAsync( CancellationToken cancellationToken = default ) { - if (passportFile == null) + if (passportFile is null) throw new ArgumentNullException(nameof(passportFile)); - if (fileCredentials == null) + if (fileCredentials is null) throw new ArgumentNullException(nameof(fileCredentials)); - if (destination == null) + if (destination is null) throw new ArgumentNullException(nameof(destination)); Types.File fileInfo; - var encryptedContentStream = passportFile.FileSize > 0 - ? new System.IO.MemoryStream(passportFile.FileSize) - : new System.IO.MemoryStream(); + var encryptedContentStream = (passportFile.FileSize is int fileSize and > 0) + ? new MemoryStream(capacity: fileSize) + : new MemoryStream(); using (encryptedContentStream) { diff --git a/src/Telegram.Bot.Extensions.Passport/Types/Credentials.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/Credentials.cs similarity index 79% rename from src/Telegram.Bot.Extensions.Passport/Types/Credentials.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/Credentials.cs index 0493645..6c90134 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/Credentials.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Passport/Credentials.cs @@ -14,11 +14,11 @@ public class Credentials : IDecryptedValue /// Credentials for encrypted data /// [JsonProperty(Required = Required.Always)] - public SecureData SecureData { get; set; } + public SecureData SecureData { get; set; } = default!; /// /// Bot-specified nonce. Make sure that the payload is the same as was passed in the request. /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string Nonce { get; set; } + [JsonProperty(Required = Required.Always)] + public string Nonce { get; set; } = default!; } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/DataCredentials.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/DataCredentials.cs similarity index 86% rename from src/Telegram.Bot.Extensions.Passport/Types/DataCredentials.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/DataCredentials.cs index 7499acc..0bd3d69 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/DataCredentials.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Passport/DataCredentials.cs @@ -14,11 +14,11 @@ public class DataCredentials /// Checksum of encrypted data /// [JsonProperty(Required = Required.Always)] - public string DataHash { get; set; } + public string DataHash { get; set; } = default!; /// /// Secret of encrypted data /// [JsonProperty(Required = Required.Always)] - public string Secret { get; set; } + public string Secret { get; set; } = default!; } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/FileCredentials.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/FileCredentials.cs similarity index 86% rename from src/Telegram.Bot.Extensions.Passport/Types/FileCredentials.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/FileCredentials.cs index 4b4b018..415049e 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/FileCredentials.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Passport/FileCredentials.cs @@ -15,11 +15,11 @@ public class FileCredentials /// Checksum of encrypted file /// [JsonProperty(Required = Required.Always)] - public string FileHash { get; set; } + public string FileHash { get; set; } = default!; /// /// Secret of encrypted file /// [JsonProperty(Required = Required.Always)] - public string Secret { get; set; } + public string Secret { get; set; } = default!; } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/IDecryptedValue.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/IDecryptedValue.cs similarity index 100% rename from src/Telegram.Bot.Extensions.Passport/Types/IDecryptedValue.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/IDecryptedValue.cs diff --git a/src/Telegram.Bot.Extensions.Passport/Types/IdDocumentData.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/IdDocumentData.cs similarity index 56% rename from src/Telegram.Bot.Extensions.Passport/Types/IdDocumentData.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/IdDocumentData.cs index ec11428..d43b277 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/IdDocumentData.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Passport/IdDocumentData.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System.Globalization; +using Telegram.Bot.Converters; // ReSharper disable once CheckNamespace namespace Telegram.Bot.Types.Passport; @@ -15,30 +16,12 @@ public class IdDocumentData : IDecryptedValue /// Document number /// [JsonProperty(Required = Required.Always)] - public string DocumentNo { get; set; } + public string DocumentNo { get; set; } = default!; /// /// Optional. Date of expiry, in DD.MM.YYYY format /// + [JsonConverter(typeof(CustomDateTimeConverter))] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string ExpiryDate { get; set; } - - /// - /// Date of expiry if available - /// - public DateTime? Expiry - { - get - { - if ( - !string.IsNullOrWhiteSpace(ExpiryDate) && - DateTime.TryParseExact(ExpiryDate, "dd.MM.yyyy", null, DateTimeStyles.None, out var result) - ) - { - return result; - } - - return null; - } - } + public DateTime? ExpiryDate { get; set; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/PersonalDetails.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/PersonalDetails.cs similarity index 67% rename from src/Telegram.Bot.Extensions.Passport/Types/PersonalDetails.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/PersonalDetails.cs index c31945d..d73930d 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/PersonalDetails.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Passport/PersonalDetails.cs @@ -1,6 +1,6 @@ -using System.Globalization; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Telegram.Bot.Converters; // ReSharper disable once CheckNamespace namespace Telegram.Bot.Types.Passport; @@ -15,65 +15,60 @@ public class PersonalDetails : IDecryptedValue /// First Name /// [JsonProperty(Required = Required.Always)] - public string FirstName { get; set; } + public string FirstName { get; set; } = default!; /// /// Last Name /// [JsonProperty(Required = Required.Always)] - public string LastName { get; set; } + public string LastName { get; set; } = default!; /// /// Optional. Middle Name /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string MiddleName { get; set; } + public string? MiddleName { get; set; } /// /// Date of birth in DD.MM.YYYY format /// + [JsonConverter(typeof(CustomDateTimeConverter))] [JsonProperty(Required = Required.Always)] - public string BirthDate { get; set; } + public DateTime BirthDate { get; set; } = default!; /// /// Gender, male or female /// [JsonProperty(Required = Required.Always)] - public string Gender { get; set; } + public string Gender { get; set; } = default!; /// /// Citizenship (ISO 3166-1 alpha-2 country code) /// [JsonProperty(Required = Required.Always)] - public string CountryCode { get; set; } + public string CountryCode { get; set; } = default!; /// /// Country of residence (ISO 3166-1 alpha-2 country code) /// [JsonProperty(Required = Required.Always)] - public string ResidenceCountryCode { get; set; } + public string ResidenceCountryCode { get; set; } = default!; /// /// First Name in the language of the user's country of residence /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string FirstNameNative { get; set; } + [JsonProperty(Required = Required.Always)] + public string FirstNameNative { get; set; } = default!; /// /// Last Name in the language of the user's country of residence /// - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string LastNameNative { get; set; } + [JsonProperty(Required = Required.Always)] + public string LastNameNative { get; set; } = default!; /// /// Optional. Middle Name in the language of the user's country of residence /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string MiddleNameNative { get; set; } - - /// - /// Date of birth - /// - public DateTime Birthdate => - DateTime.ParseExact(BirthDate, "dd.MM.yyyy", null, DateTimeStyles.None); + public string? MiddleNameNative { get; set; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/ResidentialAddress.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/ResidentialAddress.cs similarity index 78% rename from src/Telegram.Bot.Extensions.Passport/Types/ResidentialAddress.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/ResidentialAddress.cs index 7c77f01..534e685 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/ResidentialAddress.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Passport/ResidentialAddress.cs @@ -14,35 +14,35 @@ public class ResidentialAddress : IDecryptedValue /// First line for the address /// [JsonProperty(Required = Required.Always)] - public string StreetLine1 { get; set; } + public string StreetLine1 { get; set; } = default!; /// /// Optional. Second line for the address /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string StreetLine2 { get; set; } + public string? StreetLine2 { get; set; } /// /// City /// [JsonProperty(Required = Required.Always)] - public string City { get; set; } + public string City { get; set; } = default!; /// /// Optional. State /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string State { get; set; } + public string? State { get; set; } /// /// ISO 3166-1 alpha-2 country code /// [JsonProperty(Required = Required.Always)] - public string CountryCode { get; set; } + public string CountryCode { get; set; } = default!; /// /// Address post code /// [JsonProperty(Required = Required.Always)] - public string PostCode { get; set; } + public string PostCode { get; set; } = default!; } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/SecureData.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/SecureData.cs similarity index 79% rename from src/Telegram.Bot.Extensions.Passport/Types/SecureData.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/SecureData.cs index f67bf3b..d55c771 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/SecureData.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Passport/SecureData.cs @@ -15,65 +15,65 @@ public class SecureData /// Optional. Credentials for encrypted personal details /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue PersonalDetails { get; set; } + public SecureValue? PersonalDetails { get; set; } /// /// Optional. Credentials for encrypted passport /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue Passport { get; set; } + public SecureValue? Passport { get; set; } /// /// Optional. Credentials for encrypted internal passport /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue InternalPassport { get; set; } + public SecureValue? InternalPassport { get; set; } /// /// Optional. Credentials for encrypted driver license /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue DriverLicense { get; set; } + public SecureValue? DriverLicense { get; set; } /// /// Optional. Credentials for encrypted ID card /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue IdentityCard { get; set; } + public SecureValue? IdentityCard { get; set; } /// /// Optional. Credentials for encrypted residential address /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue Address { get; set; } + public SecureValue? Address { get; set; } /// /// Optional. Credentials for encrypted utility bill /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue UtilityBill { get; set; } + public SecureValue? UtilityBill { get; set; } /// /// Optional. Credentials for encrypted bank statement /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue BankStatement { get; set; } + public SecureValue? BankStatement { get; set; } /// /// Optional. Credentials for encrypted rental agreement /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue RentalAgreement { get; set; } + public SecureValue? RentalAgreement { get; set; } /// /// Optional. Credentials for encrypted registration from internal passport /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue PassportRegistration { get; set; } + public SecureValue? PassportRegistration { get; set; } /// /// Optional. Credentials for encrypted temporary registration /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public SecureValue TemporaryRegistration { get; set; } + public SecureValue? TemporaryRegistration { get; set; } } diff --git a/src/Telegram.Bot.Extensions.Passport/Types/SecureValue.cs b/src/Telegram.Bot.Extensions.Passport/Types/Passport/SecureValue.cs similarity index 87% rename from src/Telegram.Bot.Extensions.Passport/Types/SecureValue.cs rename to src/Telegram.Bot.Extensions.Passport/Types/Passport/SecureValue.cs index e8c2daf..28ea4bd 100644 --- a/src/Telegram.Bot.Extensions.Passport/Types/SecureValue.cs +++ b/src/Telegram.Bot.Extensions.Passport/Types/Passport/SecureValue.cs @@ -16,28 +16,28 @@ public class SecureValue /// "driver_license", "identity_card", "identity_passport" and "address" types. /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public DataCredentials Data { get; set; } + public DataCredentials? Data { get; set; } /// /// Optional. Credentials for encrypted document's front side. Available for "passport", "driver_license", /// "identity_card" and "internal_passport". /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials FrontSide { get; set; } + public FileCredentials? FrontSide { get; set; } /// /// Optional. Credentials for encrypted document's reverse side. Available for "driver_license" and /// "identity_card". /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials ReverseSide { get; set; } + public FileCredentials? ReverseSide { get; set; } /// /// Optional. Credentials for encrypted selfie of the user with a document. Can be available for "passport", /// "driver_license", "identity_card" and "internal_passport". /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials Selfie { get; set; } + public FileCredentials? Selfie { get; set; } /// /// Optional. Credentials for an encrypted translation of the document. Available for "passport", @@ -45,12 +45,12 @@ public class SecureValue /// "rental_agreement", "passport_registration" and "temporary_registration". /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials[] Translation { get; set; } + public FileCredentials[]? Translation { get; set; } /// /// Optional. Credentials for encrypted files. Available for "utility_bill", "bank_statement", /// "rental_agreement", "passport_registration" and "temporary_registration" types. /// [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public FileCredentials[] Files { get; set; } + public FileCredentials[]? Files { get; set; } } From fc65354408e23847569349bcc409e2790db459b4 Mon Sep 17 00:00:00 2001 From: karb0f0s <17474471+karb0f0s@users.noreply.github.com> Date: Fri, 29 Apr 2022 16:24:48 +0300 Subject: [PATCH 4/7] Fix UnitTests --- .../Authorization Request Parameters Tests.cs | 20 ++++---- .../Credentials Decryption Tests.cs | 15 ++---- .../Decryption/Data Decryption Tests.cs | 23 ++++----- .../Decryption/File Bytes Decryption Tests.cs | 23 ++++----- .../File Stream Decryption Tests.cs | 37 +++++--------- .../Decryption/Private Key Import Tests.cs | 14 +++--- test/UnitTests/Encryption Key.cs | 6 +-- .../Identity Card and Utility Bill Tests.cs | 48 ++++++++++--------- .../Address Decryption Tests.cs | 6 +-- .../Driver License Decryption Tests.cs | 16 ++++--- .../Personal Details Decryption Tests.cs | 8 ++-- test/UnitTests/UnitTests.csproj | 29 +++++------ 12 files changed, 115 insertions(+), 130 deletions(-) diff --git a/test/UnitTests/Authorization Request/Authorization Request Parameters Tests.cs b/test/UnitTests/Authorization Request/Authorization Request Parameters Tests.cs index 9a1519a..91d7042 100644 --- a/test/UnitTests/Authorization Request/Authorization Request Parameters Tests.cs +++ b/test/UnitTests/Authorization Request/Authorization Request Parameters Tests.cs @@ -1,7 +1,9 @@ // ReSharper disable StringLiteralTypo -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; +using Telegram.Bot.Requests.Abstractions; using Xunit; +using System; namespace UnitTests.Authorization_Request { @@ -16,19 +18,19 @@ public void Should_Create_Request_Params() "&public_key=PUB%20KEY" + "&nonce=%2FNonce%21%2F"; - AuthorizationRequestParameters requestParameters = new AuthorizationRequestParameters( - 123, - "PUB KEY", - "/Nonce!/", - new PassportScope(new IPassportScopeElement[0]) + AuthorizationRequestParameters requestParameters = new( + botId: 123, + publicKey: "PUB KEY", + nonce: "/Nonce!/", + scope: new PassportScope(Array.Empty()) ); Assert.Equal(123, requestParameters.BotId); Assert.Equal("PUB KEY", requestParameters.PublicKey); Assert.Equal("/Nonce!/", requestParameters.Nonce); - Assert.NotNull(requestParameters.PassportScope); - Assert.Equal(1, requestParameters.PassportScope.V); - Assert.Empty(requestParameters.PassportScope.Data); + Assert.NotNull(requestParameters.Scope); + Assert.Equal(1, requestParameters.Scope.V); + Assert.Empty(requestParameters.Scope.Data); Assert.Equal(expectedQuery, requestParameters.Query); Assert.Equal("tg:resolve?" + expectedQuery, requestParameters.AndroidUri); Assert.Equal("tg://resolve?" + expectedQuery, requestParameters.Uri); diff --git a/test/UnitTests/Decryption/Credentials Decryption Tests.cs b/test/UnitTests/Decryption/Credentials Decryption Tests.cs index a37d32b..5806e80 100644 --- a/test/UnitTests/Decryption/Credentials Decryption Tests.cs +++ b/test/UnitTests/Decryption/Credentials Decryption Tests.cs @@ -269,10 +269,9 @@ public void Should_Throw_If_Null_Credentials() IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => - decrypter.DecryptCredentials(null, null) + decrypter.DecryptCredentials(null!, null!) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: encryptedCredentials$", exception.Message); Assert.IsType(exception); } @@ -282,10 +281,9 @@ public void Should_Throw_If_Null_Key() IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => - decrypter.DecryptCredentials(new EncryptedCredentials(), null) + decrypter.DecryptCredentials(new EncryptedCredentials(), null!) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: key$", exception.Message); Assert.IsType(exception); } @@ -298,7 +296,6 @@ public void Should_Throw_If_Null_Credentials_Data() decrypter.DecryptCredentials(new EncryptedCredentials(), RSA.Create()) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: Data$", exception.Message); Assert.IsType(exception); } @@ -316,8 +313,7 @@ public void Should_Throw_If_Null_Credentials_Secret() decrypter.DecryptCredentials(encryptedCredentials, RSA.Create()) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: Secret$", exception.Message); - Assert.IsType(exception); + Assert.IsType(exception); } [Fact(DisplayName = "Should throw when null credentials hash is passed")] @@ -335,11 +331,11 @@ public void Should_Throw_If_Null_Credentials_Hash() decrypter.DecryptCredentials(encryptedCredentials, RSA.Create()) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: Hash$", exception.Message); - Assert.IsType(exception); + Assert.IsType(exception); } [Fact(DisplayName = "Should throw when credentials data string is empty")] + public void Should_Throw_If_Empty_Credentials_Data_String() { EncryptedCredentials encryptedCredentials = new EncryptedCredentials @@ -355,7 +351,6 @@ public void Should_Throw_If_Empty_Credentials_Data_String() decrypter.DecryptCredentials(encryptedCredentials, RSA.Create()) ); - Assert.Matches(@"^Data is empty\.\s+Parameter name: Data$", exception.Message); Assert.IsType(exception); } diff --git a/test/UnitTests/Decryption/Data Decryption Tests.cs b/test/UnitTests/Decryption/Data Decryption Tests.cs index f7b4a2f..4ba2b22 100644 --- a/test/UnitTests/Decryption/Data Decryption Tests.cs +++ b/test/UnitTests/Decryption/Data Decryption Tests.cs @@ -1,8 +1,8 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo -using System; using Newtonsoft.Json; +using System; using Telegram.Bot.Exceptions; using Telegram.Bot.Passport; using Telegram.Bot.Types.Passport; @@ -48,10 +48,9 @@ public void Should_Throw_If_Null_EncryptedContent() IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => - decrypter.DecryptData(null, null) + decrypter.DecryptData(null!, null!) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: encryptedData$", exception.Message); Assert.IsType(exception); } @@ -61,10 +60,9 @@ public void Should_Throw_If_Null_DataCredentials() IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => - decrypter.DecryptData("", null) + decrypter.DecryptData("", null!) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: dataCredentials$", exception.Message); Assert.IsType(exception); } @@ -76,7 +74,6 @@ public void Should_Throw_If_Null_Secret() decrypter.DecryptData("", new DataCredentials()) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: Secret$", exception.Message); Assert.IsType(exception); } @@ -85,12 +82,11 @@ public void Should_Throw_If_Null_Hash() { IDecrypter decrypter = new Decrypter(); - DataCredentials dataCredentials = new DataCredentials {Secret = ""}; + DataCredentials dataCredentials = new DataCredentials { Secret = "" }; Exception exception = Assert.ThrowsAny(() => decrypter.DecryptData("", dataCredentials) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: DataHash$", exception.Message); Assert.IsType(exception); } @@ -98,13 +94,12 @@ public void Should_Throw_If_Null_Hash() public void Should_Throw_If_Empty_Data_String_Length() { IDecrypter decrypter = new Decrypter(); - DataCredentials dataCredentials = new DataCredentials {Secret = "", DataHash = ""}; + DataCredentials dataCredentials = new DataCredentials { Secret = "", DataHash = "" }; Exception exception = Assert.ThrowsAny(() => decrypter.DecryptData("", dataCredentials) ); - Assert.Matches(@"^Data is empty\.\s+Parameter name: encryptedData$", exception.Message); Assert.IsType(exception); } @@ -112,7 +107,7 @@ public void Should_Throw_If_Empty_Data_String_Length() public void Should_Throw_If_Invalid_Data_String_Length() { IDecrypter decrypter = new Decrypter(); - DataCredentials dataCredentials = new DataCredentials {Secret = "", DataHash = ""}; + DataCredentials dataCredentials = new DataCredentials { Secret = "", DataHash = "" }; Exception exception = Assert.ThrowsAny(() => decrypter.DecryptData("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", dataCredentials) @@ -127,7 +122,7 @@ public void Should_Throw_If_Invalid_Data_String_Length() [InlineData("FooBarBazlg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=")] public void Should_Throw_If_Invalid_Secret(string secret) { - DataCredentials dataCredentials = new DataCredentials {Secret = secret, DataHash = ""}; + DataCredentials dataCredentials = new DataCredentials { Secret = secret, DataHash = "" }; IDecrypter decrypter = new Decrypter(); Assert.Throws(() => @@ -140,7 +135,7 @@ public void Should_Throw_If_Invalid_Secret(string secret) [InlineData("FooBarBazlg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=")] public void Should_Throw_If_Invalid_Data_Hash(string dataHash) { - DataCredentials dataCredentials = new DataCredentials {Secret = "", DataHash = dataHash}; + DataCredentials dataCredentials = new DataCredentials { Secret = "", DataHash = dataHash }; IDecrypter decrypter = new Decrypter(); Assert.ThrowsAny(() => @@ -154,7 +149,7 @@ public void Should_Throw_If_Invalid_Data_Hash(string dataHash) [InlineData("Zm9v")] public void Should_Throw_If_Invalid_Data_Hash_Length(string dataHash) { - DataCredentials dataCredentials = new DataCredentials {Secret = "", DataHash = dataHash}; + DataCredentials dataCredentials = new DataCredentials { Secret = "", DataHash = dataHash }; IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => diff --git a/test/UnitTests/Decryption/File Bytes Decryption Tests.cs b/test/UnitTests/Decryption/File Bytes Decryption Tests.cs index c1b9613..bb3587d 100644 --- a/test/UnitTests/Decryption/File Bytes Decryption Tests.cs +++ b/test/UnitTests/Decryption/File Bytes Decryption Tests.cs @@ -123,10 +123,9 @@ public void Should_Throw_If_Null_EncryptedContent() IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => - decrypter.DecryptFile(null, null) + decrypter.DecryptFile(null!, null!) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: encryptedContent$", exception.Message); Assert.IsType(exception); } @@ -136,10 +135,9 @@ public void Should_Throw_If_Null_FileCredentials() IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => - decrypter.DecryptFile(new byte[0], null) + decrypter.DecryptFile(Array.Empty(), null!) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: fileCredentials$", exception.Message); Assert.IsType(exception); } @@ -148,10 +146,9 @@ public void Should_Throw_If_Null_Secret() { IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => - decrypter.DecryptFile(new byte[0], new FileCredentials()) + decrypter.DecryptFile(Array.Empty(), new FileCredentials()) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: Secret$", exception.Message); Assert.IsType(exception); } @@ -160,12 +157,11 @@ public void Should_Throw_If_Null_Hash() { IDecrypter decrypter = new Decrypter(); - FileCredentials fileCredentials = new FileCredentials {Secret = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "" }; Exception exception = Assert.ThrowsAny(() => decrypter.DecryptFile(new byte[0], fileCredentials) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: FileHash$", exception.Message); Assert.IsType(exception); } @@ -173,13 +169,12 @@ public void Should_Throw_If_Null_Hash() public void Should_Throw_If_Empty_Data_Bytes_Length() { IDecrypter decrypter = new Decrypter(); - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = "" }; Exception exception = Assert.ThrowsAny(() => decrypter.DecryptFile(new byte[0], fileCredentials) ); - Assert.Matches(@"^Data array is empty\.\s+Parameter name: encryptedContent$", exception.Message); Assert.IsType(exception); } @@ -187,7 +182,7 @@ public void Should_Throw_If_Empty_Data_Bytes_Length() public void Should_Throw_If_Invalid_Data_Bytes_Length() { IDecrypter decrypter = new Decrypter(); - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = "" }; Exception exception = Assert.ThrowsAny(() => decrypter.DecryptFile(new byte[16 + 1], fileCredentials) @@ -202,7 +197,7 @@ public void Should_Throw_If_Invalid_Data_Bytes_Length() [InlineData("FooBarBazlg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=")] public void Should_Throw_If_Invalid_Secret(string secret) { - FileCredentials fileCredentials = new FileCredentials {Secret = secret, FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = secret, FileHash = "" }; IDecrypter decrypter = new Decrypter(); Assert.Throws(() => @@ -215,7 +210,7 @@ public void Should_Throw_If_Invalid_Secret(string secret) [InlineData("FooBarBazlg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=")] public void Should_Throw_If_Invalid_Hash(string fileHash) { - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = fileHash}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = fileHash }; IDecrypter decrypter = new Decrypter(); Assert.ThrowsAny(() => @@ -229,7 +224,7 @@ public void Should_Throw_If_Invalid_Hash(string fileHash) [InlineData("Zm9v")] public void Should_Throw_If_Invalid_Hash_Length(string fileHash) { - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = fileHash}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = fileHash }; IDecrypter decrypter = new Decrypter(); Exception exception = Assert.ThrowsAny(() => diff --git a/test/UnitTests/Decryption/File Stream Decryption Tests.cs b/test/UnitTests/Decryption/File Stream Decryption Tests.cs index 58d1f3e..e3967a7 100644 --- a/test/UnitTests/Decryption/File Stream Decryption Tests.cs +++ b/test/UnitTests/Decryption/File Stream Decryption Tests.cs @@ -113,8 +113,8 @@ public async Task Should_Throw_Decrypting_Unencrypted_File() ); } - Assert.Equal("The input data is not a complete block.", exception.Message); Assert.IsType(exception); + Assert.Equal("The input data is not a complete block.", exception.Message); } [Fact(DisplayName = "Should throw when decrypting from a stream that is not positioned at the beginning")] @@ -187,10 +187,9 @@ public async Task Should_Throw_If_Null_EncryptedContent() IDecrypter decrypter = new Decrypter(); Exception exception = await Assert.ThrowsAnyAsync(() => - decrypter.DecryptFileAsync(null, null, null) + decrypter.DecryptFileAsync(null!, null!, null!) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: encryptedContent$", exception.Message); Assert.IsType(exception); } @@ -200,10 +199,9 @@ public async Task Should_Throw_If_Null_FileCredentials() IDecrypter decrypter = new Decrypter(); Exception exception = await Assert.ThrowsAnyAsync(() => - decrypter.DecryptFileAsync(new MemoryStream(), null, null) + decrypter.DecryptFileAsync(new MemoryStream(), null!, null!) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: fileCredentials$", exception.Message); Assert.IsType(exception); } @@ -215,7 +213,6 @@ public async Task Should_Throw_If_Null_Secret() decrypter.DecryptFileAsync(new MemoryStream(), new FileCredentials(), new MemoryStream()) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: Secret$", exception.Message); Assert.IsType(exception); } @@ -224,12 +221,11 @@ public async Task Should_Throw_If_Null_Hash() { IDecrypter decrypter = new Decrypter(); - FileCredentials fileCredentials = new FileCredentials {Secret = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "" }; Exception exception = await Assert.ThrowsAnyAsync(() => decrypter.DecryptFileAsync(new MemoryStream(), fileCredentials, new MemoryStream()) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: FileHash$", exception.Message); Assert.IsType(exception); } @@ -237,7 +233,7 @@ public async Task Should_Throw_If_Null_Hash() public async Task Should_Throw_If_NonReadable_Data_Stream() { IDecrypter decrypter = new Decrypter(); - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = "" }; Exception exception; using (Stream encStream = File.OpenWrite("Files/s_dec6.driver_license-selfie.jpg")) @@ -247,24 +243,19 @@ public async Task Should_Throw_If_NonReadable_Data_Stream() ); } - Assert.Matches( - @"^Stream does not support reading\.\s+Parameter name: encryptedContent$", - exception.Message - ); Assert.IsType(exception); } [Fact(DisplayName = "Should throw when seekable data stream is empty")] public async Task Should_Throw_If_Empty_Data_Stream() { - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = "" }; IDecrypter decrypter = new Decrypter(); Exception exception = await Assert.ThrowsAnyAsync(() => decrypter.DecryptFileAsync(new MemoryStream(), fileCredentials, new MemoryStream()) ); - Assert.Matches(@"^Stream is empty\.\s+Parameter name: encryptedContent$", exception.Message); Assert.IsType(exception); } @@ -272,7 +263,7 @@ public async Task Should_Throw_If_Empty_Data_Stream() public async Task Should_Throw_If_Invalid_Seekable_Data_Stream_Length() { IDecrypter decrypter = new Decrypter(); - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = "" }; Exception exception; using (Stream encStream = new MemoryStream(new byte[16 - 1])) @@ -290,13 +281,12 @@ public async Task Should_Throw_If_Invalid_Seekable_Data_Stream_Length() public async Task Should_Throw_If_Null_Destination() { IDecrypter decrypter = new Decrypter(); - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = "" }; Exception exception = await Assert.ThrowsAnyAsync(() => decrypter.DecryptFileAsync(new MemoryStream(), fileCredentials, null) ); - Assert.Matches(@"^Value cannot be null\.\s+Parameter name: destination$", exception.Message); Assert.IsType(exception); } @@ -304,7 +294,7 @@ public async Task Should_Throw_If_Null_Destination() public async Task Should_Throw_If_NonWritable_Destination() { IDecrypter decrypter = new Decrypter(); - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = "" }; Exception exception; using (Stream destStream = File.OpenRead("Files/s_dec7.driver_license-selfie.jpg")) @@ -314,7 +304,6 @@ public async Task Should_Throw_If_NonWritable_Destination() ); } - Assert.Matches(@"^Stream does not support writing\.\s+Parameter name: destination$", exception.Message); Assert.IsType(exception); } @@ -323,7 +312,7 @@ public async Task Should_Throw_If_NonWritable_Destination() [InlineData("FooBarBazlg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=")] public async Task Should_Throw_If_Invalid_Secret(string secret) { - FileCredentials fileCredentials = new FileCredentials {Secret = secret, FileHash = ""}; + FileCredentials fileCredentials = new FileCredentials { Secret = secret, FileHash = "" }; IDecrypter decrypter = new Decrypter(); await Assert.ThrowsAsync(() => @@ -336,7 +325,7 @@ await Assert.ThrowsAsync(() => [InlineData("FooBarBazlg/H/pEaTJigo4mQJ0s8B+HGCWKTWtOTIdo=")] public async Task Should_Throw_If_Invalid_Hash(string fileHash) { - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = fileHash}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = fileHash }; IDecrypter decrypter = new Decrypter(); await Assert.ThrowsAnyAsync(() => @@ -350,7 +339,7 @@ await Assert.ThrowsAnyAsync(() => [InlineData("Zm9v")] public async Task Should_Throw_If_Invalid_Hash_Length(string fileHash) { - FileCredentials fileCredentials = new FileCredentials {Secret = "", FileHash = fileHash}; + FileCredentials fileCredentials = new FileCredentials { Secret = "", FileHash = fileHash }; IDecrypter decrypter = new Decrypter(); Exception exception = await Assert.ThrowsAnyAsync(() => @@ -384,7 +373,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - public override void Flush() => throw new NotSupportedException(); + public override void Flush() {} public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); diff --git a/test/UnitTests/Decryption/Private Key Import Tests.cs b/test/UnitTests/Decryption/Private Key Import Tests.cs index f88a7ba..11fb27b 100644 --- a/test/UnitTests/Decryption/Private Key Import Tests.cs +++ b/test/UnitTests/Decryption/Private Key Import Tests.cs @@ -2,15 +2,15 @@ // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo -using System; -using System.Reflection; -using System.Security.Cryptography; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; +using System; +using System.Reflection; +using System.Security.Cryptography; using Xunit; using Xunit.Abstractions; @@ -111,7 +111,7 @@ public void Should_Read_RSA_Parameters_From_Pem() RSAParameters parameters; { PemReader pemReader = new PemReader(new System.IO.StringReader(PrivateKeyPem)); - AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair) pemReader.ReadObject(); + AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); parameters = DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters); } @@ -146,7 +146,7 @@ public void Should_JSON_Serialize_RSAParameters_If_Supported() RSAParameters parameters; { PemReader pemReader = new PemReader(new System.IO.StringReader(PrivateKeyPem)); - AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair) pemReader.ReadObject(); + AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); parameters = DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters); } string json = JsonConvert.SerializeObject(parameters); @@ -583,7 +583,7 @@ public void Should_Cast_RSAParameters_To_EncryptionKeyParameters() InverseQ = inverseQ, }; - EncryptionKeyParameters keyParameters = (EncryptionKeyParameters) rsaParameters; + EncryptionKeyParameters keyParameters = (EncryptionKeyParameters)rsaParameters; Assert.Equal(exponent, keyParameters.E); Assert.Equal(mod, keyParameters.M); @@ -687,7 +687,7 @@ public void Should_Cast_EncryptionKeyParameters_To_RSAParameters() IQ = inverseQ, }; - RSAParameters rsaParameters = (RSAParameters) keyParameters; + RSAParameters rsaParameters = (RSAParameters)keyParameters; Assert.Equal(exponent, rsaParameters.Exponent); Assert.Equal(mod, rsaParameters.Modulus); diff --git a/test/UnitTests/Encryption Key.cs b/test/UnitTests/Encryption Key.cs index b41c00b..477e98b 100644 --- a/test/UnitTests/Encryption Key.cs +++ b/test/UnitTests/Encryption Key.cs @@ -1,9 +1,9 @@ -using System.IO; -using System.Security.Cryptography; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; +using System.IO; +using System.Security.Cryptography; namespace UnitTests { @@ -42,7 +42,7 @@ public static RSA RsaPrivateKey -----END RSA PRIVATE KEY----- "; PemReader pemReader = new PemReader(new StringReader(privateKeyPem)); - AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair) pemReader.ReadObject(); + AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters); RSA rsa = RSA.Create(parameters); diff --git a/test/UnitTests/Multiple Scope Requests/Identity Card and Utility Bill Tests.cs b/test/UnitTests/Multiple Scope Requests/Identity Card and Utility Bill Tests.cs index 3805156..0b911b2 100644 --- a/test/UnitTests/Multiple Scope Requests/Identity Card and Utility Bill Tests.cs +++ b/test/UnitTests/Multiple Scope Requests/Identity Card and Utility Bill Tests.cs @@ -2,10 +2,10 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo +using Newtonsoft.Json; using System; using System.Security.Cryptography; using System.Threading.Tasks; -using Newtonsoft.Json; using Telegram.Bot.Passport; using Telegram.Bot.Types.Passport; using Xunit; @@ -37,18 +37,18 @@ public void Should_Decrypt_Credentials() // decryption of document data in 'identity_card' element requires accompanying DataCredentials Assert.NotNull(credentials.SecureData.IdentityCard); - Assert.NotNull(credentials.SecureData.IdentityCard.Data); - Assert.NotEmpty(credentials.SecureData.IdentityCard.Data.Secret); + Assert.NotNull(credentials.SecureData.IdentityCard!.Data); + Assert.NotEmpty(credentials.SecureData.IdentityCard.Data!.Secret); Assert.NotEmpty(credentials.SecureData.IdentityCard.Data.DataHash); // decryption of front side of 'identity_card' element requires accompanying FileCredentials Assert.NotNull(credentials.SecureData.IdentityCard.FrontSide); - Assert.NotEmpty(credentials.SecureData.IdentityCard.FrontSide.Secret); + Assert.NotEmpty(credentials.SecureData.IdentityCard.FrontSide!.Secret); Assert.NotEmpty(credentials.SecureData.IdentityCard.FrontSide.FileHash); // decryption of reverse side of 'identity_card' element requires accompanying FileCredentials Assert.NotNull(credentials.SecureData.IdentityCard.ReverseSide); - Assert.NotEmpty(credentials.SecureData.IdentityCard.ReverseSide.Secret); + Assert.NotEmpty(credentials.SecureData.IdentityCard.ReverseSide!.Secret); Assert.NotEmpty(credentials.SecureData.IdentityCard.ReverseSide.FileHash); // decryption of selfie of 'identity_card' element requires accompanying FileCredentials @@ -60,7 +60,7 @@ public void Should_Decrypt_Credentials() Assert.Null(credentials.SecureData.IdentityCard.Files); // decryption of file scan in 'utility_bill' element requires accompanying FileCredentials - Assert.NotNull(credentials.SecureData.UtilityBill.Files); + Assert.NotNull(credentials.SecureData.UtilityBill!.Files); FileCredentials billFileCredentials = Assert.Single(credentials.SecureData.UtilityBill.Files); Assert.NotEmpty(billFileCredentials.Secret); Assert.NotEmpty(billFileCredentials.FileHash); @@ -73,7 +73,7 @@ public void Should_Decrypt_Credentials() Assert.NotEmpty(billTranslationFileCredentials.FileHash); } - [Fact(DisplayName = "Should decrypt docuemnt data in 'identity_card' element")] + [Fact(DisplayName = "Should decrypt document data in 'identity_card' element")] public void Should_Decrypt_Element_Document() { PassportData passportData = GetPassportData(); @@ -82,7 +82,7 @@ public void Should_Decrypt_Element_Document() Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, EncryptionKey.RsaPrivateKey); - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); IdDocumentData documentData = decrypter.DecryptData( idCardEl.Data, @@ -90,20 +90,20 @@ public void Should_Decrypt_Element_Document() ); Assert.Equal("9999R", documentData.DocumentNo); - Assert.Empty(documentData.ExpiryDate); - Assert.Null(documentData.Expiry); + //Assert.Empty(documentData.ExpiryDate); + Assert.Null(documentData.ExpiryDate); } [Fact(DisplayName = "Should decrypt front side photo in 'identity_card' element")] public async Task Should_Decrypt_Identity_Card_Element_Front_Side() { PassportData passportData = GetPassportData(); - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); Assert.NotNull(idCardEl.FrontSide); Assert.Equal("DgADAQADGwADQnBBRBexahgtkoPgAg", idCardEl.FrontSide.FileId); Assert.InRange(idCardEl.FrontSide.FileDate, new DateTime(2018, 8, 30), new DateTime(2018, 8, 31)); - Assert.Equal(0, idCardEl.FrontSide.FileSize); + Assert.Null(idCardEl.FrontSide.FileSize); IDecrypter decrypter = new Decrypter(); Credentials credentials = @@ -138,12 +138,12 @@ await decrypter.DecryptFileAsync( public async Task Should_Decrypt_Identity_Card_Element_Reverse_Side() { PassportData passportData = GetPassportData(); - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); Assert.NotNull(idCardEl.ReverseSide); Assert.Equal("DgADAQADKAADNfRARK9jbzh5AAFqvAI", idCardEl.ReverseSide.FileId); Assert.InRange(idCardEl.ReverseSide.FileDate, new DateTime(2018, 8, 30), new DateTime(2018, 8, 31)); - Assert.Equal(0, idCardEl.ReverseSide.FileSize); + Assert.Null(idCardEl.ReverseSide.FileSize); IDecrypter decrypter = new Decrypter(); Credentials credentials = @@ -179,12 +179,12 @@ await decrypter.DecryptFileAsync( public async Task Should_decrypt_identity_card_element_selfie() { PassportData passportData = GetPassportData(); - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); Assert.NotNull(idCardEl.Selfie); Assert.Equal("DgADAQADNAADA1BJRCUHz9fqxiqJAg", idCardEl.Selfie.FileId); Assert.InRange(idCardEl.Selfie.FileDate, new DateTime(2018, 8, 30), new DateTime(2018, 8, 31)); - Assert.Equal(0, idCardEl.Selfie.FileSize); + Assert.Null(idCardEl.Selfie.FileSize); IDecrypter decrypter = new Decrypter(); Credentials credentials = @@ -219,14 +219,14 @@ await decrypter.DecryptFileAsync( public async Task Should_Decrypt_Utility_Bill_Element_File() { PassportData passportData = GetPassportData(); - EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == "utility_bill"); + EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.UtilityBill); Assert.NotNull(billElement.Files); PassportFile scanFile = Assert.Single(billElement.Files); Assert.Equal("DgADAQADQAADPupBRDIrCqSwkb4iAg", scanFile.FileId); Assert.InRange(scanFile.FileDate, new DateTime(2018, 8, 30), new DateTime(2018, 8, 31)); - Assert.Equal(0, scanFile.FileSize); + Assert.Null(scanFile.FileSize); IDecrypter decrypter = new Decrypter(); Credentials credentials = @@ -263,14 +263,14 @@ await decrypter.DecryptFileAsync( public async Task Should_Decrypt_Utility_Bill_Element_Translation() { PassportData passportData = GetPassportData(); - EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == "utility_bill"); + EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.UtilityBill); Assert.NotNull(billElement.Translation); PassportFile translationFile = Assert.Single(billElement.Translation); Assert.Equal("DgADAQADOwADGV9BRP4b7RLGAtUKAg", translationFile.FileId); Assert.InRange(translationFile.FileDate, new DateTime(2018, 8, 30), new DateTime(2018, 8, 31)); - Assert.Equal(0, translationFile.FileSize); + Assert.Null(translationFile.FileSize); IDecrypter decrypter = new Decrypter(); Credentials credentials = @@ -312,14 +312,17 @@ static PassportData GetPassportData() => ""data"": ""Xi+gxIkl9rgOvnK6NNT1kg8mf8DaXusx0gkENI/QrUTdQ7qfdT/FhOI8nq/xUiGVuX3QlBWT2kVk0CJ0NFhckQ+tbicHuErxq9+80hjBsaoRp2j6CDxU6gl1B3ZfJ9nVnk/HNMiXGfnz8GVk7XAp2A=="", ""front_side"": { ""file_id"": ""DgADAQADGwADQnBBRBexahgtkoPgAg"", + ""file_unique_id"": ""DgADAQADGwADQnBBRBexahgtkoPgAg"", ""file_date"": 1535639975 }, ""reverse_side"": { ""file_id"": ""DgADAQADKAADNfRARK9jbzh5AAFqvAI"", + ""file_unique_id"": ""DgADAQADGwADQnBBRBexahgtkoPgAg"", ""file_date"": 1535639975 }, ""selfie"": { ""file_id"": ""DgADAQADNAADA1BJRCUHz9fqxiqJAg"", + ""file_unique_id"": ""DgADAQADGwADQnBBRBexahgtkoPgAg"", ""file_date"": 1535639975 }, ""hash"": ""XAIrZ+liSIWhyaYerm4yj14ZJhw93S/IVeeoSUSavI8="" @@ -329,12 +332,14 @@ static PassportData GetPassportData() => ""files"": [ { ""file_id"": ""DgADAQADQAADPupBRDIrCqSwkb4iAg"", + ""file_unique_id"": ""DgADAQADGwADQnBBRBexahgtkoPgAg"", ""file_date"": 1535639861 } ], ""translation"": [ { ""file_id"": ""DgADAQADOwADGV9BRP4b7RLGAtUKAg"", + ""file_unique_id"": ""DgADAQADGwADQnBBRBexahgtkoPgAg"", ""file_date"": 1535639861 } ], @@ -346,7 +351,6 @@ static PassportData GetPassportData() => ""hash"": ""TMUO1tE81hIOCgaqN7buhqG8SIZHjFfJrD93LNKL4Yg="", ""secret"": ""QcOxuwd9OiB/9akjMzyY7wR4NcrpbhpjQuO9yOWhe0u34VVLraTr3gwkBNv0eKEZHoyulhhLr9tkSSO+BYZAp4engued3eL11jqQkosJQBCPg8m1arIvNM+/E5Kw8dnF7dEx9v8t9QA11kSfAqdgnqCtSAq6GGGu5ixuYM1VMbk270qcm3F7wrLN+9YQwUVkiai8WvdA7Q7BnywsbrekKOam95tiFeA7jE8Cf78D6gh47/uirO/KD3Hwl1PNo1f8ORgFf8EixSQuV5Gh8HxEY1uE+yfOxksG5MiWOC5A1lNQuVcZqzVfbReRvs2M2tvX5KeeN+/xsIps+Xp+szWSaw=="" } -} - "); +}"); } } diff --git a/test/UnitTests/Single Scope Requests/Address Decryption Tests.cs b/test/UnitTests/Single Scope Requests/Address Decryption Tests.cs index 5edffea..ae085de 100644 --- a/test/UnitTests/Single Scope Requests/Address Decryption Tests.cs +++ b/test/UnitTests/Single Scope Requests/Address Decryption Tests.cs @@ -33,8 +33,8 @@ public void Should_decrypt_credentials() // 'address' element decryption needs accompanying DataCredentials Assert.NotNull(credentials.SecureData.Address); - Assert.NotNull(credentials.SecureData.Address.Data); - Assert.NotEmpty(credentials.SecureData.Address.Data.Secret); + Assert.NotNull(credentials.SecureData.Address!.Data); + Assert.NotEmpty(credentials.SecureData.Address.Data!.Secret); Assert.NotEmpty(credentials.SecureData.Address.Data.DataHash); } @@ -47,7 +47,7 @@ public void Should_decrypt_address_element() Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, EncryptionKey.RsaPrivateKey); - EncryptedPassportElement addressEl = Assert.Single(passportData.Data, el => el.Type == "address"); + EncryptedPassportElement addressEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.Address); ResidentialAddress residentialAddress = decrypter.DecryptData( encryptedData: addressEl.Data, diff --git a/test/UnitTests/Single Scope Requests/Driver License Decryption Tests.cs b/test/UnitTests/Single Scope Requests/Driver License Decryption Tests.cs index 66b17c5..551cdd7 100644 --- a/test/UnitTests/Single Scope Requests/Driver License Decryption Tests.cs +++ b/test/UnitTests/Single Scope Requests/Driver License Decryption Tests.cs @@ -3,10 +3,10 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo +using Newtonsoft.Json; using System; using System.Linq; using System.Threading.Tasks; -using Newtonsoft.Json; using Telegram.Bot.Passport; using Telegram.Bot.Types.Passport; using Xunit; @@ -69,7 +69,7 @@ public void Should_Decrypt_Document_Data() Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, EncryptionKey.RsaPrivateKey); - EncryptedPassportElement licenseEl = Assert.Single(passportData.Data, el => el.Type == "driver_license"); + EncryptedPassportElement licenseEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.DriverLicence); IdDocumentData licenseDoc = decrypter.DecryptData( encryptedData: licenseEl.Data, @@ -77,9 +77,9 @@ public void Should_Decrypt_Document_Data() ); Assert.Equal("G544-061", licenseDoc.DocumentNo); - Assert.Equal("26.11.2022", licenseDoc.ExpiryDate); - Assert.NotNull(licenseDoc.Expiry); - Assert.InRange(licenseDoc.Expiry.Value, new DateTime(2022, 11, 26), new DateTime(2022, 11, 26, 0, 0, 1)); + //Assert.Equal("26.11.2022", licenseDoc.ExpiryDate); + Assert.NotNull(licenseDoc.ExpiryDate); + Assert.InRange(licenseDoc.ExpiryDate!.Value, new DateTime(2022, 11, 26), new DateTime(2022, 11, 26, 0, 0, 1)); } [Fact(DisplayName = "Should decrypt front side photo file of 'driver_license' element")] @@ -207,23 +207,27 @@ static PassportData GetPassportData() => { ""data"": [ { - ""type"": ""driver_license"", + ""type"": ""driver_licence"", ""data"": ""+ThtmwC1Cq5JPUg8h3E3aSia0qFhuDCGBQap3FzC7MpPp32DtElU1WucEwEHi2zLmCQbSABZ4JbHREYGJhjfJJthRhrdmyvFQDWeU6kQjD2FG2QzPCWB1hNorAXX/X9pLRZiHUrmbqMcU2Mch/X/jVEQcTAXYwkTUdit3ZU+aGzz79tXos4ZCHfnBf5bKww/1n54TPLsVwOa4TAywcaahsJdCDZn2gxCNig2/dY7nfU="", ""front_side"": { ""file_id"": ""DgADAQADQwAD8dA5RLvzieFGLU4nAg"", + ""file_unique_id"": ""DgADAQADQwAD8dA5RLvzieFGLU4nAg"", ""file_date"": 1535597542 }, ""reverse_side"": { ""file_id"": ""DgADAQADHAADkeFARJRE1buibKe-Ag"", + ""file_unique_id"": ""DgADAQADQwAD8dA5RLvzieFGLU4nAg"", ""file_date"": 1535597542 }, ""selfie"": { ""file_id"": ""DgADAQADLgADdYs5RME9OcP0l7GQAg"", + ""file_unique_id"": ""DgADAQADQwAD8dA5RLvzieFGLU4nAg"", ""file_date"": 1535597542 }, ""translation"": [ { ""file_id"": ""DgADAQADMQADnHM4RKOZghHOVNRHAg"", + ""file_unique_id"": ""DgADAQADQwAD8dA5RLvzieFGLU4nAg"", ""file_date"": 1535597788 } ], diff --git a/test/UnitTests/Single Scope Requests/Personal Details Decryption Tests.cs b/test/UnitTests/Single Scope Requests/Personal Details Decryption Tests.cs index 5cd8d58..c954432 100644 --- a/test/UnitTests/Single Scope Requests/Personal Details Decryption Tests.cs +++ b/test/UnitTests/Single Scope Requests/Personal Details Decryption Tests.cs @@ -3,8 +3,8 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo -using System; using Newtonsoft.Json; +using System; using Telegram.Bot; using Telegram.Bot.Passport; using Telegram.Bot.Types.Passport; @@ -49,7 +49,7 @@ public void Should_Decrypt_Personal_Details_Element() Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, EncryptionKey.RsaPrivateKey); - EncryptedPassportElement element = Assert.Single(passportData.Data, el => el.Type == "personal_details"); + EncryptedPassportElement element = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.PersonalDetails); PersonalDetails personalDetails = decrypter.DecryptData( encryptedData: element.Data, @@ -66,8 +66,8 @@ public void Should_Decrypt_Personal_Details_Element() Assert.Equal(PassportEnums.Gender.Male, personalDetails.Gender); Assert.Equal("US", personalDetails.CountryCode); // U.S.A Assert.Equal("IR", personalDetails.ResidenceCountryCode); // Iran - Assert.Equal("30.07.1990", personalDetails.BirthDate); - Assert.InRange(personalDetails.Birthdate, new DateTime(1990, 7, 30), new DateTime(1990, 7, 30, 1, 0, 0)); + //Assert.Equal("30.07.1990", personalDetails.BirthDate); + Assert.InRange(personalDetails.BirthDate, new DateTime(1990, 7, 30), new DateTime(1990, 7, 30, 1, 0, 0)); } static PassportData GetPassportData() => diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index b88955b..c7231a8 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -1,23 +1,24 @@ - + - netcoreapp2.0 - latest + net6.0 + 10 + enable false - - - - - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - - - - + + - + From 21b0a8cb787fcb775d16daaa1baca8ed694320c4 Mon Sep 17 00:00:00 2001 From: karb0f0s <17474471+karb0f0s@users.noreply.github.com> Date: Fri, 29 Apr 2022 16:25:30 +0300 Subject: [PATCH 5/7] Fix QuickStart project --- src/Quickstart/EncryptionKeyParameters.cs | 4 +- src/Quickstart/EncryptionKeyUtility.cs | 12 +- src/Quickstart/Program.cs | 249 ++++++++++++---------- src/Quickstart/Quickstart.csproj | 10 +- 4 files changed, 146 insertions(+), 129 deletions(-) diff --git a/src/Quickstart/EncryptionKeyParameters.cs b/src/Quickstart/EncryptionKeyParameters.cs index 018b9d4..321e8e7 100644 --- a/src/Quickstart/EncryptionKeyParameters.cs +++ b/src/Quickstart/EncryptionKeyParameters.cs @@ -19,7 +19,7 @@ public struct EncryptionKeyParameters public byte[] D; public static explicit operator EncryptionKeyParameters(RSAParameters parameters) => - new EncryptionKeyParameters + new() { E = parameters.Exponent, M = parameters.Modulus, @@ -32,7 +32,7 @@ public static explicit operator EncryptionKeyParameters(RSAParameters parameters }; public static explicit operator RSAParameters(EncryptionKeyParameters parameters) => - new RSAParameters + new() { Exponent = parameters.E, Modulus = parameters.M, diff --git a/src/Quickstart/EncryptionKeyUtility.cs b/src/Quickstart/EncryptionKeyUtility.cs index 1cf9d28..41bfb2f 100644 --- a/src/Quickstart/EncryptionKeyUtility.cs +++ b/src/Quickstart/EncryptionKeyUtility.cs @@ -1,10 +1,10 @@ -using System.IO; -using System.Security.Cryptography; using Newtonsoft.Json; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; +using System.IO; +using System.Security.Cryptography; namespace Quickstart { @@ -20,10 +20,10 @@ public static class EncryptionKeyUtility /// JSON string representing RSA parameters of the public static string SerializeRsaParameters(string key) { - PemReader pemReader = new PemReader(new StringReader(key)); - AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair) pemReader.ReadObject(); + PemReader pemReader = new(new StringReader(key)); + AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters); - return JsonConvert.SerializeObject((EncryptionKeyParameters) parameters); + return JsonConvert.SerializeObject((EncryptionKeyParameters)parameters); } /// @@ -33,6 +33,6 @@ public static string SerializeRsaParameters(string key) /// JSON-serialized RSA key parameters /// Created RSA instance representing the encryption key public static RSA GetRsaKeyFromJson(string json) => - RSA.Create((RSAParameters) JsonConvert.DeserializeObject(json)); + RSA.Create((RSAParameters)JsonConvert.DeserializeObject(json)); } } diff --git a/src/Quickstart/Program.cs b/src/Quickstart/Program.cs index 8f2be5e..d77c379 100644 --- a/src/Quickstart/Program.cs +++ b/src/Quickstart/Program.cs @@ -2,135 +2,49 @@ * https://telegrambots.github.io/book/4/passport/quickstart.html */ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Security; using System; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.OpenSsl; -using Org.BouncyCastle.Security; using Telegram.Bot; -using Telegram.Bot.Args; +using Telegram.Bot.Exceptions; using Telegram.Bot.Passport; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Polling; +using Telegram.Bot.Requests; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Passport; using Telegram.Bot.Types.ReplyMarkups; -namespace Quickstart -{ - class Program - { - static ITelegramBotClient _botClient; - - static void Main() - { - _botClient = new TelegramBotClient("YOUR_ACCESS_TOKEN_HERE"); - - User me = _botClient.GetMeAsync().Result; - Console.WriteLine( - $"Hello, World! I am user {me.Id} and my name is {me.FirstName}." - ); - - _botClient.OnMessage += Bot_OnMessage; - _botClient.StartReceiving(); - Thread.Sleep(int.MaxValue); - } - - static async void Bot_OnMessage(object sender, MessageEventArgs e) - { - if (e.Message.Text != null) - { - await SendAuthorizationRequestAsync(e.Message.From.Id); - } - else if (e.Message.PassportData != null) - { - await DecryptPassportDataAsync(e.Message); - } - } - - static async Task SendAuthorizationRequestAsync(int userId) - { - PassportScope scope = new PassportScope(new[] - { - new PassportScopeElementOne(PassportEnums.Scope.Address), - new PassportScopeElementOne(PassportEnums.Scope.PhoneNumber), - }); - AuthorizationRequestParameters authReq = new AuthorizationRequestParameters( - botId: _botClient.BotId, - publicKey: PublicKey, - nonce: "Test nonce for this demo", - scope: scope - ); - - await _botClient.SendTextMessageAsync( - userId, - "Share your *residential address* and *phone number* with bot using Telegram Passport.\n\n" + - "1. Click inline button\n" + - "2. Open link in browser so it redirects you to Telegram Passport\n" + - "3. Authorize bot to access the info", - ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( - "Share via Passport", - $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" - ) - ); - } - - static async Task DecryptPassportDataAsync(Message message) - { - IDecrypter decrypter = new Decrypter(); - - // Step 1: Decrypt credentials - Credentials credentials = decrypter.DecryptCredentials( - message.PassportData.Credentials, - GetRsaPrivateKey() - ); - - // Step 2: Validate nonce - if (credentials.Nonce != "Test nonce for this demo") - { - throw new Exception($"Invalid nonce: \"{credentials.Nonce}\"."); - } - - // Step 3: Decrypt residential address using credentials - EncryptedPassportElement addressElement = message.PassportData.Data.Single( - el => el.Type == PassportEnums.Scope.Address - ); - ResidentialAddress address = decrypter.DecryptData( - encryptedData: addressElement.Data, - dataCredentials: credentials.SecureData.Address.Data - ); - - // Step 4: Get phone number - string phoneNumber = message.PassportData.Data.Single( - el => el.Type == PassportEnums.Scope.PhoneNumber - ).PhoneNumber; - - await _botClient.SendTextMessageAsync( - message.From.Id, - "Your 🏠 address is:\n" + - $"{address.StreetLine1}\n" + - $"{address.City}, {address.CountryCode}\n" + - $"{address.PostCode}\n\n" + - $"📱 {phoneNumber}" - ); - } - - static RSA GetRsaPrivateKey() - { - PemReader pemReader = new PemReader(new StringReader(PrivateKey)); - AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); - RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters); - RSA rsa = RSA.Create(parameters); - return rsa; - } - - const string PublicKey = @"-----BEGIN PUBLIC KEY----- +ITelegramBotClient botClient = new TelegramBotClient("{BOT_TOKEN}"); + +User me = await botClient.GetMeAsync(); +Console.Title = me.Username ?? "My awesome Bot"; + +using var cts = new CancellationTokenSource(); + +// StartReceiving does not block the caller thread. Receiving is done on the ThreadPool. +botClient.StartReceiving(updateHandler: HandleUpdateAsync, + errorHandler: HandleErrorAsync, + receiverOptions: new ReceiverOptions() + { + AllowedUpdates = Array.Empty() + }, + cancellationToken: cts.Token); + +Console.WriteLine($"Start listening for @{me.Username}"); +Console.ReadLine(); + +// Send cancellation request to stop bot +cts.Cancel(); + +const string PublicKey = @"-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0VElWoQA2SK1csG2/sY/ wlssO1bjXRx+t+JlIgS6jLPCefyCAcZBv7ElcSPJQIPEXNwN2XdnTc2wEIjZ8bTg BlBqXppj471bJeX8Mi2uAxAqOUDuvGuqth+mq7DMqol3MNH5P9FO6li7nZxI1FX3 @@ -141,7 +55,7 @@ static RSA GetRsaPrivateKey() -----END PUBLIC KEY----- "; - const string PrivateKey = @"-----BEGIN RSA PRIVATE KEY----- +const string PrivateKey = @"-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA0VElWoQA2SK1csG2/sY/wlssO1bjXRx+t+JlIgS6jLPCefyC AcZBv7ElcSPJQIPEXNwN2XdnTc2wEIjZ8bTgBlBqXppj471bJeX8Mi2uAxAqOUDu vGuqth+mq7DMqol3MNH5P9FO6li7nZxI1FX39u2r/4H4PXRiWx13gsVQRL6Clq2j @@ -169,5 +83,106 @@ static RSA GetRsaPrivateKey() gaZk6+H62W5zGnIbtzodB2n7JasK561Ic/QcrEtheC4Qmr+RXe03pg== -----END RSA PRIVATE KEY----- "; + +static Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) +{ + var ErrorMessage = exception switch + { + ApiRequestException apiRequestException => $"Telegram API Error:\n[{apiRequestException.ErrorCode}]\n{apiRequestException.Message}", + _ => exception.ToString() + }; + + Console.WriteLine(ErrorMessage); + return Task.CompletedTask; +} + +static async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) +{ + if (update.Message.Text is not null) + { + await SendAuthorizationRequestAsync(botClient, update.Message.From.Id); } + else if (update.Message.PassportData is not null) + { + await DecryptPassportDataAsync(botClient, update.Message); + } +} + +static async Task SendAuthorizationRequestAsync( + ITelegramBotClient botClient, + long userId) +{ + PassportScope scope = new(new[] + { + new PassportScopeElementOne(PassportEnums.Scope.Address), + new PassportScopeElementOne(PassportEnums.Scope.PhoneNumber), + }); + AuthorizationRequestParameters authReq = new( + botId: botClient.BotId ?? throw new ArgumentNullException(nameof(botClient.BotId)), + publicKey: PublicKey, + nonce: "Test nonce for this demo", + scope: scope + ); + + await botClient.SendTextMessageAsync( + userId, + "Share your *residential address* and *phone number* with bot using Telegram Passport.\n\n" + + "1. Click inline button\n" + + "2. Open link in browser so it redirects you to Telegram Passport\n" + + "3. Authorize bot to access the info", + ParseMode.Markdown, + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( + "Share via Passport", + $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" + ) + ); +} + +static async Task DecryptPassportDataAsync(ITelegramBotClient botClient, Message message) +{ + IDecrypter decrypter = new Decrypter(); + + // Step 1: Decrypt credentials + Credentials credentials = decrypter.DecryptCredentials( + message.PassportData.Credentials, + GetRsaPrivateKey() + ); + + // Step 2: Validate nonce + if (credentials.Nonce != "Test nonce for this demo") + { + throw new Exception($"Invalid nonce: \"{credentials.Nonce}\"."); + } + + // Step 3: Decrypt residential address using credentials + EncryptedPassportElement addressElement = message.PassportData.Data.Single( + el => el.Type == EncryptedPassportElementType.Address + ); + ResidentialAddress address = decrypter.DecryptData( + encryptedData: addressElement.Data, + dataCredentials: credentials.SecureData.Address.Data + ); + + // Step 4: Get phone number + string phoneNumber = message.PassportData.Data.Single( + el => el.Type == EncryptedPassportElementType.PhoneNumber + ).PhoneNumber; + + await botClient.SendTextMessageAsync( + message.From.Id, + "Your 🏠 address is:\n" + + $"{address.StreetLine1}\n" + + $"{address.City}, {address.CountryCode}\n" + + $"{address.PostCode}\n\n" + + $"📱 {phoneNumber}" + ); +} + +static RSA GetRsaPrivateKey() +{ + PemReader pemReader = new(new StringReader(PrivateKey)); + AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); + RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters); + RSA rsa = RSA.Create(parameters); + return rsa; } diff --git a/src/Quickstart/Quickstart.csproj b/src/Quickstart/Quickstart.csproj index 0bfa06d..583de80 100644 --- a/src/Quickstart/Quickstart.csproj +++ b/src/Quickstart/Quickstart.csproj @@ -1,10 +1,12 @@ - + Exe - netcoreapp2.1 + net6.0 - - + + + + From 4a03b82a14501854687fedcac1d50506321f5609 Mon Sep 17 00:00:00 2001 From: karb0f0s <17474471+karb0f0s@users.noreply.github.com> Date: Fri, 29 Apr 2022 16:25:50 +0300 Subject: [PATCH 6/7] Update solution file --- .editorconfig | 1 + Telegram.Bot.Extensions.Passport.sln | 62 ++++++++++++++++------------ test/.editorconfig | 7 ++++ 3 files changed, 43 insertions(+), 27 deletions(-) create mode 100644 test/.editorconfig diff --git a/.editorconfig b/.editorconfig index b69f6a0..778cc97 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,6 +20,7 @@ csharp_align_multiline_extends_list = true csharp_align_linq_query = true csharp_place_attribute_on_same_line = false csharp_empty_block_style = together +max_line_length = 120 # Solution Files [*.sln] diff --git a/Telegram.Bot.Extensions.Passport.sln b/Telegram.Bot.Extensions.Passport.sln index 8db0448..8afb8a8 100644 --- a/Telegram.Bot.Extensions.Passport.sln +++ b/Telegram.Bot.Extensions.Passport.sln @@ -1,19 +1,24 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32422.2 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{219FA372-F501-4189-A114-9F4BC224374A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Telegram.Bot.Extensions.Passport", "src\Telegram.Bot.Extensions.Passport\Telegram.Bot.Extensions.Passport.csproj", "{D6753021-F292-4465-9E8B-7D0CE7A4D504}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Telegram.Bot.Extensions.Passport", "src\Telegram.Bot.Extensions.Passport\Telegram.Bot.Extensions.Passport.csproj", "{D6753021-F292-4465-9E8B-7D0CE7A4D504}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quickstart", "src\Quickstart\Quickstart.csproj", "{1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quickstart", "src\Quickstart\Quickstart.csproj", "{1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8804BAE3-C00C-4618-91B3-65A38CEF34F0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "test\IntegrationTests\IntegrationTests.csproj", "{8B4D1075-75E4-49D1-8AB7-19EC01DF3F17}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "test\IntegrationTests\IntegrationTests.csproj", "{8B4D1075-75E4-49D1-8AB7-19EC01DF3F17}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "test\UnitTests\UnitTests.csproj", "{8CF594BF-BA8C-43C2-B7C4-69F7155A48F0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "test\UnitTests\UnitTests.csproj", "{8CF594BF-BA8C-43C2-B7C4-69F7155A48F0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{90B24D96-E3FB-44DB-AAE4-14BE3C1BB0C2}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -24,15 +29,6 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {D6753021-F292-4465-9E8B-7D0CE7A4D504} = {219FA372-F501-4189-A114-9F4BC224374A} - {8B4D1075-75E4-49D1-8AB7-19EC01DF3F17} = {8804BAE3-C00C-4618-91B3-65A38CEF34F0} - {8CF594BF-BA8C-43C2-B7C4-69F7155A48F0} = {8804BAE3-C00C-4618-91B3-65A38CEF34F0} - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8} = {219FA372-F501-4189-A114-9F4BC224374A} - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D6753021-F292-4465-9E8B-7D0CE7A4D504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D6753021-F292-4465-9E8B-7D0CE7A4D504}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -46,6 +42,18 @@ Global {D6753021-F292-4465-9E8B-7D0CE7A4D504}.Release|x64.Build.0 = Release|Any CPU {D6753021-F292-4465-9E8B-7D0CE7A4D504}.Release|x86.ActiveCfg = Release|Any CPU {D6753021-F292-4465-9E8B-7D0CE7A4D504}.Release|x86.Build.0 = Release|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|x64.ActiveCfg = Debug|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|x64.Build.0 = Debug|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|x86.ActiveCfg = Debug|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|x86.Build.0 = Debug|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|Any CPU.Build.0 = Release|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|x64.ActiveCfg = Release|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|x64.Build.0 = Release|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|x86.ActiveCfg = Release|Any CPU + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|x86.Build.0 = Release|Any CPU {8B4D1075-75E4-49D1-8AB7-19EC01DF3F17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8B4D1075-75E4-49D1-8AB7-19EC01DF3F17}.Debug|Any CPU.Build.0 = Debug|Any CPU {8B4D1075-75E4-49D1-8AB7-19EC01DF3F17}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -70,17 +78,17 @@ Global {8CF594BF-BA8C-43C2-B7C4-69F7155A48F0}.Release|x64.Build.0 = Release|Any CPU {8CF594BF-BA8C-43C2-B7C4-69F7155A48F0}.Release|x86.ActiveCfg = Release|Any CPU {8CF594BF-BA8C-43C2-B7C4-69F7155A48F0}.Release|x86.Build.0 = Release|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|x64.ActiveCfg = Debug|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|x64.Build.0 = Debug|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|x86.ActiveCfg = Debug|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Debug|x86.Build.0 = Debug|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|Any CPU.Build.0 = Release|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|x64.ActiveCfg = Release|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|x64.Build.0 = Release|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|x86.ActiveCfg = Release|Any CPU - {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D6753021-F292-4465-9E8B-7D0CE7A4D504} = {219FA372-F501-4189-A114-9F4BC224374A} + {1DFDD1AD-8543-46CC-B54A-1E2E2D2B62C8} = {219FA372-F501-4189-A114-9F4BC224374A} + {8B4D1075-75E4-49D1-8AB7-19EC01DF3F17} = {8804BAE3-C00C-4618-91B3-65A38CEF34F0} + {8CF594BF-BA8C-43C2-B7C4-69F7155A48F0} = {8804BAE3-C00C-4618-91B3-65A38CEF34F0} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BEA217AA-95F7-41B7-A7A4-835147B5B53E} EndGlobalSection EndGlobal diff --git a/test/.editorconfig b/test/.editorconfig new file mode 100644 index 0000000..a6173fc --- /dev/null +++ b/test/.editorconfig @@ -0,0 +1,7 @@ +# http://EditorConfig.org + +# CSharp code style settings: +[*.cs] +csharp_style_var_for_built_in_types = false:error +csharp_style_var_when_type_is_apparent = false:error +csharp_style_var_elsewhere = false:error From de8df4df99d8b3b9caa0cc7d8500737b328a71c4 Mon Sep 17 00:00:00 2001 From: karb0f0s <17474471+karb0f0s@users.noreply.github.com> Date: Fri, 29 Apr 2022 16:26:12 +0300 Subject: [PATCH 7/7] Fix Integration tests --- .../Framework/ConfigurationProvider.cs | 2 +- .../Framework/EncryptionKey.cs | 6 +- .../Framework/TestCollectionOrderer.cs | 2 +- .../Framework/TestsFixture.cs | 70 +++++++++++++------ .../Framework/UpdateReceiver.cs | 8 +-- .../XunitExtensions/RetryTestCase.cs | 2 +- ...itTestAssemblyRunnerWithAssemblyFixture.cs | 4 +- test/IntegrationTests/IntegrationTests.csproj | 32 +++++---- .../Identity Card and Utility Bill Tests.cs | 52 +++++++------- .../Phone and Email Tests.cs | 16 ++--- .../Passport Document Error Tests.cs | 9 +-- .../Passport Registration Error Tests.cs | 9 +-- .../Unspecified Error Tests.cs | 12 ++-- .../Driver License Tests.cs | 39 +++++------ .../Personal Details Tests.cs | 15 ++-- .../Residential Address Tests.cs | 11 ++- 16 files changed, 155 insertions(+), 134 deletions(-) diff --git a/test/IntegrationTests/Framework/ConfigurationProvider.cs b/test/IntegrationTests/Framework/ConfigurationProvider.cs index 69150e8..06ca308 100644 --- a/test/IntegrationTests/Framework/ConfigurationProvider.cs +++ b/test/IntegrationTests/Framework/ConfigurationProvider.cs @@ -1,6 +1,6 @@ +using Microsoft.Extensions.Configuration; using System; using System.IO; -using Microsoft.Extensions.Configuration; namespace IntegrationTests.Framework { diff --git a/test/IntegrationTests/Framework/EncryptionKey.cs b/test/IntegrationTests/Framework/EncryptionKey.cs index ab2bc54..a66ee67 100644 --- a/test/IntegrationTests/Framework/EncryptionKey.cs +++ b/test/IntegrationTests/Framework/EncryptionKey.cs @@ -1,9 +1,9 @@ -using System.IO; -using System.Security.Cryptography; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; +using System.IO; +using System.Security.Cryptography; // ReSharper disable once CheckNamespace namespace IntegrationTests @@ -14,7 +14,7 @@ public static RSA ReadAsRsa() { string privateKeyPem = File.ReadAllText("Files/private.pem"); PemReader pemReader = new PemReader(new StringReader(privateKeyPem)); - AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair) pemReader.ReadObject(); + AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters); RSA rsa = RSA.Create(parameters); return rsa; diff --git a/test/IntegrationTests/Framework/TestCollectionOrderer.cs b/test/IntegrationTests/Framework/TestCollectionOrderer.cs index 61ebdf0..a0a4f23 100644 --- a/test/IntegrationTests/Framework/TestCollectionOrderer.cs +++ b/test/IntegrationTests/Framework/TestCollectionOrderer.cs @@ -53,7 +53,7 @@ private int FindExecutionOrder(ITestCollection collection) $"Collection \"{collection.DisplayName}\" not found in execution list.", nameof(collection)); } - return (int) order; + return (int)order; } } } diff --git a/test/IntegrationTests/Framework/TestsFixture.cs b/test/IntegrationTests/Framework/TestsFixture.cs index 20d471a..4436b6f 100644 --- a/test/IntegrationTests/Framework/TestsFixture.cs +++ b/test/IntegrationTests/Framework/TestsFixture.cs @@ -67,10 +67,10 @@ public Task SendTestInstructionsAsync( ) { string text = string.Format(Constants.InstructionsMessageFormat, instructions); - chatid = chatid ?? SupergroupChat.Id; + chatid ??= SupergroupChat.Id; IReplyMarkup replyMarkup = startInlineQuery - ? (InlineKeyboardMarkup) InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("Start inline query") + ? (InlineKeyboardMarkup)InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("Start inline query") : default; return BotClient.SendTextMessageAsync( @@ -123,10 +123,9 @@ bool IsMatch(Update u) => public async Task GetChatFromAdminAsync() { - bool IsMatch(Update u) => ( + static bool IsMatch(Update u) => u.Message.Type == MessageType.Contact || - u.Message.ForwardFrom?.Id != null - ); + u.Message.ForwardFrom?.Id != null; var update = await UpdateReceiver .GetUpdatesAsync(IsMatch, updateTypes: UpdateType.Message) @@ -134,7 +133,7 @@ bool IsMatch(Update u) => ( await UpdateReceiver.DiscardNewUpdatesAsync(); - int userId = update.Message.Type == MessageType.Contact + long? userId = update.Message.Type == MessageType.Contact ? update.Message.Contact.UserId : update.Message.ForwardFrom.Id; @@ -146,7 +145,7 @@ private async Task InitAsync() string apiToken = ConfigurationProvider.TestConfigurations.ApiToken; BotClient = new TelegramBotClient(apiToken); BotUser = await BotClient.GetMeAsync(CancellationToken); - await BotClient.DeleteWebhookAsync(CancellationToken); + await BotClient.DeleteWebhookAsync(cancellationToken: CancellationToken); SupergroupChat = await FindSupergroupTestChatAsync(); var allowedUserNames = await FindAllowedTesterUserNames(); @@ -161,8 +160,8 @@ await BotClient.SendTextMessageAsync( ); #if DEBUG - BotClient.MakingApiRequest += OnMakingApiRequest; - BotClient.ApiResponseReceived += OnApiResponseReceived; + BotClient.OnMakingApiRequest += OnMakingApiRequest; + BotClient.OnApiResponseReceived += OnApiResponseReceived; #endif } @@ -180,14 +179,14 @@ private Task SendNotificationToChatAsync( string text = string.Format(textFormat, name); - chatid = chatid ?? SupergroupChat.Id; + chatid ??= SupergroupChat.Id; if (instructions != default) { text += "\n\n" + string.Format(Constants.InstructionsMessageFormat, instructions); } IReplyMarkup replyMarkup = switchInlineQuery - ? (InlineKeyboardMarkup) InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("Start inline query") + ? (InlineKeyboardMarkup)InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("Start inline query") : default; var task = BotClient.SendTextMessageAsync(chatid, text, ParseMode.Markdown, @@ -222,7 +221,7 @@ private async Task> FindAllowedTesterUserNames() .Select(n => n.Trim()) .ToArray(); - if (!allowedUserNames.Any()) + if (allowedUserNames.Length == 0) { // Assume all chat admins are allowed testers ChatMember[] admins = await BotClient.GetChatAdministratorsAsync(SupergroupChat, CancellationToken); @@ -236,28 +235,53 @@ private async Task> FindAllowedTesterUserNames() } #if DEBUG - private void OnMakingApiRequest(object sender, ApiRequestEventArgs e) + async ValueTask OnMakingApiRequest( + ITelegramBotClient botClient, + ApiRequestEventArgs e, + CancellationToken cancellationToken = default) { + bool hasContent; string content; string[] multipartContent; - if (e.HttpContent is MultipartFormDataContent multipartFormDataContent) + if (e.HttpRequestMessage.Content is null) { - multipartContent = multipartFormDataContent - .Select(c => c is StringContent - ? $"{c.Headers}\n{c.ReadAsStringAsync().Result}" - : c.Headers.ToString() - ) - .ToArray(); + hasContent = false; + } + else if (e.HttpRequestMessage.Content is MultipartFormDataContent multipartFormDataContent) + { + hasContent = true; + var stringifiedFormContent = new List(multipartFormDataContent.Count()); + + foreach (var formContent in multipartFormDataContent) + { + if (formContent is StringContent stringContent) + { + var stringifiedContent = await stringContent.ReadAsStringAsync(cancellationToken); + stringifiedFormContent.Add(stringifiedContent); + } + else + { + stringifiedFormContent.Add(formContent.Headers.ToString()); + } + } + + multipartContent = stringifiedFormContent.ToArray(); } else { - content = e.HttpContent.ReadAsStringAsync().Result; + hasContent = true; + content = await e.HttpRequestMessage.Content.ReadAsStringAsync(cancellationToken); } + + /* Debugging Hint: set breakpoints with conditions here in order to investigate the HTTP request values. */ } - private async void OnApiResponseReceived(object sender, ApiResponseEventArgs e) + async ValueTask OnApiResponseReceived( + ITelegramBotClient botClient, + ApiResponseEventArgs e, + CancellationToken cancellationToken = default) { - string content = await e.ResponseMessage.Content.ReadAsStringAsync() + string content = await e.ResponseMessage.Content.ReadAsStringAsync(cancellationToken) .ConfigureAwait(false); } #endif diff --git a/test/IntegrationTests/Framework/UpdateReceiver.cs b/test/IntegrationTests/Framework/UpdateReceiver.cs index a887981..3b46e69 100644 --- a/test/IntegrationTests/Framework/UpdateReceiver.cs +++ b/test/IntegrationTests/Framework/UpdateReceiver.cs @@ -87,13 +87,13 @@ params UpdateType[] updateTypes public async Task GetCallbackQueryUpdateAsync( int messageId = default, - string data = default, + string? data = default, bool discardNewUpdates = true, CancellationToken cancellationToken = default) { bool IsMatch(Update u) => - (messageId == default || u.CallbackQuery.Message.MessageId == messageId) && - (data == default || u.CallbackQuery.Data == data); + (messageId == default || u.CallbackQuery?.Message?.MessageId == messageId) && + (data == default || u.CallbackQuery?.Data == data); var updates = await GetUpdatesAsync(IsMatch, cancellationToken: cancellationToken, @@ -141,7 +141,7 @@ public async Task GetInlineQueryUpdateAsync(bool discardNewUpdates = tru var updates = await GetUpdatesAsync( u => u.Message?.Type == messageType || u.ChosenInlineResult != null, cancellationToken: cancellationToken, - updateTypes: new[] {UpdateType.Message, UpdateType.ChosenInlineResult} + updateTypes: new[] { UpdateType.Message, UpdateType.ChosenInlineResult } ); messageUpdate = updates.SingleOrDefault(u => u.Message?.Type == messageType); diff --git a/test/IntegrationTests/Framework/XunitExtensions/RetryTestCase.cs b/test/IntegrationTests/Framework/XunitExtensions/RetryTestCase.cs index 2203f40..394b457 100644 --- a/test/IntegrationTests/Framework/XunitExtensions/RetryTestCase.cs +++ b/test/IntegrationTests/Framework/XunitExtensions/RetryTestCase.cs @@ -1,10 +1,10 @@ +using Polly; using System; using System.ComponentModel; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Polly; using Telegram.Bot.Exceptions; using Xunit.Abstractions; using Xunit.Sdk; diff --git a/test/IntegrationTests/Framework/XunitExtensions/XunitTestAssemblyRunnerWithAssemblyFixture.cs b/test/IntegrationTests/Framework/XunitExtensions/XunitTestAssemblyRunnerWithAssemblyFixture.cs index 30e746a..a511f88 100644 --- a/test/IntegrationTests/Framework/XunitExtensions/XunitTestAssemblyRunnerWithAssemblyFixture.cs +++ b/test/IntegrationTests/Framework/XunitExtensions/XunitTestAssemblyRunnerWithAssemblyFixture.cs @@ -28,7 +28,7 @@ protected override async Task AfterTestAssemblyStartingAsync() // Go find all the AssemblyFixtureAttributes adorned on the test assembly Aggregator.Run(() => { - var fixturesAttrs = ((IReflectionAssemblyInfo) TestAssembly.Assembly).Assembly + var fixturesAttrs = ((IReflectionAssemblyInfo)TestAssembly.Assembly).Assembly .GetCustomAttributes(typeof(AssemblyFixtureAttribute)) .Cast() .ToList(); @@ -76,7 +76,7 @@ protected override Task RunTestCollectionAsync(IMessageBus messageBu .RunAsync() .ContinueWith(t => { - var fixture = (TestsFixture) _assemblyFixtureMappings.Single().Value; + var fixture = (TestsFixture)_assemblyFixtureMappings.Single().Value; fixture.RunSummary.Aggregate(t.Result); return t.Result; }); diff --git a/test/IntegrationTests/IntegrationTests.csproj b/test/IntegrationTests/IntegrationTests.csproj index 0027da0..fb76a80 100644 --- a/test/IntegrationTests/IntegrationTests.csproj +++ b/test/IntegrationTests/IntegrationTests.csproj @@ -1,22 +1,24 @@ - + - netcoreapp2.0 - latest + net6.0 + 10 + enable false - - - - - - - - - - - - + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/test/IntegrationTests/Multiple Scope Requests/Identity Card and Utility Bill Tests.cs b/test/IntegrationTests/Multiple Scope Requests/Identity Card and Utility Bill Tests.cs index fe00d8c..fe330b9 100644 --- a/test/IntegrationTests/Multiple Scope Requests/Identity Card and Utility Bill Tests.cs +++ b/test/IntegrationTests/Multiple Scope Requests/Identity Card and Utility Bill Tests.cs @@ -1,16 +1,12 @@ -// ReSharper disable PossibleNullReferenceException -// ReSharper disable CheckNamespace -// ReSharper disable StringLiteralTypo - +using IntegrationTests.Framework; +using IntegrationTests.Framework.Fixtures; using System; using System.Net.Http; using System.Security.Cryptography; using System.Threading.Tasks; -using IntegrationTests.Framework; -using IntegrationTests.Framework.Fixtures; using Telegram.Bot; using Telegram.Bot.Passport; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Passport; @@ -88,7 +84,7 @@ await BotClient.SendTextMessageAsync( "2. Open link in browser to redirect you back to Telegram passport\n" + "3. Authorize bot to access the info", ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Share via Passport", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) @@ -106,9 +102,9 @@ public void Should_Validate_Passport_Message() #region identity card element validation - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); Assert.NotNull(idCardEl); - Assert.Equal(PassportEnums.Scope.IdentityCard, idCardEl.Type); + Assert.Equal(EncryptedPassportElementType.IdentityCard, idCardEl.Type); Assert.NotEmpty(idCardEl.Data); Assert.NotEmpty(idCardEl.Hash); @@ -129,9 +125,9 @@ public void Should_Validate_Passport_Message() #region utility bill element validation - EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == "utility_bill"); + EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.UtilityBill); Assert.NotNull(billElement); - Assert.Equal(PassportEnums.Scope.UtilityBill, billElement.Type); + Assert.Equal(EncryptedPassportElementType.UtilityBill, billElement.Type); Assert.NotEmpty(billElement.Hash); Assert.Null(billElement.Data); @@ -221,7 +217,7 @@ public void Should_Decrypt_Identity_Card_Element_Document() IDecrypter decrypter = new Decrypter(); Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, key); - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); IdDocumentData documentData = decrypter.DecryptData( idCardEl.Data, @@ -229,13 +225,13 @@ public void Should_Decrypt_Identity_Card_Element_Document() ); Assert.NotEmpty(documentData.DocumentNo); - if (string.IsNullOrEmpty(documentData.ExpiryDate)) + if (documentData.ExpiryDate is null) { - Assert.Null(documentData.Expiry); + Assert.Null(documentData.ExpiryDate); } else { - Assert.NotNull(documentData.Expiry); + Assert.NotNull(documentData.ExpiryDate); } } @@ -245,13 +241,13 @@ public async Task Should_Decrypt_Identity_Card_Element_Front_Side() Update update = _classFixture.Entity; PassportData passportData = update.Message.PassportData; RSA key = EncryptionKey.ReadAsRsa(); - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); IDecrypter decrypter = new Decrypter(); Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, key); byte[] encryptedContent; - using (System.IO.MemoryStream stream = new System.IO.MemoryStream(idCardEl.FrontSide.FileSize)) + using (System.IO.MemoryStream stream = new System.IO.MemoryStream(idCardEl.FrontSide.FileSize ?? 0)) { await BotClient.GetInfoAndDownloadFileAsync( idCardEl.FrontSide.FileId, @@ -275,7 +271,7 @@ public async Task Should_Decrypt_Identity_Card_Element_Reverse_Side() Update update = _classFixture.Entity; PassportData passportData = update.Message.PassportData; RSA key = EncryptionKey.ReadAsRsa(); - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); IDecrypter decrypter = new Decrypter(); Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, key); @@ -298,7 +294,7 @@ await decrypter.DecryptFileAsync( reverseSideFile ); - Assert.InRange(reverseSideFile.Length, encFileInfo.FileSize - 256, encFileInfo.FileSize + 256); + Assert.InRange(reverseSideFile.Length, encFileInfo.FileSize ?? 0 - 256, encFileInfo.FileSize ?? 0 + 256); } _output.WriteLine("Reverse side photo is written to file \"{0}\".", destFilePath); @@ -310,13 +306,13 @@ public async Task Should_Decrypt_Identity_Card_Element_Selfie() Update update = _classFixture.Entity; PassportData passportData = update.Message.PassportData; RSA key = EncryptionKey.ReadAsRsa(); - EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == "identity_card"); + EncryptedPassportElement idCardEl = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.IdentityCard); IDecrypter decrypter = new Decrypter(); Credentials credentials = decrypter.DecryptCredentials(passportData.Credentials, key); byte[] encryptedContent; - using (System.IO.MemoryStream stream = new System.IO.MemoryStream(idCardEl.Selfie.FileSize)) + using (System.IO.MemoryStream stream = new System.IO.MemoryStream(idCardEl.Selfie.FileSize ?? 0)) { await BotClient.GetInfoAndDownloadFileAsync( idCardEl.Selfie.FileId, @@ -339,7 +335,7 @@ public async Task Should_Decrypt_Utility_Bill_Element_File() Update update = _classFixture.Entity; PassportData passportData = update.Message.PassportData; RSA key = EncryptionKey.ReadAsRsa(); - EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == "utility_bill"); + EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.UtilityBill); PassportFile billScanFile = Assert.Single(billElement.Files); @@ -356,12 +352,12 @@ public async Task Should_Decrypt_Utility_Bill_Element_File() fileCredentials, decryptedFile ); - Assert.InRange(decryptedFile.Length, billScanFile.FileSize - 256, billScanFile.FileSize + 256); + Assert.InRange(decryptedFile.Length, billScanFile.FileSize ?? 0 - 256, billScanFile.FileSize ?? 0 + 256); } Assert.NotEmpty(encryptedFileInfo.FilePath); Assert.NotEmpty(encryptedFileInfo.FileId); - Assert.InRange(encryptedFileInfo.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedFileInfo.FileSize ?? 0, 1_000, 50_000_000); } [OrderedFact("Should decrypt the single translation file in 'utility_bill' element")] @@ -370,7 +366,7 @@ public async Task Should_decrypt_utility_bill_element_translation() Update update = _classFixture.Entity; PassportData passportData = update.Message.PassportData; RSA key = EncryptionKey.ReadAsRsa(); - EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == "utility_bill"); + EncryptedPassportElement billElement = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.UtilityBill); PassportFile translationFile = Assert.Single(billElement.Translation); @@ -387,12 +383,12 @@ public async Task Should_decrypt_utility_bill_element_translation() fileCredentials, decryptedFile ); - Assert.InRange(decryptedFile.Length, translationFile.FileSize - 256, translationFile.FileSize + 256); + Assert.InRange(decryptedFile.Length, translationFile.FileSize ?? 0 - 256, translationFile.FileSize ?? 0 + 256); } Assert.NotEmpty(encryptedFileInfo.FilePath); Assert.NotEmpty(encryptedFileInfo.FileId); - Assert.InRange(encryptedFileInfo.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedFileInfo.FileSize ?? 0, 1_000, 50_000_000); } } } diff --git a/test/IntegrationTests/Multiple Scope Requests/Phone and Email Tests.cs b/test/IntegrationTests/Multiple Scope Requests/Phone and Email Tests.cs index 3a5e280..0de8af4 100644 --- a/test/IntegrationTests/Multiple Scope Requests/Phone and Email Tests.cs +++ b/test/IntegrationTests/Multiple Scope Requests/Phone and Email Tests.cs @@ -2,13 +2,13 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo -using System.Security.Cryptography; -using System.Threading.Tasks; using IntegrationTests.Framework; using IntegrationTests.Framework.Fixtures; +using System.Security.Cryptography; +using System.Threading.Tasks; using Telegram.Bot; using Telegram.Bot.Passport; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Passport; @@ -64,7 +64,7 @@ await BotClient.SendTextMessageAsync( "2. Open link in browser to redirect you back to Telegram passport\n" + "3. Authorize bot to access the info", ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Share via Passport", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) @@ -80,16 +80,16 @@ public void Should_validate_passport_message() Update update = _classFixture.Entity; PassportData passportData = update.Message.PassportData; - EncryptedPassportElement phoneElement = Assert.Single(passportData.Data, el => el.Type == "phone_number"); + EncryptedPassportElement phoneElement = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.PhoneNumber); Assert.NotNull(phoneElement); - Assert.Equal(PassportEnums.Scope.PhoneNumber, phoneElement.Type); + Assert.Equal(EncryptedPassportElementType.PhoneNumber, phoneElement.Type); Assert.NotEmpty(phoneElement.PhoneNumber); Assert.True(long.TryParse(phoneElement.PhoneNumber, out _)); Assert.NotEmpty(phoneElement.Hash); - EncryptedPassportElement emailElement = Assert.Single(passportData.Data, el => el.Type == "email"); + EncryptedPassportElement emailElement = Assert.Single(passportData.Data, el => el.Type == EncryptedPassportElementType.Email); Assert.NotNull(emailElement); - Assert.Equal(PassportEnums.Scope.Email, emailElement.Type); + Assert.Equal(EncryptedPassportElementType.Email, emailElement.Type); Assert.NotEmpty(emailElement.Email); Assert.NotEmpty(emailElement.Hash); diff --git a/test/IntegrationTests/Passport Element Errors/Passport Document Error Tests.cs b/test/IntegrationTests/Passport Element Errors/Passport Document Error Tests.cs index e191cb9..b86dc34 100644 --- a/test/IntegrationTests/Passport Element Errors/Passport Document Error Tests.cs +++ b/test/IntegrationTests/Passport Element Errors/Passport Document Error Tests.cs @@ -2,12 +2,13 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo +using IntegrationTests.Framework; using System.Security.Cryptography; using System.Threading.Tasks; -using IntegrationTests.Framework; using Telegram.Bot; using Telegram.Bot.Passport; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; +using Telegram.Bot.Requests.PassportErrors; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Passport; @@ -66,7 +67,7 @@ await BotClient.SendTextMessageAsync( "2. Open link in browser to redirect you back to Telegram passport\n" + "3. Authorize bot to access the info", ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Share via Passport", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) @@ -127,7 +128,7 @@ await BotClient.SendTextMessageAsync( _fixture.SupergroupChat, "Errors are set on all passport data and documents.\n" + "You can see error message with opening the request link again.", - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Passport Authorization Request", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) diff --git a/test/IntegrationTests/Passport Element Errors/Passport Registration Error Tests.cs b/test/IntegrationTests/Passport Element Errors/Passport Registration Error Tests.cs index 7335944..eb8e30d 100644 --- a/test/IntegrationTests/Passport Element Errors/Passport Registration Error Tests.cs +++ b/test/IntegrationTests/Passport Element Errors/Passport Registration Error Tests.cs @@ -2,13 +2,14 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo +using IntegrationTests.Framework; using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; -using IntegrationTests.Framework; using Telegram.Bot; using Telegram.Bot.Passport; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; +using Telegram.Bot.Requests.PassportErrors; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Passport; @@ -66,7 +67,7 @@ await BotClient.SendTextMessageAsync( "2. Open link in browser to redirect you back to Telegram passport\n" + "3. Authorize bot to access the info", ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Share via Passport", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) @@ -121,7 +122,7 @@ await BotClient.SendTextMessageAsync( _fixture.SupergroupChat, "Errors are set on all the files for passport registration document.\n" + "You can see error message with opening the request link again.", - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Passport Authorization Request", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) diff --git a/test/IntegrationTests/Passport Element Errors/Unspecified Error Tests.cs b/test/IntegrationTests/Passport Element Errors/Unspecified Error Tests.cs index 04edcb4..06e7c6a 100644 --- a/test/IntegrationTests/Passport Element Errors/Unspecified Error Tests.cs +++ b/test/IntegrationTests/Passport Element Errors/Unspecified Error Tests.cs @@ -2,14 +2,14 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo +using IntegrationTests.Framework; using System.Linq; using System.Threading.Tasks; -using IntegrationTests.Framework; using Telegram.Bot; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; +using Telegram.Bot.Requests.PassportErrors; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; -using Telegram.Bot.Types.Passport; using Telegram.Bot.Types.ReplyMarkups; using Xunit; @@ -47,7 +47,7 @@ public async Task Should_generate_auth_link() { new PassportScopeElementOne(PassportEnums.Scope.BankStatement), }); - AuthorizationRequestParameters authReq = new AuthorizationRequestParameters( + AuthorizationRequestParameters authReq = new( botId: _fixture.BotUser.Id, publicKey: publicKey, nonce: "Test nonce for bank statement", @@ -61,7 +61,7 @@ await BotClient.SendTextMessageAsync( "2. Open link in browser to redirect you back to Telegram passport\n" + "3. Authorize bot to access the info", ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Share via Passport", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) @@ -97,7 +97,7 @@ await BotClient.SendTextMessageAsync( _fixture.SupergroupChat, "An error is set on the bank statement.\n" + "You can see error message with opening the request link again.", - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Passport Authorization Request", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) diff --git a/test/IntegrationTests/Single Scope Requests/Driver License Tests.cs b/test/IntegrationTests/Single Scope Requests/Driver License Tests.cs index b2d00fa..259a0c7 100644 --- a/test/IntegrationTests/Single Scope Requests/Driver License Tests.cs +++ b/test/IntegrationTests/Single Scope Requests/Driver License Tests.cs @@ -2,14 +2,14 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo +using IntegrationTests.Framework; +using IntegrationTests.Framework.Fixtures; using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; -using IntegrationTests.Framework; -using IntegrationTests.Framework.Fixtures; using Telegram.Bot; using Telegram.Bot.Passport; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Passport; @@ -73,7 +73,7 @@ await BotClient.SendTextMessageAsync( "2. Open link in browser to so it redirects you to Telegram Passport\n" + "3. Authorize bot to access the info", ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Share via Passport", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) @@ -91,23 +91,22 @@ public void Should_Validate_Passport_Update() EncryptedPassportElement encryptedElement = Assert.Single(passportData.Data); Assert.NotNull(encryptedElement); - Assert.Equal("driver_license", encryptedElement.Type); - Assert.Equal(PassportEnums.Scope.DriverLicense, encryptedElement.Type); + Assert.Equal(EncryptedPassportElementType.DriverLicence, encryptedElement.Type); Assert.NotEmpty(encryptedElement.Data); Assert.NotEmpty(encryptedElement.Hash); Assert.NotNull(encryptedElement.FrontSide); Assert.NotEmpty(encryptedElement.FrontSide.FileId); - Assert.InRange(encryptedElement.FrontSide.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedElement.FrontSide.FileSize ?? 0, 1_000, 50_000_000); Assert.NotNull(encryptedElement.ReverseSide); Assert.NotEmpty(encryptedElement.ReverseSide.FileId); - Assert.InRange(encryptedElement.ReverseSide.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedElement.ReverseSide.FileSize ?? 0, 1_000, 50_000_000); Assert.NotNull(encryptedElement.Selfie); Assert.NotEmpty(encryptedElement.Selfie.FileId); - Assert.InRange(encryptedElement.Selfie.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedElement.Selfie.FileSize ?? 0, 1_000, 50_000_000); Assert.NotNull(encryptedElement.Translation); Assert.NotEmpty(encryptedElement.Translation); @@ -118,7 +117,7 @@ public void Should_Validate_Passport_Update() ); Assert.All( encryptedElement.Translation, - translation => Assert.InRange(translation.FileSize, 1_000, 50_000_000) + translation => Assert.InRange(translation.FileSize ?? 0, 1_000, 50_000_000) ); Assert.NotNull(passportData.Credentials); @@ -183,13 +182,13 @@ public void Should_Decrypt_Document_Data() ); Assert.NotEmpty(licenseDoc.DocumentNo); - if (string.IsNullOrEmpty(licenseDoc.ExpiryDate)) + if (licenseDoc.ExpiryDate is null) { - Assert.Null(licenseDoc.Expiry); + Assert.Null(licenseDoc.ExpiryDate); } else { - Assert.NotNull(licenseDoc.Expiry); + Assert.NotNull(licenseDoc.ExpiryDate); } } @@ -218,7 +217,7 @@ public async Task Should_Decrypt_Front_Side_File() Assert.NotEmpty(encryptedFileInfo.FilePath); Assert.NotEmpty(encryptedFileInfo.FileId); - Assert.InRange(encryptedFileInfo.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedFileInfo.FileSize ?? 0, 1_000, 50_000_000); } [OrderedFact("Should decrypt reverse side photo file of 'driver_license' element")] @@ -234,7 +233,7 @@ public async Task Should_Decrypt_Reverse_Side_File() File encryptedFileInfo; string decryptedFilePath = System.IO.Path.GetTempFileName(); using (System.IO.Stream - encryptedContent = new System.IO.MemoryStream(element.ReverseSide.FileSize), + encryptedContent = new System.IO.MemoryStream(element.ReverseSide.FileSize ?? 0), decryptedFile = System.IO.File.OpenWrite(decryptedFilePath) ) { @@ -255,7 +254,7 @@ await decrypter.DecryptFileAsync( Assert.NotEmpty(encryptedFileInfo.FilePath); Assert.NotEmpty(encryptedFileInfo.FileId); - Assert.InRange(encryptedFileInfo.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedFileInfo.FileSize ?? 0, 1_000, 50_000_000); } [OrderedFact("Should decrypt selfie photo file of 'driver_license' element and send it to chat")] @@ -274,9 +273,9 @@ public async Task Should_Decrypt_Selfie_File() Assert.NotEmpty(encryptedFileInfo.FilePath); Assert.NotEmpty(encryptedFileInfo.FileId); - Assert.InRange(encryptedFileInfo.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedFileInfo.FileSize ?? 0, 1_000, 50_000_000); - using (System.IO.MemoryStream stream = new System.IO.MemoryStream(encryptedFileInfo.FileSize)) + using (System.IO.MemoryStream stream = new System.IO.MemoryStream(encryptedFileInfo.FileSize ?? 0)) { await BotClient.DownloadFileAsync(encryptedFileInfo.FilePath, stream); encryptedContent = stream.ToArray(); @@ -323,9 +322,9 @@ public async Task Should_Decrypt_Translation_File() Assert.NotEmpty(encryptedFileInfo.FilePath); Assert.NotEmpty(encryptedFileInfo.FileId); - Assert.InRange(encryptedFileInfo.FileSize, 1_000, 50_000_000); + Assert.InRange(encryptedFileInfo.FileSize ?? 0, 1_000, 50_000_000); - using (System.IO.MemoryStream stream = new System.IO.MemoryStream(encryptedFileInfo.FileSize)) + using (System.IO.MemoryStream stream = new System.IO.MemoryStream(encryptedFileInfo.FileSize ?? 0)) { await BotClient.DownloadFileAsync(encryptedFileInfo.FilePath, stream); encryptedContent = stream.ToArray(); diff --git a/test/IntegrationTests/Single Scope Requests/Personal Details Tests.cs b/test/IntegrationTests/Single Scope Requests/Personal Details Tests.cs index 1fe31a0..b4c3873 100644 --- a/test/IntegrationTests/Single Scope Requests/Personal Details Tests.cs +++ b/test/IntegrationTests/Single Scope Requests/Personal Details Tests.cs @@ -2,15 +2,15 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo +using IntegrationTests.Framework; +using IntegrationTests.Framework.Fixtures; using System; using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; -using IntegrationTests.Framework; -using IntegrationTests.Framework.Fixtures; using Telegram.Bot; using Telegram.Bot.Passport; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Passport; @@ -71,7 +71,7 @@ await BotClient.SendTextMessageAsync( "2. Open link in browser to redirect you back to Telegram passport\n" + "3. Authorize bot to access the info", ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Share via Passport", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) @@ -88,8 +88,7 @@ public void Should_Validate_Passport_Update() PassportData passportData = update.Message.PassportData; EncryptedPassportElement encryptedElement = Assert.Single(passportData.Data); - Assert.Equal("personal_details", encryptedElement.Type); - Assert.Equal(PassportEnums.Scope.PersonalDetails, encryptedElement.Type); + Assert.Equal(EncryptedPassportElementType.PersonalDetails, encryptedElement.Type); Assert.NotEmpty(encryptedElement.Data); Assert.NotEmpty(encryptedElement.Hash); @@ -138,8 +137,8 @@ public void Should_Decrypt_Data() Assert.Equal(2, personalDetails.CountryCode.Length); Assert.NotEmpty(personalDetails.ResidenceCountryCode); Assert.Equal(2, personalDetails.ResidenceCountryCode.Length); - Assert.NotEmpty(personalDetails.BirthDate); - Assert.InRange(personalDetails.Birthdate, new DateTime(1900, 1, 1), DateTime.Today); + //Assert.NotEmpty(personalDetails.BirthDate); + Assert.InRange(personalDetails.BirthDate, new DateTime(1900, 1, 1), DateTime.Today); } } } diff --git a/test/IntegrationTests/Single Scope Requests/Residential Address Tests.cs b/test/IntegrationTests/Single Scope Requests/Residential Address Tests.cs index 86a9bde..9ad3cbe 100644 --- a/test/IntegrationTests/Single Scope Requests/Residential Address Tests.cs +++ b/test/IntegrationTests/Single Scope Requests/Residential Address Tests.cs @@ -2,13 +2,13 @@ // ReSharper disable CheckNamespace // ReSharper disable StringLiteralTypo -using System.Linq; -using System.Threading.Tasks; using IntegrationTests.Framework; using IntegrationTests.Framework.Fixtures; +using System.Linq; +using System.Threading.Tasks; using Telegram.Bot; using Telegram.Bot.Passport; -using Telegram.Bot.Passport.Request; +using Telegram.Bot.Requests; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Passport; @@ -64,7 +64,7 @@ await BotClient.SendTextMessageAsync( "2. Open link in browser to redirect you back to Telegram passport\n" + "3. Authorize bot to access the info", ParseMode.Markdown, - replyMarkup: (InlineKeyboardMarkup) InlineKeyboardButton.WithUrl( + replyMarkup: (InlineKeyboardMarkup)InlineKeyboardButton.WithUrl( "Share via Passport", $"https://telegrambots.github.io/Telegram.Bot.Extensions.Passport/redirect.html?{authReq.Query}" ) @@ -84,8 +84,7 @@ public void Should_Validate_Passport_Update() EncryptedPassportElement encryptedElement = Assert.Single(passportData.Data); Assert.NotNull(encryptedElement); - Assert.Equal("address", encryptedElement.Type); - Assert.Equal(PassportEnums.Scope.Address, encryptedElement.Type); + Assert.Equal(EncryptedPassportElementType.Address, encryptedElement.Type); Assert.NotEmpty(encryptedElement.Data); Assert.NotEmpty(encryptedElement.Hash);