commit 17393a47d3ed8a027e127f72c9a5d2465ddca0fb Author: BlubbFish Date: Mon Aug 3 22:52:44 2026 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..108e3ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vs +EnumSerializer.Generator/bin +EnumSerializer.Generator/obj +TelegramBot/bin +TelegramBot/obj \ No newline at end of file diff --git a/EnumSerializer.Generator/EnumConverterGenerator.cs b/EnumSerializer.Generator/EnumConverterGenerator.cs new file mode 100644 index 0000000..9b5e97f --- /dev/null +++ b/EnumSerializer.Generator/EnumConverterGenerator.cs @@ -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 enumDeclarations = context.SyntaxProvider + .CreateSyntaxProvider( + predicate: static (s, _) => IsSyntaxTargetForGeneration(s), + transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)) + .Where(static m => m is not null)!; + + IncrementalValueProvider<(Compilation, ImmutableArray)> 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 enums, + SourceProductionContext context) { + if(enums.IsDefaultOrEmpty) { + // nothing to do yet + return; + } + + IEnumerable distinctEnums = enums.Distinct(); + + List 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 GetTypesToGenerate( + Compilation compilation, + IEnumerable enums, CancellationToken ct) { + var enumsToProcess = new List(); + 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>(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(); + } +} \ No newline at end of file diff --git a/EnumSerializer.Generator/EnumInfo.cs b/EnumSerializer.Generator/EnumInfo.cs new file mode 100644 index 0000000..4f4f10c --- /dev/null +++ b/EnumSerializer.Generator/EnumInfo.cs @@ -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; + + /// + /// Key is the enum name. + /// + public readonly List> Members; + + public EnumInfo( + string name, + string ns, + string fullyQualifiedName, + List> members) { + Name = name; + Namespace = ns; + Members = members; + FullyQualifiedName = fullyQualifiedName; + } + } +} \ No newline at end of file diff --git a/EnumSerializer.Generator/EnumSerializer.Generator.csproj b/EnumSerializer.Generator/EnumSerializer.Generator.csproj new file mode 100644 index 0000000..dbbcc53 --- /dev/null +++ b/EnumSerializer.Generator/EnumSerializer.Generator.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0 + 9 + enable + enable + false + true + True + True + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EnumSerializer.Generator/README.md b/EnumSerializer.Generator/README.md new file mode 100644 index 0000000..35c7b43 --- /dev/null +++ b/EnumSerializer.Generator/README.md @@ -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. diff --git a/EnumSerializer.Generator/SourceGenerationHelper.cs b/EnumSerializer.Generator/SourceGenerationHelper.cs new file mode 100644 index 0000000..caf2ccf --- /dev/null +++ b/EnumSerializer.Generator/SourceGenerationHelper.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; + +using Scriban; + +namespace EnumSerializer.Generator { + + + public static class SourceGenerationHelper { + internal const string ConverterTemplate = @"//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +#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() 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; + } + } +} \ No newline at end of file diff --git a/TelegramBot.sln b/TelegramBot.sln new file mode 100644 index 0000000..41fc82b --- /dev/null +++ b/TelegramBot.sln @@ -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 diff --git a/TelegramBot/Args/ApiRequestEventArgs.cs b/TelegramBot/Args/ApiRequestEventArgs.cs new file mode 100644 index 0000000..53f0c25 --- /dev/null +++ b/TelegramBot/Args/ApiRequestEventArgs.cs @@ -0,0 +1,36 @@ +using System; +using System.Net.Http; +using Telegram.Bot.Requests.Abstractions; + +namespace Telegram.Bot.Args { + + + /// + /// Provides data for MakingApiRequest event + /// + public class ApiRequestEventArgs : EventArgs { + /// + /// Bot API Request + /// + public IRequest Request { + get; + } + + /// + /// HTTP Request Message + /// + public HttpRequestMessage? HttpRequestMessage { + get; + } + + /// + /// + /// + /// + /// + public ApiRequestEventArgs(IRequest request, HttpRequestMessage? httpRequestMessage = default) { + Request = request; + HttpRequestMessage = httpRequestMessage; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Args/ApiResponseEventArgs.cs b/TelegramBot/Args/ApiResponseEventArgs.cs new file mode 100644 index 0000000..319c397 --- /dev/null +++ b/TelegramBot/Args/ApiResponseEventArgs.cs @@ -0,0 +1,36 @@ +using System.Net.Http; + +namespace Telegram.Bot.Args { + + + /// + /// Provides data for ApiResponseReceived event + /// + public class ApiResponseEventArgs { + /// + /// HTTP response received from API + /// + public HttpResponseMessage ResponseMessage { + get; + } + + /// + /// Event arguments of this request + /// + public ApiRequestEventArgs ApiRequestEventArgs { + get; + } + + /// + /// Initialize an object + /// + /// HTTP response received from API + /// Event arguments of this request + public ApiResponseEventArgs( + HttpResponseMessage responseMessage, + ApiRequestEventArgs apiRequestEventArgs) { + ResponseMessage = responseMessage; + ApiRequestEventArgs = apiRequestEventArgs; + } + } +} \ No newline at end of file diff --git a/TelegramBot/AsyncEventHandler`T.cs b/TelegramBot/AsyncEventHandler`T.cs new file mode 100644 index 0000000..f97505f --- /dev/null +++ b/TelegramBot/AsyncEventHandler`T.cs @@ -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( +#pragma warning restore CA1711 + ITelegramBotClient botClient, + TArgs args, + CancellationToken cancellationToken = default + ); +} \ No newline at end of file diff --git a/TelegramBot/Converters/BanTimeUnixDateTimeConverter.cs b/TelegramBot/Converters/BanTimeUnixDateTimeConverter.cs new file mode 100644 index 0000000..d484578 --- /dev/null +++ b/TelegramBot/Converters/BanTimeUnixDateTimeConverter.cs @@ -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); + } + } + } +} \ No newline at end of file diff --git a/TelegramBot/Converters/ChatIdConverter.cs b/TelegramBot/Converters/ChatIdConverter.cs new file mode 100644 index 0000000..b643370 --- /dev/null +++ b/TelegramBot/Converters/ChatIdConverter.cs @@ -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 { + 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(); + return new ChatId(value); + } + } +} \ No newline at end of file diff --git a/TelegramBot/Converters/ChatMemberConverter.cs b/TelegramBot/Converters/ChatMemberConverter.cs new file mode 100644 index 0000000..c253c62 --- /dev/null +++ b/TelegramBot/Converters/ChatMemberConverter.cs @@ -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(); + + 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!; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Converters/InputFileConverter.cs b/TelegramBot/Converters/InputFileConverter.cs new file mode 100644 index 0000000..29e3c9b --- /dev/null +++ b/TelegramBot/Converters/InputFileConverter.cs @@ -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(); + if(value is null) { + return new InputFileStream(Stream.Null); + } + + return Uri.TryCreate(value, UriKind.Absolute, out _) + ? new InputOnlineFile(value) + : new InputTelegramFile(value); + } + } +} \ No newline at end of file diff --git a/TelegramBot/Converters/InputMediaConverter.cs b/TelegramBot/Converters/InputMediaConverter.cs new file mode 100644 index 0000000..1e5f465 --- /dev/null +++ b/TelegramBot/Converters/InputMediaConverter.cs @@ -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(); + + if(value is null) { + return null!; + } + + return value.StartsWith("attach://", StringComparison.InvariantCulture) + ? new(Stream.Null, value.Substring(9)) + : new InputMedia(value); + } + } +} \ No newline at end of file diff --git a/TelegramBot/Converters/MenuButtonConverter.cs b/TelegramBot/Converters/MenuButtonConverter.cs new file mode 100644 index 0000000..41a0460 --- /dev/null +++ b/TelegramBot/Converters/MenuButtonConverter.cs @@ -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(); + + 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!; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Exceptions/ApiRequestException.cs b/TelegramBot/Exceptions/ApiRequestException.cs new file mode 100644 index 0000000..6df53f4 --- /dev/null +++ b/TelegramBot/Exceptions/ApiRequestException.cs @@ -0,0 +1,99 @@ +using System; +using Telegram.Bot.Types; + +namespace Telegram.Bot.Exceptions { + + + /// + /// Represents an API error + /// + // ReSharper disable once ClassWithVirtualMembersNeverInherited.Global +#pragma warning disable CA1032 + public class ApiRequestException : RequestException +#pragma warning restore CA1032 +{ + /// + /// Gets the error code. + /// + public virtual int ErrorCode { + get; + } + + /// + /// Contains information about why a request was unsuccessful. + /// + // ReSharper disable once UnusedAutoPropertyAccessor.Global + // ReSharper disable once MemberCanBePrivate.Global + public ResponseParameters? Parameters { + get; + } + + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public ApiRequestException(string message) + : base(message) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The error code. + public ApiRequestException(string message, int errorCode) + : base(message) => + ErrorCode = errorCode; + + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) + /// if no inner exception is specified. + /// + public ApiRequestException(string message, Exception innerException) + : base(message, innerException) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The error code. + /// The inner exception. + public ApiRequestException(string message, int errorCode, Exception innerException) + : base(message, innerException) => + ErrorCode = errorCode; + + /// + /// Initializes a new instance of the class + /// + /// The message + /// The error code + /// Response parameters + public ApiRequestException(string message, int errorCode, ResponseParameters? parameters) + : base(message) { + ErrorCode = errorCode; + Parameters = parameters; + } + + /// + /// Initializes a new instance of the class + /// + /// The message + /// The error code + /// Response parameters + /// The inner exception + public ApiRequestException( + string message, + int errorCode, + ResponseParameters? parameters, + Exception innerException) + : base(message, innerException) { + ErrorCode = errorCode; + Parameters = parameters; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Exceptions/ApiResponse.cs b/TelegramBot/Exceptions/ApiResponse.cs new file mode 100644 index 0000000..955fd61 --- /dev/null +++ b/TelegramBot/Exceptions/ApiResponse.cs @@ -0,0 +1,52 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +namespace Telegram.Bot.Exceptions { + + + /// + /// Represents failed API response + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ApiResponse { + /// + /// Gets the error message. + /// + [JsonProperty(Required = Required.Always)] + public string Description { + get; private set; + } + + /// + /// Gets the error code. + /// + [JsonProperty(Required = Required.Always)] + public int ErrorCode { + get; private set; + } + + /// + /// Contains information about why a request was unsuccessful. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ResponseParameters? Parameters { + get; private set; + } + + /// + /// Initializes an instance of + /// + /// Error code + /// Error message + /// Information about why a request was unsuccessful + public ApiResponse( + int errorCode, + string description, + ResponseParameters? parameters) { + ErrorCode = errorCode; + Description = description; + Parameters = parameters; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Exceptions/DefaultExceptionParser.cs b/TelegramBot/Exceptions/DefaultExceptionParser.cs new file mode 100644 index 0000000..539ec4c --- /dev/null +++ b/TelegramBot/Exceptions/DefaultExceptionParser.cs @@ -0,0 +1,23 @@ +using System; + +namespace Telegram.Bot.Exceptions { + + + /// + /// Default implementation of that always returns + /// + public class DefaultExceptionParser : IExceptionParser { + /// + 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 + ); + } + } +} \ No newline at end of file diff --git a/TelegramBot/Exceptions/IExceptionParser.cs b/TelegramBot/Exceptions/IExceptionParser.cs new file mode 100644 index 0000000..16d52fb --- /dev/null +++ b/TelegramBot/Exceptions/IExceptionParser.cs @@ -0,0 +1,15 @@ +namespace Telegram.Bot.Exceptions { + + + /// + /// Parses unsuccessful responses from Telegram Bot API to make specific exceptions + /// + public interface IExceptionParser { + /// + /// Parses HTTP response and constructs a specific exception out of it + /// + /// ApiResponse with an error + /// + ApiRequestException Parse(ApiResponse apiResponse); + } +} \ No newline at end of file diff --git a/TelegramBot/Exceptions/RequestException.cs b/TelegramBot/Exceptions/RequestException.cs new file mode 100644 index 0000000..0b1cb0b --- /dev/null +++ b/TelegramBot/Exceptions/RequestException.cs @@ -0,0 +1,73 @@ +using System; +using System.Net; + +namespace Telegram.Bot.Exceptions { + + + /// + /// Represents a request error + /// +#pragma warning disable CA1032 + public class RequestException : Exception +#pragma warning restore CA1032 +{ + /// + /// of the received response + /// + public HttpStatusCode? HttpStatusCode { + get; + } + + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public RequestException(string message) + : base(message) { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The error message that explains the reason for the exception. + /// + /// + /// The exception that is the cause of the current exception, or a null reference + /// (Nothing in Visual Basic) if no inner exception is specified. + /// + public RequestException(string message, Exception innerException) + : base(message, innerException) { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The error message that explains the reason for the exception. + /// + /// + /// of the received response + /// + public RequestException(string message, HttpStatusCode httpStatusCode) + : base(message) => + HttpStatusCode = httpStatusCode; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The error message that explains the reason for the exception. + /// + /// + /// of the received response + /// + /// + /// The exception that is the cause of the current exception, or a null reference + /// (Nothing in Visual Basic) if no inner exception is specified. + /// + public RequestException(string message, HttpStatusCode httpStatusCode, Exception innerException) + : base(message, innerException) => + HttpStatusCode = httpStatusCode; + } +} \ No newline at end of file diff --git a/TelegramBot/Extensions/Extensions.cs b/TelegramBot/Extensions/Extensions.cs new file mode 100644 index 0000000..8d4ad9a --- /dev/null +++ b/TelegramBot/Extensions/Extensions.cs @@ -0,0 +1,15 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Telegram.Bot.Extensions { + + + /// + /// Extension Methods + /// + internal static class ObjectExtensions { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static T ThrowIfNull(this T? value, string parameterName) => + value ?? throw new ArgumentNullException(parameterName); + } +} \ No newline at end of file diff --git a/TelegramBot/Extensions/HttpContentExtensions.cs b/TelegramBot/Extensions/HttpContentExtensions.cs new file mode 100644 index 0000000..8e7e13e --- /dev/null +++ b/TelegramBot/Extensions/HttpContentExtensions.cs @@ -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! + ); + } + } + } + } +} \ No newline at end of file diff --git a/TelegramBot/Extensions/HttpResponseMessageExtensions.cs b/TelegramBot/Extensions/HttpResponseMessageExtensions.cs new file mode 100644 index 0000000..ebd62c5 --- /dev/null +++ b/TelegramBot/Extensions/HttpResponseMessageExtensions.cs @@ -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 { + /// + /// Deserialize body from HttpContent into + /// + /// instance + /// + /// Type of the resulting object + /// + /// + /// Thrown when body in the response can not be deserialized into + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static async Task DeserializeContentAsync( + this HttpResponseMessage httpResponse, + Func 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(); + } 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 + ); + } +} \ No newline at end of file diff --git a/TelegramBot/Extensions/StreamExtensions.cs b/TelegramBot/Extensions/StreamExtensions.cs new file mode 100644 index 0000000..6322bb7 --- /dev/null +++ b/TelegramBot/Extensions/StreamExtensions.cs @@ -0,0 +1,31 @@ +using System.IO; +using System.Runtime.CompilerServices; +using Newtonsoft.Json; + +namespace Telegram.Bot.Extensions { + + + internal static class StreamExtensions { + /// + /// Deserialized JSON in Stream into + /// + /// with content + /// Type of the resulting object + /// Deserialized instance of or null + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? DeserializeJsonFromStream(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(jsonTextReader); + + return searchResult; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Extensions/StringExtensions.cs b/TelegramBot/Extensions/StringExtensions.cs new file mode 100644 index 0000000..7fd9e1b --- /dev/null +++ b/TelegramBot/Extensions/StringExtensions.cs @@ -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()); + } +} \ No newline at end of file diff --git a/TelegramBot/ITelegramBotClient.cs b/TelegramBot/ITelegramBotClient.cs new file mode 100644 index 0000000..791bd14 --- /dev/null +++ b/TelegramBot/ITelegramBotClient.cs @@ -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 { + + + /// + /// A client interface to use the Telegram Bot API + /// +// [PublicAPI] + public interface ITelegramBotClient { + /// + /// + /// + bool LocalBotServer { + get; + } + + /// + /// 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. + /// + long? BotId { + get; + } + + /// + /// Timeout for requests + /// + TimeSpan Timeout { + get; set; + } + + /// + /// Instance of to parse errors from Bot API into + /// + /// + /// This property is not thread safe + IExceptionParser ExceptionsParser { + get; set; + } + + /// + /// Occurs before sending a request to API + /// + event AsyncEventHandler? OnMakingApiRequest; + + /// + /// Occurs after receiving the response to an API request + /// + event AsyncEventHandler? OnApiResponseReceived; + + /// + /// Send a request to Bot API + /// + /// Type of expected result in the response object + /// API request object + /// + /// Result of the API request + Task MakeRequestAsync( + IRequest request, + CancellationToken cancellationToken = default + ); + + /// + /// Test the API token + /// + /// + /// true if token is valid + Task TestApiAsync(CancellationToken cancellationToken = default); + + /// + /// Use this method to download a file. Get by calling + /// + /// + /// Path to file on server + /// Destination stream to write file to + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// filePath is null, empty or too short + /// is null + Task DownloadFileAsync( + string filePath, + Stream destination, + CancellationToken cancellationToken = default + ); + } +} \ No newline at end of file diff --git a/TelegramBot/Polling/Abstractions/IUpdateHandler.cs b/TelegramBot/Polling/Abstractions/IUpdateHandler.cs new file mode 100644 index 0000000..b7e4fb3 --- /dev/null +++ b/TelegramBot/Polling/Abstractions/IUpdateHandler.cs @@ -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 { + + + /// + /// Processes s and errors. + /// See for a simple implementation + /// + //[PublicAPI] + public interface IUpdateHandler { + /// + /// Handles an + /// + /// + /// The instance of the bot receiving the + /// + /// The to handle + /// + /// The which will notify that method execution should be cancelled + /// + /// + Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken); + + /// + /// Handles an + /// + /// + /// The instance of the bot receiving the + /// + /// The to handle + /// + /// The which will notify that method execution should be cancelled + /// + /// + Task HandlePollingErrorAsync( + ITelegramBotClient botClient, + Exception exception, + CancellationToken cancellationToken + ); + } +} \ No newline at end of file diff --git a/TelegramBot/Polling/Abstractions/IUpdateReceiver.cs b/TelegramBot/Polling/Abstractions/IUpdateReceiver.cs new file mode 100644 index 0000000..32d9243 --- /dev/null +++ b/TelegramBot/Polling/Abstractions/IUpdateReceiver.cs @@ -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 { + + + /// + /// Requests new s and processes them using provided instance + /// + //[PublicAPI] + public interface IUpdateReceiver { + /// + /// Starts receiving s invoking + /// for each . + /// This method will block if awaited. + /// + /// + /// The used for processing s + /// + /// + /// The with which you can stop receiving + /// + /// + /// A that will be completed when cancellation will be requested through + /// + /// + Task ReceiveAsync( + IUpdateHandler updateHandler, + CancellationToken cancellationToken = default + ); + } +} \ No newline at end of file diff --git a/TelegramBot/Polling/Abstractions/ReceiverOptions.cs b/TelegramBot/Polling/Abstractions/ReceiverOptions.cs new file mode 100644 index 0000000..766dc4b --- /dev/null +++ b/TelegramBot/Polling/Abstractions/ReceiverOptions.cs @@ -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 { + + + /// + /// Options to configure getUpdates requests + /// + //[PublicAPI] + public sealed class ReceiverOptions { + int? _limit; + + /// + /// Identifier of the first update to be returned. Will be ignored if + /// is set to true. + /// + public int? Offset { + get; set; + } + + /// + /// Indicates which s are allowed to be received. + /// In case of null the previous setting will be used + /// + public UpdateType[]? AllowedUpdates { + get; set; + } + + /// + /// Limits the number of updates to be retrieved. Values between 1-100 are accepted. + /// Defaults to 100 when is set to null. + /// + /// + /// Thrown when the value doesn't satisfies constraints + /// + 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; + } + } + + /// + /// Indicates if all pending s should be thrown out before start + /// polling. If set to true should be set to not + /// null, otherwise will effectively be set to + /// receive all s. + /// + public bool ThrowPendingUpdates { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Polling/AsyncEnumerableReceivers/BlockingUpdateReceiver.cs b/TelegramBot/Polling/AsyncEnumerableReceivers/BlockingUpdateReceiver.cs new file mode 100644 index 0000000..0974c92 --- /dev/null +++ b/TelegramBot/Polling/AsyncEnumerableReceivers/BlockingUpdateReceiver.cs @@ -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 { + + + /// + /// Supports asynchronous iteration over s + /// + //[PublicAPI] + public class BlockingUpdateReceiver : IAsyncEnumerable { + readonly ReceiverOptions? _receiverOptions; + readonly ITelegramBotClient _botClient; + readonly Func? _pollingErrorHandler; + + int _inProcess; + + /// + /// Constructs a new for the specified + /// + /// The used for making GetUpdates calls + /// + /// + /// The function used to handle s thrown by ReceiveUpdates + /// + public BlockingUpdateReceiver( + ITelegramBotClient botClient, + ReceiverOptions? receiverOptions = default, + Func? pollingErrorHandler = default) { + _botClient = botClient ?? throw new ArgumentNullException(nameof(botClient)); + _receiverOptions = receiverOptions; + _pollingErrorHandler = pollingErrorHandler; + } + + /// + /// Gets the . This method may only be called once. + /// + /// + /// The with which you can stop receiving + /// + public IAsyncEnumerator 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 { + readonly BlockingUpdateReceiver _receiver; + readonly CancellationTokenSource _cts; + readonly CancellationToken _token; + readonly UpdateType[]? _allowedUpdates; + readonly int? _limit; + + Update[] _updateArray = Array.Empty(); + 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 MoveNextAsync() { + _token.ThrowIfCancellationRequested(); + + _updateIndex += 1; + + return _updateIndex < _updateArray.Length + ? new(true) + : new(ReceiveUpdatesAsync()); + } + + async Task 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(); + _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 diff --git a/TelegramBot/Polling/AsyncEnumerableReceivers/QueuedUpdateReceiver.cs b/TelegramBot/Polling/AsyncEnumerableReceivers/QueuedUpdateReceiver.cs new file mode 100644 index 0000000..99b1516 --- /dev/null +++ b/TelegramBot/Polling/AsyncEnumerableReceivers/QueuedUpdateReceiver.cs @@ -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 { + + + /// + /// Supports asynchronous iteration over s. + /// Updates are received on a different thread and enqueued. + /// + //[PublicAPI] + public class QueuedUpdateReceiver : IAsyncEnumerable { + readonly ITelegramBotClient _botClient; + readonly ReceiverOptions? _receiverOptions; + readonly Func? _pollingErrorHandler; + + int _inProcess; + Enumerator? _enumerator; + + /// + /// Constructs a new for the specified + /// + /// The used for making GetUpdates calls + /// + /// + /// The function used to handle s thrown by GetUpdates requests + /// + public QueuedUpdateReceiver( + ITelegramBotClient botClient, + ReceiverOptions? receiverOptions = default, + Func? pollingErrorHandler = default) { + _botClient = botClient ?? throw new ArgumentNullException(nameof(botClient)); + _receiverOptions = receiverOptions; + _pollingErrorHandler = pollingErrorHandler; + } + + /// + /// Indicates how many s are ready to be returned the enumerator + /// + public int PendingUpdates => _enumerator?.PendingUpdates ?? 0; + + /// + /// Gets the . This method may only be called once. + /// + /// + /// The with which you can stop receiving + /// + public IAsyncEnumerator 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 { + readonly QueuedUpdateReceiver _receiver; + readonly CancellationTokenSource _cts; + readonly CancellationToken _token; + readonly UpdateType[]? _allowedUpdates; + readonly int? _limit; + + Exception? _uncaughtException; + + readonly Channel _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( + new() { + SingleReader = true, + SingleWriter = true + } + ); + +#pragma warning disable CA2016 + Task.Run(ReceiveUpdatesAsync); +#pragma warning restore CA2016 + } + + public ValueTask 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 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 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 diff --git a/TelegramBot/Polling/DefaultUpdateHandler.cs b/TelegramBot/Polling/DefaultUpdateHandler.cs new file mode 100644 index 0000000..d07d5c8 --- /dev/null +++ b/TelegramBot/Polling/DefaultUpdateHandler.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +//using JetBrains.Annotations; +using Telegram.Bot.Types; + +namespace Telegram.Bot.Polling { + + + /// + /// A very simple implementation + /// + //[PublicAPI] + public class DefaultUpdateHandler : IUpdateHandler { + readonly Func _updateHandler; + readonly Func _pollingErrorHandler; + + /// + /// Constructs a new with the specified callback functions + /// + /// The function to invoke when an update is received + /// The function to invoke when an error occurs + public DefaultUpdateHandler( + Func updateHandler, + Func pollingErrorHandler) { + _updateHandler = updateHandler ?? throw new ArgumentNullException(nameof(updateHandler)); + _pollingErrorHandler = pollingErrorHandler ?? throw new ArgumentNullException(nameof(pollingErrorHandler)); + } + + /// + public async Task HandleUpdateAsync( + ITelegramBotClient botClient, + Update update, + CancellationToken cancellationToken + ) => + await _updateHandler(botClient, update, cancellationToken).ConfigureAwait(false); + + /// + public async Task HandlePollingErrorAsync( + ITelegramBotClient botClient, + Exception exception, + CancellationToken cancellationToken + ) => + await _pollingErrorHandler(botClient, exception, cancellationToken).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/TelegramBot/Polling/DefaultUpdateReceiver.cs b/TelegramBot/Polling/DefaultUpdateReceiver.cs new file mode 100644 index 0000000..04ab075 --- /dev/null +++ b/TelegramBot/Polling/DefaultUpdateReceiver.cs @@ -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 { + + + /// + /// A simple > implementation that requests new updates and handles them sequentially + /// + //[PublicAPI] + public class DefaultUpdateReceiver : IUpdateReceiver { + static readonly Update[] EmptyUpdates = Array.Empty(); + + readonly ITelegramBotClient _botClient; + readonly ReceiverOptions? _receiverOptions; + + /// + /// Constructs a new with the specified > + /// instance and optional + /// + /// The used for making GetUpdates calls + /// Options used to configure getUpdates requests + public DefaultUpdateReceiver( + ITelegramBotClient botClient, + ReceiverOptions? receiverOptions = default) { + _botClient = botClient ?? throw new ArgumentNullException(nameof(botClient)); + _receiverOptions = receiverOptions; + } + + /// + 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 + } + } + } + } + } +} \ No newline at end of file diff --git a/TelegramBot/Polling/TelegramBotClientExtensions.cs b/TelegramBot/Polling/TelegramBotClientExtensions.cs new file mode 100644 index 0000000..70b5718 --- /dev/null +++ b/TelegramBot/Polling/TelegramBotClientExtensions.cs @@ -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 { + /// + /// Will attempt to throw the last update using offset set to -1. + /// + /// + /// + /// + /// Update ID of the last increased by 1 if there were any + /// + internal static async Task ThrowOutPendingUpdatesAsync( + this ITelegramBotClient botClient, + CancellationToken cancellationToken = default) { + var request = new GetUpdatesRequest { + Limit = 1, + Offset = -1, + Timeout = 0, + AllowedUpdates = Array.Empty(), + }; + 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; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Properties/AssemblyInfo.cs b/TelegramBot/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..696aaa0 --- /dev/null +++ b/TelegramBot/Properties/AssemblyInfo.cs @@ -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)] diff --git a/TelegramBot/Requests/Abstractions/Documentation.cs b/TelegramBot/Requests/Abstractions/Documentation.cs new file mode 100644 index 0000000..749f01e --- /dev/null +++ b/TelegramBot/Requests/Abstractions/Documentation.cs @@ -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() { + } + + /// + /// List of special entities that appear in the caption, which can be specified instead of + /// + /// + object CaptionEntities; + + /// + /// List of special entities that appear in message text, which can be specified instead of + /// + /// + object Entities; + + /// + /// Mode for parsing entities in the new caption. See + /// formatting + /// options for more details. + /// + object ParseMode; + + /// + /// Identifier of the inline message + /// + object InlineMessageId; + + /// + /// An inline keyboard + /// + object InlineReplyMarkup; + + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user. + /// + object ReplyMarkup; + + /// + /// Sends the message silently. Users will receive a notification with no sound. + /// + object DisableNotification; + + /// + /// If the message is a reply, ID of the original message + /// + object ReplyToMessageId; + + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + object AllowSendingWithoutReply; + + /// + /// 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://<file_attach_name>" if the thumbnail was uploaded using + /// multipart/form-data under <file_attach_name> + /// + object Thumb; + + /// + /// Protects the contents of sent messages from forwarding and saving + /// + object ProtectContent; + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Abstractions/IChatTargetable.cs b/TelegramBot/Requests/Abstractions/IChatTargetable.cs new file mode 100644 index 0000000..12e9480 --- /dev/null +++ b/TelegramBot/Requests/Abstractions/IChatTargetable.cs @@ -0,0 +1,18 @@ +using Telegram.Bot.Types; + +namespace Telegram.Bot.Requests.Abstractions { + + + /// + /// Represents a request having parameter + /// + public interface IChatTargetable { + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + ChatId ChatId { + get; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Abstractions/IRequest.cs b/TelegramBot/Requests/Abstractions/IRequest.cs new file mode 100644 index 0000000..d27853d --- /dev/null +++ b/TelegramBot/Requests/Abstractions/IRequest.cs @@ -0,0 +1,38 @@ +using System.Net.Http; + +// ReSharper disable once UnusedTypeParameter +namespace Telegram.Bot.Requests.Abstractions { + + + /// + /// Represents a request to Bot API + /// + public interface IRequest { + /// + /// HTTP method of request + /// + HttpMethod Method { + get; + } + + /// + /// API method name + /// + string MethodName { + get; + } + + /// + /// Allows this object to be used as a response in webhooks + /// + bool IsWebhookResponse { + get; set; + } + + /// + /// Generate content of HTTP message + /// + /// Content of HTTP request + HttpContent? ToHttpContent(); + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Abstractions/IRequest`T.cs b/TelegramBot/Requests/Abstractions/IRequest`T.cs new file mode 100644 index 0000000..c27a9e7 --- /dev/null +++ b/TelegramBot/Requests/Abstractions/IRequest`T.cs @@ -0,0 +1,11 @@ +namespace Telegram.Bot.Requests.Abstractions { + + + /// + /// Represents a request to Bot API + /// + /// Type of result expected in result + // ReSharper disable once UnusedTypeParameter + public interface IRequest : IRequest { + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Abstractions/IUserTargetable.cs b/TelegramBot/Requests/Abstractions/IUserTargetable.cs new file mode 100644 index 0000000..43aafa7 --- /dev/null +++ b/TelegramBot/Requests/Abstractions/IUserTargetable.cs @@ -0,0 +1,15 @@ +namespace Telegram.Bot.Requests.Abstractions { + + + /// + /// Represents a request having parameter + /// + public interface IUserTargetable { + /// + /// User identifier + /// + long UserId { + get; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/AnswerCallbackQueryRequest.cs b/TelegramBot/Requests/Available methods/AnswerCallbackQueryRequest.cs new file mode 100644 index 0000000..3e20b04 --- /dev/null +++ b/TelegramBot/Requests/Available methods/AnswerCallbackQueryRequest.cs @@ -0,0 +1,79 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to send answers to callback queries sent from + /// inline keyboards. The answer will be + /// displayed to the user as a notification at the top of the chat screen or as an alert. On success, + /// true is returned. + /// + /// + /// 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 @Botfather and accept the terms. Otherwise, you + /// may use links like t.me/your_bot? start = XXXX that open your bot with a parameter. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class AnswerCallbackQueryRequest : RequestBase { + /// + /// Unique identifier for the query to be answered + /// + [JsonProperty(Required = Required.Always)] + public string CallbackQueryId { + get; + } + + /// + /// Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Text { + get; set; + } + + /// + /// If true, an alert will be shown by the client instead of a notification at the top of + /// the chat screen. Defaults to false + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ShowAlert { + get; set; + } + + /// + /// URL that will be opened by the user's client. If you have created a + /// Game and accepted the conditions + /// via @Botfather, specify the URL that opens your game — note that this will only work + /// if the query comes from a callback_game button. + /// + /// Otherwise, you may use links like t.me/your_bot? start = XXXX that open your bot with + /// a parameter + /// + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Url { + get; set; + } + + /// + /// 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 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? CacheTime { + get; set; + } + + /// + /// Initializes a new request with callbackQueryId + /// + /// Unique identifier for the query to be answered + public AnswerCallbackQueryRequest(string callbackQueryId) + : base("answerCallbackQuery") { + CallbackQueryId = callbackQueryId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Commands/DeleteMyCommandsRequest.cs b/TelegramBot/Requests/Available methods/Commands/DeleteMyCommandsRequest.cs new file mode 100644 index 0000000..c29a8b0 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Commands/DeleteMyCommandsRequest.cs @@ -0,0 +1,43 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to delete the list of the bot’s commands for the given + /// scope and user language. After deletion, + /// higher level commands + /// will be shown to affected users. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class DeleteMyCommandsRequest : RequestBase { + /// + /// An object, describing scope of users for which the commands are relevant. + /// Defaults to . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public BotCommandScope? Scope { + get; set; + } + + /// + /// A two-letter ISO 639-1 language code. If empty, commands will be applied to all users + /// from the given Scope, for whose language there are no dedicated + /// commands + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LanguageCode { + get; set; + } + + /// + /// Initializes a new request + /// + public DeleteMyCommandsRequest() + : base("deleteMyCommands") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Commands/GetMyCommandsRequest.cs b/TelegramBot/Requests/Available methods/Commands/GetMyCommandsRequest.cs new file mode 100644 index 0000000..8376740 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Commands/GetMyCommandsRequest.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to get the current list of the bot’s commands for the given scope + /// and user language. Returns Array of on success. + /// If commands aren't set, an empty list is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetMyCommandsRequest : RequestBase { + /// + /// An object, describing scope of users. Defaults to . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public BotCommandScope? Scope { + get; set; + } + + /// + /// A two-letter ISO 639-1 language code or an empty string + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LanguageCode { + get; set; + } + + /// + /// Initializes a new request + /// + public GetMyCommandsRequest() + : base("getMyCommands") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Commands/SetMyCommandsRequest.cs b/TelegramBot/Requests/Available methods/Commands/SetMyCommandsRequest.cs new file mode 100644 index 0000000..e19cabc --- /dev/null +++ b/TelegramBot/Requests/Available methods/Commands/SetMyCommandsRequest.cs @@ -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 { + + + /// + /// Use this method to change the list of the bot’s commands. See + /// for more details about bot commands. + /// Returns true on success + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetMyCommandsRequest : RequestBase { + /// + /// A list of bot commands to be set as the list of the bot’s commands. + /// At most 100 commands can be specified. + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable Commands { + get; + } + + /// + /// An object, describing scope of users for which the commands are relevant. + /// Defaults to . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public BotCommandScope? Scope { + get; set; + } + + /// + /// A two-letter ISO 639-1 language code. If empty, commands will be applied to all users + /// from the given , for whose language there are no dedicated commands + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LanguageCode { + get; set; + } + + /// + /// Initializes a new request with commands + /// + /// A list of bot commands to be set + public SetMyCommandsRequest(IEnumerable commands) + : base("setMyCommands") { + Commands = commands; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Get Files/GetFileRequest.cs b/TelegramBot/Requests/Available methods/Get Files/GetFileRequest.cs new file mode 100644 index 0000000..d56c657 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Get Files/GetFileRequest.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// 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 object is + /// returned. The file can then be downloaded via the link + /// https://api.telegram.org/file/bot<token>/<file_path>, where + /// <file_path> 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 + /// again. + /// + /// + /// You can use or + /// methods to download the file + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetFileRequest : RequestBase { + /// + /// File identifier to get info about + /// + [JsonProperty(Required = Required.Always)] + public string FileId { + get; + } + + /// + /// Initializes a new request with + /// + /// File identifier to get info about + public GetFileRequest(string fileId) + : base("getFile") { + FileId = fileId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Get Files/GetUserProfilePhotosRequest.cs b/TelegramBot/Requests/Available methods/Get Files/GetUserProfilePhotosRequest.cs new file mode 100644 index 0000000..57b80ab --- /dev/null +++ b/TelegramBot/Requests/Available methods/Get Files/GetUserProfilePhotosRequest.cs @@ -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 { + + + /// + /// Use this method to get a list of profile pictures for a user. Returns a + /// object. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetUserProfilePhotosRequest : RequestBase, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Sequential number of the first photo to be returned. By default, all photos are returned + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Offset { + get; set; + } + + /// + /// Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to 100 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Limit { + get; set; + } + + /// + /// Initializes a new request with userId + /// + /// Unique identifier of the target user + public GetUserProfilePhotosRequest(long userId) + : base("getUserProfilePhotos") { + UserId = userId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/GetChatMenuButtonRequest.cs b/TelegramBot/Requests/Available methods/GetChatMenuButtonRequest.cs new file mode 100644 index 0000000..9dd85ec --- /dev/null +++ b/TelegramBot/Requests/Available methods/GetChatMenuButtonRequest.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to get the current value of the bot’s menu button in a private chat, or the default menu button. + /// Returns on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetChatMenuButtonRequest : RequestBase { + /// + /// Optional. Unique identifier for the target private chat. If not specified, default bot’s menu button + /// will be changed + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public long? ChatId { + get; set; + } + + /// + /// Initializes a new request + /// + public GetChatMenuButtonRequest() + : base("getChatMenuButton") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/GetMeRequest.cs b/TelegramBot/Requests/Available methods/GetMeRequest.cs new file mode 100644 index 0000000..3de47ae --- /dev/null +++ b/TelegramBot/Requests/Available methods/GetMeRequest.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// A simple method for testing your bot’s auth token. Requires no parameters. Returns basic information + /// about the bot in form of a object. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetMeRequest : ParameterlessRequest { + /// + /// Initializes a new request + /// + public GetMeRequest() + : base("getMe") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/GetMyDefaultAdministratorRightsRequest.cs b/TelegramBot/Requests/Available methods/GetMyDefaultAdministratorRightsRequest.cs new file mode 100644 index 0000000..ab21bd4 --- /dev/null +++ b/TelegramBot/Requests/Available methods/GetMyDefaultAdministratorRightsRequest.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to get the current default administrator rights of the bot. + /// Returns on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetMyDefaultAdministratorRightsRequest : RequestBase { + /// + /// Pass true to get default administrator rights of the bot in channels. Otherwise, default administrator + /// rights of the bot for groups and supergroups will be returned. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ForChannels { + get; set; + } + + /// + /// + /// + public GetMyDefaultAdministratorRightsRequest() + : base("getMyDefaultAdministratorRights") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Local Server/CloseRequest.cs b/TelegramBot/Requests/Available methods/Local Server/CloseRequest.cs new file mode 100644 index 0000000..810e5c2 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Local Server/CloseRequest.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// 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. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class CloseRequest : ParameterlessRequest { + /// + /// Initializes a new request + /// + public CloseRequest() : base("close") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Local Server/LogOutRequest.cs b/TelegramBot/Requests/Available methods/Local Server/LogOutRequest.cs new file mode 100644 index 0000000..2535488 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Local Server/LogOutRequest.cs @@ -0,0 +1,20 @@ +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to log out from the cloud Bot API server before launching the bot locally. + /// You must 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 true on success. Requires no parameters. + /// + /// + public class LogOutRequest : ParameterlessRequest { + /// + /// Initializes a new request + /// + public LogOutRequest() : base("logOut") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/BanChatMemberRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/BanChatMemberRequest.cs new file mode 100644 index 0000000..9ef8fa3 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/BanChatMemberRequest.cs @@ -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 { + + + /// + /// 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 unbanned first. The bot must be an + /// administrator in the chat for this to work and must have the appropriate admin rights. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BanChatMemberRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// 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. + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? UntilDate { + get; set; + } + + /// + /// Pass True to delete all messages from the chat for the user that is being removed. If + /// false, 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. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? RevokeMessages { + get; set; + } + + /// + /// Initializes a new request with chatId and userId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + public BanChatMemberRequest(ChatId chatId, long userId) + : base("banChatMember") { + ChatId = chatId; + UserId = userId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/BanChatSenderChatRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/BanChatSenderChatRequest.cs new file mode 100644 index 0000000..5bcfd65 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/BanChatSenderChatRequest.cs @@ -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 { + + + /// + /// 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 true on success + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BanChatSenderChatRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Unique identifier of the target sender chat + /// + [JsonProperty(Required = Required.Always)] + public long SenderChatId { + get; + } + + /// + /// 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. + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? UntilDate { + get; set; + } + + /// + /// Initializes a new request with chatId and senderChatId + /// + /// + /// Unique identifier for the target chat or username of the target channel (in the format @channelusername) + /// + /// + /// Unique identifier of the target sender chat + /// + public BanChatSenderChatRequest(ChatId chatId, long senderChatId) + : base("banChatSenderChat") { + ChatId = chatId; + SenderChatId = senderChatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/ApproveChatJoinRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/ApproveChatJoinRequest.cs new file mode 100644 index 0000000..dae0799 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/ApproveChatJoinRequest.cs @@ -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 { + + + /// + /// 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 administrator right. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ApproveChatJoinRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Unique identifier of the target user + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Initializes a new request with chatId and userId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + public ApproveChatJoinRequest(ChatId chatId, long userId) + : base("approveChatJoinRequest") { + ChatId = chatId; + UserId = userId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/CreateChatInviteLinkRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/CreateChatInviteLinkRequest.cs new file mode 100644 index 0000000..4f40206 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/CreateChatInviteLinkRequest.cs @@ -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 { + + + /// + /// 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 . + /// Returns the new invite link as object. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class CreateChatInviteLinkRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Invite link name; 0-32 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Name { + get; set; + } + + /// + /// Point in time when the link will expire + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? ExpireDate { + get; set; + } + + /// + /// Maximum number of users that can be members of the chat simultaneously after joining the + /// chat via this invite link; 1-99999 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MemberLimit { + get; set; + } + + /// + /// Set to true, if users joining the chat via the link need to be approved by chat administrators. + /// If true, can't be specified + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CreatesJoinRequest { + get; set; + } + + /// + /// Initializes a new request with chatId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + public CreateChatInviteLinkRequest(ChatId chatId) + : base("createChatInviteLink") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/DeclineChatJoinRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/DeclineChatJoinRequest.cs new file mode 100644 index 0000000..ed36f99 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/DeclineChatJoinRequest.cs @@ -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 { + + + /// + /// 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 administrator right. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class DeclineChatJoinRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Unique identifier of the target user + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Initializes a new request with chatId and userId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + public DeclineChatJoinRequest(ChatId chatId, long userId) + : base("declineChatJoinRequest") { + ChatId = chatId; + UserId = userId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/EditChatInviteLinkRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/EditChatInviteLinkRequest.cs new file mode 100644 index 0000000..18ccf8d --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/EditChatInviteLinkRequest.cs @@ -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 { + + + /// + /// 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 object. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditChatInviteLinkRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// The invite link to edit + /// + [JsonProperty(Required = Required.Always)] + public string InviteLink { + get; + } + + /// + /// Invite link name; 0-32 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Name { + get; set; + } + + /// + /// Point in time when the link will expire + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? ExpireDate { + get; set; + } + + /// + /// Maximum number of users that can be members of the chat simultaneously after joining the + /// chat via this invite link; 1-99999 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MemberLimit { + get; set; + } + + /// + /// Set to true, if users joining the chat via the link need to be approved by chat administrators. + /// If true, can't be specified + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CreatesJoinRequest { + get; set; + } + + /// + /// Initializes a new request with chatId and inviteLink + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// The invite link to edit + public EditChatInviteLinkRequest(ChatId chatId, string inviteLink) + : base("editChatInviteLink") { + ChatId = chatId; + InviteLink = inviteLink; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/ExportChatInviteLinkRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/ExportChatInviteLinkRequest.cs new file mode 100644 index 0000000..f6db814 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/ExportChatInviteLinkRequest.cs @@ -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 { + + + /// + /// 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 string on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ExportChatInviteLinkRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + public ExportChatInviteLinkRequest(ChatId chatId) + : base("exportChatInviteLink") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/RevokeChatInviteLinkRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/RevokeChatInviteLinkRequest.cs new file mode 100644 index 0000000..208752b --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Chat Invite Link/RevokeChatInviteLinkRequest.cs @@ -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 { + + + /// + /// 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 + /// object. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class RevokeChatInviteLinkRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// The invite link to revoke + /// + [JsonProperty(Required = Required.Always)] + public string InviteLink { + get; + } + + /// + /// Initializes a new request with chatId and inviteLink + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// The invite link to revoke + public RevokeChatInviteLinkRequest(ChatId chatId, string inviteLink) + : base("revokeChatInviteLink") { + ChatId = chatId; + InviteLink = inviteLink; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/DeleteChatPhotoRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/DeleteChatPhotoRequest.cs new file mode 100644 index 0000000..3de34b5 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/DeleteChatPhotoRequest.cs @@ -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 { + + + /// + /// 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 true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class DeleteChatPhotoRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + public DeleteChatPhotoRequest(ChatId chatId) + : base("deleteChatPhoto") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/DeleteChatStickerSetRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/DeleteChatStickerSetRequest.cs new file mode 100644 index 0000000..f69fc0e --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/DeleteChatStickerSetRequest.cs @@ -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 { + + + /// + /// 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 + /// optionally returned in + /// requests to check if the bot can use this method. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class DeleteChatStickerSetRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + public DeleteChatStickerSetRequest(ChatId chatId) + : base("deleteChatStickerSet") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatAdministratorsRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatAdministratorsRequest.cs new file mode 100644 index 0000000..fa58198 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatAdministratorsRequest.cs @@ -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 { + + + /// + /// Use this method to get a list of administrators in a chat. On success, returns an Array of + /// 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. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetChatAdministratorsRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + public GetChatAdministratorsRequest(ChatId chatId) + : base("getChatAdministrators") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMemberCountRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMemberCountRequest.cs new file mode 100644 index 0000000..f87f178 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMemberCountRequest.cs @@ -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 { + + + /// + /// Use this method to get the number of members in a chat. Returns int on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetChatMemberCountRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + public GetChatMemberCountRequest(ChatId chatId) + : base("getChatMemberCount") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMemberRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMemberRequest.cs new file mode 100644 index 0000000..b604e47 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMemberRequest.cs @@ -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 { + + + /// + /// Use this method to get information about a member of a chat. Returns a + /// object on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetChatMemberRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Initializes a new request with chatId and userId + /// + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + public GetChatMemberRequest(ChatId chatId, long userId) + : base("getChatMember") { + ChatId = chatId; + UserId = userId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMembersCountRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMembersCountRequest.cs new file mode 100644 index 0000000..4892155 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatMembersCountRequest.cs @@ -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 { + + + /// + /// Use this method to get the number of members in a chat. Returns int on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + [Obsolete("Use GetChatMemberCountRequest instead")] + public class GetChatMembersCountRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + public GetChatMembersCountRequest(ChatId chatId) + : base("getChatMembersCount") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatRequest.cs new file mode 100644 index 0000000..afa1851 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/Get Chat/GetChatRequest.cs @@ -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 { + + + /// + /// 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 object on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetChatRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + [JsonConverter(typeof(ChatIdConverter))] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + public GetChatRequest(ChatId chatId) + : base("getChat") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/KickChatMemberRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/KickChatMemberRequest.cs new file mode 100644 index 0000000..5174c39 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/KickChatMemberRequest.cs @@ -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 { + + + /// + /// 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 unbanned first. The bot must be an administrator + /// in the chat for this to work and must have the appropriate admin rights. Returns true on success. + /// + [Obsolete("Use BanChatMemberRequest instead")] + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class KickChatMemberRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// 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. + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? UntilDate { + get; set; + } + + /// + /// Pass True to delete all messages from the chat for the user that is being removed. If + /// false, 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. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? RevokeMessages { + get; set; + } + + /// + /// Initializes a new request with chatId and userId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + public KickChatMemberRequest(ChatId chatId, long userId) + : base("kickChatMember") { + ChatId = chatId; + UserId = userId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/LeaveChatRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/LeaveChatRequest.cs new file mode 100644 index 0000000..6c353c7 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/LeaveChatRequest.cs @@ -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 { + + + /// + /// Use this method for your bot to leave a group, supergroup or channel. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class LeaveChatRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + public LeaveChatRequest(ChatId chatId) + : base("leaveChat") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/PinChatMessageRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/PinChatMessageRequest.cs new file mode 100644 index 0000000..1583d4c --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/PinChatMessageRequest.cs @@ -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 { + + + /// + /// 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 + /// '' admin right in a supergroup or + /// '' admin right in a channel. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class PinChatMessageRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of a message to pin + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + /// Initializes a new request with chatId and messageId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of a message to pin + public PinChatMessageRequest(ChatId chatId, int messageId) + : base("pinChatMessage") { + ChatId = chatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/PromoteChatMemberRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/PromoteChatMemberRequest.cs new file mode 100644 index 0000000..4fe4ff1 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/PromoteChatMemberRequest.cs @@ -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 { + + + /// + /// 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 false for all boolean parameters to demote a user. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class PromoteChatMemberRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Pass True, if the administrator's presence in the chat is hidden + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? IsAnonymous { + get; set; + } + + /// + /// 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 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanManageChat { + get; set; + } + + /// + /// Pass True, if the administrator can create channel posts, channels only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanPostMessages { + get; set; + } + + /// + /// Pass True, if the administrator can edit messages of other users and can pin messages, + /// channels only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanEditMessages { + get; set; + } + + /// + /// Pass True, if the administrator can delete messages of other users + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanDeleteMessages { + get; set; + } + + /// + /// Pass True, if the administrator can manage voice chats + /// + [Obsolete("This property will be removed in the next major version, use CanManageVideoChat instead")] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanManageVoiceChat { + get; set; + } + + /// + /// Pass True, if the administrator can manage video chats + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanManageVideoChat { + get; set; + } + + /// + /// Pass True, if the administrator can restrict, ban or unban chat members + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanRestrictMembers { + get; set; + } + + /// + /// 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) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanPromoteMembers { + get; set; + } + + /// + /// Pass True, if the administrator can change chat title, photo and other settings + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanChangeInfo { + get; set; + } + + /// + /// Pass True, if the administrator can invite new users to the chat + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanInviteUsers { + get; set; + } + + /// + /// Pass True, if the administrator can pin messages, supergroups only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanPinMessages { + get; set; + } + + /// + /// Initializes a new request with chatId and userId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + public PromoteChatMemberRequest(ChatId chatId, long userId) + : base("promoteChatMember") { + ChatId = chatId; + UserId = userId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/RestrictChatMemberRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/RestrictChatMemberRequest.cs new file mode 100644 index 0000000..13a08a2 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/RestrictChatMemberRequest.cs @@ -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 { + + + /// + /// 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 true + /// for all permissions to lift restrictions from a user. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class RestrictChatMemberRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// New user permissions + /// + [JsonProperty(Required = Required.Always)] + public ChatPermissions Permissions { + get; + } + + /// + /// 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. + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? UntilDate { + get; set; + } + + /// + /// Initializes a new request with chatId, userId and new user permissions + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + /// New user permissions + public RestrictChatMemberRequest(ChatId chatId, long userId, ChatPermissions permissions) + : base("restrictChatMember") { + ChatId = chatId; + UserId = userId; + Permissions = permissions; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/SetChatAdministratorCustomTitleRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/SetChatAdministratorCustomTitleRequest.cs new file mode 100644 index 0000000..3f168a9 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/SetChatAdministratorCustomTitleRequest.cs @@ -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 { + + + /// + /// Use this method to set a custom title for an administrator in a supergroup promoted by the bot. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetChatAdministratorCustomTitleRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// New custom title for the administrator; 0-16 characters, emoji are not allowed + /// + [JsonProperty(Required = Required.Always)] + public string CustomTitle { + get; + } + + /// + /// Initializes a new request with chatId, userId and customTitle + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + /// + /// New custom title for the administrator; 0-16 characters, emoji are not allowed + /// + public SetChatAdministratorCustomTitleRequest(ChatId chatId, long userId, string customTitle) + : base("setChatAdministratorCustomTitle") { + ChatId = chatId; + UserId = userId; + CustomTitle = customTitle; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/SetChatDescriptionRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/SetChatDescriptionRequest.cs new file mode 100644 index 0000000..392bb44 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/SetChatDescriptionRequest.cs @@ -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 { + + + /// + /// 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 true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetChatDescriptionRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// New chat Description, 0-255 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + /// Initializes a new request with chatId + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + public SetChatDescriptionRequest(ChatId chatId) + : base("setChatDescription") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/SetChatPermissionsRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/SetChatPermissionsRequest.cs new file mode 100644 index 0000000..1c4f796 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/SetChatPermissionsRequest.cs @@ -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 { + + + /// + /// 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 true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetChatPermissionsRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// New default chat permissions + /// + [JsonProperty(Required = Required.Always)] + public ChatPermissions Permissions { + get; + } + + /// + /// Initializes a new request with chatId and new default permissions + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// New default chat permissions + public SetChatPermissionsRequest(ChatId chatId, ChatPermissions permissions) + : base("setChatPermissions") { + ChatId = chatId; + Permissions = permissions; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/SetChatPhotoRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/SetChatPhotoRequest.cs new file mode 100644 index 0000000..4fd615d --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/SetChatPhotoRequest.cs @@ -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 { + + + /// + /// 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 true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetChatPhotoRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// New chat photo, uploaded using multipart/form-data + /// + [JsonProperty(Required = Required.Always)] + public InputFileStream Photo { + get; + } + + /// + /// Initializes a new request with chatId and photo + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// New chat photo, uploaded using multipart/form-data + public SetChatPhotoRequest(ChatId chatId, InputFileStream photo) + : base("setChatPhoto") { + ChatId = chatId; + Photo = photo; + } + + /// + public override HttpContent? ToHttpContent() => + Photo.FileType switch { + FileType.Stream => ToMultipartFormDataContent("photo", Photo), + _ => base.ToHttpContent() + }; + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/SetChatStickerSetRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/SetChatStickerSetRequest.cs new file mode 100644 index 0000000..d7a313b --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/SetChatStickerSetRequest.cs @@ -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 { + + + /// + /// 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 + /// optionally returned in requests to + /// check if the bot can use this method. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetChatStickerSetRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Name of the sticker set to be set as the group sticker set + /// + [JsonProperty(Required = Required.Always)] + public string StickerSetName { + get; + } + + /// + /// Initializes a new request with chatId and new stickerSetName + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Name of the sticker set to be set as the group sticker set + public SetChatStickerSetRequest(ChatId chatId, string stickerSetName) + : base("setChatStickerSet") { + ChatId = chatId; + StickerSetName = stickerSetName; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/SetChatTitleRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/SetChatTitleRequest.cs new file mode 100644 index 0000000..bf8c82c --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/SetChatTitleRequest.cs @@ -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 { + + + /// + /// 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 true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetChatTitleRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// New chat title, 1-255 characters + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Initializes a new request with chatId and title + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// New chat title, 1-255 characters + public SetChatTitleRequest(ChatId chatId, string title) + : base("setChatTitle") { + ChatId = chatId; + Title = title; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/UnbanChatMemberRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/UnbanChatMemberRequest.cs new file mode 100644 index 0000000..0bdbd1c --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/UnbanChatMemberRequest.cs @@ -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 { + + + /// + /// Use this method to unban a previously banned user in a supergroup or channel. The user will + /// not 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 removed from the chat. + /// If you don't want this, use the parameter . Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class UnbanChatMemberRequest : RequestBase, IChatTargetable, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Do nothing if the user is not banned + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? OnlyIfBanned { + get; set; + } + + /// + /// Initializes a new request with chatId and userId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + public UnbanChatMemberRequest(ChatId chatId, long userId) + : base("unbanChatMember") { + ChatId = chatId; + UserId = userId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/UnbanChatSenderChatRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/UnbanChatSenderChatRequest.cs new file mode 100644 index 0000000..f51c656 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/UnbanChatSenderChatRequest.cs @@ -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 { + + + /// + /// 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 true + /// on success + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class UnbanChatSenderChatRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Unique identifier of the target sender chat + /// + [JsonProperty(Required = Required.Always)] + public long SenderChatId { + get; + } + + /// + /// Initializes a new request with chatId and senderChatId + /// + /// + /// Unique identifier for the target chat or username of the target channel (in the format @channelusername) + /// + /// + /// Unique identifier of the target sender chat + /// + public UnbanChatSenderChatRequest(ChatId chatId, long senderChatId) + : base("unbanChatSenderChat") { + ChatId = chatId; + SenderChatId = senderChatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/UnpinAllChatMessagesRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/UnpinAllChatMessagesRequest.cs new file mode 100644 index 0000000..1d854e6 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/UnpinAllChatMessagesRequest.cs @@ -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 { + + + /// + /// 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 + /// '' admin right in a supergroup or + /// '' admin right in a channel. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class UnpinAllChatMessagesRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Initializes a new request with chatId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + public UnpinAllChatMessagesRequest(ChatId chatId) + : base("unpinAllChatMessages") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Manage Chat/UnpinChatMessageRequest.cs b/TelegramBot/Requests/Available methods/Manage Chat/UnpinChatMessageRequest.cs new file mode 100644 index 0000000..0d4c1b5 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Manage Chat/UnpinChatMessageRequest.cs @@ -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 { + + + /// + /// 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 + /// '' admin right in a supergroup or + /// '' admin right in a channel. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class UnpinChatMessageRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of a message to unpin. If not specified, the most recent pinned message + /// (by sending date) will be unpinned. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MessageId { + get; set; + } + + /// + /// Initializes a new request with chatId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + public UnpinChatMessageRequest(ChatId chatId) + : base("unpinChatMessage") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/CopyMessageRequest.cs b/TelegramBot/Requests/Available methods/Messages/CopyMessageRequest.cs new file mode 100644 index 0000000..bbcb5a7 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/CopyMessageRequest.cs @@ -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 { + + + /// + /// 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 , but the copied message + /// doesn't have a link to the original message. Returns the of the + /// sent on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class CopyMessageRequest : RequestBase, IChatTargetable { + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Unique identifier for the chat where the original message was sent + /// (or channel username in the format @channelusername) + /// + [JsonProperty(Required = Required.Always)] + public ChatId FromChatId { + get; + } + + /// + /// Message identifier in the chat specified in + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + /// New caption for media, 0-1024 characters after entities parsing. + /// If not specified, the original caption is kept + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, fromChatId and messageId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Unique identifier for the chat where the original message was sent + /// (or channel username in the format @channelusername) + /// + /// + /// Message identifier in the chat specified in + /// + public CopyMessageRequest(ChatId chatId, ChatId fromChatId, int messageId) + : base("copyMessage") { + ChatId = chatId; + FromChatId = fromChatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/ForwardMessageRequest.cs b/TelegramBot/Requests/Available methods/Messages/ForwardMessageRequest.cs new file mode 100644 index 0000000..8396bc5 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/ForwardMessageRequest.cs @@ -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 { + + + /// + /// Use this method to forward messages of any kind. Service messages can't be forwarded. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ForwardMessageRequest : RequestBase, IChatTargetable { + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Unique identifier for the chat where the original message was sent + /// (or channel username in the format @channelusername) + /// + [JsonProperty(Required = Required.Always)] + public ChatId FromChatId { + get; + } + + /// + /// Message identifier in the chat specified in + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + /// Initializes a new request with chatId, fromChatId and messageId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Unique identifier for the chat where the original message was sent + /// (or channel username in the format @channelusername) + /// + /// + /// Message identifier in the chat specified in + /// + public ForwardMessageRequest(ChatId chatId, ChatId fromChatId, int messageId) + : base("forwardMessage") { + ChatId = chatId; + FromChatId = fromChatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/Location/EditInlineMessageLiveLocationRequest.cs b/TelegramBot/Requests/Available methods/Messages/Location/EditInlineMessageLiveLocationRequest.cs new file mode 100644 index 0000000..b80599c --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/Location/EditInlineMessageLiveLocationRequest.cs @@ -0,0 +1,82 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.ReplyMarkups; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to edit live location messages. A location can be edited until its + /// expires or editing is explicitly disabled by a call to + /// . On success True is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditInlineMessageLiveLocationRequest : RequestBase { + /// + [JsonProperty(Required = Required.Always)] + public string InlineMessageId { + get; + } + + /// + /// Latitude of new location + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; + } + + /// + /// Longitude of new location + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; + } + + /// + /// The radius of uncertainty for the location, measured in meters; 0-1500 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public float? HorizontalAccuracy { + get; set; + } + + /// + /// Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Heading { + get; set; + } + + /// + /// Maximum distance for proximity alerts about approaching another chat member, in meters. Must be + /// between 1 and 100000 if specified. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ProximityAlertRadius { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with inlineMessageId, latitude and longitude + /// + /// Identifier of the inline message + /// Latitude of new location + /// Longitude of new location + public EditInlineMessageLiveLocationRequest(string inlineMessageId, double latitude, double longitude) + : base("editMessageLiveLocation") { + InlineMessageId = inlineMessageId; + Latitude = latitude; + Longitude = longitude; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/Location/EditMessageLiveLocationRequest.cs b/TelegramBot/Requests/Available methods/Messages/Location/EditMessageLiveLocationRequest.cs new file mode 100644 index 0000000..37187f9 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/Location/EditMessageLiveLocationRequest.cs @@ -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 { + + + /// + /// Use this method to edit live location messages. A location can be edited until its + /// expires or editing is explicitly disabled by a call to + /// . On success the edited is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditMessageLiveLocationRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of the message to edit + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + /// Latitude of new location + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; + } + + /// + /// Longitude of new location + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; + } + + /// + /// The radius of uncertainty for the location, measured in meters; 0-1500 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public float? HorizontalAccuracy { + get; set; + } + + /// + /// Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Heading { + get; set; + } + + /// + /// Maximum distance for proximity alerts about approaching another chat member, in meters. + /// Must be between 1 and 100000 if specified. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ProximityAlertRadius { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, messageId, latitude and longitude + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + /// Latitude of new location + /// Longitude of new location + public EditMessageLiveLocationRequest(ChatId chatId, int messageId, double latitude, double longitude) + : base("editMessageLiveLocation") { + ChatId = chatId; + MessageId = messageId; + Latitude = latitude; + Longitude = longitude; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/Location/SendLocationRequest.cs b/TelegramBot/Requests/Available methods/Messages/Location/SendLocationRequest.cs new file mode 100644 index 0000000..5c13e8c --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/Location/SendLocationRequest.cs @@ -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 { + + + /// + /// Use this method to send point on the map. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendLocationRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Latitude of the location + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; + } + + /// + /// Longitude of the location + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; + } + + /// + /// Period in seconds for which the location will be updated, should be between 60 and 86400 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? LivePeriod { + get; set; + } + + /// + /// For live locations, a direction in which the user is moving, in degrees. + /// Must be between 1 and 360 if specified. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Heading { + get; set; + } + + /// + /// For live locations, a maximum distance for proximity alerts about approaching another + /// chat member, in meters. Must be between 1 and 100000 if specified. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ProximityAlertRadius { + get; set; + } + + /// + /// Sends the message silently. Users will receive a notification with no sound. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + /// If the message is a reply, ID of the original message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, latitude and longitude + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Latitude of the location + /// Longitude of the location + public SendLocationRequest(ChatId chatId, double latitude, double longitude) + : base("sendLocation") { + ChatId = chatId; + Latitude = latitude; + Longitude = longitude; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/Location/SendVenueRequest.cs b/TelegramBot/Requests/Available methods/Messages/Location/SendVenueRequest.cs new file mode 100644 index 0000000..ed6c2b9 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/Location/SendVenueRequest.cs @@ -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 { + + + /// + /// Use this method to send information about a venue. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendVenueRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Latitude of the venue + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; + } + + /// + /// Longitude of the venue + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; + } + + /// + /// Name of the venue + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Address of the venue + /// + [JsonProperty(Required = Required.Always)] + public string Address { + get; + } + + /// + /// Foursquare identifier of the venue + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FoursquareId { + get; set; + } + + /// + /// Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, + /// “arts_entertainment/aquarium” or “food/icecream”.) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FoursquareType { + get; set; + } + + /// + /// Google Places identifier of the venue + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GooglePlaceId { + get; set; + } + + /// + /// Google Places type of the venue. + /// (See supported types.) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GooglePlaceType { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, location, venue title and address + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Latitude of the venue + /// Longitude of the venue + /// Name of the venue + /// Address of the venue + public SendVenueRequest( + ChatId chatId, + double latitude, + double longitude, + string title, + string address) : base("sendVenue") { + ChatId = chatId; + Latitude = latitude; + Longitude = longitude; + Title = title; + Address = address; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/Location/StopInlineMessageLiveLocationRequest.cs b/TelegramBot/Requests/Available methods/Messages/Location/StopInlineMessageLiveLocationRequest.cs new file mode 100644 index 0000000..df5e40d --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/Location/StopInlineMessageLiveLocationRequest.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.ReplyMarkups; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to stop updating a live location message before expires. On success True is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class StopInlineMessageLiveLocationRequest : RequestBase { + /// + [JsonProperty(Required = Required.Always)] + public string InlineMessageId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with inlineMessageId + /// + /// Identifier of the inline message + public StopInlineMessageLiveLocationRequest(string inlineMessageId) + : base("stopMessageLiveLocation") { + InlineMessageId = inlineMessageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/Location/StopMessageLiveLocationRequest.cs b/TelegramBot/Requests/Available methods/Messages/Location/StopMessageLiveLocationRequest.cs new file mode 100644 index 0000000..f2127cb --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/Location/StopMessageLiveLocationRequest.cs @@ -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 { + + + /// + /// Use this method to stop updating a live location message before + /// expires. On success the sent + /// is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class StopMessageLiveLocationRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of the sent message + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and messageId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the sent message + public StopMessageLiveLocationRequest(ChatId chatId, int messageId) + : base("stopMessageLiveLocation") { + ChatId = chatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendAnimationRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendAnimationRequest.cs new file mode 100644 index 0000000..8f3a36e --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendAnimationRequest.cs @@ -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 { + + + /// + /// Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, + /// the sent is returned. Bots can currently send animation files of up to + /// 50 MB in size, this limit may be changed in the future. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendAnimationRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Animation to send. Pass a 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 + /// + [JsonProperty(Required = Required.Always)] + public InputOnlineFile Animation { + get; + } + + /// + /// Duration of sent animation in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Duration { + get; set; + } + + /// + /// Animation width + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Width { + get; set; + } + + /// + /// Animation height + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Height { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + /// Animation caption (may also be used when resending animation by + /// ), 0-1024 characters after entities parsing + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and animation + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Animation to send. Pass a 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 + /// + public SendAnimationRequest(ChatId chatId, InputOnlineFile animation) + : base("sendAnimation") { + ChatId = chatId; + Animation = animation; + } + + /// + 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; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendAudioRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendAudioRequest.cs new file mode 100644 index 0000000..ec46d21 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendAudioRequest.cs @@ -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 { + + + /// + /// 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 + /// is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be + /// changed in the future. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendAudioRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Audio file to send. Pass a 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 + /// + [JsonProperty(Required = Required.Always)] + public InputOnlineFile Audio { + get; + } + + /// + /// Audio caption, 0-1024 characters after entities parsing + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + /// Duration of the audio in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Duration { + get; set; + } + + /// + /// Performer + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Performer { + get; set; + } + + /// + /// Track name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and audio + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Audio file to send. Pass a 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 + /// + public SendAudioRequest(ChatId chatId, InputOnlineFile audio) + : base("sendAudio") { + ChatId = chatId; + Audio = audio; + } + + /// + 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; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendChatActionRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendChatActionRequest.cs new file mode 100644 index 0000000..0873ded --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendChatActionRequest.cs @@ -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 { + + + /// + /// Use this request when you need to tell the user that something is happening on the bot’s side. + /// The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients + /// clear its typing status). Returns true on success. + /// + /// + /// Example: The ImageBot 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 with + /// = . The user will see a “sending photo” + /// status for the bot. + /// + /// We only recommend using this method when a response from the bot will take a noticeable + /// amount of time to arrive. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendChatActionRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Type of action to broadcast. Choose one, depending on what the user is about to receive: + /// for text messages, + /// for photos, + /// or for + /// videos, or + /// for voice notes, + /// for general files, + /// for location data, + /// or for + /// video notes + /// + [JsonProperty(Required = Required.Always)] + public ChatAction Action { + get; + } + + /// + /// Initializes a new request chatId and action + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Type of action to broadcast. Choose one, depending on what the user is about to receive + /// + public SendChatActionRequest(ChatId chatId, ChatAction action) + : base("sendChatAction") { + ChatId = chatId; + Action = action; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendContactRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendContactRequest.cs new file mode 100644 index 0000000..2425b36 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendContactRequest.cs @@ -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 { + + + /// + /// Use this method to send phone contacts. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendContactRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Contact's phone number + /// + [JsonProperty(Required = Required.Always)] + public string PhoneNumber { + get; + } + + /// + /// Contact's first name + /// + [JsonProperty(Required = Required.Always)] + public string FirstName { + get; + } + + /// + /// Contact's last name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LastName { + get; set; + } + + /// + /// Additional data about the contact in the form of a vCard, 0-2048 bytes + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Vcard { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, phoneNumber and firstName + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Contact's phone number + /// Contact's first name + public SendContactRequest(ChatId chatId, string phoneNumber, string firstName) + : base("sendContact") { + ChatId = chatId; + PhoneNumber = phoneNumber; + FirstName = firstName; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendDiceRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendDiceRequest.cs new file mode 100644 index 0000000..bdaec20 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendDiceRequest.cs @@ -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 { + + + /// + /// Use this method to send an animated emoji that will display a random value. On success, + /// the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendDiceRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Emoji on which the dice throw animation is based. Defaults to + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Emoji? Emoji { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + public SendDiceRequest(ChatId chatId) + : base("sendDice") { + ChatId = chatId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendDocumentRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendDocumentRequest.cs new file mode 100644 index 0000000..ce7fa27 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendDocumentRequest.cs @@ -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 { + + + /// + /// Use this method to send general files. On success, the sent + /// 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. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendDocumentRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// File to send. Pass a 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 + /// + [JsonProperty(Required = Required.Always)] + public InputOnlineFile Document { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + /// Document caption (may also be used when resending documents by file_id), 0-1024 characters + /// after entities parsing + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + /// Disables automatic server-side content type detection for files uploaded using multipart/form-data + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableContentTypeDetection { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and document + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// File to send. Pass a 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 + /// + public SendDocumentRequest(ChatId chatId, InputOnlineFile document) + : base("sendDocument") { + ChatId = chatId; + Document = document; + } + + /// + 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; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendMediaGroupRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendMediaGroupRequest.cs new file mode 100644 index 0000000..d90ae3e --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendMediaGroupRequest.cs @@ -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 { + + + /// + /// 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 s that were sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendMediaGroupRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// An array describing messages to be sent, must include 2-10 items + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable Media { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + /// Initializes a request with chatId and media + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// An array describing messages to be sent, must include 2-10 items + public SendMediaGroupRequest(ChatId chatId, IEnumerable media) + : base("sendMediaGroup") { + ChatId = chatId; + Media = media; + } + + /// + public override HttpContent ToHttpContent() { + var httpContent = GenerateMultipartFormDataContent(); + httpContent.AddContentIfInputFileStream(Media.Cast().ToArray()); + return httpContent; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendMessageRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendMessageRequest.cs new file mode 100644 index 0000000..7b18ef5 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendMessageRequest.cs @@ -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 { + + + /// + /// Use this method to send text messages. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendMessageRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Text of the message to be sent, 1-4096 characters after entities parsing + /// + [JsonProperty(Required = Required.Always)] + public string Text { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? Entities { + get; set; + } + + /// + /// Disables link previews for links in this message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableWebPagePreview { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and text + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Text of the message to be sent, 1-4096 characters after entities parsing + public SendMessageRequest(ChatId chatId, string text) + : base("sendMessage") { + ChatId = chatId; + Text = text; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendPhotoRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendPhotoRequest.cs new file mode 100644 index 0000000..1b25b64 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendPhotoRequest.cs @@ -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 { + + + /// + /// Use this method to send photos. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendPhotoRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Photo to send. Pass a 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 + /// + [JsonProperty(Required = Required.Always)] + public InputOnlineFile Photo { + get; + } + + /// + /// Photo caption (may also be used when resending photos by ), + /// 0-1024 characters after entities parsing + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and photo + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Photo to send. Pass a 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 + public SendPhotoRequest(ChatId chatId, InputOnlineFile photo) + : base("sendPhoto") { + ChatId = chatId; + Photo = photo; + } + + /// + public override HttpContent? ToHttpContent() => + Photo.FileType switch { + FileType.Stream => ToMultipartFormDataContent(fileParameterName: "photo", inputFile: Photo), + _ => base.ToHttpContent() + }; + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendPollRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendPollRequest.cs new file mode 100644 index 0000000..a127f50 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendPollRequest.cs @@ -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 { + + + /// + /// Use this method to send a native poll. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendPollRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Poll question, 1-300 characters + /// + [JsonProperty(Required = Required.Always)] + public string Question { + get; + } + + /// + /// A list of answer options, 2-10 strings 1-100 characters each + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable Options { + get; + } + + /// + /// True, if the poll needs to be anonymous, defaults to True + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? IsAnonymous { + get; set; + } + + /// + /// Poll type, defaults to + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PollType? Type { + get; set; + } + + /// + /// True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowsMultipleAnswers { + get; set; + } + + /// + /// 0-based identifier of the correct answer option, required for polls in quiz mode + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? CorrectOptionId { + get; set; + } + + /// + /// 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 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Explanation { + get; set; + } + + /// + /// Mode for parsing entities in the explanation. See + /// formatting options + /// for more details. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ExplanationParseMode { + get; set; + } + + /// + /// List of special entities that appear in the poll explanation, which can be specified instead + /// of + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? ExplanationEntities { + get; set; + } + + /// + /// Amount of time in seconds the poll will be active after creation, 5-600. Can't be used + /// together with . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? OpenPeriod { + get; set; + } + + /// + /// 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 . + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? CloseDate { + get; set; + } + + /// + /// Pass True, if the poll needs to be immediately closed. This can be useful for poll preview. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? IsClosed { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, question and + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Poll question, 1-300 characters + /// A list of answer options, 2-10 strings 1-100 characters each + public SendPollRequest(ChatId chatId, string question, IEnumerable options) + : base("sendPoll") { + ChatId = chatId; + Question = question; + Options = options; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendVideoNoteRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendVideoNoteRequest.cs new file mode 100644 index 0000000..1679f69 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendVideoNoteRequest.cs @@ -0,0 +1,136 @@ +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 { + + + /// + /// As of v.4.0, + /// Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method + /// to send video messages. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendVideoNoteRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Video note to send. Pass a as String to send a video + /// note that exists on the Telegram servers (recommended) or upload a new video using + /// multipart/form-data. Sending video notes by a URL is currently unsupported + /// + [JsonProperty(Required = Required.Always)] + public InputTelegramFile VideoNote { + get; + } + + /// + /// Duration of sent video in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Duration { + get; set; + } + + /// + /// Video width and height, i.e. diameter of the video message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Length { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and videoNote + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Video note to send. Pass a as String to send a video + /// note that exists on the Telegram servers (recommended) or upload a new video using + /// multipart/form-data. Sending video notes by a URL is currently unsupported + /// + public SendVideoNoteRequest(ChatId chatId, InputTelegramFile videoNote) + : base("sendVideoNote") { + ChatId = chatId; + VideoNote = videoNote; + } + + /// + public override HttpContent? ToHttpContent() { + HttpContent? httpContent; + if(VideoNote.FileType == FileType.Stream || Thumb?.FileType == FileType.Stream) { + var multipartContent = GenerateMultipartFormDataContent("video_note", "thumb"); + if(VideoNote.FileType == FileType.Stream) { + multipartContent.AddStreamContent( + content: VideoNote.Content!, + name: "video_note", + fileName: VideoNote.FileName + ); + } + + if(Thumb?.FileType == FileType.Stream) { + multipartContent.AddStreamContent( + content: Thumb.Content!, + name: "thumb", + fileName: Thumb.FileName + ); + } + + httpContent = multipartContent; + } else { + httpContent = base.ToHttpContent(); + } + + return httpContent; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendVideoRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendVideoRequest.cs new file mode 100644 index 0000000..ad2e862 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendVideoRequest.cs @@ -0,0 +1,174 @@ +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 { + + + /// + /// Use this method to send video files, Telegram clients support mp4 videos (other formats may be + /// sent as ). On success, the sent is returned. + /// Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendVideoRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Video to send. Pass a as String to send a video that + /// exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to + /// get a video from the Internet, or upload a new video using multipart/form-data + /// + [JsonProperty(Required = Required.Always)] + public InputOnlineFile Video { + get; + } + + /// + /// Duration of sent video in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Duration { + get; set; + } + + /// + /// Video width + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Width { + get; set; + } + + /// + /// Video height + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Height { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + /// Video caption (may also be used when resending videos by file_id), + /// 0-1024 characters after entities parsing + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + /// Pass True, if the uploaded video is suitable for streaming + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? SupportsStreaming { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and video + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Video to send. Pass a as String to send a video that + /// exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to + /// get a video from the Internet, or upload a new video using multipart/form-data + /// + public SendVideoRequest(ChatId chatId, InputOnlineFile video) + : base("sendVideo") { + ChatId = chatId; + Video = video; + } + + /// + public override HttpContent? ToHttpContent() { + HttpContent? httpContent; + if(Video.FileType == FileType.Stream || Thumb?.FileType == FileType.Stream) { + var multipartContent = GenerateMultipartFormDataContent("video", "thumb"); + if(Video.FileType == FileType.Stream) { + multipartContent.AddStreamContent( + content: Video.Content!, + name: "video", + fileName: Video.FileName + ); + } + + if(Thumb?.FileType == FileType.Stream) { + multipartContent.AddStreamContent( + content: Thumb.Content!, + name: "thumb", fileName: + Thumb.FileName + ); + } + + httpContent = multipartContent; + } else { + httpContent = base.ToHttpContent(); + } + + return httpContent; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/Messages/SendVoiceRequest.cs b/TelegramBot/Requests/Available methods/Messages/SendVoiceRequest.cs new file mode 100644 index 0000000..25dd470 --- /dev/null +++ b/TelegramBot/Requests/Available methods/Messages/SendVoiceRequest.cs @@ -0,0 +1,122 @@ +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 { + + + /// + /// Use this method to send audio files, if you want Telegram clients to display the file as a playable + /// voice message. For this to work, your audio must be in an .OGG file encoded with OPUS (other + /// formats may be sent as or ). On success, the sent + /// is returned. Bots can currently send voice messages of up to 50 MB in size, + /// this limit may be changed in the future. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendVoiceRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Audio file to send. Pass a 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 + /// + [JsonProperty(Required = Required.Always)] + public InputOnlineFile Voice { + get; + } + + /// + /// Voice message caption, 0-1024 characters after entities parsing + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + /// Duration of the voice message in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Duration { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and voice + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Audio file to send. Pass a 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 + /// + public SendVoiceRequest(ChatId chatId, InputOnlineFile voice) + : base("sendVoice") { + ChatId = chatId; + Voice = voice; + } + + /// + public override HttpContent? ToHttpContent() => + Voice.FileType switch { + FileType.Stream => ToMultipartFormDataContent(fileParameterName: "voice", inputFile: Voice), + _ => base.ToHttpContent() + }; + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/SetChatMenuButtonRequest.cs b/TelegramBot/Requests/Available methods/SetChatMenuButtonRequest.cs new file mode 100644 index 0000000..a16e0e5 --- /dev/null +++ b/TelegramBot/Requests/Available methods/SetChatMenuButtonRequest.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to change the bot’s menu button in a private chat, or the default menu button. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetChatMenuButtonRequest : RequestBase { + /// + /// Optional. Unique identifier for the target private chat. If not specified, default bot’s menu button + /// will be changed + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public long? ChatId { + get; set; + } + + /// + /// Optional. An object for the new bot’s menu button. Defaults to + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MenuButton? MenuButton { + get; set; + } + + /// + /// Initializes a new request + /// + public SetChatMenuButtonRequest() + : base("setChatMenuButton") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Available methods/SetMyDefaultAdministratorRightsRequest.cs b/TelegramBot/Requests/Available methods/SetMyDefaultAdministratorRightsRequest.cs new file mode 100644 index 0000000..2cfd865 --- /dev/null +++ b/TelegramBot/Requests/Available methods/SetMyDefaultAdministratorRightsRequest.cs @@ -0,0 +1,43 @@ + +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to change the default administrator rights requested by the bot when it's added as an + /// administrator to groups or channels. These rights will be suggested to users, but they are are free to + /// modify the list before adding the bot. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetMyDefaultAdministratorRightsRequest : RequestBase { + + /// + /// Optional. An object describing new default administrator rights. If not specified, the default administrator + /// rights will be cleared. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatAdministratorRights? Rights { + get; set; + } + + /// + /// Optional. Pass true to change the default administrator rights of the bot in channels. Otherwise, + /// the default administrator rights of the bot for groups and supergroups will be changed. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ForChannels { + get; set; + } + + /// + /// + /// + public SetMyDefaultAdministratorRightsRequest() + : base("setMyDefaultAdministratorRights") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/FileRequestBase.cs b/TelegramBot/Requests/FileRequestBase.cs new file mode 100644 index 0000000..e3aecde --- /dev/null +++ b/TelegramBot/Requests/FileRequestBase.cs @@ -0,0 +1,86 @@ +using System; +using System.Linq; +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Extensions; +using Telegram.Bot.Types.InputFiles; + +namespace Telegram.Bot.Requests { + + + /// + /// Represents an API request with a file + /// + /// Type of result expected in result + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class FileRequestBase : RequestBase { + /// + /// Initializes an instance of request + /// + /// Bot API method + protected FileRequestBase(string methodName) + : base(methodName) { + } + + /// + /// Initializes an instance of request + /// + /// Bot API method + /// HTTP method to use + protected FileRequestBase(string methodName, HttpMethod method) + : base(methodName, method) { + } + + /// + /// Generate multipart form data content + /// + /// + /// + /// + protected MultipartFormDataContent ToMultipartFormDataContent( + string fileParameterName, + InputFileStream inputFile) { + if(inputFile is null or { Content: null }) { + throw new ArgumentNullException(nameof(inputFile), $"{nameof(inputFile)} or it's content is null"); + } + + var multipartContent = GenerateMultipartFormDataContent(fileParameterName); + + multipartContent.AddStreamContent( + // Probably is a compiler bug, inputFile is already checked at this point +#pragma warning disable CA1062 + content: inputFile.Content, +#pragma warning restore CA1062 + name: fileParameterName, + fileName: inputFile.FileName + ); + + return multipartContent; + } + + /// + /// Generate multipart form data content + /// + /// + /// + protected MultipartFormDataContent GenerateMultipartFormDataContent(params string[] exceptPropertyNames) { + var multipartContent = new MultipartFormDataContent($"{Guid.NewGuid()}{DateTime.UtcNow.Ticks}"); + + var stringContents = JObject.FromObject(this) + .Properties() + .Where(prop => exceptPropertyNames.Contains(prop.Name) == false) + .Select(prop => new { + prop.Name, + Content = new StringContent(prop.Value.ToString()) + }); + + foreach(var strContent in stringContents) { + multipartContent.Add(content: strContent.Content, name: strContent.Name); + } + + return multipartContent; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Games/GetGameHighScoresRequest.cs b/TelegramBot/Requests/Games/GetGameHighScoresRequest.cs new file mode 100644 index 0000000..9e5105a --- /dev/null +++ b/TelegramBot/Requests/Games/GetGameHighScoresRequest.cs @@ -0,0 +1,60 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to get data for high score tables. Will return the score of the specified user + /// and several of their neighbors in a game. On success, returns an Array of + /// objects. + /// + /// + /// This method will currently return scores for the target user, plus two of their closest neighbors + /// on each side. Will also return the top three users if the user and his neighbors are not among + /// them. Please note that this behavior is subject to change. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetGameHighScoresRequest : RequestBase, IUserTargetable, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Unique identifier for the target chat + /// + [JsonProperty(Required = Required.Always)] + public long ChatId { + get; + } + + /// + ChatId IChatTargetable.ChatId => ChatId; + + /// + /// Identifier of the sent message + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + /// Initializes a new request with userId, chatId and messageId + /// + /// Target user id + /// Unique identifier for the target chat + /// Identifier of the sent message + public GetGameHighScoresRequest(long userId, long chatId, int messageId) + : base("getGameHighScores") { + UserId = userId; + ChatId = chatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Games/GetInlineGameHighScoresRequest.cs b/TelegramBot/Requests/Games/GetInlineGameHighScoresRequest.cs new file mode 100644 index 0000000..67473da --- /dev/null +++ b/TelegramBot/Requests/Games/GetInlineGameHighScoresRequest.cs @@ -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 { + + + /// + /// Use this method to get data for high score tables. Will return the score of the specified user + /// and several of their neighbors in a game. On success, returns an Array of + /// objects. + /// + /// + /// This method will currently return scores for the target user, plus two of their closest neighbors + /// on each side. Will also return the top three users if the user and his neighbors are not among them. + /// Please note that this behavior is subject to change. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetInlineGameHighScoresRequest : RequestBase, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public string InlineMessageId { + get; + } + + /// + /// Initializes a new request with userId and inlineMessageId + /// + /// User identifier + /// Identifier of the inline message + public GetInlineGameHighScoresRequest(long userId, string inlineMessageId) + : base("getGameHighScores") { + UserId = userId; + InlineMessageId = inlineMessageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Games/SendGameRequest.cs b/TelegramBot/Requests/Games/SendGameRequest.cs new file mode 100644 index 0000000..64264c7 --- /dev/null +++ b/TelegramBot/Requests/Games/SendGameRequest.cs @@ -0,0 +1,80 @@ +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 { + + + /// + /// Use this method to send a game. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendGameRequest : RequestBase, IChatTargetable { + /// + /// Unique identifier for the target chat + /// + [JsonProperty(Required = Required.Always)] + public long ChatId { + get; + } + + /// + ChatId IChatTargetable.ChatId => ChatId; + + /// + /// Short name of the game, serves as the unique identifier for the game. Set up your games + /// via @Botfather + /// + [JsonProperty(Required = Required.Always)] + public string GameShortName { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and gameShortName + /// + /// Unique identifier for the target chat + /// + /// Short name of the game, serves as the unique identifier for the game. Set up your games via + /// @Botfather + /// + public SendGameRequest(long chatId, string gameShortName) + : base("sendGame") { + ChatId = chatId; + GameShortName = gameShortName; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Games/SetGameScoreRequest.cs b/TelegramBot/Requests/Games/SetGameScoreRequest.cs new file mode 100644 index 0000000..770e6a8 --- /dev/null +++ b/TelegramBot/Requests/Games/SetGameScoreRequest.cs @@ -0,0 +1,83 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to set the score of the specified user in a game. On success returns the edited + /// . Returns an error, if the new score is not greater than the user's current + /// score in the chat and is false. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetGameScoreRequest : RequestBase, IUserTargetable, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// New score, must be non-negative + /// + [JsonProperty(Required = Required.Always)] + public int Score { + get; + } + + /// + /// Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes + /// or banning cheaters. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? Force { + get; set; + } + + /// + /// Pass true, if the game message should not be automatically edited to include + /// the current scoreboard + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableEditMessage { + get; set; + } + + /// + /// Unique identifier for the target chat + /// + [JsonProperty(Required = Required.Always)] + public long ChatId { + get; + } + + /// + ChatId IChatTargetable.ChatId => ChatId; + + /// + /// Identifier of the sent message + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + /// Initializes a new request + /// + /// User identifier + /// New score, must be non-negative + /// Unique identifier for the target chat + /// Identifier of the sent message + public SetGameScoreRequest(long userId, int score, long chatId, int messageId) + : base("setGameScore") { + UserId = userId; + Score = score; + ChatId = chatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Games/SetInlineGameScoreRequest.cs b/TelegramBot/Requests/Games/SetInlineGameScoreRequest.cs new file mode 100644 index 0000000..1d8ae96 --- /dev/null +++ b/TelegramBot/Requests/Games/SetInlineGameScoreRequest.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to set the score of the specified user in a game. On success returns true. + /// Returns an error, if the new score is not greater than the user's current score in the chat and + /// is false. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetInlineGameScoreRequest : RequestBase, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// New score, must be non-negative + /// + [JsonProperty(Required = Required.Always)] + public int Score { + get; + } + + /// + /// Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes + /// or banning cheaters. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? Force { + get; set; + } + + /// + /// Pass True, if the game message should not be automatically edited to include the current scoreboard + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableEditMessage { + get; set; + } + + /// + [JsonProperty(Required = Required.Always)] + public string InlineMessageId { + get; + } + + /// + /// Initializes a new request with userId, inlineMessageId and new score + /// + /// User identifier + /// New score, must be non-negative + /// Identifier of the inline message + public SetInlineGameScoreRequest(long userId, int score, string inlineMessageId) + : base("setGameScore") { + UserId = userId; + Score = score; + InlineMessageId = inlineMessageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Getting Updates/DeleteWebhookRequest.cs b/TelegramBot/Requests/Getting Updates/DeleteWebhookRequest.cs new file mode 100644 index 0000000..2298ef5 --- /dev/null +++ b/TelegramBot/Requests/Getting Updates/DeleteWebhookRequest.cs @@ -0,0 +1,30 @@ +// ReSharper disable once CheckNamespace +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to remove webhook integration if you decide to switch back to + /// . Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class DeleteWebhookRequest : RequestBase { + /// + /// Pass True to drop all pending updates + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DropPendingUpdates { + get; set; + } + + /// + /// Initializes a new request + /// + public DeleteWebhookRequest() + : base("deleteWebhook") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Getting Updates/GetUpdatesRequest.cs b/TelegramBot/Requests/Getting Updates/GetUpdatesRequest.cs new file mode 100644 index 0000000..9ef11dd --- /dev/null +++ b/TelegramBot/Requests/Getting Updates/GetUpdatesRequest.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to receive incoming updates using long polling + /// (wiki). + /// An Array of objects is returned. + /// + /// + /// + /// This method will not work if an outgoing webhook is set up. + /// + /// In order to avoid getting duplicate updates, recalculate + /// after each server response. + /// + /// + /// + [JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetUpdatesRequest : RequestBase { + /// + /// Identifier of the first update to be returned. Must be greater by one than the highest among + /// the identifiers of previously received updates. By default, updates starting with the earliest + /// unconfirmed update are returned. An update is considered confirmed as soon as + /// is called with an higher than its + /// . The negative offset can be specified to retrieve updates + /// starting from -offset update from the end of the updates queue. + /// All previous updates will forgotten. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Offset { + get; set; + } + + /// + /// Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults to 100 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Limit { + get; set; + } + + /// + /// Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be positive, + /// short polling should be used for testing purposes only. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Timeout { + get; set; + } + + /// + /// A list of the update types you want your bot to receive. For example, specify + /// [, , + /// ] to only receive updates of these types. + /// See for a complete list of available update types. Specify + /// an empty list to receive all update types except + /// (default). If not specified, the previous setting will be used. + /// + /// + /// Please note that this parameter doesn't affect updates created before the call to the + /// getUpdates, so unwanted updates may be received for a short period of time. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? AllowedUpdates { + get; set; + } + + /// + /// Initializes a new GetUpdates request + /// + public GetUpdatesRequest() + : base("getUpdates") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Getting Updates/GetWebhookInfoRequest.cs b/TelegramBot/Requests/Getting Updates/GetWebhookInfoRequest.cs new file mode 100644 index 0000000..d9b3e57 --- /dev/null +++ b/TelegramBot/Requests/Getting Updates/GetWebhookInfoRequest.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to get current webhook status. Requires no parameters. On success, returns + /// a object. If the bot is using , + /// will return an object with the field empty. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetWebhookInfoRequest : ParameterlessRequest { + /// + /// Initializes a new request + /// + public GetWebhookInfoRequest() + : base("getWebhookInfo") { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Getting Updates/SetWebhookRequest.cs b/TelegramBot/Requests/Getting Updates/SetWebhookRequest.cs new file mode 100644 index 0000000..20711c6 --- /dev/null +++ b/TelegramBot/Requests/Getting Updates/SetWebhookRequest.cs @@ -0,0 +1,122 @@ +using System.Collections.Generic; +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to specify a url and receive incoming updates via an outgoing webhook. + /// Whenever there is an update for the bot, we will send an HTTPS POST request to the + /// specified url, containing a JSON-serialized . In case of + /// an unsuccessful request, we will give up after a reasonable amount of attempts. + /// Returns true on success. + /// + /// If you'd like to make sure that the Webhook request comes from Telegram, we recommend + /// using a secret path in the URL, e.g. https://www.example.com/<token>. + /// Since nobody else knows your bot’s token, you can be pretty sure it's us. + /// + /// + /// + /// + /// You will not be able to receive updates using for as long as an outgoing + /// webhook is set up. + /// + /// To use a self-signed certificate, you need to upload your + /// public key certificate using + /// parameter. Please upload as , sending + /// a String will not work. + /// + /// Ports currently supported for Webhooks: 443, 80, 88, 8443 + /// + /// If you're having any trouble setting up webhooks, please check out this + /// amazing guide to Webhooks. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetWebhookRequest : FileRequestBase { + /// + /// HTTPS url to send updates to. Use an empty string to remove webhook integration + /// + [JsonProperty(Required = Required.Always)] + public string Url { + get; + } + + /// + /// Upload your public key certificate so that the root certificate in use can be checked. See + /// our self-signed guide for details + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputFileStream? Certificate { + get; set; + } + + /// + /// The fixed IP address which will be used to send webhook requests instead of the + /// IP address resolved through DNS + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? IpAddress { + get; set; + } + + /// + /// Maximum allowed number of simultaneous HTTPS connections to the webhook for update + /// delivery, 1-100. Defaults to 40. Use lower values to limit the load on your + /// bot’s server, and higher values to increase your bot’s throughput + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MaxConnections { + get; set; + } + + /// + /// A list of the update types you want your bot to receive. For example, specify + /// [, , + /// ] to only receive updates of these types. + /// See for a complete list of available update types. + /// Specify an empty list to receive all update types except + /// (default). If not specified, + /// the previous setting will be used + /// + /// + /// Please note that this parameter doesn't affect updates created before the call to the + /// , so unwanted updates may be received for a short period of time. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? AllowedUpdates { + get; set; + } + + /// + /// Pass true to drop all pending updates + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DropPendingUpdates { + get; set; + } + + /// + /// Initializes a new request with uri + /// + /// + /// HTTPS url to send updates to. Use an empty string to remove webhook integration + /// + public SetWebhookRequest(string url) + : base("setWebhook") { + Url = url; + } + + /// + public override HttpContent? ToHttpContent() => + Certificate is null + ? base.ToHttpContent() + : ToMultipartFormDataContent("certificate", Certificate); + } + +} \ No newline at end of file diff --git a/TelegramBot/Requests/Inline Mode/AnswerInlineQueryRequest.cs b/TelegramBot/Requests/Inline Mode/AnswerInlineQueryRequest.cs new file mode 100644 index 0000000..d1701bc --- /dev/null +++ b/TelegramBot/Requests/Inline Mode/AnswerInlineQueryRequest.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.InlineQueryResults; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to send answers to an inline query. On success, true is returned. + /// + /// + /// No more than 50 results per query are allowed. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class AnswerInlineQueryRequest : RequestBase { + /// + /// Unique identifier for the answered query + /// + [JsonProperty(Required = Required.Always)] + public string InlineQueryId { + get; + } + + /// + /// An array of results for the inline query + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable Results { + get; + } + + /// + /// The maximum amount of time in seconds that the result of the + /// inline query may be cached on the server. Defaults to 300 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? CacheTime { + get; set; + } + + /// + /// Pass true, if results may be cached on the server side only for the user that sent + /// the query. By default, results may be returned to any user who sends the same query + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? IsPersonal { + get; set; + } + + /// + /// Pass the offset that a client should send in the next query with the same text to + /// receive more results. Pass an empty string if there are no more results or if you + /// don't support pagination. Offset length can't exceed 64 bytes + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? NextOffset { + get; set; + } + + /// + /// If passed, clients will display a button with specified text that switches the + /// user to a private chat with the bot and sends the bot a start message with the + /// parameter + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? SwitchPmText { + get; set; + } + + /// + /// Deep-linking parameter for + /// the /start message sent to the bot when user presses the switch button. + /// 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed. + /// + /// + /// An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube + /// account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube + /// account' button above the results, or even before showing any. The user presses the button, + /// switches to a private chat with the bot and, in doing so, passes a start parameter that + /// instructs the bot to return an oauth link. Once done, the bot can offer a + /// button so that the + /// user can easily return to the chat where they wanted to use the bot’s inline capabilities. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? SwitchPmParameter { + get; set; + } + + /// + /// Initializes a new request with inlineQueryId and an array of + /// + /// Unique identifier for the answered query + /// An array of results for the inline query + public AnswerInlineQueryRequest(string inlineQueryId, IEnumerable results) + : base("answerInlineQuery") { + InlineQueryId = inlineQueryId; + Results = results; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Inline Mode/AnswerWebAppQueryRequest.cs b/TelegramBot/Requests/Inline Mode/AnswerWebAppQueryRequest.cs new file mode 100644 index 0000000..5d0580a --- /dev/null +++ b/TelegramBot/Requests/Inline Mode/AnswerWebAppQueryRequest.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; +using Telegram.Bot.Types.InlineQueryResults; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to set the result of an interaction with a + /// Web App and send a corresponding message on behalf of the + /// user to the chat from which the query originated. On success, a object is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class AnswerWebAppQueryRequest : RequestBase { + /// + /// Unique identifier for the query to be answered + /// + [JsonProperty(Required = Required.Always)] + public string WebAppQueryId { + get; + } + + /// + /// An object describing the message to be sent + /// + [JsonProperty(Required = Required.Always)] + public InlineQueryResult Result { + get; + } + + /// + /// Initializes a new request with and a + /// + /// Unique identifier for the query to be answered + /// An object describing the message to be sent + public AnswerWebAppQueryRequest(string webAppQueryId, InlineQueryResult result) + : base("answerWebAppQuery") { + WebAppQueryId = webAppQueryId; + Result = result; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/ParameterlessRequest.cs b/TelegramBot/Requests/ParameterlessRequest.cs new file mode 100644 index 0000000..89ddf15 --- /dev/null +++ b/TelegramBot/Requests/ParameterlessRequest.cs @@ -0,0 +1,37 @@ +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Requests { + + + /// + /// Represents a request that doesn't require any parameters + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ParameterlessRequest : RequestBase { + /// + /// Initializes an instance of + /// + /// Name of request method + public ParameterlessRequest(string methodName) + : base(methodName) { + } + + /// + /// Initializes an instance of + /// + /// Name of request method + /// HTTP request method + public ParameterlessRequest(string methodName, HttpMethod method) + : base(methodName, method) { + } + + /// + public override HttpContent? ToHttpContent() => + IsWebhookResponse + ? base.ToHttpContent() + : default; + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Payments/AnswerPreCheckoutQueryRequest.cs b/TelegramBot/Requests/Payments/AnswerPreCheckoutQueryRequest.cs new file mode 100644 index 0000000..4e98ca7 --- /dev/null +++ b/TelegramBot/Requests/Payments/AnswerPreCheckoutQueryRequest.cs @@ -0,0 +1,74 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Once the user has confirmed their payment and shipping details, the Bot API sends the final + /// confirmation in the form of an with the field + /// . Use this method to respond to such pre-checkout + /// queries. On success, true is returned. + /// + /// + /// The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class AnswerPreCheckoutQueryRequest : RequestBase { + /// + /// Unique identifier for the query to be answered + /// + [JsonProperty(Required = Required.Always)] + public string PreCheckoutQueryId { + get; + } + + /// + /// Specify True if everything is alright (goods are available, etc.) and the + /// bot is ready to proceed with the order. Use False if there are any problems. + /// + [JsonProperty(Required = Required.Always)] + public bool Ok { + get; + } + + /// + /// Required if is False. Error message in human readable form that explains + /// the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought + /// the last of our amazing black T-shirts while you were busy filling out your payment details. + /// Please choose a different color or garment!"). Telegram will display this message to the user. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ErrorMessage { + get; + } + + /// + /// Initializes a new successful answerPreCheckoutQuery request + /// + /// Unique identifier for the query to be answered + public AnswerPreCheckoutQueryRequest(string preCheckoutQueryId) + : base("answerPreCheckoutQuery") { + PreCheckoutQueryId = preCheckoutQueryId; + Ok = true; + } + + /// + /// Initializes a new failing answerPreCheckoutQuery request with error message + /// + /// Unique identifier for the query to be answered + /// + /// Required if is true. Error message in human readable form that explains the + /// reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of + /// our amazing black T-shirts while you were busy filling out your payment details. Please + /// choose a different color or garment!"). Telegram will display this message to the user. + /// + public AnswerPreCheckoutQueryRequest(string preCheckoutQueryId, string errorMessage) + : base("answerPreCheckoutQuery") { + PreCheckoutQueryId = preCheckoutQueryId; + Ok = false; + ErrorMessage = errorMessage; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Payments/AnswerShippingQueryRequest.cs b/TelegramBot/Requests/Payments/AnswerShippingQueryRequest.cs new file mode 100644 index 0000000..7ff1874 --- /dev/null +++ b/TelegramBot/Requests/Payments/AnswerShippingQueryRequest.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Payments; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// If you sent an invoice requesting a shipping address and the parameter + /// was specified, the Bot API will send an + /// with a field to the + /// bot. Use this method to reply to shipping queries. On success, true is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class AnswerShippingQueryRequest : RequestBase { + /// + /// Unique identifier for the query to be answered + /// + [JsonProperty(Required = Required.Always)] + public string ShippingQueryId { + get; + } + + /// + /// Specify true if delivery to the specified address is possible and false + /// if there are any problems (for example, if delivery to the specified address is not possible) + /// + [JsonProperty(Required = Required.Always)] + public bool Ok { + get; + } + + /// + /// Required if is True. An array of available shipping options. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? ShippingOptions { + get; + } + + /// + /// Required if is False. Error message in human readable form that explains + /// why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address + /// is unavailable'). Telegram will display this message to the user. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ErrorMessage { + get; + } + + /// + /// Initializes a new failing answerShippingQuery request with error message + /// + /// Unique identifier for the query to be answered + /// Error message in human readable form + public AnswerShippingQueryRequest(string shippingQueryId, string errorMessage) + : base("answerShippingQuery") { + ShippingQueryId = shippingQueryId; + Ok = false; + ErrorMessage = errorMessage; + } + + /// + /// Initializes a new successful answerShippingQuery request with shipping options + /// + /// Unique identifier for the query to be answered + /// A JSON-serialized array of available shipping options + public AnswerShippingQueryRequest( + string shippingQueryId, + IEnumerable shippingOptions) : base("answerShippingQuery") { + ShippingQueryId = shippingQueryId; + Ok = true; + ShippingOptions = shippingOptions; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Payments/SendInvoiceRequest.cs b/TelegramBot/Requests/Payments/SendInvoiceRequest.cs new file mode 100644 index 0000000..e332395 --- /dev/null +++ b/TelegramBot/Requests/Payments/SendInvoiceRequest.cs @@ -0,0 +1,283 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Payments; +using Telegram.Bot.Types.ReplyMarkups; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to send invoices. On success, the sent is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendInvoiceRequest : RequestBase, IChatTargetable { + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + [JsonProperty(Required = Required.Always)] + public long ChatId { + get; + } + + /// + ChatId IChatTargetable.ChatId => ChatId; + + /// + /// Product name, 1-32 characters + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Product description, 1-255 characters + /// + [JsonProperty(Required = Required.Always)] + public string Description { + get; + } + + /// + /// Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, + /// use for your internal processes + /// + [JsonProperty(Required = Required.Always)] + public string Payload { + get; + } + + /// + /// Payments provider token, obtained via @Botfather + /// + [JsonProperty(Required = Required.Always)] + public string ProviderToken { + get; + } + + /// + /// Three-letter ISO 4217 currency code, see + /// more on currencies + /// + [JsonProperty(Required = Required.Always)] + public string Currency { + get; + } + + /// + /// Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, + /// delivery tax, bonus, etc.) + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable Prices { + get; + } + + /// + /// The maximum accepted amount for tips in the smallest units of the currency. + /// For example, for a maximum tip of US$ 1.45 pass = 145. + /// See the exp parameter in + /// currencies.json, + /// it shows the number of digits past the decimal point for each currency (2 for the majority + /// of currencies). Defaults to 0 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MaxTipAmount { + get; set; + } + + /// + /// An array of suggested amounts of tips in the smallest units of the currency. At most 4 + /// suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a + /// strictly increased order and must not exceed + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? SuggestedTipAmounts { + get; set; + } + + /// + /// Unique deep-linking parameter. If left empty, forwarded copies of the sent message will + /// have a Pay button, allowing multiple users to pay directly from the forwarded message, + /// using the same invoice. If non-empty, forwarded copies of the sent message will have a URL + /// button with a deep link to the bot (instead of a Pay button), with the value used as the + /// start parameter + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? StartParameter { + get; set; + } + + /// + /// A JSON-serialized data about the invoice, which will be shared with the payment provider. + /// A detailed description of required fields should be provided by the payment provider. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ProviderData { + get; set; + } + + /// + /// URL of the product photo for the invoice. Can be a photo of the goods or a marketing image + /// for a service. People like it better when they see what they are paying for. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? PhotoUrl { + get; set; + } + + /// + /// Photo size + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? PhotoSize { + get; set; + } + + /// + /// Photo width + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? PhotoWidth { + get; set; + } + + /// + /// Photo height + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? PhotoHeight { + get; set; + } + + /// + /// Pass True, if you require the user's full name to complete the order + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NeedName { + get; set; + } + + /// + /// Pass True, if you require the user's phone number to complete the order + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NeedPhoneNumber { + get; set; + } + + /// + /// Pass True, if you require the user's email to complete the order + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NeedEmail { + get; set; + } + + /// + /// Pass True, if you require the user's shipping address to complete the order + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NeedShippingAddress { + get; set; + } + + /// + /// Pass True, if user's phone number should be sent to provider + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? SendPhoneNumberToProvider { + get; set; + } + + /// + ///Pass True, if user's email address should be sent to provider + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? SendEmailToProvider { + get; set; + } + + /// + /// Pass True, if the final price depends on the shipping method + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? IsFlexible { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, title, description, payload, providerToken, currency + /// and an array of + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Product name, 1-32 characters + /// Product description, 1-255 characters + /// Bot-defined invoice payload, 1-128 bytes + /// + /// Payments provider token, obtained via @Botfather + /// + /// + /// Three-letter ISO 4217 currency code, see + /// more on currencies + /// + /// + /// Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, + /// delivery tax, bonus, etc.) + /// + public SendInvoiceRequest( + long chatId, + string title, + string description, + string payload, + string providerToken, + string currency, + IEnumerable prices) : base("sendInvoice") { + ChatId = chatId; + Title = title; + Description = description; + Payload = payload; + ProviderToken = providerToken; + Currency = currency; + Prices = prices; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/RequestBase.cs b/TelegramBot/Requests/RequestBase.cs new file mode 100644 index 0000000..2ebff8c --- /dev/null +++ b/TelegramBot/Requests/RequestBase.cs @@ -0,0 +1,68 @@ +using System.Net.Http; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; + +namespace Telegram.Bot.Requests { + + + /// + /// Represents an API request + /// + /// Type of result expected in result + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class RequestBase : IRequest { + /// + [JsonIgnore] + public HttpMethod Method { + get; + } + + /// + [JsonIgnore] + public string MethodName { + get; + } + + /// + /// Initializes an instance of request + /// + /// Bot API method + protected RequestBase(string methodName) + : this(methodName, HttpMethod.Post) { + } + + /// + /// Initializes an instance of request + /// + /// Bot API method + /// HTTP method to use + protected RequestBase(string methodName, HttpMethod method) { + MethodName = methodName; + Method = method; + } + + /// + /// Generate content of HTTP message + /// + /// Content of HTTP request + public virtual HttpContent? ToHttpContent() { + string payload = JsonConvert.SerializeObject(this); + return new StringContent(content: payload, encoding: Encoding.UTF8, mediaType: "application/json"); + } + + /// + [JsonIgnore] + public bool IsWebhookResponse { + get; set; + } + + /// + /// If is set to is set to the method + /// name, otherwise it won't be serialized + /// + [JsonProperty("method", DefaultValueHandling = DefaultValueHandling.Ignore)] + internal string? WebHookMethodName => IsWebhookResponse ? MethodName : default; + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/AddAnimatedStickerToSetRequest.cs b/TelegramBot/Requests/Stickers/AddAnimatedStickerToSetRequest.cs new file mode 100644 index 0000000..a799622 --- /dev/null +++ b/TelegramBot/Requests/Stickers/AddAnimatedStickerToSetRequest.cs @@ -0,0 +1,48 @@ +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this request to add a new animated sticker to a set created by the bot. Static sticker sets + /// can have up to 120 stickers. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class AddAnimatedStickerToSetRequest : AddStickerToSetRequest { + /// + /// WEBM video with the sticker, uploaded using multipart/form-data. + /// + /// for technical requirements + /// + [JsonProperty(Required = Required.Always)] + public InputFileStream TgsSticker { + get; + } + + /// + /// + /// WEBM video with the sticker, uploaded using multipart/form-data. + /// + /// for technical requirements + /// +#pragma warning disable CS1573 + public AddAnimatedStickerToSetRequest( + long userId, + string name, + InputFileStream tgsSticker, + string emojis) + : base(userId, name, emojis) => + TgsSticker = tgsSticker; +#pragma warning restore CS1573 + + /// + public override HttpContent? ToHttpContent() => + TgsSticker.Content is not null + ? ToMultipartFormDataContent(fileParameterName: "tgs_sticker", inputFile: TgsSticker) + : base.ToHttpContent(); + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/AddStaticStickerToSetRequest.cs b/TelegramBot/Requests/Stickers/AddStaticStickerToSetRequest.cs new file mode 100644 index 0000000..dfc3cf2 --- /dev/null +++ b/TelegramBot/Requests/Stickers/AddStaticStickerToSetRequest.cs @@ -0,0 +1,57 @@ +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this request to add a new static sticker to a set created by the bot. Static sticker sets + /// can have up to 120 stickers. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class AddStaticStickerToSetRequest : AddStickerToSetRequest { + /// + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must + /// not exceed 512px, and either width or height must be exactly 512px. + /// + /// + /// Pass a as a String to send a file that already + /// exists on the Telegram servers, 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 + /// + /// + [JsonProperty(Required = Required.Always)] + public InputOnlineFile PngSticker { + get; + } + + /// + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not + /// exceed 512px, and either width or height must be exactly 512px. Pass a + /// as a String to send a file that + /// already exists on the Telegram servers, 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 + /// +#pragma warning disable CS1573 + public AddStaticStickerToSetRequest( + long userId, + string name, + InputOnlineFile pngSticker, + string emojis) + : base(userId, name, emojis) => + PngSticker = pngSticker; +#pragma warning restore CS1573 + + /// + public override HttpContent? ToHttpContent() => + PngSticker.FileType == FileType.Stream + ? ToMultipartFormDataContent(fileParameterName: "png_sticker", inputFile: PngSticker) + : base.ToHttpContent(); + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/AddStickerToSetRequest.cs b/TelegramBot/Requests/Stickers/AddStickerToSetRequest.cs new file mode 100644 index 0000000..5fd513b --- /dev/null +++ b/TelegramBot/Requests/Stickers/AddStickerToSetRequest.cs @@ -0,0 +1,62 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this request to add a new sticker to a set created by the bot. Static sticker sets + /// can have up to 120 stickers. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class AddStickerToSetRequest : FileRequestBase, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Sticker set name + /// + [JsonProperty(Required = Required.Always)] + public string Name { + get; + } + + /// + /// One or more emoji corresponding to the sticker + /// + [JsonProperty(Required = Required.Always)] + public string Emojis { + get; + } + + /// + /// An object for position where the mask should be placed on faces + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MaskPosition? MaskPosition { + get; set; + } + + /// + /// + /// + /// User identifier + /// Sticker set name + /// One or more emoji corresponding to the sticker + protected AddStickerToSetRequest( + long userId, + string name, + string emojis) + : base("addStickerToSet") { + UserId = userId; + Name = name; + Emojis = emojis; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/AddVideoStickerToSetRequest.cs b/TelegramBot/Requests/Stickers/AddVideoStickerToSetRequest.cs new file mode 100644 index 0000000..a85a0c9 --- /dev/null +++ b/TelegramBot/Requests/Stickers/AddVideoStickerToSetRequest.cs @@ -0,0 +1,48 @@ +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this request to add a new video sticker to a set created by the bot. Static sticker sets + /// can have up to 120 stickers. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class AddVideoStickerToSetRequest : AddStickerToSetRequest { + /// + /// WEBM video with the sticker, uploaded using multipart/form-data. + /// + /// for technical requirements + /// + [JsonProperty(Required = Required.Always)] + public InputFileStream WebmSticker { + get; + } + + /// + /// + /// WEBM video with the sticker, uploaded using multipart/form-data. + /// + /// for technical requirements + /// +#pragma warning disable CS1573 + public AddVideoStickerToSetRequest( + long userId, + string name, + InputFileStream webmSticker, + string emojis) + : base(userId, name, emojis) => + WebmSticker = webmSticker; +#pragma warning restore CS1573 + + /// + public override HttpContent? ToHttpContent() => + WebmSticker.Content is not null + ? ToMultipartFormDataContent(fileParameterName: "webm_sticker", inputFile: WebmSticker) + : base.ToHttpContent(); + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/CreateNewAnimatedStickerSetRequest.cs b/TelegramBot/Requests/Stickers/CreateNewAnimatedStickerSetRequest.cs new file mode 100644 index 0000000..bd3de7c --- /dev/null +++ b/TelegramBot/Requests/Stickers/CreateNewAnimatedStickerSetRequest.cs @@ -0,0 +1,49 @@ +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to create a new animated sticker set owned by a user. The bot will be able to + /// edit the sticker set thus created. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class CreateNewAnimatedStickerSetRequest : CreateNewStickerSetRequest { + /// + /// TGS animation with the sticker, uploaded using multipart/form-data. See + /// + /// for technical requirements + /// + [JsonProperty(Required = Required.Always)] + public InputFileStream TgsSticker { + get; + } + + /// + /// + /// TGS animation with the sticker, uploaded using multipart/form-data. See + /// + /// for technical requirements + /// +#pragma warning disable CS1573 + public CreateNewAnimatedStickerSetRequest( + long userId, + string name, + string title, + InputFileStream tgsSticker, + string emojis) : base(userId, name, title, emojis) { + TgsSticker = tgsSticker; + } +#pragma warning restore CS1573 + + /// + public override HttpContent? ToHttpContent() => + TgsSticker.Content is not null + ? ToMultipartFormDataContent(fileParameterName: "tgs_sticker", inputFile: TgsSticker) + : base.ToHttpContent(); + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/CreateNewStaticStickerSetRequest.cs b/TelegramBot/Requests/Stickers/CreateNewStaticStickerSetRequest.cs new file mode 100644 index 0000000..71038ea --- /dev/null +++ b/TelegramBot/Requests/Stickers/CreateNewStaticStickerSetRequest.cs @@ -0,0 +1,55 @@ +using System; +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to create a new static sticker set owned by a user. The bot will be able to edit + /// the sticker set thus created. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class CreateNewStaticStickerSetRequest : CreateNewStickerSetRequest { + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must + /// not exceed 512px, and either width or height must be exactly 512px. Pass a + /// as a String to send a file that + /// already exists on the Telegram servers, 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 + /// + [JsonProperty(Required = Required.Always)] + public InputFileStream PngSticker { + get; + } + + /// + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must + /// not exceed 512px, and either width or height must be exactly 512px. Pass a + /// as a String to send a file that + /// already exists on the Telegram servers, 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 + /// +#pragma warning disable CS1573 + public CreateNewStaticStickerSetRequest( + long userId, + string name, + string title, + InputOnlineFile pngSticker, + string emojis) : base(userId, name, title, emojis) { + PngSticker = pngSticker ?? throw new ArgumentNullException(nameof(pngSticker), "Sticker is null"); + } +#pragma warning restore CS1573 + + /// + public override HttpContent? ToHttpContent() => + PngSticker.FileType == FileType.Stream + ? ToMultipartFormDataContent(fileParameterName: "png_sticker", inputFile: PngSticker) + : base.ToHttpContent(); + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/CreateNewStickerSetRequest.cs b/TelegramBot/Requests/Stickers/CreateNewStickerSetRequest.cs new file mode 100644 index 0000000..c1a43dc --- /dev/null +++ b/TelegramBot/Requests/Stickers/CreateNewStickerSetRequest.cs @@ -0,0 +1,85 @@ +using Newtonsoft.Json; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// + /// + public abstract class CreateNewStickerSetRequest : FileRequestBase, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). + /// Can contain only english letters, digits and underscores. Must begin with a letter, can't + /// contain consecutive underscores and must end in "_by_<bot username>". + /// <bot_username> is case insensitive. 1-64 characters + /// + [JsonProperty(Required = Required.Always)] + public string Name { + get; + } + + /// + /// Sticker set title, 1-64 characters + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// One or more emoji corresponding to the sticker + /// + [JsonProperty(Required = Required.Always)] + public string Emojis { + get; + } + + /// + /// Pass True, if a set of mask stickers should be created + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ContainsMasks { + get; set; + } + + /// + /// An object for position where the mask should be placed on faces + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MaskPosition? MaskPosition { + get; set; + } + + /// + /// Initializes a new request with userId, name and emojis + /// + /// User identifier of sticker set owner + /// + /// Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). + /// Can contain only english letters, digits and underscores. Must begin with a letter, can't + /// contain consecutive underscores and must end in "_by_<bot username>". + /// <bot_username> is case insensitive. 1-64 characters + /// + /// Sticker set title, 1-64 characters + /// One or more emoji corresponding to the sticker + protected CreateNewStickerSetRequest( + long userId, + string name, + string title, + string emojis) : base("createNewStickerSet") { + UserId = userId; + Name = name; + Title = title; + Emojis = emojis; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/CreateNewVideoStickerSetRequest.cs b/TelegramBot/Requests/Stickers/CreateNewVideoStickerSetRequest.cs new file mode 100644 index 0000000..5afe45b --- /dev/null +++ b/TelegramBot/Requests/Stickers/CreateNewVideoStickerSetRequest.cs @@ -0,0 +1,51 @@ +using System; +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to create a new video sticker set owned by a user. The bot will be able to edit + /// the sticker set thus created. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class CreateNewVideoStickerSetRequest : CreateNewStickerSetRequest { + /// + /// WEBM animation with the sticker, uploaded using multipart/form-data. See + /// + /// for technical requirements + /// + [JsonProperty(Required = Required.Always)] + public InputFileStream WebmSticker { + get; + } + + /// + /// + /// WEBM animation with the sticker, uploaded using multipart/form-data. See + /// + /// for technical requirements + /// +#pragma warning disable CS1573 + public CreateNewVideoStickerSetRequest( + long userId, + string name, + string title, + InputFileStream webmSticker, + string emojis) : base(userId, name, title, emojis) { + WebmSticker = webmSticker ?? throw new ArgumentNullException(nameof(webmSticker), "Sticker is null"); + } +#pragma warning restore CS1573 + + /// + public override HttpContent? ToHttpContent() => + WebmSticker.FileType == FileType.Stream + ? ToMultipartFormDataContent(fileParameterName: "webm_sticker", inputFile: WebmSticker) + : base.ToHttpContent(); + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/DeleteStickerFromSetRequest.cs b/TelegramBot/Requests/Stickers/DeleteStickerFromSetRequest.cs new file mode 100644 index 0000000..01f911b --- /dev/null +++ b/TelegramBot/Requests/Stickers/DeleteStickerFromSetRequest.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to delete a sticker from a set created by the bot. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class DeleteStickerFromSetRequest : RequestBase { + /// + /// File identifier of the sticker + /// + [JsonProperty(Required = Required.Always)] + public string Sticker { + get; + } + + /// + /// Initializes a new request with sticker + /// + /// File identifier of the sticker + public DeleteStickerFromSetRequest(string sticker) + : base("deleteStickerFromSet") { + Sticker = sticker; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/GetStickerSetRequest.cs b/TelegramBot/Requests/Stickers/GetStickerSetRequest.cs new file mode 100644 index 0000000..c5762d9 --- /dev/null +++ b/TelegramBot/Requests/Stickers/GetStickerSetRequest.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to get a sticker set. On success, a object is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GetStickerSetRequest : RequestBase { + /// + /// Name of the sticker set + /// + [JsonProperty(Required = Required.Always)] + public string Name { + get; + } + + /// + /// Initializes a new request with name + /// + /// Name of the sticker set + public GetStickerSetRequest(string name) + : base("getStickerSet") { + Name = name; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/SendStickerRequest.cs b/TelegramBot/Requests/Stickers/SendStickerRequest.cs new file mode 100644 index 0000000..1d730a9 --- /dev/null +++ b/TelegramBot/Requests/Stickers/SendStickerRequest.cs @@ -0,0 +1,90 @@ +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 { + + + /// + /// Use this method to send static .WEBP or animated .TGS stickers. On success, the sent + /// is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SendStickerRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Sticker to send. Pass a 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 .WEBP file from the Internet, or upload a new one using multipart/form-data + /// + [JsonProperty(Required = Required.Always)] + public InputOnlineFile Sticker { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableNotification { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ProtectContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ReplyToMessageId { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? AllowSendingWithoutReply { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IReplyMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request chatId and sticker + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Sticker to send. Pass a 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 .WEBP file from the Internet, or upload a new one using multipart/form-data + /// + public SendStickerRequest(ChatId chatId, InputOnlineFile sticker) + : base("sendSticker") { + ChatId = chatId; + Sticker = sticker; + } + + /// + public override HttpContent? ToHttpContent() => + Sticker.FileType == FileType.Stream + ? ToMultipartFormDataContent(fileParameterName: "sticker", inputFile: Sticker) + : base.ToHttpContent(); + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/SetStickerPositionInSetRequest.cs b/TelegramBot/Requests/Stickers/SetStickerPositionInSetRequest.cs new file mode 100644 index 0000000..966bcc6 --- /dev/null +++ b/TelegramBot/Requests/Stickers/SetStickerPositionInSetRequest.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to move a sticker in a set created by the bot to a specific position. + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetStickerPositionInSetRequest : RequestBase { + /// + /// File identifier of the sticker + /// + [JsonProperty(Required = Required.Always)] + public string Sticker { + get; + } + + /// + /// New sticker position in the set, zero-based + /// + [JsonProperty(Required = Required.Always)] + public int Position { + get; + } + + /// + /// Initializes a new request with sticker and position + /// + /// File identifier of the sticker + /// New sticker position in the set, zero-based + public SetStickerPositionInSetRequest(string sticker, int position) + : base("setStickerPositionInSet") { + Sticker = sticker; + Position = position; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/SetStickerSetThumbRequest.cs b/TelegramBot/Requests/Stickers/SetStickerSetThumbRequest.cs new file mode 100644 index 0000000..c7a1ad3 --- /dev/null +++ b/TelegramBot/Requests/Stickers/SetStickerSetThumbRequest.cs @@ -0,0 +1,64 @@ +using System.Net.Http; + using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types.Enums; + using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for + /// animated sticker sets only. Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SetStickerSetThumbRequest : FileRequestBase, IUserTargetable { + /// + /// Sticker set name + /// + [JsonProperty(Required = Required.Always)] + public string Name { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// A PNG image with the thumbnail, must be up to 128 kilobytes in size and have width + /// and height exactly 100px, or a TGS animation with the thumbnail up to 32 kilobytes in + /// size; see + /// for animated sticker technical requirements. Pass a + /// as a String to send a file that already exists on the Telegram servers, 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. Animated sticker set thumbnail can't be uploaded via HTTP URL + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputOnlineFile? Thumb { + get; set; + } + + /// + /// Initializes a new request with sticker and position + /// + /// Sticker set name + /// User identifier of the sticker set owner + public SetStickerSetThumbRequest(string name, long userId) + : base("setStickerSetThumb") { + Name = name; + UserId = userId; + } + + /// + public override HttpContent? ToHttpContent() => + Thumb?.FileType switch { + FileType.Stream => ToMultipartFormDataContent(fileParameterName: "thumb", inputFile: Thumb), + _ => base.ToHttpContent() + }; + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Stickers/UploadStickerFileRequest.cs b/TelegramBot/Requests/Stickers/UploadStickerFileRequest.cs new file mode 100644 index 0000000..7ffdaf9 --- /dev/null +++ b/TelegramBot/Requests/Stickers/UploadStickerFileRequest.cs @@ -0,0 +1,57 @@ +using System.Net.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.InputFiles; +using File = Telegram.Bot.Types.File; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to upload a .PNG file with a sticker for later use in + /// / and + /// / methods + /// (can be used multiple times). Returns the uploaded on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class UploadStickerFileRequest : FileRequestBase, IUserTargetable { + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; + } + + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not + /// exceed 512px, and either width or height must be exactly 512px + /// + [JsonProperty(Required = Required.Always)] + public InputFileStream PngSticker { + get; + } + + /// + /// Initializes a new request with userId and pngSticker + /// + /// User identifier of sticker file owner + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not + /// exceed 512px, and either width or height must be exactly 512px + /// + public UploadStickerFileRequest(long userId, InputFileStream pngSticker) + : base("uploadStickerFile") { + UserId = userId; + PngSticker = pngSticker; + } + + /// + public override HttpContent? ToHttpContent() => + PngSticker.FileType switch { + FileType.Stream => ToMultipartFormDataContent(fileParameterName: "png_sticker", inputFile: PngSticker), + _ => base.ToHttpContent() + }; + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/DeleteMessageRequest.cs b/TelegramBot/Requests/Updating messages/DeleteMessageRequest.cs new file mode 100644 index 0000000..4dc7240 --- /dev/null +++ b/TelegramBot/Requests/Updating messages/DeleteMessageRequest.cs @@ -0,0 +1,56 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to delete a message, including service messages, with the following limitations: + /// + /// A message can only be deleted if it was sent less than 48 hours ago + /// A dice message in a private chat can only be deleted if it was sent more than 24 hours ago + /// Bots can delete outgoing messages in private chats, groups, and supergroups + /// Bots can delete incoming messages in private chats + /// Bots granted can_post_messages permissions can delete outgoing messages in channels + /// If the bot is an administrator of a group, it can delete any message there + /// + /// If the bot has can_delete_messages permission in a supergroup or a channel, + /// it can delete any message there + /// + /// + /// Returns true on success. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class DeleteMessageRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of the message to delete + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + /// Initializes a new request with chatId and messageId + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to delete + public DeleteMessageRequest(ChatId chatId, int messageId) + : base("deleteMessage") { + ChatId = chatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/EditInlineMessageCaptionRequest.cs b/TelegramBot/Requests/Updating messages/EditInlineMessageCaptionRequest.cs new file mode 100644 index 0000000..3544b6b --- /dev/null +++ b/TelegramBot/Requests/Updating messages/EditInlineMessageCaptionRequest.cs @@ -0,0 +1,59 @@ +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 { + + + /// + /// Use this method to edit captions of messages. On success true is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditInlineMessageCaptionRequest : RequestBase { + /// + [JsonProperty(Required = Required.Always)] + public string InlineMessageId { + get; + } + + /// + /// New caption of the message, 0-1024 characters after entities parsing + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with inlineMessageId and new caption + /// + /// Identifier of the inline message + public EditInlineMessageCaptionRequest(string inlineMessageId) + : base("editMessageCaption") { + InlineMessageId = inlineMessageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/EditInlineMessageMediaRequest.cs b/TelegramBot/Requests/Updating messages/EditInlineMessageMediaRequest.cs new file mode 100644 index 0000000..3997b3c --- /dev/null +++ b/TelegramBot/Requests/Updating messages/EditInlineMessageMediaRequest.cs @@ -0,0 +1,52 @@ +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 { + + + /// + /// Use this method to edit animation, audio, document, photo, or video messages. If a message is + /// part of a message album, then it can be edited only to an audio for audio albums, only to a + /// document for document albums and to a photo or a video otherwise. Use a previously uploaded file + /// via its or specify a URL. On success + /// true is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditInlineMessageMediaRequest : RequestBase { + + /// + [JsonProperty(Required = Required.Always)] + public string InlineMessageId { + get; + } + + /// + /// A new media content of the message + /// + [JsonProperty(Required = Required.Always)] + public InputMediaBase Media { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with inlineMessageId and new media + /// + /// Identifier of the inline message + /// A new media content of the message + public EditInlineMessageMediaRequest(string inlineMessageId, InputMediaBase media) + : base("editMessageMedia") { + InlineMessageId = inlineMessageId; + Media = media; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/EditInlineMessageReplyMarkupRequest.cs b/TelegramBot/Requests/Updating messages/EditInlineMessageReplyMarkupRequest.cs new file mode 100644 index 0000000..7769faa --- /dev/null +++ b/TelegramBot/Requests/Updating messages/EditInlineMessageReplyMarkupRequest.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types.ReplyMarkups; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to edit only the reply markup of messages. On success true is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditInlineMessageReplyMarkupRequest : RequestBase { + /// + [JsonProperty(Required = Required.Always)] + public string InlineMessageId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with inlineMessageId and new inline keyboard + /// + /// Identifier of the inline message + public EditInlineMessageReplyMarkupRequest(string inlineMessageId) + : base("editMessageReplyMarkup") { + InlineMessageId = inlineMessageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/EditInlineMessageTextRequest.cs b/TelegramBot/Requests/Updating messages/EditInlineMessageTextRequest.cs new file mode 100644 index 0000000..533a500 --- /dev/null +++ b/TelegramBot/Requests/Updating messages/EditInlineMessageTextRequest.cs @@ -0,0 +1,69 @@ +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 { + + + /// + /// Use this method to edit text and game messages. On success true is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditInlineMessageTextRequest : RequestBase { + /// + [JsonProperty(Required = Required.Always)] + public string InlineMessageId { + get; + } + + /// + /// New text of the message, 1-4096 characters after entities parsing + /// + [JsonProperty(Required = Required.Always)] + public string Text { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? Entities { + get; set; + } + + /// + /// Disables link previews for links in this message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableWebPagePreview { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with inlineMessageId and new text + /// + /// Identifier of the inline message + /// New text of the message, 1-4096 characters after entities parsing + public EditInlineMessageTextRequest(string inlineMessageId, string text) + : base("editMessageText") { + InlineMessageId = inlineMessageId; + Text = text; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/EditMessageCaptionRequest.cs b/TelegramBot/Requests/Updating messages/EditMessageCaptionRequest.cs new file mode 100644 index 0000000..caecdcc --- /dev/null +++ b/TelegramBot/Requests/Updating messages/EditMessageCaptionRequest.cs @@ -0,0 +1,71 @@ +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 { + + + /// + /// Use this method to edit captions of messages. On success the edited is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditMessageCaptionRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of the message to edit + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + /// New caption of the message, 0-1024 characters after entities parsing + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and messageIdn + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + public EditMessageCaptionRequest(ChatId chatId, int messageId) + : base("editMessageCaption") { + ChatId = chatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/EditMessageMediaRequest.cs b/TelegramBot/Requests/Updating messages/EditMessageMediaRequest.cs new file mode 100644 index 0000000..10d875f --- /dev/null +++ b/TelegramBot/Requests/Updating messages/EditMessageMediaRequest.cs @@ -0,0 +1,74 @@ +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.ReplyMarkups; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to edit animation, audio, document, photo, or video messages. If a message is part + /// of a message album, then it can be edited only to an audio for audio albums, only to a + /// document for document albums and to a photo or a video otherwise. Use a previously uploaded + /// file via its or specify a URL. + /// On success the edited is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditMessageMediaRequest : FileRequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of the message to edit + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + /// A new media content of the message + /// + [JsonProperty(Required = Required.Always)] + public InputMediaBase Media { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, messageId and new media + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + /// A new media content of the message + public EditMessageMediaRequest(ChatId chatId, int messageId, InputMediaBase media) + : base("editMessageMedia") { + ChatId = chatId; + MessageId = messageId; + Media = media; + } + + // ToDo: If there is no file stream in the request, request content should be string + /// + public override HttpContent ToHttpContent() { + var httpContent = GenerateMultipartFormDataContent(); + httpContent.AddContentIfInputFileStream(Media); + return httpContent; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/EditMessageReplyMarkupRequest.cs b/TelegramBot/Requests/Updating messages/EditMessageReplyMarkupRequest.cs new file mode 100644 index 0000000..ac77f86 --- /dev/null +++ b/TelegramBot/Requests/Updating messages/EditMessageReplyMarkupRequest.cs @@ -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 { + + + /// + /// Use this method to edit only the reply markup of messages. On success the edited + /// is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditMessageReplyMarkupRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of the message to edit + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId and messageId + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + public EditMessageReplyMarkupRequest(ChatId chatId, int messageId) + : base("editMessageReplyMarkup") { + ChatId = chatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/EditMessageTextRequest.cs b/TelegramBot/Requests/Updating messages/EditMessageTextRequest.cs new file mode 100644 index 0000000..67308a3 --- /dev/null +++ b/TelegramBot/Requests/Updating messages/EditMessageTextRequest.cs @@ -0,0 +1,82 @@ +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 { + + + /// + /// Use this method to edit text and game messages. On success the edited is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EditMessageTextRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of the message to edit + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + /// New text of the message, 1-4096 characters after entities parsing + /// + [JsonProperty(Required = Required.Always)] + public string Text { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public IEnumerable? Entities { + get; set; + } + + /// + /// Disables link previews for links in this message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableWebPagePreview { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, messageId and text + /// + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + /// New text of the message, 1-4096 characters after entities parsing + public EditMessageTextRequest(ChatId chatId, int messageId, string text) + : base("editMessageText") { + ChatId = chatId; + MessageId = messageId; + Text = text; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Requests/Updating messages/StopPollRequest.cs b/TelegramBot/Requests/Updating messages/StopPollRequest.cs new file mode 100644 index 0000000..9bb2f39 --- /dev/null +++ b/TelegramBot/Requests/Updating messages/StopPollRequest.cs @@ -0,0 +1,52 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types; +using Telegram.Bot.Types.ReplyMarkups; + +// ReSharper disable CheckNamespace + +namespace Telegram.Bot.Requests { + + + /// + /// Use this method to stop a poll which was sent by the bot. On success, the stopped + /// with the final results is returned. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class StopPollRequest : RequestBase, IChatTargetable { + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { + get; + } + + /// + /// Identifier of the original message with the poll + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new request with chatId, messageId + /// + /// + /// Unique identifier for the target chat or username of the target channel (in the format + /// @channelusername) + /// + /// Identifier of the original message with the poll + public StopPollRequest(ChatId chatId, int messageId) + : base("stopPoll") { + ChatId = chatId; + MessageId = messageId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/TelegramBot.csproj b/TelegramBot/TelegramBot.csproj new file mode 100644 index 0000000..a4ac562 --- /dev/null +++ b/TelegramBot/TelegramBot.csproj @@ -0,0 +1,57 @@ + + + + netstandard2.0;netcoreapp3.1 + 9 + enable + Telegram Bot API Client + The Bot API is an HTTP-based interface created for developers keen on building bots for Telegram. + Telegram.Bot + RoundRobin,Poulad,tuscen + Copyright © Robin Müller 2016 + https://github.com/TelegramBots/telegram.bot + MIT + Telegram;Bot;Api;Payment;Inline;Games + AllEnabledByDefault + Telegram.Bot + Telegram.Bot + + + + $(NoWarn);CA1003 + $(NoWarn);CA1819 + $(NoWarn);CA1008 + $(NoWarn);CA1056 + $(NoWarn);CA1711 + $(NoWarn);CA1040 + $(NoWarn);CA1822 + $(NoWarn);CA2225 + $(NoWarn);CA1054 + $(NoWarn);CA2234 + $(NoWarn);CA1031 + + + + + + + + + + + + + + + + + diff --git a/TelegramBot/TelegramBotClient.cs b/TelegramBot/TelegramBotClient.cs new file mode 100644 index 0000000..d359c3a --- /dev/null +++ b/TelegramBot/TelegramBotClient.cs @@ -0,0 +1,290 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +//using JetBrains.Annotations; +using Telegram.Bot.Args; +using Telegram.Bot.Exceptions; +using Telegram.Bot.Extensions; +using Telegram.Bot.Requests; +using Telegram.Bot.Requests.Abstractions; +using Telegram.Bot.Types; + +namespace Telegram.Bot { + + + /// + /// A client to use the Telegram Bot API + /// + //[PublicAPI] + public class TelegramBotClient : ITelegramBotClient { + readonly TelegramBotClientOptions _options; + + readonly HttpClient _httpClient; + + /// + public long? BotId => _options.BotId; + + /// + public bool LocalBotServer => _options.LocalBotServer; + + /// + /// Timeout for requests + /// + public TimeSpan Timeout { + get => _httpClient.Timeout; + set => _httpClient.Timeout = value; + } + + /// + public IExceptionParser ExceptionsParser { get; set; } = new DefaultExceptionParser(); + + /// + /// Occurs before sending a request to API + /// + public event AsyncEventHandler? OnMakingApiRequest; + + /// + /// Occurs after receiving the response to an API request + /// + public event AsyncEventHandler? OnApiResponseReceived; + + /// + /// Create a new instance. + /// + /// Configuration for + /// A custom + /// + /// Thrown if is null + /// + public TelegramBotClient( + TelegramBotClientOptions options, + HttpClient? httpClient = default) { + _options = options ?? throw new ArgumentNullException(nameof(options)); + _httpClient = httpClient ?? new HttpClient(); + } + + /// + /// Create a new instance. + /// + /// + /// A custom + /// + /// Thrown if format is invalid + /// + public TelegramBotClient( + string token, + HttpClient? httpClient = null) : + this(new TelegramBotClientOptions(token), httpClient) { + } + + /// + public virtual async Task MakeRequestAsync( + IRequest request, + CancellationToken cancellationToken = default) { + if(request is null) { + throw new ArgumentNullException(nameof(request)); + } + + var url = $"{_options.BaseRequestUrl}/{request.MethodName}"; + +#pragma warning disable CA2000 + var httpRequest = new HttpRequestMessage(method: request.Method, requestUri: url) { + Content = request.ToHttpContent() + }; +#pragma warning restore CA2000 + + if(OnMakingApiRequest is not null) { + var requestEventArgs = new ApiRequestEventArgs( + request: request, + httpRequestMessage: httpRequest + ); + await OnMakingApiRequest.Invoke( + botClient: this, + args: requestEventArgs, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + } + + using var httpResponse = await SendRequestAsync( + httpClient: _httpClient, + httpRequest: httpRequest, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + + if(OnApiResponseReceived is not null) { + var requestEventArgs = new ApiRequestEventArgs( + request: request, + httpRequestMessage: httpRequest + ); + var responseEventArgs = new ApiResponseEventArgs( + responseMessage: httpResponse, + apiRequestEventArgs: requestEventArgs + ); + await OnApiResponseReceived.Invoke( + botClient: this, + args: responseEventArgs, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + } + + if(httpResponse.StatusCode != HttpStatusCode.OK) { + var failedApiResponse = await httpResponse + .DeserializeContentAsync( + guard: response => + response.ErrorCode == default || + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + response.Description is null + ) + .ConfigureAwait(false); + + throw ExceptionsParser.Parse(failedApiResponse); + } + + var apiResponse = await httpResponse + .DeserializeContentAsync>( + guard: response => response.Ok == false || + response.Result is null + ) + .ConfigureAwait(false); + + return apiResponse.Result!; + + [MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)] + static async Task SendRequestAsync( + HttpClient httpClient, + HttpRequestMessage httpRequest, + CancellationToken cancellationToken) { + HttpResponseMessage? httpResponse; + try { + httpResponse = await httpClient + .SendAsync(request: httpRequest, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + } catch(TaskCanceledException exception) { + if(cancellationToken.IsCancellationRequested) { + throw; + } + + throw new RequestException(message: "Request timed out", innerException: exception); + } catch(Exception exception) { + throw new RequestException( + message: "Exception during making request", + innerException: exception + ); + } + + return httpResponse; + } + } + + /// + /// Test the API token + /// + /// true if token is valid + public async Task TestApiAsync(CancellationToken cancellationToken = default) { + try { + await MakeRequestAsync(request: new GetMeRequest(), cancellationToken: cancellationToken) + .ConfigureAwait(false); + return true; + } catch(ApiRequestException e) + when(e.ErrorCode == 401) { + return false; + } + } + + /// + public async Task DownloadFileAsync( + string filePath, + Stream destination, + CancellationToken cancellationToken = default) { + if(string.IsNullOrWhiteSpace(filePath) || filePath.Length < 2) { + throw new ArgumentException(message: "Invalid file path", paramName: nameof(filePath)); + } + + if(destination is null) { + throw new ArgumentNullException(nameof(destination)); + } + + var fileUri = $"{_options.BaseFileUrl}/{filePath}"; + using HttpResponseMessage httpResponse = await GetResponseAsync( + httpClient: _httpClient, + fileUri: fileUri, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + + if(!httpResponse.IsSuccessStatusCode) { + var failedApiResponse = await httpResponse + .DeserializeContentAsync( + guard: response => + response.ErrorCode == default || + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + response.Description is null + ) + .ConfigureAwait(false); + + throw ExceptionsParser.Parse(failedApiResponse); + } + + if(httpResponse.Content is null) { + throw new RequestException( + message: "Response doesn't contain any content", + httpResponse.StatusCode + ); + } + + try { + await httpResponse.Content.CopyToAsync(destination) + .ConfigureAwait(false); + } catch(Exception exception) { + throw new RequestException( + message: "Exception during file download", + httpResponse.StatusCode, + exception + ); + } + + [MethodImpl(methodImplOptions: MethodImplOptions.AggressiveInlining)] + static async Task GetResponseAsync( + HttpClient httpClient, + string fileUri, + CancellationToken cancellationToken) { + HttpResponseMessage? httpResponse; + try { + httpResponse = await httpClient + .GetAsync( + requestUri: fileUri, + completionOption: HttpCompletionOption.ResponseHeadersRead, + cancellationToken: cancellationToken + ) + .ConfigureAwait(continueOnCapturedContext: false); + } catch(TaskCanceledException exception) { + if(cancellationToken.IsCancellationRequested) { + throw; + } + + throw new RequestException( + message: "Request timed out", + innerException: exception + ); + } catch(Exception exception) { + throw new RequestException( + message: "Exception during file download", + innerException: exception + ); + } + + return httpResponse; + } + } + + #region For testing purposes + + internal string BaseRequestUrl => _options.BaseRequestUrl; + internal string BaseFileUrl => _options.BaseFileUrl; + + #endregion + } +} \ No newline at end of file diff --git a/TelegramBot/TelegramBotClientExtensions.ApiMethods.cs b/TelegramBot/TelegramBotClientExtensions.ApiMethods.cs new file mode 100644 index 0000000..1cdc6d1 --- /dev/null +++ b/TelegramBot/TelegramBotClientExtensions.ApiMethods.cs @@ -0,0 +1,4301 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Telegram.Bot.Extensions; +using Telegram.Bot.Requests; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.InlineQueryResults; +using Telegram.Bot.Types.InputFiles; +using Telegram.Bot.Types.Payments; +using Telegram.Bot.Types.ReplyMarkups; +using File = Telegram.Bot.Types.File; + +namespace Telegram.Bot { + + + /// + /// Extension methods that map to requests from Bot API documentation + /// + public static partial class TelegramBotClientExtensions { + #region Getting updates + + /// + /// Use this method to receive incoming updates using long polling + /// (wiki) + /// + /// An instance of + /// + /// Identifier of the first update to be returned. Must be greater by one than the highest among the + /// identifiers of previously received updates. By default, updates starting with the earliest unconfirmed + /// update are returned. An update is considered confirmed as soon as is called + /// with an higher than its . The negative offset can be + /// specified to retrieve updates starting from -offset update from the end + /// of the updates queue. All previous updates will forgotten. + /// + /// + /// Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults to 100 + /// + /// + /// Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be positive, short + /// polling should be used for testing purposes only. + /// + /// + /// A list of the update types you want your bot to receive. For example, specify + /// [, , + /// ] to only receive updates of these types. See + /// for a complete list of available update types. Specify an empty list to receive + /// all update types except (default). If not specified, the previous + /// setting will be used. + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// + /// + /// This method will not work if an outgoing webhook is set up + /// + /// In order to avoid getting duplicate updates, recalculate after each server + /// response + /// + /// + /// + /// An Array of objects is returned. + public static async Task GetUpdatesAsync( + this ITelegramBotClient botClient, + int? offset = default, + int? limit = default, + int? timeout = default, + IEnumerable? allowedUpdates = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new GetUpdatesRequest { + Offset = offset, + Limit = limit, + Timeout = timeout, + AllowedUpdates = allowedUpdates + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is + /// an update for the bot, we will send an HTTPS POST request to the specified url, containing a + /// JSON-serialized . In case of an unsuccessful request, we will give up after a + /// reasonable amount of attempts + /// + /// If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path + /// in the URL, e.g. https://www.example.com/<token>. Since nobody else knows your bot’s token, + /// you can be pretty sure it's us. + /// + /// + /// An instance of + /// HTTPS url to send updates to. Use an empty string to remove webhook integration + /// + /// Upload your public key certificate so that the root certificate in use can be checked. See our + /// self-signed guide for details + /// + /// + /// The fixed IP address which will be used to send webhook requests instead of the IP address resolved + /// through DNS + /// + /// + /// Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. + /// Defaults to 40. Use lower values to limit the load on your bot’s server, and higher values to + /// increase your bot’s throughput + /// + /// + /// A list of the update types you want your bot to receive. For example, specify + /// [, , + /// ] to only receive updates of these types. See + /// for a complete list of available update types. Specify an empty list to receive + /// all update types except (default). If not specified, the previous + /// setting will be used + /// + /// + /// Please note that this parameter doesn't affect updates created before the call to the + /// , so unwanted updates may be received for a short period of time. + /// + /// + /// Pass true to drop all pending updates + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// + /// + /// + /// You will not be able to receive updates using for as long as an outgoing + /// webhook is set up + /// + /// + /// To use a self-signed certificate, you need to upload your + /// public key certificate using + /// parameter. Please upload as , sending a + /// string will not work + /// + /// Ports currently supported for Webhooks: 443, 80, 88, 8443 + /// + /// If you're having any trouble setting up webhooks, please check out this + /// amazing guide to Webhooks. + /// + public static async Task SetWebhookAsync( + this ITelegramBotClient botClient, + string url, + InputFileStream? certificate = default, + string? ipAddress = default, + int? maxConnections = default, + IEnumerable? allowedUpdates = default, + bool? dropPendingUpdates = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetWebhookRequest(url) { + Certificate = certificate, + IpAddress = ipAddress, + MaxConnections = maxConnections, + AllowedUpdates = allowedUpdates, + DropPendingUpdates = dropPendingUpdates + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to remove webhook integration if you decide to switch back to + /// + /// An instance of + /// Pass true to drop all pending updates + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns true on success + public static async Task DeleteWebhookAsync( + this ITelegramBotClient botClient, + bool? dropPendingUpdates = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new DeleteWebhookRequest { + DropPendingUpdates = dropPendingUpdates + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to get current webhook status. + /// + /// An instance of + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// + /// On success, returns a object. If the bot is using , + /// will return an object with the field empty. + /// + public static async Task GetWebhookInfoAsync( + this ITelegramBotClient botClient, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetWebhookInfoRequest(), cancellationToken) + .ConfigureAwait(false); + + #endregion Getting updates + + #region Available methods + + /// + /// A simple method for testing your bot’s auth token. + /// + /// An instance of + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns basic information about the bot in form of a object. + public static async Task GetMeAsync( + this ITelegramBotClient botClient, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetMeRequest(), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to log out from the cloud Bot API server before launching the bot locally. You must + /// 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. + /// + /// An instance of + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task LogOutAsync( + this ITelegramBotClient botClient, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new LogOutRequest(), cancellationToken) + .ConfigureAwait(false); + + /// + /// 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. + /// + /// An instance of + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task CloseAsync( + this ITelegramBotClient botClient, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new CloseRequest(), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to send text messages. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Text of the message to be sent, 1-4096 characters after entities parsing + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for more + /// details + /// + /// + /// List of special entities that appear in message text, which can be specified instead + /// of + /// + /// Disables link previews for links in this message + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to force a + /// reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendTextMessageAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string text, + ParseMode? parseMode = default, + IEnumerable? entities = default, + bool? disableWebPagePreview = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendMessageRequest(chatId, text) { + ParseMode = parseMode, + Entities = entities, + DisableWebPagePreview = disableWebPagePreview, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to forward messages of any kind. Service messages can't be forwarded. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Unique identifier for the chat where the original message was sent + /// (or channel username in the format @channelusername) + /// + /// Message identifier in the chat specified in + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task ForwardMessageAsync( + this ITelegramBotClient botClient, + ChatId chatId, + ChatId fromChatId, + int messageId, + bool? disableNotification = default, + bool? protectContent = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new ForwardMessageRequest(chatId, fromChatId, messageId) { + DisableNotification = disableNotification, + ProtectContent = protectContent + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 , but the copied message doesn't + /// have a link to the original message. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Unique identifier for the chat where the original message was sent + /// (or channel username in the format @channelusername) + /// + /// Message identifier in the chat specified in + /// + /// New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption + /// is kept + /// + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns the of the sent message on success. + public static async Task CopyMessageAsync( + this ITelegramBotClient botClient, + ChatId chatId, + ChatId fromChatId, + int messageId, + string? caption = default, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new CopyMessageRequest(chatId, fromChatId, messageId) { + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + ReplyToMessageId = replyToMessageId, + DisableNotification = disableNotification, + ProtectContent = protectContent, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send photos. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Photo to send. Pass a 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 + /// + /// + /// Photo caption (may also be used when resending photos by ), + /// 0-1024 characters after entities parsing + /// + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendPhotoAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputOnlineFile photo, + string? caption = default, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)). + MakeRequestAsync( + request: new SendPhotoRequest(chatId, photo) { + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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. Bots can currently send audio files of up to 50 MB in size, + /// this limit may be changed in the future. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Audio file to send. Pass a 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 + /// + /// Audio caption, 0-1024 characters after entities parsing + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// Duration of the audio in seconds + /// Performer + /// Track name + /// + /// 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://<file_attach_name>" if the + /// thumbnail was uploaded using multipart/form-data under <file_attach_name> + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendAudioAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputOnlineFile audio, + string? caption = default, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + int? duration = default, + string? performer = default, + string? title = default, + InputMedia? thumb = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendAudioRequest(chatId, audio) { + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + Duration = duration, + Performer = performer, + Title = title, + Thumb = thumb, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send general files. Bots can currently send files of any type of up to 50 MB in size, + /// this limit may be changed in the future. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// File to send. Pass a 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 + /// + /// + /// 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://<file_attach_name>" if the + /// thumbnail was uploaded using multipart/form-data under <file_attach_name> + /// + /// + /// Document caption (may also be used when resending documents by file_id), 0-1024 characters after + /// entities parsing + /// + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// + /// Disables automatic server-side content type detection for files uploaded using multipart/form-data + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendDocumentAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputOnlineFile document, + InputMedia? thumb = default, + string? caption = default, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + bool? disableContentTypeDetection = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendDocumentRequest(chatId, document) { + Thumb = thumb, + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + DisableContentTypeDetection = disableContentTypeDetection, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as + /// ). Bots can currently send video files of up to 50 MB in size, this limit may be + /// changed in the future. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Video to send. Pass a as String to send a video that exists on + /// the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a video from the + /// Internet, or upload a new video using multipart/form-data + /// + /// Duration of sent video in seconds + /// Video width + /// Video height + /// + /// 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://<file_attach_name>" if the + /// thumbnail was uploaded using multipart/form-data under <file_attach_name> + /// + /// + /// Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing + /// + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// Pass true, if the uploaded video is suitable for streaming + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendVideoAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputOnlineFile video, + int? duration = default, + int? width = default, + int? height = default, + InputMedia? thumb = default, + string? caption = default, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + bool? supportsStreaming = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendVideoRequest(chatId, video) { + Duration = duration, + Width = width, + Height = height, + Thumb = thumb, + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + SupportsStreaming = supportsStreaming, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). Bots can currently + /// send animation files of up to 50 MB in size, this limit may be changed in the future. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Animation to send. Pass a 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 + /// + /// Duration of sent animation in seconds + /// Animation width + /// Animation height + /// + /// 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://<file_attach_name>" if the + /// thumbnail was uploaded using multipart/form-data under <file_attach_name> + /// + /// + /// Animation caption (may also be used when resending animation by ), + /// 0-1024 characters after entities parsing + /// + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendAnimationAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputOnlineFile animation, + int? duration = default, + int? width = default, + int? height = default, + InputMedia? thumb = default, + string? caption = default, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendAnimationRequest(chatId, animation) { + Duration = duration, + Width = width, + Height = height, + Thumb = thumb, + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup, + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send audio files, if you want Telegram clients to display the file as a playable voice + /// message. For this to work, your audio must be in an .OGG file encoded with OPUS (other formats may be sent + /// as or ). Bots can currently send voice messages of up to 50 MB + /// in size, this limit may be changed in the future. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Audio file to send. Pass a 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 + /// + /// Voice message caption, 0-1024 characters after entities parsing + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// Duration of the voice message in seconds + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendVoiceAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputOnlineFile voice, + string? caption = default, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + int? duration = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendVoiceRequest(chatId, voice) { + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + Duration = duration, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// As of v.4.0, Telegram clients + /// support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Video note to send. Pass a as String to send a video note that + /// exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. Sending + /// video notes by a URL is currently unsupported + /// + /// Duration of sent video in seconds + /// Video width and height, i.e. diameter of the video message + /// + /// 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://<file_attach_name>" if the + /// thumbnail was uploaded using multipart/form-data under <file_attach_name> + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendVideoNoteAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputTelegramFile videoNote, + int? duration = default, + int? length = default, + InputMedia? thumb = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendVideoNoteRequest(chatId, videoNote) { + Duration = duration, + Length = length, + Thumb = thumb, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// An array describing messages to be sent, must include 2-10 items + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, an array of s that were sent is returned. + public static async Task SendMediaGroupAsync( + this ITelegramBotClient botClient, + ChatId chatId, + IEnumerable media, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendMediaGroupRequest(chatId, media) { + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send point on the map. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Latitude of location + /// Longitude of location + /// + /// Period in seconds for which the location will be updated, should be between 60 and 86400 + /// + /// + /// For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 + /// if specified + /// + /// + /// For live locations, a maximum distance for proximity alerts about approaching another chat member, + /// in meters. Must be between 1 and 100000 if specified + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendLocationAsync( + this ITelegramBotClient botClient, + ChatId chatId, + double latitude, + double longitude, + int? livePeriod = default, + int? heading = default, + int? proximityAlertRadius = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendLocationRequest(chatId, latitude, longitude) { + LivePeriod = livePeriod, + Heading = heading, + ProximityAlertRadius = proximityAlertRadius, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup, + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit live location messages. A location can be edited until its + /// expires or editing is explicitly disabled by a call to + /// . + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + /// Latitude of new location + /// Longitude of new location + /// + /// The radius of uncertainty for the location, measured in meters; 0-1500 + /// + /// + /// Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified + /// + /// + /// Maximum distance for proximity alerts about approaching another chat member, in meters. + /// Must be between 1 and 100000 if specified + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success the edited is returned. + public static async Task EditMessageLiveLocationAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + double latitude, + double longitude, + float? horizontalAccuracy = default, + int? heading = default, + int? proximityAlertRadius = default, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditMessageLiveLocationRequest(chatId, messageId, latitude, longitude) { + HorizontalAccuracy = horizontalAccuracy, + Heading = heading, + ProximityAlertRadius = proximityAlertRadius, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit live location messages. A location can be edited until its + /// expires or editing is explicitly disabled by a call to + /// . + /// + /// An instance of + /// Identifier of the inline message + /// Latitude of new location + /// Longitude of new location + /// + /// The radius of uncertainty for the location, measured in meters; 0-1500 + /// + /// + /// Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified + /// + /// + /// Maximum distance for proximity alerts about approaching another chat member, in meters. + /// Must be between 1 and 100000 if specified + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task EditMessageLiveLocationAsync( + this ITelegramBotClient botClient, + string inlineMessageId, + double latitude, + double longitude, + float? horizontalAccuracy = default, + int? heading = default, + int? proximityAlertRadius = default, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditInlineMessageLiveLocationRequest(inlineMessageId, latitude, longitude) { + HorizontalAccuracy = horizontalAccuracy, + Heading = heading, + ProximityAlertRadius = proximityAlertRadius, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to stop updating a live location message before + /// expires. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the sent message + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success the sent is returned. + public static async Task StopMessageLiveLocationAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new StopMessageLiveLocationRequest(chatId, messageId) { + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to stop updating a live location message before + /// expires. + /// + /// An instance of + /// Identifier of the inline message + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task StopMessageLiveLocationAsync( + this ITelegramBotClient botClient, + string inlineMessageId, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new StopInlineMessageLiveLocationRequest(inlineMessageId) { + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send information about a venue. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Latitude of the venue + /// Longitude of the venue + /// Name of the venue + /// Address of the venue + /// Foursquare identifier of the venue + /// + /// Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, + /// “arts_entertainment/aquarium” or “food/icecream”.) + /// + /// Google Places identifier of the venue + /// + /// Google Places type of the venue. (See + /// supported types) + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + /// + public static async Task SendVenueAsync( + this ITelegramBotClient botClient, + ChatId chatId, + double latitude, + double longitude, + string title, + string address, + string? foursquareId = default, + string? foursquareType = default, + string? googlePlaceId = default, + string? googlePlaceType = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendVenueRequest(chatId, latitude, longitude, title, address) { + FoursquareId = foursquareId, + FoursquareType = foursquareType, + GooglePlaceId = googlePlaceId, + GooglePlaceType = googlePlaceType, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send phone contacts. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Contact's phone number + /// Contact's first name + /// Contact's last name + /// Additional data about the contact in the form of a vCard, 0-2048 bytes + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendContactAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string phoneNumber, + string firstName, + string? lastName = default, + string? vCard = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendContactRequest(chatId, phoneNumber, firstName) { + LastName = lastName, + Vcard = vCard, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send a native poll. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Poll question, 1-300 characters + /// A list of answer options, 2-10 strings 1-100 characters each + /// true, if the poll needs to be anonymous, defaults to true + /// + /// Poll type, or , + /// defaults to + /// + /// + /// true, if the poll allows multiple answers, ignored for polls in quiz mode, + /// defaults to false + /// + /// + /// 0-based identifier of the correct answer option, required for polls in quiz mode + /// + /// + /// 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 + /// + /// + /// Mode for parsing entities in the explanation. See + /// formatting options + /// for more details + /// + /// + /// List of special entities that appear in the poll explanation, which can be specified instead + /// of + /// + /// + /// Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together + /// with + /// + /// + /// 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 + /// + /// + /// Pass true, if the poll needs to be immediately closed. This can be useful for poll preview + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendPollAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string question, + IEnumerable options, + bool? isAnonymous = default, + PollType? type = default, + bool? allowsMultipleAnswers = default, + int? correctOptionId = default, + string? explanation = default, + ParseMode? explanationParseMode = default, + IEnumerable? explanationEntities = default, + int? openPeriod = default, + DateTime? closeDate = default, + bool? isClosed = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendPollRequest(chatId, question, options) { + IsAnonymous = isAnonymous, + Type = type, + AllowsMultipleAnswers = allowsMultipleAnswers, + CorrectOptionId = correctOptionId, + Explanation = explanation, + ExplanationParseMode = explanationParseMode, + ExplanationEntities = explanationEntities, + OpenPeriod = openPeriod, + CloseDate = closeDate, + IsClosed = isClosed, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to send an animated emoji that will display a random value. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Emoji on which the dice throw animation is based. Currently, must be one of , + /// , , , + /// or . Dice can have values 1-6 for + /// , and , values 1-5 for + /// and , and values 1-64 for + /// . Defauts to + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendDiceAsync( + this ITelegramBotClient botClient, + ChatId chatId, + Emoji? emoji = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendDiceRequest(chatId) { + Emoji = emoji, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup, + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method when you need to tell the user that something is happening on the bot’s side. The status is + /// set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). + /// + /// + /// + /// The ImageBot 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 with = . + /// The user will see a “sending photo” status for the bot. + /// + /// + /// We only recommend using this method when a response from the bot will take a noticeable amount of + /// time to arrive. + /// + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Type of action to broadcast. Choose one, depending on what the user is about to receive: + /// for text messages, + /// for photos, + /// or for + /// videos, or + /// for voice notes, + /// for general files, + /// for location data, + /// or for + /// video notes + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SendChatActionAsync( + this ITelegramBotClient botClient, + ChatId chatId, + ChatAction chatAction, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new SendChatActionRequest(chatId, chatAction), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to get a list of profile pictures for a user. + /// + /// An instance of + /// Unique identifier of the target user + /// + /// Sequential number of the first photo to be returned. By default, all photos are returned + /// + /// + /// Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to 100 + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns a object + public static async Task GetUserProfilePhotosAsync( + this ITelegramBotClient botClient, + long userId, + int? offset = default, + int? limit = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new GetUserProfilePhotosRequest(userId) { + Offset = offset, + Limit = limit + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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. The file can then be downloaded via the link + /// https://api.telegram.org/file/bot<token>/<file_path>, where <file_path> + /// 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 again. + /// + /// + /// You can use or + /// methods to download the file + /// + /// An instance of + /// File identifier to get info about + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, a object is returned. + public static async Task GetFileAsync( + this ITelegramBotClient botClient, + string fileId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new GetFileRequest(fileId), + cancellationToken: cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to get basic info about a file download it. For the moment, bots can download files + /// of up to 20MB in size. + /// + /// An instance of + /// File identifier to get info about + /// Destination stream to write file to + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, a object is returned. + public static async Task GetInfoAndDownloadFileAsync( + this ITelegramBotClient botClient, + string fileId, + Stream destination, + CancellationToken cancellationToken = default) { + var file = await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetFileRequest(fileId), cancellationToken) + .ConfigureAwait(false); + + await botClient.DownloadFileAsync(filePath: file.FilePath!, destination, cancellationToken) + .ConfigureAwait(false); + + return file; + } + + /// + /// 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 + /// unbanned + /// first. The bot must be an administrator in the chat for this to work and must have the appropriate + /// admin rights. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + /// + /// 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 + /// + /// + /// Pass true to delete all messages from the chat for the user that is being removed. + /// If false, 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 + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + [Obsolete("Use BanChatMemberAsync instead")] + public static async Task KickChatMemberAsync( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + DateTime? untilDate = default, + bool? revokeMessages = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new KickChatMemberRequest(chatId, userId) { + UntilDate = untilDate, + RevokeMessages = revokeMessages + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// unbanned + /// first. The bot must be an administrator in the chat for this to work and must have the appropriate + /// admin rights. + /// + /// An instance of + /// + /// Unique identifier for the target group or username of the target supergroup or channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + /// + /// 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 + /// + /// + /// Pass true to delete all messages from the chat for the user that is being removed. + /// If false, 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 + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task BanChatMemberAsync( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + DateTime? untilDate = default, + bool? revokeMessages = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new BanChatMemberRequest(chatId, userId) { + UntilDate = untilDate, + RevokeMessages = revokeMessages + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to unban a previously banned user in a supergroup or channel. The user will not + /// 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 + /// removed from the chat. If you don't want this, use the parameter + /// + /// An instance of + /// + /// Unique identifier for the target group or username of the target supergroup or channel + /// (in the format @username) + /// + /// Unique identifier of the target user + /// Do nothing if the user is not banned + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task UnbanChatMemberAsync( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + bool? onlyIfBanned = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new UnbanChatMemberRequest(chatId, userId) { + OnlyIfBanned = onlyIfBanned + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 true for all permissions to + /// lift restrictions from a user. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup + /// (in the format @supergroupusername) + /// + /// Unique identifier of the target user + /// New user permissions + /// 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. + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task RestrictChatMemberAsync( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + ChatPermissions permissions, + DateTime? untilDate = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new RestrictChatMemberRequest(chatId, userId, permissions) { + UntilDate = untilDate + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 false for all boolean parameters to demote a user. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + /// Pass true, if the administrator's presence in the chat is hidden + /// 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 + /// Pass true, if the administrator can create channel posts, channels only + /// Pass true, if the administrator can edit messages of other users, channels only + /// Pass true, if the administrator can delete messages of other users + /// Pass true, if the administrator can manage voice chats, supergroups only + /// Pass true, if the administrator can restrict, ban or unban chat members + /// Pass true, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) + /// Pass true, if the administrator can change chat title, photo and other settings + /// Pass true, if the administrator can invite new users to the chat + /// Pass true, if the administrator can pin messages, supergroups only + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task PromoteChatMemberAsync( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + bool? isAnonymous = default, + bool? canManageChat = default, + bool? canPostMessages = default, + bool? canEditMessages = default, + bool? canDeleteMessages = default, + bool? canManageVideoChats = default, + bool? canRestrictMembers = default, + bool? canPromoteMembers = default, + bool? canChangeInfo = default, + bool? canInviteUsers = default, + bool? canPinMessages = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new PromoteChatMemberRequest(chatId, userId) { + IsAnonymous = isAnonymous, + CanManageChat = canManageChat, + CanPostMessages = canPostMessages, + CanEditMessages = canEditMessages, + CanDeleteMessages = canDeleteMessages, + CanManageVideoChat = canManageVideoChats, + CanRestrictMembers = canRestrictMembers, + CanPromoteMembers = canPromoteMembers, + CanChangeInfo = canChangeInfo, + CanInviteUsers = canInviteUsers, + CanPinMessages = canPinMessages, + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to set a custom title for an administrator in a supergroup promoted by the bot. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup + /// (in the format @supergroupusername) + /// + /// Unique identifier of the target user + /// + /// New custom title for the administrator; 0-16 characters, emoji are not allowed + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetChatAdministratorCustomTitleAsync( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + string customTitle, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetChatAdministratorCustomTitleRequest(chatId, userId, customTitle), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method 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 true on success. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup + /// (in the format @supergroupusername) + /// + /// Unique identifier of the target sender chat + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task BanChatSenderChatAsync(this ITelegramBotClient botClient, + ChatId chatId, + long senderChatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + new BanChatSenderChatRequest(chatId, senderChatId), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method 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 true on success. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup + /// (in the format @supergroupusername) + /// + /// Unique identifier of the target sender chat + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task UnbanChatSenderChatAsync(this ITelegramBotClient botClient, + ChatId chatId, + long senderChatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + new UnbanChatSenderChatRequest(chatId, senderChatId), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup + /// (in the format @supergroupusername) + /// + /// New default chat permissions + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetChatPermissionsAsync( + this ITelegramBotClient botClient, + ChatId chatId, + ChatPermissions permissions, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetChatPermissionsRequest(chatId, permissions), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task ExportChatInviteLinkAsync( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new ExportChatInviteLinkRequest(chatId), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Invite link name; 0-32 characters + /// Point in time when the link will expire + /// + /// Maximum number of users that can be members of the chat simultaneously after joining the chat + /// via this invite link; 1-99999 + /// + /// + /// Set to true, if users joining the chat via the link need to be approved by chat administrators. + /// If true, can't be specified + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns the new invite link as object. + public static async Task CreateChatInviteLinkAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string? name = default, + DateTime? expireDate = default, + int? memberLimit = default, + bool? createsJoinRequest = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new CreateChatInviteLinkRequest(chatId) { + Name = name, + ExpireDate = expireDate, + MemberLimit = memberLimit, + CreatesJoinRequest = createsJoinRequest, + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// The invite link to edit + /// Invite link name; 0-32 characters + /// Point in time when the link will expire + /// + /// Maximum number of users that can be members of the chat simultaneously after joining the chat + /// via this invite link; 1-99999 + /// + /// + /// Set to true, if users joining the chat via the link need to be approved by chat administrators. + /// If true, can't be specified + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns the edited invite link as a object. + public static async Task EditChatInviteLinkAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string inviteLink, + string? name = default, + DateTime? expireDate = default, + int? memberLimit = default, + bool? createsJoinRequest = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditChatInviteLinkRequest(chatId, inviteLink) { + Name = name, + ExpireDate = expireDate, + MemberLimit = memberLimit, + CreatesJoinRequest = createsJoinRequest, + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// The invite link to revoke + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns the revoked invite link as object. + public static async Task RevokeChatInviteLinkAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string inviteLink, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new RevokeChatInviteLinkRequest(chatId, inviteLink), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to approve a chat join request. The bot must be an administrator in the chat for this to + /// work and must have the administrator right. + /// Returns true on success. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task ApproveChatJoinRequest( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new ApproveChatJoinRequest(chatId, userId), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to decline a chat join request. The bot must be an administrator in the chat for this to + /// work and must have the administrator right. + /// Returns true on success. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task DeclineChatJoinRequest( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new DeclineChatJoinRequest(chatId, userId), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// New chat photo, uploaded using multipart/form-data + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetChatPhotoAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputFileStream photo, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new SetChatPhotoRequest(chatId, photo), cancellationToken) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task DeleteChatPhotoAsync( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new DeleteChatPhotoRequest(chatId), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// New chat title, 1-255 characters + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetChatTitleAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string title, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new SetChatTitleRequest(chatId, title), cancellationToken) + .ConfigureAwait(false); + + /// + /// 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 + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// New chat Description, 0-255 characters + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetChatDescriptionAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string? description = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetChatDescriptionRequest(chatId) { Description = description }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// '' admin right in a supergroup or + /// '' admin right in a channel + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of a message to pin + /// + /// Pass true, if it is not necessary to send a notification to all chat members about + /// the new pinned message. Notifications are always disabled in channels and private chats + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task PinChatMessageAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + bool? disableNotification = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)). + MakeRequestAsync( + request: new PinChatMessageRequest(chatId, messageId) { + DisableNotification = disableNotification + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// '' admin right in a supergroup or + /// '' admin right in a channel + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Identifier of a message to unpin. If not specified, the most recent pinned message (by sending date) + /// will be unpinned + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task UnpinChatMessageAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int? messageId = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)). + MakeRequestAsync( + request: new UnpinChatMessageRequest(chatId) { MessageId = messageId }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// 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 + /// '' admin right in a supergroup or + /// '' admin right in a channel + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task UnpinAllChatMessages( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)). + MakeRequestAsync(request: new UnpinAllChatMessagesRequest(chatId), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method for your bot to leave a group, supergroup or channel. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task LeaveChatAsync( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)). + MakeRequestAsync(request: new LeaveChatRequest(chatId), cancellationToken) + .ConfigureAwait(false); + + /// + /// 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.) + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns a object on success. + public static async Task GetChatAsync( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetChatRequest(chatId), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to get a list of administrators in a chat. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// + /// On success, returns an Array of 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 + /// + public static async Task GetChatAdministratorsAsync( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetChatAdministratorsRequest(chatId), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to get the number of members in a chat. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns on success.. + [Obsolete("Use GetChatMemberCountAsync")] + public static async Task GetChatMembersCountAsync( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetChatMembersCountRequest(chatId), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to get the number of members in a chat. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns on success.. + public static async Task GetChatMemberCountAsync( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetChatMemberCountRequest(chatId), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to get information about a member of a chat. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target supergroup or channel + /// (in the format @channelusername) + /// + /// Unique identifier of the target user + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns a object on success. + public static async Task GetChatMemberAsync( + this ITelegramBotClient botClient, + ChatId chatId, + long userId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetChatMemberRequest(chatId, userId), cancellationToken) + .ConfigureAwait(false); + + /// + /// 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 + /// optionally returned in requests to check + /// if the bot can use this method. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Name of the sticker set to be set as the group sticker set + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetChatStickerSetAsync( + this ITelegramBotClient botClient, + ChatId chatId, + string stickerSetName, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new SetChatStickerSetRequest(chatId, stickerSetName), cancellationToken) + .ConfigureAwait(false); + + /// + /// 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 + /// optionally returned in requests to + /// check if the bot can use this method + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task DeleteChatStickerSetAsync( + this ITelegramBotClient botClient, + ChatId chatId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new DeleteChatStickerSetRequest(chatId), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to send answers to callback queries sent from + /// inline keyboards. The answer will be displayed + /// to the user as a notification at the top of the chat screen or as an alert + /// + /// + /// 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 @Botfather and accept the terms. Otherwise, you may use + /// links like t.me/your_bot?start=XXXX that open your bot with a parameter + /// + /// An instance of + /// Unique identifier for the query to be answered + /// + /// Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters + /// + /// + /// If true, an alert will be shown by the client instead of a notification at the top of the chat + /// screen. Defaults to false + /// + /// + /// URL that will be opened by the user's client. If you have created a + /// Game and accepted the conditions via + /// @Botfather, specify the URL that opens your game — note that this will only work if the query + /// comes from a callback_game button + /// + /// Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter + /// + /// + /// + /// 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 + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AnswerCallbackQueryAsync( + this ITelegramBotClient botClient, + string callbackQueryId, + string? text = default, + bool? showAlert = default, + string? url = default, + int? cacheTime = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AnswerCallbackQueryRequest(callbackQueryId) { + Text = text, + ShowAlert = showAlert, + Url = url, + CacheTime = cacheTime + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to set the result of an interaction with a Web App and send a corresponding message on + /// behalf of the user to the chat from which the query originated. On success, a + /// object is returned. + /// + /// An instance of + /// Unique identifier for the query to be answered + /// + /// An object describing the message to be sent + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AnswerWebAppQueryAsync( + this ITelegramBotClient botClient, + string webAppQueryId, + InlineQueryResult result, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AnswerWebAppQueryRequest(webAppQueryId, result), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to change the list of the bot’s commands. + /// See for more details about bot commands + /// + /// An instance of + /// + /// A list of bot commands to be set as the list of the bot’s commands. At most 100 commands can be specified + /// + /// + /// An object, describing scope of users for which the commands are relevant. + /// Defaults to . + /// + /// + /// A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given + /// , for whose language there are no dedicated commands + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetMyCommandsAsync( + this ITelegramBotClient botClient, + IEnumerable commands, + BotCommandScope? scope = default, + string? languageCode = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetMyCommandsRequest(commands) { + Scope = scope, + LanguageCode = languageCode + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to delete the list of the bot’s commands for the given and + /// user language. After deletion, + /// higher level commands + /// will be shown to affected users + /// + /// An instance of + /// + /// An object, describing scope of users for which the commands are relevant. + /// Defaults to . + /// + /// + /// A two-letter ISO 639-1 language code. If empty, commands will be applied to all users from the given + /// , for whose language there are no dedicated commands + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task DeleteMyCommandsAsync( + this ITelegramBotClient botClient, + BotCommandScope? scope = default, + string? languageCode = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new DeleteMyCommandsRequest { + Scope = scope, + LanguageCode = languageCode + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to get the current list of the bot’s commands for the given and + /// user language + /// + /// An instance of + /// + /// An object, describing scope of users. Defaults to . + /// + /// + /// A two-letter ISO 639-1 language code or an empty string + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// + /// Returns Array of on success. If commands aren't set, an empty list is returned + /// + public static async Task GetMyCommandsAsync( + this ITelegramBotClient botClient, + BotCommandScope? scope = default, + string? languageCode = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new GetMyCommandsRequest { + Scope = scope, + LanguageCode = languageCode + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to change the bot’s menu button in a private chat, or the default menu button. + /// + /// An instance of + /// + /// Unique identifier for the target private chat. If not specified, default bot’s menu button will be changed + /// + /// + /// An object for the new bot’s menu button. Defaults to + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetChatMenuButtonAsync( + this ITelegramBotClient botClient, + long? chatId = default, + MenuButton? menuButton = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetChatMenuButtonRequest { ChatId = chatId, MenuButton = menuButton }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to get the current value of the bot’s menu button in a private chat, + /// or the default menu button. + /// + /// An instance of + /// + /// Unique identifier for the target private chat. If not specified, default bot’s menu button will be returned + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// set for the given chat id or a default one + public static async Task GetChatMenuButtonAsync( + this ITelegramBotClient botClient, + long? chatId = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new GetChatMenuButtonRequest() { ChatId = chatId }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to change the default administrator rights requested by the bot when it's added as an + /// administrator to groups or channels. These rights will be suggested to users, but they are free to modify + /// the list before adding the bot. + /// + /// An instance of + /// + /// An object describing new default administrator rights. If not specified, the default administrator rights + /// will be cleared. + /// + /// + /// Pass true to change the default administrator rights of the bot in channels. Otherwise, the default + /// administrator rights of the bot for groups and supergroups will be changed. + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetMyDefaultAdministratorRightsAsync( + this ITelegramBotClient botClient, + ChatAdministratorRights? rights = default, + bool? forChannels = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetMyDefaultAdministratorRightsRequest() { + Rights = rights, + ForChannels = forChannels, + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to get the current default administrator rights of the bot. + /// + /// An instance of + /// + /// Pass true to change the default administrator rights of the bot in channels. Otherwise, the default + /// administrator rights of the bot for groups and supergroups will be changed. + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Default or channel + public static async Task GetMyDefaultAdministratorRightsAsync( + this ITelegramBotClient botClient, + bool? forChannels = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new GetMyDefaultAdministratorRightsRequest { ForChannels = forChannels }, + cancellationToken + ) + .ConfigureAwait(false); + + #endregion Available methods + + #region Updating messages + + /// + /// Use this method to edit text and game messages. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + /// New text of the message, 1-4096 characters after entities parsing + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in message text, which can be specified instead + /// of + /// + /// Disables link previews for links in this message + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success the edited is returned. + public static async Task EditMessageTextAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + string text, + ParseMode? parseMode = default, + IEnumerable? entities = default, + bool? disableWebPagePreview = default, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditMessageTextRequest(chatId, messageId, text) { + ParseMode = parseMode, + Entities = entities, + DisableWebPagePreview = disableWebPagePreview, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit text and game messages. + /// + /// An instance of + /// Identifier of the inline message + /// New text of the message, 1-4096 characters after entities parsing + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in message text, which can be specified instead + /// of + /// + /// Disables link previews for links in this message + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task EditMessageTextAsync( + this ITelegramBotClient botClient, + string inlineMessageId, + string text, + ParseMode? parseMode = default, + IEnumerable? entities = default, + bool? disableWebPagePreview = default, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditInlineMessageTextRequest(inlineMessageId, text) { + ParseMode = parseMode, + Entities = entities, + DisableWebPagePreview = disableWebPagePreview, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit captions of messages. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// dentifier of the message to edit + /// New caption of the message, 0-1024 characters after entities parsing + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success the edited is returned. + public static async Task EditMessageCaptionAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + string? caption, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditMessageCaptionRequest(chatId, messageId) { + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit captions of messages. + /// + /// An instance of + /// Identifier of the inline message + /// New caption of the message, 0-1024 characters after entities parsing + /// + /// Mode for parsing entities in the new caption. See + /// formatting options for + /// more details + /// + /// + /// List of special entities that appear in the caption, which can be specified instead + /// of + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task EditMessageCaptionAsync( + this ITelegramBotClient botClient, + string inlineMessageId, + string? caption, + ParseMode? parseMode = default, + IEnumerable? captionEntities = default, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditInlineMessageCaptionRequest(inlineMessageId) { + Caption = caption, + ParseMode = parseMode, + CaptionEntities = captionEntities, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit animation, audio, document, photo, or video messages. If a message is part of + /// a message album, then it can be edited only to an audio for audio albums, only to a document for document + /// albums and to a photo or a video otherwise. Use a previously uploaded file via its + /// or specify a URL + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + /// A new media content of the message + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success the edited is returned. + public static async Task EditMessageMediaAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + InputMediaBase media, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditMessageMediaRequest(chatId, messageId, media) { + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit animation, audio, document, photo, or video messages. If a message is part of + /// a message album, then it can be edited only to an audio for audio albums, only to a document for document + /// albums and to a photo or a video otherwise. Use a previously uploaded file via its + /// or specify a URL + /// + /// An instance of + /// Identifier of the inline message + /// A new media content of the message + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task EditMessageMediaAsync( + this ITelegramBotClient botClient, + string inlineMessageId, + InputMediaBase media, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditInlineMessageMediaRequest(inlineMessageId, media) { + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit only the reply markup of messages. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to edit + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success the edited is returned. + public static async Task EditMessageReplyMarkupAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditMessageReplyMarkupRequest(chatId, messageId) { + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to edit only the reply markup of messages. + /// + /// An instance of + /// Identifier of the inline message + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task EditMessageReplyMarkupAsync( + this ITelegramBotClient botClient, + string inlineMessageId, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new EditInlineMessageReplyMarkupRequest(inlineMessageId) { + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to stop a poll which was sent by the bot. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the original message with the poll + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the stopped with the final results is returned. + public static async Task StopPollAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new StopPollRequest(chatId, messageId) { + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to delete a message, including service messages, with the following limitations: + /// + /// A message can only be deleted if it was sent less than 48 hours ago + /// A dice message in a private chat can only be deleted if it was sent more than 24 hours ago + /// Bots can delete outgoing messages in private chats, groups, and supergroups + /// Bots can delete incoming messages in private chats + /// Bots granted can_post_messages permissions can delete outgoing messages in channels + /// If the bot is an administrator of a group, it can delete any message there + /// + /// If the bot has can_delete_messages permission in a supergroup or a channel, it can delete any message there + /// + /// + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Identifier of the message to delete + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task DeleteMessageAsync( + this ITelegramBotClient botClient, + ChatId chatId, + int messageId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new DeleteMessageRequest(chatId, messageId), cancellationToken) + .ConfigureAwait(false); + + #endregion Updating messages + + #region Stickers + + /// + /// Use this method to send static .WEBP or animated .TGS stickers. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// + /// Sticker to send. Pass a 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 .WEBP file from + /// the Internet, or upload a new one using multipart/form-data + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendStickerAsync( + this ITelegramBotClient botClient, + ChatId chatId, + InputOnlineFile sticker, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + IReplyMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendStickerRequest(chatId, sticker) { + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to get a sticker set. + /// + /// An instance of + /// Name of the sticker set + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, a object is returned. + public static async Task GetStickerSetAsync( + this ITelegramBotClient botClient, + string name, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetStickerSetRequest(name), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to upload a .PNG file with a sticker for later use in + /// , , + /// , , + /// and methods + /// (can be used multiple times). + /// + /// An instance of + /// User identifier of sticker file owner + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, + /// and either width or height must be exactly 512px + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// Returns the uploaded on success. + public static async Task UploadStickerFileAsync( + this ITelegramBotClient botClient, + long userId, + InputFileStream pngSticker, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new UploadStickerFileRequest(userId, pngSticker), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to create a new static sticker set owned by a user. The bot will be able to edit the + /// sticker set thus created. + /// + /// An instance of + /// User identifier of created sticker set owner + /// + /// Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). Can contain + /// only english letters, digits and underscores. Must begin with a letter, can't contain consecutive + /// underscores and must end in "_by_<bot username>". <bot_username> is case + /// insensitive. 1-64 characters + /// + /// Sticker set title, 1-64 characters + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, + /// and either width or height must be exactly 512px. Pass a + /// as a String to send a file that already exists + /// on the Telegram servers, 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 + /// + /// One or more emoji corresponding to the sticker + /// Pass true, if a set of mask stickers should be created + /// An object for position where the mask should be placed on faces + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task CreateNewStaticStickerSetAsync( + this ITelegramBotClient botClient, + long userId, + string name, + string title, + InputOnlineFile pngSticker, + string emojis, + bool? containsMasks = default, + MaskPosition? maskPosition = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new CreateNewStaticStickerSetRequest(userId, name, title, pngSticker, emojis) { + ContainsMasks = containsMasks, + MaskPosition = maskPosition + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to create a new animated sticker set owned by a user. The bot will be able to edit + /// the sticker set thus created. + /// + /// An instance of + /// User identifier of created sticker set owner + /// + /// Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). + /// Can contain only english letters, digits and underscores. Must begin with a letter, can't contain + /// consecutive underscores and must end in "_by_<bot username>". <bot_username> + /// is case insensitive. 1-64 characters + /// + /// Sticker set title, 1-64 characters + /// + /// TGS animation with the sticker, uploaded using multipart/form-data. See + /// + /// for technical requirements + /// + /// One or more emoji corresponding to the sticker + /// Pass true, if a set of mask stickers should be created + /// An object for position where the mask should be placed on faces + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task CreateNewAnimatedStickerSetAsync( + this ITelegramBotClient botClient, + long userId, + string name, + string title, + InputFileStream tgsSticker, + string emojis, + bool? containsMasks = default, + MaskPosition? maskPosition = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new CreateNewAnimatedStickerSetRequest(userId, name, title, tgsSticker, emojis) { + ContainsMasks = containsMasks, + MaskPosition = maskPosition + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to create a new animated sticker set owned by a user. The bot will be able to edit + /// the sticker set thus created. + /// + /// An instance of + /// User identifier of created sticker set owner + /// + /// Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). + /// Can contain only english letters, digits and underscores. Must begin with a letter, can't contain + /// consecutive underscores and must end in "_by_<bot username>". <bot_username> + /// is case insensitive. 1-64 characters + /// + /// Sticker set title, 1-64 characters + /// + /// WEBM video with the sticker, uploaded using multipart/form-data. See + /// + /// for technical requirements + /// + /// One or more emoji corresponding to the sticker + /// Pass true, if a set of mask stickers should be created + /// An object for position where the mask should be placed on faces + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task CreateNewVideoStickerSetAsync( + this ITelegramBotClient botClient, + long userId, + string name, + string title, + InputFileStream webmSticker, + string emojis, + bool? containsMasks = default, + MaskPosition? maskPosition = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new CreateNewVideoStickerSetRequest( + userId: userId, + name: name, + title: title, + webmSticker: webmSticker, + emojis: emojis) { + ContainsMasks = containsMasks, + MaskPosition = maskPosition + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to add a new static sticker to a set created by the bot. Static sticker sets can have up + /// to 120 stickers. + /// + /// An instance of + /// User identifier of sticker set owner + /// Sticker set name + /// + /// PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, + /// and either width or height must be exactly 512px. Pass a + /// as a String to send a file that already exists + /// on the Telegram servers, 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 + /// + /// One or more emoji corresponding to the sticker + /// An object for position where the mask should be placed on faces + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AddStaticStickerToSetAsync( + this ITelegramBotClient botClient, + long userId, + string name, + InputOnlineFile pngSticker, + string emojis, + MaskPosition? maskPosition = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AddStaticStickerToSetRequest(userId, name, pngSticker, emojis) { + MaskPosition = maskPosition + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to add a new video sticker to a set created by the bot. Video stickers can be added to + /// video sticker sets and only to them. Video sticker sets can have up to 50 stickers + /// + /// An instance of + /// User identifier of sticker set owner + /// Sticker set name + /// + /// TGS animation with the sticker, uploaded using multipart/form-data. See + /// + /// for technical requirements + /// + /// One or more emoji corresponding to the sticker + /// An object for position where the mask should be placed on faces + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AddVideoStickerToSetAsync( + this ITelegramBotClient botClient, + long userId, + string name, + InputFileStream webmSticker, + string emojis, + MaskPosition? maskPosition = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AddVideoStickerToSetRequest(userId, name, webmSticker, emojis) { + MaskPosition = maskPosition + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to add a new animated sticker to a set created by the bot. Animated stickers can be added to + /// animated sticker sets and only to them. Animated sticker sets can have up to 50 stickers + /// + /// An instance of + /// User identifier of sticker set owner + /// Sticker set name + /// + /// TGS animation with the sticker, uploaded using multipart/form-data. See + /// + /// for technical requirements + /// + /// One or more emoji corresponding to the sticker + /// An object for position where the mask should be placed on faces + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AddAnimatedStickerToSetAsync( + this ITelegramBotClient botClient, + long userId, + string name, + InputFileStream tgsSticker, + string emojis, + MaskPosition? maskPosition = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AddAnimatedStickerToSetRequest(userId, name, tgsSticker, emojis) { + MaskPosition = maskPosition + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to move a sticker in a set created by the bot to a specific position. + /// + /// An instance of + /// File identifier of the sticker + /// New sticker position in the set, zero-based + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetStickerPositionInSetAsync( + this ITelegramBotClient botClient, + string sticker, + int position, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new SetStickerPositionInSetRequest(sticker, position), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to delete a sticker from a set created by the bot. + /// + /// An instance of + /// File identifier of the sticker + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task DeleteStickerFromSetAsync( + this ITelegramBotClient botClient, + string sticker, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new DeleteStickerFromSetRequest(sticker), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated + /// sticker sets only. + /// + /// An instance of + /// Sticker set name + /// User identifier of the sticker set owner + /// + /// A PNG image with the thumbnail, must be up to 128 kilobytes in size and have width and height + /// exactly 100px, or a TGS animation with the thumbnail up to 32 kilobytes in size; see + /// for animated sticker + /// technical requirements. Pass a as a String to send a file that + /// already exists on the Telegram servers, 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. Animated sticker set thumbnail can't be + /// uploaded via HTTP URL + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task SetStickerSetThumbAsync( + this ITelegramBotClient botClient, + string name, + long userId, + InputOnlineFile? thumb = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetStickerSetThumbRequest(name, userId) { + Thumb = thumb + }, + cancellationToken + ) + .ConfigureAwait(false); + + #endregion + + #region Inline mode + + /// + /// Use this method to send answers to an inline query. + /// + /// + /// No more than 50 results per query are allowed. + /// + /// An instance of + /// Unique identifier for the answered query + /// An array of results for the inline query + /// + /// The maximum amount of time in seconds that the result of the inline query may be cached on the server. + /// Defaults to 300 + /// + /// + /// Pass true, if results may be cached on the server side only for the user that sent the query. + /// By default, results may be returned to any user who sends the same query + /// + /// + /// Pass the offset that a client should send in the next query with the same text to receive more results. + /// Pass an empty string if there are no more results or if you don't support pagination. + /// Offset length can't exceed 64 bytes + /// + /// + /// If passed, clients will display a button with specified text that switches the user to a private chat + /// with the bot and sends the bot a start message with the parameter + /// + /// + /// Deep-linking parameter for the /start + /// message sent to the bot when user presses the switch button. 1-64 characters, only A-Z, a-z, + /// 0-9, _ and - are allowed + /// + /// Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their + /// YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube + /// account' button above the results, or even before showing any. The user presses the button, switches + /// to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to + /// return an oauth link. Once done, the bot can offer a + /// button so that the user can + /// easily return to the chat where they wanted to use the bot’s inline capabilities + /// + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AnswerInlineQueryAsync( + this ITelegramBotClient botClient, + string inlineQueryId, + IEnumerable results, + int? cacheTime = default, + bool? isPersonal = default, + string? nextOffset = default, + string? switchPmText = default, + string? switchPmParameter = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AnswerInlineQueryRequest(inlineQueryId, results) { + CacheTime = cacheTime, + IsPersonal = isPersonal, + NextOffset = nextOffset, + SwitchPmText = switchPmText, + SwitchPmParameter = switchPmParameter + }, + cancellationToken + ) + .ConfigureAwait(false); + + # endregion Inline mode + + #region Payments + + /// + /// Use this method to send invoices. + /// + /// An instance of + /// + /// Unique identifier for the target chat or username of the target channel + /// (in the format @channelusername) + /// + /// Product name, 1-32 characters + /// Product description, 1-255 characters + /// + /// Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, + /// use for your internal processes + /// + /// + /// Payments provider token, obtained via @Botfather + /// + /// + /// Three-letter ISO 4217 currency code, see + /// more on currencies + /// + /// + /// Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, + /// bonus, etc.) + /// + /// + /// The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). + /// For example, for a maximum tip of US$ 1.45 pass = 145. + /// See the exp parameter in + /// currencies.json, it shows the + /// number of digits past the decimal point for each currency (2 for the majority of currencies). + /// Defaults to 0 + /// + /// + /// An array of suggested amounts of tips in the smallest units of the currency (integer, + /// not float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must + /// be positive, passed in a strictly increased order and must not exceed + /// + /// + /// Unique deep-linking parameter. If left empty, forwarded copies of the sent message will have + /// a Pay button, allowing multiple users to pay directly from the forwarded message, using the same + /// invoice. If non-empty, forwarded copies of the sent message will have a URL button with a deep + /// link to the bot (instead of a Pay button), with the value used as the start parameter + /// + /// + /// A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed + /// description of required fields should be provided by the payment provide + /// + /// + /// URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. + /// People like it better when they see what they are paying for + /// + /// Photo size + /// Photo width + /// Photo height + /// Pass true, if you require the user's full name to complete the order + /// + /// Pass true, if you require the user's phone number to complete the order + /// + /// Pass true, if you require the user's email to complete the order + /// + /// Pass true, if you require the user's shipping address to complete the order + /// + /// + /// Pass true, if user's phone number should be sent to provider + /// + /// + /// Pass true, if user's email address should be sent to provider + /// + /// Pass true, if the final price depends on the shipping method + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendInvoiceAsync( + this ITelegramBotClient botClient, + long chatId, + string title, + string description, + string payload, + string providerToken, + string currency, + IEnumerable prices, + int? maxTipAmount = default, + IEnumerable? suggestedTipAmounts = default, + string? startParameter = default, + string? providerData = default, + string? photoUrl = default, + int? photoSize = default, + int? photoWidth = default, + int? photoHeight = default, + bool? needName = default, + bool? needPhoneNumber = default, + bool? needEmail = default, + bool? needShippingAddress = default, + bool? sendPhoneNumberToProvider = default, + bool? sendEmailToProvider = default, + bool? isFlexible = default, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendInvoiceRequest( + chatId, + title, + description, + payload, + providerToken, + currency, + // ReSharper disable once PossibleMultipleEnumeration + prices) { + MaxTipAmount = maxTipAmount, + SuggestedTipAmounts = suggestedTipAmounts, + StartParameter = startParameter, + ProviderData = providerData, + PhotoUrl = photoUrl, + PhotoSize = photoSize, + PhotoWidth = photoWidth, + PhotoHeight = photoHeight, + NeedName = needName, + NeedPhoneNumber = needPhoneNumber, + NeedEmail = needEmail, + NeedShippingAddress = needShippingAddress, + SendPhoneNumberToProvider = sendPhoneNumberToProvider, + SendEmailToProvider = sendEmailToProvider, + IsFlexible = isFlexible, + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// If you sent an invoice requesting a shipping address and the parameter isFlexible" was specified, + /// the Bot API will send an with a field + /// to the bot. Use this method to reply to shipping queries + /// + /// An instance of + /// Unique identifier for the query to be answered + /// + /// Required if ok is true. An array of available shipping options + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AnswerShippingQueryAsync( + this ITelegramBotClient botClient, + string shippingQueryId, + IEnumerable shippingOptions, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AnswerShippingQueryRequest(shippingQueryId, shippingOptions), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// If you sent an invoice requesting a shipping address and the parameter isFlexible" was specified, + /// the Bot API will send an with a field + /// to the bot. Use this method to indicate failed shipping query + /// + /// An instance of + /// Unique identifier for the query to be answered + /// + /// Required if is false. Error message in + /// human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to + /// your desired address is unavailable'). Telegram will display this message to the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AnswerShippingQueryAsync( + this ITelegramBotClient botClient, + string shippingQueryId, + string errorMessage, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AnswerShippingQueryRequest(shippingQueryId, errorMessage), + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation + /// in the form of an with the field . + /// Use this method to respond to such pre-checkout queries. + /// + /// + /// Note: The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. + /// + /// An instance of + /// Unique identifier for the query to be answered + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AnswerPreCheckoutQueryAsync( + this ITelegramBotClient botClient, + string preCheckoutQueryId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new AnswerPreCheckoutQueryRequest(preCheckoutQueryId), cancellationToken) + .ConfigureAwait(false); + + /// + /// Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation + /// in the form of an with the field . + /// Use this method to respond to indicate failed pre-checkout query + /// + /// An instance of + /// Unique identifier for the query to be answered + /// + /// Required if is false. Error message in + /// human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, + /// somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment + /// details. Please choose a different color or garment!"). Telegram will display this message to the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + public static async Task AnswerPreCheckoutQueryAsync( + this ITelegramBotClient botClient, + string preCheckoutQueryId, + string errorMessage, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new AnswerPreCheckoutQueryRequest(preCheckoutQueryId, errorMessage), + cancellationToken + ) + .ConfigureAwait(false); + + #endregion Payments + + #region Games + + /// + /// Use this method to send a game. + /// + /// An instance of + /// Unique identifier for the target chat + /// + /// Short name of the game, serves as the unique identifier for the game. Set up your games via + /// @Botfather + /// + /// + /// Sends the message silently. Users will receive a notification with no sound + /// + /// Protects the contents of sent messages from forwarding and saving + /// If the message is a reply, ID of the original message + /// + /// Pass true, if the message should be sent even if the specified replied-to message is not found + /// + /// + /// Additional interface options. An inline keyboard, + /// custom reply keyboard, instructions to + /// remove reply keyboard or to + /// force a reply from the user + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, the sent is returned. + public static async Task SendGameAsync( + this ITelegramBotClient botClient, + long chatId, + string gameShortName, + bool? disableNotification = default, + bool? protectContent = default, + int? replyToMessageId = default, + bool? allowSendingWithoutReply = default, + InlineKeyboardMarkup? replyMarkup = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SendGameRequest(chatId, gameShortName) { + DisableNotification = disableNotification, + ProtectContent = protectContent, + ReplyToMessageId = replyToMessageId, + AllowSendingWithoutReply = allowSendingWithoutReply, + ReplyMarkup = replyMarkup + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to set the score of the specified user in a game. + /// + /// An instance of + /// User identifier + /// New score, must be non-negative + /// Unique identifier for the target chat + /// Identifier of the sent message + /// + /// Pass true, if the high score is allowed to decrease. This can be useful when fixing mistakes + /// or banning cheaters + /// + /// + /// Pass true, if the game message should not be automatically edited to include the current scoreboard + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// + /// On success returns the edited . Returns an error, if the new score is not greater + /// than the user's current score in the chat and is false + /// + public static async Task SetGameScoreAsync( + this ITelegramBotClient botClient, + long userId, + int score, + long chatId, + int messageId, + bool? force = default, + bool? disableEditMessage = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetGameScoreRequest(userId, score, chatId, messageId) { + Force = force, + DisableEditMessage = disableEditMessage + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to set the score of the specified user in a game. + /// + /// An instance of + /// User identifier + /// New score, must be non-negative + /// Identifier of the inline message. + /// + /// Pass true, if the high score is allowed to decrease. This can be useful when fixing mistakes + /// or banning cheaters + /// + /// + /// Pass true, if the game message should not be automatically edited to include the current scoreboard + /// + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// + /// Returns an error, if the new score is not greater than the user's current score in the chat and + /// is false + /// + public static async Task SetGameScoreAsync( + this ITelegramBotClient botClient, + long userId, + int score, + string inlineMessageId, + bool? force = default, + bool? disableEditMessage = default, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new SetInlineGameScoreRequest(userId, score, inlineMessageId) { + Force = force, + DisableEditMessage = disableEditMessage + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Use this method to get data for high score tables. Will return the score of the specified user and + /// several of their neighbors in a game. + /// + /// + /// This method will currently return scores for the target user, plus two of their closest neighbors on + /// each side. Will also return the top three users if the user and his neighbors are not among them. + /// Please note that this behavior is subject to change. + /// + /// An instance of + /// Target user id + /// Unique identifier for the target chat + /// Identifier of the sent message + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, returns an Array of objects. + public static async Task GetGameHighScoresAsync( + this ITelegramBotClient botClient, + long userId, + long chatId, + int messageId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync(request: new GetGameHighScoresRequest(userId, chatId, messageId), cancellationToken) + .ConfigureAwait(false); + + /// + /// Use this method to get data for high score tables. Will return the score of the specified user and + /// several of their neighbors in a game. + /// + /// + /// This method will currently return scores for the target user, plus two of their closest neighbors + /// on each side. Will also return the top three users if the user and his neighbors are not among them. + /// Please note that this behavior is subject to change. + /// + /// An instance of + /// User identifier + /// Identifier of the inline message + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation + /// + /// On success, returns an Array of objects. + public static async Task GetGameHighScoresAsync( + this ITelegramBotClient botClient, + long userId, + string inlineMessageId, + CancellationToken cancellationToken = default + ) => + await botClient.ThrowIfNull(nameof(botClient)) + .MakeRequestAsync( + request: new GetInlineGameHighScoresRequest(userId, inlineMessageId), + cancellationToken + ) + .ConfigureAwait(false); + + #endregion Games + } +} \ No newline at end of file diff --git a/TelegramBot/TelegramBotClientExtensions.Polling.cs b/TelegramBot/TelegramBotClientExtensions.Polling.cs new file mode 100644 index 0000000..6fb714d --- /dev/null +++ b/TelegramBot/TelegramBotClientExtensions.Polling.cs @@ -0,0 +1,309 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Telegram.Bot.Types; +//using JetBrains.Annotations; +using Telegram.Bot.Polling; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot { + + + /// + /// Provides extension methods for that allow for polling + /// + //[PublicAPI] + public static partial class TelegramBotClientExtensions { + /// + /// Starts receiving s on the ThreadPool, invoking + /// for each. + /// + /// This method does not block. GetUpdates will be called AFTER the + /// returns + /// + /// + /// + /// The used for processing s + /// + /// The used for making GetUpdates calls + /// Options used to configure getUpdates request + /// + /// The with which you can stop receiving + /// + public static void StartReceiving( + this ITelegramBotClient botClient, + ReceiverOptions? receiverOptions = default, + CancellationToken cancellationToken = default + ) where TUpdateHandler : IUpdateHandler, new() => + StartReceiving( + botClient: botClient, + updateHandler: new TUpdateHandler(), + receiverOptions: receiverOptions, + cancellationToken: cancellationToken + ); + + /// + /// Starts receiving s on the ThreadPool, invoking + /// for each. + /// + /// This method does not block. GetUpdates will be called AFTER the returns + /// + /// + /// The used for making GetUpdates calls + /// Delegate used for processing s + /// Delegate used for processing polling errors + /// Options used to configure getUpdates request + /// + /// The with which you can stop receiving + /// + public static void StartReceiving( + this ITelegramBotClient botClient, + Func updateHandler, + Func pollingErrorHandler, + ReceiverOptions? receiverOptions = default, + CancellationToken cancellationToken = default + ) => + StartReceiving( + botClient: botClient, + updateHandler: new DefaultUpdateHandler( + updateHandler: updateHandler, + pollingErrorHandler: pollingErrorHandler + ), + receiverOptions: receiverOptions, + cancellationToken: cancellationToken + ); + + /// + /// Starts receiving s on the ThreadPool, invoking + /// for each. + /// + /// This method does not block. GetUpdates will be called AFTER the returns + /// + /// + /// The used for making GetUpdates calls + /// Delegate used for processing s + /// Delegate used for processing polling errors + /// Options used to configure getUpdates request + /// + /// The with which you can stop receiving + /// + public static void StartReceiving( + this ITelegramBotClient botClient, + Action updateHandler, + Action pollingErrorHandler, + ReceiverOptions? receiverOptions = default, + CancellationToken cancellationToken = default + ) => + StartReceiving( + botClient: botClient, + updateHandler: new DefaultUpdateHandler( + updateHandler: (bot, update, token) => { + updateHandler.Invoke(bot, update, token); + return Task.CompletedTask; + }, + pollingErrorHandler: (bot, exception, token) => { + pollingErrorHandler.Invoke(bot, exception, token); + return Task.CompletedTask; + } + ), + receiverOptions: receiverOptions, + cancellationToken: cancellationToken + ); + + /// + /// Starts receiving s on the ThreadPool, invoking + /// for each. + /// + /// This method does not block. GetUpdates will be called AFTER the + /// returns + /// + /// + /// The used for making GetUpdates calls + /// + /// The used for processing s + /// + /// Options used to configure getUpdates request + /// + /// The with which you can stop receiving + /// + public static void StartReceiving( + this ITelegramBotClient botClient, + IUpdateHandler updateHandler, + ReceiverOptions? receiverOptions = default, + CancellationToken cancellationToken = default) { + if(botClient is null) { + throw new ArgumentNullException(nameof(botClient)); + } + if(updateHandler is null) { + throw new ArgumentNullException(nameof(updateHandler)); + } + + // ReSharper disable once MethodSupportsCancellation +#pragma warning disable CA2016 + Task.Run(async () => +#pragma warning restore CA2016 + { + try { + await ReceiveAsync( + botClient: botClient, + updateHandler: updateHandler, + receiverOptions: receiverOptions, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + } catch(OperationCanceledException) { + // ignored + } catch(Exception ex) { + try { + await updateHandler.HandlePollingErrorAsync( + botClient: botClient, + exception: ex, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + } catch(OperationCanceledException) { + // ignored + } + } + }); + } + + /// + /// Starts receiving s on the ThreadPool, invoking + /// for each. + /// + /// This method will block if awaited. GetUpdates will be called AFTER the + /// returns + /// + /// + /// + /// The used for processing s + /// + /// The used for making GetUpdates calls + /// Options used to configure getUpdates request + /// + /// The with which you can stop receiving + /// + /// + /// A that will be completed when cancellation will be requested through + /// + /// + public static async Task ReceiveAsync( + this ITelegramBotClient botClient, + ReceiverOptions? receiverOptions = default, + CancellationToken cancellationToken = default + ) where TUpdateHandler : IUpdateHandler, new() => + await ReceiveAsync( + botClient: botClient, + updateHandler: new TUpdateHandler(), + receiverOptions: receiverOptions, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + + /// + /// Starts receiving s on the ThreadPool, invoking + /// for each. + /// + /// This method will block if awaited. GetUpdates will be called AFTER the + /// returns + /// + /// + /// The used for making GetUpdates calls + /// Delegate used for processing s + /// Delegate used for processing polling errors + /// Options used to configure getUpdates requests + /// + /// The with which you can stop receiving + /// + /// + /// A that will be completed when cancellation will be requested through + /// + /// + public static async Task ReceiveAsync( + this ITelegramBotClient botClient, + Func updateHandler, + Func pollingErrorHandler, + ReceiverOptions? receiverOptions = default, + CancellationToken cancellationToken = default + ) => + await ReceiveAsync( + botClient: botClient, + updateHandler: new DefaultUpdateHandler( + updateHandler: updateHandler, + pollingErrorHandler: pollingErrorHandler + ), + receiverOptions: receiverOptions, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + + /// + /// Starts receiving s on the ThreadPool, invoking + /// for each. + /// + /// This method will block if awaited. GetUpdates will be called AFTER the + /// returns + /// + /// + /// The used for making GetUpdates calls + /// Delegate used for processing s + /// Delegate used for processing polling errors + /// Options used to configure getUpdates requests + /// + /// The with which you can stop receiving + /// + /// + /// A that will be completed when cancellation will be requested through + /// + /// + public static async Task ReceiveAsync( + this ITelegramBotClient botClient, + Action updateHandler, + Action pollingErrorHandler, + ReceiverOptions? receiverOptions = default, + CancellationToken cancellationToken = default + ) => + await ReceiveAsync( + botClient: botClient, + updateHandler: new DefaultUpdateHandler( + updateHandler: (bot, update, token) => { + updateHandler.Invoke(bot, update, token); + return Task.CompletedTask; + }, + pollingErrorHandler: (bot, exception, token) => { + pollingErrorHandler.Invoke(bot, exception, token); + return Task.CompletedTask; + } + ), + receiverOptions: receiverOptions, + cancellationToken: cancellationToken + ).ConfigureAwait(false); + + /// + /// Starts receiving s on the ThreadPool, invoking + /// for each. + /// + /// This method will block if awaited. GetUpdates will be called AFTER the + /// returns + /// + /// + /// The used for making GetUpdates calls + /// + /// The used for processing s + /// + /// Options used to configure getUpdates requests + /// + /// The with which you can stop receiving + /// + /// + /// A that will be completed when cancellation will be requested through + /// + /// + public static async Task ReceiveAsync( + this ITelegramBotClient botClient, + IUpdateHandler updateHandler, + ReceiverOptions? receiverOptions = default, + CancellationToken cancellationToken = default + ) => + await new DefaultUpdateReceiver(botClient: botClient, receiverOptions: receiverOptions) + .ReceiveAsync(updateHandler: updateHandler, cancellationToken: cancellationToken) + .ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/TelegramBot/TelegramBotClientOptions.cs b/TelegramBot/TelegramBotClientOptions.cs new file mode 100644 index 0000000..7a62afb --- /dev/null +++ b/TelegramBot/TelegramBotClientOptions.cs @@ -0,0 +1,148 @@ +using System; +using System.Runtime.CompilerServices; +//using JetBrains.Annotations; + +namespace Telegram.Bot { + + + /// + /// This class is used to provide configuration for + /// + //[PublicAPI] + public class TelegramBotClientOptions { + const string BaseTelegramUrl = "https://api.telegram.org"; + + /// + /// API token + /// + public string Token { + get; + } + + /// + /// Used to change base url to your private bot api server URL. It looks like + /// http://localhost:8081. Path, query and fragment will be omitted if present. + /// + public string? BaseUrl { + get; + } + + /// + /// Indicates that test environment will be used + /// + public bool UseTestEnvironment { + get; + } + + /// + /// 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. + /// + public long? BotId { + get; + } + + /// + /// Indicates that local bot server will be used + /// + public bool LocalBotServer { + get; + } + + /// + /// Contains base url for downloading files + /// + public string BaseFileUrl { + get; + } + + /// + /// Contains base url for making requests + /// + public string BaseRequestUrl { + get; + } + + /// + /// Create a new instance. + /// + /// API token + /// + /// Used to change base url to your private bot api server URL. It looks like + /// http://localhost:8081. Path, query and fragment will be omitted if present. + /// + /// + /// + /// Thrown if format is invalid + /// + /// + /// Thrown if format is invalid + /// + public TelegramBotClientOptions(string token, string? baseUrl = default, bool useTestEnvironment = false) { + Token = token ?? throw new ArgumentNullException(nameof(token)); + BaseUrl = baseUrl; + UseTestEnvironment = useTestEnvironment; + + BotId = GetIdFromToken(token); + + LocalBotServer = baseUrl is not null; + var effectiveBaseUrl = LocalBotServer + ? ExtractBaseUrl(baseUrl) + : BaseTelegramUrl; + + BaseRequestUrl = useTestEnvironment + ? $"{effectiveBaseUrl}/bot{token}/test" + : $"{effectiveBaseUrl}/bot{token}"; + + BaseFileUrl = useTestEnvironment + ? $"{effectiveBaseUrl}/file/bot{token}/test" + : $"{effectiveBaseUrl}/file/bot{token}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static long? GetIdFromToken(string token) { +#if NETCOREAPP3_1_OR_GREATER + var span = token.AsSpan(); + var index = span.IndexOf(':'); + + if(index is < 1 or > 16) { + return null; + } + + var botIdSpan = span[..index]; + if(!long.TryParse(botIdSpan, out var botId)) { + return null; + } +#else + var index = token.IndexOf(value: ':'); + + if (index is < 1 or > 16) { return null; } + + var botIdSpan = token.Substring(startIndex: 0, length: index); + if (!long.TryParse(botIdSpan, out var botId)) { return null; } +#endif + + return botId; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static string ExtractBaseUrl(string? baseUrl) { + if(baseUrl is null) { + throw new ArgumentNullException(paramName: nameof(baseUrl)); + } + + if(!Uri.TryCreate(uriString: baseUrl, uriKind: UriKind.Absolute, out var baseUri) + || string.IsNullOrEmpty(value: baseUri.Scheme) + || string.IsNullOrEmpty(value: baseUri.Authority)) { + throw new ArgumentException( + message: "Invalid format. A valid base url looks \"http://localhost:8081\" ", + paramName: nameof(baseUrl) + ); + } + + return $"{baseUri.Scheme}://{baseUri.Authority}"; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Animation.cs b/TelegramBot/Types/Animation.cs new file mode 100644 index 0000000..f1493a8 --- /dev/null +++ b/TelegramBot/Types/Animation.cs @@ -0,0 +1,61 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents an animation file (GIF or H.264/MPEG-4 AVC video without sound). + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Animation : FileBase { + + /// + /// Video width as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Width { + get; set; + } + + /// + /// Video height as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Height { + get; set; + } + + /// + /// Duration of the video in seconds as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Duration { + get; set; + } + + /// + /// Optional. Animation thumbnail as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PhotoSize? Thumb { + get; set; + } + + /// + /// Optional. Original animation filename as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FileName { + get; set; + } + + /// + /// Optional. MIME type of the file as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? MimeType { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ApiResponse.cs b/TelegramBot/Types/ApiResponse.cs new file mode 100644 index 0000000..7ee9756 --- /dev/null +++ b/TelegramBot/Types/ApiResponse.cs @@ -0,0 +1,81 @@ +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Represents bot API response + /// + /// Expected type of operation result + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ApiResponse { + /// + /// Gets a value indicating whether the request was successful. + /// + [JsonProperty(Required = Required.Always)] + public bool Ok { + get; private set; + } + + /// + /// Gets the error message. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; private set; + } + + /// + /// Gets the error code. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ErrorCode { + get; private set; + } + + /// + /// Contains information about why a request was unsuccessful. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ResponseParameters? Parameters { + get; private set; + } + + /// + /// Gets the result object. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [MaybeNull] + [AllowNull] + public TResult Result { + get; private set; + } + + /// + /// Initializes an instance of + /// + /// Indicating whether the request was successful + /// Result object + /// Error code + /// Error message + /// Information about why a request was unsuccessful + public ApiResponse( + bool ok, + TResult result, + int errorCode, + string description, + ResponseParameters? parameters = default) { + Ok = ok; + ErrorCode = errorCode; + Description = description; + Parameters = parameters; + Result = result; + } + + [JsonConstructor] + private ApiResponse() { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Audio.cs b/TelegramBot/Types/Audio.cs new file mode 100644 index 0000000..b9910f0 --- /dev/null +++ b/TelegramBot/Types/Audio.cs @@ -0,0 +1,60 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents an audio file to be treated as music by the Telegram clients. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Audio : FileBase { + /// + /// Duration of the audio in seconds as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Duration { + get; set; + } + + /// + /// Optional. Performer of the audio as defined by sender or by audio tags + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Performer { + get; set; + } + + /// + /// Optional. Title of the audio as defined by sender or by audio tags + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + /// Optional. Original filename as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FileName { + get; set; + } + + /// + /// Optional. MIME type of the file as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? MimeType { + get; set; + } + + /// + /// Optional. Thumbnail of the album cover to which the music file belongs + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PhotoSize? Thumb { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/BotCommand.cs b/TelegramBot/Types/BotCommand.cs new file mode 100644 index 0000000..5315e76 --- /dev/null +++ b/TelegramBot/Types/BotCommand.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a bot command + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BotCommand { + /// + /// Text of the command, 1-32 characters. Can contain only lowercase English letters, digits and underscores. + /// + [JsonProperty(Required = Required.Always)] + public string Command { get; set; } = default!; + + /// + /// Description of the command, 3-256 characters. + /// + [JsonProperty(Required = Required.Always)] + public string Description { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/BotCommandScope.cs b/TelegramBot/Types/BotCommandScope.cs new file mode 100644 index 0000000..1033c2a --- /dev/null +++ b/TelegramBot/Types/BotCommandScope.cs @@ -0,0 +1,159 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents the scope to which bot commands are applied + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class BotCommandScope { + /// + /// Scope type + /// + [JsonProperty(Required = Required.Always)] + public abstract BotCommandScopeType Type { + get; + } + + /// + /// Create a default instance + /// + /// + public static BotCommandScopeDefault Default() => new(); + + /// + /// Create a instance for all private chats + /// + /// + public static BotCommandScopeAllPrivateChats AllPrivateChats() => new(); + + /// + /// Create a instance for all group chats + /// + public static BotCommandScopeAllGroupChats AllGroupChats() => new(); + + /// + /// Create a instance for all chat administrators + /// + public static BotCommandScopeAllChatAdministrators AllChatAdministrators() => + new(); + + /// + /// Create a instance for a specific + /// + /// + /// Unique identifier for the target or username of the target supergroup + /// + public static BotCommandScopeChat Chat(ChatId chatId) => new() { ChatId = chatId }; + + /// + /// Create a instance for a specific member in a specific + /// + /// + /// Unique identifier for the target or username of the target supergroup + /// + public static BotCommandScopeChatAdministrators ChatAdministrators(ChatId chatId) => + new() { + ChatId = chatId + }; + + /// + /// Represents the scope of bot commands, covering a specific member of a group or supergroup chat. + /// + /// + /// Unique identifier for the target or username of the target supergroup + /// + /// Unique identifier of the target user + public static BotCommandScopeChatMember ChatMember(ChatId chatId, long userId) => + new() { + ChatId = chatId, UserId = userId + }; + } + + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BotCommandScopeDefault : BotCommandScope { + /// + [JsonProperty(Required = Required.Always)] + public override BotCommandScopeType Type => BotCommandScopeType.Default; + } + + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BotCommandScopeAllPrivateChats : BotCommandScope { + /// + [JsonProperty(Required = Required.Always)] + public override BotCommandScopeType Type => BotCommandScopeType.AllPrivateChats; + } + + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BotCommandScopeAllGroupChats : BotCommandScope { + /// + [JsonProperty(Required = Required.Always)] + public override BotCommandScopeType Type => BotCommandScopeType.AllGroupChats; + } + + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BotCommandScopeAllChatAdministrators : BotCommandScope { + /// + [JsonProperty(Required = Required.Always)] + public override BotCommandScopeType Type => BotCommandScopeType.AllChatAdministrators; + } + + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BotCommandScopeChat : BotCommandScope { + /// + public override BotCommandScopeType Type => BotCommandScopeType.Chat; + + /// + /// Unique identifier for the target or username of the target supergroup + /// (in the format @supergroupusername) + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { get; set; } = default!; + } + + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BotCommandScopeChatAdministrators : BotCommandScope { + /// + [JsonProperty(Required = Required.Always)] + public override BotCommandScopeType Type => BotCommandScopeType.ChatAdministrators; + + /// + /// Unique identifier for the target or username of the target supergroup + /// (in the format @supergroupusername) + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { get; set; } = default!; + } + + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class BotCommandScopeChatMember : BotCommandScope { + /// + [JsonProperty(Required = Required.Always)] + public override BotCommandScopeType Type => BotCommandScopeType.ChatMember; + + /// + /// Unique identifier for the target or username of the target supergroup + /// (in the format @supergroupusername) + /// + [JsonProperty(Required = Required.Always)] + public ChatId ChatId { get; set; } = default!; + + /// + /// Unique identifier of the target user + /// + [JsonProperty(Required = Required.Always)] + public long UserId { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/CallbackGame.cs b/TelegramBot/Types/CallbackGame.cs new file mode 100644 index 0000000..99e4599 --- /dev/null +++ b/TelegramBot/Types/CallbackGame.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// A placeholder, currently holds no information. Use @BotFather + /// to set up your game. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class CallbackGame { + } +} \ No newline at end of file diff --git a/TelegramBot/Types/CallbackQuery.cs b/TelegramBot/Types/CallbackQuery.cs new file mode 100644 index 0000000..d79d712 --- /dev/null +++ b/TelegramBot/Types/CallbackQuery.cs @@ -0,0 +1,83 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.ReplyMarkups; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents an incoming callback query from a callback button in an + /// inline keyboard. If the button that originated the query was attached to + /// a message sent by the bot, the field will be present. If the button was attached to a + /// message sent via the bot (in inline mode), the field will be present. Exactly one + /// of the fields data or will be present. + /// + /// + /// NOTE: After the user presses a callback button, Telegram clients will display a progress bar until + /// you call . It is, therefore, necessary to react by calling + /// even if no notification to the user is needed (e.g., without + /// specifying any of the optional parameters). + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class CallbackQuery { + /// + /// Unique identifier for this query + /// + [JsonProperty(Required = Required.Always)] + public string Id { get; set; } = default!; + + /// + /// Sender + /// + [JsonProperty(Required = Required.Always)] + public User From { get; set; } = default!; + + /// + /// Optional. Description with the callback button that originated the query. Note that message content and + /// message date will not be available if the message is too old + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Message? Message { + get; set; + } + + /// + /// Optional. Identifier of the message sent via the bot in inline mode, that originated the query + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? InlineMessageId { + get; set; + } + + /// + /// Global identifier, uniquely corresponding to the chat to which the message with the callback button was + /// sent. Useful for high scores in games. + /// + [JsonProperty(Required = Required.Always)] + public string ChatInstance { get; set; } = default!; + + /// + /// Optional. Data associated with the callback button. + /// + /// + /// Be aware that a bad client can send arbitrary data in this field. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Data { + get; set; + } + + /// + /// Optional. Short name of a to be returned, serves as the unique identifier for the game. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GameShortName { + get; set; + } + + /// + /// Indicates if the User requests a Game + /// + public bool IsGameQuery => GameShortName != default; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Chat.cs b/TelegramBot/Types/Chat.cs new file mode 100644 index 0000000..8622eb5 --- /dev/null +++ b/TelegramBot/Types/Chat.cs @@ -0,0 +1,193 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Chat { + /// + /// Unique identifier for this chat. This number may have more + /// than 32 significant bits and some programming languages may have + /// difficulty/silent defects in interpreting it. But it has + /// at most 52 significant bits, so a signed 64-bit integer + /// or double-precision float type are safe for storing this identifier. + /// + [JsonProperty(Required = Required.Always)] + public long Id { + get; set; + } + + /// + /// Type of chat, can be either “private”, “group”, “supergroup” or “channel” + /// + [JsonProperty(Required = Required.Always)] + public ChatType Type { + get; set; + } + + /// + /// Optional. Title, for supergroups, channels and group chats + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + /// Optional. Username, for private chats, supergroups and channels if available + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Username { + get; set; + } + + /// + /// Optional. First name of the other party in a private chat + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FirstName { + get; set; + } + + /// + /// Optional. Last name of the other party in a private chat + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LastName { + get; set; + } + + /// + /// Optional. Chat photo. Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatPhoto? Photo { + get; set; + } + + /// + /// Optional. Bio of the other party in a private chat. Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Bio { + get; set; + } + + /// + /// Optional. true, if privacy settings of the other party in the private chat allows to use + /// tg://user?id=<user_id> links only in chats with the user. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? HasPrivateForwards { + get; set; + } + + /// + /// Optional. Description, for groups, supergroups and channel chats. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + /// Optional. Primary invite link, for groups, supergroups and channel chats. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? InviteLink { + get; set; + } + + /// + /// Optional. The most recent pinned message (by sending date). + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Message? PinnedMessage { + get; set; + } + + /// + /// Optional. Default chat member permissions, for groups and supergroups. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatPermissions? Permissions { + get; set; + } + + /// + /// Optional. For supergroups, the minimum allowed delay between consecutive messages sent by each + /// unpriviledged user. Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? SlowModeDelay { + get; set; + } + + /// + /// Optional. The time after which all messages sent to the chat will be automatically deleted; in seconds. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MessageAutoDeleteTime { + get; set; + } + + /// + /// Optional. true, if messages from the chat can't be forwarded to other chats. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? HasProtectedContent { + get; set; + } + + /// + /// Optional. For supergroups, name of group sticker set. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? StickerSetName { + get; set; + } + + /// + /// Optional. True, if the bot can change the group sticker set. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanSetStickerSet { + get; set; + } + + /// + /// Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for a channel + /// and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some + /// programming languages may have difficulty/silent defects in interpreting it. But it is smaller than + /// 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public long? LinkedChatId { + get; set; + } + + /// + /// Optional. For supergroups, the location to which the supergroup is connected. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatLocation? Location { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatAdministratorRights.cs b/TelegramBot/Types/ChatAdministratorRights.cs new file mode 100644 index 0000000..07a96f2 --- /dev/null +++ b/TelegramBot/Types/ChatAdministratorRights.cs @@ -0,0 +1,105 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Represents the rights of an administrator in a chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatAdministratorRights { + /// + /// true, if the user's presence in the chat is hidden + /// + [JsonProperty(Required = Required.Always)] + public bool IsAnonymous { + get; set; + } + + /// + /// 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 + /// + [JsonProperty(Required = Required.Always)] + public bool CanManageChat { + get; set; + } + + /// + /// true, if the administrator can delete messages of other users + /// + [JsonProperty(Required = Required.Always)] + public bool CanDeleteMessages { + get; set; + } + + /// + /// true, if the administrator can manage video chats + /// + [JsonProperty(Required = Required.Always)] + public bool CanManageVideoChats { + get; set; + } + + /// + /// true, if the administrator can restrict, ban or unban chat members + /// + [JsonProperty(Required = Required.Always)] + public bool CanRestrictMembers { + get; set; + } + + /// + /// 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 the user) + /// + [JsonProperty(Required = Required.Always)] + public bool CanPromoteMembers { + get; set; + } + + /// + /// true, if the user is allowed to change the chat title, photo and other settings + /// + [JsonProperty(Required = Required.Always)] + public bool CanChangeInfo { + get; set; + } + + /// + /// true, if the user is allowed to invite new users to the chat + /// + [JsonProperty(Required = Required.Always)] + public bool CanInviteUsers { + get; set; + } + + /// + /// Optional. true, if the administrator can post in the channel; channels only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanPostMessages { + get; set; + } + + /// + /// Optional. true, if the administrator can edit messages of other users and can pin messages; + /// channels only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanEditMessages { + get; set; + } + + /// + /// Optional. true, if the user is allowed to pin messages; groups and supergroups only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanPinMessages { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatId.cs b/TelegramBot/Types/ChatId.cs new file mode 100644 index 0000000..6de2416 --- /dev/null +++ b/TelegramBot/Types/ChatId.cs @@ -0,0 +1,147 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Newtonsoft.Json; +using Telegram.Bot.Converters; + +namespace Telegram.Bot.Types { + + + /// + /// Represents a ChatId + /// + [JsonConverter(typeof(ChatIdConverter))] + public class ChatId : IEquatable { + /// + /// Unique identifier for the chat + /// + public long? Identifier { + get; + } + + /// + /// Username of the supergroup or channel (in the format @channelusername) + /// + public string? Username { + get; + } + + /// + /// Create a using unique identifier for the chat + /// + /// Unique identifier for the chat + // ReSharper disable once MemberCanBePrivate.Global + public ChatId(long identifier) => Identifier = identifier; + + /// + /// Create a using unique identifier for the chat or username of + /// the supergroup or channel (in the format @channelusername) + /// + /// Unique identifier for the chat or username of + /// the supergroup or channel (in the format @channelusername) + /// + /// Thrown when string value isn`t number and doesn't start with @ + /// + /// Thrown when string value is null + public ChatId(string username) { + if(username is null) { + throw new ArgumentNullException(nameof(username)); + } + if(username.Length > 1 && username.StartsWith("@", StringComparison.InvariantCulture)) { + Username = username; + } else if(long.TryParse(username, out var identifier)) { + Identifier = identifier; + } else { + throw new ArgumentException("Username value should be Identifier or Username that starts with @"); + } + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => + obj switch { + ChatId chatId => this == chatId, + _ => false, + }; + + /// + public bool Equals(ChatId? other) => this == other; + + /// + /// Gets the hash code of this object + /// + /// A hash code for the current object. +#if NETCOREAPP3_1_OR_GREATER + public override int GetHashCode() => ToString().GetHashCode(StringComparison.InvariantCulture); +#else + public override int GetHashCode() => ToString().GetHashCode(); +#endif + + /// + /// Create a string out of a + /// + /// The as string + public override string ToString() => (Username ?? Identifier?.ToString(CultureInfo.InvariantCulture))!; + + /// + /// Create a using unique identifier for the chat + /// + /// Unique identifier for the chat + public static implicit operator ChatId(long identifier) => new(identifier); + + /// + /// Create a using unique identifier for the chat or username of + /// the supergroup or channel (in the format @channelusername) + /// + /// Unique identifier for the chat or username of + /// the supergroup or channel (in the format @channelusername) + /// + /// Thrown when string value isn`t number and doesn't start with @ + /// + /// Thrown when string value is null + public static implicit operator ChatId(string username) => new(username); + + /// + /// Create a string out of a + /// + /// The The ChatId + public static implicit operator string?(ChatId? chatId) => chatId?.ToString(); + + /// + /// Convert a Chat Object to a + /// + /// + [return: NotNullIfNotNull("chat")] + public static implicit operator ChatId?(Chat? chat) => chat is null ? null : new(chat.Id); + + /// + /// Compares two ChatId objects + /// + public static bool operator ==(ChatId? obj1, ChatId? obj2) { + if(obj1 is null || obj2 is null) { + return false; + } + + if(obj1.Identifier is not null && obj2.Identifier is not null) { + return obj1.Identifier == obj2.Identifier; + } + + if(obj1.Username is not null && obj2.Username is not null) { + return obj1.Username == obj2.Username; + } + + return false; + } + + /// + /// Compares two ChatId objects + /// + /// + /// + /// + public static bool operator !=(ChatId obj1, ChatId obj2) => !(obj1 == obj2); + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatInviteLink.cs b/TelegramBot/Types/ChatInviteLink.cs new file mode 100644 index 0000000..c6da6cd --- /dev/null +++ b/TelegramBot/Types/ChatInviteLink.cs @@ -0,0 +1,84 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Represents an invite link for a chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatInviteLink { + /// + /// The invite link. If the link was created by another chat administrator, then the second part of the + /// link will be replaced with “…”. + /// + [JsonProperty(Required = Required.Always)] + public string InviteLink { get; set; } = default!; + + /// + /// Creator of the link + /// + [JsonProperty(Required = Required.Always)] + public User Creator { get; set; } = default!; + + /// + /// true, if users joining the chat via the link need to be approved by chat administrators + /// + [JsonProperty(Required = Required.Always)] + public bool CreatesJoinRequest { + get; set; + } + + /// + /// true, if the link is primary + /// + [JsonProperty(Required = Required.Always)] + public bool IsPrimary { + get; set; + } + + /// + /// true, if the link is revoked + /// + [JsonProperty(Required = Required.Always)] + public bool IsRevoked { + get; set; + } + + /// + /// Optional. Invite link name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Name { + get; set; + } + + /// + /// Optional. Point in time when the link will expire or has been expired + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? ExpireDate { + get; set; + } + + /// + /// Optional. Maximum number of users that can be members of the chat simultaneously after joining the chat + /// via this invite link; 1-99999 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MemberLimit { + get; set; + } + + /// + /// Optional. Number of pending join requests created using this link + /// + public int? PendingJoinRequestCount { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatJoinRequest.cs b/TelegramBot/Types/ChatJoinRequest.cs new file mode 100644 index 0000000..896124b --- /dev/null +++ b/TelegramBot/Types/ChatJoinRequest.cs @@ -0,0 +1,51 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Represents a join request sent to a chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatJoinRequest { + /// + /// Chat to which the request was sent + /// + [JsonProperty(Required = Required.Always)] + public Chat Chat { get; set; } = default!; + + /// + /// User that sent the join request + /// + [JsonProperty(Required = Required.Always)] + public User From { get; set; } = default!; + + /// + /// Date the request was sent + /// + [JsonProperty(Required = Required.Always)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime Date { + get; set; + } + + /// + /// Optional. Bio of the user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Bio { + get; set; + } + + /// + /// Optional. Chat invite link that was used by the user to send the join request + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatInviteLink? InviteLink { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatLocation.cs b/TelegramBot/Types/ChatLocation.cs new file mode 100644 index 0000000..a8d09b9 --- /dev/null +++ b/TelegramBot/Types/ChatLocation.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Represents a location to which a chat is connected. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatLocation { + /// + /// The location to which the supergroup is connected. Can't be a live location. + /// + [JsonProperty(Required = Required.Always)] + public Location Location { get; set; } = default!; + + /// + /// Location address; 1-64 characters, as defined by the chat owner + /// + [JsonProperty(Required = Required.Always)] + public string Address { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatMember.cs b/TelegramBot/Types/ChatMember.cs new file mode 100644 index 0000000..d5823fe --- /dev/null +++ b/TelegramBot/Types/ChatMember.cs @@ -0,0 +1,311 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Converters; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types { + + + /// + /// This object contains information about one member of the chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + [JsonConverter(typeof(ChatMemberConverter))] + public abstract class ChatMember { + /// + /// The member's status in the chat. + /// + [JsonProperty] + public abstract ChatMemberStatus Status { + get; + } + + /// + /// Information about the user + /// + [JsonProperty(Required = Required.Always)] + public User User { get; set; } = default!; + } + + /// + /// Represents a that owns the chat and has all administrator privileges + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatMemberOwner : ChatMember { + /// + public override ChatMemberStatus Status => ChatMemberStatus.Creator; + + /// + /// Custom title for this user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? CustomTitle { + get; set; + } + + /// + /// True, if the user's presence in the chat is hidden + /// + [JsonProperty(Required = Required.Always)] + public bool IsAnonymous { + get; set; + } + } + + /// + /// Represents a that has some additional privileges + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatMemberAdministrator : ChatMember { + /// + public override ChatMemberStatus Status => ChatMemberStatus.Administrator; + + /// + /// true, if the bot is allowed to edit administrator privileges of that user + /// + [JsonProperty(Required = Required.Always)] + public bool CanBeEdited { + get; set; + } + + /// + /// Custom title for this user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? CustomTitle { + get; set; + } + + /// + /// true, if the user's presence in the chat is hidden + /// + [JsonProperty(Required = Required.Always)] + public bool IsAnonymous { + get; set; + } + + /// + /// 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 + /// + [JsonProperty(Required = Required.Always)] + public bool CanManageChat { + get; set; + } + + /// + /// true, if the administrator can post in the channel, channels only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanPostMessages { + get; set; + } + + /// + /// true, if the administrator can edit messages of other users, channels only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanEditMessages { + get; set; + } + + /// + /// true, if the administrator can delete messages of other users + /// + [JsonProperty(Required = Required.Always)] + public bool CanDeleteMessages { + get; set; + } + + /// + /// true, if the administrator can manage video chats + /// + [Obsolete("This property will be removed in the next major version, use CanManageVideoChat instead")] + [JsonProperty(Required = Required.Always)] + public bool CanManageVoiceChats { + get; set; + } + + /// + /// true, if the administrator can manage video chats + /// + [JsonProperty(Required = Required.Always)] + public bool CanManageVideoChats { + get; set; + } + + /// + /// true, if the administrator can restrict, ban or unban chat members + /// + [JsonProperty(Required = Required.Always)] + public bool CanRestrictMembers { + get; set; + } + + /// + /// true, if the administrator can add new administrators with a subset of his own privileges or + /// demote administrators that he has promoted, directly or indirectly (promoted by administrators that + /// were appointed by the user) + /// + [JsonProperty(Required = Required.Always)] + public bool CanPromoteMembers { + get; set; + } + + /// + /// true, if the administrator can change the chat title, photo and other settings + /// + [JsonProperty(Required = Required.Always)] + public bool CanChangeInfo { + get; set; + } + + /// + /// true, if the administrator can invite new users to the chat + /// + [JsonProperty(Required = Required.Always)] + public bool CanInviteUsers { + get; set; + } + + /// + /// true, if the administrator can pin messages, supergroups only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanPinMessages { + get; set; + } + } + + /// + /// Represents a that has no additional privileges or restrictions. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatMemberMember : ChatMember { + /// + public override ChatMemberStatus Status => ChatMemberStatus.Member; + } + + /// + /// Represents a that is under certain restrictions in the chat. Supergroups only. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatMemberRestricted : ChatMember { + /// + public override ChatMemberStatus Status => ChatMemberStatus.Restricted; + + /// + /// true, if the user is a member of the chat at the moment of the request + /// + [JsonProperty(Required = Required.Always)] + public bool IsMember { + get; set; + } + + /// + /// true, if the user can change the chat title, photo and other settings + /// + [JsonProperty(Required = Required.Always)] + public bool CanChangeInfo { + get; set; + } + + /// + /// true, if the user can invite new users to the chat + /// + [JsonProperty(Required = Required.Always)] + public bool CanInviteUsers { + get; set; + } + + /// + /// true, if the user can pin messages, supergroups only + /// + [JsonProperty(Required = Required.Always)] + public bool CanPinMessages { + get; set; + } + + /// + /// true, if the user can send text messages, contacts, locations and venues + /// + [JsonProperty(Required = Required.Always)] + public bool CanSendMessages { + get; set; + } + + /// + /// true, if the user can send audios, documents, photos, videos, video notes and voice notes, + /// implies + /// + [JsonProperty(Required = Required.Always)] + public bool CanSendMediaMessages { + get; set; + } + + /// + /// true, if the user is allowed to send polls + /// + [JsonProperty(Required = Required.Always)] + public bool CanSendPolls { + get; set; + } + + /// + /// true, if the user can send animations, games, stickers and use inline bots, + /// implies + /// + [JsonProperty(Required = Required.Always)] + public bool CanSendOtherMessages { + get; set; + } + + /// + /// true, if user may add web page previews to his messages, + /// implies + /// + [JsonProperty(Required = Required.Always)] + public bool CanAddWebPagePreviews { + get; set; + } + + /// + /// Date when restrictions will be lifted for this user, UTC time + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonConverter(typeof(BanTimeUnixDateTimeConverter))] + public DateTime? UntilDate { + get; set; + } + } + + /// + /// Represents a that isn't currently a member of the chat, but may join it themselves + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatMemberLeft : ChatMember { + /// + public override ChatMemberStatus Status => ChatMemberStatus.Left; + } + + /// + /// Represents a that was banned in the chat and can't return to the chat + /// or view chat messages + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatMemberBanned : ChatMember { + /// + public override ChatMemberStatus Status => ChatMemberStatus.Kicked; + + /// + /// Date when restrictions will be lifted for this user, UTC time + /// + [JsonConverter(typeof(BanTimeUnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? UntilDate { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatMemberUpdated.cs b/TelegramBot/Types/ChatMemberUpdated.cs new file mode 100644 index 0000000..23b6bc6 --- /dev/null +++ b/TelegramBot/Types/ChatMemberUpdated.cs @@ -0,0 +1,56 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents changes in the status of a chat member. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatMemberUpdated { + /// + /// Chat the user belongs to + /// + [JsonProperty(Required = Required.Always)] + public Chat Chat { get; set; } = default!; + + /// + /// Performer of the action, which resulted in the change + /// + [JsonProperty(Required = Required.Always)] + public User From { get; set; } = default!; + + /// + /// Date the change was done + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(Required = Required.Always)] + public DateTime Date { + get; set; + } + + /// + /// Previous information about the chat member + /// + [JsonProperty(Required = Required.Always)] + public ChatMember OldChatMember { get; set; } = default!; + + /// + /// New information about the chat member + /// + [JsonProperty(Required = Required.Always)] + public ChatMember NewChatMember { get; set; } = default!; + + /// + /// Optional. Chat invite link, which was used by the user to join the chat; for joining by invite link + /// events only. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatInviteLink? InviteLink { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatPermissions.cs b/TelegramBot/Types/ChatPermissions.cs new file mode 100644 index 0000000..84fb0c0 --- /dev/null +++ b/TelegramBot/Types/ChatPermissions.cs @@ -0,0 +1,76 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Describes actions that a non-administrator user is allowed to take in a chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatPermissions { + /// + /// Optional. True, if the user is allowed to send text messages, contacts, locations and venues + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanSendMessages { + get; set; + } + + /// + /// Optional. True, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes, implies + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanSendMediaMessages { + get; set; + } + + /// + /// Optional. True, if the user is allowed to send polls, implies + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanSendPolls { + get; set; + } + + /// + /// Optional. True, if the user is allowed to send animations, games, stickers and use inline bots, implies + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanSendOtherMessages { + get; set; + } + + /// + /// Optional. True, if the user is allowed to add web page previews to their messages, implies + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanAddWebPagePreviews { + get; set; + } + + /// + /// Optional. True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanChangeInfo { + get; set; + } + + /// + /// Optional. True, if the user is allowed to invite new users to the chat + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanInviteUsers { + get; set; + } + + /// + /// Optional. True, if the user is allowed to pin messages. Ignored in public supergroups + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanPinMessages { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChatPhoto.cs b/TelegramBot/Types/ChatPhoto.cs new file mode 100644 index 0000000..4fd7cb6 --- /dev/null +++ b/TelegramBot/Types/ChatPhoto.cs @@ -0,0 +1,40 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Collection of fileIds of profile pictures of a chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChatPhoto { + /// + /// File identifier of small (160x160) chat photo. This FileId can be used only for photo download and only + /// for as long as the photo is not changed. + /// + [JsonProperty(Required = Required.Always)] + public string SmallFileId { get; set; } = default!; + + /// + /// Unique file identifier of small (160x160) chat photo, which is supposed to be the same over time and for + /// different bots. Can't be used to download or reuse the file. + /// + [JsonProperty(Required = Required.Always)] + public string SmallFileUniqueId { get; set; } = default!; + + /// + /// File identifier of big (640x640) chat photo. This FileId can be used only for photo download and only for + /// as long as the photo is not changed. + /// + [JsonProperty(Required = Required.Always)] + public string BigFileId { get; set; } = default!; + + /// + /// Unique file identifier of big (640x640) chat photo, which is supposed to be the same over time and for + /// different bots. Can't be used to download or reuse the file. + /// + [JsonProperty(Required = Required.Always)] + public string BigFileUniqueId { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ChosenInlineResult.cs b/TelegramBot/Types/ChosenInlineResult.cs new file mode 100644 index 0000000..8479eaf --- /dev/null +++ b/TelegramBot/Types/ChosenInlineResult.cs @@ -0,0 +1,48 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a result of an that was chosen by the + /// and sent to their chat partner. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ChosenInlineResult { + /// + /// The unique identifier for the result that was chosen. + /// + [JsonProperty(Required = Required.Always)] + public string ResultId { get; set; } = default!; + + /// + /// The user that chose the result. + /// + [JsonProperty(Required = Required.Always)] + public User From { get; set; } = default!; + + /// + /// Optional. Sender location, only for bots that require user location + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Location? Location { + get; set; + } + + /// + /// Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached + /// to the message. Will be also received in callback queries and can be used to edit the message. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? InlineMessageId { + get; set; + } + + /// + /// The query that was used to obtain the result. + /// + [JsonProperty(Required = Required.Always)] + public string Query { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Contact.cs b/TelegramBot/Types/Contact.cs new file mode 100644 index 0000000..5a56229 --- /dev/null +++ b/TelegramBot/Types/Contact.cs @@ -0,0 +1,48 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a phone contact. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Contact { + /// + /// Contact's phone number + /// + [JsonProperty(Required = Required.Always)] + public string PhoneNumber { get; set; } = default!; + + /// + /// Contact's first name + /// + [JsonProperty(Required = Required.Always)] + public string FirstName { get; set; } = default!; + + /// + /// Optional. Contact's last name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LastName { + get; set; + } + + /// + /// Optional. Contact's user identifier in Telegram + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public long? UserId { + get; set; + } + + /// + /// Optional. Additional data about the contact in the form of a vCard + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Vcard { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Dice.cs b/TelegramBot/Types/Dice.cs new file mode 100644 index 0000000..403380c --- /dev/null +++ b/TelegramBot/Types/Dice.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using static Telegram.Bot.Types.Enums.Emoji; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a dice with random value + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Dice { + /// + /// Emoji on which the dice throw animation is based + /// + [JsonProperty(Required = Required.Always)] + public string Emoji { get; set; } = default!; + /// + /// Value of the dice, 1-6 for (“🎲”), + /// (“🎯”) and ("🎳"), 1-5 for (“🏀”) and + /// ("⚽"), and values 1-64 for ("🎰"). Defaults to + /// (“🎲”) + /// + [JsonProperty(Required = Required.Always)] + public int Value { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Document.cs b/TelegramBot/Types/Document.cs new file mode 100644 index 0000000..256eafb --- /dev/null +++ b/TelegramBot/Types/Document.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a general file (as opposed to photos, voice messages and audio files). + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Document : FileBase { + /// + /// Optional. Document thumbnail as defined by sender + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public PhotoSize? Thumb { + get; set; + } + + /// + /// Optional. Original filename as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FileName { + get; set; + } + + /// + /// Optional. MIME type of the file as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? MimeType { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/BotCommandScopeType.cs b/TelegramBot/Types/Enums/BotCommandScopeType.cs new file mode 100644 index 0000000..fddb3b5 --- /dev/null +++ b/TelegramBot/Types/Enums/BotCommandScopeType.cs @@ -0,0 +1,50 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// Scope type + /// + [JsonConverter(typeof(BotCommandScopeTypeConverter))] + public enum BotCommandScopeType { + /// + /// Represents the default of bot commands. Default commands are used if no + /// commands with a narrower are specified for the user. + /// + Default = 1, + + /// + /// Represents the of bot commands, covering all private chats. + /// + AllPrivateChats, + + /// + /// Represents the of bot commands, covering all group and supergroup chats. + /// + AllGroupChats, + + /// + /// Represents the of bot commands, covering all group and supergroup + /// chat administrators. + /// + AllChatAdministrators, + + /// + /// Represents the of bot commands, covering a specific . + /// + Chat, + + /// + /// Represents the of bot commands, covering all administrators of + /// a specific group or supergroup . + /// + ChatAdministrators, + + /// + /// Represents the of bot commands, covering a specific member of + /// a group or supergroup . + /// + ChatMember + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/ChatAction.cs b/TelegramBot/Types/Enums/ChatAction.cs new file mode 100644 index 0000000..92b95a0 --- /dev/null +++ b/TelegramBot/Types/Enums/ChatAction.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// Type of action to broadcast + /// + [JsonConverter(typeof(ChatActionConverter))] + public enum ChatAction { + /// + /// Typing + /// + Typing = 1, + + /// + /// Uploading a + /// + UploadPhoto, + + /// + /// Recording a + /// + RecordVideo, + + /// + /// Uploading a + /// + UploadVideo, + + /// + /// Recording a + /// + RecordVoice, + + /// + /// Uploading a + /// + UploadVoice, + + /// + /// Uploading a + /// + UploadDocument, + + /// + /// Finding a + /// + FindLocation, + + /// + /// Recording a + /// + RecordVideoNote, + + /// + /// Uploading a + /// + UploadVideoNote, + + /// + /// Choosing a + /// + ChooseSticker, + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/ChatMemberStatus.cs b/TelegramBot/Types/Enums/ChatMemberStatus.cs new file mode 100644 index 0000000..4549394 --- /dev/null +++ b/TelegramBot/Types/Enums/ChatMemberStatus.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// ChatMember status + /// + [JsonConverter(typeof(ChatMemberStatusConverter))] + public enum ChatMemberStatus { + /// + /// Creator of the + /// + Creator = 1, + + /// + /// Administrator of the + /// + Administrator, + + /// + /// Normal member of the + /// + Member, + + /// + /// A who left the + /// + Left, + + /// + /// A who was kicked from the + /// + Kicked, + + /// + /// A who is restricted in the + /// + Restricted + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/ChatType.cs b/TelegramBot/Types/Enums/ChatType.cs new file mode 100644 index 0000000..70072bb --- /dev/null +++ b/TelegramBot/Types/Enums/ChatType.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// Type of the , from which the inline query was sent + /// + [JsonConverter(typeof(ChatTypeConverter))] + public enum ChatType { + /// + /// Normal one to one + /// + Private = 1, + + /// + /// Normal group chat + /// + Group, + + /// + /// A channel + /// + Channel, + + /// + /// A supergroup + /// + Supergroup, + + /// + /// “sender” for a private chat with the inline query sender + /// + Sender + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/Emoji.cs b/TelegramBot/Types/Enums/Emoji.cs new file mode 100644 index 0000000..e41a5f8 --- /dev/null +++ b/TelegramBot/Types/Enums/Emoji.cs @@ -0,0 +1,52 @@ +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// Emoji on which the dice throw animation is based + /// + /// This enum is used only in the library APIs and is not present in types that are coming from + /// Telegram servers for compatibility reasons + /// + /// + [JsonConverter(typeof(EmojiConverter))] + public enum Emoji { + /// + /// Dice. Resulting value is 1-6 + /// + [Display(Name = "🎲")] + Dice = 1, + + /// + /// Darts. Resulting value is 1-6 + /// + [Display(Name = "🎯")] + Darts, + + /// + /// Basketball. Resulting value is 1-5 + /// + [Display(Name = "🏀")] + Basketball, + + /// + /// Football. Resulting value is 1-5 + /// + [Display(Name = "⚽")] + Football, + + /// + /// Slot machine. Resulting value is 1-64 + /// + [Display(Name = "🎰")] + SlotMachine, + + /// + /// Bowling. Result value is 1-6 + /// + [Display(Name = "🎳")] + Bowling + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/FileType.cs b/TelegramBot/Types/Enums/FileType.cs new file mode 100644 index 0000000..89af998 --- /dev/null +++ b/TelegramBot/Types/Enums/FileType.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// Type of a + /// + [JsonConverter(typeof(FileTypeConverter))] + public enum FileType { + /// + /// FileStream + /// + Stream = 1, + + /// + /// FileId + /// + Id, + + /// + /// File Url + /// + Url + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/InputMediaType.cs b/TelegramBot/Types/Enums/InputMediaType.cs new file mode 100644 index 0000000..8f044d5 --- /dev/null +++ b/TelegramBot/Types/Enums/InputMediaType.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// Type of the input media + /// + [JsonConverter(typeof(InputMediaTypeConverter))] + public enum InputMediaType { + /// + /// Photo + /// + Photo = 1, + + /// + /// Video + /// + Video, + + /// + /// Animation + /// + Animation, + + /// + /// Audio + /// + Audio, + + /// + /// Document + /// + Document + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/MaskPositionPoint.cs b/TelegramBot/Types/Enums/MaskPositionPoint.cs new file mode 100644 index 0000000..08003f9 --- /dev/null +++ b/TelegramBot/Types/Enums/MaskPositionPoint.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// The part of the face relative to which the mask should be placed. + /// + [JsonConverter(typeof(MaskPositionPointConverter))] + public enum MaskPositionPoint { + /// + /// The forehead + /// + Forehead = 1, + + /// + /// The eyes + /// + Eyes, + + /// + /// The mouth + /// + Mouth, + + /// + /// The chin + /// + Chin + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/MenuButtonType.cs b/TelegramBot/Types/Enums/MenuButtonType.cs new file mode 100644 index 0000000..5e57543 --- /dev/null +++ b/TelegramBot/Types/Enums/MenuButtonType.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// Type of the + /// + [JsonConverter(typeof(MenuButtonTypeConverter))] + public enum MenuButtonType { + /// + /// Describes that no specific value for the menu button was set. + /// + Default = 1, + + /// + /// Represents a menu button, which opens the bot’s list of commands. + /// + Commands, + + /// + /// Represents a menu button, which launches a Web App. + /// + WebApp + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/MessageEntityType.cs b/TelegramBot/Types/Enums/MessageEntityType.cs new file mode 100644 index 0000000..819f2b5 --- /dev/null +++ b/TelegramBot/Types/Enums/MessageEntityType.cs @@ -0,0 +1,91 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// Type of a + /// + [JsonConverter(typeof(MessageEntityTypeConverter))] + public enum MessageEntityType { + /// + /// A mentioned + /// + Mention = 1, + + /// + /// A searchable Hashtag + /// + Hashtag, + + /// + /// A Bot command + /// + BotCommand, + + /// + /// An url + /// + Url, + + /// + /// An email + /// + Email, + + /// + /// Bold text + /// + Bold, + + /// + /// Italic text + /// + Italic, + + /// + /// Monowidth string + /// + Code, + + /// + /// Monowidth block + /// + Pre, + + /// + /// Clickable text urls + /// + TextLink, + + /// + /// Mentions for a without + /// + TextMention, + + /// + /// Phone number + /// + PhoneNumber, + + /// + /// A cashtag (e.g. $EUR, $USD) - $ followed by the short currency code + /// + Cashtag, + + /// + /// Underlined text + /// + Underline, + + /// + /// Strikethrough text + /// + Strikethrough, + + /// + /// Spoiler message + /// + Spoiler, + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/MessageType.cs b/TelegramBot/Types/Enums/MessageType.cs new file mode 100644 index 0000000..3bddffb --- /dev/null +++ b/TelegramBot/Types/Enums/MessageType.cs @@ -0,0 +1,191 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// The type of a + /// + [JsonConverter(typeof(MessageTypeConverter))] + public enum MessageType { + /// + /// The is unknown + /// + Unknown = 0, + + /// + /// The contains text + /// + Text, + + /// + /// The contains a + /// + Photo, + + /// + /// The contains an + /// + Audio, + + /// + /// The contains a + /// + Video, + + /// + /// The contains a + /// + Voice, + + /// + /// The contains a + /// + Document, + + /// + /// The contains a + /// + Sticker, + + /// + /// The contains a + /// + Location, + + /// + /// The contains a + /// + Contact, + + /// + /// The contains a + /// + Venue, + + /// + /// The contains a + /// + Game, + + /// + /// The contains a + /// + VideoNote, + + /// + /// The contains a + /// + Invoice, + + /// + /// The contains a + /// + SuccessfulPayment, + + /// + /// The contains a + /// + WebsiteConnected, + + /// + /// The contains a + /// + ChatMembersAdded, + + /// + /// The contains a + /// + ChatMemberLeft, + + /// + /// The contains a + /// + ChatTitleChanged, + + /// + /// The contains a + /// + ChatPhotoChanged, + + /// + /// The contains a + /// + MessagePinned, + + /// + /// The contains a + /// + ChatPhotoDeleted, + + /// + /// The contains a + /// + GroupCreated, + + /// + /// The contains a + /// + SupergroupCreated, + + /// + /// The contains a + /// + ChannelCreated, + + /// + /// The contains non-default + /// + MigratedToSupergroup, + + /// + /// The contains non-default + /// + MigratedFromGroup, + + /// + /// The contains + /// + Poll, + + /// + /// The contains + /// + Dice, + + /// + /// The contains + /// + MessageAutoDeleteTimerChanged, + + /// + /// The contains + /// + ProximityAlertTriggered, + + /// + /// The contains + /// + WebAppData, + + /// + /// The contains + /// + VideoChatScheduled, + + /// + /// The contains + /// + VideoChatStarted, + + /// + /// The contains + /// + VideoChatEnded, + + /// + /// The contains + /// + VideoChatParticipantsInvited, + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/ParseMode.cs b/TelegramBot/Types/Enums/ParseMode.cs new file mode 100644 index 0000000..bb9200e --- /dev/null +++ b/TelegramBot/Types/Enums/ParseMode.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// + /// Text parsing mode + /// + /// + /// The Bot API supports basic formatting for messages. You can use bold and italic text, as well as inline + /// links and pre-formatted code in your bots' messages. Telegram clients will render them accordingly. + /// You can use either markdown-style or HTML-style formatting. + /// + /// + /// + [JsonConverter(typeof(ParseModeConverter))] + public enum ParseMode { + /// + /// Markdown-formatted A + /// + /// + /// This is a legacy mode, retained for backward compatibility + /// + [Display(Name = "Markdown")] + Markdown = 1, + + /// + /// HTML-formatted + /// + [Display(Name = "Html")] + Html, + + /// + /// MarkdownV2-formatted + /// + [Display(Name = "MarkdownV2")] + MarkdownV2, + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/PollType.cs b/TelegramBot/Types/Enums/PollType.cs new file mode 100644 index 0000000..19498c6 --- /dev/null +++ b/TelegramBot/Types/Enums/PollType.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// type + /// + /// This enum is used only in the library APIs and is not present in types that are coming from + /// Telegram servers for compatibility reasons + /// + /// + [JsonConverter(typeof(PollTypeConverter))] + public enum PollType { + /// + /// Regular poll + /// + Regular = 1, + + /// + /// Quiz + /// + Quiz + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Enums/UpdateType.cs b/TelegramBot/Types/Enums/UpdateType.cs new file mode 100644 index 0000000..5de70f0 --- /dev/null +++ b/TelegramBot/Types/Enums/UpdateType.cs @@ -0,0 +1,86 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Enums { + + + /// + /// The type of an + /// + [JsonConverter(typeof(UpdateTypeConverter))] + public enum UpdateType { + /// + /// Update Type is unknown + /// + Unknown = 0, + + /// + /// The contains a . + /// + Message, + + /// + /// The contains an . + /// + InlineQuery, + + /// + /// The contains a . + /// + ChosenInlineResult, + + /// + /// The contains a + /// + CallbackQuery, + + /// + /// The contains an edited + /// + EditedMessage, + + /// + /// The contains a channel post + /// + ChannelPost, + + /// + /// The contains an edited channel post + /// + EditedChannelPost, + + /// + /// The contains an + /// + ShippingQuery, + + /// + /// The contains an + /// + PreCheckoutQuery, + + /// + /// The contains an + /// + Poll, + + /// + /// The contains an + /// + PollAnswer, + + /// + /// The contains an + /// + MyChatMember, + + /// + /// The contains an + /// + ChatMember, + + /// + /// The contains an + /// + ChatJoinRequest, + } +} \ No newline at end of file diff --git a/TelegramBot/Types/File.cs b/TelegramBot/Types/File.cs new file mode 100644 index 0000000..60e9452 --- /dev/null +++ b/TelegramBot/Types/File.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a file ready to be downloaded. The file can be downloaded via . 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 . + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class File : FileBase { + /// + /// Optional. File path. Use to get the file. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FilePath { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/FileBase.cs b/TelegramBot/Types/FileBase.cs new file mode 100644 index 0000000..d278e77 --- /dev/null +++ b/TelegramBot/Types/FileBase.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a file ready to be downloaded. The file can be downloaded via + /// . 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 + /// . + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class FileBase { + /// + /// Identifier for this file, which can be used to download or reuse the file + /// + [JsonProperty(Required = Required.Always)] + public string FileId { get; set; } = default!; + + /// + /// Unique identifier for this file, which is supposed to be the same over time and for different bots. + /// Can't be used to download or reuse the file. + /// + [JsonProperty(Required = Required.Always)] + public string FileUniqueId { get; set; } = default!; + + /// + /// Optional. File size + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public long? FileSize { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Game.cs b/TelegramBot/Types/Game.cs new file mode 100644 index 0000000..b8a43c5 --- /dev/null +++ b/TelegramBot/Types/Game.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +namespace Telegram.Bot.Types { + + + /// + /// This object represents a game. Use BotFather to create and edit games, their short names will act as unique + /// identifiers. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Game { + /// + /// Title of the game. + /// + [JsonProperty(Required = Required.Always)] + public string Title { get; set; } = default!; + + /// + /// Description of the game. + /// + [JsonProperty(Required = Required.Always)] + public string Description { get; set; } = default!; + + /// + /// Photo that will be displayed in the game message in chats. + /// + [JsonProperty(Required = Required.Always)] + public PhotoSize[] Photo { get; set; } = default!; + + /// + /// Optional. Brief description of the game or high scores included in the game message. Can be automatically + /// edited to include current high scores for the game when the bot calls + /// , or manually edited using + /// . 0-4096 characters. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Text { + get; set; + } + + /// + /// Optional. Special entities that appear in text, such as usernames, URLs, bot commands, etc. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? TextEntities { + get; set; + } + + /// + /// Optional. Animation that will be displayed in the game message in chats. Upload via + /// @BotFather + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Animation? Animation { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/GameHighScore.cs b/TelegramBot/Types/GameHighScore.cs new file mode 100644 index 0000000..6483ff8 --- /dev/null +++ b/TelegramBot/Types/GameHighScore.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents one row of the high scores table for a game. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class GameHighScore { + /// + /// Position in high score table for the game. + /// + [JsonProperty(Required = Required.Always)] + public int Position { + get; set; + } + + /// + /// User + /// + [JsonProperty(Required = Required.Always)] + public User User { get; set; } = default!; + + /// + /// Score + /// + [JsonProperty(Required = Required.Always)] + public int Score { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQuery.cs b/TelegramBot/Types/InlineQuery.cs new file mode 100644 index 0000000..48d95b2 --- /dev/null +++ b/TelegramBot/Types/InlineQuery.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; +using static Telegram.Bot.Types.Enums.ChatType; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents an incoming inline query. When the user sends an empty query, your bot could return + /// some default or trending results. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQuery { + /// + /// Unique identifier for this query + /// + [JsonProperty(Required = Required.Always)] + public string Id { get; set; } = default!; + + /// + /// Sender + /// + [JsonProperty(Required = Required.Always)] + public User From { get; set; } = default!; + + /// + /// Text of the query (up to 256 characters) + /// + [JsonProperty(Required = Required.Always)] + public string Query { get; set; } = default!; + + /// + /// Offset of the results to be returned, can be controlled by the bot + /// + [JsonProperty(Required = Required.Always)] + public string Offset { get; set; } = default!; + + /// + /// Optional. Type of the chat, from which the inline query was sent. Can be either for + /// a private chat with the inline query sender, , , + /// , or . The chat type should be always known for requests + /// sent from official clients and most third-party clients, unless the request was sent from a secret chat + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatType? ChatType { + get; set; + } + + /// + /// Optional. Sender location, only for bots that request user location + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Location? Location { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/Documentation.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/Documentation.cs new file mode 100644 index 0000000..2786f25 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/Documentation.cs @@ -0,0 +1,59 @@ +#nullable disable +#pragma warning disable 169 +#pragma warning disable CA1823 +// ReSharper disable InconsistentNaming +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + // ReSharper disable once UnusedType.Global + internal static class Documentation { + /// + /// Content of the message to be sent instead of the result + /// + static object InputMessageContent; + + /// + /// Caption of the result to be sent, 0-1024 characters after entities parsing + /// + static object Caption; + + /// + /// Mode for parsing entities in the result caption. See + /// formatting options + /// for more details. + /// + static object ParseMode; + + /// + /// List of special entities that appear in the caption, which can be specified + /// instead of + /// + static object CaptionEntities; + + /// + /// Location latitude in degrees + /// + static object Latitude; + + /// + /// Location longitude in degrees + /// + static object Longitude; + + /// + /// Thumbnail width + /// + static object ThumbWidth; + + /// + /// Thumbnail height + /// + static object ThumbHeight; + + /// + /// Url of the thumbnail for the result + /// + static object ThumbUrl; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResult.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResult.cs new file mode 100644 index 0000000..441ce10 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResult.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.ReplyMarkups; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Base Class for inline results send in response to an + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class InlineQueryResult { + /// + /// Type of the result + /// + [JsonProperty(Required = Required.Always)] + public abstract InlineQueryResultType Type { + get; + } + + /// + /// Unique identifier for this result, 1-64 Bytes + /// + [JsonProperty(Required = Required.Always)] + public string Id { + get; + } + + /// + /// Optional. Inline keyboard attached to the message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier for this result, 1-64 Bytes + protected InlineQueryResult(string id) => Id = id; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultArticle.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultArticle.cs new file mode 100644 index 0000000..13390f8 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultArticle.cs @@ -0,0 +1,89 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to an article or web page. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultArticle : InlineQueryResult { + /// + /// Type of the result, must be article + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Article; + + /// + /// Title of the result + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Content of the message to be sent + /// + [JsonProperty(Required = Required.Always)] + public InputMessageContent InputMessageContent { + get; + } + + /// + /// Optional. URL of the result. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Url { + get; set; + } + + /// + /// Optional. Pass true, if you don't want the URL to be shown in the message. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? HideUrl { + get; set; + } + + /// + /// Optional. Short description of the result. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ThumbUrl { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbWidth { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbHeight { + get; set; + } + + /// + /// Initializes a new object + /// + /// Unique identifier of this result + /// Title of the result + /// Content of the message to be sent + public InlineQueryResultArticle(string id, string title, InputMessageContent inputMessageContent) + : base(id) { + Title = title; + InputMessageContent = inputMessageContent; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultAudio.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultAudio.cs new file mode 100644 index 0000000..8e3b649 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultAudio.cs @@ -0,0 +1,90 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to an MP3 audio file. By default, this audio file will be sent by the user. + /// Alternatively, you can use to send + /// a message with the specified content instead of the audio. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultAudio : InlineQueryResult { + /// + /// Type of the result, must be audio + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Audio; + + /// + /// A valid URL for the audio file + /// + [JsonProperty(Required = Required.Always)] + public string AudioUrl { + get; + } + + /// + /// Title + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + /// Optional. Performer + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Performer { + get; set; + } + + /// + /// Optional. Audio duration in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? AudioDuration { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid URL for the audio file + /// Title of the result + public InlineQueryResultAudio(string id, string audioUrl, string title) + : base(id) { + AudioUrl = audioUrl; + Title = title; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedAudio.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedAudio.cs new file mode 100644 index 0000000..c995142 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedAudio.cs @@ -0,0 +1,65 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to an MP3 audio file stored on the Telegram servers. By default, this audio + /// file will be sent by the user. Alternatively, you can use + /// to send a message with the + /// specified content instead of the audio. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultCachedAudio : InlineQueryResult { + /// + /// Type of the result, must be audio + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Audio; + + /// + /// A valid file identifier for the audio file + /// + [JsonProperty(Required = Required.Always)] + public string AudioFileId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid file identifier for the audio file + public InlineQueryResultCachedAudio(string id, string audioFileId) + : base(id) { + AudioFileId = audioFileId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedDocument.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedDocument.cs new file mode 100644 index 0000000..72f5427 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedDocument.cs @@ -0,0 +1,83 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a file stored on the Telegram servers. By default, this file will be sent + /// by the user with an optional caption. Alternatively, you can use + /// to send a message with the + /// specified content instead of the file. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultCachedDocument : InlineQueryResult { + /// + /// Type of the result, must be document + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Document; + + /// + /// Title for the result + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// A valid file identifier for the file + /// + [JsonProperty(Required = Required.Always)] + public string DocumentFileId { + get; + } + + /// + /// Optional. Short description of the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid file identifier for the file + /// Title of the result + public InlineQueryResultCachedDocument(string id, string documentFileId, string title) + : base(id) { + DocumentFileId = documentFileId; + Title = title; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedGif.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedGif.cs new file mode 100644 index 0000000..3a5b2e3 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedGif.cs @@ -0,0 +1,73 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to an animated GIF file stored on the Telegram servers. By default, this + /// animated GIF file will be sent by the user with an optional caption. Alternatively, you can + /// use to send a message with + /// specified content instead of the animation. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultCachedGif : InlineQueryResult { + /// + /// Type of the result, must be gif + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Gif; + + /// + /// A valid file identifier for the GIF file + /// + [JsonProperty(Required = Required.Always)] + public string GifFileId { + get; + } + + /// + /// Optional. Title for the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid file identifier for the GIF file + public InlineQueryResultCachedGif(string id, string gifFileId) + : base(id) { + GifFileId = gifFileId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedMpeg4Gif.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedMpeg4Gif.cs new file mode 100644 index 0000000..e3fb169 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedMpeg4Gif.cs @@ -0,0 +1,74 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the + /// Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an + /// optional caption. Alternatively, you can use + /// to send a message with + /// the specified content instead of the animation. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultCachedMpeg4Gif : InlineQueryResult { + /// + /// Type of the result, must be mpeg4_gif + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Mpeg4Gif; + + /// + /// A valid file identifier for the MP4 file + /// + [JsonProperty(Required = Required.Always)] + public string Mpeg4FileId { + get; + } + + /// + /// Optional. Title for the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid file identifier for the MP4 file + public InlineQueryResultCachedMpeg4Gif(string id, string mpeg4FileId) + : base(id) { + Mpeg4FileId = mpeg4FileId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedPhoto.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedPhoto.cs new file mode 100644 index 0000000..1784ebf --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedPhoto.cs @@ -0,0 +1,81 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent + /// by the user with an optional caption. Alternatively, you can use + /// to send a message with the + /// specified content instead of the photo. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultCachedPhoto : InlineQueryResult { + /// + /// Type of the result, must be photo + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Photo; + + /// + /// A valid file identifier of the photo + /// + [JsonProperty(Required = Required.Always)] + public string PhotoFileId { + get; + } + + /// + /// Optional. Title for the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + /// Optional. Short description of the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid file identifier of the photo + public InlineQueryResultCachedPhoto(string id, string photoFileId) + : base(id) { + PhotoFileId = photoFileId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedSticker.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedSticker.cs new file mode 100644 index 0000000..0a13c50 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedSticker.cs @@ -0,0 +1,46 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a sticker stored on the Telegram servers. By default, this sticker will + /// be sent by the user. Alternatively, you can use + /// to send a message with + /// the specified content instead of the sticker. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultCachedSticker : InlineQueryResult { + /// + /// Type of the result, must be sticker + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Sticker; + + /// + /// A valid file identifier of the sticker + /// + [JsonProperty(Required = Required.Always)] + public string StickerFileId { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid file identifier of the sticker + public InlineQueryResultCachedSticker(string id, string stickerFileId) + : base(id) { + StickerFileId = stickerFileId; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedVideo.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedVideo.cs new file mode 100644 index 0000000..e2e0880 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedVideo.cs @@ -0,0 +1,83 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a video file stored on the Telegram servers. By default, this video file will + /// be sent by the user with an optional caption. Alternatively, you can use + /// to send a message with + /// the specified content instead of the video. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultCachedVideo : InlineQueryResult { + /// + /// Type of the result, must be video + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Video; + + /// + /// A valid file identifier for the video file + /// + [JsonProperty(Required = Required.Always)] + public string VideoFileId { + get; + } + + /// + /// Title for the result + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Optional. Short description of the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid file identifier for the video file + /// Title of the result + public InlineQueryResultCachedVideo(string id, string videoFileId, string title) + : base(id) { + VideoFileId = videoFileId; + Title = title; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedVoice.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedVoice.cs new file mode 100644 index 0000000..6bdb891 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultCached/InlineQueryResultCachedVoice.cs @@ -0,0 +1,75 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a voice message stored on the Telegram servers. By default, this voice + /// message will be sent by the user. Alternatively, you can use + /// to send a message + /// with the specified content instead of the voice message. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultCachedVoice : InlineQueryResult { + /// + /// Type of the result, must be voice + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Voice; + + /// + /// A valid file identifier for the voice message + /// + [JsonProperty(Required = Required.Always)] + public string VoiceFileId { + get; + } + + /// + /// Voice message title + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid file identifier for the voice message + /// Title of the result + public InlineQueryResultCachedVoice(string id, string fileId, string title) + : base(id) { + VoiceFileId = fileId; + Title = title; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultContact.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultContact.cs new file mode 100644 index 0000000..fec7d3c --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultContact.cs @@ -0,0 +1,89 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a contact with a phone number. By default, this contact will be sent by the user. + /// Alternatively, you can use to send + /// a message with the specified content instead of the contact. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultContact : InlineQueryResult { + /// + /// Type of the result, must be contact + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Contact; + + /// + /// Contact's phone number + /// + [JsonProperty(Required = Required.Always)] + public string PhoneNumber { + get; + } + + /// + /// Contact's first name + /// + [JsonProperty(Required = Required.Always)] + public string FirstName { + get; + } + + /// + /// Optional. Contact's last name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LastName { + get; set; + } + + /// + /// Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Vcard { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ThumbUrl { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbWidth { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbHeight { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// Contact's phone number + /// Contact's first name + public InlineQueryResultContact(string id, string phoneNumber, string firstName) + : base(id) { + PhoneNumber = phoneNumber; + FirstName = firstName; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultDocument.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultDocument.cs new file mode 100644 index 0000000..66641e6 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultDocument.cs @@ -0,0 +1,113 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a file. By default, this file will be sent by the user with an optional caption. + /// Alternatively, you can use to send + /// a message with the specified content instead of the file. Currently, only .PDF and .ZIP files + /// can be sent using this method. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultDocument : InlineQueryResult { + /// + /// Type of the result, must be document + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Document; + + /// + /// Title for the result + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + /// A valid URL for the file + /// + [JsonProperty(Required = Required.Always)] + public string DocumentUrl { + get; + } + + /// + /// Mime type of the content of the file, either “application/pdf” or “application/zip” + /// + [JsonProperty(Required = Required.Always)] + public string MimeType { + get; + } + + /// + /// Optional. Short description of the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ThumbUrl { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbWidth { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbHeight { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid URL for the file + /// Title of the result + /// + /// Mime type of the content of the file, either “application/pdf” or “application/zip” + /// + public InlineQueryResultDocument(string id, string documentUrl, string title, string mimeType) + : base(id) { + DocumentUrl = documentUrl; + Title = title; + MimeType = mimeType; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultGame.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultGame.cs new file mode 100644 index 0000000..3f3eb33 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultGame.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a . + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultGame : InlineQueryResult { + /// + /// Type of the result, must be game + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Game; + + /// + /// Short name of the game + /// + [JsonProperty(Required = Required.Always)] + public string GameShortName { + get; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// Short name of the game + public InlineQueryResultGame(string id, string gameShortName) + : base(id) { + GameShortName = gameShortName; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultGif.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultGif.cs new file mode 100644 index 0000000..bbac60b --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultGif.cs @@ -0,0 +1,116 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the + /// user with optional caption. Alternatively, you can use + /// to send a message with the + /// specified content instead of the animation. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultGif : InlineQueryResult { + /// + /// Type of the result, must be gif + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Gif; + + /// + /// A valid URL for the GIF file. File size must not exceed 1MB + /// + [JsonProperty(Required = Required.Always)] + public string GifUrl { + get; + } + + /// + /// Optional. Width of the GIF. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? GifWidth { + get; set; + } + + /// + /// Optional. Height of the GIF. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? GifHeight { + get; set; + } + + /// + /// Optional. Duration of the GIF. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? GifDuration { + get; set; + } + + /// + /// URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result + /// + [JsonProperty(Required = Required.Always)] + public string ThumbUrl { + get; + } + + /// + /// Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, + /// or “video/mp4”. Defaults to “image/jpeg” + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ThumbMimeType { + get; set; + } + + /// + /// Optional. Title for the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// Width of the GIF + /// Url of the thumbnail for the result. + public InlineQueryResultGif(string id, string gifUrl, string thumbUrl) + : base(id) { + GifUrl = gifUrl; + ThumbUrl = thumbUrl; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultLocation.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultLocation.cs new file mode 100644 index 0000000..343834a --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultLocation.cs @@ -0,0 +1,113 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a location on a map. By default, the location will be sent by the user. Alternatively, + /// you can use to send a message with + /// the specified content instead of the location. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultLocation : InlineQueryResult { + /// + /// Type of the result, must be location + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Location; + + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; + } + + /// + /// Location title + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Optional. The radius of uncertainty for the location, measured in meters; 0-1500 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public float? HorizontalAccuracy { + get; set; + } + + /// + /// Optional. Period in seconds for which the location can be updated, should be between 60 and 86400. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? LivePeriod { + get; set; + } + + /// + /// Optional. For live locations, a direction in which the user is moving, in degrees. + /// Must be between 1 and 360 if specified. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Heading { + get; set; + } + + /// + /// Optional. For live locations, a maximum distance for proximity alerts about approaching + /// another chat member, in meters. Must be between 1 and 100000 if specified. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ProximityAlertRadius { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ThumbUrl { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbWidth { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbHeight { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// Latitude of the location in degrees + /// Longitude of the location in degrees + /// Title of the result + public InlineQueryResultLocation(string id, double latitude, double longitude, string title) + : base(id) { + Latitude = latitude; + Longitude = longitude; + Title = title; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultMpeg4Gif.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultMpeg4Gif.cs new file mode 100644 index 0000000..af4dc6a --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultMpeg4Gif.cs @@ -0,0 +1,116 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this + /// animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can use + /// to send a message with the specified + /// content instead of the animation. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultMpeg4Gif : InlineQueryResult { + /// + /// Type of the result, must be mpeg4_gif + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Mpeg4Gif; + + /// + /// A valid URL for the MP4 file. File size must not exceed 1MB + /// + [JsonProperty(Required = Required.Always)] + public string Mpeg4Url { + get; + } + + /// + /// Optional. Video width + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Mpeg4Width { + get; set; + } + + /// + /// Optional. Video height + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Mpeg4Height { + get; set; + } + + /// + /// Optional. Video duration + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Mpeg4Duration { + get; set; + } + + /// + /// URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result + /// + [JsonProperty(Required = Required.Always)] + public string ThumbUrl { + get; + } + + /// + /// Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, + /// or “video/mp4”. Defaults to “image/jpeg” + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ThumbMimeType { + get; set; + } + + /// + /// Optional. Title for the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid URL for the MP4 file. File size must not exceed 1MB. + /// Url of the thumbnail for the result. + public InlineQueryResultMpeg4Gif(string id, string mpeg4Url, string thumbUrl) + : base(id) { + Mpeg4Url = mpeg4Url; + ThumbUrl = thumbUrl; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultPhoto.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultPhoto.cs new file mode 100644 index 0000000..6c6cc0d --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultPhoto.cs @@ -0,0 +1,104 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a photo. By default, this photo will be sent by the user with optional caption. + /// Alternatively, you can use to send a message + /// with the specified content instead of the photo. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultPhoto : InlineQueryResult { + /// + /// Type of the result, must be photo + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Photo; + + /// + /// A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB + /// + [JsonProperty(Required = Required.Always)] + public string PhotoUrl { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public string ThumbUrl { + get; + } + + /// + /// Optional. Width of the photo + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? PhotoWidth { + get; set; + } + + /// + /// Optional. Height of the photo + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? PhotoHeight { + get; set; + } + + /// + /// Optional. Title for the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + /// Optional. Short description of the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query representing a link to a photo + /// + /// Unique identifier of this result + /// A valid URL of the photo. Photo size must not exceed 5MB. + /// Optional. Url of the thumbnail for the result. + public InlineQueryResultPhoto(string id, string photoUrl, string thumbUrl) + : base(id) { + PhotoUrl = photoUrl; + ThumbUrl = thumbUrl; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultType.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultType.cs new file mode 100644 index 0000000..acc4dec --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultType.cs @@ -0,0 +1,84 @@ +using Newtonsoft.Json; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Type of the InlineQueryResult + /// + [JsonConverter(typeof(InlineQueryResultTypeConverter))] + public enum InlineQueryResultType { + /// + /// + /// + Article = 1, + + /// + /// + /// + /// + Photo, + + /// + /// + /// + /// + Gif, + + /// + /// + /// + /// + Mpeg4Gif, + + /// + /// + /// /// + /// + Video, + + /// + /// + /// + /// + Audio, + + /// + /// + /// + Contact, + + /// + /// + /// /// + /// + Document, + + /// + /// + /// + Location, + + /// + /// + /// + Venue, + + /// + /// + /// + /// + Voice, + + /// + /// + /// + Game, + + /// + /// + /// + Sticker, + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVenue.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVenue.cs new file mode 100644 index 0000000..f303273 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVenue.cs @@ -0,0 +1,127 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use + /// to send a message with the specified + /// content instead of the venue. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultVenue : InlineQueryResult { + /// + /// Type of the result, must be venue + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Venue; + + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; + } + + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; + } + + /// + /// Title of the venue + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Address of the venue + /// + [JsonProperty(Required = Required.Always)] + public string Address { + get; + } + + /// + /// Optional. Foursquare identifier of the venue if known + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FoursquareId { + get; set; + } + + /// + /// Optional. Foursquare type of the venue. (For example, "arts_entertainment/default", + /// "arts_entertainment/aquarium" or "food/icecream".) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FoursquareType { + get; set; + } + + /// + /// Google Places identifier of the venue + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GooglePlaceId { + get; set; + } + + /// + /// Google Places type of the venue. + /// + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GooglePlaceType { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ThumbUrl { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbWidth { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ThumbHeight { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// Latitude of the location in degrees + /// Longitude of the location in degrees + /// Title of the result + /// Address of the venue + public InlineQueryResultVenue( + string id, + double latitude, + double longitude, + string title, + string address) : base(id) { + Latitude = latitude; + Longitude = longitude; + Title = title; + Address = address; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVideo.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVideo.cs new file mode 100644 index 0000000..98bc9a3 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVideo.cs @@ -0,0 +1,144 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a page containing an embedded video player or a video file. By default, this + /// video file will be sent by the user with an optional caption. Alternatively, you can use + /// to send a message with the specified + /// content instead of the video. + /// + /// + /// If an message contains an embedded video (e.g., YouTube), + /// you must replace its content using . + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultVideo : InlineQueryResult { + /// + /// Type of the result, must be video + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Video; + + /// + /// A valid URL for the embedded video player or video file + /// + [JsonProperty(Required = Required.Always)] + public string VideoUrl { + get; + } + + /// + /// Mime type of the content of video url, “text/html” or “video/mp4” + /// + [JsonProperty(Required = Required.Always)] + public string MimeType { + get; + } + + /// + /// URL of the thumbnail (jpeg only) for the video + /// + [JsonProperty(Required = Required.Always)] + public string ThumbUrl { + get; + } + + /// + /// Title for the result + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + /// Optional. Video width + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? VideoWidth { + get; set; + } + + /// + /// Optional. Video height + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? VideoHeight { + get; set; + } + + /// + /// Optional. Video duration in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? VideoDuration { + get; set; + } + + /// + /// Optional. Short description of the result + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Description { + get; set; + } + + /// + /// Optional. Content of the message to be sent instead of the video. This field is + /// required if is used to send an + /// HTML-page as a result (e.g., a YouTube video). + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid URL for the embedded video player + /// Url of the thumbnail for the result + /// Title of the result + /// + /// Content of the message to be sent instead of the video. This field is required if + /// is used to send an HTML-page as a result + /// (e.g., a YouTube video). + /// + public InlineQueryResultVideo( + string id, + string videoUrl, + string thumbUrl, + string title, + InputMessageContent? inputMessageContent = default) : base(id) { + VideoUrl = videoUrl; + MimeType = inputMessageContent is null ? "video/mp4" : "text/html"; + ThumbUrl = thumbUrl; + Title = title; + InputMessageContent = inputMessageContent; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVoice.cs b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVoice.cs new file mode 100644 index 0000000..19b137f --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InlineQueryResult/InlineQueryResultVoice.cs @@ -0,0 +1,83 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents a link to a voice recording in an .OGG container encoded with OPUS. By default, this + /// voice recording will be sent by the user. Alternatively, you can use + /// to send a message with the specified + /// content instead of the the voice message. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineQueryResultVoice : InlineQueryResult { + /// + /// Type of the result, must be voice + /// + [JsonProperty(Required = Required.Always)] + public override InlineQueryResultType Type => InlineQueryResultType.Voice; + + /// + /// A valid URL for the voice recording + /// + [JsonProperty(Required = Required.Always)] + public string VoiceUrl { + get; + } + + /// + /// Recording title + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + /// Optional. Recording duration in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? VoiceDuration { + get; set; + } + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMessageContent? InputMessageContent { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// Unique identifier of this result + /// A valid URL for the voice recording + /// Title of the result + public InlineQueryResultVoice(string id, string voiceUrl, string title) + : base(id) { + VoiceUrl = voiceUrl; + Title = title; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputContactMessageContent.cs b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputContactMessageContent.cs new file mode 100644 index 0000000..0cba9d6 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputContactMessageContent.cs @@ -0,0 +1,55 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents the content of a contact message to be sent as the result of an inline query. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputContactMessageContent : InputMessageContent { + /// + /// Contact's phone number + /// + [JsonProperty(Required = Required.Always)] + public string PhoneNumber { + get; + } + + /// + /// Contact's first name + /// + [JsonProperty(Required = Required.Always)] + public string FirstName { + get; + } + + /// + /// Optional. Contact's last name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LastName { + get; set; + } + + /// + /// Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Vcard { + get; set; + } + + /// + /// Initializes a new input contact message content + /// + /// The phone number of the contact + /// The first name of the contact + public InputContactMessageContent(string phoneNumber, string firstName) { + PhoneNumber = phoneNumber; + FirstName = firstName; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputInvoiceMessageContent.cs b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputInvoiceMessageContent.cs new file mode 100644 index 0000000..4df37c5 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputInvoiceMessageContent.cs @@ -0,0 +1,218 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Payments; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents the content of an invoice message to be sent as the result of an + /// inline query. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputInvoiceMessageContent : InputMessageContent { + /// + /// Product name, 1-32 characters + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Product description, 1-255 characters + /// + [JsonProperty(Required = Required.Always)] + public string Description { + get; + } + + /// + /// Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, + /// use for your internal processes. + /// + [JsonProperty(Required = Required.Always)] + public string Payload { + get; + } + + /// + /// Payment provider token, obtained via @Botfather + /// + [JsonProperty(Required = Required.Always)] + public string ProviderToken { + get; + } + + /// + /// Three-letter ISO 4217 currency code, see + /// more on currencies + /// + [JsonProperty(Required = Required.Always)] + public string Currency { + get; + } + + /// + /// Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, + /// delivery tax, bonus, etc.) + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable Prices { + get; + } + + /// + /// Optional. The maximum accepted amount for tips in the smallest units of the currency + /// (integer, not float/double). For example, for a maximum tip of US$ 1.45 pass + /// max_tip_amount = 145. See the exp parameter in + /// currencies.json, + /// it shows the number of digits past the decimal point for each currency (2 for the + /// majority of currencies). Defaults to 0 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MaxTipAmount { + get; set; + } + + /// + /// Optional. An array of suggested amounts of tip in the smallest units of the currency + /// (integer, not float/double). At most 4 suggested tip amounts can be specified. The + /// suggested tip amounts must be positive, passed in a strictly increased order and + /// must not exceed . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int[]? SuggestedTipAmounts { + get; set; + } + + /// + /// Optional. A JSON-serialized object for data about the invoice, which will be shared with + /// the payment provider. A detailed description of the required fields should be provided by + /// the payment provider. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ProviderData { + get; set; + } + + /// + /// Optional. URL of the product photo for the invoice. Can be a photo of the goods or a + /// marketing image for a service. People like it better when they see what they are paying for. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? PhotoUrl { + get; set; + } + + /// + /// Optional. Photo size + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? PhotoSize { + get; set; + } + + /// + /// Optional. Photo width + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? PhotoWidth { + get; set; + } + + /// + /// Optional. Photo height + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? PhotoHeight { + get; set; + } + + /// + /// Optional. Pass True, if you require the user's full name to complete the order + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NeedName { + get; set; + } + + /// + /// Optional. Pass True, if you require the user's phone number to complete the order + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NeedPhoneNumber { + get; set; + } + + /// + /// Optional. Pass True, if you require the user's email address to complete the order + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NeedEmail { + get; set; + } + + /// + /// Optional. Pass True, if you require the user's shipping address to complete the order + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? NeedShippingAddress { + get; set; + } + + /// + /// Optional. Pass True, if user's phone number should be sent to provider + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? SendPhoneNumberToProvider { + get; set; + } + + /// + /// Optional. Pass True, if user's email address should be sent to provider + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? SendEmailToProvider { + get; set; + } + + /// + /// Optional. Pass True, if the final price depends on the shipping method + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? IsFlexible { + get; set; + } + + /// + /// Initializes with title, description, payload, providerToken, currency and an array of + /// + /// + /// Product name, 1-32 characters + /// Product description, 1-255 characters + /// Bot-defined invoice payload, 1-128 bytes + /// Payments provider token, obtained via BotFather + /// Three-letter ISO 4217 currency code + /// + /// Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, + /// delivery tax, bonus, etc.) + /// + public InputInvoiceMessageContent( + string title, + string description, + string payload, + string providerToken, + string currency, + IEnumerable prices) { + Title = title; + Description = description; + Payload = payload; + ProviderToken = providerToken; + Currency = currency; + Prices = prices; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputLocationMessageContent.cs b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputLocationMessageContent.cs new file mode 100644 index 0000000..aa0110b --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputLocationMessageContent.cs @@ -0,0 +1,73 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents the content of a location message to be sent as the result of an + /// inline query. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputLocationMessageContent : InputMessageContent { + /// + /// Latitude of the location in degrees + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; + } + + /// + /// Longitude of the location in degrees + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; + } + + /// + /// Optional. The radius of uncertainty for the location, measured in meters; 0-1500 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public float? HorizontalAccuracy { + get; set; + } + + /// + /// Optional. Period in seconds for which the location can be updated, should be between 60 and 86400. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? LivePeriod { + get; set; + } + + /// + /// Optional. The direction in which user is moving, in degrees; 1-360. For active live locations only. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Heading { + get; set; + } + + /// + /// Optional. Maximum distance for proximity alerts about approaching another chat member, + /// in meters. For sent live locations only. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ProximityAlertRadius { + get; set; + } + + /// + /// Initializes a new input location message content + /// + /// The latitude of the location + /// The longitude of the location + public InputLocationMessageContent(double latitude, double longitude) { + Latitude = latitude; + Longitude = longitude; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputMessageContent.cs b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputMessageContent.cs new file mode 100644 index 0000000..36eeb8c --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputMessageContent.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// This object represents the content of a message to be sent as a result of an + /// inline query. + /// + [JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class InputMessageContent { + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputTextMessageContent.cs b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputTextMessageContent.cs new file mode 100644 index 0000000..109e396 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputTextMessageContent.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents the content of a text message to be sent as the result of an + /// inline query. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputTextMessageContent : InputMessageContent { + /// + /// Text of the message to be sent, 1-4096 characters + /// + [JsonProperty(Required = Required.Always)] + public string MessageText { + get; + } + + /// + /// Optional. Mode for + /// parsing entities in the message + /// text. See formatting options for more details. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + /// Optional. List of special entities that appear in message text, which can be specified + /// instead of + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? Entities { + get; set; + } // ToDo: add test + + /// + /// Optional. Disables link previews for links in the sent message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableWebPagePreview { + get; set; + } + + /// + /// Initializes a new input text message content + /// + /// The text of the message + public InputTextMessageContent(string messageText) { + MessageText = messageText; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputVenueMessageContent.cs b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputVenueMessageContent.cs new file mode 100644 index 0000000..86332e4 --- /dev/null +++ b/TelegramBot/Types/InlineQueryResults/InputMessageContent/InputVenueMessageContent.cs @@ -0,0 +1,94 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.InlineQueryResults { + + + /// + /// Represents the content of a message to be sent as the result of an + /// inline query. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputVenueMessageContent : InputMessageContent { + /// + /// Latitude of the venue in degrees + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; + } + + /// + /// Longitude of the venue in degrees + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; + } + + /// + /// Name of the venue + /// + [JsonProperty(Required = Required.Always)] + public string Title { + get; + } + + /// + /// Address of the venue + /// + [JsonProperty(Required = Required.Always)] + public string Address { + get; + } + + /// + /// Optional. Foursquare identifier of the venue, if known + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FoursquareId { + get; set; + } + + /// + /// Optional. Foursquare type of the venue. (For example, “arts_entertainment/default”, + /// “arts_entertainment/aquarium” or “food/icecream”.) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FoursquareType { + get; set; + } + + /// + /// Google Places identifier of the venue + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GooglePlaceId { + get; set; + } + + /// + /// Google Places type of the venue. + /// + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GooglePlaceType { + get; set; + } + + /// + /// Initializes a new inline query result + /// + /// The name of the venue + /// The address of the venue + /// The latitude of the venue + /// The longitude of the venue + public InputVenueMessageContent(string title, string address, double latitude, double longitude) { + Title = title; + Address = address; + Latitude = latitude; + Longitude = longitude; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/IAlbumInputMedia.cs b/TelegramBot/Types/InputFiles/IAlbumInputMedia.cs new file mode 100644 index 0000000..b145c9a --- /dev/null +++ b/TelegramBot/Types/InputFiles/IAlbumInputMedia.cs @@ -0,0 +1,11 @@ +// ReSharper disable once CheckNamespace + +namespace Telegram.Bot.Types { + + + /// + /// A marker for input media types that can be used in sendMediaGroup method. + /// + public interface IAlbumInputMedia : IInputMedia { + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/IInputFile.cs b/TelegramBot/Types/InputFiles/IInputFile.cs new file mode 100644 index 0000000..c83648b --- /dev/null +++ b/TelegramBot/Types/InputFiles/IInputFile.cs @@ -0,0 +1,19 @@ +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// This object represents the contents of a file to be uploaded. Must be posted using multipart/form-data in + /// the usual way that files are uploaded via the browser + /// + public interface IInputFile { + /// + /// Type of file to send + /// + FileType FileType { + get; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/IInputMedia.cs b/TelegramBot/Types/InputFiles/IInputMedia.cs new file mode 100644 index 0000000..0f6b4e0 --- /dev/null +++ b/TelegramBot/Types/InputFiles/IInputMedia.cs @@ -0,0 +1,48 @@ +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// A marker interface for input media content + /// + public interface IInputMedia { + /// + /// Type of the media + /// + InputMediaType Type { + get; + } + + /// + /// Media to send + /// + InputMedia Media { + get; + } + + /// + /// Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing + /// + string? Caption { + get; + } + + /// + /// Optional. Mode for parsing entities in the photo caption. See + /// formatting options for more details. + /// + ParseMode? ParseMode { + get; + } + + /// + /// Optional. List of special entities that appear in the caption, which can be specified + /// instead of + /// + MessageEntity[]? CaptionEntities { + get; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/IInputMediaThumb.cs b/TelegramBot/Types/InputFiles/IInputMediaThumb.cs new file mode 100644 index 0000000..da9f773 --- /dev/null +++ b/TelegramBot/Types/InputFiles/IInputMediaThumb.cs @@ -0,0 +1,19 @@ +// ReSharper disable once CheckNamespace + +namespace Telegram.Bot.Types { + + + /// + /// Indicates that an has a thumbnail. + /// + public interface IInputMediaThumb { + /// + /// Optional. 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. + /// + InputMedia? Thumb { + get; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputFileStream.cs b/TelegramBot/Types/InputFiles/InputFileStream.cs new file mode 100644 index 0000000..27893a1 --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputFileStream.cs @@ -0,0 +1,62 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Converters; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types.InputFiles { + + + /// + /// Used for sending files to Telegram + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + [JsonConverter(typeof(InputFileConverter))] + public class InputFileStream : IInputFile { + /// + public FileType FileType { + get; + } + + /// + /// File content to upload + /// + public Stream? Content { + get; + } + + /// + /// Name of a file to upload using multipart/form-data + /// + public string? FileName { + get; set; + } + + /// + /// Constructs an with a given + /// + protected InputFileStream(FileType fileType) { + FileType = fileType; + } + + /// + /// Constructs an from a and a file name + /// + /// A containing a file to send + /// A name of the file + public InputFileStream(Stream content, string? fileName = default) { + Content = content; + FileName = fileName; + FileType = FileType.Stream; + } + + /// + /// Constructs an from a + /// + /// A containing a file to send + [return: NotNullIfNotNull("stream")] + public static implicit operator InputFileStream?(Stream? stream) => + stream is null ? default : new InputFileStream(stream); + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputMedia.cs b/TelegramBot/Types/InputFiles/InputMedia.cs new file mode 100644 index 0000000..7cf66be --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputMedia.cs @@ -0,0 +1,47 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Newtonsoft.Json; +using Telegram.Bot.Converters; +using Telegram.Bot.Types.InputFiles; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// Media to send in request that could be a file_id, HTTP url, or a file + /// + [JsonConverter(typeof(InputMediaConverter))] + public class InputMedia : InputOnlineFile { + /// + /// Initializes media with a file to send + /// + /// File content to upload + /// Name of the file to send + public InputMedia(Stream content, string fileName) + : base(content, fileName) { + } + + /// + /// Initializes an instance of with either a file_id or a HTTP URL + /// + /// + /// file_id to send a file that exists on the Telegram servers or an HTTP URL for Telegram to get a file + /// from the Internet + /// + public InputMedia(string value) + : base(value) { + } + + /// + /// Initializes an instance of with either a file_id or a HTTP URL + /// + /// + /// file_id to send a file that exists on the Telegram servers or an HTTP URL for Telegram to get a file + /// from the Internet + /// + [return: NotNullIfNotNull("value")] + public static implicit operator InputMedia?(string? value) => + value is null ? default : new InputMedia(value); + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputMediaAnimation.cs b/TelegramBot/Types/InputFiles/InputMediaAnimation.cs new file mode 100644 index 0000000..04bfbe1 --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputMediaAnimation.cs @@ -0,0 +1,57 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputMediaAnimation : InputMediaBase, + IInputMediaThumb { + /// + [JsonProperty(Required = Required.Always)] + public override InputMediaType Type => InputMediaType.Animation; + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + /// Optional. Animation width + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Width { + get; set; + } + + /// + /// Optional. Animation height + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Height { + get; set; + } + + /// + /// Optional. Animation duration + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Duration { + get; set; + } + + /// + /// Initializes a new animation media to send with an + /// + /// File to send + public InputMediaAnimation(InputMedia media) + : base(media) { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputMediaAudio.cs b/TelegramBot/Types/InputFiles/InputMediaAudio.cs new file mode 100644 index 0000000..9bae092 --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputMediaAudio.cs @@ -0,0 +1,58 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// Represents an audio file to be treated as music to be sent. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputMediaAudio : InputMediaBase, + IInputMediaThumb, + IAlbumInputMedia { + /// + [JsonProperty(Required = Required.Always)] + public override InputMediaType Type => InputMediaType.Audio; + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + /// Optional. Duration of the audio in seconds + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Duration { + get; set; + } + + /// + /// Optional. Performer of the audio + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Performer { + get; set; + } + + /// + /// Optional. Title of the audio + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Title { + get; set; + } + + /// + /// Initializes a new audio media to send with an + /// + /// File to send + public InputMediaAudio(InputMedia media) + : base(media) { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputMediaBase.cs b/TelegramBot/Types/InputFiles/InputMediaBase.cs new file mode 100644 index 0000000..237e6ae --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputMediaBase.cs @@ -0,0 +1,61 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// This object represents the content of a media message to be sent + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class InputMediaBase : IInputMedia { + /// + /// Type of the media + /// + [JsonProperty(Required = Required.Always)] + public abstract InputMediaType Type { + get; + } + + /// + /// File to send + /// + [JsonProperty(Required = Required.Always)] + public InputMedia Media { + get; + } + + /// + /// Optional. Caption of the photo to be sent, 0-1024 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + /// Optional. List of special entities that appear in the caption, which can be specified instead + /// of + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + /// Change, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in a caption + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ParseMode? ParseMode { + get; set; + } + + /// + /// Initialize an object + /// + /// File to send + protected InputMediaBase(InputMedia media) => Media = media; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputMediaDocument.cs b/TelegramBot/Types/InputFiles/InputMediaDocument.cs new file mode 100644 index 0000000..2469c62 --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputMediaDocument.cs @@ -0,0 +1,43 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// Represents a general file to be sent + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputMediaDocument : InputMediaBase, + IInputMediaThumb, + IAlbumInputMedia { + /// + [JsonProperty(Required = Required.Always)] + public override InputMediaType Type => InputMediaType.Document; + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + /// Optional. Disables automatic server-side content type detection for files uploaded using + /// multipart/form-data. Always true, if the document is sent as part of an album. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DisableContentTypeDetection { + get; set; + } + + /// + /// Initializes a new document media to send with an + /// + /// File to send + public InputMediaDocument(InputMedia media) + : base(media) { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputMediaPhoto.cs b/TelegramBot/Types/InputFiles/InputMediaPhoto.cs new file mode 100644 index 0000000..ac41aab --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputMediaPhoto.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// Represents a photo to be sent + /// + public class InputMediaPhoto : InputMediaBase, + IAlbumInputMedia { + /// + [JsonProperty(Required = Required.Always)] + public override InputMediaType Type => InputMediaType.Photo; + + /// + /// Initializes a new photo media to send with an + /// + /// File to send + public InputMediaPhoto(InputMedia media) + : base(media) { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputMediaVideo.cs b/TelegramBot/Types/InputFiles/InputMediaVideo.cs new file mode 100644 index 0000000..f5d582e --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputMediaVideo.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types { + + + /// + /// Represents a video to be sent + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InputMediaVideo : InputMediaBase, + IInputMediaThumb, + IAlbumInputMedia { + /// + [JsonProperty(Required = Required.Always)] + public override InputMediaType Type => InputMediaType.Video; + + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InputMedia? Thumb { + get; set; + } + + /// + /// Optional. Video width + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Width { + get; set; + } + + /// + /// Optional. Video height + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Height { + get; set; + } + + /// + /// Optional. Video duration + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Duration { + get; set; + } + + /// + /// Optional. Pass True, if the uploaded video is suitable for streaming + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? SupportsStreaming { + get; set; + } + + /// + /// Initializes a new video media to send with an + /// + /// File to send + public InputMediaVideo(InputMedia media) + : base(media) { + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputOnlineFile.cs b/TelegramBot/Types/InputFiles/InputOnlineFile.cs new file mode 100644 index 0000000..af044bb --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputOnlineFile.cs @@ -0,0 +1,80 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Converters; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types.InputFiles { + + + /// + /// Used for sending files to Telegram + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + [JsonConverter(typeof(InputFileConverter))] + public class InputOnlineFile : InputTelegramFile { + /// + /// HTTP URL for Telegram to get a file from the Internet + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Url { + get; + } + + /// + /// Constructs an from a and a file name + /// + /// A containing a file to send + /// A name of the file + public InputOnlineFile(Stream content, string? fileName = default) + : base(content, fileName) { + } + + /// + /// Constructs an from a string containing a uri or file id + /// + /// A containing a url or file_id + public InputOnlineFile(string value) + : base(DetectFileType(value, out var isUrl)) { + if(isUrl) { + Url = value; + } else { + FileId = value; + } + } + + /// + /// Constructs an from a + /// + /// A pointing to a file + public InputOnlineFile(Uri url) : base(FileType.Url) => + // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract + Url = url?.AbsoluteUri ?? throw new ArgumentNullException(nameof(url)); + + /// + /// Constructs an from a + /// + /// A containing a file to send + public static implicit operator InputOnlineFile?(Stream? stream) => + stream is null ? default : new(stream); + + /// + /// Constructs an from a string containing a uri or file id + /// + /// A containing a url or file_id + [return: NotNullIfNotNull("value")] + public static implicit operator InputOnlineFile?(string? value) => + value is null ? default : new(value); + + static FileType DetectFileType(string value, out bool isUrl) { + if(Uri.TryCreate(value, UriKind.Absolute, out _)) { + isUrl = true; + return FileType.Url; + } + isUrl = false; + return FileType.Id; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/InputFiles/InputTelegramFile.cs b/TelegramBot/Types/InputFiles/InputTelegramFile.cs new file mode 100644 index 0000000..b43bbfd --- /dev/null +++ b/TelegramBot/Types/InputFiles/InputTelegramFile.cs @@ -0,0 +1,63 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Converters; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types.InputFiles { + + + /// + /// Used for sending files to Telegram + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + [JsonConverter(typeof(InputFileConverter))] + public class InputTelegramFile : InputFileStream { + /// + /// Id of a file that exists on Telegram servers + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FileId { + get; private protected set; + } + + /// + /// Constructs an with a + /// + protected InputTelegramFile(FileType fileType) + : base(fileType) { + } + + /// + /// Constructs an from a and a file name + /// + /// A containing a file to send + /// A name of the file + public InputTelegramFile(Stream content, string? fileName = default) + : base(content, fileName) { + } + + /// + /// Constructs an with a + /// + /// A file identifier + public InputTelegramFile(string fileId) : base(FileType.Id) => FileId = fileId; + + /// + /// Constructs an from a + /// + /// A containing a file to send + [return: NotNullIfNotNull("stream")] + public static implicit operator InputTelegramFile?(Stream? stream) => + stream is null ? default : new InputTelegramFile(stream); + + /// + /// Constructs an with a + /// + /// A file identifier + [return: NotNullIfNotNull("fileId")] + public static implicit operator InputTelegramFile?(string? fileId) => + fileId is null ? default : new InputTelegramFile(fileId); + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Location.cs b/TelegramBot/Types/Location.cs new file mode 100644 index 0000000..224b03b --- /dev/null +++ b/TelegramBot/Types/Location.cs @@ -0,0 +1,60 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a point on the map. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Location { + /// + /// Longitude as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public double Longitude { + get; set; + } + + /// + /// Latitude as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public double Latitude { + get; set; + } + + /// + /// Optional. The radius of uncertainty for the location, measured in meters; 0-1500 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public float? HorizontalAccuracy { + get; set; + } + + /// + /// Optional. Time relative to the message sending date, during which the location can be updated, in seconds. For active live locations only. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? LivePeriod { + get; set; + } + + /// + /// Optional. The direction in which user is moving, in degrees; 1-360. For active live locations only. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? Heading { + get; set; + } + + /// + /// Optional. Maximum distance for proximity alerts about approaching another chat member, in meters. For sent live locations only. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ProximityAlertRadius { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/LoginUrl.cs b/TelegramBot/Types/LoginUrl.cs new file mode 100644 index 0000000..abee405 --- /dev/null +++ b/TelegramBot/Types/LoginUrl.cs @@ -0,0 +1,65 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a parameter of the inline keyboard button used to automatically authorize a user. + /// Serves as a great replacement for the + /// Telegram Login Widget when the user is coming from + /// Telegram. All the user needs to do is tap/click a button and confirm that they want to log in. + /// + /// Telegram apps support these buttons as of + /// version 5.7. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class LoginUrl { + + /// + /// An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. + /// If the user refuses to provide authorization data, the original URL without information about the user will + /// be opened. The data added is the same as described in + /// + /// Receiving authorization data + /// . + /// + /// NOTE: You must always check the hash of the received data to verify the authentication and + /// the integrity of the data as described in + /// Checking authorization. + /// + /// + [JsonProperty(Required = Required.Always)] + public string Url { get; set; } = default!; + + /// + /// Optional. New text of the button in forwarded messages + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ForwardText { + get; set; + } + + /// + /// Optional. Username of a bot, which will be used for user authorization. See + /// Setting up a bot for more + /// details. If not specified, the current bot’s username will be assumed. The url's domain must be the same + /// as the domain linked with the bot. See + /// + /// Linking your domain to the bot for more details. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? BotUsername { + get; set; + } + + /// + /// Optional. Pass True to request the permission for your bot to send messages to the user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? RequestWriteAccess { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/MaskPosition.cs b/TelegramBot/Types/MaskPosition.cs new file mode 100644 index 0000000..b1c6151 --- /dev/null +++ b/TelegramBot/Types/MaskPosition.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types { + + + /// + /// This object describes the position on faces where a mask should be placed by default. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class MaskPosition { + /// + /// The part of the face relative to which the mask should be placed. + /// + [JsonProperty(Required = Required.Always)] + public MaskPositionPoint Point { + get; set; + } + + /// + /// Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, choosing -1.0 will place mask just to the left of the default mask position. + /// + [JsonProperty(Required = Required.Always)] + public float XShift { + get; set; + } + + /// + /// Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For example, 1.0 will place the mask just below the default mask position. + /// + [JsonProperty(Required = Required.Always)] + public float YShift { + get; set; + } + + /// + /// Mask scaling coefficient. For example, 2.0 means double size. + /// + [JsonProperty(Required = Required.Always)] + public float Scale { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/MenuButton.cs b/TelegramBot/Types/MenuButton.cs new file mode 100644 index 0000000..9a73138 --- /dev/null +++ b/TelegramBot/Types/MenuButton.cs @@ -0,0 +1,71 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Converters; +using Telegram.Bot.Requests; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types { + + + /// + /// This object describes the bot’s menu button in a private chat. It should be one of: + /// + /// MenuButtonCommands + /// MenuButtonWebApp + /// MenuButtonDefault + /// + /// If a menu button other than MenuButtonDefault is set for a private chat, then it is applied in the chat. + /// Otherwise the default menu button is applied. By default, the menu button opens the list of bot commands. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + [JsonConverter(typeof(MenuButtonConverter))] + public abstract class MenuButton { + /// + /// Type of the button + /// + [JsonProperty] + public abstract MenuButtonType Type { + get; + } + } + + /// + /// Represents a menu button, which opens the bot’s list of commands. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class MenuButtonCommands : MenuButton { + /// + public override MenuButtonType Type => MenuButtonType.Commands; + } + + /// + /// Represents a menu button, which launches a Web App. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class MenuButtonWebApp : MenuButton { + /// + public override MenuButtonType Type => MenuButtonType.WebApp; + + /// + /// Text on the button + /// + [JsonProperty(Required = Required.Always)] + public string Text { get; set; } = default!; + + /// + /// Description of the Web App that will be launched when the user presses the button. The Web App will be able + /// to send an arbitrary message on behalf of the user using the method . + /// + [JsonProperty(Required = Required.Always)] + public WebAppInfo WebApp { get; set; } = default!; + } + + /// + /// Describes that no specific value for the menu button was set. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class MenuButtonDefault : MenuButton { + /// + public override MenuButtonType Type => MenuButtonType.Default; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Message.cs b/TelegramBot/Types/Message.cs new file mode 100644 index 0000000..9e64dce --- /dev/null +++ b/TelegramBot/Types/Message.cs @@ -0,0 +1,584 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.Passport; +using Telegram.Bot.Types.Payments; +using Telegram.Bot.Types.ReplyMarkups; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a message. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Message { + /// + /// Unique message identifier inside this chat + /// + [JsonProperty(Required = Required.Always)] + public int MessageId { + get; set; + } + + /// + /// Optional. Sender, empty for messages sent to channels + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public User? From { + get; set; + } + + /// + /// Optional. Sender of the message, sent on behalf of a chat. The channel itself for channel messages. + /// The supergroup itself for messages from anonymous group administrators. The linked channel for messages + /// automatically forwarded to the discussion group + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Chat? SenderChat { + get; set; + } + + /// + /// Date the message was sent + /// + [JsonProperty(Required = Required.Always)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime Date { + get; set; + } + + /// + /// Conversation the message belongs to + /// + [JsonProperty(Required = Required.Always)] + public Chat Chat { get; set; } = default!; + + /// + /// Optional. For forwarded messages, sender of the original message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public User? ForwardFrom { + get; set; + } + + /// + /// Optional. For messages forwarded from channels or from anonymous administrators, information about the + /// original sender chat + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Chat? ForwardFromChat { + get; set; + } + + /// + /// Optional. For messages forwarded from channels, identifier of the original message in the channel + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? ForwardFromMessageId { + get; set; + } + + /// + /// Optional. For messages forwarded from channels, signature of the post author if present + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ForwardSignature { + get; set; + } + + /// + /// Optional. Sender's name for messages forwarded from users who disallow adding a link to their account in + /// forwarded messages + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ForwardSenderName { + get; set; + } + + /// + /// Optional. For forwarded messages, date the original message was sent + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? ForwardDate { + get; set; + } + + /// + /// Optional. true, if the message is a channel post that was automatically forwarded to the connected + /// discussion group + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? IsAutomaticForward { + get; set; + } + + /// + /// Optional. For replies, the original message. Note that the object in this field + /// will not contain further fields even if it itself is a reply. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Message? ReplyToMessage { + get; set; + } + + /// + /// Optional. Bot through which the message was sent + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public User? ViaBot { + get; set; + } + + /// + /// Optional. Date the message was last edited + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? EditDate { + get; set; + } + + /// + /// Optional. true, if messages from the chat can't be forwarded to other chats. + /// Returned only in . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? HasProtectedContent { + get; set; + } + + /// + /// Optional. The unique identifier of a media message group this message belongs to + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? MediaGroupId { + get; set; + } + + /// + /// Optional. Signature of the post author for messages in channels, or the custom title of an anonymous + /// group administrator + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? AuthorSignature { + get; set; + } + + /// + /// Optional. For text messages, the actual text of the message, 0-4096 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Text { + get; set; + } + + /// + /// Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear + /// in the text + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? Entities { + get; set; + } + + /// + /// Gets the entity values. + /// + /// + /// The entity contents. + /// + public IEnumerable? EntityValues => + Text is null + ? default + : Entities?.Select(entity => Text.Substring(entity.Offset, entity.Length)); + + /// + /// Optional. Message is an animation, information about the animation. For backward compatibility, when this + /// field is set, the field will also be set + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Animation? Animation { + get; set; + } + + /// + /// Optional. Message is an audio file, information about the file + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Audio? Audio { + get; set; + } + + /// + /// Optional. Message is a general file, information about the file + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Document? Document { + get; set; + } + + /// + /// Optional. Message is a photo, available sizes of the photo + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PhotoSize[]? Photo { + get; set; + } + + /// + /// Optional. Message is a sticker, information about the sticker + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Sticker? Sticker { + get; set; + } + + /// + /// Optional. Message is a video, information about the video + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Video? Video { + get; set; + } + + /// + /// Optional. Message is a video note, information about the video message + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public VideoNote? VideoNote { + get; set; + } + + /// + /// Optional. Message is a voice message, information about the file + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Voice? Voice { + get; set; + } + + /// + /// Optional. Caption for the animation, audio, document, photo, video or voice, 0-1024 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Caption { + get; set; + } + + /// + /// Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that + /// appear in the caption + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? CaptionEntities { + get; set; + } + + /// + /// Gets the caption entity values. + /// + /// + /// The caption entity contents. + /// + public IEnumerable? CaptionEntityValues => + Caption is null + ? default + : CaptionEntities?.Select(entity => Caption.Substring(entity.Offset, entity.Length)); + + /// + /// Optional. Message is a shared contact, information about the contact + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Contact? Contact { + get; set; + } + + /// + /// Optional. Message is a dice with random value + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Dice? Dice { + get; set; + } + + /// + ///Optional. Message is a game, information about the game + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Game? Game { + get; set; + } + + /// + /// Optional. Message is a native poll, information about the poll + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Poll? Poll { + get; set; + } + + /// + /// Optional. Message is a venue, information about the venue. For backward compatibility, when this field + /// is set, the field will also be set + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Venue? Venue { + get; set; + } + + /// + /// Optional. Message is a shared location, information about the location + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Location? Location { + get; set; + } + + /// + /// Optional. New members that were added to the group or supergroup and information about them + /// (the bot itself may be one of these members) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public User[]? NewChatMembers { + get; set; + } + + /// + /// Optional. A member was removed from the group, information about them (this member may be the bot itself) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public User? LeftChatMember { + get; set; + } + + /// + /// Optional. A chat title was changed to this value + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? NewChatTitle { + get; set; + } + + /// + /// Optional. A chat photo was change to this value + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PhotoSize[]? NewChatPhoto { + get; set; + } + + /// + /// Optional. Service message: the chat photo was deleted + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? DeleteChatPhoto { + get; set; + } + + /// + /// Optional. Service message: the group has been created + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? GroupChatCreated { + get; set; + } + + /// + /// Optional. Service message: the supergroup has been created. This field can't be received in a message + /// coming through updates, because bot can't be a member of a supergroup when it is created. It can only be + /// found in if someone replies to a very first message in a directly created + /// supergroup. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? SupergroupChatCreated { + get; set; + } + + /// + /// Optional. Service message: the channel has been created. This field can't be received in a message coming + /// through updates, because bot can't be a member of a channel when it is created. It can only be found in + /// if someone replies to a very first message in a channel. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ChannelChatCreated { + get; set; + } + + /// + /// Optional. Service message: auto-delete timer settings changed in the chat + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageAutoDeleteTimerChanged? MessageAutoDeleteTimerChanged { + get; set; + } + + /// + /// Optional. The group has been migrated to a supergroup with the specified identifier + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public long? MigrateToChatId { + get; set; + } + + /// + /// Optional. The supergroup has been migrated from a group with the specified identifier + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public long? MigrateFromChatId { + get; set; + } + + /// + /// Optional. Specified message was pinned. Note that the Message object in this field will not contain + /// further fields even if it is itself a reply. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Message? PinnedMessage { + get; set; + } + + /// + /// Optional. Message is an invoice for a + /// payment, information about the invoice + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Invoice? Invoice { + get; set; + } + + /// + /// Optional. Message is a service message about a successful payment, information about the payment + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public SuccessfulPayment? SuccessfulPayment { + get; set; + } + + /// + /// Optional. The domain name of the website on which the user has logged in + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ConnectedWebsite { + get; set; + } + + /// + /// Optional. Telegram Passport data + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PassportData? PassportData { + get; set; + } + + /// + /// Optional. Service message. A user in the chat triggered another user's proximity alert while + /// sharing Live Location + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ProximityAlertTriggered? ProximityAlertTriggered { + get; set; + } + + /// + /// Optional. Service message: video chat scheduled + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public VideoChatScheduled? VideoChatScheduled { + get; set; + } + + /// + /// Optional. Service message: video chat started + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public VideoChatStarted? VideoChatStarted { + get; set; + } + + /// + /// Optional. Service message: video chat ended + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public VideoChatEnded? VideoChatEnded { + get; set; + } + + /// + /// Optional. Service message: new participants invited to a video chat + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public VideoChatParticipantsInvited? VideoChatParticipantsInvited { + get; set; + } + + /// + /// Optional. Service message: data sent by a Web App + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public WebAppData? WebAppData { + get; set; + } + + /// + /// Optional. Inline keyboard attached to the message. buttons are represented as + /// ordinary url buttons. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineKeyboardMarkup? ReplyMarkup { + get; set; + } + + /// + /// Gets the of the + /// + /// + /// The of the + /// + public MessageType Type => + this switch { + { Text: { } } => MessageType.Text, + { Photo: { } } => MessageType.Photo, + { Audio: { } } => MessageType.Audio, + { Video: { } } => MessageType.Video, + { Voice: { } } => MessageType.Voice, + { Document: { } } => MessageType.Document, + { Sticker: { } } => MessageType.Sticker, + // Venue also contains Location + { Location: { } } and { Venue: null } => MessageType.Location, + { Venue: { } } => MessageType.Venue, + { Contact: { } } => MessageType.Contact, + { Game: { } } => MessageType.Game, + { VideoNote: { } } => MessageType.VideoNote, + { Invoice: { } } => MessageType.Invoice, + { SuccessfulPayment: { } } => MessageType.SuccessfulPayment, + { ConnectedWebsite: { } } => MessageType.WebsiteConnected, + { NewChatMembers: { Length: > 0 } } => MessageType.ChatMembersAdded, + { LeftChatMember: { } } => MessageType.ChatMemberLeft, + { NewChatTitle: { } } => MessageType.ChatTitleChanged, + { NewChatPhoto: { } } => MessageType.ChatPhotoChanged, + { PinnedMessage: { } } => MessageType.MessagePinned, + { DeleteChatPhoto: { } } => MessageType.ChatPhotoDeleted, + { GroupChatCreated: { } } => MessageType.GroupCreated, + { SupergroupChatCreated: { } } => MessageType.SupergroupCreated, + { ChannelChatCreated: { } } => MessageType.ChannelCreated, + { MigrateToChatId: { } } => MessageType.MigratedToSupergroup, + { MigrateFromChatId: { } } => MessageType.MigratedFromGroup, + { Poll: { } } => MessageType.Poll, + { Dice: { } } => MessageType.Dice, + { MessageAutoDeleteTimerChanged: { } } => MessageType.MessageAutoDeleteTimerChanged, + { ProximityAlertTriggered: { } } => MessageType.ProximityAlertTriggered, + { VideoChatScheduled: { } } => MessageType.VideoChatScheduled, + { VideoChatStarted: { } } => MessageType.VideoChatStarted, + { VideoChatEnded: { } } => MessageType.VideoChatEnded, + { VideoChatParticipantsInvited: { } } => MessageType.VideoChatParticipantsInvited, + { WebAppData: { } } => MessageType.WebAppData, + _ => MessageType.Unknown + }; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/MessageAutoDeleteTimerChanged.cs b/TelegramBot/Types/MessageAutoDeleteTimerChanged.cs new file mode 100644 index 0000000..554d9b6 --- /dev/null +++ b/TelegramBot/Types/MessageAutoDeleteTimerChanged.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about a change in auto-delete timer settings. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class MessageAutoDeleteTimerChanged { + /// + /// New auto-delete time for messages in the chat + /// + [JsonProperty(Required = Required.Always)] + public int MessageAutoDeleteTime { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/MessageEntity.cs b/TelegramBot/Types/MessageEntity.cs new file mode 100644 index 0000000..70e398a --- /dev/null +++ b/TelegramBot/Types/MessageEntity.cs @@ -0,0 +1,61 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class MessageEntity { + /// + /// Type of the entity + /// + [JsonProperty(Required = Required.Always)] + public MessageEntityType Type { + get; set; + } + + /// + /// Offset in UTF-16 code units to the start of the entity + /// + [JsonProperty(Required = Required.Always)] + public int Offset { + get; set; + } + + /// + /// Length of the entity in UTF-16 code units + /// + [JsonProperty(Required = Required.Always)] + public int Length { + get; set; + } + + /// + /// Optional. For only, url that will be opened after user taps on the text + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Url { + get; set; + } + + /// + /// Optional. For only, the mentioned user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public User? User { + get; set; + } + + /// + /// Optional. For only, the programming language of the entity text + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Language { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/MessageId.cs b/TelegramBot/Types/MessageId.cs new file mode 100644 index 0000000..356c952 --- /dev/null +++ b/TelegramBot/Types/MessageId.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a messageId. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class MessageId { + /// + /// Message identifier in the chat specified in + /// + [JsonProperty(Required = Required.Always, PropertyName = "message_id")] + public int Id { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Passport/EncryptedCredentials.cs b/TelegramBot/Types/Passport/EncryptedCredentials.cs new file mode 100644 index 0000000..ece83cc --- /dev/null +++ b/TelegramBot/Types/Passport/EncryptedCredentials.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.Passport { + + + /// + /// Contains data required for decrypting and authenticating . + /// See the Telegram Passport + /// Documentation for a complete description of the data decryption and authentication processes. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EncryptedCredentials { + /// + /// Base64-encoded encrypted JSON-serialized data with unique user's payload, data hashes and secrets + /// required for decryption and authentication. + /// + [JsonProperty(Required = Required.Always)] + public string Data { get; set; } = default!; + + /// + /// Base64-encoded data hash for data authentication. + /// + [JsonProperty(Required = Required.Always)] + public string Hash { get; set; } = default!; + + /// + /// Base64-encoded secret, encrypted with the bot’s public RSA key, required for data decryption. + /// + [JsonProperty(Required = Required.Always)] + public string Secret { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Passport/EncryptedPassportElement.cs b/TelegramBot/Types/Passport/EncryptedPassportElement.cs new file mode 100644 index 0000000..9825201 --- /dev/null +++ b/TelegramBot/Types/Passport/EncryptedPassportElement.cs @@ -0,0 +1,111 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using static Telegram.Bot.Types.Passport.EncryptedPassportElementType; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.Passport { + + + /// + /// Contains information about documents or other Telegram Passport elements shared with the bot by the user. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class EncryptedPassportElement { + /// + /// Element type. One of + /// + [JsonProperty(Required = Required.Always)] + public EncryptedPassportElementType Type { + get; set; + } + + /// + /// Optional. Base64-encoded encrypted Telegram Passport element data provided by the user, available for + /// , , , + /// , and + /// types. Can be decrypted and verified using the accompanying . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Data { + get; set; + } + + /// + /// Optional. User's verified phone number, available only for type. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? PhoneNumber { + get; set; + } + + /// + /// Optional. User's verified email address, available only for type. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Email { + get; set; + } + + /// + /// Optional. Array of encrypted files with documents provided by the user, available for + /// , , , + /// and types. + /// Files can be decrypted and verified using the accompanying . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PassportFile[]? Files { + get; set; + } + + /// + /// Optional. Encrypted file with the front side of the document, provided by the user. Available for + /// , , and + /// . The file can be decrypted and verified using the accompanying + /// . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PassportFile? FrontSide { + get; set; + } + + /// + /// Optional. Encrypted file with the reverse side of the document, provided by the user. Available for + /// and . The file can be decrypted and verified using + /// the accompanying . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PassportFile? ReverseSide { + get; set; + } + + /// + /// Optional. Encrypted file with the selfie of the user holding a document, provided by the user; + /// available for , , and + /// . The file can be decrypted and verified using the accompanying + /// . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PassportFile? Selfie { + get; set; + } + + /// + /// Optional. Array of encrypted files with translated versions of documents provided by the user. + /// Available if requested for , , + /// , , , + /// , , and + /// types. Files can be decrypted and verified using the accompanying + /// . + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PassportFile[]? Translation { + get; set; + } + + /// + /// Base64-encoded element hash for using in PassportElementErrorUnspecified + /// + [JsonProperty(Required = Required.Always)] + public string Hash { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Passport/EncryptedPassportElementType.cs b/TelegramBot/Types/Passport/EncryptedPassportElementType.cs new file mode 100644 index 0000000..2c1d72e --- /dev/null +++ b/TelegramBot/Types/Passport/EncryptedPassportElementType.cs @@ -0,0 +1,76 @@ +using Newtonsoft.Json; + +namespace Telegram.Bot.Types.Passport { + + + /// + /// + /// + [JsonConverter(typeof(EncryptedPassportElementTypeConverter))] + public enum EncryptedPassportElementType { + /// + /// Personal details + /// + PersonalDetails = 1, + + /// + /// Passport + /// + Passport, + + /// + /// Driver licence + /// + DriverLicence, + + /// + /// Identity card + /// + IdentityCard, + + /// + /// Internal passport + /// + InternalPassport, + + /// + /// Address + /// + Address, + + /// + /// Utility bill + /// + UtilityBill, + + /// + /// Bank statement + /// + BankStatement, + + /// + /// Rental agreement + /// + RentalAgreement, + + /// + /// Passport registration + /// + PassportRegistration, + + /// + /// Temporary registration + /// + TemporaryRegistration, + + /// + /// Phone number + /// + PhoneNumber, + + /// + /// Email + /// + Email + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Passport/PassportData.cs b/TelegramBot/Types/Passport/PassportData.cs new file mode 100644 index 0000000..5c378be --- /dev/null +++ b/TelegramBot/Types/Passport/PassportData.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.Passport { + + + /// + /// Contains information about Telegram Passport data shared with the bot by the user. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class PassportData { + /// + /// Array with information about documents and other Telegram Passport elements that was shared with the bot. + /// + [JsonProperty(Required = Required.Always)] + public EncryptedPassportElement[] Data { get; set; } = default!; + + /// + /// Encrypted credentials required to decrypt the data. + /// + [JsonProperty(Required = Required.Always)] + public EncryptedCredentials Credentials { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Passport/PassportFile.cs b/TelegramBot/Types/Passport/PassportFile.cs new file mode 100644 index 0000000..c8b2163 --- /dev/null +++ b/TelegramBot/Types/Passport/PassportFile.cs @@ -0,0 +1,24 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +// ReSharper disable once CheckNamespace +namespace Telegram.Bot.Types.Passport { + + + /// + /// This object represents a file uploaded to Telegram Passport. Currently all Telegram Passport files are in JPEG format when decrypted and don't exceed 10MB. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class PassportFile : FileBase { + /// + /// DateTime when the file was uploaded + /// + [JsonProperty(Required = Required.Always)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime FileDate { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Payments/Invoice.cs b/TelegramBot/Types/Payments/Invoice.cs new file mode 100644 index 0000000..a658df5 --- /dev/null +++ b/TelegramBot/Types/Payments/Invoice.cs @@ -0,0 +1,53 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.Payments { + + + /// + /// This object contains basic information about an invoice. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Invoice { + /// + /// Product name + /// + [JsonProperty(Required = Required.Always)] + public string Title { get; set; } = default!; + + /// + /// Product description + /// + [JsonProperty(Required = Required.Always)] + public string Description { get; set; } = default!; + + /// + /// Unique bot deep-linking parameter that can be used to generate this invoice + /// + [JsonProperty(Required = Required.Always)] + public string StartParameter { get; set; } = default!; + + /// + /// Three-letter ISO 4217 + /// currency code + /// + [JsonProperty(Required = Required.Always)] + public string Currency { get; set; } = default!; + + /// + /// Total price in the smallest units of the + /// currency + /// (integer, not float/double). + /// + /// For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in + /// currencies.json, it shows the + /// number of digits past the decimal point for each currency (2 for the majority of currencies). + /// + /// + [JsonProperty(Required = Required.Always)] + public int TotalAmount { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Payments/LabeledPrice.cs b/TelegramBot/Types/Payments/LabeledPrice.cs new file mode 100644 index 0000000..4e051ea --- /dev/null +++ b/TelegramBot/Types/Payments/LabeledPrice.cs @@ -0,0 +1,47 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.Payments { + + + /// + /// This object represents a portion of the price for goods or services. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class LabeledPrice { + /// + /// Portion label + /// + [JsonProperty(Required = Required.Always)] + public string Label { + get; set; + } + + /// + /// Price of the product in the smallest units of the + /// currency + /// (integer, not float/double). + /// + /// For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in + /// currencies.json, it shows the number + /// of digits past the decimal point for each currency (2 for the majority of currencies). + /// + /// + [JsonProperty(Required = Required.Always)] + public int Amount { + get; set; + } + + /// + /// Initializes an instance of + /// + /// Portion label + /// Price of the product + [JsonConstructor] + public LabeledPrice(string label, int amount) { + Label = label; + Amount = amount; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Payments/OrderInfo.cs b/TelegramBot/Types/Payments/OrderInfo.cs new file mode 100644 index 0000000..373c9a0 --- /dev/null +++ b/TelegramBot/Types/Payments/OrderInfo.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.Payments { + + + /// + /// This object represents information about an order. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class OrderInfo { + /// + /// Optional. User name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Name { + get; set; + } + + /// + /// Optional. User's phone number + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? PhoneNumber { + get; set; + } + + /// + /// Optional. User email + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Email { + get; set; + } + + /// + /// Optional. User shipping address + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ShippingAddress? ShippingAddress { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Payments/PreCheckoutQuery.cs b/TelegramBot/Types/Payments/PreCheckoutQuery.cs new file mode 100644 index 0000000..e048622 --- /dev/null +++ b/TelegramBot/Types/Payments/PreCheckoutQuery.cs @@ -0,0 +1,68 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.Payments { + + + /// + /// This object contains information about an incoming pre-checkout query. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class PreCheckoutQuery { + /// + /// Unique query identifier + /// + [JsonProperty(Required = Required.Always)] + public string Id { get; set; } = default!; + + /// + /// User who sent the query + /// + [JsonProperty(Required = Required.Always)] + public User From { get; set; } = default!; + + /// + /// Three-letter ISO 4217 + /// currency code + /// + [JsonProperty(Required = Required.Always)] + public string Currency { get; set; } = default!; + + /// + /// Total price in the smallest units of the + /// currency + /// (integer, not float/double). + /// + /// For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in + /// currencies.json, it shows the + /// number of digits past the decimal point for each currency (2 for the majority of currencies). + /// + /// + [JsonProperty(Required = Required.Always)] + public int TotalAmount { + get; set; + } + + /// + /// Bot specified invoice payload + /// + [JsonProperty(Required = Required.Always)] + public string InvoicePayload { get; set; } = default!; + + /// + /// Optional. Identifier of the shipping option chosen by the user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ShippingOptionId { + get; set; + } + + /// + /// Optional. Order info provided by the user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public OrderInfo? OrderInfo { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Payments/ShippingAddress.cs b/TelegramBot/Types/Payments/ShippingAddress.cs new file mode 100644 index 0000000..ca387db --- /dev/null +++ b/TelegramBot/Types/Payments/ShippingAddress.cs @@ -0,0 +1,48 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.Payments { + + + /// + /// This object represents a shipping address. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ShippingAddress { + /// + /// ISO 3166-1 alpha-2 country code + /// + [JsonProperty(Required = Required.Always)] + public string CountryCode { get; set; } = default!; + + /// + /// State, if applicable + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string State { get; set; } = default!; + + /// + /// City + /// + [JsonProperty(Required = Required.Always)] + public string City { get; set; } = default!; + + /// + /// First line for the address + /// + [JsonProperty(Required = Required.Always)] + public string StreetLine1 { get; set; } = default!; + + /// + /// Second line for the address + /// + [JsonProperty(Required = Required.Always)] + public string StreetLine2 { get; set; } = default!; + + /// + /// Address post code + /// + [JsonProperty(Required = Required.Always)] + public string PostCode { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Payments/ShippingOption.cs b/TelegramBot/Types/Payments/ShippingOption.cs new file mode 100644 index 0000000..2eb8544 --- /dev/null +++ b/TelegramBot/Types/Payments/ShippingOption.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.Payments { + + + /// + /// This object represents one shipping option. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ShippingOption { + /// + /// Shipping option identifier + /// + [JsonProperty(Required = Required.Always)] + public string Id { get; set; } = default!; + + /// + /// Option title + /// + [JsonProperty(Required = Required.Always)] + public string Title { get; set; } = default!; + + /// + /// List of price portions + /// + [JsonProperty(Required = Required.Always)] + public LabeledPrice[] Prices { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Payments/ShippingQuery.cs b/TelegramBot/Types/Payments/ShippingQuery.cs new file mode 100644 index 0000000..805bcbb --- /dev/null +++ b/TelegramBot/Types/Payments/ShippingQuery.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.Payments { + + + /// + /// This object contains information about an incoming shipping query. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ShippingQuery { + /// + /// Unique query identifier + /// + [JsonProperty(Required = Required.Always)] + public string Id { get; set; } = default!; + + /// + /// User who sent the query + /// + [JsonProperty(Required = Required.Always)] + public User From { get; set; } = default!; + + /// + /// Bot specified invoice payload + /// + [JsonProperty(Required = Required.Always)] + public string InvoicePayload { get; set; } = default!; + + /// + /// User specified shipping address + /// + [JsonProperty(Required = Required.Always)] + public ShippingAddress ShippingAddress { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Payments/SuccessfulPayment.cs b/TelegramBot/Types/Payments/SuccessfulPayment.cs new file mode 100644 index 0000000..775cc5e --- /dev/null +++ b/TelegramBot/Types/Payments/SuccessfulPayment.cs @@ -0,0 +1,68 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.Payments { + + + /// + /// This object contains basic information about a successful payment. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SuccessfulPayment { + /// + /// Three-letter ISO 4217 + /// currency code + /// + [JsonProperty(Required = Required.Always)] + public string Currency { get; set; } = default!; + + /// + /// Total price in the smallest units of the + /// currency + /// (integer, not float/double). + /// + /// For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter + /// in currencies.json, it shows + /// the number of digits past the decimal point for each currency (2 for the majority of currencies). + /// + /// + [JsonProperty(Required = Required.Always)] + public int TotalAmount { + get; set; + } + + /// + /// Bot specified invoice payload + /// + [JsonProperty(Required = Required.Always)] + public string InvoicePayload { get; set; } = default!; + + /// + /// Optional. Identifier of the shipping option chosen by the user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ShippingOptionId { + get; set; + } + + /// + /// Optional. Order info provided by the user + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public OrderInfo? OrderInfo { + get; set; + } + + /// + /// Telegram payment identifier + /// + [JsonProperty(Required = Required.Always)] + public string TelegramPaymentChargeId { get; set; } = default!; + + /// + /// Provider payment identifier + /// + [JsonProperty(Required = Required.Always)] + public string ProviderPaymentChargeId { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/PhotoSize.cs b/TelegramBot/Types/PhotoSize.cs new file mode 100644 index 0000000..5cea067 --- /dev/null +++ b/TelegramBot/Types/PhotoSize.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents one size of a photo or a file / sticker thumbnail. + /// + /// A missing thumbnail for a file (or sticker) is presented as an empty object. + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class PhotoSize : FileBase { + /// + /// Photo width + /// + [JsonProperty(Required = Required.Always)] + public int Width { + get; set; + } + + /// + /// Photo height + /// + [JsonProperty(Required = Required.Always)] + public int Height { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Poll.cs b/TelegramBot/Types/Poll.cs new file mode 100644 index 0000000..a2d49a1 --- /dev/null +++ b/TelegramBot/Types/Poll.cs @@ -0,0 +1,114 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object contains information about a poll. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Poll { + /// + /// Unique poll identifier + /// + [JsonProperty(Required = Required.Always)] + public string Id { get; set; } = default!; + + /// + /// Poll question, 1-300 characters + /// + [JsonProperty(Required = Required.Always)] + public string Question { get; set; } = default!; + + /// + /// List of poll options + /// + [JsonProperty(Required = Required.Always)] + public PollOption[] Options { get; set; } = default!; + + /// + /// Total number of users that voted in the poll + /// + [JsonProperty(Required = Required.Always)] + public int TotalVoterCount { + get; set; + } + + /// + /// True, if the poll is closed + /// + [JsonProperty(Required = Required.Always)] + public bool IsClosed { + get; set; + } + + /// + /// True, if the poll is anonymous + /// + [JsonProperty(Required = Required.Always)] + public bool IsAnonymous { + get; set; + } + + /// + /// Poll type, currently can be “regular” or “quiz” + /// + [JsonProperty(Required = Required.Always)] + public string Type { get; set; } = default!; + + /// + /// True, if the poll allows multiple answers + /// + [JsonProperty(Required = Required.Always)] + public bool AllowsMultipleAnswers { + get; set; + } + + /// + /// Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, + /// which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? CorrectOptionId { + get; set; + } + + /// + /// Optional. 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 + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Explanation { + get; set; + } + + /// + /// Optional. Special entities like usernames, URLs, bot commands, etc. that appear in the + /// + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MessageEntity[]? ExplanationEntities { + get; set; + } + + /// + /// Optional. Amount of time in seconds the poll will be active after creation + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? OpenPeriod { + get; set; + } + + /// + /// Optional. Point in time when the poll will be automatically closed + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? CloseDate { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/PollAnswer.cs b/TelegramBot/Types/PollAnswer.cs new file mode 100644 index 0000000..417047f --- /dev/null +++ b/TelegramBot/Types/PollAnswer.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents an answer of a user in a non-anonymous poll. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class PollAnswer { + /// + /// Unique poll identifier + /// + [JsonProperty(Required = Required.Always)] + public string PollId { get; set; } = default!; + + /// + /// The user, who changed the answer to the poll + /// + [JsonProperty(Required = Required.Always)] + public User User { get; set; } = default!; + + /// + /// 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote. + /// + [JsonProperty(Required = Required.Always)] + public int[] OptionIds { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/PollOption.cs b/TelegramBot/Types/PollOption.cs new file mode 100644 index 0000000..28c0d5f --- /dev/null +++ b/TelegramBot/Types/PollOption.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object contains information about one answer option in a poll. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class PollOption { + /// + /// Option text, 1-100 characters + /// + [JsonProperty(Required = Required.Always)] + public string Text { get; set; } = default!; + + /// + /// Number of users that voted for this option + /// + [JsonProperty(Required = Required.Always)] + public int VoterCount { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ProximityAlertTriggered.cs b/TelegramBot/Types/ProximityAlertTriggered.cs new file mode 100644 index 0000000..972820b --- /dev/null +++ b/TelegramBot/Types/ProximityAlertTriggered.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + /// + /// Represents the content of a service message, sent whenever a user in the chat triggers a proximity alert set + /// by another user. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ProximityAlertTriggered { + /// + /// User that triggered the alert + /// + [JsonProperty(Required = Required.Always)] + public User Traveler { get; set; } = default!; + + /// + /// User that set the alert + /// + [JsonProperty(Required = Required.Always)] + public User Watcher { get; set; } = default!; + + /// + /// The distance between the users + /// + [JsonProperty(Required = Required.Always)] + public int Distance { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/ForceReplyMarkup.cs b/TelegramBot/Types/ReplyMarkups/ForceReplyMarkup.cs new file mode 100644 index 0000000..9ef42cb --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/ForceReplyMarkup.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// Upon receiving a with this object, Telegram clients will display a reply interface to the + /// user (act as if the user has selected the bot’s message and tapped 'Reply'). This can be extremely useful if you + /// want to create user-friendly step-by-step interfaces without having to sacrifice + /// privacy mode. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ForceReplyMarkup : ReplyMarkupBase { + /// + /// Shows reply interface to the user, as if they manually selected the bot’s message and tapped 'Reply' + /// + [JsonProperty(Required = Required.Always)] + public bool ForceReply => true; + + /// + /// Optional. The placeholder to be shown in the input field when the reply is active; 1-64 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? InputFieldPlaceholder { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/IKeyboardButton.cs b/TelegramBot/Types/ReplyMarkups/IKeyboardButton.cs new file mode 100644 index 0000000..6acc044 --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/IKeyboardButton.cs @@ -0,0 +1,15 @@ +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// Marker interface for a regular or inline button of the reply keyboard + /// + public interface IKeyboardButton { + /// + /// Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed + /// + string Text { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/IReplyMarkup.cs b/TelegramBot/Types/ReplyMarkups/IReplyMarkup.cs new file mode 100644 index 0000000..d18a124 --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/IReplyMarkup.cs @@ -0,0 +1,9 @@ +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// A marker interface for reply markups that define how a can reply to the sent + /// + public interface IReplyMarkup { + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/InlineKeyboardButton.cs b/TelegramBot/Types/ReplyMarkups/InlineKeyboardButton.cs new file mode 100644 index 0000000..f1b4d1d --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/InlineKeyboardButton.cs @@ -0,0 +1,239 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// This object represents one button of an inline keyboard. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineKeyboardButton : IKeyboardButton { + /// + [JsonProperty(Required = Required.Always)] + public string Text { + get; set; + } + + /// + /// Optional. HTTP or tg:// url to be opened when button is pressed + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Url { + get; set; + } + + /// + /// Optional. An HTTP URL used to automatically authorize the user. Can be used as a replacement for the + /// Telegram Login Widget. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public LoginUrl? LoginUrl { + get; set; + } + + /// + /// Optional. Data to be sent in a callback query to the bot when button + /// is pressed, 1-64 bytes + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? CallbackData { + get; set; + } + + /// + /// Optional. Description of the Web App that will be launched when the user presses the button. The Web App will + /// be able to send an arbitrary message on behalf of the user using the request + /// . Available only in private chats between a user and the bot. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public WebAppInfo? WebApp { + get; set; + } + + /// + /// Optional. If set, pressing the button will prompt the user to select one of their chats, open that chat and + /// insert the bot’s username and the specified inline query in the input field. Can be empty, in which case just + /// the bot’s username will be inserted. + /// + /// + /// Note: This offers an easy way for users to start using your bot in + /// inline mode when they are currently in a private chat + /// with it. Especially useful when combined with SwitchPm… + /// actions – in this case the user will be automatically returned to the chat they switched from, skipping the + /// chat selection screen. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? SwitchInlineQuery { + get; set; + } + + /// + /// Optional. If set, pressing the button will insert the bot’s username and the specified inline query in the + /// current chat’s input field. Can be empty, in which case only the bot’s username will be inserted. + /// + /// + /// This offers a quick way for the user to open your bot in inline mode in the same chat – good for selecting + /// something from multiple options. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? SwitchInlineQueryCurrentChat { + get; set; + } + + /// + /// Optional. Description of the game that will be launched when the user presses the button. + /// + /// + /// NOTE: This type of button must always be the first button in the first row. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public CallbackGame? CallbackGame { + get; set; + } + + /// + /// Optional. Specify True, to send a Pay button. + /// + /// + /// NOTE: This type of button must always be the first button in the first row. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? Pay { + get; set; + } + + /// + /// Instantiates new Inline Keyboard object + /// + /// Label text on the button + [JsonConstructor] + public InlineKeyboardButton(string text) { + Text = text; + } + + /// + /// Creates an inline keyboard button that opens a HTTP url when pressed + /// + /// Label text on the button + /// HTTP or tg:// url to be opened when button is pressed + public static InlineKeyboardButton WithUrl(string text, string url) => + new(text) { + Url = url + }; + + /// + /// Creates an inline keyboard button that opens a HTTP url to automatically authorize the user + /// + /// Label text on the button + /// + /// An HTTP URL used to automatically authorize the user. Can be used as a replacement for the + /// Telegram Login Widget. + /// + /// + public static InlineKeyboardButton WithLoginUrl(string text, LoginUrl loginUrl) => + new(text) { + LoginUrl = loginUrl + }; + + /// + /// Creates an inline keyboard button that sends to bot when pressed + /// + /// + /// Text and data of the button to be sent in a callback query to the bot when + /// button is pressed, 1-64 bytes + /// + public static InlineKeyboardButton WithCallbackData(string textAndCallbackData) => + new(textAndCallbackData) { + CallbackData = textAndCallbackData + }; + + /// + /// Creates an inline keyboard button that sends to bot when pressed + /// + /// Label text on the button + /// + /// Data to be sent in a callback query to the bot when button is pressed, + /// 1-64 bytes + /// + public static InlineKeyboardButton WithCallbackData(string text, string callbackData) => + new(text) { + CallbackData = callbackData + }; + + /// + /// Creates an inline keyboard button. Pressing the button will prompt the user to select one of their chats, + /// open that chat and insert the bot’s username and the specified inline query in the input field. + /// + /// Label text on the button + /// + /// If set, pressing the button will prompt the user to select one of their chats, open that chat and insert + /// the bot’s username and the specified inline query in the input field. Can be empty, in which case just the + /// bot’s username will be inserted. + /// + /// + public static InlineKeyboardButton WithSwitchInlineQuery(string text, string query = "") => + new(text) { + SwitchInlineQuery = query + }; + + /// + /// Creates an inline keyboard button. Pressing the button will insert the bot’s username and the specified inline + /// query in the current chat’s input field. + /// + /// Label text on the button + /// + /// If set, pressing the button will insert the bot’s username and the specified inline query in the current + /// chat’s input field. Can be empty, in which case only the bot’s username will be inserted. + /// + public static InlineKeyboardButton WithSwitchInlineQueryCurrentChat(string text, string query = "") => + new(text) { + SwitchInlineQueryCurrentChat = query + }; + + /// + /// Creates an inline keyboard button. Pressing the button will launch the game. + /// + /// Label text on the button + /// + /// Description of the game that will be launched when the user presses the button. + /// + public static InlineKeyboardButton WithCallBackGame(string text, CallbackGame? callbackGame = default) => + new(text) { + CallbackGame = callbackGame ?? new() + }; + + /// + /// Creates an inline keyboard button for a PayButton + /// + /// Label text on the button + public static InlineKeyboardButton WithPayment(string text) => + new(text) { + Pay = true + }; + + /// + /// Generate an inline keyboard button to request a web app + /// + /// Button's text + /// Web app information + /// + public static InlineKeyboardButton WithWebApp(string text, WebAppInfo webAppInfo) => + new(text) { + WebApp = webAppInfo + }; + + /// + /// Performs an implicit conversion from to + /// with callback data + /// + /// Label text and callback data of the button + /// + /// The result of the conversion. + /// + public static implicit operator InlineKeyboardButton?(string? textAndCallbackData) => + textAndCallbackData is null + ? default + : WithCallbackData(textAndCallbackData); + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/InlineKeyboardMarkup.cs b/TelegramBot/Types/ReplyMarkups/InlineKeyboardMarkup.cs new file mode 100644 index 0000000..ee197ad --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/InlineKeyboardMarkup.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// This object represents an inline keyboard that appears right next to the it belongs to. + /// + /// + /// Note: This will only work in Telegram versions released after 9 April, 2016. Older clients will display + /// unsupported message. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class InlineKeyboardMarkup : IReplyMarkup { + /// + /// Array of rows, each represented by an Array of + /// . + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable> InlineKeyboard { + get; + } + + /// + /// Initializes a new instance of the class with only one keyboard button + /// + /// Keyboard button + public InlineKeyboardMarkup(InlineKeyboardButton inlineKeyboardButton) + : this(new[] { inlineKeyboardButton }) { + } + + /// + /// Initializes a new instance of the class with a one-row keyboard + /// + /// The inline keyboard row + public InlineKeyboardMarkup(IEnumerable inlineKeyboardRow) + : this(new[] { inlineKeyboardRow }) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The inline keyboard. + [JsonConstructor] + public InlineKeyboardMarkup(IEnumerable> inlineKeyboard) => + InlineKeyboard = inlineKeyboard; + + /// + /// Generate an empty inline keyboard markup + /// + /// Empty inline keyboard markup + public static InlineKeyboardMarkup Empty() => + new(Array.Empty()); + + /// + /// Generate an inline keyboard markup with one button + /// + /// Inline keyboard button + [return: NotNullIfNotNull("button")] + public static implicit operator InlineKeyboardMarkup?(InlineKeyboardButton? button) => + button is null ? default : new(button); + + /// + /// Generate an inline keyboard markup with one button + /// + /// Text of the button + [return: NotNullIfNotNull("buttonText")] + public static implicit operator InlineKeyboardMarkup?(string? buttonText) => + buttonText is null ? default : new(buttonText!); + + /// + /// Generate an inline keyboard markup from multiple buttons + /// + /// Keyboard buttons + [return: NotNullIfNotNull("inlineKeyboard")] + public static implicit operator InlineKeyboardMarkup?(IEnumerable[]? inlineKeyboard) => + inlineKeyboard is null ? default : new(inlineKeyboard); + + /// + /// Generate an inline keyboard markup from multiple buttons on 1 row + /// + /// Keyboard buttons + [return: NotNullIfNotNull("inlineKeyboard")] + public static implicit operator InlineKeyboardMarkup?(InlineKeyboardButton[]? inlineKeyboard) => + inlineKeyboard is null ? default : new(inlineKeyboard); + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/KeyboardButton.cs b/TelegramBot/Types/ReplyMarkups/KeyboardButton.cs new file mode 100644 index 0000000..dd7760b --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/KeyboardButton.cs @@ -0,0 +1,130 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// This object represents one button of the reply keyboard. For simple text buttons can be + /// used instead of this object to specify text of the button. + /// + /// + /// + /// Note: and options will only work in Telegram + /// versions released after 9 April, 2016. Older clients will display unsupported message. + /// + /// + /// Note: option will only work in Telegram versions released after 23 January, 2020. + /// Older clients will display unsupported message. + /// + /// + /// Note: option will only work in Telegram versions released after 16 April, 2022. Older + /// clients will display unsupported message. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class KeyboardButton : IKeyboardButton { + /// + [JsonProperty(Required = Required.Always)] + public string Text { + get; set; + } + + /// + /// Optional. If true, the user's phone number will be sent as a contact when the button is pressed. + /// Available in private chats only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? RequestContact { + get; set; + } + + /// + /// Optional. If true, the user's current location will be sent when the button is pressed. + /// Available in private chats only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? RequestLocation { + get; set; + } + + /// + /// Optional. If specified, the user will be asked to create a poll and send it to the bot when the button + /// is pressed. Available in private chats only + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public KeyboardButtonPollType? RequestPoll { + get; set; + } + + /// + /// Optional. If specified, the described Web App will be launched when the button is pressed. The Web App will + /// be able to send a “web_app_data” service message. Available in private chats only. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public WebAppInfo? WebApp { + get; set; + } + + /// + /// Initializes a new instance of the class. + /// + /// Label text on the button + [JsonConstructor] + public KeyboardButton(string text) { + Text = text; + } + + /// + /// Generate a keyboard button to request for contact + /// + /// Button's text + /// Keyboard button + public static KeyboardButton WithRequestContact(string text) => + new(text) { + RequestContact = true + }; + + /// + /// Generate a keyboard button to request for location + /// + /// Button's text + /// Keyboard button + public static KeyboardButton WithRequestLocation(string text) => + new(text) { + RequestLocation = true + }; + + /// + /// Generate a keyboard button to request a poll + /// + /// Button's text + /// Poll's type + /// Keyboard button + public static KeyboardButton WithRequestPoll(string text, string? type = default) => + new(text) { + RequestPoll = new() { + Type = type + } + }; + + /// + /// Generate a keyboard button to request a web app + /// + /// Button's text + /// Web app information + /// + public static KeyboardButton WithWebApp(string text, WebAppInfo webAppInfo) => + new(text) { + WebApp = webAppInfo + }; + + /// + /// Generate a keyboard button from text + /// + /// Button's text + /// Keyboard button + public static implicit operator KeyboardButton(string text) + => new(text); + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/KeyboardButtonPollType.cs b/TelegramBot/Types/ReplyMarkups/KeyboardButtonPollType.cs new file mode 100644 index 0000000..d17cbcd --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/KeyboardButtonPollType.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// This object represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class KeyboardButtonPollType { + /// + /// Optional. If quiz is passed, the user will be allowed to create only polls in the quiz mode. If regular is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Type { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/ReplyKeyboardMarkup.cs b/TelegramBot/Types/ReplyMarkups/ReplyKeyboardMarkup.cs new file mode 100644 index 0000000..7af3c06 --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/ReplyKeyboardMarkup.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// Represents a custom keyboard with reply options + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ReplyKeyboardMarkup : ReplyMarkupBase { + /// + /// Array of button rows, each represented by an Array of KeyboardButton objects + /// + [JsonProperty(Required = Required.Always)] + public IEnumerable> Keyboard { + get; set; + } + + /// + /// Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? ResizeKeyboard { + get; set; + } + + /// + /// Optional. Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again. Defaults to false. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? OneTimeKeyboard { + get; set; + } + + /// + /// Optional. The placeholder to be shown in the input field when the keyboard is active; 1-64 characters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? InputFieldPlaceholder { + get; set; + } + + /// + /// Initializes a new instance of with one button + /// + /// Button on keyboard + public ReplyKeyboardMarkup(KeyboardButton button) + : this(new[] { button }) { + } + + /// + /// Initializes a new instance of + /// + /// The keyboard row. + public ReplyKeyboardMarkup(IEnumerable keyboardRow) + : this(new[] { keyboardRow }) { + } + + /// + /// Initializes a new instance of the class. + /// + /// The keyboard. + [JsonConstructor] + public ReplyKeyboardMarkup(IEnumerable> keyboard) { + Keyboard = keyboard; + } + + /// + /// Generates a reply keyboard markup with one button + /// + /// Button's text + public static implicit operator ReplyKeyboardMarkup?(string? text) => + text is null + ? default + : new(new[] { new KeyboardButton(text) }); + + /// + /// Generates a reply keyboard markup with multiple buttons on one row + /// + /// Texts of buttons + public static implicit operator ReplyKeyboardMarkup?(string[]? texts) => + texts is null + ? default + : new[] { texts }; + + /// + /// Generates a reply keyboard markup with multiple buttons + /// + /// Texts of buttons + public static implicit operator ReplyKeyboardMarkup?(string[][]? textsItems) => + textsItems is null + ? default + : new ReplyKeyboardMarkup( + textsItems.Select(texts => + texts.Select(t => new KeyboardButton(t)) + )); + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/ReplyKeyboardRemove.cs b/TelegramBot/Types/ReplyMarkups/ReplyKeyboardRemove.cs new file mode 100644 index 0000000..efc92df --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/ReplyKeyboardRemove.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button (see ). + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ReplyKeyboardRemove : ReplyMarkupBase { + /// + /// Requests clients to remove the custom keyboard (user will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use '' in ) + /// + [JsonProperty(Required = Required.Always)] + public bool RemoveKeyboard => true; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ReplyMarkups/ReplyMarkupBase.cs b/TelegramBot/Types/ReplyMarkups/ReplyMarkupBase.cs new file mode 100644 index 0000000..2e44837 --- /dev/null +++ b/TelegramBot/Types/ReplyMarkups/ReplyMarkupBase.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types.ReplyMarkups { + + + /// + /// Defines how clients display a reply interface to the + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public abstract class ReplyMarkupBase : IReplyMarkup { + /// + /// Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: + /// + /// + /// users that are @mentioned in the of the object; + /// + /// + /// if the bot’s message is a reply (has ), sender of the original + /// message. + /// + /// + /// + /// + /// Example: A user requests to change the bot’s language, bot replies to the request with a keyboard + /// to select the new language. Other users in the group don't see the keyboard. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? Selective { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/ResponseParameters.cs b/TelegramBot/Types/ResponseParameters.cs new file mode 100644 index 0000000..31a3ebd --- /dev/null +++ b/TelegramBot/Types/ResponseParameters.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Contains information about why a request was unsuccessful. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class ResponseParameters { + /// + /// The group has been migrated to a supergroup with the specified identifier. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public long? MigrateToChatId { + get; set; + } + + /// + /// In case of exceeding flood control, the number of seconds left to wait before the request can be repeated. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? RetryAfter { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/SentWebAppMessage.cs b/TelegramBot/Types/SentWebAppMessage.cs new file mode 100644 index 0000000..6b0dcfc --- /dev/null +++ b/TelegramBot/Types/SentWebAppMessage.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Contains information about an inline message sent by a + /// Web App on behalf of a user. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class SentWebAppMessage { + /// + /// Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached + /// to the message. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? InlineMessageId { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Sticker.cs b/TelegramBot/Types/Sticker.cs new file mode 100644 index 0000000..cfcfb1a --- /dev/null +++ b/TelegramBot/Types/Sticker.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a sticker. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Sticker : FileBase { + /// + /// Sticker width + /// + [JsonProperty(Required = Required.Always)] + public int Width { + get; set; + } + + /// + /// Sticker height + /// + [JsonProperty(Required = Required.Always)] + public int Height { + get; set; + } + + /// + /// true, if the sticker is animated + /// + [JsonProperty(Required = Required.Always)] + public bool IsAnimated { + get; set; + } + + /// + /// true, if the sticker is a video sticker + /// + [JsonProperty(Required = Required.Always)] + public bool IsVideo { + get; set; + } + + /// + /// Optional. Sticker thumbnail in the .WEBP or .JPG format + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PhotoSize? Thumb { + get; set; + } + + /// + /// Optional. Emoji associated with the sticker + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Emoji { + get; set; + } + + /// + /// Optional. Name of the sticker set to which the sticker belongs + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? SetName { + get; set; + } + + /// + /// Optional. For mask stickers, the position where the mask should be placed + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public MaskPosition? MaskPosition { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/StickerSet.cs b/TelegramBot/Types/StickerSet.cs new file mode 100644 index 0000000..006dbc3 --- /dev/null +++ b/TelegramBot/Types/StickerSet.cs @@ -0,0 +1,63 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a sticker set. + /// + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class StickerSet { + /// + /// Sticker set name + /// + [JsonProperty(Required = Required.Always)] + public string Name { get; set; } = default!; + + /// + /// Sticker set title + /// + [JsonProperty(Required = Required.Always)] + public string Title { get; set; } = default!; + + /// + /// True, if the sticker set contains animated stickers + /// + [JsonProperty(Required = Required.Always)] + public bool IsAnimated { + get; set; + } + + /// + /// true, if the sticker set contains video stickers + /// + [JsonProperty(Required = Required.Always)] + public bool IsVideo { + get; set; + } + + /// + /// True, if the sticker set contains masks + /// + [JsonProperty(Required = Required.Always)] + public bool ContainsMasks { + get; set; + } + + /// + /// List of all set stickers + /// + [JsonProperty(Required = Required.Always)] + public Sticker[] Stickers { get; set; } = default!; + + /// + /// Optional. Sticker set thumbnail in the .WEBP or .TGS format + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PhotoSize? Thumb { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Update.cs b/TelegramBot/Types/Update.cs new file mode 100644 index 0000000..7f2a5bd --- /dev/null +++ b/TelegramBot/Types/Update.cs @@ -0,0 +1,169 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.Payments; +namespace Telegram.Bot.Types { + + + /// + /// This object represents an incoming update. + /// + /// + /// Only one of the optional parameters can be present in any given update. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Update { + /// + /// The update's unique identifier. Update identifiers start from a certain positive number and increase + /// sequentially. This ID becomes especially handy if you're using + /// Webhooks, since it allows you to ignore repeated + /// updates or to restore the correct update sequence, should they get out of order. If there are no new updates + /// for at least a week, then identifier of the next update will be chosen randomly instead of sequentially. + /// + [JsonProperty("update_id", Required = Required.Always)] + public int Id { + get; set; + } + + /// + /// Optional. New incoming message of any kind — text, photo, sticker, etc. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Message? Message { + get; set; + } + + /// + /// Optional. New version of a message that is known to the bot and was edited + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Message? EditedMessage { + get; set; + } + + /// + /// Optional. New incoming channel post of any kind — text, photo, sticker, etc. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Message? ChannelPost { + get; set; + } + + /// + /// Optional. New version of a channel post that is known to the bot and was edited + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Message? EditedChannelPost { + get; set; + } + + /// + /// Optional. New incoming inline query + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public InlineQuery? InlineQuery { + get; set; + } + + /// + /// Optional. The result of a inline query that was chosen by a user and sent to their chat partner + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChosenInlineResult? ChosenInlineResult { + get; set; + } + + /// + /// Optional. New incoming callback query + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public CallbackQuery? CallbackQuery { + get; set; + } + + /// + /// Optional. New incoming shipping query. Only for invoices with flexible price + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ShippingQuery? ShippingQuery { + get; set; + } + + /// + /// Optional. New incoming pre-checkout query. Contains full information about checkout + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PreCheckoutQuery? PreCheckoutQuery { + get; set; + } + + /// + /// Optional. New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Poll? Poll { + get; set; + } + + /// + /// Optional. A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were + /// sent by the bot itself. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PollAnswer? PollAnswer { + get; set; + } + + /// + /// Optional. The bot’s chat member status was updated in a chat. For private chats, this update is received + /// only when the bot is blocked or unblocked by the user. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatMemberUpdated? MyChatMember { + get; set; + } + + /// + /// Optional. A chat member's status was updated in a chat. The bot must be an administrator in the chat + /// and must explicitly specify “” in the list of allowed_updates to + /// receive these updates. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatMemberUpdated? ChatMember { + get; set; + } + + /// + /// Optional. A request to join the chat has been sent. The bot must have the + /// administrator right in the chat to receive these updates. + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public ChatJoinRequest? ChatJoinRequest { + get; set; + } + + /// + /// Gets the update type. + /// + /// + /// The update type. + /// + public UpdateType Type => this switch { + { Message: { } } => UpdateType.Message, + { EditedMessage: { } } => UpdateType.EditedMessage, + { InlineQuery: { } } => UpdateType.InlineQuery, + { ChosenInlineResult: { } } => UpdateType.ChosenInlineResult, + { CallbackQuery: { } } => UpdateType.CallbackQuery, + { ChannelPost: { } } => UpdateType.ChannelPost, + { EditedChannelPost: { } } => UpdateType.EditedChannelPost, + { ShippingQuery: { } } => UpdateType.ShippingQuery, + { PreCheckoutQuery: { } } => UpdateType.PreCheckoutQuery, + { Poll: { } } => UpdateType.Poll, + { PollAnswer: { } } => UpdateType.PollAnswer, + { MyChatMember: { } } => UpdateType.MyChatMember, + { ChatMember: { } } => UpdateType.ChatMember, + { ChatJoinRequest: { } } => UpdateType.ChatJoinRequest, + _ => UpdateType.Unknown + }; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/User.cs b/TelegramBot/Types/User.cs new file mode 100644 index 0000000..3b38866 --- /dev/null +++ b/TelegramBot/Types/User.cs @@ -0,0 +1,87 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a Telegram user or bot. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class User { + /// + /// Unique identifier for this user or bot + /// + [JsonProperty(Required = Required.Always)] + public long Id { + get; set; + } + + /// + /// True, if this user is a bot + /// + [JsonProperty(Required = Required.Always)] + public bool IsBot { + get; set; + } + + /// + /// User's or bot’s first name + /// + [JsonProperty(Required = Required.Always)] + public string FirstName { get; set; } = default!; + + /// + /// Optional. User's or bot’s last name + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LastName { + get; set; + } + + /// + /// Optional. User's or bot’s username + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? Username { + get; set; + } + + /// + /// Optional. IETF language tag of the + /// user's language + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LanguageCode { + get; set; + } + + /// + /// Optional. True, if the bot can be invited to groups. Returned only in + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanJoinGroups { + get; set; + } + + /// + /// Optional. True, if privacy mode is disabled for the bot. Returned only in + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? CanReadAllGroupMessages { + get; set; + } + + /// + /// Optional. True, if the bot supports inline queries. Returned only in + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public bool? SupportsInlineQueries { + get; set; + } + + /// + public override string ToString() => + $"{(Username is null ? $"{FirstName}{LastName?.Insert(0, " ")}" : $"@{Username}")} ({Id})"; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/UserProfilePhotos.cs b/TelegramBot/Types/UserProfilePhotos.cs new file mode 100644 index 0000000..862d555 --- /dev/null +++ b/TelegramBot/Types/UserProfilePhotos.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represent a user's profile pictures. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class UserProfilePhotos { + /// + /// Total number of profile pictures the target user has + /// + [JsonProperty(Required = Required.Always)] + public int TotalCount { + get; set; + } + + /// + /// Requested profile pictures (in up to 4 sizes each) + /// + [JsonProperty(Required = Required.Always)] + public PhotoSize[][] Photos { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Venue.cs b/TelegramBot/Types/Venue.cs new file mode 100644 index 0000000..1e5905f --- /dev/null +++ b/TelegramBot/Types/Venue.cs @@ -0,0 +1,64 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a venue. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Venue { + /// + /// Venue location + /// + [JsonProperty(Required = Required.Always)] + public Location Location { get; set; } = default!; + + /// + /// Name of the venue + /// + [JsonProperty(Required = Required.Always)] + public string Title { get; set; } = default!; + + /// + /// Address of the venue + /// + [JsonProperty(Required = Required.Always)] + public string Address { get; set; } = default!; + + /// + /// Optional. Foursquare identifier of the venue + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FoursquareId { + get; set; + } + + /// + /// Optional. Foursquare type of the venue. (For example, "arts_entertainment/default", + /// "arts_entertainment/aquarium" or "food/icecream".) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FoursquareType { + get; set; + } + + /// + /// Optional. Google Places identifier of the venue + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GooglePlaceId { + get; set; + } + + /// + /// Optional. Google Places type of the venue. (See + /// supported types.) + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GooglePlaceType { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Video.cs b/TelegramBot/Types/Video.cs new file mode 100644 index 0000000..af2f8f6 --- /dev/null +++ b/TelegramBot/Types/Video.cs @@ -0,0 +1,60 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a video file. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Video : FileBase { + /// + /// Video width as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Width { + get; set; + } + + /// + /// Video height as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Height { + get; set; + } + + /// + /// Duration of the video in seconds as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Duration { + get; set; + } + + /// + /// Optional. Video thumbnail + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PhotoSize? Thumb { + get; set; + } + + /// + /// Optional. Original filename as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? FileName { + get; set; + } + + /// + /// Optional. Mime type of a file as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? MimeType { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VideoChatEnded.cs b/TelegramBot/Types/VideoChatEnded.cs new file mode 100644 index 0000000..8e09538 --- /dev/null +++ b/TelegramBot/Types/VideoChatEnded.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about a video chat ended in the chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VideoChatEnded { + /// + /// Video chat duration; in seconds + /// + [JsonProperty(Required = Required.Always)] + public int Duration { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VideoChatParticipantsInvited.cs b/TelegramBot/Types/VideoChatParticipantsInvited.cs new file mode 100644 index 0000000..e41ad16 --- /dev/null +++ b/TelegramBot/Types/VideoChatParticipantsInvited.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about new members invited to a video chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VideoChatParticipantsInvited { + /// + /// Optional. New members that were invited to the voice chat + /// + [JsonProperty(Required = Required.Always)] + public User[] Users { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VideoChatScheduled.cs b/TelegramBot/Types/VideoChatScheduled.cs new file mode 100644 index 0000000..4c7a40d --- /dev/null +++ b/TelegramBot/Types/VideoChatScheduled.cs @@ -0,0 +1,23 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about a video chat scheduled in the chat. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VideoChatScheduled { + /// + /// Point in time when the voice chat is supposed to be started by a chat administrator + /// + [JsonProperty(Required = Required.Always)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime StartDate { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VideoChatStarted.cs b/TelegramBot/Types/VideoChatStarted.cs new file mode 100644 index 0000000..58e4371 --- /dev/null +++ b/TelegramBot/Types/VideoChatStarted.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about a video chat started in the chat. Currently holds no information. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VideoChatStarted { + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VideoNote.cs b/TelegramBot/Types/VideoNote.cs new file mode 100644 index 0000000..40d2cc9 --- /dev/null +++ b/TelegramBot/Types/VideoNote.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a video message + /// (available in Telegram apps as of + /// v.4.0). + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VideoNote : FileBase { + /// + /// Video width and height (diameter of the video message) as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Length { + get; set; + } + + /// + /// Duration of the video in seconds as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Duration { + get; set; + } + + /// + /// Optional. Video thumbnail + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public PhotoSize? Thumb { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/Voice.cs b/TelegramBot/Types/Voice.cs new file mode 100644 index 0000000..9633b91 --- /dev/null +++ b/TelegramBot/Types/Voice.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a voice note. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class Voice : FileBase { + /// + /// Duration of the audio in seconds as defined by sender + /// + [JsonProperty(Required = Required.Always)] + public int Duration { + get; set; + } + + /// + /// Optional. MIME type of the file as defined by sender + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? MimeType { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VoiceChatEnded.cs b/TelegramBot/Types/VoiceChatEnded.cs new file mode 100644 index 0000000..2a14087 --- /dev/null +++ b/TelegramBot/Types/VoiceChatEnded.cs @@ -0,0 +1,22 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about a voice chat ended in the chat. + /// + [Obsolete("This type will be removed in the next major version, use VoiceChatEnded instead")] + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VoiceChatEnded { + /// + /// Voice chat duration; in seconds + /// + [JsonProperty(Required = Required.Always)] + public int Duration { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VoiceChatParticipantsInvited.cs b/TelegramBot/Types/VoiceChatParticipantsInvited.cs new file mode 100644 index 0000000..51a843d --- /dev/null +++ b/TelegramBot/Types/VoiceChatParticipantsInvited.cs @@ -0,0 +1,20 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about new members invited to a voice chat. + /// + [Obsolete("This type will be removed in the next major version, use VideoChatParticipantsInvited instead")] + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VoiceChatParticipantsInvited { + /// + /// Optional. New members that were invited to the voice chat + /// + [JsonProperty(Required = Required.Always)] + public User[] Users { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VoiceChatScheduled.cs b/TelegramBot/Types/VoiceChatScheduled.cs new file mode 100644 index 0000000..a8e3e76 --- /dev/null +++ b/TelegramBot/Types/VoiceChatScheduled.cs @@ -0,0 +1,24 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about a voice chat scheduled in the chat. + /// + [Obsolete("This type will be removed in the next major version, use VideoChatScheduled instead")] + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VoiceChatScheduled { + /// + /// Point in time when the voice chat is supposed to be started by a chat administrator + /// + [JsonProperty(Required = Required.Always)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime StartDate { + get; set; + } + } +} \ No newline at end of file diff --git a/TelegramBot/Types/VoiceChatStarted.cs b/TelegramBot/Types/VoiceChatStarted.cs new file mode 100644 index 0000000..813ef1f --- /dev/null +++ b/TelegramBot/Types/VoiceChatStarted.cs @@ -0,0 +1,15 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// This object represents a service message about a voice chat started in the chat. Currently holds no information. + /// + [Obsolete("This type will be removed in the next major version, use VoiceChatStarted instead")] + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class VoiceChatStarted { + } +} \ No newline at end of file diff --git a/TelegramBot/Types/WebAppData.cs b/TelegramBot/Types/WebAppData.cs new file mode 100644 index 0000000..d344813 --- /dev/null +++ b/TelegramBot/Types/WebAppData.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Contains data sent from a Web App to the bot. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class WebAppData { + /// + /// The data. Be aware that a bad client can send arbitrary data in this field. + /// + [JsonProperty(Required = Required.Always)] + public string Data { get; set; } = default!; + + /// + /// Text of the web_app keyboard button, from which the Web App was opened. Be aware that a bad client can + /// send arbitrary data in this field. + /// + [JsonProperty(Required = Required.Always)] + public string ButtonText { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/WebAppInfo.cs b/TelegramBot/Types/WebAppInfo.cs new file mode 100644 index 0000000..2f6589c --- /dev/null +++ b/TelegramBot/Types/WebAppInfo.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Telegram.Bot.Types { + + + /// + /// Contains information about a Web App + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class WebAppInfo { + /// + /// An HTTPS URL of a Web App to be opened with additional data as specified in + /// Initializing Web Apps + /// + [JsonProperty(Required = Required.Always)] + public string Url { get; set; } = default!; + } +} \ No newline at end of file diff --git a/TelegramBot/Types/WebhookInfo.cs b/TelegramBot/Types/WebhookInfo.cs new file mode 100644 index 0000000..4be3e88 --- /dev/null +++ b/TelegramBot/Types/WebhookInfo.cs @@ -0,0 +1,90 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Telegram.Bot.Types.Enums; + +namespace Telegram.Bot.Types { + + + /// + /// Contains information about the current status of a webhook. + /// + [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + public class WebhookInfo { + /// + /// Webhook URL, may be empty if webhook is not set up + /// + [JsonProperty(Required = Required.Always)] + public string Url { get; set; } = default!; + + /// + /// True, if a custom certificate was provided for webhook certificate checks + /// + [JsonProperty(Required = Required.Always)] + public bool HasCustomCertificate { + get; set; + } + + /// + /// Number of updates awaiting delivery + /// + [JsonProperty(Required = Required.Always)] + public int PendingUpdateCount { + get; set; + } + + /// + /// Optional. Currently used webhook IP address + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? IpAddress { + get; set; + } + + /// + /// Optional. Time for the most recent error that happened when trying to deliver an update via webhook + /// + [JsonConverter(typeof(UnixDateTimeConverter))] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime? LastErrorDate { + get; set; + } + + /// + /// Optional. Error message in human-readable format for the most recent error that happened when trying to + /// deliver an update via webhook + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? LastErrorMessage { + get; set; + } + + /// + /// Optional. Unix time of the most recent error that happened when trying to synchronize available updates + /// with Telegram datacenters + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonConverter(typeof(UnixDateTimeConverter))] + public DateTime? LastSynchronizationErrorDate { + get; set; + } + + /// + /// Optional. Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public int? MaxConnections { + get; set; + } + + /// + /// Optional. A list of update types the bot is subscribed to. Defaults to all update types except + /// + /// + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public UpdateType[]? AllowedUpdates { + get; set; + } + } +} \ No newline at end of file