Reapply Events PR #679

This commit is contained in:
Alexander Merkulov 2020-01-09 10:44:08 +03:00
parent 3c6f53be68
commit 38f088abab
8 changed files with 586 additions and 89 deletions

251
README.md
View file

@ -143,6 +143,257 @@ To download file you should call **GetFile** method
Full code you can see at [DownloadFileFromContactTest](https://github.com/sochix/TLSharp/blob/master/TLSharp.Tests/TLSharpTests.cs#L167)
# Events Sample code
```csharp
using System;
using System.Threading.Tasks;
using TeleSharp.TL;
using TLSharp.Core;
using System.Linq;
using TeleSharp.TL.Messages;
using System.Collections.Generic;
namespace TLSharpPOC
{
class MainClass
{
const int APIId = 0;
const string APIHash = "???";
const string phone = "???";
public static void Main(string[] args)
{
new MainClass().MainAsync(args).Wait();
}
private async Task MainAsync(string[] args)
{
TelegramClient client = null;
try
{
// -- if necessary, IP can be changed so the client can connect to the test network.
Session session = null;
// new Session(new FileSessionStore(), "session")
//{
// ServerAddress = "149.154.175.10",
// Port = 443
//};
//Console.WriteLine($"{session.ServerAddress}:{session.Port} {phone}");
client = new TelegramClient(APIId, APIHash, session);
// subscribe an event to receive live messages
client.Updates += Client_Updates;
await client.ConnectAsync();
Console.WriteLine($"Authorised: {client.IsUserAuthorized()}");
TLUser user = null;
// -- If the user has already authenticated, this step will prevent account from being blocked as it
// -- reuses the data from last authorisation.
if (client.IsUserAuthorized())
user = client.Session.TLUser;
else
{
var registered = await client.IsPhoneRegisteredAsync(phone);
var hash = await client.SendCodeRequestAsync(phone);
Console.Write("Code: ");
var code = Console.ReadLine();
if (!registered)
{
Console.WriteLine($"Sign up {phone}");
user = await client.SignUpAsync(phone, hash, code, "First", "Last");
}
Console.WriteLine($"Sign in {phone}");
user = await client.MakeAuthAsync(phone, hash, code);
}
var contacts = await client.GetContactsAsync();
Console.WriteLine("Contacts:");
foreach (var contact in contacts.Users.OfType<TLUser>())
{
var contactUser = contact as TLUser;
Console.WriteLine($"\t{contact.Id} {contact.Phone} {contact.FirstName} {contact.LastName}");
}
var dialogs = (TLDialogs) await client.GetUserDialogsAsync();
Console.WriteLine("Channels: ");
foreach (var channelObj in dialogs.Chats.OfType<TLChannel>())
{
var channel = channelObj as TLChannel;
Console.WriteLine($"\tChat: {channel.Title}");
}
Console.WriteLine("Groups:");
TLChat chat = null;
foreach (var chatObj in dialogs.Chats.OfType<TLChat>())
{
chat = chatObj as TLChat;
Console.WriteLine($"Chat name: {chat.Title}");
var request = new TLRequestGetFullChat() { ChatId = chat.Id };
var fullChat = await client.SendRequestAsync<TeleSharp.TL.Messages.TLChatFull>(request);
var participants = (fullChat.FullChat as TeleSharp.TL.TLChatFull).Participants as TLChatParticipants;
foreach (var p in participants.Participants)
{
if (p is TLChatParticipant)
{
var participant = p as TLChatParticipant;
Console.WriteLine($"\t{participant.UserId}");
}
else if (p is TLChatParticipantAdmin)
{
var participant = p as TLChatParticipantAdmin;
Console.WriteLine($"\t{participant.UserId}**");
}
else if (p is TLChatParticipantCreator)
{
var participant = p as TLChatParticipantCreator;
Console.WriteLine($"\t{participant.UserId}**");
}
}
var peer = new TLInputPeerChat() { ChatId = chat.Id };
var m = await client.GetHistoryAsync(peer, 0, 0, 0);
Console.WriteLine(m);
if (m is TLMessages)
{
var messages = m as TLMessages;
foreach (var message in messages.Messages)
{
if (message is TLMessage)
{
var m1 = message as TLMessage;
Console.WriteLine($"\t\t{m1.Id} {m1.Message}");
}
else if (message is TLMessageService)
{
var m1 = message as TLMessageService;
Console.WriteLine($"\t\t{m1.Id} {m1.Action}");
}
}
}
else if (m is TLMessagesSlice)
{
bool done = false;
int total = 0;
while (!done)
{
var messages = m as TLMessagesSlice;
foreach (var m1 in messages.Messages)
{
if (m1 is TLMessage)
{
var message = m1 as TLMessage;
Console.WriteLine($"\t\t{message.Id} {message.Message}");
++total;
}
else if (m1 is TLMessageService)
{
var message = m1 as TLMessageService;
Console.WriteLine($"\t\t{message.Id} {message.Action}");
++total;
done = message.Action is TLMessageActionChatCreate;
}
}
m = await client.GetHistoryAsync(peer, total, 0, 0);
}
}
}
// -- Wait in a loop to handle incoming updates. No need to poll.
for (;;)
{
await client.WaitEventAsync();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private void Client_Updates(TelegramClient client, TLAbsUpdates updates)
{
Console.WriteLine($"Got update: {updates}");
if (updates is TLUpdateShort)
{
var updateShort = updates as TLUpdateShort;
Console.WriteLine($"Short: {updateShort.Update}");
if (updateShort.Update is TLUpdateUserStatus)
{
var status = updateShort.Update as TLUpdateUserStatus;
Console.WriteLine($"User {status.UserId} is {status.Status}");
if (status.Status is TLUserStatusOnline)
{
try
{
var peer = new TLInputPeerUser() { UserId = status.UserId };
client.SendMessageAsync(peer, "Você está online.").Wait();
} catch {}
}
}
}
else if (updates is TLUpdateShortMessage)
{
var message = updates as TLUpdateShortMessage;
Console.WriteLine($"Message: {message.Message}");
MarkMessageRead(client, new TLInputPeerUser() { UserId = message.UserId }, message.Id);
}
else if (updates is TLUpdateShortChatMessage)
{
var message = updates as TLUpdateShortChatMessage;
Console.WriteLine($"Chat Message: {message.Message}");
MarkMessageRead(client, new TLInputPeerChat() { ChatId = message.ChatId }, message.Id);
}
else if (updates is TLUpdates)
{
var allUpdates = updates as TLUpdates;
foreach (var update in allUpdates.Updates)
{
Console.WriteLine($"\t{update}");
if (update is TLUpdateNewChannelMessage)
{
var metaMessage = update as TLUpdateNewChannelMessage;
var message = metaMessage.Message as TLMessage;
Console.WriteLine($"Channel message: {message.Message}");
var channel = allUpdates.Chats[0] as TLChannel;
MarkMessageRead(client,
new TLInputPeerChannel() { ChannelId = channel.Id, AccessHash = channel.AccessHash.Value },
message.Id );
}
}
foreach(var user in allUpdates.Users)
{
Console.WriteLine($"{user}");
}
foreach (var chat in allUpdates.Chats)
{
Console.WriteLine($"{chat}");
}
}
}
private void MarkMessageRead(TelegramClient client, TLAbsInputPeer peer, int id)
{
// An exception happens here but it's not fatal.
try
{
var request = new TLRequestReadHistory();
request.MaxId = id;
request.Peer = peer;
client.SendRequestAsync<bool>(request).Wait();
}
catch (InvalidOperationException e){
System.Console.WriteLine(e.getMessage())
}
}
}
}
```
# Available Methods
For your convenience TLSharp have wrappers for several Telegram API methods. You could add your own, see details below.

View file

@ -0,0 +1,69 @@
using System;
namespace TLSharp.Core.Network
{
public class FloodException : Exception
{
public TimeSpan TimeToWait { get; private set; }
internal FloodException(TimeSpan timeToWait)
: base($"Flood prevention. Telegram now requires your program to do requests again only after {timeToWait.TotalSeconds} seconds have passed ({nameof(TimeToWait)} property)." +
" If you think the culprit of this problem may lie in TLSharp's implementation, open a Github issue please.")
{
TimeToWait = timeToWait;
}
}
public class BadMessageException : Exception
{
internal BadMessageException(string description) : base(description)
{
}
}
internal abstract class DataCenterMigrationException : Exception
{
internal int DC { get; private set; }
private const string REPORT_MESSAGE =
" See: https://github.com/sochix/TLSharp#i-get-a-xxxmigrationexception-or-a-migrate_x-error";
protected DataCenterMigrationException(string msg, int dc) : base(msg + REPORT_MESSAGE)
{
DC = dc;
}
}
internal class PhoneMigrationException : DataCenterMigrationException
{
internal PhoneMigrationException(int dc)
: base($"Phone number registered to a different DC: {dc}.", dc)
{
}
}
internal class FileMigrationException : DataCenterMigrationException
{
internal FileMigrationException(int dc)
: base($"File located on a different DC: {dc}.", dc)
{
}
}
internal class UserMigrationException : DataCenterMigrationException
{
internal UserMigrationException(int dc)
: base($"User located on a different DC: {dc}.", dc)
{
}
}
internal class NetworkMigrationException : DataCenterMigrationException
{
internal NetworkMigrationException(int dc)
: base($"Network located on a different DC: {dc}.", dc)
{
}
}
}

View file

@ -18,11 +18,19 @@ namespace TLSharp.Core.Network
{
//private ulong sessionId = GenerateRandomUlong();
private readonly TcpTransport _transport;
private readonly Session _session;
private static NLog.Logger logger = NLog.LogManager.GetLogger("MTProto");
private readonly uint UpdatesTooLongID = (uint) new TeleSharp.TL.TLUpdatesTooLong ().Constructor;
private TcpTransport _transport;
private Session _session;
public readonly List<ulong> needConfirmation = new List<ulong>();
public delegate void HandleUpdates (TeleSharp.TL.TLAbsUpdates updates);
public event HandleUpdates UpdatesEvent;
public MtProtoSender(TcpTransport transport, Session session)
{
_transport = transport;
@ -77,20 +85,26 @@ namespace TLSharp.Core.Network
plaintextWriter.Write(packet.Length);
plaintextWriter.Write(packet);
msgKey = Helpers.CalcMsgKey(plaintextPacket.GetBuffer());
ciphertext = AES.EncryptAES(Helpers.CalcKey(_session.AuthKey.Data, msgKey, true), plaintextPacket.GetBuffer());
var buffer = plaintextPacket.GetBuffer();
logger.Debug("Send {0} {1:x8} {2}", request, request.Constructor, Sniffer.MessageOut(buffer));
msgKey = Helpers.CalcMsgKey(buffer);
ciphertext = AES.EncryptAES(Helpers.CalcKey(_session.AuthKey.Data, msgKey, true),
plaintextPacket.GetBuffer());
}
}
}
using (MemoryStream ciphertextPacket = makeMemory(8 + 16 + ciphertext.Length))
private async Task Ack()
{
if (needConfirmation.Any())
{
using (BinaryWriter writer = new BinaryWriter(ciphertextPacket))
var ackRequest = new AckRequest(needConfirmation);
using (var memory = new MemoryStream())
using (var writer = new BinaryWriter(memory))
{
writer.Write(_session.AuthKey.Id);
writer.Write(msgKey);
writer.Write(ciphertext);
await _transport.Send(ciphertextPacket.GetBuffer());
ackRequest.SerializeBody(writer);
await Send(memory.ToArray(), ackRequest);
needConfirmation.Clear();
}
}
}
@ -112,6 +126,7 @@ namespace TLSharp.Core.Network
AESKeyData keyData = Helpers.CalcKey(_session.AuthKey.Data, msgKey, false);
byte[] plaintext = AES.DecryptAES(keyData, inputReader.ReadBytes((int)(inputStream.Length - inputStream.Position)));
logger.Debug(Sniffer.MessageIn(plaintext));
using (MemoryStream plaintextStream = new MemoryStream(plaintext))
using (BinaryReader plaintextReader = new BinaryReader(plaintextStream))
@ -127,22 +142,35 @@ namespace TLSharp.Core.Network
return new Tuple<byte[], ulong, int>(message, remoteMessageId, remoteSequence);
}
public async Task<byte[]> Receive(TeleSharp.TL.TLMethod request)
public async Task<byte []> Receive (TeleSharp.TL.TLMethod request)
{
while (!request.ConfirmReceived)
while (!request.ConfirmReceived)
{
var result = DecodeMessage((await _transport.Receive()).Body);
using (var messageStream = new MemoryStream(result.Item1, false))
using (var messageReader = new BinaryReader(messageStream))
using (var messageStream = new MemoryStream (result.Item1, false))
using (var messageReader = new BinaryReader (messageStream))
{
processMessage(result.Item2, result.Item3, messageReader, request);
processMessage (result.Item2, result.Item3, messageReader, request);
}
}
return null;
}
public async Task<byte[]> Receive(int timeoutms)
{
var result = DecodeMessage ((await _transport.Receieve (timeoutms)).Body);
using (var messageStream = new MemoryStream (result.Item1, false))
using (var messageReader = new BinaryReader (messageStream))
{
processMessage (result.Item2, result.Item3, messageReader, null);
}
return null;
}
public async Task SendPingAsync()
{
var pingRequest = new PingRequest();
@ -162,11 +190,14 @@ namespace TLSharp.Core.Network
// TODO: check sessionid
// TODO: check seqno
//logger.debug("processMessage: msg_id {0}, sequence {1}, data {2}", BitConverter.ToString(((MemoryStream)messageReader.BaseStream).GetBuffer(), (int) messageReader.BaseStream.Position, (int) (messageReader.BaseStream.Length - messageReader.BaseStream.Position)).Replace("-","").ToLower());
needConfirmation.Add(messageId);
Ack().Wait();
uint code = messageReader.ReadUInt32();
messageReader.BaseStream.Position -= 4;
logger.Info("Processing message {0:x8}", code);
switch (code)
{
case 0x73f1f8dc: // container
@ -208,29 +239,60 @@ namespace TLSharp.Core.Network
case 0x78d4dec1:
case 0x725b04c3:
case 0x74ae4240:
return HandleUpdate(messageId, sequence, messageReader);
case 0x11f1331c:
return HandleUpdate(code, sequence, messageReader, request);
default:
//logger.debug("unknown message: {0}", code);
logger.Info("unhandled message");
return false;
}
}
private bool HandleUpdate(ulong messageId, int sequence, BinaryReader messageReader)
private bool HandleUpdate(uint code, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request)
{
return false;
/*
try
{
UpdatesEvent(TL.Parse<Updates>(messageReader));
return true;
var update = ParseUpdate (code, messageReader);
if (update != null && UpdatesEvent != null)
{
UpdatesEvent (update);
}
return true;
}
catch (Exception e)
{
logger.warning("update processing exception: {0}", e);
return false;
}
*/
logger.Debug($"HandleUpdate failed: {e}");
return false;
}
}
private TeleSharp.TL.TLAbsUpdates ParseUpdate(uint code, BinaryReader messageReader)
{
switch (code)
{
case 0xe317af7e:
return DecodeUpdate<TeleSharp.TL.TLUpdatesTooLong>(messageReader);
case 0x914fbf11:
return DecodeUpdate<TeleSharp.TL.TLUpdateShortMessage> (messageReader);
case 0x16812688:
return DecodeUpdate<TeleSharp.TL.TLUpdateShortChatMessage> (messageReader);
case 0x78d4dec1:
return DecodeUpdate<TeleSharp.TL.TLUpdateShort> (messageReader);
case 0x725b04c3:
return DecodeUpdate<TeleSharp.TL.TLUpdatesCombined> (messageReader);
case 0x74ae4240:
return DecodeUpdate<TeleSharp.TL.TLUpdates> (messageReader);
case 0x11f1331c:
return DecodeUpdate<TeleSharp.TL.TLUpdateShortSentMessage> (messageReader);
default:
return null;
}
}
private TeleSharp.TL.TLAbsUpdates DecodeUpdate<T>(BinaryReader messageReader) where T: TeleSharp.TL.TLAbsUpdates
{
var ms = messageReader.BaseStream as MemoryStream;
var update = (T) TeleSharp.TL.ObjectUtils.DeserializeObject (messageReader);
return update;
}
private bool HandleGzipPacked(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request)
@ -275,6 +337,7 @@ namespace TLSharp.Core.Network
{ // rpc_error
int errorCode = messageReader.ReadInt32();
string errorMessage = Serializers.String.read(messageReader);
Console.Error.WriteLine($"ERROR: {errorMessage} - {errorCode}");
if (errorMessage.StartsWith("FLOOD_WAIT_"))
{
@ -392,7 +455,7 @@ namespace TLSharp.Core.Network
throw new InvalidOperationException("invalid container");
}
throw new NotImplementedException("This should never happens");
throw new NotImplementedException("This should never happen!");
/*
logger.debug("bad_msg_notification: msgid {0}, seq {1}, errorcode {2}", requestId, requestSequence,
errorCode);
@ -508,6 +571,10 @@ namespace TLSharp.Core.Network
messageReader.BaseStream.Position = beginPosition + innerLength;
}
}
catch (InvalidOperationException e)
{
throw e;
}
catch (Exception e)
{
// logger.error("failed to process message in container: {0}", e);
@ -523,61 +590,4 @@ namespace TLSharp.Core.Network
return new MemoryStream(new byte[len], 0, len, true, true);
}
}
public class FloodException : Exception
{
public TimeSpan TimeToWait { get; private set; }
internal FloodException(TimeSpan timeToWait)
: base($"Flood prevention. Telegram now requires your program to do requests again only after {timeToWait.TotalSeconds} seconds have passed ({nameof(TimeToWait)} property)." +
" If you think the culprit of this problem may lie in TLSharp's implementation, open a Github issue please.")
{
TimeToWait = timeToWait;
}
}
internal abstract class DataCenterMigrationException : Exception
{
internal int DC { get; private set; }
private const string REPORT_MESSAGE =
" See: https://github.com/sochix/TLSharp#i-get-a-xxxmigrationexception-or-a-migrate_x-error";
protected DataCenterMigrationException(string msg, int dc) : base (msg + REPORT_MESSAGE)
{
DC = dc;
}
}
internal class PhoneMigrationException : DataCenterMigrationException
{
internal PhoneMigrationException(int dc)
: base ($"Phone number registered to a different DC: {dc}.", dc)
{
}
}
internal class FileMigrationException : DataCenterMigrationException
{
internal FileMigrationException(int dc)
: base ($"File located on a different DC: {dc}.", dc)
{
}
}
internal class UserMigrationException : DataCenterMigrationException
{
internal UserMigrationException(int dc)
: base($"User located on a different DC: {dc}.", dc)
{
}
}
internal class NetworkMigrationException : DataCenterMigrationException
{
internal NetworkMigrationException(int dc)
: base($"Network located on a different DC: {dc}.", dc)
{
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Text;
namespace TLSharp.Core.Network
{
public static class Sniffer
{
public static string MessageOut(byte[] data)
{
return WriteMessage(new StringBuilder("[OUT]:"), data);
}
public static string MessageIn(byte[] data)
{
return WriteMessage(new StringBuilder("[IN]:"), data);
}
private static string WriteMessage(StringBuilder log, byte[] data)
{
foreach (var b in data)
log.AppendFormat(" {0:x2}", b);
return log.ToString();
}
}
}

View file

@ -1,6 +1,7 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace TLSharp.Core.Network
@ -9,9 +10,11 @@ namespace TLSharp.Core.Network
public class TcpTransport : IDisposable
{
private readonly TcpClient _tcpClient;
private static NLog.Logger logger = TelegramClient.logger;
private readonly TcpClient _tcpClient;
private readonly NetworkStream _stream;
private int sendCounter = 0;
private CancellationTokenSource tokenSource = new CancellationTokenSource();
public TcpTransport(string address, int port, TcpClientConnectionHandler handler = null)
{
@ -45,10 +48,14 @@ namespace TLSharp.Core.Network
public async Task<TcpMessage> Receive()
{
logger.Trace($"Wait for answer {_tcpClient.Available} ...");
var stream = _tcpClient.GetStream();
var packetLengthBytes = new byte[4];
if (await _stream.ReadAsync(packetLengthBytes, 0, 4) != 4)
throw new InvalidOperationException("Couldn't read the packet length");
int packetLength = BitConverter.ToInt32(packetLengthBytes, 0);
logger.Debug("[IN] Packet length: {0}", packetLength);
var seqBytes = new byte[4];
if (await _stream.ReadAsync(seqBytes, 0, 4) != 4)
@ -91,6 +98,72 @@ namespace TLSharp.Core.Network
return new TcpMessage(seq, body);
}
public async Task<TcpMessage> Receieve(int timeoutms)
{
logger.Trace($"Wait for event {_tcpClient.Available} ...");
var stream = _tcpClient.GetStream();
var packetLengthBytes = new byte[4];
var token = tokenSource.Token;
stream.ReadTimeout = timeoutms;
int bytes = 0;
try
{
bytes = stream.Read(packetLengthBytes, 0, 4);
} catch (System.IO.IOException io)
{
var socketError = io.InnerException as SocketException;
if (socketError != null && socketError.SocketErrorCode == SocketError.TimedOut)
throw new OperationCanceledException();
throw io;
}
if (bytes != 4)
throw new InvalidOperationException("Couldn't read the packet length");
int packetLength = BitConverter.ToInt32(packetLengthBytes, 0);
logger.Debug("[IN]* Packet length: {0}", packetLength);
var seqBytes = new byte[4];
if (await _stream.ReadAsync(seqBytes, 0, 4) != 4)
throw new InvalidOperationException("Couldn't read the sequence");
int seq = BitConverter.ToInt32(seqBytes, 0);
logger.Debug("[IN]* sequence: {0}", seq);
int readBytes = 0;
var body = new byte[packetLength - 12];
int neededToRead = packetLength - 12;
do
{
var bodyByte = new byte[packetLength - 12];
var availableBytes = await _stream.ReadAsync(bodyByte, 0, neededToRead);
neededToRead -= availableBytes;
Buffer.BlockCopy(bodyByte, 0, body, readBytes, availableBytes);
readBytes += availableBytes;
}
while (readBytes != packetLength - 12);
var crcBytes = new byte[4];
if (await _stream.ReadAsync(crcBytes, 0, 4) != 4)
throw new InvalidOperationException("Couldn't read the crc");
int checksum = BitConverter.ToInt32(crcBytes, 0);
byte[] rv = new byte[packetLengthBytes.Length + seqBytes.Length + body.Length];
Buffer.BlockCopy(packetLengthBytes, 0, rv, 0, packetLengthBytes.Length);
Buffer.BlockCopy(seqBytes, 0, rv, packetLengthBytes.Length, seqBytes.Length);
Buffer.BlockCopy(body, 0, rv, packetLengthBytes.Length + seqBytes.Length, body.Length);
var crc32 = new Ionic.Crc.CRC32();
crc32.SlurpBlock(rv, 0, rv.Length);
var validChecksum = crc32.Crc32Result;
if (checksum != validChecksum)
{
throw new InvalidOperationException("invalid checksum! skip");
}
return new TcpMessage(seq, body);
}
public bool IsConnected
{
get
@ -99,7 +172,6 @@ namespace TLSharp.Core.Network
}
}
public void Dispose()
{
if (_tcpClient.Connected)

View file

@ -42,6 +42,10 @@
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="NLog">
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Auth\Authenticator.cs" />
@ -68,6 +72,8 @@
<Compile Include="Session.cs" />
<Compile Include="TelegramClient.cs" />
<Compile Include="Utils\Helpers.cs" />
<Compile Include="Network\Sniffer.cs" />
<Compile Include="Network\Exceptions.cs" />
<Compile Include="DataCenter.cs" />
</ItemGroup>
<ItemGroup>

View file

@ -1,8 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TeleSharp.TL;
using TeleSharp.TL.Account;
@ -21,7 +22,9 @@ namespace TLSharp.Core
{
public class TelegramClient : IDisposable
{
internal static NLog.Logger logger = NLog.LogManager.GetLogger("TelegramClient");
private MtProtoSender _sender;
private AuthKey _key;
private TcpTransport _transport;
private string _apiHash = "";
private int _apiId = 0;
@ -29,6 +32,16 @@ namespace TLSharp.Core
private List<TLDcOption> dcOptions;
private TcpClientConnectionHandler _handler;
private bool _looping = true;
public volatile bool AllowEvents = false;
public delegate void UpdatesEvent (TelegramClient source, TLAbsUpdates updates);
public delegate void ClientEvent(TelegramClient source);
public event UpdatesEvent Updates;
public event ClientEvent ScheduledTasks;
public event ClientEvent IdleTasks;
public Session Session
{
get { return _session; }
@ -62,6 +75,7 @@ namespace TLSharp.Core
_session.TimeOffset = result.TimeOffset;
}
_sender.UpdatesEvent += _sender_UpdatesEvent;
_sender = new MtProtoSender(_transport, _session);
//set-up layer
@ -110,6 +124,50 @@ namespace TLSharp.Core
}
}
public void Close()
{
_looping = false;
}
public async Task MainLoopAsync(int timeslicems)
{
logger.Trace("Entered loop");
var lastPing = DateTime.UtcNow;
await SendPingAsync();
while (_looping)
{
try
{
await WaitEventAsync(timeslicems);
} catch (OperationCanceledException)
{
logger.Trace("Timeout");
}
finally
{
var now = DateTime.UtcNow;
if ((now - lastPing).TotalSeconds >= 30)
{
await SendPingAsync();
lastPing = now;
}
if (ScheduledTasks != null)
{
logger.Trace("Running idle tasks");
ScheduledTasks.Invoke(this);
ScheduledTasks = null;
}
IdleTasks?.Invoke(this);
}
}
}
private void _sender_UpdatesEvent (TLAbsUpdates updates)
{
if (AllowEvents && Updates != null)
Updates(this, updates);
}
private async Task RequestWithDcMigration(TLMethod request)
{
if (_sender == null)
@ -139,6 +197,11 @@ namespace TLSharp.Core
}
}
public async Task WaitEventAsync(int timeoutms)
{
await _sender.Receive (timeoutms);
}
public bool IsUserAuthorized()
{
return _session.TLUser != null;

View file

@ -2,4 +2,5 @@
<packages>
<package id="DotNetZip" version="1.11.0" targetFramework="net451" />
<package id="MarkerMetro.Unity.Ionic.Zlib" version="2.0.0.14" targetFramework="net452" />
<package id="NLog" version="4.4.12" targetFramework="net45" />
</packages>