Hassan Faghihi
Hassan Faghihi

Reputation: 2031

How to transfer file to and from Telegram using POCO (I don't want to use any middleware)

I'm currently working on a Telegram bot, recently i almost finished it's sensitive parts, and now i want to activate the webhook, but the webhook require me to send certificate file to the telegram before it get activated, also later i may want to send a file for our client, or receive their reply as a file (since we want to activate our website features to them through telegram).

Here is a reference to telegram bot APIs: https://core.telegram.org/bots/api#inputfile

I, myself, done all my api call through HttpClient class, and i wish to continue it as it is. here is my failed method:

    public static Exception SetWebhook(SetWebhook webhook)
    {
        try
        {
            using (var hc = new HttpClient())
            {
                HttpContent requestContent = new ObjectContent(typeof(SetWebhook), webhook,
                    new JsonMediaTypeFormatter
                    {
                        SerializerSettings = new JsonSerializerSettings
                        {
                            ContractResolver = new CustomPropertyNamesContractResolver
                            {
                                Case = IdentifierCase.UnderscoreSeparator
                            },
                            NullValueHandling = NullValueHandling.Ignore
                        },
                        SupportedEncodings = {Encoding.UTF8}
                    }, "multipart/form-data");

                var responseMessage =
                    hc.PostAsync("https://api.telegram.org/bot" + AppSetting.Token + "/setWebhook",
                        requestContent).Result;

                if (responseMessage.IsSuccessStatusCode)
                {
                    return null;
                }
                else
                {
                    return new Exception("Status Code: " + responseMessage.StatusCode + "\n\nRequest" + responseMessage.RequestMessage.ToString() + "\n\nResponse" + responseMessage.ToString() );
                }
            }
        }
        catch (Exception ex)
        {
            return ex;
        }
    }

And here are my models: Since telegram didn't defined the Certificate exact type, i take a look at this: https://github.com/MrRoundRobin/telegram.bot to generate it.

public class SetWebhook
{
    /// <summary>
    /// Optional<br/>
    /// If empty remove webhook
    /// </summary>
    public string Url { get; set; }

    /// <summary>
    /// Optional<br/>
    /// Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details.
    /// </summary>
    public InputFile Certificate { get; set; }
}

/// <summary>
/// Represents information for a file to be sent
/// </summary>
public class InputFile
{
    /// <summary>
    /// Required <b/>
    /// Gets or sets the filename.
    /// </summary>
    public string Filename { get; set; }

    /// <summary>
    /// Required <b/>
    /// Gets or sets the content.
    /// </summary>
    public Stream Content { get; set; }

    public InputFile()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="InputFile"/> class.
    /// </summary>
    /// <param name="filename">The <see cref="Filename"/>.</param>
    /// <param name="content">The <see cref="Content"/>.</param>
    public InputFile(string filename, Stream content)
    {
        Filename = filename;
        Content = content;
    }
}

and here is the way i call it:

public ActionResult SetWebhook()
    {
        var result = true;
        var text = "-";

        try
        {
            WebHook.SetWebhook("http://ravis.ir:444/Data/Message", Server.MapPath("~/Files/ravis.ir-PEM.cer"));
        }
        catch(Exception ex)
        {

            result = false;
            text = ex.Message;
        }

        return View(new WebhookResult
        {
            Result = result,
            Text = text
        });
    }

This last way, errors:

((ex.InnerException).InnerException).Message -> Timeouts are not supported on this stream.

(ex.InnerException).Message -> Error getting value from 'ReadTimeout' on 'System.IO.FileStream'.

ex.Message -> One or more errors occurred.

So how should i send file? how should i receive them? what kind of entity should i define to be more accurate?

Upvotes: 0

Views: 1022

Answers (1)

Aleksey L.
Aleksey L.

Reputation: 37986

In order to get this working you should construct MultipartFormDataContent with StreamContent inside (not an ObjectContent):

using (var httpClient = new HttpClient())
using (var form = new MultipartFormDataContent())
{
    var content = new StreamContent(setWebhook.Certificate.Content);
    form.Add(content, "certificate", setWebhook.Certificate.Filename);
    form.Add(new StringContent(setWebhook.Url, Encoding.UTF8), "url");

    var response = await httpClient.PostAsync(uri, form);
}

As for receiving files - first you should get "file info" containing file_path and then it can be downloaded:

var fileId = "fileId";

using (var httpClient = new HttpClient())
{
    var responseMessage = await httpClient.PostAsJsonAsync("https://api.telegram.org/bot" + token + "/getFile", new { file_id = fileId });
    responseMessage.EnsureSuccessStatusCode();
    var fileInfoResponse = await responseMessage.Content.ReadAsAsync<TelegramResponse<FileInfo>>();

    var fileUri = new Uri("https://api.telegram.org/file/bot" + token + "/" + fileInfoResponse.result.file_path);
    var fileStreamResponse = await httpClient.GetAsync(fileUri, HttpCompletionOption.ResponseHeadersRead);
    //and here's downloaded file
    var stream = await fileStreamResponse.Content.ReadAsStreamAsync();
}

Here TelegramResponse and FileInfo will look something like this (* not a C# convention, but you can fix this if you want):

class TelegramResponse<T>
{
    public bool ok { get; set; }
    public T result { get; set; }
}
class FileInfo
{
    public string file_path { get; set; }
}

Upvotes: 1

Related Questions