mirror of
https://github.com/sochix/TLSharp.git
synced 2025-12-06 08:02:00 +01:00
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
This commit is contained in:
parent
bca8bc5631
commit
3e0e53bb09
251
README.md
251
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)
|
Full code you can see at [DownloadFileFromContactTest](https://github.com/sochix/TLSharp/blob/master/TLSharp.Tests/TLSharpTests.cs#L167)
|
||||||
|
|
||||||
|
# Events Sample code
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TeleSharp.TL;
|
||||||
|
using TLSharp.Core;
|
||||||
|
using System.Linq;
|
||||||
|
using TeleSharp.TL.Messages;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TLSharpPOC
|
||||||
|
{
|
||||||
|
class MainClass
|
||||||
|
{
|
||||||
|
const int APIId = 0;
|
||||||
|
const string APIHash = "???";
|
||||||
|
const string phone = "???";
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
new MainClass().MainAsync(args).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task MainAsync(string[] args)
|
||||||
|
{
|
||||||
|
TelegramClient client = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// -- if necessary, IP can be changed so the client can connect to the test network.
|
||||||
|
Session session = null;
|
||||||
|
// new Session(new FileSessionStore(), "session")
|
||||||
|
//{
|
||||||
|
// ServerAddress = "149.154.175.10",
|
||||||
|
// Port = 443
|
||||||
|
//};
|
||||||
|
//Console.WriteLine($"{session.ServerAddress}:{session.Port} {phone}");
|
||||||
|
client = new TelegramClient(APIId, APIHash, session);
|
||||||
|
// subscribe an event to receive live messages
|
||||||
|
client.Updates += Client_Updates;
|
||||||
|
await client.ConnectAsync();
|
||||||
|
Console.WriteLine($"Authorised: {client.IsUserAuthorized()}");
|
||||||
|
TLUser user = null;
|
||||||
|
// -- If the user has already authenticated, this step will prevent account from being blocked as it
|
||||||
|
// -- reuses the data from last authorisation.
|
||||||
|
if (client.IsUserAuthorized())
|
||||||
|
user = client.Session.TLUser;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var registered = await client.IsPhoneRegisteredAsync(phone);
|
||||||
|
var hash = await client.SendCodeRequestAsync(phone);
|
||||||
|
Console.Write("Code: ");
|
||||||
|
var code = Console.ReadLine();
|
||||||
|
if (!registered)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Sign up {phone}");
|
||||||
|
user = await client.SignUpAsync(phone, hash, code, "First", "Last");
|
||||||
|
}
|
||||||
|
Console.WriteLine($"Sign in {phone}");
|
||||||
|
user = await client.MakeAuthAsync(phone, hash, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
var contacts = await client.GetContactsAsync();
|
||||||
|
Console.WriteLine("Contacts:");
|
||||||
|
foreach (var contact in contacts.Users.OfType<TLUser>())
|
||||||
|
{
|
||||||
|
var contactUser = contact as TLUser;
|
||||||
|
Console.WriteLine($"\t{contact.Id} {contact.Phone} {contact.FirstName} {contact.LastName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var dialogs = (TLDialogs) await client.GetUserDialogsAsync();
|
||||||
|
Console.WriteLine("Channels: ");
|
||||||
|
foreach (var channelObj in dialogs.Chats.OfType<TLChannel>())
|
||||||
|
{
|
||||||
|
var channel = channelObj as TLChannel;
|
||||||
|
Console.WriteLine($"\tChat: {channel.Title}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("Groups:");
|
||||||
|
TLChat chat = null;
|
||||||
|
foreach (var chatObj in dialogs.Chats.OfType<TLChat>())
|
||||||
|
{
|
||||||
|
chat = chatObj as TLChat;
|
||||||
|
Console.WriteLine($"Chat name: {chat.Title}");
|
||||||
|
var request = new TLRequestGetFullChat() { ChatId = chat.Id };
|
||||||
|
var fullChat = await client.SendRequestAsync<TeleSharp.TL.Messages.TLChatFull>(request);
|
||||||
|
|
||||||
|
var participants = (fullChat.FullChat as TeleSharp.TL.TLChatFull).Participants as TLChatParticipants;
|
||||||
|
foreach (var p in participants.Participants)
|
||||||
|
{
|
||||||
|
if (p is TLChatParticipant)
|
||||||
|
{
|
||||||
|
var participant = p as TLChatParticipant;
|
||||||
|
Console.WriteLine($"\t{participant.UserId}");
|
||||||
|
}
|
||||||
|
else if (p is TLChatParticipantAdmin)
|
||||||
|
{
|
||||||
|
var participant = p as TLChatParticipantAdmin;
|
||||||
|
Console.WriteLine($"\t{participant.UserId}**");
|
||||||
|
}
|
||||||
|
else if (p is TLChatParticipantCreator)
|
||||||
|
{
|
||||||
|
var participant = p as TLChatParticipantCreator;
|
||||||
|
Console.WriteLine($"\t{participant.UserId}**");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var peer = new TLInputPeerChat() { ChatId = chat.Id };
|
||||||
|
var m = await client.GetHistoryAsync(peer, 0, 0, 0);
|
||||||
|
Console.WriteLine(m);
|
||||||
|
if (m is TLMessages)
|
||||||
|
{
|
||||||
|
var messages = m as TLMessages;
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var message in messages.Messages)
|
||||||
|
{
|
||||||
|
if (message is TLMessage)
|
||||||
|
{
|
||||||
|
var m1 = message as TLMessage;
|
||||||
|
Console.WriteLine($"\t\t{m1.Id} {m1.Message}");
|
||||||
|
}
|
||||||
|
else if (message is TLMessageService)
|
||||||
|
{
|
||||||
|
var m1 = message as TLMessageService;
|
||||||
|
Console.WriteLine($"\t\t{m1.Id} {m1.Action}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m is TLMessagesSlice)
|
||||||
|
{
|
||||||
|
bool done = false;
|
||||||
|
int total = 0;
|
||||||
|
while (!done)
|
||||||
|
{
|
||||||
|
var messages = m as TLMessagesSlice;
|
||||||
|
|
||||||
|
foreach (var m1 in messages.Messages)
|
||||||
|
{
|
||||||
|
if (m1 is TLMessage)
|
||||||
|
{
|
||||||
|
var message = m1 as TLMessage;
|
||||||
|
Console.WriteLine($"\t\t{message.Id} {message.Message}");
|
||||||
|
++total;
|
||||||
|
}
|
||||||
|
else if (m1 is TLMessageService)
|
||||||
|
{
|
||||||
|
var message = m1 as TLMessageService;
|
||||||
|
Console.WriteLine($"\t\t{message.Id} {message.Action}");
|
||||||
|
++total;
|
||||||
|
done = message.Action is TLMessageActionChatCreate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m = await client.GetHistoryAsync(peer, total, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Wait in a loop to handle incoming updates. No need to poll.
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
await client.WaitEventAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Client_Updates(TelegramClient client, TLAbsUpdates updates)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Got update: {updates}");
|
||||||
|
if (updates is TLUpdateShort)
|
||||||
|
{
|
||||||
|
var updateShort = updates as TLUpdateShort;
|
||||||
|
Console.WriteLine($"Short: {updateShort.Update}");
|
||||||
|
if (updateShort.Update is TLUpdateUserStatus)
|
||||||
|
{
|
||||||
|
var status = updateShort.Update as TLUpdateUserStatus;
|
||||||
|
Console.WriteLine($"User {status.UserId} is {status.Status}");
|
||||||
|
if (status.Status is TLUserStatusOnline)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var peer = new TLInputPeerUser() { UserId = status.UserId };
|
||||||
|
client.SendMessageAsync(peer, "Você está online.").Wait();
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (updates is TLUpdateShortMessage)
|
||||||
|
{
|
||||||
|
var message = updates as TLUpdateShortMessage;
|
||||||
|
Console.WriteLine($"Message: {message.Message}");
|
||||||
|
MarkMessageRead(client, new TLInputPeerUser() { UserId = message.UserId }, message.Id);
|
||||||
|
}
|
||||||
|
else if (updates is TLUpdateShortChatMessage)
|
||||||
|
{
|
||||||
|
var message = updates as TLUpdateShortChatMessage;
|
||||||
|
Console.WriteLine($"Chat Message: {message.Message}");
|
||||||
|
MarkMessageRead(client, new TLInputPeerChat() { ChatId = message.ChatId }, message.Id);
|
||||||
|
}
|
||||||
|
else if (updates is TLUpdates)
|
||||||
|
{
|
||||||
|
var allUpdates = updates as TLUpdates;
|
||||||
|
foreach (var update in allUpdates.Updates)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\t{update}");
|
||||||
|
if (update is TLUpdateNewChannelMessage)
|
||||||
|
{
|
||||||
|
var metaMessage = update as TLUpdateNewChannelMessage;
|
||||||
|
var message = metaMessage.Message as TLMessage;
|
||||||
|
Console.WriteLine($"Channel message: {message.Message}");
|
||||||
|
var channel = allUpdates.Chats[0] as TLChannel;
|
||||||
|
MarkMessageRead(client,
|
||||||
|
new TLInputPeerChannel() { ChannelId = channel.Id, AccessHash = channel.AccessHash.Value },
|
||||||
|
message.Id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var user in allUpdates.Users)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{user}");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var chat in allUpdates.Chats)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{chat}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MarkMessageRead(TelegramClient client, TLAbsInputPeer peer, int id)
|
||||||
|
{
|
||||||
|
// An exception happens here but it's not fatal.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new TLRequestReadHistory();
|
||||||
|
request.MaxId = id;
|
||||||
|
request.Peer = peer;
|
||||||
|
client.SendRequestAsync<bool>(request).Wait();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException e){
|
||||||
|
System.Console.WriteLine($"MarkMessageRead Error: {e.getMessage()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# 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.
|
||||||
|
|
|
||||||
69
TLSharp.Core/Network/Exceptions.cs
Normal file
69
TLSharp.Core/Network/Exceptions.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
using System;
|
||||||
|
namespace TLSharp.Core.Network
|
||||||
|
{
|
||||||
|
public class FloodException : Exception
|
||||||
|
{
|
||||||
|
public TimeSpan TimeToWait { get; private set; }
|
||||||
|
|
||||||
|
internal FloodException(TimeSpan timeToWait)
|
||||||
|
: base($"Flood prevention. Telegram now requires your program to do requests again only after {timeToWait.TotalSeconds} seconds have passed ({nameof(TimeToWait)} property)." +
|
||||||
|
" If you think the culprit of this problem may lie in TLSharp's implementation, open a Github issue please.")
|
||||||
|
{
|
||||||
|
TimeToWait = timeToWait;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BadMessageException : Exception
|
||||||
|
{
|
||||||
|
internal BadMessageException(string description) : base(description)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal abstract class DataCenterMigrationException : Exception
|
||||||
|
{
|
||||||
|
internal int DC { get; private set; }
|
||||||
|
|
||||||
|
private const string REPORT_MESSAGE =
|
||||||
|
" See: https://github.com/sochix/TLSharp#i-get-a-xxxmigrationexception-or-a-migrate_x-error";
|
||||||
|
|
||||||
|
protected DataCenterMigrationException(string msg, int dc) : base(msg + REPORT_MESSAGE)
|
||||||
|
{
|
||||||
|
DC = dc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PhoneMigrationException : DataCenterMigrationException
|
||||||
|
{
|
||||||
|
internal PhoneMigrationException(int dc)
|
||||||
|
: base($"Phone number registered to a different DC: {dc}.", dc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class FileMigrationException : DataCenterMigrationException
|
||||||
|
{
|
||||||
|
internal FileMigrationException(int dc)
|
||||||
|
: base($"File located on a different DC: {dc}.", dc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class UserMigrationException : DataCenterMigrationException
|
||||||
|
{
|
||||||
|
internal UserMigrationException(int dc)
|
||||||
|
: base($"User located on a different DC: {dc}.", dc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NetworkMigrationException : DataCenterMigrationException
|
||||||
|
{
|
||||||
|
internal NetworkMigrationException(int dc)
|
||||||
|
: base($"Network located on a different DC: {dc}.", dc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -11,7 +10,6 @@ using TeleSharp.TL;
|
||||||
using TLSharp.Core.Exceptions;
|
using TLSharp.Core.Exceptions;
|
||||||
using TLSharp.Core.MTProto;
|
using TLSharp.Core.MTProto;
|
||||||
using TLSharp.Core.MTProto.Crypto;
|
using TLSharp.Core.MTProto.Crypto;
|
||||||
using TLSharp.Core.Network.Exceptions;
|
|
||||||
using TLSharp.Core.Network.Requests;
|
using TLSharp.Core.Network.Requests;
|
||||||
using TLSharp.Core.Utils;
|
using TLSharp.Core.Utils;
|
||||||
|
|
||||||
|
|
@ -24,8 +22,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;
|
||||||
|
|
@ -37,7 +41,7 @@ namespace TLSharp.Core.Network
|
||||||
return confirmed ? session.Sequence++ * 2 + 1 : session.Sequence * 2;
|
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();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
|
@ -54,7 +58,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))
|
||||||
{
|
{
|
||||||
|
|
@ -65,7 +68,7 @@ namespace TLSharp.Core.Network
|
||||||
session.Save();
|
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();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
|
@ -84,11 +87,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))
|
||||||
|
|
@ -102,6 +106,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;
|
||||||
|
|
@ -134,16 +155,16 @@ namespace TLSharp.Core.Network
|
||||||
return new Tuple<byte[], ulong, int>(message, remoteMessageId, remoteSequence);
|
return new Tuple<byte[], ulong, int>(message, remoteMessageId, remoteSequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<byte[]> Receive(TeleSharp.TL.TLMethod request, CancellationToken token = default(CancellationToken))
|
public async Task<byte[]> Receive(TLMethod request, CancellationToken token = default(CancellationToken))
|
||||||
{
|
{
|
||||||
while (!request.ConfirmReceived)
|
while (!request.ConfirmReceived)
|
||||||
{
|
{
|
||||||
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();
|
||||||
|
|
@ -152,6 +173,21 @@ namespace TLSharp.Core.Network
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<byte[]> 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))
|
public async Task SendPingAsync(CancellationToken token = default(CancellationToken))
|
||||||
{
|
{
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
@ -167,7 +203,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();
|
||||||
|
|
||||||
|
|
@ -175,8 +211,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;
|
||||||
|
|
@ -184,7 +222,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);
|
||||||
|
|
@ -202,7 +240,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);
|
||||||
|
|
@ -214,39 +252,68 @@ 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;
|
|
||||||
|
|
||||||
/*
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
UpdatesEvent(TL.Parse<Updates>(messageReader));
|
var update = ParseUpdate (code, messageReader);
|
||||||
return true;
|
if (update != null && UpdatesEvent != null)
|
||||||
|
{
|
||||||
|
UpdatesEvent (update);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
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<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();
|
||||||
|
|
||||||
|
|
@ -263,14 +330,14 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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();
|
uint code = messageReader.ReadUInt32();
|
||||||
ulong requestId = messageReader.ReadUInt64();
|
ulong requestId = messageReader.ReadUInt64();
|
||||||
|
|
@ -299,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_"))
|
||||||
{
|
{
|
||||||
|
|
@ -409,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);
|
||||||
|
|
@ -429,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();
|
||||||
|
|
||||||
|
|
@ -444,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");
|
||||||
|
|
@ -492,7 +560,7 @@ namespace TLSharp.Core.Network
|
||||||
return true;
|
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();
|
uint code = messageReader.ReadUInt32();
|
||||||
ulong msgId = messageReader.ReadUInt64();
|
ulong msgId = messageReader.ReadUInt64();
|
||||||
|
|
@ -510,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();
|
||||||
|
|
||||||
|
|
@ -524,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 e;
|
||||||
|
}
|
||||||
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);
|
||||||
|
|
|
||||||
25
TLSharp.Core/Network/Sniffer.cs
Normal file
25
TLSharp.Core/Network/Sniffer.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,75 @@ 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> 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];
|
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 +171,6 @@ namespace TLSharp.Core.Network
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (tcpClient.Connected)
|
if (tcpClient.Connected)
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,8 @@
|
||||||
<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="Network\Exceptions.cs" />
|
||||||
<Compile Include="DataCenter.cs" />
|
<Compile Include="DataCenter.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -16,7 +16,6 @@ using TLSharp.Core.Auth;
|
||||||
using TLSharp.Core.Exceptions;
|
using TLSharp.Core.Exceptions;
|
||||||
using TLSharp.Core.MTProto.Crypto;
|
using TLSharp.Core.MTProto.Crypto;
|
||||||
using TLSharp.Core.Network;
|
using TLSharp.Core.Network;
|
||||||
using TLSharp.Core.Network.Exceptions;
|
|
||||||
using TLSharp.Core.Utils;
|
using TLSharp.Core.Utils;
|
||||||
using TLAuthorization = TeleSharp.TL.Auth.TLAuthorization;
|
using TLAuthorization = TeleSharp.TL.Auth.TLAuthorization;
|
||||||
|
|
||||||
|
|
@ -25,14 +24,24 @@ 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 = "";
|
||||||
private int apiId = 0;
|
private int apiId = 0;
|
||||||
private Session session;
|
private Session session;
|
||||||
private List<TLDcOption> dcOptions;
|
private List<TLDcOption> dcOptions;
|
||||||
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 +89,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 +159,50 @@ namespace TLSharp.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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))
|
private async Task RequestWithDcMigration(TLMethod request, CancellationToken token = default(CancellationToken))
|
||||||
{
|
{
|
||||||
if (sender == null)
|
if (sender == null)
|
||||||
|
|
@ -178,6 +232,11 @@ namespace TLSharp.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task WaitEventAsync(int timeoutms, CancellationToken token = default(CancellationToken))
|
||||||
|
{
|
||||||
|
await sender.Receive (timeoutms, token);
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsUserAuthorized()
|
public bool IsUserAuthorized()
|
||||||
{
|
{
|
||||||
return session.TLUser != null;
|
return session.TLUser != null;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
|
<package id="MarkerMetro.Unity.Ionic.Zlib" version="2.0.0.14" targetFramework="net452" />
|
||||||
|
<package id="NLog" version="4.4.12" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
||||||
|
|
@ -81,5 +81,11 @@ namespace TLSharp.Tests
|
||||||
{
|
{
|
||||||
await base.SendMessageByUserNameTest();
|
await base.SendMessageByUserNameTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public override async Task GetUpdatesForUser()
|
||||||
|
{
|
||||||
|
await base.GetUpdatesForUser();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,5 +79,12 @@ namespace TLSharp.Tests
|
||||||
{
|
{
|
||||||
await base.SendMessageByUserNameTest();
|
await base.SendMessageByUserNameTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public override async Task GetUpdatesForUser()
|
||||||
|
{
|
||||||
|
await base.GetUpdatesForUser();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,8 +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.Utils;
|
using TLSharp.Core.Utils;
|
||||||
|
|
||||||
namespace TLSharp.Tests
|
namespace TLSharp.Tests
|
||||||
|
|
@ -129,7 +128,7 @@ namespace TLSharp.Tests
|
||||||
var hash = await client.SendCodeRequestAsync(NumberToAuthenticate);
|
var hash = await client.SendCodeRequestAsync(NumberToAuthenticate);
|
||||||
var code = CodeToAuthenticate; // you can change code in debugger too
|
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");
|
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 @@ namespace TLSharp.Tests
|
||||||
|
|
||||||
if (user == null)
|
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 });
|
await client.SendTypingAsync(new TLInputPeerUser() { UserId = user.Id });
|
||||||
|
|
@ -192,7 +191,7 @@ namespace TLSharp.Tests
|
||||||
|
|
||||||
await client.ConnectAsync();
|
await client.ConnectAsync();
|
||||||
|
|
||||||
var dialogs = (TLDialogs) await client.GetUserDialogsAsync();
|
var dialogs = (TLDialogs)await client.GetUserDialogsAsync();
|
||||||
var chat = dialogs.Chats
|
var chat = dialogs.Chats
|
||||||
.OfType<TLChannel>()
|
.OfType<TLChannel>()
|
||||||
.FirstOrDefault(c => c.Title == "TestGroup");
|
.FirstOrDefault(c => c.Title == "TestGroup");
|
||||||
|
|
@ -286,7 +285,7 @@ namespace TLSharp.Tests
|
||||||
.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;
|
||||||
|
|
||||||
var resFile = await client.GetFile(new TLInputFileLocation()
|
var resFile = await client.GetFile(new TLInputFileLocation()
|
||||||
{
|
{
|
||||||
|
|
@ -333,7 +332,7 @@ namespace TLSharp.Tests
|
||||||
{
|
{
|
||||||
await CheckPhones();
|
await CheckPhones();
|
||||||
}
|
}
|
||||||
catch (FloodException floodException)
|
catch (Core.Network.Exceptions.FloodException floodException)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"FLOODEXCEPTION: {floodException}");
|
Console.WriteLine($"FLOODEXCEPTION: {floodException}");
|
||||||
Thread.Sleep(floodException.TimeToWait);
|
Thread.Sleep(floodException.TimeToWait);
|
||||||
|
|
@ -370,12 +369,74 @@ namespace TLSharp.Tests
|
||||||
|
|
||||||
if (user == null)
|
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 });
|
await client.SendTypingAsync(new TLInputPeerUser() { UserId = user.Id });
|
||||||
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 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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue