mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
Support for connecting to Telegram via on-demand HTTP requests instead of permanent TCP connection: client.HttpMode (experimental)
This commit is contained in:
parent
4f9accdfc8
commit
a19db86c1d
|
|
@ -187,10 +187,10 @@ namespace WTelegram
|
|||
/// <param name="entities">Text formatting entities for the caption. You can use <see cref="Markdown.MarkdownToEntities">MarkdownToEntities</see> to create these</param>
|
||||
/// <param name="schedule_date">UTC timestamp when the message should be sent</param>
|
||||
/// <param name="videoUrlAsFile">Any <see cref="InputMediaDocumentExternal"/> URL pointing to a video should be considered as non-streamable</param>
|
||||
/// <returns>The last of the media group messages, confirmed by Telegram</returns>
|
||||
/// <returns>The media group messages as received by Telegram</returns>
|
||||
/// <remarks>
|
||||
/// * The caption/entities are set on the last media<br/>
|
||||
/// * <see cref="InputMediaDocumentExternal"/> and <see cref="InputMediaPhotoExternal"/> are supported by downloading the file from the web via HttpClient and sending it to Telegram.
|
||||
/// * The caption/entities are set on the first media<br/>
|
||||
/// * <see cref="InputMediaDocumentExternal"/> and <see cref="InputMediaPhotoExternal"/> are supported natively for bot accounts, and for user accounts by downloading the file from the web via HttpClient and sending it to Telegram.
|
||||
/// WTelegramClient proxy settings don't apply to HttpClient<br/>
|
||||
/// * You may run into errors if you mix, in the same album, photos and file documents having no thumbnails/video attributes
|
||||
/// </remarks>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
|
|
@ -28,8 +29,6 @@ namespace WTelegram
|
|||
/// <summary>This event will be called when unsollicited updates/messages are sent by Telegram servers</summary>
|
||||
/// <remarks>Make your handler <see langword="async"/>, or return <see cref="Task.CompletedTask"/> or <see langword="null"/><br/>See <see href="https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ReactorError.cs?ts=4#L30">Examples/Program_ReactorError.cs</see> for how to use this<br/>or <see href="https://github.com/wiz0u/WTelegramClient/blob/master/Examples/Program_ListenUpdates.cs?ts=4#L21">Examples/Program_ListenUpdate.cs</see> using the UpdateManager class instead</remarks>
|
||||
public event Func<UpdatesBase, Task> OnUpdates;
|
||||
[Obsolete("This event was renamed OnUpdates (plural). You may also want to consider using our new UpdateManager class instead (see FAQ)")]
|
||||
public event Func<UpdatesBase, Task> OnUpdate { add { OnUpdates += value; } remove { OnUpdates -= value; } }
|
||||
/// <summary>This event is called for other types of notifications (login states, reactor errors, ...)</summary>
|
||||
public event Func<IObject, Task> OnOther;
|
||||
/// <summary>Use this handler to intercept Updates that resulted from your own API calls</summary>
|
||||
|
|
@ -67,6 +66,7 @@ namespace WTelegram
|
|||
private Session.DCSession _dcSession;
|
||||
private TcpClient _tcpClient;
|
||||
private Stream _networkStream;
|
||||
private HttpClient _httpClient;
|
||||
private IObject _lastSentMsg;
|
||||
private long _lastRecvMsgId;
|
||||
private readonly List<long> _msgsToAck = [];
|
||||
|
|
@ -199,6 +199,10 @@ namespace WTelegram
|
|||
|
||||
public void DisableUpdates(bool disable = true) => _dcSession.DisableUpdates(disable);
|
||||
|
||||
/// <summary>Enable connecting to Telegram via on-demand HTTP requests instead of permanent TCP connection</summary>
|
||||
/// <param name="httpClient">HttpClient to use. Leave <see langword="null"/> for a default one</param>
|
||||
public void HttpMode(HttpClient httpClient = null) => _httpClient = httpClient ?? new();
|
||||
|
||||
/// <summary>Disconnect from Telegram <i>(shouldn't be needed in normal usage)</i></summary>
|
||||
/// <param name="resetUser">Forget about logged-in user</param>
|
||||
/// <param name="resetSessions">Disconnect secondary sessions with other DCs</param>
|
||||
|
|
@ -513,6 +517,7 @@ namespace WTelegram
|
|||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string TransportError(int error_code) => error_code switch
|
||||
{
|
||||
|
|
@ -521,7 +526,6 @@ namespace WTelegram
|
|||
444 => "Invalid DC",
|
||||
_ => Enum.GetName(typeof(HttpStatusCode), error_code) ?? "Transport error"
|
||||
};
|
||||
}
|
||||
|
||||
internal void CheckSalt()
|
||||
{
|
||||
|
|
@ -871,6 +875,8 @@ namespace WTelegram
|
|||
throw new Exception("Library was not compiled with OBFUSCATION symbol");
|
||||
#endif
|
||||
}
|
||||
if (_httpClient != null)
|
||||
_reactorTask = Task.CompletedTask;
|
||||
else
|
||||
{
|
||||
endpoint = _dcSession?.EndPoint ?? GetDefaultEndpoint(out int defaultDc);
|
||||
|
|
@ -930,6 +936,9 @@ namespace WTelegram
|
|||
_networkStream = _tcpClient.GetStream();
|
||||
}
|
||||
|
||||
_dcSession.Salts?.Remove(DateTime.MaxValue);
|
||||
if (_networkStream != null)
|
||||
{
|
||||
byte protocolId = (byte)(_paddedMode ? 0xDD : 0xEE);
|
||||
#if OBFUSCATION
|
||||
(_sendCtr, _recvCtr, preamble) = InitObfuscation(secret, protocolId, dcId);
|
||||
|
|
@ -938,8 +947,8 @@ namespace WTelegram
|
|||
#endif
|
||||
await _networkStream.WriteAsync(preamble, 0, preamble.Length, _cts.Token);
|
||||
|
||||
_dcSession.Salts?.Remove(DateTime.MaxValue);
|
||||
_reactorTask = Reactor(_networkStream, _cts);
|
||||
}
|
||||
_sendSemaphore.Release();
|
||||
|
||||
try
|
||||
|
|
@ -947,7 +956,7 @@ namespace WTelegram
|
|||
if (_dcSession.AuthKeyID == 0)
|
||||
await CreateAuthorizationKey(this, _dcSession);
|
||||
|
||||
var keepAliveTask = KeepAlive(_cts.Token);
|
||||
if (_networkStream != null) _ = KeepAlive(_cts.Token);
|
||||
if (quickResume && _dcSession.Layer == Layer.Version && _dcSession.DataCenter != null && _session.MainDC != 0)
|
||||
TLConfig = new Config { this_dc = _session.MainDC, dc_options = _session.DcOptions };
|
||||
else
|
||||
|
|
@ -1467,10 +1476,11 @@ namespace WTelegram
|
|||
int frameLength = (int)memStream.Length;
|
||||
BinaryPrimitives.WriteInt32LittleEndian(buffer, frameLength - 4); // patch payload_len with correct value
|
||||
#if OBFUSCATION
|
||||
_sendCtr.EncryptDecrypt(buffer, frameLength);
|
||||
_sendCtr?.EncryptDecrypt(buffer, frameLength);
|
||||
#endif
|
||||
await _networkStream.WriteAsync(buffer, 0, frameLength);
|
||||
var sending = SendFrame(buffer, frameLength);
|
||||
_lastSentMsg = msg;
|
||||
await sending;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -1478,6 +1488,24 @@ namespace WTelegram
|
|||
}
|
||||
}
|
||||
|
||||
private async Task SendFrame(byte[] buffer, int frameLength)
|
||||
{
|
||||
if (_httpClient == null)
|
||||
await _networkStream.WriteAsync(buffer, 0, frameLength);
|
||||
else
|
||||
{
|
||||
var endpoint = _dcSession?.EndPoint ?? GetDefaultEndpoint(out _);
|
||||
var content = new ByteArrayContent(buffer, 4, frameLength - 4);
|
||||
var response = await _httpClient.PostAsync($"http://{endpoint}/api", content);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
throw new RpcException((int)response.StatusCode, TransportError((int)response.StatusCode));
|
||||
var data = await response.Content.ReadAsByteArrayAsync();
|
||||
var obj = ReadFrame(data, data.Length);
|
||||
if (obj != null)
|
||||
await HandleMessageAsync(obj);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<T> InvokeBare<T>(IMethod<T> request)
|
||||
{
|
||||
if (_bareRpc != null) throw new WTException("A bare request is already undergoing");
|
||||
|
|
@ -1501,6 +1529,12 @@ namespace WTelegram
|
|||
retry:
|
||||
var rpc = new Rpc { type = typeof(T) };
|
||||
await SendAsync(query, true, rpc);
|
||||
if (_httpClient != null && !rpc.Task.IsCompleted)
|
||||
{
|
||||
await SendAsync(new HttpWait { max_delay = 30, wait_after = 10, max_wait = 1000 * PingInterval }, true);
|
||||
if (!rpc.Task.IsCompleted) rpc.tcs.TrySetException(new RpcException(417, "Missing RPC response via HTTP"));
|
||||
}
|
||||
|
||||
var result = await rpc.Task;
|
||||
switch (result)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue