Skip to content

Commit

Permalink
net5.0, system.text.json, ITelegramBot interface for mocks
Browse files Browse the repository at this point in the history
  • Loading branch information
justdmitry committed May 26, 2021
1 parent 03e6ccf commit f6ba076
Show file tree
Hide file tree
Showing 21 changed files with 137 additions and 1,597 deletions.
19 changes: 7 additions & 12 deletions NetTelegramBotApi.Tests/NetTelegramBotApi.Tests.csproj
@@ -1,22 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net461;netcoreapp1.0</TargetFrameworks>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<RuntimeFrameworkVersion>1.0.3</RuntimeFrameworkVersion>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>NetTelegramBotApi</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="Microsoft.CSharp" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
25 changes: 8 additions & 17 deletions NetTelegramBotApi.sln
@@ -1,19 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
# Visual Studio Version 16
VisualStudioVersion = 16.0.31313.79
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ABC2A4CB-9443-434E-B2EA-E739D288F330}"
ProjectSection(SolutionItems) = preProject
appveyor.yml = appveyor.yml
LICENSE = LICENSE
README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetTelegramBotApi", "NetTelegramBotApi\NetTelegramBotApi.csproj", "{0CA802AD-0F85-4314-8D70-2C5F182C9F42}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TelegramBotDemo", "TelegramBotDemo\TelegramBotDemo.csproj", "{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelegramBotDemo-vNext", "TelegramBotDemo-vNext\TelegramBotDemo-vNext.csproj", "{0A5DF37F-F6E0-412E-8B84-AE7120B4D8C5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetTelegramBotApi.Tests", "NetTelegramBotApi.Tests\NetTelegramBotApi.Tests.csproj", "{82AAECA1-D92F-4C7D-9CA8-B9EBACCCCCAF}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetTelegramBotApi.Tests", "NetTelegramBotApi.Tests\NetTelegramBotApi.Tests.csproj", "{82AAECA1-D92F-4C7D-9CA8-B9EBACCCCCAF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -37,18 +37,6 @@ Global
{0CA802AD-0F85-4314-8D70-2C5F182C9F42}.Release|x64.Build.0 = Release|Any CPU
{0CA802AD-0F85-4314-8D70-2C5F182C9F42}.Release|x86.ActiveCfg = Release|Any CPU
{0CA802AD-0F85-4314-8D70-2C5F182C9F42}.Release|x86.Build.0 = Release|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Debug|x64.ActiveCfg = Debug|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Debug|x64.Build.0 = Debug|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Debug|x86.ActiveCfg = Debug|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Debug|x86.Build.0 = Debug|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Release|Any CPU.Build.0 = Release|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Release|x64.ActiveCfg = Release|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Release|x64.Build.0 = Release|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Release|x86.ActiveCfg = Release|Any CPU
{E0CDD1D2-E50E-4CD8-97F4-C1AE44DE7E43}.Release|x86.Build.0 = Release|Any CPU
{0A5DF37F-F6E0-412E-8B84-AE7120B4D8C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A5DF37F-F6E0-412E-8B84-AE7120B4D8C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A5DF37F-F6E0-412E-8B84-AE7120B4D8C5}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -77,4 +65,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1AE94356-8779-4DAE-A0A5-7F6DE8FC7E48}
EndGlobalSection
EndGlobal
13 changes: 13 additions & 0 deletions NetTelegramBotApi/ITelegramBot.cs
@@ -0,0 +1,13 @@
using System.Threading.Tasks;
using NetTelegramBotApi.Requests;
using NetTelegramBotApi.Types;

namespace NetTelegramBotApi
{
public interface ITelegramBot
{
Task<T> MakeRequestAsync<T>(RequestBase<T> request);

Update DeserializeUpdate(string json);
}
}
29 changes: 12 additions & 17 deletions NetTelegramBotApi/NetTelegramBotApi.csproj
Expand Up @@ -3,37 +3,32 @@
<PropertyGroup>
<Description>Telegram Bot API library</Description>
<AssemblyTitle>NetTelegramBotApi</AssemblyTitle>
<Version>4.2.0</Version>
<TargetFrameworks>net45;net46;netstandard1.3</TargetFrameworks>
<NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion>
<Version>5.0.0</Version>
<TargetFramework>net5.0</TargetFramework>
<AssemblyName>NetTelegramBotApi</AssemblyName>
<PackageId>NetTelegramBotApi</PackageId>
<PackageTags>telegram;bot;api</PackageTags>
<PackageReleaseNotes>See https://github.com/justdmitry/NetTelegramBotApi/releases/tag/v4.2.0</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/justdmitry/NetTelegramBotApi/master/package-icon.gif</PackageIconUrl>
<PackageReleaseNotes>See https://github.com/justdmitry/NetTelegramBotApi/releases/tag/v5.0.0</PackageReleaseNotes>
<PackageIcon>package-icon.png</PackageIcon>
<PackageProjectUrl>https://github.com/justdmitry/NetTelegramBotApi</PackageProjectUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/justdmitry/NetTelegramBotApi.git</RepositoryUrl>
<Authors>Dmitry Popov</Authors>
<Company />
<Copyright>Copyright © Dmitry Popov, 2015-2017</Copyright>
<Copyright>Copyright © Dmitry Popov, 2015-2021</Copyright>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="System.Text.Json" Version="5.0.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="Microsoft.CSharp" />
<ItemGroup>
<None Include="..\package-icon.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>

</Project>
6 changes: 3 additions & 3 deletions NetTelegramBotApi/Requests/RequestBase.cs
@@ -1,6 +1,6 @@
using System;
using System.Net.Http;
using Newtonsoft.Json;
using System.Text.Json;

namespace NetTelegramBotApi.Requests
{
Expand All @@ -12,12 +12,12 @@ public RequestBase(string methodName)
}

public string MethodName { get; protected set; }

public abstract HttpContent CreateHttpContent();

protected string JsonSerialize(object value)
{
return JsonConvert.SerializeObject(value, TelegramBot.JsonSettings);
return JsonSerializer.Serialize(value, TelegramBot.JsonOptions);
}
}
}
114 changes: 45 additions & 69 deletions NetTelegramBotApi/TelegramBot.cs
@@ -1,105 +1,81 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NetTelegramBotApi.Requests;
using NetTelegramBotApi.Util;
using NetTelegramBotApi.Types;
using Newtonsoft.Json;
using System.Text.Json;

namespace NetTelegramBotApi
{
public class TelegramBot
public class TelegramBot : ITelegramBot
{
public static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
public static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
{
ContractResolver = new Util.JsonLowerCaseUnderscoreContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
PropertyNamingPolicy = new JsonLowerCaseUnderscoreNamingPolicy(),
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
};

private string accessToken;

private Uri baseAddress;
private readonly string accessToken;
private readonly HttpClient httpClient;

static TelegramBot()
{
JsonSettings.Converters.Add(new UnixDateTimeConverter());
JsonOptions.Converters.Add(new UnixDateTimeConverter());
}

/// <summary>
/// Proxy information for internet access
/// </summary>
public IWebProxy WebProxy { get; set; }

public TelegramBot(string accessToken)
public TelegramBot(string accessToken, HttpClient httpClient)
{
if (string.IsNullOrWhiteSpace(accessToken))
{
throw new ArgumentNullException("accessToken");
throw new ArgumentNullException(nameof(accessToken));
}

this.accessToken = accessToken;
this.baseAddress = new Uri("https://api.telegram.org/bot" + accessToken + "/");
this.httpClient = httpClient ?? new HttpClient();
}

/// <exception cref="BotRequestException">When non-Ok response returned from server.</exception>
public async Task<T> MakeRequestAsync<T>(RequestBase<T> request)
{
using (var client = new HttpClient(MakeHttpMessageHandler()))
var uri = new Uri("https://api.telegram.org/bot" + accessToken + "/" + request.MethodName);
using var httpMessage = new HttpRequestMessage(HttpMethod.Get, uri);
using var postContent = request.CreateHttpContent();
if (postContent != null)
{
client.BaseAddress = baseAddress;
using (var httpMessage = new HttpRequestMessage(HttpMethod.Get, request.MethodName))
{
var postContent = request.CreateHttpContent();
if (postContent != null)
{
httpMessage.Method = HttpMethod.Post;
httpMessage.Content = postContent;
}

using (var response = await client.SendAsync(httpMessage).ConfigureAwait(false))
{
if ((int)response.StatusCode >= 500)
{
// Let's throw exception. It's server fault
response.EnsureSuccessStatusCode();
}

var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var result = DeserializeMessage<BotResponse<T>>(responseText);
if (!result.Ok || !response.IsSuccessStatusCode)
{
var exceptionMessage = $"Request failed (status code {(int)response.StatusCode}): {result.Description}";
throw new BotRequestException(exceptionMessage)
{
StatusCode = response.StatusCode,
ResponseBody = responseText,
Description = result.Description,
ErrorCode = result.ErrorCode,
Parameters = result.Parameters,
};
}
httpMessage.Method = HttpMethod.Post;
httpMessage.Content = postContent;
}

var retVal = result.Result;
var forPostProcessing = retVal as IPostProcessingRequired;
if (forPostProcessing != null)
{
forPostProcessing.PostProcess(accessToken);
}
using var response = await httpClient.SendAsync(httpMessage).ConfigureAwait(false);
if ((int)response.StatusCode >= 500)
{
// Let's throw exception. It's server fault
response.EnsureSuccessStatusCode();
}

return retVal;
}
}
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var result = DeserializeMessage<BotResponse<T>>(responseText);
if (!result.Ok || !response.IsSuccessStatusCode)
{
var exceptionMessage = $"Request failed (status code {(int)response.StatusCode}): {result.Description}";
throw new BotRequestException(exceptionMessage)
{
StatusCode = response.StatusCode,
ResponseBody = responseText,
Description = result.Description,
ErrorCode = result.ErrorCode,
Parameters = result.Parameters,
};
}
}

protected virtual HttpClientHandler MakeHttpMessageHandler()
{
return new HttpClientHandler
var retVal = result.Result;
if (retVal is IPostProcessingRequired forPostProcessing)
{
Proxy = WebProxy,
UseProxy = (WebProxy != null)
};
forPostProcessing.PostProcess(accessToken);
}

return retVal;
}

/// <summary>
Expand All @@ -112,9 +88,9 @@ public Update DeserializeUpdate(string json)
return DeserializeMessage<Update>(json);
}

protected T DeserializeMessage<T>(string json)
protected static T DeserializeMessage<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json, JsonSettings);
return JsonSerializer.Deserialize<T>(json, JsonOptions);
}
}
}
30 changes: 15 additions & 15 deletions NetTelegramBotApi/Types/ForceReply.cs
@@ -1,12 +1,12 @@
using System;
using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace NetTelegramBotApi.Types
{
/// <summary>
/// Upon receiving a message with this object, Telegram clients will display a reply interface to the user
/// (act as if the user has selected the bot‘s message and tapped ’Reply').
/// This can be extremely useful if you want to create user-friendly step-by-step interfaces
/// Upon receiving a message with this object, Telegram clients will display a reply interface to the user
/// (act as if the user has selected the bot‘s message and tapped ’Reply').
/// This can be extremely useful if you want to create user-friendly step-by-step interfaces
/// without having to sacrifice privacy mode.
/// <see cref="https://core.telegram.org/bots/api#forcereply">See more info on Telegram site</see>
/// </summary>
Expand All @@ -18,25 +18,25 @@ public class ForceReply : ReplyMarkupBase
/// <remarks>
/// In original Telegram API, member is called 'ForceReply', but in C# it's forbidden to have properties with same name as class
/// </remarks>
[JsonProperty("force_reply")]
[JsonPropertyName("force_reply")]
public bool Force { get; set; }

/// <summary>
/// Optional. Use this parameter if you want to force reply from specific users only.
/// Targets:
/// 1) users that are @mentioned in the text of the Message object;
/// Optional. Use this parameter if you want to force reply from specific users only.
/// Targets:
/// 1) users that are @mentioned in the text of the Message object;
/// 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
/// </summary>
/// <example>
/// A poll bot for groups runs in privacy mode (only receives commands, replies to its messages and mentions).
/// There could be two ways to create a new poll:
/// 1) Explain the user how to send a command with parameters (e.g. /newpoll question answer1 answer2).
/// A poll bot for groups runs in privacy mode (only receives commands, replies to its messages and mentions).
/// There could be two ways to create a new poll:
/// 1) Explain the user how to send a command with parameters (e.g. /newpoll question answer1 answer2).
/// May be appealing for hardcore users but lacks modern day polish.
/// 2) Guide the user through a step-by-step process. ‘Please send me your question’,
/// ‘Cool, now let’s add the first answer option‘, ’Great.
/// 2) Guide the user through a step-by-step process. ‘Please send me your question’,
/// ‘Cool, now let’s add the first answer option‘, ’Great.
/// Keep adding answer options, then send /done when you‘re ready’.
/// The last option is definitely more attractive. And if you use ForceReply in your bot‘s questions,
/// it will receive the user’s answers even if it only receives replies, commands and mentions — without any
/// The last option is definitely more attractive. And if you use ForceReply in your bot‘s questions,
/// it will receive the user’s answers even if it only receives replies, commands and mentions — without any
/// extra work for the user.
/// </example>
public bool Selective { get; set; }
Expand Down

0 comments on commit f6ba076

Please sign in to comment.