This commit is contained in:
Mark West 2021-12-09 16:51:48 +08:00 committed by GitHub
commit 9ded8c75c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 553 additions and 41 deletions

214
README.md
View file

@ -151,6 +151,220 @@ 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) 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;
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<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 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.
while(true)
{
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<bool>(request).Wait();
}
catch (InvalidOperationException e)
{
Console.WriteLine($"MarkMessageRead Error: {e.Message}");
}
}
}
}
```
# Available Methods # Available Methods
For your convenience TLSharp have wrappers for several Telegram API methods. You could add your own, see details below. For your convenience TLSharp have wrappers for several Telegram API methods. You could add your own, see details below.

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
@ -24,8 +23,14 @@ namespace TLSharp.Core.Network
private readonly TcpTransport transport; private readonly TcpTransport transport;
private readonly Session session; private readonly Session session;
private readonly uint UpdatesTooLongID = (uint) new TLUpdatesTooLong().Constructor;
public readonly List<ulong> needConfirmation = new List<ulong>(); public readonly List<ulong> needConfirmation = new List<ulong>();
public delegate void HandleUpdates (TLAbsUpdates updates);
public event HandleUpdates UpdatesEvent;
public MtProtoSender(TcpTransport transport, Session session) public MtProtoSender(TcpTransport transport, Session session)
{ {
this.transport = transport; this.transport = transport;
@ -60,7 +65,6 @@ namespace TLSharp.Core.Network
} }
} }
using (var memory = new MemoryStream()) using (var memory = new MemoryStream())
using (var writer = new BinaryWriter(memory)) using (var writer = new BinaryWriter(memory))
{ {
@ -90,11 +94,12 @@ namespace TLSharp.Core.Network
plaintextWriter.Write(packet.Length); plaintextWriter.Write(packet.Length);
plaintextWriter.Write(packet); plaintextWriter.Write(packet);
msgKey = Helpers.CalcMsgKey(plaintextPacket.GetBuffer()); var buffer = plaintextPacket.GetBuffer();
ciphertext = AES.EncryptAES(Helpers.CalcKey(session.AuthKey.Data, msgKey, true), 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 (MemoryStream ciphertextPacket = makeMemory(8 + 16 + ciphertext.Length))
{ {
using (BinaryWriter writer = new BinaryWriter(ciphertextPacket)) using (BinaryWriter writer = new BinaryWriter(ciphertextPacket))
@ -108,6 +113,23 @@ namespace TLSharp.Core.Network
} }
} }
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<byte[], ulong, int> DecodeMessage(byte[] body) private Tuple<byte[], ulong, int> DecodeMessage(byte[] body)
{ {
byte[] message; byte[] message;
@ -147,9 +169,9 @@ namespace TLSharp.Core.Network
var result = DecodeMessage((await transport.Receive(token).ConfigureAwait(false)).Body); var result = DecodeMessage((await transport.Receive(token).ConfigureAwait(false)).Body);
using (var messageStream = new MemoryStream(result.Item1, false)) using (var messageStream = new MemoryStream(result.Item1, false))
using (var messageReader = new BinaryReader(messageStream)) 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(); token.ThrowIfCancellationRequested();
@ -158,6 +180,21 @@ namespace TLSharp.Core.Network
return null; return null;
} }
public async Task<byte[]> Receive(TimeSpan timeToWait, CancellationToken token = default(CancellationToken))
{
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);
}
token.ThrowIfCancellationRequested();
return null;
}
public async Task SendPingAsync(CancellationToken token = default(CancellationToken)) public async Task SendPingAsync(CancellationToken token = default(CancellationToken))
{ {
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
@ -173,7 +210,7 @@ namespace TLSharp.Core.Network
await Receive(pingRequest, token).ConfigureAwait(false); await Receive(pingRequest, token).ConfigureAwait(false);
} }
private bool processMessage(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request, CancellationToken token = default(CancellationToken)) private async Task<bool> ProcessMessageAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken))
{ {
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
@ -181,8 +218,10 @@ namespace TLSharp.Core.Network
// TODO: check sessionid // TODO: check sessionid
// TODO: check seqno // 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()); //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); needConfirmation.Add(messageId);
await Ack(token);
uint code = messageReader.ReadUInt32(); uint code = messageReader.ReadUInt32();
messageReader.BaseStream.Position -= 4; messageReader.BaseStream.Position -= 4;
@ -190,7 +229,7 @@ namespace TLSharp.Core.Network
{ {
case 0x73f1f8dc: // container case 0x73f1f8dc: // container
//logger.debug("MSG container"); //logger.debug("MSG container");
return HandleContainer(messageId, sequence, messageReader, request, token); return await HandleContainerAsync(messageId, sequence, messageReader, request, token);
case 0x7abe77ec: // ping case 0x7abe77ec: // ping
//logger.debug("MSG ping"); //logger.debug("MSG ping");
return HandlePing(messageId, sequence, messageReader); return HandlePing(messageId, sequence, messageReader);
@ -208,7 +247,7 @@ namespace TLSharp.Core.Network
return HandleMsgsAck(messageId, sequence, messageReader); return HandleMsgsAck(messageId, sequence, messageReader);
case 0xedab447b: // bad_server_salt case 0xedab447b: // bad_server_salt
//logger.debug("MSG 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 case 0xa7eff811: // bad_msg_notification
//logger.debug("MSG bad_msg_notification"); //logger.debug("MSG bad_msg_notification");
return HandleBadMsgNotification(messageId, sequence, messageReader); return HandleBadMsgNotification(messageId, sequence, messageReader);
@ -220,39 +259,61 @@ namespace TLSharp.Core.Network
return HandleRpcResult(messageId, sequence, messageReader, request); return HandleRpcResult(messageId, sequence, messageReader, request);
case 0x3072cfa1: // gzip_packed case 0x3072cfa1: // gzip_packed
//logger.debug("MSG 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 0xe317af7e:
case 0xd3f45784: case 0xd3f45784:
case 0x2b2fbd4e: case 0x2b2fbd4e:
case 0x78d4dec1: case 0x78d4dec1:
case 0x725b04c3: case 0x725b04c3:
case 0x74ae4240: case 0x74ae4240:
return HandleUpdate(messageId, sequence, messageReader); case 0x11f1331c:
return HandleUpdate(code, sequence, messageReader, request);
default: default:
//logger.debug("unknown message: {0}", code);
return false; return false;
} }
} }
private bool HandleUpdate(ulong messageId, int sequence, BinaryReader messageReader) private bool HandleUpdate(uint code, int sequence, BinaryReader messageReader, TLMethod request)
{ {
return false; var update = ParseUpdate(code, messageReader);
if (update != null && UpdatesEvent != null)
/* {
try UpdatesEvent(update);
{ }
UpdatesEvent(TL.Parse<Updates>(messageReader)); return true;
return true;
}
catch (Exception e)
{
logger.warning("update processing exception: {0}", e);
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<TLUpdatesTooLong>(messageReader);
case 0x914fbf11:
return DecodeUpdate<TLUpdateShortMessage> (messageReader);
case 0x16812688:
return DecodeUpdate<TLUpdateShortChatMessage> (messageReader);
case 0x78d4dec1:
return DecodeUpdate<TLUpdateShort> (messageReader);
case 0x725b04c3:
return DecodeUpdate<TLUpdatesCombined> (messageReader);
case 0x74ae4240:
return DecodeUpdate<TLUpdates> (messageReader);
case 0x11f1331c:
return DecodeUpdate<TLUpdateShortSentMessage> (messageReader);
default:
return null;
}
}
private TLAbsUpdates DecodeUpdate<T>(BinaryReader messageReader) where T : TLAbsUpdates
{
var ms = messageReader.BaseStream as MemoryStream;
var update = (T)ObjectUtils.DeserializeObject(messageReader);
return update;
}
private async Task<bool> HandleGzipPackedAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken))
{ {
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
@ -269,7 +330,7 @@ namespace TLSharp.Core.Network
} }
using (BinaryReader compressedReader = new BinaryReader(ms)) using (BinaryReader compressedReader = new BinaryReader(ms))
{ {
processMessage(messageId, sequence, compressedReader, request, token); await ProcessMessageAsync(messageId, sequence, compressedReader, request, token);
} }
} }
@ -305,6 +366,7 @@ namespace TLSharp.Core.Network
{ // rpc_error { // rpc_error
int errorCode = messageReader.ReadInt32(); int errorCode = messageReader.ReadInt32();
string errorMessage = Serializers.String.Read(messageReader); string errorMessage = Serializers.String.Read(messageReader);
Console.Error.WriteLine($"ERROR: {errorMessage} - {errorCode}");
if (errorMessage.StartsWith("FLOOD_WAIT_")) if (errorMessage.StartsWith("FLOOD_WAIT_"))
{ {
@ -415,7 +477,7 @@ namespace TLSharp.Core.Network
throw new InvalidOperationException("invalid container"); 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, logger.debug("bad_msg_notification: msgid {0}, seq {1}, errorcode {2}", requestId, requestSequence,
errorCode); errorCode);
@ -435,7 +497,7 @@ namespace TLSharp.Core.Network
return true; return true;
} }
private bool HandleBadServerSalt(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request, CancellationToken token = default(CancellationToken)) private async Task<bool> HandleBadServerSaltAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken))
{ {
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
@ -450,7 +512,7 @@ namespace TLSharp.Core.Network
session.Salt = newSalt; session.Salt = newSalt;
//resend //resend
Send(request, token); await Send(request, token);
/* /*
if(!runningRequests.ContainsKey(badMsgId)) { if(!runningRequests.ContainsKey(badMsgId)) {
logger.debug("bad server salt on unknown message"); logger.debug("bad server salt on unknown message");
@ -516,7 +578,7 @@ namespace TLSharp.Core.Network
return false; return false;
} }
private bool HandleContainer(ulong messageId, int sequence, BinaryReader messageReader, TLMethod request, CancellationToken token = default(CancellationToken)) private async Task<bool> HandleContainerAsync(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken))
{ {
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
@ -530,11 +592,16 @@ namespace TLSharp.Core.Network
long beginPosition = messageReader.BaseStream.Position; long beginPosition = messageReader.BaseStream.Position;
try try
{ {
if (!processMessage(innerMessageId, sequence, messageReader, request, token)) var processedMessage = await ProcessMessageAsync(innerMessageId, sequence, messageReader, request, token);
if (!processedMessage)
{ {
messageReader.BaseStream.Position = beginPosition + innerLength; messageReader.BaseStream.Position = beginPosition + innerLength;
} }
} }
catch (InvalidOperationException e)
{
throw;
}
catch (Exception e) catch (Exception e)
{ {
// logger.error("failed to process message in container: {0}", e); // logger.error("failed to process message in container: {0}", e);

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

@ -15,6 +15,7 @@ namespace TLSharp.Core.Network
private readonly TcpClient tcpClient; private readonly TcpClient tcpClient;
private readonly NetworkStream stream; private readonly NetworkStream stream;
private int sendCounter = 0; private int sendCounter = 0;
private CancellationTokenSource tokenSource = new CancellationTokenSource();
public TcpTransport(string address, int port, TcpClientConnectionHandler handler = null) public TcpTransport(string address, int port, TcpClientConnectionHandler handler = null)
{ {
@ -54,11 +55,76 @@ namespace TLSharp.Core.Network
public async Task<TcpMessage> Receive(CancellationToken token = default(CancellationToken)) public async Task<TcpMessage> Receive(CancellationToken token = default(CancellationToken))
{ {
var stream = tcpClient.GetStream();
var packetLengthBytes = new byte[4]; var packetLengthBytes = new byte[4];
if (await stream.ReadAsync(packetLengthBytes, 0, 4, token).ConfigureAwait(false) != 4) if (await stream.ReadAsync(packetLengthBytes, 0, 4, token).ConfigureAwait(false) != 4)
throw new InvalidOperationException("Couldn't read the packet length"); throw new InvalidOperationException("Couldn't read the packet length");
int packetLength = BitConverter.ToInt32(packetLengthBytes, 0); 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<TcpMessage> Receive(TimeSpan timeToWait)
{
var stream = tcpClient.GetStream();
var packetLengthBytes = new byte[4];
var token = tokenSource.Token;
stream.ReadTimeout = (int)timeToWait.TotalMilliseconds;
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;
}
if (bytes != 4)
throw new InvalidOperationException("Couldn't read the packet length");
int packetLength = BitConverter.ToInt32(packetLengthBytes, 0);
var seqBytes = new byte[4]; var seqBytes = new byte[4];
if (await stream.ReadAsync(seqBytes, 0, 4, token).ConfigureAwait(false) != 4) if (await stream.ReadAsync(seqBytes, 0, 4, token).ConfigureAwait(false) != 4)
throw new InvalidOperationException("Couldn't read the sequence"); throw new InvalidOperationException("Couldn't read the sequence");
@ -106,7 +172,6 @@ namespace TLSharp.Core.Network
} }
} }
public void Dispose() public void Dispose()
{ {
if (tcpClient.Connected) if (tcpClient.Connected)

View file

@ -75,6 +75,7 @@
<Compile Include="Session.cs" /> <Compile Include="Session.cs" />
<Compile Include="TelegramClient.cs" /> <Compile Include="TelegramClient.cs" />
<Compile Include="Utils\Helpers.cs" /> <Compile Include="Utils\Helpers.cs" />
<Compile Include="Network\Sniffer.cs" />
<Compile Include="DataCenter.cs" /> <Compile Include="DataCenter.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
@ -25,6 +25,7 @@ namespace TLSharp.Core
public class TelegramClient : IDisposable public class TelegramClient : IDisposable
{ {
private MtProtoSender sender; private MtProtoSender sender;
private AuthKey key;
private TcpTransport transport; private TcpTransport transport;
private string apiHash = String.Empty; private string apiHash = String.Empty;
private int apiId = 0; private int apiId = 0;
@ -33,6 +34,15 @@ namespace TLSharp.Core
private TcpClientConnectionHandler handler; private TcpClientConnectionHandler handler;
private DataCenterIPVersion dcIpVersion; 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 public Session Session
{ {
get { return session; } get { return session; }
@ -80,6 +90,7 @@ namespace TLSharp.Core
} }
sender = new MtProtoSender(transport, session); sender = new MtProtoSender(transport, session);
sender.UpdatesEvent += SenderUpdatesEvent;
//set-up layer //set-up layer
var config = new TLRequestGetConfig(); var config = new TLRequestGetConfig();
@ -149,6 +160,50 @@ namespace TLSharp.Core
} }
} }
public void Close()
{
looping = false;
}
public async Task MainLoopAsync(TimeSpan timeToWait, CancellationToken token = default(CancellationToken))
{
var lastPing = DateTime.UtcNow;
await SendPingAsync();
while (looping)
{
try
{
await WaitEventAsync(timeToWait, 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)) private async Task RequestWithDcMigration(TLMethod request, CancellationToken token = default(CancellationToken))
{ {
if (sender == null) if (sender == null)
@ -178,6 +233,11 @@ namespace TLSharp.Core
} }
} }
public async Task WaitEventAsync(TimeSpan timeToWait, CancellationToken token = default(CancellationToken))
{
await sender.Receive (timeToWait, token);
}
public bool IsUserAuthorized() public bool IsUserAuthorized()
{ {
return session.TLUser != null; return session.TLUser != null;

View file

@ -81,5 +81,11 @@ namespace TLSharp.Tests
{ {
await base.SendMessageByUserNameTest(); await base.SendMessageByUserNameTest();
} }
[Test]
public override async Task GetUpdatesForUser()
{
await base.GetUpdatesForUser();
}
} }
} }

View file

@ -79,5 +79,12 @@ namespace TLSharp.Tests
{ {
await base.SendMessageByUserNameTest(); await base.SendMessageByUserNameTest();
} }
[TestMethod]
public override async Task GetUpdatesForUser()
{
await base.GetUpdatesForUser();
}
} }
} }

View file

@ -1,5 +1,6 @@
 
using System; using System;
using System.Collections.Generic;
using System.Configuration; using System.Configuration;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -11,7 +12,6 @@ using TeleSharp.TL;
using TeleSharp.TL.Messages; using TeleSharp.TL.Messages;
using TLSharp.Core; using TLSharp.Core;
using TLSharp.Core.Exceptions; using TLSharp.Core.Exceptions;
using TLSharp.Core.Network;
using TLSharp.Core.Network.Exceptions; using TLSharp.Core.Network.Exceptions;
using TLSharp.Core.Utils; using TLSharp.Core.Utils;
@ -269,7 +269,7 @@ namespace TLSharp.Tests
Version = document.Version Version = document.Version
}, },
document.Size); document.Size);
Assert.IsTrue(resFile.Bytes.Length > 0); Assert.IsTrue(resFile.Bytes.Length > 0);
} }
@ -284,7 +284,7 @@ namespace TLSharp.Tests
var user = result.Users var user = result.Users
.OfType<TLUser>() .OfType<TLUser>()
.FirstOrDefault(x => x.Id == 5880094); .FirstOrDefault(x => x.Id == 5880094);
var photo = ((TLUserProfilePhoto)user.Photo); var photo = ((TLUserProfilePhoto)user.Photo);
var photoLocation = (TLFileLocation) photo.PhotoBig; var photoLocation = (TLFileLocation) photo.PhotoBig;
@ -377,5 +377,72 @@ namespace TLSharp.Tests
Thread.Sleep(3000); Thread.Sleep(3000);
await client.SendMessageAsync(new TLInputPeerUser() { UserId = user.Id }, "TEST"); await client.SendMessageAsync(new TLInputPeerUser() { UserId = user.Id }, "TEST");
} }
public virtual async Task GetUpdatesForUser()
{
IList<TLMessage> newMsgs = new List<TLMessage>();
TLUser user = null;
var updateMsg = "Send yourself an UPDATE_1 message to trigger update during loop";
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);
}
}
// 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;
foreach (var update in allUpdates.Updates)
{
if (update is TLUpdateNewMessage)
{
var metaMsg = update as TLUpdateNewMessage;
var msg = metaMsg.Message as TLMessage;
newMsgs.Add(msg);
}
}
}
};
Console.WriteLine(updateMsg);
Debug.WriteLine(updateMsg);
await client.MainLoopAsync(new TimeSpan(0, 0, 1));
Assert.IsTrue(newMsgs.Count == 1);
Assert.IsTrue(newMsgs.First().Message.Equals("UPDATE_1"));
}
} }
} }