From 3e0e53bb091f9b88b18becea05bccaffde8a697f Mon Sep 17 00:00:00 2001 From: Alexander Merkulov Date: Thu, 9 Jan 2020 10:44:08 +0300 Subject: [PATCH 1/4] Reapply Events PR #679 pr updates amended pr review changes resolved merge conflicts updates from last night before rebase update on message test now passing removed nlog references and usage resolve conflicts from HEAD Reapply Events PR #679 update on message test now passing removed nlog references and usage --- README.md | 251 ++++++++++++++++++++++++++ TLSharp.Core/Network/Exceptions.cs | 69 +++++++ TLSharp.Core/Network/MtProtoSender.cs | 149 +++++++++++---- TLSharp.Core/Network/Sniffer.cs | 25 +++ TLSharp.Core/Network/TcpTransport.cs | 66 ++++++- TLSharp.Core/TLSharp.Core.csproj | 2 + TLSharp.Core/TelegramClient.cs | 65 ++++++- TLSharp.Core/packages.config | 2 + TLSharp.Tests.NUnit/Test.cs | 6 + TLSharp.Tests.VS/TLSharpTestsVs.cs | 7 + TLSharp.Tests/TLSharpTests.cs | 85 +++++++-- 11 files changed, 673 insertions(+), 54 deletions(-) create mode 100644 TLSharp.Core/Network/Exceptions.cs create mode 100644 TLSharp.Core/Network/Sniffer.cs diff --git a/README.md b/README.md index 16c95d69..727af523 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,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()) + { + 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()) + { + var channel = channelObj as TLChannel; + Console.WriteLine($"\tChat: {channel.Title}"); + } + + Console.WriteLine("Groups:"); + TLChat chat = null; + foreach (var chatObj in dialogs.Chats.OfType()) + { + chat = chatObj as TLChat; + Console.WriteLine($"Chat name: {chat.Title}"); + var request = new TLRequestGetFullChat() { ChatId = chat.Id }; + var fullChat = await client.SendRequestAsync(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(request).Wait(); + } + catch (InvalidOperationException e){ + System.Console.WriteLine($"MarkMessageRead Error: {e.getMessage()}") + } + + } + } + } +``` + # Available Methods For your convenience TLSharp have wrappers for several Telegram API methods. You could add your own, see details below. diff --git a/TLSharp.Core/Network/Exceptions.cs b/TLSharp.Core/Network/Exceptions.cs new file mode 100644 index 00000000..878e64e4 --- /dev/null +++ b/TLSharp.Core/Network/Exceptions.cs @@ -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) + { + } + } + + +} diff --git a/TLSharp.Core/Network/MtProtoSender.cs b/TLSharp.Core/Network/MtProtoSender.cs index 91e44574..cd010e9e 100644 --- a/TLSharp.Core/Network/MtProtoSender.cs +++ b/TLSharp.Core/Network/MtProtoSender.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; @@ -11,7 +10,6 @@ using TLSharp.Core.Exceptions; using TLSharp.Core.MTProto; using TLSharp.Core.MTProto.Crypto; -using TLSharp.Core.Network.Exceptions; using TLSharp.Core.Network.Requests; using TLSharp.Core.Utils; @@ -24,8 +22,14 @@ public class MtProtoSender private readonly TcpTransport transport; private readonly Session session; + private readonly uint UpdatesTooLongID = (uint) new TLUpdatesTooLong().Constructor; + public readonly List needConfirmation = new List(); + public delegate void HandleUpdates (TLAbsUpdates updates); + + public event HandleUpdates UpdatesEvent; + public MtProtoSender(TcpTransport transport, Session session) { this.transport = transport; @@ -37,7 +41,7 @@ private int GenerateSequence(bool confirmed) return confirmed ? session.Sequence++ * 2 + 1 : session.Sequence * 2; } - public async Task Send(TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) + public async Task Send(TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -54,7 +58,6 @@ private int GenerateSequence(bool confirmed) } } - using (var memory = new MemoryStream()) using (var writer = new BinaryWriter(memory)) { @@ -65,7 +68,7 @@ private int GenerateSequence(bool confirmed) session.Save(); } - public async Task Send(byte[] packet, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) + public async Task Send(byte[] packet, TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -84,11 +87,12 @@ private int GenerateSequence(bool confirmed) 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(); + msgKey = Helpers.CalcMsgKey(buffer); + ciphertext = AES.EncryptAES(Helpers.CalcKey(session.AuthKey.Data, msgKey, true), + plaintextPacket.GetBuffer()); } } - using (MemoryStream ciphertextPacket = makeMemory(8 + 16 + ciphertext.Length)) { using (BinaryWriter writer = new BinaryWriter(ciphertextPacket)) @@ -102,6 +106,23 @@ private int GenerateSequence(bool confirmed) } } + private async Task Ack(CancellationToken token = default(CancellationToken)) + { + token.ThrowIfCancellationRequested(); + + if (needConfirmation.Any()) + { + var ackRequest = new AckRequest(needConfirmation); + using (var memory = new MemoryStream()) + using (var writer = new BinaryWriter(memory)) + { + ackRequest.SerializeBody(writer); + await Send(memory.ToArray(), ackRequest); + needConfirmation.Clear(); + } + } + } + private Tuple DecodeMessage(byte[] body) { byte[] message; @@ -134,16 +155,16 @@ private Tuple DecodeMessage(byte[] body) return new Tuple(message, remoteMessageId, remoteSequence); } - public async Task Receive(TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) + public async Task Receive(TLMethod request, CancellationToken token = default(CancellationToken)) { - while (!request.ConfirmReceived) + while (!request.ConfirmReceived) { var result = DecodeMessage((await transport.Receive(token).ConfigureAwait(false)).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, token); + await processMessageAsync(result.Item2, result.Item3, messageReader, request, token); } token.ThrowIfCancellationRequested(); @@ -152,6 +173,21 @@ private Tuple DecodeMessage(byte[] body) return null; } + public async Task Receive(int timeoutms, CancellationToken token = default(CancellationToken)) + { + var result = DecodeMessage((await transport.Receieve(timeoutms)).Body); + + using (var messageStream = new MemoryStream(result.Item1, false)) + using (var messageReader = new BinaryReader(messageStream)) + { + await processMessageAsync(result.Item2, result.Item3, messageReader, null); + } + + token.ThrowIfCancellationRequested(); + + return null; + } + public async Task SendPingAsync(CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -167,7 +203,7 @@ private Tuple DecodeMessage(byte[] body) await Receive(pingRequest, token).ConfigureAwait(false); } - private bool processMessage(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request, CancellationToken token = default(CancellationToken)) + private async Task processMessageAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -175,8 +211,10 @@ private Tuple DecodeMessage(byte[] body) // 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); + await Ack(token); uint code = messageReader.ReadUInt32(); messageReader.BaseStream.Position -= 4; @@ -184,7 +222,7 @@ private Tuple DecodeMessage(byte[] body) { case 0x73f1f8dc: // container //logger.debug("MSG container"); - return HandleContainer(messageId, sequence, messageReader, request, token); + return await HandleContainerAsync(messageId, sequence, messageReader, request, token); case 0x7abe77ec: // ping //logger.debug("MSG ping"); return HandlePing(messageId, sequence, messageReader); @@ -202,7 +240,7 @@ private Tuple DecodeMessage(byte[] body) return HandleMsgsAck(messageId, sequence, messageReader); case 0xedab447b: // bad_server_salt //logger.debug("MSG bad_server_salt"); - return HandleBadServerSalt(messageId, sequence, messageReader, request, token); + return await HandleBadServerSaltAsync(messageId, sequence, messageReader, request, token); case 0xa7eff811: // bad_msg_notification //logger.debug("MSG bad_msg_notification"); return HandleBadMsgNotification(messageId, sequence, messageReader); @@ -214,39 +252,68 @@ private Tuple DecodeMessage(byte[] body) return HandleRpcResult(messageId, sequence, messageReader, request); case 0x3072cfa1: // gzip_packed //logger.debug("MSG gzip_packed"); - return HandleGzipPacked(messageId, sequence, messageReader, request, token); + return await HandleGzipPackedAsync(messageId, sequence, messageReader, request, token); case 0xe317af7e: case 0xd3f45784: case 0x2b2fbd4e: 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); return false; } } - private bool HandleUpdate(ulong messageId, int sequence, BinaryReader messageReader) + private bool HandleUpdate(uint code, int sequence, BinaryReader messageReader, TLMethod request) { - return false; - - /* try { - UpdatesEvent(TL.Parse(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; - } - */ + return false; + } } - private bool HandleGzipPacked(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request, CancellationToken token = default(CancellationToken)) + private TLAbsUpdates ParseUpdate(uint code, BinaryReader messageReader) + { + switch (code) + { + case 0xe317af7e: + return DecodeUpdate(messageReader); + case 0x914fbf11: + return DecodeUpdate (messageReader); + case 0x16812688: + return DecodeUpdate (messageReader); + case 0x78d4dec1: + return DecodeUpdate (messageReader); + case 0x725b04c3: + return DecodeUpdate (messageReader); + case 0x74ae4240: + return DecodeUpdate (messageReader); + case 0x11f1331c: + return DecodeUpdate (messageReader); + default: + return null; + } + } + + private TLAbsUpdates DecodeUpdate(BinaryReader messageReader) where T : TLAbsUpdates + { + var ms = messageReader.BaseStream as MemoryStream; + var update = (T)ObjectUtils.DeserializeObject(messageReader); + return update; + } + + private async Task HandleGzipPackedAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -263,14 +330,14 @@ private bool HandleUpdate(ulong messageId, int sequence, BinaryReader messageRea } using (BinaryReader compressedReader = new BinaryReader(ms)) { - processMessage(messageId, sequence, compressedReader, request, token); + await processMessageAsync(messageId, sequence, compressedReader, request, token); } } return true; } - private bool HandleRpcResult(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request) + private bool HandleRpcResult(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request) { uint code = messageReader.ReadUInt32(); ulong requestId = messageReader.ReadUInt64(); @@ -299,6 +366,7 @@ private bool HandleRpcResult(ulong messageId, int sequence, BinaryReader message { // rpc_error int errorCode = messageReader.ReadInt32(); string errorMessage = Serializers.String.Read(messageReader); + Console.Error.WriteLine($"ERROR: {errorMessage} - {errorCode}"); if (errorMessage.StartsWith("FLOOD_WAIT_")) { @@ -409,7 +477,7 @@ private bool HandleBadMsgNotification(ulong messageId, int sequence, BinaryReade 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); @@ -429,7 +497,7 @@ private bool HandleBadMsgNotification(ulong messageId, int sequence, BinaryReade return true; } - private bool HandleBadServerSalt(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request, CancellationToken token = default(CancellationToken)) + private async Task HandleBadServerSaltAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -444,7 +512,7 @@ private bool HandleBadMsgNotification(ulong messageId, int sequence, BinaryReade session.Salt = newSalt; //resend - Send(request, token); + await Send(request, token); /* if(!runningRequests.ContainsKey(badMsgId)) { logger.debug("bad server salt on unknown message"); @@ -492,7 +560,7 @@ private bool HandleFutureSalts(ulong messageId, int sequence, BinaryReader messa return true; } - private bool HandlePong(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request) + private bool HandlePong(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request) { uint code = messageReader.ReadUInt32(); ulong msgId = messageReader.ReadUInt64(); @@ -510,7 +578,7 @@ private bool HandlePing(ulong messageId, int sequence, BinaryReader messageReade return false; } - private bool HandleContainer(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request, CancellationToken token = default(CancellationToken)) + private async Task HandleContainerAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -524,11 +592,16 @@ private bool HandlePing(ulong messageId, int sequence, BinaryReader messageReade long beginPosition = messageReader.BaseStream.Position; try { - if (!processMessage(innerMessageId, sequence, messageReader, request, token)) + var processedMessage = await processMessageAsync(innerMessageId, sequence, messageReader, request, token); + if (!processedMessage) { messageReader.BaseStream.Position = beginPosition + innerLength; } } + catch (InvalidOperationException e) + { + throw e; + } catch (Exception e) { // logger.error("failed to process message in container: {0}", e); diff --git a/TLSharp.Core/Network/Sniffer.cs b/TLSharp.Core/Network/Sniffer.cs new file mode 100644 index 00000000..a1033bc2 --- /dev/null +++ b/TLSharp.Core/Network/Sniffer.cs @@ -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(); + } + } +} diff --git a/TLSharp.Core/Network/TcpTransport.cs b/TLSharp.Core/Network/TcpTransport.cs index cfc79561..306501e8 100644 --- a/TLSharp.Core/Network/TcpTransport.cs +++ b/TLSharp.Core/Network/TcpTransport.cs @@ -15,6 +15,7 @@ public class TcpTransport : IDisposable 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) { @@ -54,11 +55,75 @@ public TcpTransport(string address, int port, TcpClientConnectionHandler handler public async Task Receive(CancellationToken token = default(CancellationToken)) { + var stream = tcpClient.GetStream(); + var packetLengthBytes = new byte[4]; if (await stream.ReadAsync(packetLengthBytes, 0, 4, token).ConfigureAwait(false) != 4) throw new InvalidOperationException("Couldn't read the packet length"); int packetLength = BitConverter.ToInt32(packetLengthBytes, 0); + 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); + + 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 Crc32(); + var computedChecksum = crc32.ComputeHash(rv).Reverse(); + + if (!crcBytes.SequenceEqual(computedChecksum)) + { + throw new InvalidOperationException("invalid checksum! skip"); + } + + return new TcpMessage(seq, body); + } + + public async Task Receieve(int timeoutms) + { + 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); + var seqBytes = new byte[4]; if (await stream.ReadAsync(seqBytes, 0, 4, token).ConfigureAwait(false) != 4) throw new InvalidOperationException("Couldn't read the sequence"); @@ -106,7 +171,6 @@ public bool IsConnected } } - public void Dispose() { if (tcpClient.Connected) diff --git a/TLSharp.Core/TLSharp.Core.csproj b/TLSharp.Core/TLSharp.Core.csproj index 9924ab2e..52d19662 100644 --- a/TLSharp.Core/TLSharp.Core.csproj +++ b/TLSharp.Core/TLSharp.Core.csproj @@ -75,6 +75,8 @@ + + diff --git a/TLSharp.Core/TelegramClient.cs b/TLSharp.Core/TelegramClient.cs index e5b0819f..13b76ba6 100644 --- a/TLSharp.Core/TelegramClient.cs +++ b/TLSharp.Core/TelegramClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; @@ -16,7 +16,6 @@ using TLSharp.Core.Exceptions; using TLSharp.Core.MTProto.Crypto; using TLSharp.Core.Network; -using TLSharp.Core.Network.Exceptions; using TLSharp.Core.Utils; using TLAuthorization = TeleSharp.TL.Auth.TLAuthorization; @@ -25,14 +24,24 @@ namespace TLSharp.Core public class TelegramClient : IDisposable { private MtProtoSender sender; + private AuthKey _key; private TcpTransport transport; - private string apiHash = String.Empty; + private string apiHash = ""; private int apiId = 0; private Session session; private List dcOptions; private TcpClientConnectionHandler handler; private DataCenterIPVersion dcIpVersion; + private bool _looping = true; + + 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; } @@ -80,6 +89,7 @@ public TelegramClient(int apiId, string apiHash, } sender = new MtProtoSender(transport, session); + sender.UpdatesEvent += SenderUpdatesEvent; //set-up layer var config = new TLRequestGetConfig(); @@ -149,6 +159,50 @@ public TelegramClient(int apiId, string apiHash, } } + public void Close() + { + _looping = false; + } + + public async Task MainLoopAsync(int timeslicems, CancellationToken token = default(CancellationToken)) + { + var lastPing = DateTime.UtcNow; + await SendPingAsync(); + while (_looping) + { + try + { + await WaitEventAsync(timeslicems, token); + } + catch (OperationCanceledException) + { + // Handle timeout, no problem + } + finally + { + var now = DateTime.UtcNow; + if ((now - lastPing).TotalSeconds >= 30) + { + await SendPingAsync(); + lastPing = now; + } + if (ScheduledTasks != null) + { + ScheduledTasks.Invoke(this); + ScheduledTasks = null; + } + IdleTasks?.Invoke(this); + } + + token.ThrowIfCancellationRequested(); + } + } + + private void SenderUpdatesEvent (TLAbsUpdates updates) + { + Updates?.Invoke(this, updates); + } + private async Task RequestWithDcMigration(TLMethod request, CancellationToken token = default(CancellationToken)) { if (sender == null) @@ -178,6 +232,11 @@ public TelegramClient(int apiId, string apiHash, } } + public async Task WaitEventAsync(int timeoutms, CancellationToken token = default(CancellationToken)) + { + await sender.Receive (timeoutms, token); + } + public bool IsUserAuthorized() { return session.TLUser != null; diff --git a/TLSharp.Core/packages.config b/TLSharp.Core/packages.config index 6b8deb9c..518c7dc2 100644 --- a/TLSharp.Core/packages.config +++ b/TLSharp.Core/packages.config @@ -1,3 +1,5 @@  + + \ No newline at end of file diff --git a/TLSharp.Tests.NUnit/Test.cs b/TLSharp.Tests.NUnit/Test.cs index 40e54ac6..3696b07e 100644 --- a/TLSharp.Tests.NUnit/Test.cs +++ b/TLSharp.Tests.NUnit/Test.cs @@ -81,5 +81,11 @@ public override async Task SendMessageByUserNameTest() { await base.SendMessageByUserNameTest(); } + + [Test] + public override async Task GetUpdatesForUser() + { + await base.GetUpdatesForUser(); + } } } diff --git a/TLSharp.Tests.VS/TLSharpTestsVs.cs b/TLSharp.Tests.VS/TLSharpTestsVs.cs index 8f6c58dc..ccb7ea7a 100644 --- a/TLSharp.Tests.VS/TLSharpTestsVs.cs +++ b/TLSharp.Tests.VS/TLSharpTestsVs.cs @@ -79,5 +79,12 @@ public override async Task SendMessageByUserNameTest() { await base.SendMessageByUserNameTest(); } + + [TestMethod] + public override async Task GetUpdatesForUser() + { + await base.GetUpdatesForUser(); + } + } } diff --git a/TLSharp.Tests/TLSharpTests.cs b/TLSharp.Tests/TLSharpTests.cs index 6acde729..e54c1318 100644 --- a/TLSharp.Tests/TLSharpTests.cs +++ b/TLSharp.Tests/TLSharpTests.cs @@ -1,5 +1,6 @@  using System; +using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.IO; @@ -11,8 +12,6 @@ using TeleSharp.TL.Messages; using TLSharp.Core; using TLSharp.Core.Exceptions; -using TLSharp.Core.Network; -using TLSharp.Core.Network.Exceptions; using TLSharp.Core.Utils; namespace TLSharp.Tests @@ -129,7 +128,7 @@ public virtual async Task AuthUser() var hash = await client.SendCodeRequestAsync(NumberToAuthenticate); var code = CodeToAuthenticate; // you can change code in debugger too - if (String.IsNullOrWhiteSpace(code)) + if (string.IsNullOrWhiteSpace(code)) { throw new Exception("CodeToAuthenticate is empty in the app.config file, fill it with the code you just got now by SMS/Telegram"); } @@ -178,7 +177,7 @@ public virtual async Task SendMessageTest() if (user == null) { - throw new System.Exception("Number was not found in Contacts List of user: " + NumberToSendMessage); + throw new Exception("Number was not found in Contacts List of user: " + NumberToSendMessage); } await client.SendTypingAsync(new TLInputPeerUser() { UserId = user.Id }); @@ -192,7 +191,7 @@ public virtual async Task SendMessageToChannelTest() await client.ConnectAsync(); - var dialogs = (TLDialogs) await client.GetUserDialogsAsync(); + var dialogs = (TLDialogs)await client.GetUserDialogsAsync(); var chat = dialogs.Chats .OfType() .FirstOrDefault(c => c.Title == "TestGroup"); @@ -269,7 +268,7 @@ public virtual async Task DownloadFileFromContactTest() Version = document.Version }, document.Size); - + Assert.IsTrue(resFile.Bytes.Length > 0); } @@ -284,9 +283,9 @@ public virtual async Task DownloadFileFromWrongLocationTest() var user = result.Users .OfType() .FirstOrDefault(x => x.Id == 5880094); - + var photo = ((TLUserProfilePhoto)user.Photo); - var photoLocation = (TLFileLocation) photo.PhotoBig; + var photoLocation = (TLFileLocation)photo.PhotoBig; var resFile = await client.GetFile(new TLInputFileLocation() { @@ -295,7 +294,7 @@ public virtual async Task DownloadFileFromWrongLocationTest() VolumeId = photoLocation.VolumeId }, 1024); - var res = await client.GetUserDialogsAsync(); + var res = await client.GetUserDialogsAsync(); Assert.IsTrue(resFile.Bytes.Length > 0); } @@ -333,7 +332,7 @@ public virtual async Task FloodExceptionShouldNotCauseCannotReadPackageLengthErr { await CheckPhones(); } - catch (FloodException floodException) + catch (Core.Network.Exceptions.FloodException floodException) { Console.WriteLine($"FLOODEXCEPTION: {floodException}"); Thread.Sleep(floodException.TimeToWait); @@ -370,12 +369,74 @@ public virtual async Task SendMessageByUserNameTest() if (user == null) { - throw new System.Exception("Username was not found: " + UserNameToSendMessage); + throw new Exception("Username was not found: " + UserNameToSendMessage); } await client.SendTypingAsync(new TLInputPeerUser() { UserId = user.Id }); Thread.Sleep(3000); await client.SendMessageAsync(new TLInputPeerUser() { UserId = user.Id }, "TEST"); } + + public virtual async Task GetUpdatesForUser() + { + IList newMsgs = new List(); + TLUser user = null; + + var client = NewClient(); + await client.ConnectAsync(); + + if (client.IsUserAuthorized()) + user = client.Session.TLUser; + + else + { + var hash = await client.SendCodeRequestAsync(NumberToAuthenticate); + var code = CodeToAuthenticate; // you can change code in debugger too + if (string.IsNullOrWhiteSpace(code)) + { + throw new Exception("CodeToAuthenticate is empty in the app.config file, fill it with the code you just got now by SMS/Telegram"); + } + + try + { + user = await client.MakeAuthAsync(NumberToAuthenticate, hash, code); + } + catch (CloudPasswordNeededException) + { + var passwordSetting = await client.GetPasswordSetting(); + var password = PasswordToAuthenticate; + user = await client.MakeAuthWithPasswordAsync(passwordSetting, password); + } + catch (InvalidPhoneCodeException ex) + { + throw new Exception("CodeToAuthenticate is wrong in the app.config file, fill it with the code you just got now by SMS/Telegram", ex); + } + } + + client.Updates += (TelegramClient tclient, TLAbsUpdates updates) => + { + if (updates is TLUpdates) + { + var allupdates = updates as TLUpdates; + + foreach (var update in allupdates.Updates) + { + if (update is TLUpdateNewMessage) + { + var metaMsg = update as TLUpdateNewMessage; + var msg = metaMsg.Message as TLMessage; + newMsgs.Add(msg); + } + } + } + }; + + await client.MainLoopAsync(1000); + + // At this point you would send yourself a UPDATE_1 message to trigger update + + Assert.IsTrue(newMsgs.Count == 1); + Assert.IsTrue(newMsgs.First().Message.Equals("UPDATE_1")); + } } -} +} \ No newline at end of file From 43d9d121679855afa109b5773ceb952eb8904a22 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 26 Apr 2020 17:29:26 +0100 Subject: [PATCH 2/4] PR review changes --- README.md | 288 +++++++++++++------------- TLSharp.Core/Network/Exceptions.cs | 69 ------ TLSharp.Core/Network/MtProtoSender.cs | 23 +- TLSharp.Core/Network/TcpTransport.cs | 6 +- TLSharp.Core/TLSharp.Core.csproj | 1 - TLSharp.Core/TelegramClient.cs | 9 +- TLSharp.Core/packages.config | 2 - TLSharp.Tests/TLSharpTests.cs | 10 +- 8 files changed, 168 insertions(+), 240 deletions(-) delete mode 100644 TLSharp.Core/Network/Exceptions.cs diff --git a/README.md b/README.md index 727af523..0046d2b8 100644 --- a/README.md +++ b/README.md @@ -161,149 +161,143 @@ Full code you can see at [DownloadFileFromContactTest](https://github.com/sochix 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()) - { - 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()) - { - var channel = channelObj as TLChannel; - Console.WriteLine($"\tChat: {channel.Title}"); - } - - Console.WriteLine("Groups:"); - TLChat chat = null; - foreach (var chatObj in dialogs.Chats.OfType()) - { - chat = chatObj as TLChat; - Console.WriteLine($"Chat name: {chat.Title}"); - var request = new TLRequestGetFullChat() { ChatId = chat.Id }; - var fullChat = await client.SendRequestAsync(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); - } + TelegramClient client = null; + + // -- 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()) + { + 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()) + { + var channel = channelObj as TLChannel; + Console.WriteLine($"\tChat: {channel.Title}"); + } + + Console.WriteLine("Groups:"); + TLChat chat = null; + foreach (var chatObj in dialogs.Chats.OfType()) + { + chat = chatObj as TLChat; + Console.WriteLine($"Chat name: {chat.Title}"); + var request = new TLRequestGetFullChat() { ChatId = chat.Id }; + var fullChat = await client.SendRequestAsync(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(); + } } private void Client_Updates(TelegramClient client, TLAbsUpdates updates) @@ -323,7 +317,11 @@ Full code you can see at [DownloadFileFromContactTest](https://github.com/sochix { var peer = new TLInputPeerUser() { UserId = status.UserId }; client.SendMessageAsync(peer, "Você está online.").Wait(); - } catch {} + } + catch (Exception e) + { + Console.WriteLine(e); + } } } } @@ -371,7 +369,7 @@ Full code you can see at [DownloadFileFromContactTest](https://github.com/sochix private void MarkMessageRead(TelegramClient client, TLAbsInputPeer peer, int id) { - // An exception happens here but it's not fatal. + // An exception happens here but it's not fatal. try { var request = new TLRequestReadHistory(); diff --git a/TLSharp.Core/Network/Exceptions.cs b/TLSharp.Core/Network/Exceptions.cs deleted file mode 100644 index 878e64e4..00000000 --- a/TLSharp.Core/Network/Exceptions.cs +++ /dev/null @@ -1,69 +0,0 @@ -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) - { - } - } - - -} diff --git a/TLSharp.Core/Network/MtProtoSender.cs b/TLSharp.Core/Network/MtProtoSender.cs index cd010e9e..a2de927b 100644 --- a/TLSharp.Core/Network/MtProtoSender.cs +++ b/TLSharp.Core/Network/MtProtoSender.cs @@ -10,6 +10,7 @@ using TLSharp.Core.Exceptions; using TLSharp.Core.MTProto; using TLSharp.Core.MTProto.Crypto; +using TLSharp.Core.Network.Exceptions; using TLSharp.Core.Network.Requests; using TLSharp.Core.Utils; @@ -157,14 +158,14 @@ private Tuple DecodeMessage(byte[] body) public async Task Receive(TLMethod request, CancellationToken token = default(CancellationToken)) { - while (!request.ConfirmReceived) + while (!request.ConfirmReceived) { var result = DecodeMessage((await transport.Receive(token).ConfigureAwait(false)).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)) { - await processMessageAsync(result.Item2, result.Item3, messageReader, request, token); + await ProcessMessageAsync(result.Item2, result.Item3, messageReader, request, token); } token.ThrowIfCancellationRequested(); @@ -173,14 +174,14 @@ private Tuple DecodeMessage(byte[] body) return null; } - public async Task Receive(int timeoutms, CancellationToken token = default(CancellationToken)) + public async Task Receive(TimeSpan timeToWait, CancellationToken token = default(CancellationToken)) { - var result = DecodeMessage((await transport.Receieve(timeoutms)).Body); + var result = DecodeMessage((await transport.Receive(timeToWait)).Body); using (var messageStream = new MemoryStream(result.Item1, false)) using (var messageReader = new BinaryReader(messageStream)) { - await processMessageAsync(result.Item2, result.Item3, messageReader, null); + await ProcessMessageAsync(result.Item2, result.Item3, messageReader, null); } token.ThrowIfCancellationRequested(); @@ -203,7 +204,7 @@ private Tuple DecodeMessage(byte[] body) await Receive(pingRequest, token).ConfigureAwait(false); } - private async Task processMessageAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) + private async Task ProcessMessageAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -330,7 +331,7 @@ private TLAbsUpdates DecodeUpdate(BinaryReader messageReader) where T : TLAbs } using (BinaryReader compressedReader = new BinaryReader(ms)) { - await processMessageAsync(messageId, sequence, compressedReader, request, token); + await ProcessMessageAsync(messageId, sequence, compressedReader, request, token); } } @@ -592,7 +593,7 @@ private bool HandlePing(ulong messageId, int sequence, BinaryReader messageReade long beginPosition = messageReader.BaseStream.Position; try { - var processedMessage = await processMessageAsync(innerMessageId, sequence, messageReader, request, token); + var processedMessage = await ProcessMessageAsync(innerMessageId, sequence, messageReader, request, token); if (!processedMessage) { messageReader.BaseStream.Position = beginPosition + innerLength; @@ -600,7 +601,7 @@ private bool HandlePing(ulong messageId, int sequence, BinaryReader messageReade } catch (InvalidOperationException e) { - throw e; + throw; } catch (Exception e) { diff --git a/TLSharp.Core/Network/TcpTransport.cs b/TLSharp.Core/Network/TcpTransport.cs index 306501e8..5cac2524 100644 --- a/TLSharp.Core/Network/TcpTransport.cs +++ b/TLSharp.Core/Network/TcpTransport.cs @@ -102,13 +102,13 @@ public TcpTransport(string address, int port, TcpClientConnectionHandler handler return new TcpMessage(seq, body); } - public async Task Receieve(int timeoutms) + public async Task Receive(TimeSpan timeToWait) { var stream = tcpClient.GetStream(); var packetLengthBytes = new byte[4]; var token = tokenSource.Token; - stream.ReadTimeout = timeoutms; + stream.ReadTimeout = (int)timeToWait.TotalMilliseconds; int bytes = 0; try { @@ -118,7 +118,7 @@ public async Task Receieve(int timeoutms) var socketError = io.InnerException as SocketException; if (socketError != null && socketError.SocketErrorCode == SocketError.TimedOut) throw new OperationCanceledException(); - throw io; + throw; } if (bytes != 4) throw new InvalidOperationException("Couldn't read the packet length"); diff --git a/TLSharp.Core/TLSharp.Core.csproj b/TLSharp.Core/TLSharp.Core.csproj index 52d19662..62bdc6cc 100644 --- a/TLSharp.Core/TLSharp.Core.csproj +++ b/TLSharp.Core/TLSharp.Core.csproj @@ -76,7 +76,6 @@ - diff --git a/TLSharp.Core/TelegramClient.cs b/TLSharp.Core/TelegramClient.cs index 13b76ba6..6a23399f 100644 --- a/TLSharp.Core/TelegramClient.cs +++ b/TLSharp.Core/TelegramClient.cs @@ -16,6 +16,7 @@ using TLSharp.Core.Exceptions; using TLSharp.Core.MTProto.Crypto; using TLSharp.Core.Network; +using TLSharp.Core.Network.Exceptions; using TLSharp.Core.Utils; using TLAuthorization = TeleSharp.TL.Auth.TLAuthorization; @@ -164,7 +165,7 @@ public void Close() _looping = false; } - public async Task MainLoopAsync(int timeslicems, CancellationToken token = default(CancellationToken)) + public async Task MainLoopAsync(TimeSpan timeToWait, CancellationToken token = default(CancellationToken)) { var lastPing = DateTime.UtcNow; await SendPingAsync(); @@ -172,7 +173,7 @@ public void Close() { try { - await WaitEventAsync(timeslicems, token); + await WaitEventAsync(timeToWait, token); } catch (OperationCanceledException) { @@ -232,9 +233,9 @@ private void SenderUpdatesEvent (TLAbsUpdates updates) } } - public async Task WaitEventAsync(int timeoutms, CancellationToken token = default(CancellationToken)) + public async Task WaitEventAsync(TimeSpan timeToWait, CancellationToken token = default(CancellationToken)) { - await sender.Receive (timeoutms, token); + await sender.Receive (timeToWait, token); } public bool IsUserAuthorized() diff --git a/TLSharp.Core/packages.config b/TLSharp.Core/packages.config index 518c7dc2..6b8deb9c 100644 --- a/TLSharp.Core/packages.config +++ b/TLSharp.Core/packages.config @@ -1,5 +1,3 @@  - - \ No newline at end of file diff --git a/TLSharp.Tests/TLSharpTests.cs b/TLSharp.Tests/TLSharpTests.cs index e54c1318..c73bcec2 100644 --- a/TLSharp.Tests/TLSharpTests.cs +++ b/TLSharp.Tests/TLSharpTests.cs @@ -128,7 +128,7 @@ public virtual async Task AuthUser() var hash = await client.SendCodeRequestAsync(NumberToAuthenticate); var code = CodeToAuthenticate; // you can change code in debugger too - if (string.IsNullOrWhiteSpace(code)) + if (String.IsNullOrWhiteSpace(code)) { throw new Exception("CodeToAuthenticate is empty in the app.config file, fill it with the code you just got now by SMS/Telegram"); } @@ -177,7 +177,7 @@ public virtual async Task SendMessageTest() if (user == null) { - throw new Exception("Number was not found in Contacts List of user: " + NumberToSendMessage); + throw new System.Exception("Number was not found in Contacts List of user: " + NumberToSendMessage); } await client.SendTypingAsync(new TLInputPeerUser() { UserId = user.Id }); @@ -191,7 +191,7 @@ public virtual async Task SendMessageToChannelTest() await client.ConnectAsync(); - var dialogs = (TLDialogs)await client.GetUserDialogsAsync(); + var dialogs = (TLDialogs) await client.GetUserDialogsAsync(); var chat = dialogs.Chats .OfType() .FirstOrDefault(c => c.Title == "TestGroup"); @@ -294,7 +294,7 @@ public virtual async Task DownloadFileFromWrongLocationTest() VolumeId = photoLocation.VolumeId }, 1024); - var res = await client.GetUserDialogsAsync(); + var res = await client.GetUserDialogsAsync(); Assert.IsTrue(resFile.Bytes.Length > 0); } @@ -431,7 +431,7 @@ public virtual async Task GetUpdatesForUser() } }; - await client.MainLoopAsync(1000); + await client.MainLoopAsync(new TimeSpan(0, 0, 1)); // At this point you would send yourself a UPDATE_1 message to trigger update From f258b6ede8df747e2b14c1cdbb9d5790a3ceea55 Mon Sep 17 00:00:00 2001 From: mark Date: Sun, 10 May 2020 18:19:52 +0100 Subject: [PATCH 3/4] pr review updates --- README.md | 453 ++++++++++++-------------- TLSharp.Core/Network/MtProtoSender.cs | 26 +- TLSharp.Core/Network/TcpTransport.cs | 3 +- TLSharp.Core/TelegramClient.cs | 10 +- TLSharp.Tests/TLSharpTests.cs | 19 +- 5 files changed, 241 insertions(+), 270 deletions(-) diff --git a/README.md b/README.md index 0046d2b8..6382910e 100644 --- a/README.md +++ b/README.md @@ -140,250 +140,215 @@ Full code you can see at [DownloadFileFromContactTest](https://github.com/sochix # 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; - - // -- 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()) - { - 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()) - { - var channel = channelObj as TLChannel; - Console.WriteLine($"\tChat: {channel.Title}"); - } - - Console.WriteLine("Groups:"); - TLChat chat = null; - foreach (var chatObj in dialogs.Chats.OfType()) - { - chat = chatObj as TLChat; - Console.WriteLine($"Chat name: {chat.Title}"); - var request = new TLRequestGetFullChat() { ChatId = chat.Id }; - var fullChat = await client.SendRequestAsync(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(); - } - } - - 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 (Exception e) - { - Console.WriteLine(e); - } - } - } - } - 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(request).Wait(); - } - catch (InvalidOperationException e){ - System.Console.WriteLine($"MarkMessageRead Error: {e.getMessage()}") - } - - } - } - } +using System.Threading.Tasks; +using TeleSharp.TL; +using TLSharp.Core; +using System.Linq; +using TeleSharp.TL.Messages; + +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; + + client = new TelegramClient(APIId, APIHash); + // subscribe an event to receive live messages + client.Updates += ClientUpdates; + 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()) + { + 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()) + { + var channel = channelObj as TLChannel; + Console.WriteLine($"\tChat: {channel.Title}"); + } + + Console.WriteLine("Groups:"); + TLChat chat = null; + foreach (var chatObj in dialogs.Chats.OfType()) + { + chat = chatObj as TLChat; + Console.WriteLine($"Chat name: {chat.Title}"); + var request = new TLRequestGetFullChat() { ChatId = chat.Id }; + var fullChat = await client.SendRequestAsync(request); + + var participants = (fullChat.FullChat as TeleSharp.TL.TLChatFull).Participants as TLChatParticipants; + foreach (var p in participants.Participants) + { + if (p is TLChatParticipant chatParticipant) + { + Console.WriteLine($"\t{chatParticipant.UserId}"); + } + else if (p is TLChatParticipantAdmin chatParticipantAdmin) + { + Console.WriteLine($"\t{chatParticipantAdmin.UserId}**"); + } + else if (p is TLChatParticipantCreator chatParticipantCreator) + { + Console.WriteLine($"\t{chatParticipantCreator.UserId}**"); + } + } + + var peer = new TLInputPeerChat() { ChatId = chat.Id }; + var msg = await client.GetHistoryAsync(peer, 0, 0, 0); + Console.WriteLine(msg); + if (msg is TLMessages messages) + { + foreach (var message in messages.Messages) + { + if (message is TLMessage m1) + { + Console.WriteLine($"\t\t{m1.Id} {m1.Message}"); + } + else if (message is TLMessageService msgService) + { + Console.WriteLine($"\t\t{msgService.Id} {msgService.Action}"); + } + } + } + else if (msg is TLMessagesSlice messagesSlice) + { + bool done = false; + int total = 0; + while (!done) + { + foreach (var m1 in messagesSlice.Messages) + { + if (m1 is TLMessage message) + { + Console.WriteLine($"\t\t{message.Id} {message.Message}"); + ++total; + } + else if (m1 is TLMessageService messageService) + { + Console.WriteLine($"\t\t{messageService.Id} {messageService.Action}"); + ++total; + done = messageService.Action is TLMessageActionChatCreate; + } + } + msg = await client.GetHistoryAsync(peer, total, 0, 0); + } + } + } + + // -- Wait in a loop to handle incoming updates. No need to poll. + for (; ; ) + { + await client.WaitEventAsync(TimeSpan.FromSeconds(1)); + } + } + + private void ClientUpdates(TelegramClient client, TLAbsUpdates updates) + { + Console.WriteLine($"Got update: {updates}"); + if (updates is TLUpdateShort updateShort) + { + Console.WriteLine($"Short: {updateShort.Update}"); + if (updateShort.Update is TLUpdateUserStatus status) + { + Console.WriteLine($"User {status.UserId} is {status.Status}"); + if (status.Status is TLUserStatusOnline) + { + var peer = new TLInputPeerUser() { UserId = status.UserId }; + client.SendMessageAsync(peer, "Você está online.").Wait(); + } + } + } + else if (updates is TLUpdateShortMessage message) + { + Console.WriteLine($"Message: {message.Message}"); + MarkMessageRead(client, new TLInputPeerUser() { UserId = message.UserId }, message.Id); + } + else if (updates is TLUpdateShortChatMessage shortChatMessage) + { + Console.WriteLine($"Chat Message: {shortChatMessage.Message}"); + MarkMessageRead(client, new TLInputPeerChat() { ChatId = shortChatMessage.ChatId }, shortChatMessage.Id); + } + else if (updates is TLUpdates allUpdates) + { + foreach (var update in allUpdates.Updates) + { + Console.WriteLine($"\t{update}"); + if (update is TLUpdateNewChannelMessage metaMessage) + { + var channelMsg = metaMessage.Message as TLMessage; + Console.WriteLine($"Channel message: {channelMsg.Message}"); + var channel = allUpdates.Chats[0] as TLChannel; + MarkMessageRead(client, + new TLInputPeerChannel() { ChannelId = channel.Id, AccessHash = channel.AccessHash.Value }, + channelMsg.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) + { + try + { + var request = new TLRequestReadHistory(); + request.MaxId = id; + request.Peer = peer; + client.SendRequestAsync(request).Wait(); + } + catch (InvalidOperationException e) + { + Console.WriteLine($"MarkMessageRead Error: {e.Message}"); + } + } + } +} ``` # Available Methods diff --git a/TLSharp.Core/Network/MtProtoSender.cs b/TLSharp.Core/Network/MtProtoSender.cs index a2de927b..c99e2891 100644 --- a/TLSharp.Core/Network/MtProtoSender.cs +++ b/TLSharp.Core/Network/MtProtoSender.cs @@ -42,7 +42,7 @@ private int GenerateSequence(bool confirmed) return confirmed ? session.Sequence++ * 2 + 1 : session.Sequence * 2; } - public async Task Send(TLMethod request, CancellationToken token = default(CancellationToken)) + public async Task Send(TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -69,7 +69,7 @@ private int GenerateSequence(bool confirmed) session.Save(); } - public async Task Send(byte[] packet, TLMethod request, CancellationToken token = default(CancellationToken)) + public async Task Send(byte[] packet, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) { token.ThrowIfCancellationRequested(); @@ -156,7 +156,7 @@ private Tuple DecodeMessage(byte[] body) return new Tuple(message, remoteMessageId, remoteSequence); } - public async Task Receive(TLMethod request, CancellationToken token = default(CancellationToken)) + public async Task Receive(TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken)) { while (!request.ConfirmReceived) { @@ -269,17 +269,17 @@ private Tuple DecodeMessage(byte[] body) private bool HandleUpdate(uint code, int sequence, BinaryReader messageReader, TLMethod request) { - try - { - var update = ParseUpdate (code, messageReader); - if (update != null && UpdatesEvent != null) + try + { + var update = ParseUpdate(code, messageReader); + if (update != null && UpdatesEvent != null) { - UpdatesEvent (update); + UpdatesEvent(update); } return true; - } - catch (Exception e) - { + } + catch (Exception e) + { return false; } } @@ -338,7 +338,7 @@ private TLAbsUpdates DecodeUpdate(BinaryReader messageReader) where T : TLAbs return true; } - private bool HandleRpcResult(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request) + private bool HandleRpcResult(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request) { uint code = messageReader.ReadUInt32(); ulong requestId = messageReader.ReadUInt64(); @@ -561,7 +561,7 @@ private bool HandleFutureSalts(ulong messageId, int sequence, BinaryReader messa return true; } - private bool HandlePong(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request) + private bool HandlePong(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request) { uint code = messageReader.ReadUInt32(); ulong msgId = messageReader.ReadUInt64(); diff --git a/TLSharp.Core/Network/TcpTransport.cs b/TLSharp.Core/Network/TcpTransport.cs index 5cac2524..0433df30 100644 --- a/TLSharp.Core/Network/TcpTransport.cs +++ b/TLSharp.Core/Network/TcpTransport.cs @@ -113,7 +113,8 @@ public async Task Receive(TimeSpan timeToWait) try { bytes = stream.Read(packetLengthBytes, 0, 4); - } catch (System.IO.IOException io) + } + catch (System.IO.IOException io) { var socketError = io.InnerException as SocketException; if (socketError != null && socketError.SocketErrorCode == SocketError.TimedOut) diff --git a/TLSharp.Core/TelegramClient.cs b/TLSharp.Core/TelegramClient.cs index 6a23399f..666f4304 100644 --- a/TLSharp.Core/TelegramClient.cs +++ b/TLSharp.Core/TelegramClient.cs @@ -25,16 +25,16 @@ namespace TLSharp.Core public class TelegramClient : IDisposable { private MtProtoSender sender; - private AuthKey _key; + private AuthKey key; private TcpTransport transport; - private string apiHash = ""; + private string apiHash = String.Empty; private int apiId = 0; private Session session; private List dcOptions; private TcpClientConnectionHandler handler; private DataCenterIPVersion dcIpVersion; - private bool _looping = true; + private bool looping = true; public delegate void UpdatesEvent (TelegramClient source, TLAbsUpdates updates); public delegate void ClientEvent(TelegramClient source); @@ -162,14 +162,14 @@ public TelegramClient(int apiId, string apiHash, public void Close() { - _looping = false; + looping = false; } public async Task MainLoopAsync(TimeSpan timeToWait, CancellationToken token = default(CancellationToken)) { var lastPing = DateTime.UtcNow; await SendPingAsync(); - while (_looping) + while (looping) { try { diff --git a/TLSharp.Tests/TLSharpTests.cs b/TLSharp.Tests/TLSharpTests.cs index c73bcec2..9cad26aa 100644 --- a/TLSharp.Tests/TLSharpTests.cs +++ b/TLSharp.Tests/TLSharpTests.cs @@ -12,6 +12,7 @@ using TeleSharp.TL.Messages; using TLSharp.Core; using TLSharp.Core.Exceptions; +using TLSharp.Core.Network.Exceptions; using TLSharp.Core.Utils; namespace TLSharp.Tests @@ -285,7 +286,7 @@ public virtual async Task DownloadFileFromWrongLocationTest() .FirstOrDefault(x => x.Id == 5880094); var photo = ((TLUserProfilePhoto)user.Photo); - var photoLocation = (TLFileLocation)photo.PhotoBig; + var photoLocation = (TLFileLocation) photo.PhotoBig; var resFile = await client.GetFile(new TLInputFileLocation() { @@ -332,7 +333,7 @@ public virtual async Task FloodExceptionShouldNotCauseCannotReadPackageLengthErr { await CheckPhones(); } - catch (Core.Network.Exceptions.FloodException floodException) + catch (FloodException floodException) { Console.WriteLine($"FLOODEXCEPTION: {floodException}"); Thread.Sleep(floodException.TimeToWait); @@ -369,7 +370,7 @@ public virtual async Task SendMessageByUserNameTest() if (user == null) { - throw new Exception("Username was not found: " + UserNameToSendMessage); + throw new System.Exception("Username was not found: " + UserNameToSendMessage); } await client.SendTypingAsync(new TLInputPeerUser() { UserId = user.Id }); @@ -413,13 +414,16 @@ public virtual async Task GetUpdatesForUser() } } + // Things to note:- If the updates are not getting triggered, please re-authenticate the user + // Would trigger the updates on a seperate thread if possible + client.Updates += (TelegramClient tclient, TLAbsUpdates updates) => { if (updates is TLUpdates) { - var allupdates = updates as TLUpdates; + var allUpdates = updates as TLUpdates; - foreach (var update in allupdates.Updates) + foreach (var update in allUpdates.Updates) { if (update is TLUpdateNewMessage) { @@ -431,9 +435,10 @@ public virtual async Task GetUpdatesForUser() } }; - await client.MainLoopAsync(new TimeSpan(0, 0, 1)); + Console.WriteLine("Send yourself an UPDATE_1 message to trigger update during loop"); + Debug.WriteLine("Send yourself an UPDATE_1 message to trigger update during loop"); - // At this point you would send yourself a UPDATE_1 message to trigger update + await client.MainLoopAsync(new TimeSpan(0, 0, 1)); Assert.IsTrue(newMsgs.Count == 1); Assert.IsTrue(newMsgs.First().Message.Equals("UPDATE_1")); From 2d540787e74ad6af3adfcbb02db69e424e49a8ec Mon Sep 17 00:00:00 2001 From: mark Date: Tue, 12 May 2020 23:30:12 +0100 Subject: [PATCH 4/4] resolved PR comments --- README.md | 8 ++++---- TLSharp.Core/Network/MtProtoSender.cs | 15 ++++----------- TLSharp.Tests/TLSharpTests.cs | 5 +++-- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 6382910e..6c55b392 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Full code you can see at [DownloadFileFromContactTest](https://github.com/sochix # Events Sample code ```csharp - using System; +using System; using System.Threading.Tasks; using TeleSharp.TL; using TLSharp.Core; @@ -234,7 +234,7 @@ namespace TLSharpPOC var msg = await client.GetHistoryAsync(peer, 0, 0, 0); Console.WriteLine(msg); if (msg is TLMessages messages) - { + { foreach (var message in messages.Messages) { if (message is TLMessage m1) @@ -247,7 +247,7 @@ namespace TLSharpPOC } } } - else if (msg is TLMessagesSlice messagesSlice) + else if (msg is TLMessagesSlice messagesSlice) { bool done = false; int total = 0; @@ -273,7 +273,7 @@ namespace TLSharpPOC } // -- Wait in a loop to handle incoming updates. No need to poll. - for (; ; ) + while(true) { await client.WaitEventAsync(TimeSpan.FromSeconds(1)); } diff --git a/TLSharp.Core/Network/MtProtoSender.cs b/TLSharp.Core/Network/MtProtoSender.cs index c99e2891..37073ec3 100644 --- a/TLSharp.Core/Network/MtProtoSender.cs +++ b/TLSharp.Core/Network/MtProtoSender.cs @@ -269,19 +269,12 @@ private Tuple DecodeMessage(byte[] body) private bool HandleUpdate(uint code, int sequence, BinaryReader messageReader, TLMethod request) { - try + var update = ParseUpdate(code, messageReader); + if (update != null && UpdatesEvent != null) { - var update = ParseUpdate(code, messageReader); - if (update != null && UpdatesEvent != null) - { - UpdatesEvent(update); - } - return true; - } - catch (Exception e) - { - return false; + UpdatesEvent(update); } + return true; } private TLAbsUpdates ParseUpdate(uint code, BinaryReader messageReader) diff --git a/TLSharp.Tests/TLSharpTests.cs b/TLSharp.Tests/TLSharpTests.cs index 9cad26aa..8f2cfe50 100644 --- a/TLSharp.Tests/TLSharpTests.cs +++ b/TLSharp.Tests/TLSharpTests.cs @@ -382,6 +382,7 @@ public virtual async Task GetUpdatesForUser() { IList newMsgs = new List(); TLUser user = null; + var updateMsg = "Send yourself an UPDATE_1 message to trigger update during loop"; var client = NewClient(); await client.ConnectAsync(); @@ -435,8 +436,8 @@ public virtual async Task GetUpdatesForUser() } }; - Console.WriteLine("Send yourself an UPDATE_1 message to trigger update during loop"); - Debug.WriteLine("Send yourself an UPDATE_1 message to trigger update during loop"); + Console.WriteLine(updateMsg); + Debug.WriteLine(updateMsg); await client.MainLoopAsync(new TimeSpan(0, 0, 1));