Skip to content

Commit

Permalink
Merge pull request #1091 from TelegramBots/develop
Browse files Browse the repository at this point in the history
Release v18.0.0-alpha.3
  • Loading branch information
tuscen committed Apr 29, 2022
2 parents a8ee720 + e98e2bb commit 72f6278
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 134 deletions.
2 changes: 1 addition & 1 deletion .azure-pipelines/variables.yml
Expand Up @@ -3,7 +3,7 @@ variables:
- name: versionPrefix
value: 18.0.0
- name: versionSuffix
value: 'alpha.2'
value: 'alpha.3'
- name: ciVersionSuffix
value: ci.$(Build.BuildId)+git.commit.$(Build.SourceVersion)
- name: isPreRelease
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
### Added

- Package `Telegram.Bot.Extensions.Polling` is merged in the main package
- Type `TelegramBotClientOptions`
- Types `WebAppInfo`, `SentWebAppMessage`, `WebAppData`, `MenuButton`, `MenuButtonCommands`, `MenuButtonWebApp`,
`MenuButtonDefault`, `ChatAdministratorRights`
- Requests `AnswerWebAppQueryRequest`, `SetChatMenuButtonRequest`, `GetChatMenuButtonRequest`,
Expand All @@ -37,10 +38,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
`WebAppData` in type `Message`
- Enum members `VideoChatScheduled`, `VideoChatStarted`, `VideoChatEnded`, and `VideoChatParticipantsInvited`
in type `MessageType`
- Property `bool ITelegramBotClient.LocalBotServer`
- `TelegramBotClient` constructor that accepts an instance `TelegramBotClientOptions` and `HttpClient`

### Changed
- Renamed properties `ChatMemberAdministrator.CanManageVoiceChats`, `PromoteChatMemberRequest.CanManageVoiceChats` to
`ChatMemberAdministrator.CanManageVideoChats ` and `PromoteChatMemberRequest.CanManageVideoChats`
- Removed `baseUrl` parameter from constructor in `TelegramBotClient` that accepts a token

### Fixed
- Argument `protectContent` in method `TelegramBotClientExtensions.ForwardMessageAsync` is passed to the
Expand All @@ -51,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
in type `MessageType`
- Properties `VoiceChatScheduled`, `VoiceChatStarted`, `VoiceChatEnded` and `VoiceChatParticipantsInvited`
in type `Message`
- Property `PinChatMessageRequest.ProtectedContent`

## [v18.0.0-alpha.1] - 2022-02-13

Expand Down
2 changes: 1 addition & 1 deletion README.md
@@ -1,7 +1,7 @@
# .NET Client for Telegram Bot API

[![package](https://img.shields.io/nuget/vpre/Telegram.Bot.svg?label=Telegram.Bot&style=flat-square)](https://www.nuget.org/packages/Telegram.Bot)
[![Bot API Version](https://img.shields.io/badge/Bot%20API-5.7%20(January%2031,%202022)-f36caf.svg?style=flat-square)](https://core.telegram.org/bots/api#january-31-2022)
[![Bot API Version](https://img.shields.io/badge/Bot%20API-6.0%20(April%2016,%202022)-f36caf.svg?style=flat-square)](https://core.telegram.org/bots/api#april-16-2022)
[![documentations](https://img.shields.io/badge/Documentations-Book-orange.svg?style=flat-square)](https://telegrambots.github.io/book/)
[![telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://t.me/joinchat/B35YY0QbLfd034CFnvCtCA)

Expand Down
9 changes: 8 additions & 1 deletion src/Telegram.Bot/ITelegramBotClient.cs
Expand Up @@ -2,6 +2,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Telegram.Bot.Args;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Requests.Abstractions;
Expand All @@ -11,8 +12,14 @@ namespace Telegram.Bot;
/// <summary>
/// A client interface to use the Telegram Bot API
/// </summary>
[PublicAPI]
public interface ITelegramBotClient
{
/// <summary>
///
/// </summary>
bool LocalBotServer { get; }

/// <summary>
/// Unique identifier for the bot from bot token. For example, for the bot token
/// "1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy", the bot id is "1234567".
Expand Down Expand Up @@ -78,4 +85,4 @@ public interface ITelegramBotClient
Stream destination,
CancellationToken cancellationToken = default
);
}
}
116 changes: 37 additions & 79 deletions src/Telegram.Bot/TelegramBotClient.cs
Expand Up @@ -5,6 +5,7 @@
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Telegram.Bot.Args;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Extensions;
Expand All @@ -17,17 +18,18 @@ namespace Telegram.Bot;
/// <summary>
/// A client to use the Telegram Bot API
/// </summary>
[PublicAPI]
public class TelegramBotClient : ITelegramBotClient
{
const string BaseTelegramUrl = "https://api.telegram.org";
readonly TelegramBotClientOptions _options;

readonly string _baseFileUrl;
readonly string _baseRequestUrl;
readonly bool _localBotServer;
readonly HttpClient _httpClient;

/// <inheritdoc/>
public long? BotId { get; }
public long? BotId => _options.BotId;

/// <inheritdoc />
public bool LocalBotServer => _options.LocalBotServer;

/// <summary>
/// Timeout for requests
Expand All @@ -54,72 +56,48 @@ public TimeSpan Timeout
/// <summary>
/// Create a new <see cref="TelegramBotClient"/> instance.
/// </summary>
/// <param name="token">API token</param>
/// <param name="options">Configuration for <see cref="TelegramBotClient" /></param>
/// <param name="httpClient">A custom <see cref="HttpClient"/></param>
/// <param name="baseUrl">
/// Used to change base url to your private bot api server URL. It looks like
/// http://localhost:8081. Path, query and fragment will be omitted if present.
/// </param>
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="token"/> format is invalid
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="baseUrl"/> format is invalid
/// <exception cref="ArgumentNullException">
/// Thrown if <paramref name="options"/> is <c>null</c>
/// </exception>
public TelegramBotClient(
string token,
HttpClient? httpClient = null,
string? baseUrl = default)
TelegramBotClientOptions options,
HttpClient? httpClient = default)
{
if (token is null) { throw new ArgumentNullException(nameof(token)); }

BotId = GetIdFromToken(token);

_localBotServer = baseUrl is not null;
var effectiveBaseUrl = _localBotServer
? ExtractBaseUrl(baseUrl)
: BaseTelegramUrl;

_baseRequestUrl = $"{effectiveBaseUrl}/bot{token}";
_baseFileUrl = $"{effectiveBaseUrl}/file/bot{token}";
_options = options ?? throw new ArgumentNullException(nameof(options));
_httpClient = httpClient ?? new HttpClient();

static long? GetIdFromToken(string token)
{
#if NETCOREAPP3_1_OR_GREATER
var span = token.AsSpan();
var index = span.IndexOf(':');

if (index is < 1 or > 16) { return null; }

var botIdSpan = span[..index];
if (!long.TryParse(botIdSpan, out var botId)) { return null; }
#else
var index = token.IndexOf(value: ':');

if (index is < 1 or > 16) { return null; }

var botIdSpan = token.Substring(startIndex: 0, length: index);
if (!long.TryParse(botIdSpan, out var botId)) { return null; }
#endif

return botId;
}
}

/// <summary>
/// Create a new <see cref="TelegramBotClient"/> instance.
/// </summary>
/// <param name="token"></param>
/// <param name="httpClient">A custom <see cref="HttpClient"/></param>
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="token"/> format is invalid
/// </exception>
public TelegramBotClient(
string token,
HttpClient? httpClient = null) :
this(new TelegramBotClientOptions(token), httpClient)
{ }

/// <inheritdoc />
public virtual async Task<TResponse> MakeRequestAsync<TResponse>(
IRequest<TResponse> request,
CancellationToken cancellationToken = default)
{
if (request is null) { throw new ArgumentNullException(nameof(request)); }

var url = $"{_baseRequestUrl}/{request.MethodName}";
var url = $"{_options.BaseRequestUrl}/{request.MethodName}";

using var httpRequest = new HttpRequestMessage(method: request.Method, requestUri: url)
#pragma warning disable CA2000
var httpRequest = new HttpRequestMessage(method: request.Method, requestUri: url)
{
Content = request.ToHttpContent()
};
#pragma warning restore CA2000

if (OnMakingApiRequest is not null)
{
Expand Down Expand Up @@ -163,9 +141,8 @@ public TimeSpan Timeout
.DeserializeContentAsync<ApiResponse>(
guard: response =>
response.ErrorCode == default ||
// ReSharper disable ConditionIsAlwaysTrueOrFalse
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
response.Description is null
// ReSharper restore ConditionIsAlwaysTrueOrFalse
)
.ConfigureAwait(false);

Expand Down Expand Up @@ -247,7 +224,7 @@ await MakeRequestAsync(request: new GetMeRequest(), cancellationToken: cancellat

if (destination is null) { throw new ArgumentNullException(nameof(destination)); }

var fileUri = $"{_baseFileUrl}/{filePath}";
var fileUri = $"{_options.BaseFileUrl}/{filePath}";
using HttpResponseMessage httpResponse = await GetResponseAsync(
httpClient: _httpClient,
fileUri: fileUri,
Expand All @@ -260,9 +237,8 @@ await MakeRequestAsync(request: new GetMeRequest(), cancellationToken: cancellat
.DeserializeContentAsync<ApiResponse>(
guard: response =>
response.ErrorCode == default ||
// ReSharper disable ConditionIsAlwaysTrueOrFalse
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
response.Description is null
// ReSharper restore ConditionIsAlwaysTrueOrFalse
)
.ConfigureAwait(false);

Expand Down Expand Up @@ -329,28 +305,10 @@ await httpResponse.Content.CopyToAsync(destination)
}
}

static string ExtractBaseUrl(string? baseUrl)
{
if (baseUrl is null) { throw new ArgumentNullException(paramName: nameof(baseUrl)); }

if (!Uri.TryCreate(uriString: baseUrl, uriKind: UriKind.Absolute, out var baseUri)
|| string.IsNullOrEmpty(value: baseUri.Scheme)
|| string.IsNullOrEmpty(value: baseUri.Authority))
{
throw new ArgumentException(
message: "Invalid format. A valid base url looks \"http://localhost:8081\" ",
paramName: nameof(baseUrl)
);
}

return $"{baseUri.Scheme}://{baseUri.Authority}";
}

#region For testing purposes

internal string BaseRequestUrl => _baseRequestUrl;
internal string BaseFileUrl => _baseFileUrl;
internal bool LocalBotServer => _localBotServer;
internal string BaseRequestUrl => _options.BaseRequestUrl;
internal string BaseFileUrl => _options.BaseFileUrl;

#endregion
}
}
4 changes: 1 addition & 3 deletions src/Telegram.Bot/TelegramBotClientExtensions.ApiMethods.cs
Expand Up @@ -2423,7 +2423,6 @@ await botClient.ThrowIfNull(nameof(botClient))
/// Pass <c><c>true</c></c>, if it is not necessary to send a notification to all chat members about
/// the new pinned message. Notifications are always disabled in channels and private chats
/// </param>
/// <param name="protectContent">Protects the contents of sent messages from forwarding and saving</param>
/// <param name="cancellationToken">
/// A cancellation token that can be used by other objects or threads to receive notice of cancellation
/// </param>
Expand All @@ -2432,7 +2431,6 @@ await botClient.ThrowIfNull(nameof(botClient))
ChatId chatId,
int messageId,
bool? disableNotification = default,
bool? protectContent = default,
CancellationToken cancellationToken = default
) =>
await botClient.ThrowIfNull(nameof(botClient)).
Expand Down Expand Up @@ -2921,7 +2919,7 @@ await botClient.ThrowIfNull(nameof(botClient))

/// <summary>
/// Use this method to change the default administrator rights requested by the bot when it's added as an
/// administrator to groups or channels. These rights will be suggested to users, but they are are free to modify
/// administrator to groups or channels. These rights will be suggested to users, but they are free to modify
/// the list before adding the bot.
/// </summary>
/// <param name="botClient">An instance of <see cref="ITelegramBotClient"/></param>
Expand Down

0 comments on commit 72f6278

Please sign in to comment.