如何使用 POCO 在 Telegram 之间传输文件(我不想使用任何中间件)
How to transfer file to and from Telegram using POCO (I don't want to use any middleware)
我目前正在开发 Telegram 机器人,最近我几乎完成了它的敏感部分,现在我想激活 webhook,但是 webhook 要求我在激活之前将证书文件发送到电报,另外稍后我可能想为我们的客户发送一个文件,或者以文件的形式接收他们的回复(因为我们想通过电报向他们激活我们的网站功能)。
这是对电报机器人 API 的引用:
https://core.telegram.org/bots/api#inputfile
我本人通过 HttpClient class 完成了所有 api 调用,我希望继续保持原样。
这是我失败的方法:
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;
}
}
这是我的模型:
由于电报没有定义证书的确切类型,我看一下这个:https://github.com/MrRoundRobin/telegram.bot 来生成它。
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;
}
}
我是这样称呼它的:
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
});
}
这最后一种方式,错误:
((ex.InnerException).InnerException).Message
->
此流不支持超时。
(ex.InnerException).Message
->
在 'System.IO.FileStream' 从 'ReadTimeout' 获取值时出错。
ex.Message
->
出现一个或多个错误。
那么我该如何发送文件呢?我应该如何接收它们?我应该定义什么样的实体更准确?
为了让这个工作你应该构造 MultipartFormDataContent
里面有 StreamContent
(不是 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);
}
关于接收文件-首先你应该得到包含file_path
的"file info",然后才可以下载:
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();
}
此处 TelegramResponse
和 FileInfo
看起来像这样(* 不是 C# 约定,但如果需要,您可以修复此问题):
class TelegramResponse<T>
{
public bool ok { get; set; }
public T result { get; set; }
}
class FileInfo
{
public string file_path { get; set; }
}
我目前正在开发 Telegram 机器人,最近我几乎完成了它的敏感部分,现在我想激活 webhook,但是 webhook 要求我在激活之前将证书文件发送到电报,另外稍后我可能想为我们的客户发送一个文件,或者以文件的形式接收他们的回复(因为我们想通过电报向他们激活我们的网站功能)。
这是对电报机器人 API 的引用: https://core.telegram.org/bots/api#inputfile
我本人通过 HttpClient class 完成了所有 api 调用,我希望继续保持原样。 这是我失败的方法:
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;
}
}
这是我的模型: 由于电报没有定义证书的确切类型,我看一下这个:https://github.com/MrRoundRobin/telegram.bot 来生成它。
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;
}
}
我是这样称呼它的:
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
});
}
这最后一种方式,错误:
((ex.InnerException).InnerException).Message
->
此流不支持超时。
(ex.InnerException).Message
->
在 'System.IO.FileStream' 从 'ReadTimeout' 获取值时出错。
ex.Message
->
出现一个或多个错误。
那么我该如何发送文件呢?我应该如何接收它们?我应该定义什么样的实体更准确?
为了让这个工作你应该构造 MultipartFormDataContent
里面有 StreamContent
(不是 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);
}
关于接收文件-首先你应该得到包含file_path
的"file info",然后才可以下载:
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();
}
此处 TelegramResponse
和 FileInfo
看起来像这样(* 不是 C# 约定,但如果需要,您可以修复此问题):
class TelegramResponse<T>
{
public bool ok { get; set; }
public T result { get; set; }
}
class FileInfo
{
public string file_path { get; set; }
}