first commit

This commit is contained in:
BlubbFish 2026-08-03 22:52:44 +02:00
commit 17393a47d3
292 changed files with 23678 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.vs
EnumSerializer.Generator/bin
EnumSerializer.Generator/obj
TelegramBot/bin
TelegramBot/obj

View File

@ -0,0 +1,165 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Scriban;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
namespace EnumSerializer.Generator {
[Generator]
public class EnumConverterGenerator : IIncrementalGenerator {
const string JsonConverterAttribute = "Newtonsoft.Json.JsonConverterAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context) {
IncrementalValuesProvider<EnumDeclarationSyntax> enumDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => IsSyntaxTargetForGeneration(s),
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx))
.Where(static m => m is not null)!;
IncrementalValueProvider<(Compilation, ImmutableArray<EnumDeclarationSyntax>)> compilationAndEnums
= context.CompilationProvider.Combine(enumDeclarations.Collect());
context.RegisterSourceOutput(compilationAndEnums,
static (spc, source) => Execute(source.Item1, source.Item2, spc));
}
static bool IsSyntaxTargetForGeneration(SyntaxNode node) {
return node is EnumDeclarationSyntax e && e.AttributeLists.Count > 0;
}
static EnumDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context) {
// we know the node is a EnumDeclarationSyntax thanks to IsSyntaxTargetForGeneration
var enumDeclarationSyntax = (EnumDeclarationSyntax)context.Node;
// loop through all the attributes on the method
foreach(AttributeListSyntax attributeListSyntax in enumDeclarationSyntax.AttributeLists) {
foreach(AttributeSyntax attributeSyntax in attributeListSyntax.Attributes) {
if(context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol
is not IMethodSymbol attributeSymbol) {
// weird, we couldn't get the symbol, ignore it
continue;
}
INamedTypeSymbol attributeContainingTypeSymbol = attributeSymbol.ContainingType;
string fullName = attributeContainingTypeSymbol.ToDisplayString();
// Is the attribute the [JsonConverterAttribute] attribute?
if(fullName == JsonConverterAttribute) {
// return the enum
return enumDeclarationSyntax;
}
}
}
// we didn't find the attribute we were looking for
return null;
}
static void Execute(
Compilation compilation,
ImmutableArray<EnumDeclarationSyntax> enums,
SourceProductionContext context) {
if(enums.IsDefaultOrEmpty) {
// nothing to do yet
return;
}
IEnumerable<EnumDeclarationSyntax> distinctEnums = enums.Distinct();
List<EnumInfo> enumsToProcess = GetTypesToGenerate(compilation, distinctEnums, context.CancellationToken);
if(enumsToProcess.Count == 0) {
return;
}
Template template = Template.Parse(SourceGenerationHelper.ConverterTemplate);
foreach(var enumToProcess in enumsToProcess) {
var result = SourceGenerationHelper.GenerateConverterClass(template, enumToProcess);
context.AddSource(
hintName: $"{enumToProcess.Name}Converter.g.cs",
sourceText: SourceText.From(result, Encoding.UTF8)
);
}
}
static List<EnumInfo> GetTypesToGenerate(
Compilation compilation,
IEnumerable<EnumDeclarationSyntax> enums, CancellationToken ct) {
var enumsToProcess = new List<EnumInfo>();
INamedTypeSymbol? enumAttribute = compilation.GetTypeByMetadataName(JsonConverterAttribute);
if(enumAttribute is null) {
// nothing to do if this type isn't available
return enumsToProcess;
}
foreach(var enumDeclarationSyntax in enums) {
// stop if we're asked to
ct.ThrowIfCancellationRequested();
SemanticModel semanticModel = compilation.GetSemanticModel(enumDeclarationSyntax.SyntaxTree);
if(semanticModel.GetDeclaredSymbol(enumDeclarationSyntax, cancellationToken: ct)
is not INamedTypeSymbol enumSymbol) {
// report diagnostic, something went wrong
continue;
}
string name = enumSymbol.Name;
string nameSpace = enumSymbol.ContainingNamespace.IsGlobalNamespace
? string.Empty
: enumSymbol.ContainingNamespace.ToString();
string fullyQualifiedName = enumSymbol.ToString();
var enumMembers = enumSymbol.GetMembers();
var members = new List<KeyValuePair<string, string>>(enumMembers.Length);
foreach(var member in enumMembers) {
if(member is not IFieldSymbol field
|| field.ConstantValue is null) {
continue;
}
string? displayName = null;
foreach(var attribute in member.GetAttributes()) {
if(attribute.AttributeClass is null
|| attribute.AttributeClass.Name != "DisplayAttribute") {
continue;
}
foreach(var namedArgument in attribute.NamedArguments) {
if(namedArgument.Key == "Name" && namedArgument.Value.Value?.ToString() is { } dn) {
displayName = dn;
break;
}
}
}
members.Add(new(
member.Name,
displayName ?? ToSnakeCase(member.Name)
));
}
enumsToProcess.Add(new(
name: name,
ns: nameSpace,
fullyQualifiedName: fullyQualifiedName,
members: members
));
}
return enumsToProcess;
}
static string ToSnakeCase(string name) =>
string.Concat(name.Select((x, i) => i > 0 && char.IsUpper(x)
? $"_{x}"
: x.ToString())
).ToLower();
}
}

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace EnumSerializer.Generator {
public readonly struct EnumInfo {
public readonly string Name;
public readonly string FullyQualifiedName;
public readonly string Namespace;
/// <summary>
/// Key is the enum name.
/// </summary>
public readonly List<KeyValuePair<string, string>> Members;
public EnumInfo(
string name,
string ns,
string fullyQualifiedName,
List<KeyValuePair<string, string>> members) {
Name = name;
Namespace = ns;
Members = members;
FullyQualifiedName = fullyQualifiedName;
}
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IncludeBuildOutput>false</IncludeBuildOutput>
<IsRoslynComponent>true</IsRoslynComponent>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
</PropertyGroup>
<!-- The following libraries include the source generator interfaces and types we need -->
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
<PackageReference Include="Scriban" Version="5.4.4" GeneratePathProperty="true" PrivateAssets="all" />
<!-- This ensures the library will be packaged as a source generator when we use `dotnet pack` -->
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGScriban)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
# Enum Converter Generator
## Background
Telegram.Bot library relies on [Json.NET converters](https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm)
to map JSON input to various enums and vice versa.
It's rather tedious and repeating task. So that's where C# source generators come to help.
`EnumSerializer.Generator` looks for enums
annotated with `[JsonConverter(typeof(TEnumConverter))]` attribute and generates a converter that handles all possible enum values for us.
## Credits
This project is heavily inspired by the series of posts by Andrew Lock [Creating an incremental generator](https://andrewlock.net/creating-a-source-generator-part-1-creating-an-incremental-source-generator/) and [NetEscapades.EnumGenerators
](https://github.com/andrewlock/NetEscapades.EnumGenerators) project.
We use Alexandre Mutel's [Scriban](https://github.com/scriban/scriban) templating engine to generate
converter class output.

View File

@ -0,0 +1,81 @@
using System;
using System.Linq;
using Scriban;
namespace EnumSerializer.Generator {
public static class SourceGenerationHelper {
internal const string ConverterTemplate = @"//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the EnumSerializer.Generator source generator
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Runtime.CompilerServices;
{{~ if enum_namespace ~}}
namespace {{ enum_namespace }}{
{{~ end ~}}
internal partial class {{ enum_name }}Converter : JsonConverter<{{ enum_name }}>
{
public override void WriteJson(JsonWriter writer, {{ enum_name }} value, JsonSerializer serializer) =>
writer.WriteValue(value switch
{
{{~ for enum_member in enum_members ~}}
{{ enum_name }}.{{enum_member.key}} => ""{{ enum_member.value }}"",
{{~ end ~}}
{{~ if has_unknown_member ~}}
_ => throw new NotSupportedException(),
{{~ else ~}}
({{ enum_name }})0 => ""unknown"",
_ => throw new NotSupportedException(),
{{~ end ~}}
});
public override {{ enum_name }} ReadJson(
JsonReader reader,
Type objectType,
{{ enum_name }} existingValue,
bool hasExistingValue,
JsonSerializer serializer
) =>
JToken.ReadFrom(reader).Value<string>() switch
{
{{~ for enum_member in enum_members ~}}
""{{ enum_member.value }}"" => {{ enum_name }}.{{ enum_member.key }},
{{~ end ~}}
{{~ if has_unknown_member ~}}
_ => {{ enum_name }}.Unknown,
{{~ else ~}}
_ => 0,
{{~ end ~}}
};
}}";
public static string GenerateConverterClass(Template template, EnumInfo enumToGenerate) {
var hasUnknownMember = enumToGenerate.Members.Any(
e => string.Equals(e.Value, "Unknown", StringComparison.OrdinalIgnoreCase)
);
var result = template.Render(new {
EnumNamespace = enumToGenerate.Namespace,
EnumName = enumToGenerate.Name,
EnumMembers = enumToGenerate.Members,
HasUnknownMember = hasUnknownMember
});
return result;
}
}
}

31
TelegramBot.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31112.23
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelegramBot", "TelegramBot\TelegramBot.csproj", "{3616387D-AE43-4A5C-836A-D719CA5C96E9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnumSerializer.Generator", "EnumSerializer.Generator\EnumSerializer.Generator.csproj", "{22004504-B8B9-48F4-A2B2-9D18748BF252}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3616387D-AE43-4A5C-836A-D719CA5C96E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3616387D-AE43-4A5C-836A-D719CA5C96E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3616387D-AE43-4A5C-836A-D719CA5C96E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3616387D-AE43-4A5C-836A-D719CA5C96E9}.Release|Any CPU.Build.0 = Release|Any CPU
{22004504-B8B9-48F4-A2B2-9D18748BF252}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22004504-B8B9-48F4-A2B2-9D18748BF252}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22004504-B8B9-48F4-A2B2-9D18748BF252}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22004504-B8B9-48F4-A2B2-9D18748BF252}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {33D9A7F3-619F-47CB-A8ED-80416CD00338}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,36 @@
using System;
using System.Net.Http;
using Telegram.Bot.Requests.Abstractions;
namespace Telegram.Bot.Args {
/// <summary>
/// Provides data for MakingApiRequest event
/// </summary>
public class ApiRequestEventArgs : EventArgs {
/// <summary>
/// Bot API Request
/// </summary>
public IRequest Request {
get;
}
/// <summary>
/// HTTP Request Message
/// </summary>
public HttpRequestMessage? HttpRequestMessage {
get;
}
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="httpRequestMessage"></param>
public ApiRequestEventArgs(IRequest request, HttpRequestMessage? httpRequestMessage = default) {
Request = request;
HttpRequestMessage = httpRequestMessage;
}
}
}

View File

@ -0,0 +1,36 @@
using System.Net.Http;
namespace Telegram.Bot.Args {
/// <summary>
/// Provides data for ApiResponseReceived event
/// </summary>
public class ApiResponseEventArgs {
/// <summary>
/// HTTP response received from API
/// </summary>
public HttpResponseMessage ResponseMessage {
get;
}
/// <summary>
/// Event arguments of this request
/// </summary>
public ApiRequestEventArgs ApiRequestEventArgs {
get;
}
/// <summary>
/// Initialize an <see cref="ApiRequestEventArgs"/> object
/// </summary>
/// <param name="responseMessage">HTTP response received from API</param>
/// <param name="apiRequestEventArgs">Event arguments of this request</param>
public ApiResponseEventArgs(
HttpResponseMessage responseMessage,
ApiRequestEventArgs apiRequestEventArgs) {
ResponseMessage = responseMessage;
ApiRequestEventArgs = apiRequestEventArgs;
}
}
}

View File

@ -0,0 +1,15 @@
using System.Threading;
using System.Threading.Tasks;
#pragma warning disable 1591
namespace Telegram.Bot {
#pragma warning disable CA1711
public delegate ValueTask AsyncEventHandler<in TArgs>(
#pragma warning restore CA1711
ITelegramBotClient botClient,
TArgs args,
CancellationToken cancellationToken = default
);
}

View File

@ -0,0 +1,25 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Telegram.Bot.Converters {
internal class BanTimeUnixDateTimeConverter : UnixDateTimeConverter {
public override object? ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var nonNullable = Nullable.GetUnderlyingType(objectType) is null;
return reader.TokenType == JsonToken.Integer && reader.Value is 0L
? nonNullable ? default : null
: base.ReadJson(reader, objectType, existingValue, serializer);
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) {
if(value is null || value.Equals(default(DateTime))) {
writer.WriteValue(0);
} else {
base.WriteJson(writer, value, serializer);
}
}
}
}

View File

@ -0,0 +1,28 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using Telegram.Bot.Types;
namespace Telegram.Bot.Converters {
internal class ChatIdConverter : JsonConverter<ChatId> {
public override void WriteJson(JsonWriter writer, ChatId value, JsonSerializer serializer) {
if(value.Username != null) {
writer.WriteValue(value.Username);
} else {
writer.WriteValue(value.Identifier);
}
}
public override ChatId ReadJson(
JsonReader reader,
Type objectType,
ChatId existingValue,
bool hasExistingValue,
JsonSerializer serializer) {
var value = JToken.ReadFrom(reader).Value<string>();
return new ChatId(value);
}
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace Telegram.Bot.Converters {
internal class ChatMemberConverter : JsonConverter {
static readonly TypeInfo BaseType = typeof(ChatMember).GetTypeInfo();
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType) =>
BaseType.IsAssignableFrom(objectType.GetTypeInfo());
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
var jo = JObject.FromObject(value);
jo.WriteTo(writer);
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer) {
var jo = JObject.Load(reader);
var status = jo["status"].ToObject<ChatMemberStatus>();
var actualType = status switch {
ChatMemberStatus.Creator => typeof(ChatMemberOwner),
ChatMemberStatus.Administrator => typeof(ChatMemberAdministrator),
ChatMemberStatus.Member => typeof(ChatMemberMember),
ChatMemberStatus.Left => typeof(ChatMemberLeft),
ChatMemberStatus.Kicked => typeof(ChatMemberBanned),
ChatMemberStatus.Restricted => typeof(ChatMemberRestricted),
_ => throw new JsonSerializationException($"Unknown chat member status value of '{jo["status"]}'")
};
// Remove status because status property only has getter
jo.Remove("status");
var value = Activator.CreateInstance(actualType);
serializer.Populate(jo.CreateReader(), value);
return value!;
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.IO;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
namespace Telegram.Bot.Converters {
internal class InputFileConverter : JsonConverter {
public override bool CanConvert(Type objectType) =>
objectType.GetTypeInfo().IsSubclassOf(typeof(InputFileStream));
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
var input = (IInputFile)value;
switch(input.FileType) {
case FileType.Stream:
writer.WriteValue(null as object);
break;
case FileType.Id when value is InputTelegramFile file:
writer.WriteValue(file.FileId);
break;
case FileType.Url when value is InputOnlineFile file:
writer.WriteValue(file.Url);
break;
default:
throw new NotSupportedException("File Type is not supported");
}
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer) {
var value = JToken.ReadFrom(reader).Value<string>();
if(value is null) {
return new InputFileStream(Stream.Null);
}
return Uri.TryCreate(value, UriKind.Absolute, out _)
? new InputOnlineFile(value)
: new InputTelegramFile(value);
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace Telegram.Bot.Converters {
internal class InputMediaConverter : InputFileConverter {
public override bool CanConvert(Type objectType) => typeof(InputMedia) == objectType;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
var inputMediaType = (InputMedia)value;
switch(inputMediaType.FileType) {
case FileType.Id:
case FileType.Url:
base.WriteJson(writer, value, serializer);
break;
case FileType.Stream:
writer.WriteValue($"attach://{inputMediaType.FileName}");
break;
default:
throw new NotSupportedException("File Type not supported");
}
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer) {
var value = JToken.ReadFrom(reader).Value<string>();
if(value is null) {
return null!;
}
return value.StartsWith("attach://", StringComparison.InvariantCulture)
? new(Stream.Null, value.Substring(9))
: new InputMedia(value);
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace Telegram.Bot.Converters {
internal class MenuButtonConverter : JsonConverter {
static readonly TypeInfo BaseType = typeof(MenuButton).GetTypeInfo();
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType) =>
BaseType.IsAssignableFrom(objectType.GetTypeInfo());
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
var jo = JObject.FromObject(value);
jo.WriteTo(writer);
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer) {
var jo = JObject.Load(reader);
var typeToken = jo["type"];
var status = typeToken.ToObject<MenuButtonType>();
var actualType = status switch {
MenuButtonType.Default => typeof(MenuButtonDefault),
MenuButtonType.Commands => typeof(MenuButtonCommands),
MenuButtonType.WebApp => typeof(MenuButtonWebApp),
_ => throw new JsonSerializationException($"Unknown menu button type value of '{typeToken}'")
};
// Remove status because status property only has getter
jo.Remove("type");
var value = Activator.CreateInstance(actualType);
serializer.Populate(jo.CreateReader(), value);
return value!;
}
}
}

View File

@ -0,0 +1,99 @@
using System;
using Telegram.Bot.Types;
namespace Telegram.Bot.Exceptions {
/// <summary>
/// Represents an API error
/// </summary>
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
#pragma warning disable CA1032
public class ApiRequestException : RequestException
#pragma warning restore CA1032
{
/// <summary>
/// Gets the error code.
/// </summary>
public virtual int ErrorCode {
get;
}
/// <summary>
/// Contains information about why a request was unsuccessful.
/// </summary>
// ReSharper disable once UnusedAutoPropertyAccessor.Global
// ReSharper disable once MemberCanBePrivate.Global
public ResponseParameters? Parameters {
get;
}
/// <summary>
/// Initializes a new instance of the <see cref="ApiRequestException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public ApiRequestException(string message)
: base(message) {
}
/// <summary>
/// Initializes a new instance of the <see cref="ApiRequestException"/> class.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="errorCode">The error code.</param>
public ApiRequestException(string message, int errorCode)
: base(message) =>
ErrorCode = errorCode;
/// <summary>
/// Initializes a new instance of the <see cref="ApiRequestException"/> class.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">
/// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic)
/// if no inner exception is specified.
/// </param>
public ApiRequestException(string message, Exception innerException)
: base(message, innerException) {
}
/// <summary>
/// Initializes a new instance of the <see cref="ApiRequestException"/> class.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="errorCode">The error code.</param>
/// <param name="innerException">The inner exception.</param>
public ApiRequestException(string message, int errorCode, Exception innerException)
: base(message, innerException) =>
ErrorCode = errorCode;
/// <summary>
/// Initializes a new instance of the <see cref="ApiRequestException"/> class
/// </summary>
/// <param name="message">The message</param>
/// <param name="errorCode">The error code</param>
/// <param name="parameters">Response parameters</param>
public ApiRequestException(string message, int errorCode, ResponseParameters? parameters)
: base(message) {
ErrorCode = errorCode;
Parameters = parameters;
}
/// <summary>
/// Initializes a new instance of the <see cref="ApiRequestException"/> class
/// </summary>
/// <param name="message">The message</param>
/// <param name="errorCode">The error code</param>
/// <param name="parameters">Response parameters</param>
/// <param name="innerException">The inner exception</param>
public ApiRequestException(
string message,
int errorCode,
ResponseParameters? parameters,
Exception innerException)
: base(message, innerException) {
ErrorCode = errorCode;
Parameters = parameters;
}
}
}

View File

@ -0,0 +1,52 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types;
namespace Telegram.Bot.Exceptions {
/// <summary>
/// Represents failed API response
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class ApiResponse {
/// <summary>
/// Gets the error message.
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Description {
get; private set;
}
/// <summary>
/// Gets the error code.
/// </summary>
[JsonProperty(Required = Required.Always)]
public int ErrorCode {
get; private set;
}
/// <summary>
/// Contains information about why a request was unsuccessful.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public ResponseParameters? Parameters {
get; private set;
}
/// <summary>
/// Initializes an instance of <see cref="ApiResponse"/>
/// </summary>
/// <param name="errorCode">Error code</param>
/// <param name="description">Error message</param>
/// <param name="parameters">Information about why a request was unsuccessful</param>
public ApiResponse(
int errorCode,
string description,
ResponseParameters? parameters) {
ErrorCode = errorCode;
Description = description;
Parameters = parameters;
}
}
}

View File

@ -0,0 +1,23 @@
using System;
namespace Telegram.Bot.Exceptions {
/// <summary>
/// Default implementation of <see cref="IExceptionParser"/> that always returns <see cref="ApiRequestException"/>
/// </summary>
public class DefaultExceptionParser : IExceptionParser {
/// <inheritdoc />
public ApiRequestException Parse(ApiResponse apiResponse) {
if(apiResponse is null) {
throw new ArgumentNullException(nameof(apiResponse));
}
return new(
message: apiResponse.Description,
errorCode: apiResponse.ErrorCode,
parameters: apiResponse.Parameters
);
}
}
}

View File

@ -0,0 +1,15 @@
namespace Telegram.Bot.Exceptions {
/// <summary>
/// Parses unsuccessful responses from Telegram Bot API to make specific exceptions
/// </summary>
public interface IExceptionParser {
/// <summary>
/// Parses HTTP response and constructs a specific exception out of it
/// </summary>
/// <param name="apiResponse">ApiResponse with an error</param>
/// <returns></returns>
ApiRequestException Parse(ApiResponse apiResponse);
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Net;
namespace Telegram.Bot.Exceptions {
/// <summary>
/// Represents a request error
/// </summary>
#pragma warning disable CA1032
public class RequestException : Exception
#pragma warning restore CA1032
{
/// <summary>
/// <see cref="HttpStatusCode"/> of the received response
/// </summary>
public HttpStatusCode? HttpStatusCode {
get;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestException"/> class.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public RequestException(string message)
: base(message) {
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestException"/> class.
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
/// <param name="innerException">
/// The exception that is the cause of the current exception, or a null reference
/// (Nothing in Visual Basic) if no inner exception is specified.
/// </param>
public RequestException(string message, Exception innerException)
: base(message, innerException) {
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestException"/> class.
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
/// <param name="httpStatusCode">
/// <see cref="HttpStatusCode"/> of the received response
/// </param>
public RequestException(string message, HttpStatusCode httpStatusCode)
: base(message) =>
HttpStatusCode = httpStatusCode;
/// <summary>
/// Initializes a new instance of the <see cref="RequestException"/> class.
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
/// <param name="httpStatusCode">
/// <see cref="HttpStatusCode"/> of the received response
/// </param>
/// <param name="innerException">
/// The exception that is the cause of the current exception, or a null reference
/// (Nothing in Visual Basic) if no inner exception is specified.
/// </param>
public RequestException(string message, HttpStatusCode httpStatusCode, Exception innerException)
: base(message, innerException) =>
HttpStatusCode = httpStatusCode;
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Runtime.CompilerServices;
namespace Telegram.Bot.Extensions {
/// <summary>
/// Extension Methods
/// </summary>
internal static class ObjectExtensions {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static T ThrowIfNull<T>(this T? value, string parameterName) =>
value ?? throw new ArgumentNullException(parameterName);
}
}

View File

@ -0,0 +1,55 @@
using System.IO;
using System.Net.Http;
using System.Runtime.CompilerServices;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace Telegram.Bot.Extensions {
internal static class HttpContentExtensions {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void AddStreamContent(
this MultipartFormDataContent multipartContent,
Stream content,
string name,
string? fileName = default) {
fileName ??= name;
var contentDisposition = $@"form-data; name=""{name}""; filename=""{fileName}""".EncodeUtf8();
// It will be dispose of after the request is made
#pragma warning disable CA2000
var mediaPartContent = new StreamContent(content) {
Headers =
{
{"Content-Type", "application/octet-stream"},
{"Content-Disposition", contentDisposition}
}
};
#pragma warning restore CA2000
multipartContent.Add(mediaPartContent, name, fileName);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void AddContentIfInputFileStream(
this MultipartFormDataContent multipartContent,
params IInputMedia[] inputMedia) {
foreach(var input in inputMedia) {
if(input.Media.FileType == FileType.Stream) {
multipartContent.AddStreamContent(
content: input.Media.Content!,
name: input.Media.FileName!
);
}
if(input is IInputMediaThumb mediaThumb &&
mediaThumb.Thumb?.FileType == FileType.Stream) {
multipartContent.AddStreamContent(
content: mediaThumb.Thumb.Content!,
name: mediaThumb.Thumb.FileName!
);
}
}
}
}
}

View File

@ -0,0 +1,97 @@
using System;
using System.IO;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Telegram.Bot.Exceptions;
namespace Telegram.Bot.Extensions {
internal static class HttpResponseMessageExtensions {
/// <summary>
/// Deserialize body from HttpContent into <typeparamref name="T"/>
/// </summary>
/// <param name="httpResponse"><see cref="HttpResponseMessage"/> instance</param>
/// <param name="guard"></param>
/// <typeparam name="T">Type of the resulting object</typeparam>
/// <returns></returns>
/// <exception cref="RequestException">
/// Thrown when body in the response can not be deserialized into <typeparamref name="T"/>
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static async Task<T> DeserializeContentAsync<T>(
this HttpResponseMessage httpResponse,
Func<T, bool> guard)
where T : class {
Stream? contentStream = null;
if(httpResponse.Content is null) {
throw new RequestException(
message: "Response doesn't contain any content",
httpStatusCode: httpResponse.StatusCode
);
}
try {
T? deserializedObject;
try {
contentStream = await httpResponse.Content
.ReadAsStreamAsync()
.ConfigureAwait(continueOnCapturedContext: false);
deserializedObject = contentStream
.DeserializeJsonFromStream<T>();
} catch(Exception exception) {
throw CreateRequestException(
httpResponse: httpResponse,
message: "Required properties not found in response",
exception: exception
);
}
if(deserializedObject is null) {
throw CreateRequestException(
httpResponse: httpResponse,
message: "Required properties not found in response"
);
}
if(guard(deserializedObject)) {
throw CreateRequestException(
httpResponse: httpResponse,
message: "Required properties not found in response"
);
}
return deserializedObject;
} finally {
#if NETCOREAPP3_1_OR_GREATER
if(contentStream is not null) {
await contentStream.DisposeAsync().ConfigureAwait(false);
}
#else
contentStream?.Dispose();
#endif
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static RequestException CreateRequestException(
HttpResponseMessage httpResponse,
string message,
Exception? exception = default
) =>
exception is null
? new(
message: message,
httpStatusCode: httpResponse.StatusCode
)
: new(
message: message,
httpStatusCode: httpResponse.StatusCode,
innerException: exception
);
}
}

View File

@ -0,0 +1,31 @@
using System.IO;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
namespace Telegram.Bot.Extensions {
internal static class StreamExtensions {
/// <summary>
/// Deserialized JSON in Stream into <typeparamref name="T"/>
/// </summary>
/// <param name="stream"><see cref="Stream"/> with content</param>
/// <typeparam name="T">Type of the resulting object</typeparam>
/// <returns>Deserialized instance of <typeparamref name="T" /> or <c>null</c></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? DeserializeJsonFromStream<T>(this Stream? stream)
where T : class {
if(stream is null || !stream.CanRead) {
return default;
}
using var streamReader = new StreamReader(stream);
using var jsonTextReader = new JsonTextReader(streamReader);
var jsonSerializer = JsonSerializer.CreateDefault();
var searchResult = jsonSerializer.Deserialize<T>(jsonTextReader);
return searchResult;
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace Telegram.Bot.Extensions {
internal static class StringExtensions {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static string EncodeUtf8(this string value) =>
new(Encoding.UTF8.GetBytes(value).Select(c => Convert.ToChar(c)).ToArray());
}
}

View File

@ -0,0 +1,97 @@
using System;
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;
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".
/// Token format is not public API so this property is optional and may stop working
/// in the future if Telegram changes it's token format.
/// </summary>
long? BotId {
get;
}
/// <summary>
/// Timeout for requests
/// </summary>
TimeSpan Timeout {
get; set;
}
/// <summary>
/// Instance of <see cref="IExceptionParser"/> to parse errors from Bot API into
/// <see cref="ApiRequestException"/>
/// </summary>
/// <remarks>This property is not thread safe</remarks>
IExceptionParser ExceptionsParser {
get; set;
}
/// <summary>
/// Occurs before sending a request to API
/// </summary>
event AsyncEventHandler<ApiRequestEventArgs>? OnMakingApiRequest;
/// <summary>
/// Occurs after receiving the response to an API request
/// </summary>
event AsyncEventHandler<ApiResponseEventArgs>? OnApiResponseReceived;
/// <summary>
/// Send a request to Bot API
/// </summary>
/// <typeparam name="TResponse">Type of expected result in the response object</typeparam>
/// <param name="request">API request object</param>
/// <param name="cancellationToken"></param>
/// <returns>Result of the API request</returns>
Task<TResponse> MakeRequestAsync<TResponse>(
IRequest<TResponse> request,
CancellationToken cancellationToken = default
);
/// <summary>
/// Test the API token
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns><c>true</c> if token is valid</returns>
Task<bool> TestApiAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Use this method to download a file. Get <paramref name="filePath"/> by calling
/// <see cref="TelegramBotClientExtensions.GetFileAsync(ITelegramBotClient, string, CancellationToken)"/>
/// </summary>
/// <param name="filePath">Path to file on server</param>
/// <param name="destination">Destination stream to write file to</param>
/// <param name="cancellationToken">
/// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
/// </param>
/// <exception cref="ArgumentException">filePath is <c>null</c>, empty or too short</exception>
/// <exception cref="ArgumentNullException"><paramref name="destination"/> is <c>null</c></exception>
Task DownloadFileAsync(
string filePath,
Stream destination,
CancellationToken cancellationToken = default
);
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Threading;
using System.Threading.Tasks;
//using JetBrains.Annotations;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Polling {
/// <summary>
/// Processes <see cref="Update"/>s and errors.
/// <para>See <see cref="DefaultUpdateHandler"/> for a simple implementation</para>
/// </summary>
//[PublicAPI]
public interface IUpdateHandler {
/// <summary>
/// Handles an <see cref="Update"/>
/// </summary>
/// <param name="botClient">
/// The <see cref="ITelegramBotClient"/> instance of the bot receiving the <see cref="Update"/>
/// </param>
/// <param name="update">The <see cref="Update"/> to handle</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> which will notify that method execution should be cancelled
/// </param>
/// <returns></returns>
Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken);
/// <summary>
/// Handles an <see cref="Exception"/>
/// </summary>
/// <param name="botClient">
/// The <see cref="ITelegramBotClient"/> instance of the bot receiving the <see cref="Exception"/>
/// </param>
/// <param name="exception">The <see cref="Exception"/> to handle</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> which will notify that method execution should be cancelled
/// </param>
/// <returns></returns>
Task HandlePollingErrorAsync(
ITelegramBotClient botClient,
Exception exception,
CancellationToken cancellationToken
);
}
}

View File

@ -0,0 +1,35 @@
using System.Threading;
using System.Threading.Tasks;
//using JetBrains.Annotations;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Polling {
/// <summary>
/// Requests new <see cref="Update"/>s and processes them using provided <see cref="IUpdateHandler"/> instance
/// </summary>
//[PublicAPI]
public interface IUpdateReceiver {
/// <summary>
/// Starts receiving <see cref="Update"/>s invoking <see cref="IUpdateHandler.HandleUpdateAsync"/>
/// for each <see cref="Update"/>.
/// <para>This method will block if awaited.</para>
/// </summary>
/// <param name="updateHandler">
/// The <see cref="IUpdateHandler"/> used for processing <see cref="Update"/>s
/// </param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> with which you can stop receiving
/// </param>
/// <returns>
/// A <see cref="Task"/> that will be completed when cancellation will be requested through
/// <paramref name="cancellationToken"/>
/// </returns>
Task ReceiveAsync(
IUpdateHandler updateHandler,
CancellationToken cancellationToken = default
);
}
}

View File

@ -0,0 +1,64 @@
using System;
//using JetBrains.Annotations;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Polling {
/// <summary>
/// Options to configure getUpdates requests
/// </summary>
//[PublicAPI]
public sealed class ReceiverOptions {
int? _limit;
/// <summary>
/// Identifier of the first update to be returned. Will be ignored if
/// <see cref="ThrowPendingUpdates"/> is set to <c>true</c>.
/// </summary>
public int? Offset {
get; set;
}
/// <summary>
/// Indicates which <see cref="UpdateType"/>s are allowed to be received.
/// In case of <c>null</c> the previous setting will be used
/// </summary>
public UpdateType[]? AllowedUpdates {
get; set;
}
/// <summary>
/// Limits the number of updates to be retrieved. Values between 1-100 are accepted.
/// Defaults to 100 when is set to <c>null</c>.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the value doesn't satisfies constraints
/// </exception>
public int? Limit {
get => _limit;
set {
if(value is < 1 or > 100) {
throw new ArgumentOutOfRangeException(
paramName: nameof(value),
actualValue: value,
message: $"'{nameof(Limit)}' can not be less than 1 or greater than 100"
);
}
_limit = value;
}
}
/// <summary>
/// Indicates if all pending <see cref="Update"/>s should be thrown out before start
/// polling. If set to <c>true</c> <see cref="AllowedUpdates"/> should be set to not
/// <c>null</c>, otherwise <see cref="AllowedUpdates"/> will effectively be set to
/// receive all <see cref="Update"/>s.
/// </summary>
public bool ThrowPendingUpdates {
get; set;
}
}
}

View File

@ -0,0 +1,143 @@
#if NETCOREAPP3_1_OR_GREATER
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
//using JetBrains.Annotations;
using Telegram.Bot.Requests;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Polling {
/// <summary>
/// Supports asynchronous iteration over <see cref="Update"/>s
/// </summary>
//[PublicAPI]
public class BlockingUpdateReceiver : IAsyncEnumerable<Update> {
readonly ReceiverOptions? _receiverOptions;
readonly ITelegramBotClient _botClient;
readonly Func<Exception, CancellationToken, Task>? _pollingErrorHandler;
int _inProcess;
/// <summary>
/// Constructs a new <see cref="BlockingUpdateReceiver"/> for the specified <see cref="ITelegramBotClient"/>
/// </summary>
/// <param name="botClient">The <see cref="ITelegramBotClient"/> used for making GetUpdates calls</param>
/// <param name="receiverOptions"></param>
/// <param name="pollingErrorHandler">
/// The function used to handle <see cref="Exception"/>s thrown by ReceiveUpdates
/// </param>
public BlockingUpdateReceiver(
ITelegramBotClient botClient,
ReceiverOptions? receiverOptions = default,
Func<Exception, CancellationToken, Task>? pollingErrorHandler = default) {
_botClient = botClient ?? throw new ArgumentNullException(nameof(botClient));
_receiverOptions = receiverOptions;
_pollingErrorHandler = pollingErrorHandler;
}
/// <summary>
/// Gets the <see cref="IAsyncEnumerator{Update}"/>. This method may only be called once.
/// </summary>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> with which you can stop receiving
/// </param>
public IAsyncEnumerator<Update> GetAsyncEnumerator(CancellationToken cancellationToken = default) {
if(Interlocked.CompareExchange(ref _inProcess, 1, 0) == 1) {
throw new InvalidOperationException(nameof(GetAsyncEnumerator) + " may only be called once");
}
return new Enumerator(receiver: this, cancellationToken: cancellationToken);
}
class Enumerator : IAsyncEnumerator<Update> {
readonly BlockingUpdateReceiver _receiver;
readonly CancellationTokenSource _cts;
readonly CancellationToken _token;
readonly UpdateType[]? _allowedUpdates;
readonly int? _limit;
Update[] _updateArray = Array.Empty<Update>();
int _updateIndex;
int _messageOffset;
bool _updatesThrown;
public Enumerator(BlockingUpdateReceiver receiver, CancellationToken cancellationToken) {
_receiver = receiver;
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default);
_token = _cts.Token;
_messageOffset = receiver._receiverOptions?.Offset ?? 0;
_limit = receiver._receiverOptions?.Limit ?? default;
_allowedUpdates = receiver._receiverOptions?.AllowedUpdates;
}
public ValueTask<bool> MoveNextAsync() {
_token.ThrowIfCancellationRequested();
_updateIndex += 1;
return _updateIndex < _updateArray.Length
? new(true)
: new(ReceiveUpdatesAsync());
}
async Task<bool> ReceiveUpdatesAsync() {
var shouldThrowPendingUpdates = (
_updatesThrown,
_receiver._receiverOptions?.ThrowPendingUpdates ?? false
);
if(shouldThrowPendingUpdates is (false, true)) {
try {
_messageOffset = await _receiver._botClient.ThrowOutPendingUpdatesAsync(
cancellationToken: _token
).ConfigureAwait(false);
} catch(OperationCanceledException) {
// ignored
} finally {
_updatesThrown = true;
}
}
_updateArray = Array.Empty<Update>();
_updateIndex = 0;
while(_updateArray.Length == 0) {
try {
_updateArray = await _receiver._botClient
.MakeRequestAsync(
request: new GetUpdatesRequest {
Offset = _messageOffset,
Timeout = (int)_receiver._botClient.Timeout.TotalSeconds,
Limit = _limit,
AllowedUpdates = _allowedUpdates,
},
cancellationToken: _token
)
.ConfigureAwait(false);
} catch(OperationCanceledException) {
throw;
} catch(Exception ex) when(_receiver._pollingErrorHandler is not null) {
await _receiver._pollingErrorHandler(ex, _token).ConfigureAwait(false);
}
}
_messageOffset = _updateArray[^1].Id + 1;
return true;
}
public Update Current => _updateArray[_updateIndex];
public ValueTask DisposeAsync() {
_cts.Cancel();
_cts.Dispose();
return new();
}
}
}
}
#endif

View File

@ -0,0 +1,215 @@
#if NETCOREAPP3_1_OR_GREATER
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
//using JetBrains.Annotations;
using Telegram.Bot.Requests;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Polling {
/// <summary>
/// Supports asynchronous iteration over <see cref="Update"/>s.
/// <para>Updates are received on a different thread and enqueued.</para>
/// </summary>
//[PublicAPI]
public class QueuedUpdateReceiver : IAsyncEnumerable<Update> {
readonly ITelegramBotClient _botClient;
readonly ReceiverOptions? _receiverOptions;
readonly Func<Exception, CancellationToken, Task>? _pollingErrorHandler;
int _inProcess;
Enumerator? _enumerator;
/// <summary>
/// Constructs a new <see cref="QueuedUpdateReceiver"/> for the specified <see cref="ITelegramBotClient"/>
/// </summary>
/// <param name="botClient">The <see cref="ITelegramBotClient"/> used for making GetUpdates calls</param>
/// <param name="receiverOptions"></param>
/// <param name="pollingErrorHandler">
/// The function used to handle <see cref="Exception"/>s thrown by GetUpdates requests
/// </param>
public QueuedUpdateReceiver(
ITelegramBotClient botClient,
ReceiverOptions? receiverOptions = default,
Func<Exception, CancellationToken, Task>? pollingErrorHandler = default) {
_botClient = botClient ?? throw new ArgumentNullException(nameof(botClient));
_receiverOptions = receiverOptions;
_pollingErrorHandler = pollingErrorHandler;
}
/// <summary>
/// Indicates how many <see cref="Update"/>s are ready to be returned the enumerator
/// </summary>
public int PendingUpdates => _enumerator?.PendingUpdates ?? 0;
/// <summary>
/// Gets the <see cref="IAsyncEnumerator{Update}"/>. This method may only be called once.
/// </summary>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> with which you can stop receiving
/// </param>
public IAsyncEnumerator<Update> GetAsyncEnumerator(CancellationToken cancellationToken = default) {
if(Interlocked.CompareExchange(ref _inProcess, 1, 0) == 1) {
throw new InvalidOperationException(nameof(GetAsyncEnumerator) + " may only be called once");
}
_enumerator = new(receiver: this, cancellationToken: cancellationToken);
return _enumerator;
}
class Enumerator : IAsyncEnumerator<Update> {
readonly QueuedUpdateReceiver _receiver;
readonly CancellationTokenSource _cts;
readonly CancellationToken _token;
readonly UpdateType[]? _allowedUpdates;
readonly int? _limit;
Exception? _uncaughtException;
readonly Channel<Update> _channel;
Update? _current;
int _pendingUpdates;
int _messageOffset;
public int PendingUpdates => _pendingUpdates;
public Enumerator(QueuedUpdateReceiver receiver, CancellationToken cancellationToken) {
_receiver = receiver;
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default);
_token = _cts.Token;
_messageOffset = receiver._receiverOptions?.Offset ?? 0;
_limit = receiver._receiverOptions?.Limit ?? default;
_allowedUpdates = receiver._receiverOptions?.AllowedUpdates;
_channel = Channel.CreateUnbounded<Update>(
new() {
SingleReader = true,
SingleWriter = true
}
);
#pragma warning disable CA2016
Task.Run(ReceiveUpdatesAsync);
#pragma warning restore CA2016
}
public ValueTask<bool> MoveNextAsync() {
if(_uncaughtException is not null) {
throw _uncaughtException;
}
_token.ThrowIfCancellationRequested();
if(_channel.Reader.TryRead(out _current)) {
Interlocked.Decrement(ref _pendingUpdates);
return new(true);
}
return new(ReadAsync());
}
async Task<bool> ReadAsync() {
_current = await _channel.Reader.ReadAsync(_token).ConfigureAwait(false);
Interlocked.Decrement(ref _pendingUpdates);
return true;
}
async Task ReceiveUpdatesAsync() {
if(_receiver._receiverOptions?.ThrowPendingUpdates is true) {
try {
_messageOffset = await _receiver._botClient.ThrowOutPendingUpdatesAsync(
cancellationToken: _token
).ConfigureAwait(false);
} catch(OperationCanceledException) {
// ignored
}
}
while(!_cts.IsCancellationRequested) {
try {
Update[] updateArray = await _receiver._botClient
.MakeRequestAsync(
request: new GetUpdatesRequest {
Offset = _messageOffset,
Timeout = (int)_receiver._botClient.Timeout.TotalSeconds,
AllowedUpdates = _allowedUpdates,
Limit = _limit,
},
cancellationToken: _token
)
.ConfigureAwait(false);
if(updateArray.Length > 0) {
_messageOffset = updateArray[^1].Id + 1;
Interlocked.Add(ref _pendingUpdates, updateArray.Length);
ChannelWriter<Update> writer = _channel.Writer;
foreach(Update update in updateArray) {
// ReSharper disable once RedundantAssignment
var success = writer.TryWrite(update);
Debug.Assert(success, "TryWrite should succeed as we are using an unbounded channel");
}
}
} catch(OperationCanceledException) {
// Ignore
}
#pragma warning disable CA1031
catch(Exception ex)
#pragma warning restore CA1031
{
Debug.Assert(_uncaughtException is null);
// If there is no errorHandler or the errorHandler throws, stop receiving
if(_receiver._pollingErrorHandler is null) {
_uncaughtException = ex;
_cts.Cancel();
} else {
try {
await _receiver._pollingErrorHandler(ex, _token).ConfigureAwait(false);
}
#pragma warning disable CA1031
catch(Exception errorHandlerException)
#pragma warning restore CA1031
{
_uncaughtException = new AggregateException(
message: "Exception was not caught by the errorHandler.",
ex,
errorHandlerException
);
_cts.Cancel();
}
}
if(_uncaughtException is not null) {
#pragma warning disable CA2201
_uncaughtException = new(
message: "Exception was not caught by the errorHandler.",
innerException: _uncaughtException
);
#pragma warning restore CA2201
}
}
}
}
public Update Current => _current!; // _current being null indicates MoveNextAsync was never called
public ValueTask DisposeAsync() {
_cts.Cancel();
_cts.Dispose();
return new();
}
}
}
}
#endif

View File

@ -0,0 +1,46 @@
using System;
using System.Threading;
using System.Threading.Tasks;
//using JetBrains.Annotations;
using Telegram.Bot.Types;
namespace Telegram.Bot.Polling {
/// <summary>
/// A very simple <see cref="IUpdateHandler"/> implementation
/// </summary>
//[PublicAPI]
public class DefaultUpdateHandler : IUpdateHandler {
readonly Func<ITelegramBotClient, Update, CancellationToken, Task> _updateHandler;
readonly Func<ITelegramBotClient, Exception, CancellationToken, Task> _pollingErrorHandler;
/// <summary>
/// Constructs a new <see cref="DefaultUpdateHandler"/> with the specified callback functions
/// </summary>
/// <param name="updateHandler">The function to invoke when an update is received</param>
/// <param name="pollingErrorHandler">The function to invoke when an error occurs</param>
public DefaultUpdateHandler(
Func<ITelegramBotClient, Update, CancellationToken, Task> updateHandler,
Func<ITelegramBotClient, Exception, CancellationToken, Task> pollingErrorHandler) {
_updateHandler = updateHandler ?? throw new ArgumentNullException(nameof(updateHandler));
_pollingErrorHandler = pollingErrorHandler ?? throw new ArgumentNullException(nameof(pollingErrorHandler));
}
/// <inheritdoc />
public async Task HandleUpdateAsync(
ITelegramBotClient botClient,
Update update,
CancellationToken cancellationToken
) =>
await _updateHandler(botClient, update, cancellationToken).ConfigureAwait(false);
/// <inheritdoc />
public async Task HandlePollingErrorAsync(
ITelegramBotClient botClient,
Exception exception,
CancellationToken cancellationToken
) =>
await _pollingErrorHandler(botClient, exception, cancellationToken).ConfigureAwait(false);
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Threading;
using System.Threading.Tasks;
//using JetBrains.Annotations;
using Telegram.Bot.Requests;
using Telegram.Bot.Types;
namespace Telegram.Bot.Polling {
/// <summary>
/// A simple <see cref="IUpdateReceiver"/>> implementation that requests new updates and handles them sequentially
/// </summary>
//[PublicAPI]
public class DefaultUpdateReceiver : IUpdateReceiver {
static readonly Update[] EmptyUpdates = Array.Empty<Update>();
readonly ITelegramBotClient _botClient;
readonly ReceiverOptions? _receiverOptions;
/// <summary>
/// Constructs a new <see cref="DefaultUpdateReceiver"/> with the specified <see cref="ITelegramBotClient"/>>
/// instance and optional <see cref="ReceiverOptions"/>
/// </summary>
/// <param name="botClient">The <see cref="ITelegramBotClient"/> used for making GetUpdates calls</param>
/// <param name="receiverOptions">Options used to configure getUpdates requests</param>
public DefaultUpdateReceiver(
ITelegramBotClient botClient,
ReceiverOptions? receiverOptions = default) {
_botClient = botClient ?? throw new ArgumentNullException(nameof(botClient));
_receiverOptions = receiverOptions;
}
/// <inheritdoc />
public async Task ReceiveAsync(
IUpdateHandler updateHandler,
CancellationToken cancellationToken = default) {
if(updateHandler is null) {
throw new ArgumentNullException(nameof(updateHandler));
}
var allowedUpdates = _receiverOptions?.AllowedUpdates;
var limit = _receiverOptions?.Limit ?? default;
var messageOffset = _receiverOptions?.Offset ?? 0;
var emptyUpdates = EmptyUpdates;
if(_receiverOptions?.ThrowPendingUpdates is true) {
try {
messageOffset = await _botClient.ThrowOutPendingUpdatesAsync(
cancellationToken: cancellationToken
).ConfigureAwait(false);
} catch(OperationCanceledException) {
// ignored
}
}
while(!cancellationToken.IsCancellationRequested) {
var timeout = (int)_botClient.Timeout.TotalSeconds;
var updates = emptyUpdates;
try {
var request = new GetUpdatesRequest {
Limit = limit,
Offset = messageOffset,
Timeout = timeout,
AllowedUpdates = allowedUpdates,
};
updates = await _botClient.MakeRequestAsync(
request: request,
cancellationToken:
cancellationToken
).ConfigureAwait(false);
} catch(OperationCanceledException) {
// Ignore
}
#pragma warning disable CA1031
catch(Exception exception)
#pragma warning restore CA1031
{
try {
await updateHandler.HandlePollingErrorAsync(
botClient: _botClient,
exception: exception,
cancellationToken: cancellationToken
).ConfigureAwait(false);
} catch(OperationCanceledException) {
// ignored
}
}
foreach(var update in updates) {
try {
await updateHandler.HandleUpdateAsync(
botClient: _botClient,
update: update,
cancellationToken: cancellationToken
).ConfigureAwait(false);
messageOffset = update.Id + 1;
} catch(OperationCanceledException) {
// ignored
}
}
}
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot.Requests;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace Telegram.Bot.Polling {
internal static class TelegramBotClientExtensions {
/// <summary>
/// Will attempt to throw the last update using offset set to -1.
/// </summary>
/// <param name="botClient"></param>
/// <param name="cancellationToken"></param>
/// <returns>
/// Update ID of the last <see cref="Update"/> increased by 1 if there were any
/// </returns>
internal static async Task<int> ThrowOutPendingUpdatesAsync(
this ITelegramBotClient botClient,
CancellationToken cancellationToken = default) {
var request = new GetUpdatesRequest {
Limit = 1,
Offset = -1,
Timeout = 0,
AllowedUpdates = Array.Empty<UpdateType>(),
};
var updates = await botClient.MakeRequestAsync(request: request, cancellationToken: cancellationToken)
.ConfigureAwait(false);
#if NETCOREAPP3_1_OR_GREATER
if(updates.Length > 0) {
return updates[^1].Id + 1;
}
#else
if (updates.Length > 0) { return updates[updates.Length - 1].Id + 1; }
#endif
return 0;
}
}
}

View File

@ -0,0 +1,6 @@
using System;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Telegram.Bot.Tests.Unit")]
[assembly: InternalsVisibleTo("Telegram.Bot.Tests.Integ")]
[assembly: CLSCompliant(false)]

View File

@ -0,0 +1,83 @@
#nullable disable
#pragma warning disable 169
#pragma warning disable CA1823
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable InconsistentNaming
namespace Telegram.Bot.Requests.Abstractions {
// ReSharper disable once UnusedType.Global
internal abstract class Documentation {
Documentation() {
}
/// <summary>
/// List of special entities that appear in the caption, which can be specified instead of
/// <see cref="Types.Enums.ParseMode"/>
/// </summary>
object CaptionEntities;
/// <summary>
/// List of special entities that appear in message text, which can be specified instead of
/// <see cref="Types.Enums.ParseMode"/>
/// </summary>
object Entities;
/// <summary>
/// Mode for parsing entities in the new caption. See
/// <a href="https://core.telegram.org/bots/api#formatting-options">formatting</a>
/// options for more details.
/// </summary>
object ParseMode;
/// <summary>
/// Identifier of the inline message
/// </summary>
object InlineMessageId;
/// <summary>
/// An <see cref="InlineKeyboardMarkup">inline keyboard</see>
/// </summary>
object InlineReplyMarkup;
/// <summary>
/// Additional interface options. An <see cref="InlineKeyboardMarkup">inline keyboard</see>,
/// <see cref="ReplyKeyboardMarkup">custom reply keyboard</see>, instructions to
/// <see cref="ReplyKeyboardRemove">remove reply keyboard</see> or to
/// <see cref="ForceReplyMarkup">force a reply</see> from the user.
/// </summary>
object ReplyMarkup;
/// <summary>
/// Sends the message silently. Users will receive a notification with no sound.
/// </summary>
object DisableNotification;
/// <summary>
/// If the message is a reply, ID of the original message
/// </summary>
object ReplyToMessageId;
/// <summary>
/// Pass <c>true</c>, if the message should be sent even if the specified replied-to message is not found
/// </summary>
object AllowSendingWithoutReply;
/// <summary>
/// Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported
/// server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's
/// width and height should not exceed 320. Ignored if the file is not uploaded using
/// multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so
/// you can pass "attach://&lt;file_attach_name&gt;" if the thumbnail was uploaded using
/// multipart/form-data under &lt;file_attach_name&gt;
/// </summary>
object Thumb;
/// <summary>
/// Protects the contents of sent messages from forwarding and saving
/// </summary>
object ProtectContent;
}
}

View File

@ -0,0 +1,18 @@
using Telegram.Bot.Types;
namespace Telegram.Bot.Requests.Abstractions {
/// <summary>
/// Represents a request having <see cref="ChatId"/> parameter
/// </summary>
public interface IChatTargetable {
/// <summary>
/// Unique identifier for the target chat or username of the target channel
/// (in the format @channelusername)
/// </summary>
ChatId ChatId {
get;
}
}
}

View File

@ -0,0 +1,38 @@
using System.Net.Http;
// ReSharper disable once UnusedTypeParameter
namespace Telegram.Bot.Requests.Abstractions {
/// <summary>
/// Represents a request to Bot API
/// </summary>
public interface IRequest {
/// <summary>
/// HTTP method of request
/// </summary>
HttpMethod Method {
get;
}
/// <summary>
/// API method name
/// </summary>
string MethodName {
get;
}
/// <summary>
/// Allows this object to be used as a response in webhooks
/// </summary>
bool IsWebhookResponse {
get; set;
}
/// <summary>
/// Generate content of HTTP message
/// </summary>
/// <returns>Content of HTTP request</returns>
HttpContent? ToHttpContent();
}
}

View File

@ -0,0 +1,11 @@
namespace Telegram.Bot.Requests.Abstractions {
/// <summary>
/// Represents a request to Bot API
/// </summary>
/// <typeparam name="TResponse">Type of result expected in result</typeparam>
// ReSharper disable once UnusedTypeParameter
public interface IRequest<TResponse> : IRequest {
}
}

View File

@ -0,0 +1,15 @@
namespace Telegram.Bot.Requests.Abstractions {
/// <summary>
/// Represents a request having <see cref="UserId"/> parameter
/// </summary>
public interface IUserTargetable {
/// <summary>
/// User identifier
/// </summary>
long UserId {
get;
}
}
}

View File

@ -0,0 +1,79 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send answers to callback queries sent from
/// <see cref="Types.ReplyMarkups.InlineKeyboardMarkup">inline keyboards</see>. The answer will be
/// displayed to the user as a notification at the top of the chat screen or as an alert. On success,
/// <c>true</c> is returned.
/// </summary>
/// <remarks>
/// Alternatively, the user can be redirected to the specified Game URL.For this option to work, you
/// must first create a game for your bot via <c>@Botfather</c> and accept the terms. Otherwise, you
/// may use links like <c>t.me/your_bot? start = XXXX</c> that open your bot with a parameter.
/// </remarks>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class AnswerCallbackQueryRequest : RequestBase<bool> {
/// <summary>
/// Unique identifier for the query to be answered
/// </summary>
[JsonProperty(Required = Required.Always)]
public string CallbackQueryId {
get;
}
/// <summary>
/// Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Text {
get; set;
}
/// <summary>
/// If true, an alert will be shown by the client instead of a notification at the top of
/// the chat screen. Defaults to <c>false</c>
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ShowAlert {
get; set;
}
/// <summary>
/// URL that will be opened by the user's client. If you have created a
/// <a href="https://core.telegram.org/bots/api#game">Game</a> and accepted the conditions
/// via <c>@Botfather</c>, specify the URL that opens your game — note that this will only work
/// if the query comes from a callback_game button.
/// <para>
/// Otherwise, you may use links like <c>t.me/your_bot? start = XXXX</c> that open your bot with
/// a parameter
/// </para>
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Url {
get; set;
}
/// <summary>
/// The maximum amount of time in seconds that the result of the callback query may be cached
/// client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? CacheTime {
get; set;
}
/// <summary>
/// Initializes a new request with callbackQueryId
/// </summary>
/// <param name="callbackQueryId">Unique identifier for the query to be answered</param>
public AnswerCallbackQueryRequest(string callbackQueryId)
: base("answerCallbackQuery") {
CallbackQueryId = callbackQueryId;
}
}
}

View File

@ -0,0 +1,43 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to delete the list of the bots commands for the given
/// <see cref="Scope">scope</see> and <see cref="LanguageCode">user language</see>. After deletion,
/// <a href="https://core.telegram.org/bots/api#determining-list-of-commands">higher level commands</a>
/// will be shown to affected users. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class DeleteMyCommandsRequest : RequestBase<bool> {
/// <summary>
/// An object, describing scope of users for which the commands are relevant.
/// Defaults to <see cref="BotCommandScopeDefault"/>.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public BotCommandScope? Scope {
get; set;
}
/// <summary>
/// A two-letter ISO 639-1 language code. If empty, commands will be applied to all users
/// from the given <see cref="Scope">Scope</see>, for whose language there are no dedicated
/// commands
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? LanguageCode {
get; set;
}
/// <summary>
/// Initializes a new request
/// </summary>
public DeleteMyCommandsRequest()
: base("deleteMyCommands") {
}
}
}

View File

@ -0,0 +1,39 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get the current list of the bots commands for the given <see cref="Scope">scope</see>
/// and <see cref="LanguageCode">user language</see>. Returns Array of <see cref="BotCommand"/> on success.
/// If commands aren't set, an empty list is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetMyCommandsRequest : RequestBase<BotCommand[]> {
/// <summary>
/// An object, describing scope of users. Defaults to <see cref="BotCommandScopeDefault"/>.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public BotCommandScope? Scope {
get; set;
}
/// <summary>
/// A two-letter ISO 639-1 language code or an empty string
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? LanguageCode {
get; set;
}
/// <summary>
/// Initializes a new request
/// </summary>
public GetMyCommandsRequest()
: base("getMyCommands") {
}
}
}

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to change the list of the bots commands. See
/// <a href="https://core.telegram.org/bots#commands"/> for more details about bot commands.
/// Returns <c>true</c> on success
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SetMyCommandsRequest : RequestBase<bool> {
/// <summary>
/// A list of bot commands to be set as the list of the bots commands.
/// At most 100 commands can be specified.
/// </summary>
[JsonProperty(Required = Required.Always)]
public IEnumerable<BotCommand> Commands {
get;
}
/// <summary>
/// An object, describing scope of users for which the commands are relevant.
/// Defaults to <see cref="BotCommandScopeDefault"/>.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public BotCommandScope? Scope {
get; set;
}
/// <summary>
/// A two-letter ISO 639-1 language code. If empty, commands will be applied to all users
/// from the given <see cref="Scope"/>, for whose language there are no dedicated commands
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? LanguageCode {
get; set;
}
/// <summary>
/// Initializes a new request with commands
/// </summary>
/// <param name="commands">A list of bot commands to be set</param>
public SetMyCommandsRequest(IEnumerable<BotCommand> commands)
: base("setMyCommands") {
Commands = commands;
}
}
}

View File

@ -0,0 +1,41 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get basic info about a file and prepare it for downloading. For the moment,
/// bots can download files of up to 20MB in size. On success, a <see cref="File"/> object is
/// returned. The file can then be downloaded via the link
/// <c>https://api.telegram.org/file/bot&lt;token&gt;/&lt;file_path&gt;</c>, where
/// <c>&lt;file_path&gt;</c> is taken from the response. It is guaranteed that the link will be valid
/// for at least 1 hour. When the link expires, a new one can be requested by calling
/// <see cref="GetFileRequest"/> again.
/// </summary>
/// <remarks>
/// You can use <see cref="ITelegramBotClient.DownloadFileAsync"/> or
/// <see cref="TelegramBotClientExtensions.GetInfoAndDownloadFileAsync"/> methods to download the file
/// </remarks>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetFileRequest : RequestBase<File> {
/// <summary>
/// File identifier to get info about
/// </summary>
[JsonProperty(Required = Required.Always)]
public string FileId {
get;
}
/// <summary>
/// Initializes a new request with <see cref="FileId"/>
/// </summary>
/// <param name="fileId">File identifier to get info about</param>
public GetFileRequest(string fileId)
: base("getFile") {
FileId = fileId;
}
}
}

View File

@ -0,0 +1,47 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get a list of profile pictures for a user. Returns a
/// <see cref="UserProfilePhotos"/> object.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetUserProfilePhotosRequest : RequestBase<UserProfilePhotos>, IUserTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// Sequential number of the first photo to be returned. By default, all photos are returned
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Offset {
get; set;
}
/// <summary>
/// Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to 100
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Limit {
get; set;
}
/// <summary>
/// Initializes a new request with userId
/// </summary>
/// <param name="userId">Unique identifier of the target user</param>
public GetUserProfilePhotosRequest(long userId)
: base("getUserProfilePhotos") {
UserId = userId;
}
}
}

View File

@ -0,0 +1,31 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get the current value of the bots menu button in a private chat, or the default menu button.
/// Returns <see cref="MenuButton"/> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetChatMenuButtonRequest : RequestBase<MenuButton> {
/// <summary>
/// Optional. Unique identifier for the target private chat. If not specified, default bots menu button
/// will be changed
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public long? ChatId {
get; set;
}
/// <summary>
/// Initializes a new request
/// </summary>
public GetChatMenuButtonRequest()
: base("getChatMenuButton") {
}
}
}

View File

@ -0,0 +1,22 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// A simple method for testing your bots auth token. Requires no parameters. Returns basic information
/// about the bot in form of a <see cref="User"/> object.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetMeRequest : ParameterlessRequest<User> {
/// <summary>
/// Initializes a new request
/// </summary>
public GetMeRequest()
: base("getMe") {
}
}
}

View File

@ -0,0 +1,31 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get the current default administrator rights of the bot.
/// Returns <see cref="ChatAdministratorRights"/> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetMyDefaultAdministratorRightsRequest : RequestBase<ChatAdministratorRights> {
/// <summary>
/// Pass <c>true</c> to get default administrator rights of the bot in channels. Otherwise, default administrator
/// rights of the bot for groups and supergroups will be returned.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ForChannels {
get; set;
}
/// <summary>
///
/// </summary>
public GetMyDefaultAdministratorRightsRequest()
: base("getMyDefaultAdministratorRights") {
}
}
}

View File

@ -0,0 +1,23 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to close the bot instance before moving it from one local server to another.
/// You need to delete the webhook before calling this method to ensure that the bot isn't launched
/// again after server restart. The method will return error 429 in the first 10 minutes after the
/// bot is launched. Returns True on success. Requires no parameters.
/// </summary>
/// <a href="https://core.telegram.org/bots/api#close"/>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class CloseRequest : ParameterlessRequest<bool> {
/// <summary>
/// Initializes a new request
/// </summary>
public CloseRequest() : base("close") {
}
}
}

View File

@ -0,0 +1,20 @@
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to log out from the cloud Bot API server before launching the bot locally.
/// You <b>must</b> log out the bot before running it locally, otherwise there is no guarantee
/// that the bot will receive updates. After a successful call, you can immediately log in on
/// a local server, but will not be able to log in back to the cloud Bot API server for 10
/// minutes. Returns <c>true</c> on success. Requires no parameters.
/// </summary>
/// <a href="https://core.telegram.org/bots/api#logout"/>
public class LogOutRequest : ParameterlessRequest<bool> {
/// <summary>
/// Initializes a new request
/// </summary>
public LogOutRequest() : base("logOut") {
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to ban a user in a group, a supergroup or a channel. In the case of supergroups
/// and channels, the user will not be able to return to the chat on their own using invite links,
/// etc., unless <see cref="UnbanChatMemberRequest">unbanned</see> first. The bot must be an
/// administrator in the chat for this to work and must have the appropriate admin rights.
/// Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class BanChatMemberRequest : RequestBase<bool>, IChatTargetable, IUserTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// Date when the user will be unbanned. If user is banned for more than 366 days or less
/// than 30 seconds from the current time they are considered to be banned forever.
/// Applied for supergroups and channels only.
/// </summary>
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTime? UntilDate {
get; set;
}
/// <summary>
/// Pass True to delete all messages from the chat for the user that is being removed. If
/// <c>false</c>, the user will be able to see messages in the group that were sent before
/// the user was removed. Always True for supergroups and channels.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? RevokeMessages {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and userId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
public BanChatMemberRequest(ChatId chatId, long userId)
: base("banChatMember") {
ChatId = chatId;
UserId = userId;
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this request to ban a channel chat in a supergroup or a channel. The owner of the chat will not be able
/// to send messages and join live streams on behalf of the chat, unless it is unbanned first. The bot must be
/// an administrator in the supergroup or channel for this to work and must have the appropriate administrator
/// rights. Returns <c>true</c> on success
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class BanChatSenderChatRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Unique identifier of the target sender chat
/// </summary>
[JsonProperty(Required = Required.Always)]
public long SenderChatId {
get;
}
/// <summary>
/// Date when the sender chat will be unbanned, unix time. If the chat is banned for more than 366 days or
/// less than 30 seconds from the current time they are considered to be banned forever.
/// </summary>
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTime? UntilDate {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and senderChatId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target channel (in the format @channelusername)
/// </param>
/// <param name="senderChatId">
/// Unique identifier of the target sender chat
/// </param>
public BanChatSenderChatRequest(ChatId chatId, long senderChatId)
: base("banChatSenderChat") {
ChatId = chatId;
SenderChatId = senderChatId;
}
}
}

View File

@ -0,0 +1,44 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this request to approve a chat join request. The bot must be an administrator in the chat for this to
/// work and must have the <see cref="ChatPermissions.CanInviteUsers"/> administrator right.
/// Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class ApproveChatJoinRequest : RequestBase<bool>, IChatTargetable, IUserTargetable {
/// <inheritdoc/>
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Unique identifier of the target user
/// </summary>
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// Initializes a new request with chatId and userId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
public ApproveChatJoinRequest(ChatId chatId, long userId)
: base("approveChatJoinRequest") {
ChatId = chatId;
UserId = userId;
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to create an additional invite link for a chat. The bot must be an
/// administrator in the chat for this to work and must have the appropriate admin rights.
/// The link can be revoked using the method <see cref="RevokeChatInviteLinkRequest"/>.
/// Returns the new invite link as <see cref="Types.ChatInviteLink"/> object.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class CreateChatInviteLinkRequest : RequestBase<ChatInviteLink>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Invite link name; 0-32 characters
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Name {
get; set;
}
/// <summary>
/// Point in time when the link will expire
/// </summary>
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTime? ExpireDate {
get; set;
}
/// <summary>
/// Maximum number of users that can be members of the chat simultaneously after joining the
/// chat via this invite link; 1-99999
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? MemberLimit {
get; set;
}
/// <summary>
/// Set to <c>true</c>, if users joining the chat via the link need to be approved by chat administrators.
/// If <c>true</c>, <see cref="MemberLimit"/> can't be specified
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CreatesJoinRequest {
get; set;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
public CreateChatInviteLinkRequest(ChatId chatId)
: base("createChatInviteLink") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,44 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this request to decline a chat join request. The bot must be an administrator in the chat for this to
/// work and must have the <see cref="ChatPermissions.CanInviteUsers"/> administrator right.
/// Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class DeclineChatJoinRequest : RequestBase<bool>, IChatTargetable, IUserTargetable {
/// <inheritdoc/>
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Unique identifier of the target user
/// </summary>
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// Initializes a new request with chatId and userId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
public DeclineChatJoinRequest(ChatId chatId, long userId)
: base("declineChatJoinRequest") {
ChatId = chatId;
UserId = userId;
}
}
}

View File

@ -0,0 +1,81 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator
/// in the chat for this to work and must have the appropriate admin rights. Returns the edited invite
/// link as a <see cref="Types.ChatInviteLink"/> object.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class EditChatInviteLinkRequest : RequestBase<ChatInviteLink>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// The invite link to edit
/// </summary>
[JsonProperty(Required = Required.Always)]
public string InviteLink {
get;
}
/// <summary>
/// Invite link name; 0-32 characters
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Name {
get; set;
}
/// <summary>
/// Point in time when the link will expire
/// </summary>
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTime? ExpireDate {
get; set;
}
/// <summary>
/// Maximum number of users that can be members of the chat simultaneously after joining the
/// chat via this invite link; 1-99999
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? MemberLimit {
get; set;
}
/// <summary>
/// Set to <c>true</c>, if users joining the chat via the link need to be approved by chat administrators.
/// If <c>true</c>, <see cref="MemberLimit"/> can't be specified
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CreatesJoinRequest {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and inviteLink
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="inviteLink">The invite link to edit</param>
public EditChatInviteLinkRequest(ChatId chatId, string inviteLink)
: base("editChatInviteLink") {
ChatId = chatId;
InviteLink = inviteLink;
}
}
}

View File

@ -0,0 +1,34 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to generate a new primary invite link for a chat; any previously generated primary
/// link is revoked. The bot must be an administrator in the chat for this to work and must have the
/// appropriate admin rights. Returns the new invite link as <c>string</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class ExportChatInviteLinkRequest : RequestBase<string>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
public ExportChatInviteLinkRequest(ChatId chatId)
: base("exportChatInviteLink") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,45 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to revoke an invite link created by the bot. If the primary link is revoked, a new
/// link is automatically generated. The bot must be an administrator in the chat for this to work and
/// must have the appropriate admin rights. Returns the revoked invite link as
/// <see cref="ChatInviteLink"/> object.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class RevokeChatInviteLinkRequest : RequestBase<ChatInviteLink>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// The invite link to revoke
/// </summary>
[JsonProperty(Required = Required.Always)]
public string InviteLink {
get;
}
/// <summary>
/// Initializes a new request with chatId and inviteLink
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="inviteLink">The invite link to revoke</param>
public RevokeChatInviteLinkRequest(ChatId chatId, string inviteLink)
: base("revokeChatInviteLink") {
ChatId = chatId;
InviteLink = inviteLink;
}
}
}

View File

@ -0,0 +1,34 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to delete a chat photo. Photos can't be changed for private chats. The bot
/// must be an administrator in the chat for this to work and must have the appropriate
/// admin rights. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class DeleteChatPhotoRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
public DeleteChatPhotoRequest(ChatId chatId)
: base("deleteChatPhoto") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,35 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to delete a group sticker set from a supergroup. The bot must be an administrator
/// in the chat for this to work and must have the appropriate admin rights. Use the field
/// <see cref="Types.Chat.CanSetStickerSet"/> optionally returned in <see cref="GetChatRequest"/>
/// requests to check if the bot can use this method. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class DeleteChatStickerSetRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
public DeleteChatStickerSetRequest(ChatId chatId)
: base("deleteChatStickerSet") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,36 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get a list of administrators in a chat. On success, returns an Array of
/// <see cref="ChatMember"/> objects that contains information about all chat administrators
/// except other bots. If the chat is a group or a supergroup and no administrators were appointed,
/// only the creator will be returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetChatAdministratorsRequest : RequestBase<ChatMember[]>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target supergroup or channel
/// (in the format <c>@channelusername</c>)
/// </param>
public GetChatAdministratorsRequest(ChatId chatId)
: base("getChatAdministrators") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,33 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get the number of members in a chat. Returns <c>int</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetChatMemberCountRequest : RequestBase<int>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target supergroup or channel
/// (in the format <c>@channelusername</c>)
/// </param>
public GetChatMemberCountRequest(ChatId chatId)
: base("getChatMemberCount") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,42 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get information about a member of a chat. Returns a <see cref="ChatMember"/>
/// object on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetChatMemberRequest : RequestBase<ChatMember>, IChatTargetable, IUserTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// Initializes a new request with chatId and userId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target supergroup or channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
public GetChatMemberRequest(ChatId chatId, long userId)
: base("getChatMember") {
ChatId = chatId;
UserId = userId;
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get the number of members in a chat. Returns <c>int</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
[Obsolete("Use GetChatMemberCountRequest instead")]
public class GetChatMembersCountRequest : RequestBase<int>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target supergroup or channel
/// (in the format <c>@channelusername</c>)
/// </param>
public GetChatMembersCountRequest(ChatId chatId)
: base("getChatMembersCount") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,37 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Converters;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to get up to date information about the chat (current name of the user for
/// one-on-one conversations, current username of a user, group or channel, etc.).
/// Returns a <see cref="Chat"/> object on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class GetChatRequest : RequestBase<Chat>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
[JsonConverter(typeof(ChatIdConverter))]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target supergroup or channel
/// (in the format <c>@channelusername</c>)
/// </param>
public GetChatRequest(ChatId chatId)
: base("getChat") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to ban a user in a group, a supergroup or a channel. In the case of supergroups and
/// channels, the user will not be able to return to the chat on their own using invite links, etc.,
/// unless <see cref="UnbanChatMemberRequest">unbanned</see> first. The bot must be an administrator
/// in the chat for this to work and must have the appropriate admin rights. Returns <c>true</c> on success.
/// </summary>
[Obsolete("Use BanChatMemberRequest instead")]
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class KickChatMemberRequest : RequestBase<bool>, IChatTargetable, IUserTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// Date when the user will be unbanned. If user is banned for more than 366 days or less than
/// 30 seconds from the current time they are considered to be banned forever. Applied for
/// supergroups and channels only.
/// </summary>
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTime? UntilDate {
get; set;
}
/// <summary>
/// Pass True to delete all messages from the chat for the user that is being removed. If
/// <c>false</c>, the user will be able to see messages in the group that were sent before
/// the user was removed. Always True for supergroups and channels.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? RevokeMessages {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and userId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
public KickChatMemberRequest(ChatId chatId, long userId)
: base("kickChatMember") {
ChatId = chatId;
UserId = userId;
}
}
}

View File

@ -0,0 +1,33 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method for your bot to leave a group, supergroup or channel. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class LeaveChatRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target supergroup or channel
/// (in the format <c>@channelusername</c>)
/// </param>
public LeaveChatRequest(ChatId chatId)
: base("leaveChat") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,52 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to add a message to the list of pinned messages in a chat. If the chat is not a
/// private chat, the bot must be an administrator in the chat for this to work and must have the
/// '<see cref="ChatPermissions.CanPinMessages"/>' admin right in a supergroup or
/// '<see cref="ChatMemberAdministrator.CanEditMessages"/>' admin right in a channel.
/// Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class PinChatMessageRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Identifier of a message to pin
/// </summary>
[JsonProperty(Required = Required.Always)]
public int MessageId {
get;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and messageId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="messageId">Identifier of a message to pin</param>
public PinChatMessageRequest(ChatId chatId, int messageId)
: base("pinChatMessage") {
ChatId = chatId;
MessageId = messageId;
}
}
}

View File

@ -0,0 +1,145 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to promote or demote a user in a supergroup or a channel. The bot must be
/// an administrator in the chat for this to work and must have the appropriate admin rights.
/// Pass <c>false</c> for all boolean parameters to demote a user. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class PromoteChatMemberRequest : RequestBase<bool>, IChatTargetable, IUserTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// Pass True, if the administrator's presence in the chat is hidden
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? IsAnonymous {
get; set;
}
/// <summary>
/// Pass True, if the administrator can access the chat event log, chat statistics, message
/// statistics in channels, see channel members, see anonymous administrators in supergroups
/// and ignore slow mode. Implied by any other administrator privilege
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanManageChat {
get; set;
}
/// <summary>
/// Pass True, if the administrator can create channel posts, channels only
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanPostMessages {
get; set;
}
/// <summary>
/// Pass True, if the administrator can edit messages of other users and can pin messages,
/// channels only
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanEditMessages {
get; set;
}
/// <summary>
/// Pass True, if the administrator can delete messages of other users
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanDeleteMessages {
get; set;
}
/// <summary>
/// Pass True, if the administrator can manage voice chats
/// </summary>
[Obsolete("This property will be removed in the next major version, use CanManageVideoChat instead")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanManageVoiceChat {
get; set;
}
/// <summary>
/// Pass True, if the administrator can manage video chats
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanManageVideoChat {
get; set;
}
/// <summary>
/// Pass True, if the administrator can restrict, ban or unban chat members
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanRestrictMembers {
get; set;
}
/// <summary>
/// Pass True, if the administrator can add new administrators with a subset of their own
/// privileges or demote administrators that he has promoted, directly or indirectly
/// (promoted by administrators that were appointed by him)
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanPromoteMembers {
get; set;
}
/// <summary>
/// Pass True, if the administrator can change chat title, photo and other settings
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanChangeInfo {
get; set;
}
/// <summary>
/// Pass True, if the administrator can invite new users to the chat
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanInviteUsers {
get; set;
}
/// <summary>
/// Pass True, if the administrator can pin messages, supergroups only
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? CanPinMessages {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and userId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
public PromoteChatMemberRequest(ChatId chatId, long userId)
: base("promoteChatMember") {
ChatId = chatId;
UserId = userId;
}
}
}

View File

@ -0,0 +1,65 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to restrict a user in a supergroup. The bot must be an administrator in the
/// supergroup for this to work and must have the appropriate admin rights. Pass <c>true</c>
/// for all permissions to lift restrictions from a user. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class RestrictChatMemberRequest : RequestBase<bool>, IChatTargetable, IUserTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// New user permissions
/// </summary>
[JsonProperty(Required = Required.Always)]
public ChatPermissions Permissions {
get;
}
/// <summary>
/// Date when restrictions will be lifted for the user, unix time. If user is restricted for
/// more than 366 days or less than 30 seconds from the current time, they are considered to
/// be restricted forever.
/// </summary>
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTime? UntilDate {
get; set;
}
/// <summary>
/// Initializes a new request with chatId, userId and new user permissions
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
/// <param name="permissions">New user permissions</param>
public RestrictChatMemberRequest(ChatId chatId, long userId, ChatPermissions permissions)
: base("restrictChatMember") {
ChatId = chatId;
UserId = userId;
Permissions = permissions;
}
}
}

View File

@ -0,0 +1,53 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to set a custom title for an administrator in a supergroup promoted by the bot.
/// Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SetChatAdministratorCustomTitleRequest : RequestBase<bool>, IChatTargetable, IUserTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// New custom title for the administrator; 0-16 characters, emoji are not allowed
/// </summary>
[JsonProperty(Required = Required.Always)]
public string CustomTitle {
get;
}
/// <summary>
/// Initializes a new request with chatId, userId and customTitle
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
/// <param name="customTitle">
/// New custom title for the administrator; 0-16 characters, emoji are not allowed
/// </param>
public SetChatAdministratorCustomTitleRequest(ChatId chatId, long userId, string customTitle)
: base("setChatAdministratorCustomTitle") {
ChatId = chatId;
UserId = userId;
CustomTitle = customTitle;
}
}
}

View File

@ -0,0 +1,43 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to change the description of a group, a supergroup or a channel.
/// The bot must be an administrator in the chat for this to work and must have the
/// appropriate admin rights. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SetChatDescriptionRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// New chat Description, 0-255 characters
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Description {
get; set;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
public SetChatDescriptionRequest(ChatId chatId)
: base("setChatDescription") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,44 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to set default chat permissions for all members. The bot must be an administrator
/// in the group or a supergroup for this to work and must have the can_restrict_members admin rights.
/// Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SetChatPermissionsRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// New default chat permissions
/// </summary>
[JsonProperty(Required = Required.Always)]
public ChatPermissions Permissions {
get;
}
/// <summary>
/// Initializes a new request with chatId and new default permissions
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="permissions">New default chat permissions</param>
public SetChatPermissionsRequest(ChatId chatId, ChatPermissions permissions)
: base("setChatPermissions") {
ChatId = chatId;
Permissions = permissions;
}
}
}

View File

@ -0,0 +1,54 @@
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to set a new profile photo for the chat. Photos can't be changed for private
/// chats. The bot must be an administrator in the chat for this to work and must have the appropriate
/// admin rights. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SetChatPhotoRequest : FileRequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// New chat photo, uploaded using multipart/form-data
/// </summary>
[JsonProperty(Required = Required.Always)]
public InputFileStream Photo {
get;
}
/// <summary>
/// Initializes a new request with chatId and photo
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="photo">New chat photo, uploaded using multipart/form-data</param>
public SetChatPhotoRequest(ChatId chatId, InputFileStream photo)
: base("setChatPhoto") {
ChatId = chatId;
Photo = photo;
}
/// <inheritdoc />
public override HttpContent? ToHttpContent() =>
Photo.FileType switch {
FileType.Stream => ToMultipartFormDataContent("photo", Photo),
_ => base.ToHttpContent()
};
}
}

View File

@ -0,0 +1,45 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in
/// the chat for this to work and must have the appropriate admin rights. Use the field
/// <see cref="Chat.CanSetStickerSet"/> optionally returned in <see cref="GetChatRequest"/> requests to
/// check if the bot can use this method. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SetChatStickerSetRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Name of the sticker set to be set as the group sticker set
/// </summary>
[JsonProperty(Required = Required.Always)]
public string StickerSetName {
get;
}
/// <summary>
/// Initializes a new request with chatId and new stickerSetName
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="stickerSetName">Name of the sticker set to be set as the group sticker set</param>
public SetChatStickerSetRequest(ChatId chatId, string stickerSetName)
: base("setChatStickerSet") {
ChatId = chatId;
StickerSetName = stickerSetName;
}
}
}

View File

@ -0,0 +1,44 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to change the title of a chat. Titles can't be changed for private chats.
/// The bot must be an administrator in the chat for this to work and must have the appropriate
/// admin rights. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SetChatTitleRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// New chat title, 1-255 characters
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Title {
get;
}
/// <summary>
/// Initializes a new request with chatId and title
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="title">New chat title, 1-255 characters</param>
public SetChatTitleRequest(ChatId chatId, string title)
: base("setChatTitle") {
ChatId = chatId;
Title = title;
}
}
}

View File

@ -0,0 +1,53 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to unban a previously banned user in a supergroup or channel. The user will
/// <b>not</b> return to the group or channel automatically, but will be able to join via link,
/// etc. The bot must be an administrator for this to work. By default, this method guarantees
/// that after the call the user is not a member of the chat, but will be able to join it.
/// So if the user is a member of the chat they will also be <b>removed</b> from the chat.
/// If you don't want this, use the parameter <see cref="OnlyIfBanned"/>. Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class UnbanChatMemberRequest : RequestBase<bool>, IChatTargetable, IUserTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public long UserId {
get;
}
/// <summary>
/// Do nothing if the user is not banned
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? OnlyIfBanned {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and userId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="userId">Unique identifier of the target user</param>
public UnbanChatMemberRequest(ChatId chatId, long userId)
: base("unbanChatMember") {
ChatId = chatId;
UserId = userId;
}
}
}

View File

@ -0,0 +1,46 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this request to unban a previously banned channel chat in a supergroup or channel. The bot must be an
/// administrator for this to work and must have the appropriate administrator rights. Returns <c>true</c>
/// on success
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class UnbanChatSenderChatRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Unique identifier of the target sender chat
/// </summary>
[JsonProperty(Required = Required.Always)]
public long SenderChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId and senderChatId
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target channel (in the format @channelusername)
/// </param>
/// <param name="senderChatId">
/// Unique identifier of the target sender chat
/// </param>
public UnbanChatSenderChatRequest(ChatId chatId, long senderChatId)
: base("unbanChatSenderChat") {
ChatId = chatId;
SenderChatId = senderChatId;
}
}
}

View File

@ -0,0 +1,36 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to clear the list of pinned messages in a chat. If the chat is not a private chat,
/// the bot must be an administrator in the chat for this to work and must have the
/// '<see cref="ChatMemberAdministrator.CanPinMessages"/>' admin right in a supergroup or
/// '<see cref="ChatMemberAdministrator.CanEditMessages"/>' admin right in a channel.
/// Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class UnpinAllChatMessagesRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
public UnpinAllChatMessagesRequest(ChatId chatId)
: base("unpinAllChatMessages") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,45 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to remove a message from the list of pinned messages in a chat. If the chat is not
/// a private chat, the bot must be an administrator in the chat for this to work and must have the
/// '<see cref="ChatMemberAdministrator.CanPinMessages"/>' admin right in a supergroup or
/// '<see cref="ChatMemberAdministrator.CanEditMessages"/>' admin right in a channel.
/// Returns <c>true</c> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class UnpinChatMessageRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Identifier of a message to unpin. If not specified, the most recent pinned message
/// (by sending date) will be unpinned.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? MessageId {
get; set;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
public UnpinChatMessageRequest(ChatId chatId)
: base("unpinChatMessage") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,118 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to copy messages of any kind. Service messages and invoice messages can't be copied.
/// The method is analogous to the method <see cref="ForwardMessageRequest"/>, but the copied message
/// doesn't have a link to the original message. Returns the <see cref="Types.MessageId"/> of the
/// sent <see cref="Message"/> on success.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class CopyMessageRequest : RequestBase<MessageId>, IChatTargetable {
/// <summary>
/// Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </summary>
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Unique identifier for the chat where the original message was sent
/// (or channel username in the format <c>@channelusername</c>)
/// </summary>
[JsonProperty(Required = Required.Always)]
public ChatId FromChatId {
get;
}
/// <summary>
/// Message identifier in the chat specified in <see cref="FromChatId"/>
/// </summary>
[JsonProperty(Required = Required.Always)]
public int MessageId {
get;
}
/// <summary>
/// New caption for media, 0-1024 characters after entities parsing.
/// If not specified, the original caption is kept
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Caption {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ParseMode"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParseMode? ParseMode {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.CaptionEntities"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IEnumerable<MessageEntity>? CaptionEntities {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId, fromChatId and messageId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="fromChatId">
/// Unique identifier for the chat where the original message was sent
/// (or channel username in the format <c>@channelusername</c>)
/// </param>
/// <param name="messageId">
/// Message identifier in the chat specified in <see cref="FromChatId"/>
/// </param>
public CopyMessageRequest(ChatId chatId, ChatId fromChatId, int messageId)
: base("copyMessage") {
ChatId = chatId;
FromChatId = fromChatId;
MessageId = messageId;
}
}
}

View File

@ -0,0 +1,73 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to forward messages of any kind. Service messages can't be forwarded. On success, the sent <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class ForwardMessageRequest : RequestBase<Message>, IChatTargetable {
/// <summary>
/// Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </summary>
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Unique identifier for the chat where the original message was sent
/// (or channel username in the format <c>@channelusername</c>)
/// </summary>
[JsonProperty(Required = Required.Always)]
public ChatId FromChatId {
get;
}
/// <summary>
/// Message identifier in the chat specified in <see cref="FromChatId"/>
/// </summary>
[JsonProperty(Required = Required.Always)]
public int MessageId {
get;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <summary>
/// Initializes a new request with chatId, fromChatId and messageId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="fromChatId">
/// Unique identifier for the chat where the original message was sent
/// (or channel username in the format <c>@channelusername</c>)
/// </param>
/// <param name="messageId">
/// Message identifier in the chat specified in <see cref="FromChatId"/>
/// </param>
public ForwardMessageRequest(ChatId chatId, ChatId fromChatId, int messageId)
: base("forwardMessage") {
ChatId = chatId;
FromChatId = fromChatId;
MessageId = messageId;
}
}
}

View File

@ -0,0 +1,82 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to edit live location messages. A location can be edited until its
/// <see cref="Types.Location.LivePeriod"/> expires or editing is explicitly disabled by a call to
/// <see cref="StopInlineMessageLiveLocationRequest"/>. On success True is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class EditInlineMessageLiveLocationRequest : RequestBase<bool> {
/// <inheritdoc cref="Abstractions.Documentation.InlineMessageId"/>
[JsonProperty(Required = Required.Always)]
public string InlineMessageId {
get;
}
/// <summary>
/// Latitude of new location
/// </summary>
[JsonProperty(Required = Required.Always)]
public double Latitude {
get;
}
/// <summary>
/// Longitude of new location
/// </summary>
[JsonProperty(Required = Required.Always)]
public double Longitude {
get;
}
/// <summary>
/// The radius of uncertainty for the location, measured in meters; 0-1500
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public float? HorizontalAccuracy {
get; set;
}
/// <summary>
/// Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Heading {
get; set;
}
/// <summary>
/// Maximum distance for proximity alerts about approaching another chat member, in meters. Must be
/// between 1 and 100000 if specified.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ProximityAlertRadius {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public InlineKeyboardMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with inlineMessageId, latitude and longitude
/// </summary>
/// <param name="inlineMessageId">Identifier of the inline message</param>
/// <param name="latitude">Latitude of new location</param>
/// <param name="longitude">Longitude of new location</param>
public EditInlineMessageLiveLocationRequest(string inlineMessageId, double latitude, double longitude)
: base("editMessageLiveLocation") {
InlineMessageId = inlineMessageId;
Latitude = latitude;
Longitude = longitude;
}
}
}

View File

@ -0,0 +1,97 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to edit live location messages. A location can be edited until its
/// <see cref="Types.Location.LivePeriod"/> expires or editing is explicitly disabled by a call to
/// <see cref="StopMessageLiveLocationRequest"/>. On success the edited <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class EditMessageLiveLocationRequest : RequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Identifier of the message to edit
/// </summary>
[JsonProperty(Required = Required.Always)]
public int MessageId {
get;
}
/// <summary>
/// Latitude of new location
/// </summary>
[JsonProperty(Required = Required.Always)]
public double Latitude {
get;
}
/// <summary>
/// Longitude of new location
/// </summary>
[JsonProperty(Required = Required.Always)]
public double Longitude {
get;
}
/// <summary>
/// The radius of uncertainty for the location, measured in meters; 0-1500
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public float? HorizontalAccuracy {
get; set;
}
/// <summary>
/// Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Heading {
get; set;
}
/// <summary>
/// Maximum distance for proximity alerts about approaching another chat member, in meters.
/// Must be between 1 and 100000 if specified.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ProximityAlertRadius {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.InlineReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public InlineKeyboardMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId, messageId, latitude and longitude
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="messageId">Identifier of the message to edit</param>
/// <param name="latitude">Latitude of new location</param>
/// <param name="longitude">Longitude of new location</param>
public EditMessageLiveLocationRequest(ChatId chatId, int messageId, double latitude, double longitude)
: base("editMessageLiveLocation") {
ChatId = chatId;
MessageId = messageId;
Latitude = latitude;
Longitude = longitude;
}
}
}

View File

@ -0,0 +1,115 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send point on the map. On success, the sent <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendLocationRequest : RequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Latitude of the location
/// </summary>
[JsonProperty(Required = Required.Always)]
public double Latitude {
get;
}
/// <summary>
/// Longitude of the location
/// </summary>
[JsonProperty(Required = Required.Always)]
public double Longitude {
get;
}
/// <summary>
/// Period in seconds for which the location will be updated, should be between 60 and 86400
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? LivePeriod {
get; set;
}
/// <summary>
/// For live locations, a direction in which the user is moving, in degrees.
/// Must be between 1 and 360 if specified.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Heading {
get; set;
}
/// <summary>
/// For live locations, a maximum distance for proximity alerts about approaching another
/// chat member, in meters. Must be between 1 and 100000 if specified.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ProximityAlertRadius {
get; set;
}
/// <summary>
/// Sends the message silently. Users will receive a notification with no sound.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <summary>
/// If the message is a reply, ID of the original message
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <summary>
/// Pass <c>true</c>, if the message should be sent even if the specified replied-to message is not found
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId, latitude and longitude
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="latitude">Latitude of the location</param>
/// <param name="longitude">Longitude of the location</param>
public SendLocationRequest(ChatId chatId, double latitude, double longitude)
: base("sendLocation") {
ChatId = chatId;
Latitude = latitude;
Longitude = longitude;
}
}
}

View File

@ -0,0 +1,141 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send information about a venue. On success, the sent <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendVenueRequest : RequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Latitude of the venue
/// </summary>
[JsonProperty(Required = Required.Always)]
public double Latitude {
get;
}
/// <summary>
/// Longitude of the venue
/// </summary>
[JsonProperty(Required = Required.Always)]
public double Longitude {
get;
}
/// <summary>
/// Name of the venue
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Title {
get;
}
/// <summary>
/// Address of the venue
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Address {
get;
}
/// <summary>
/// Foursquare identifier of the venue
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? FoursquareId {
get; set;
}
/// <summary>
/// Foursquare type of the venue, if known. (For example, “arts_entertainment/default”,
/// “arts_entertainment/aquarium” or “food/icecream”.)
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? FoursquareType {
get; set;
}
/// <summary>
/// Google Places identifier of the venue
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? GooglePlaceId {
get; set;
}
/// <summary>
/// Google Places type of the venue.
/// (See <a href="https://developers.google.com/places/web-service/supported_types">supported types</a>.)
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? GooglePlaceType {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId, location, venue title and address
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="latitude">Latitude of the venue</param>
/// <param name="longitude">Longitude of the venue</param>
/// <param name="title">Name of the venue</param>
/// <param name="address">Address of the venue</param>
public SendVenueRequest(
ChatId chatId,
double latitude,
double longitude,
string title,
string address) : base("sendVenue") {
ChatId = chatId;
Latitude = latitude;
Longitude = longitude;
Title = title;
Address = address;
}
}
}

View File

@ -0,0 +1,35 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to stop updating a live location message before <see cref="Types.Location.LivePeriod"/> expires. On success True is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class StopInlineMessageLiveLocationRequest : RequestBase<bool> {
/// <inheritdoc cref="Abstractions.Documentation.InlineMessageId"/>
[JsonProperty(Required = Required.Always)]
public string InlineMessageId {
get;
}
/// <inheritdoc cref="Abstractions.Documentation.InlineReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public InlineKeyboardMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with inlineMessageId
/// </summary>
/// <param name="inlineMessageId">Identifier of the inline message</param>
public StopInlineMessageLiveLocationRequest(string inlineMessageId)
: base("stopMessageLiveLocation") {
InlineMessageId = inlineMessageId;
}
}
}

View File

@ -0,0 +1,51 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to stop updating a live location message before
/// <see cref="Types.Location.LivePeriod"/> expires. On success the sent
/// <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class StopMessageLiveLocationRequest : RequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Identifier of the sent message
/// </summary>
[JsonProperty(Required = Required.Always)]
public int MessageId {
get;
}
/// <inheritdoc cref="Abstractions.Documentation.InlineReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public InlineKeyboardMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and messageId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="messageId">Identifier of the sent message</param>
public StopMessageLiveLocationRequest(ChatId chatId, int messageId)
: base("stopMessageLiveLocation") {
ChatId = chatId;
MessageId = messageId;
}
}
}

View File

@ -0,0 +1,166 @@
using System.Collections.Generic;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Extensions;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success,
/// the sent <see cref="Message"/> is returned. Bots can currently send animation files of up to
/// 50 MB in size, this limit may be changed in the future.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendAnimationRequest : FileRequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Animation to send. Pass a <see cref="InputTelegramFile.FileId"/> as String to send an animation
/// that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram
/// to get an animation from the Internet, or upload a new animation using multipart/form-data
/// </summary>
[JsonProperty(Required = Required.Always)]
public InputOnlineFile Animation {
get;
}
/// <summary>
/// Duration of sent animation in seconds
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Duration {
get; set;
}
/// <summary>
/// Animation width
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Width {
get; set;
}
/// <summary>
/// Animation height
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Height {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.Thumb"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public InputMedia? Thumb {
get; set;
}
/// <summary>
/// Animation caption (may also be used when resending animation by
/// <see cref="InputTelegramFile.FileId"/>), 0-1024 characters after entities parsing
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Caption {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ParseMode"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParseMode? ParseMode {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.CaptionEntities"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IEnumerable<MessageEntity>? CaptionEntities {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and animation
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="animation">
/// Animation to send. Pass a <see cref="InputTelegramFile.FileId"/> as String to send an animation
/// that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to
/// get an animation from the Internet, or upload a new animation using multipart/form-data
/// </param>
public SendAnimationRequest(ChatId chatId, InputOnlineFile animation)
: base("sendAnimation") {
ChatId = chatId;
Animation = animation;
}
/// <inheritdoc />
public override HttpContent? ToHttpContent() {
HttpContent? httpContent;
if(Animation.FileType == FileType.Stream || Thumb?.FileType == FileType.Stream) {
var multipartContent = GenerateMultipartFormDataContent("animation", "thumb");
if(Animation.FileType == FileType.Stream) {
multipartContent.AddStreamContent(
content: Animation.Content!,
name: "animation",
fileName: Animation.FileName
);
}
if(Thumb?.FileType == FileType.Stream) {
multipartContent.AddStreamContent(
content: Thumb.Content!,
name: "thumb",
fileName: Thumb.FileName
);
}
httpContent = multipartContent;
} else {
httpContent = base.ToHttpContent();
}
return httpContent;
}
}
}

View File

@ -0,0 +1,166 @@
using System.Collections.Generic;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Extensions;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send audio files, if you want Telegram clients to display them in the music
/// player. Your audio must be in the .MP3 or .M4A format. On success, the sent <see cref="Message"/>
/// is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be
/// changed in the future.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendAudioRequest : FileRequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Audio file to send. Pass a <see cref="InputTelegramFile.FileId"/> as String to send an audio
/// file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for
/// Telegram to get an audio file from the Internet, or upload a new one using multipart/form-data
/// </summary>
[JsonProperty(Required = Required.Always)]
public InputOnlineFile Audio {
get;
}
/// <summary>
/// Audio caption, 0-1024 characters after entities parsing
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Caption {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ParseMode"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParseMode? ParseMode {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.CaptionEntities"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IEnumerable<MessageEntity>? CaptionEntities {
get; set;
}
/// <summary>
/// Duration of the audio in seconds
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? Duration {
get; set;
}
/// <summary>
/// Performer
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Performer {
get; set;
}
/// <summary>
/// Track name
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Title {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.Thumb"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public InputMedia? Thumb {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and audio
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="audio">
/// Audio file to send. Pass a <see cref="InputTelegramFile.FileId"/> as String to send an audio
/// file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for
/// Telegram to get an audio file from the Internet, or upload a new one using multipart/form-data
/// </param>
public SendAudioRequest(ChatId chatId, InputOnlineFile audio)
: base("sendAudio") {
ChatId = chatId;
Audio = audio;
}
/// <inheritdoc />
public override HttpContent? ToHttpContent() {
HttpContent? httpContent;
if(Audio.FileType == FileType.Stream || Thumb?.FileType == FileType.Stream) {
var multipartContent = GenerateMultipartFormDataContent("audio", "thumb");
if(Audio.FileType == FileType.Stream) {
multipartContent.AddStreamContent(
content: Audio.Content!,
name: "audio",
fileName: Audio.FileName
);
}
if(Thumb?.FileType == FileType.Stream) {
multipartContent.AddStreamContent(
content: Thumb.Content!,
name: "thumb",
fileName: Thumb.FileName
);
}
httpContent = multipartContent;
} else {
httpContent = base.ToHttpContent();
}
return httpContent;
}
}
}

View File

@ -0,0 +1,67 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this request when you need to tell the user that something is happening on the bots side.
/// The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients
/// clear its typing status). Returns <c>true</c> on success.
/// </summary>
/// <remarks>
/// Example: The <a href="https://t.me/imagebot">ImageBot</a> needs some time to process a request
/// and upload the image. Instead of sending a text message along the lines of “Retrieving image,
/// please wait…”, the bot may use <see cref="SendChatActionRequest"/> with
/// <see cref="Action"/> = <see cref="ChatAction.UploadPhoto"/>. The user will see a “sending photo”
/// status for the bot.
/// <para>
/// We only recommend using this method when a response from the bot will take a <b>noticeable</b>
/// amount of time to arrive.
/// </para>
/// </remarks>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendChatActionRequest : RequestBase<bool>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Type of action to broadcast. Choose one, depending on what the user is about to receive:
/// <see cref="ChatAction.Typing"/> for <see cref="SendMessageRequest">text messages</see>,
/// <see cref="ChatAction.UploadPhoto"/> for <see cref="SendPhotoRequest">photos</see>,
/// <see cref="ChatAction.RecordVideo"/> or <see cref="ChatAction.UploadVideo"/> for
/// <see cref="SendVideoRequest">videos</see>, <see cref="ChatAction.RecordVoice"/> or
/// <see cref="ChatAction.UploadVoice"/> for <see cref="SendVoiceRequest">voice notes</see>,
/// <see cref="ChatAction.UploadDocument"/> for <see cref="SendDocumentRequest">general files</see>,
/// <see cref="ChatAction.FindLocation"/> for <see cref="SendLocationRequest">location data</see>,
/// <see cref="ChatAction.RecordVideoNote"/> or <see cref="ChatAction.UploadVideoNote"/> for
/// <see cref="SendVideoNoteRequest">video notes</see>
/// </summary>
[JsonProperty(Required = Required.Always)]
public ChatAction Action {
get;
}
/// <summary>
/// Initializes a new request chatId and action
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="action">
/// Type of action to broadcast. Choose one, depending on what the user is about to receive
/// </param>
public SendChatActionRequest(ChatId chatId, ChatAction action)
: base("sendChatAction") {
ChatId = chatId;
Action = action;
}
}
}

View File

@ -0,0 +1,99 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send phone contacts. On success, the sent <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendContactRequest : RequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Contact's phone number
/// </summary>
[JsonProperty(Required = Required.Always)]
public string PhoneNumber {
get;
}
/// <summary>
/// Contact's first name
/// </summary>
[JsonProperty(Required = Required.Always)]
public string FirstName {
get;
}
/// <summary>
/// Contact's last name
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? LastName {
get; set;
}
/// <summary>
/// Additional data about the contact in the form of a vCard, 0-2048 bytes
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Vcard {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId, phoneNumber and firstName
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="phoneNumber">Contact's phone number</param>
/// <param name="firstName">Contact's first name</param>
public SendContactRequest(ChatId chatId, string phoneNumber, string firstName)
: base("sendContact") {
ChatId = chatId;
PhoneNumber = phoneNumber;
FirstName = firstName;
}
}
}

View File

@ -0,0 +1,74 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
using EmojiEnum = Telegram.Bot.Types.Enums.Emoji;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send an animated emoji that will display a random value. On success,
/// the sent <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendDiceRequest : RequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Emoji on which the dice throw animation is based. Defaults to <see cref="EmojiEnum.Dice"/>
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public Emoji? Emoji {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)</param>
public SendDiceRequest(ChatId chatId)
: base("sendDice") {
ChatId = chatId;
}
}
}

View File

@ -0,0 +1,150 @@
using System.Collections.Generic;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Extensions;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send general files. On success, the sent <see cref="Message"/>
/// is returned. Bots can currently send files of any type of up to 50 MB in size,
/// this limit may be changed in the future.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendDocumentRequest : FileRequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// File to send. Pass a <see cref="InputTelegramFile.FileId"/> as String to send a file that
/// exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram
/// to get a file from the Internet, or upload a new one using multipart/form-data
/// </summary>
[JsonProperty(Required = Required.Always)]
public InputOnlineFile Document {
get;
}
/// <inheritdoc cref="Abstractions.Documentation.Thumb"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public InputMedia? Thumb {
get; set;
}
/// <summary>
/// Document caption (may also be used when resending documents by file_id), 0-1024 characters
/// after entities parsing
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Caption {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ParseMode"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParseMode? ParseMode {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.CaptionEntities"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IEnumerable<MessageEntity>? CaptionEntities {
get; set;
}
/// <summary>
/// Disables automatic server-side content type detection for files uploaded using multipart/form-data
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableContentTypeDetection {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and document
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="document">
/// File to send. Pass a <see cref="InputTelegramFile.FileId"/> as string to send a file that
/// exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram
/// to get a file from the Internet, or upload a new one using multipart/form-data
/// </param>
public SendDocumentRequest(ChatId chatId, InputOnlineFile document)
: base("sendDocument") {
ChatId = chatId;
Document = document;
}
/// <inheritdoc />
public override HttpContent? ToHttpContent() {
HttpContent? httpContent;
if(Document.FileType == FileType.Stream || Thumb?.FileType == FileType.Stream) {
var multipartContent = GenerateMultipartFormDataContent("document", "thumb");
if(Document.FileType == FileType.Stream) {
multipartContent.AddStreamContent(
content: Document.Content!,
name: "document",
fileName: Document.FileName
);
}
if(Thumb?.FileType == FileType.Stream) {
multipartContent.AddStreamContent(
content: Thumb.Content!,
name: "thumb",
fileName: Thumb.FileName
);
}
httpContent = multipartContent;
} else {
httpContent = base.ToHttpContent();
}
return httpContent;
}
}
}

View File

@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Extensions;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send a group of photos, videos, documents or audios as an album. Documents and
/// audio files can be only grouped in an album with messages of the same type. On success, an array
/// of <see cref="Message"/>s that were sent is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendMediaGroupRequest : FileRequestBase<Message[]>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// An array describing messages to be sent, must include 2-10 items
/// </summary>
[JsonProperty(Required = Required.Always)]
public IEnumerable<IAlbumInputMedia> Media {
get;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <summary>
/// Initializes a request with chatId and media
/// </summary>
/// <param name="chatId">
/// Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="media">An array describing messages to be sent, must include 2-10 items</param>
public SendMediaGroupRequest(ChatId chatId, IEnumerable<IAlbumInputMedia> media)
: base("sendMediaGroup") {
ChatId = chatId;
Media = media;
}
/// <inheritdoc />
public override HttpContent ToHttpContent() {
var httpContent = GenerateMultipartFormDataContent();
httpContent.AddContentIfInputFileStream(Media.Cast<IInputMedia>().ToArray());
return httpContent;
}
}
}

View File

@ -0,0 +1,95 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send text messages. On success, the sent <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendMessageRequest : RequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Text of the message to be sent, 1-4096 characters after entities parsing
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Text {
get;
}
/// <inheritdoc cref="Abstractions.Documentation.ParseMode"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParseMode? ParseMode {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.Entities"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IEnumerable<MessageEntity>? Entities {
get; set;
}
/// <summary>
/// Disables link previews for links in this message
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableWebPagePreview {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and text
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="text">Text of the message to be sent, 1-4096 characters after entities parsing</param>
public SendMessageRequest(ChatId chatId, string text)
: base("sendMessage") {
ChatId = chatId;
Text = text;
}
}
}

View File

@ -0,0 +1,114 @@
using System.Collections.Generic;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send photos. On success, the sent <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendPhotoRequest : FileRequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Photo to send. Pass a <see cref="InputTelegramFile.FileId"/> as String to send a photo that
/// exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to
/// get a photo from the Internet, or upload a new photo using multipart/form-data. The photo
/// must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total.
/// Width and height ratio must be at most 20
/// </summary>
[JsonProperty(Required = Required.Always)]
public InputOnlineFile Photo {
get;
}
/// <summary>
/// Photo caption (may also be used when resending photos by <see cref="InputTelegramFile.FileId"/>),
/// 0-1024 characters after entities parsing
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Caption {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ParseMode"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParseMode? ParseMode {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.CaptionEntities"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IEnumerable<MessageEntity>? CaptionEntities {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId and photo
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="photo">
/// Photo to send. Pass a <see cref="InputTelegramFile.FileId"/> as String to send a photo that
/// exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to
/// get a photo from the Internet, or upload a new photo using multipart/form-data. The photo
/// must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total.
/// Width and height ratio must be at most 20</param>
public SendPhotoRequest(ChatId chatId, InputOnlineFile photo)
: base("sendPhoto") {
ChatId = chatId;
Photo = photo;
}
/// <inheritdoc />
public override HttpContent? ToHttpContent() =>
Photo.FileType switch {
FileType.Stream => ToMultipartFormDataContent(fileParameterName: "photo", inputFile: Photo),
_ => base.ToHttpContent()
};
}
}

View File

@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Telegram.Bot.Requests.Abstractions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
// ReSharper disable CheckNamespace
namespace Telegram.Bot.Requests {
/// <summary>
/// Use this method to send a native poll. On success, the sent <see cref="Message"/> is returned.
/// </summary>
[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class SendPollRequest : RequestBase<Message>, IChatTargetable {
/// <inheritdoc />
[JsonProperty(Required = Required.Always)]
public ChatId ChatId {
get;
}
/// <summary>
/// Poll question, 1-300 characters
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Question {
get;
}
/// <summary>
/// A list of answer options, 2-10 strings 1-100 characters each
/// </summary>
[JsonProperty(Required = Required.Always)]
public IEnumerable<string> Options {
get;
}
/// <summary>
/// True, if the poll needs to be anonymous, defaults to True
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? IsAnonymous {
get; set;
}
/// <summary>
/// Poll type, defaults to <see cref="PollType.Regular"/>
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public PollType? Type {
get; set;
}
/// <summary>
/// True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowsMultipleAnswers {
get; set;
}
/// <summary>
/// 0-based identifier of the correct answer option, required for polls in quiz mode
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? CorrectOptionId {
get; set;
}
/// <summary>
/// Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a
/// quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string? Explanation {
get; set;
}
/// <summary>
/// Mode for parsing entities in the explanation. See
/// <a href="https://core.telegram.org/bots/api#formatting-options">formatting options</a>
/// for more details.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParseMode? ExplanationParseMode {
get; set;
}
/// <summary>
/// List of special entities that appear in the poll explanation, which can be specified instead
/// of <see cref="ParseMode"/>
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IEnumerable<MessageEntity>? ExplanationEntities {
get; set;
}
/// <summary>
/// Amount of time in seconds the poll will be active after creation, 5-600. Can't be used
/// together with <see cref="CloseDate"/>.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? OpenPeriod {
get; set;
}
/// <summary>
/// Point in time when the poll will be automatically closed. Must be at least 5 and no more
/// than 600 seconds in the future. Can't be used together with <see cref="OpenPeriod"/>.
/// </summary>
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public DateTime? CloseDate {
get; set;
}
/// <summary>
/// Pass True, if the poll needs to be immediately closed. This can be useful for poll preview.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? IsClosed {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.DisableNotification"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? DisableNotification {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ProtectContent"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? ProtectContent {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyToMessageId"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int? ReplyToMessageId {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.AllowSendingWithoutReply"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public bool? AllowSendingWithoutReply {
get; set;
}
/// <inheritdoc cref="Abstractions.Documentation.ReplyMarkup"/>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public IReplyMarkup? ReplyMarkup {
get; set;
}
/// <summary>
/// Initializes a new request with chatId, question and <see cref="PollOption"/>
/// </summary>
/// <param name="chatId">Unique identifier for the target chat or username of the target channel
/// (in the format <c>@channelusername</c>)
/// </param>
/// <param name="question">Poll question, 1-300 characters</param>
/// <param name="options">A list of answer options, 2-10 strings 1-100 characters each</param>
public SendPollRequest(ChatId chatId, string question, IEnumerable<string> options)
: base("sendPoll") {
ChatId = chatId;
Question = question;
Options = options;
}
}
}

Some files were not shown because too many files have changed in this diff Show More