Added auto dc switching

This commit is contained in:
Ilya Pirozhneko 2016-01-27 14:02:18 +03:00
parent f56ffc7c6f
commit 824ba77e12
7 changed files with 179 additions and 132 deletions

View file

@ -4321,8 +4321,8 @@ namespace TLSharp.Core.MTProto
Serializers.String.write(writer, this.first_name);
Serializers.String.write(writer, this.last_name);
Serializers.String.write(writer, this.phone);
this.photo.Write(writer);
this.status.Write(writer);
//this.photo.Write(writer);
//this.status.Write(writer);
writer.Write(this.inactive ? 0x997275b5 : 0xbc799737);
}
@ -4332,8 +4332,8 @@ namespace TLSharp.Core.MTProto
this.first_name = Serializers.String.read(reader);
this.last_name = Serializers.String.read(reader);
this.phone = Serializers.String.read(reader);
this.photo = TL.Parse<UserProfilePhoto>(reader);
this.status = TL.Parse<UserStatus>(reader);
//this.photo = TL.Parse<UserProfilePhoto>(reader);
//this.status = TL.Parse<UserStatus>(reader);
this.inactive = reader.ReadUInt32() == 0x997275b5;
}

View file

@ -29,6 +29,11 @@ namespace TLSharp.Core.Network
_session = session;
}
public void ChangeTransport(TcpTransport transport)
{
_transport = transport;
}
private int GenerateSequence(bool confirmed)
{
return confirmed ? _session.Sequence++ * 2 + 1 : _session.Sequence * 2;
@ -101,9 +106,12 @@ namespace TLSharp.Core.Network
ulong remoteMessageId;
int remoteSequence;
using (MemoryStream inputStream = new MemoryStream(body))
using (BinaryReader inputReader = new BinaryReader(inputStream))
using (var inputStream = new MemoryStream(body))
using (var inputReader = new BinaryReader(inputStream))
{
if (inputReader.BaseStream.Length < 8)
throw new InvalidOperationException($"Can't decode packet");
ulong remoteAuthKeyId = inputReader.ReadUInt64(); // TODO: check auth key id
byte[] msgKey = inputReader.ReadBytes(16); // TODO: check msg_key correctness
AESKeyData keyData = Helpers.CalcKey(_session.AuthKey.Data, msgKey, false);
@ -130,8 +138,8 @@ namespace TLSharp.Core.Network
{
var result = DecodeMessage((await _transport.Receieve()).Body);
using (MemoryStream messageStream = new MemoryStream(result.Item1, false))
using (BinaryReader 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);
}
@ -271,7 +279,9 @@ namespace TLSharp.Core.Network
{
var resultString = Regex.Match(errorMessage, @"\d+").Value;
var dcIdx = int.Parse(resultString);
throw new InvalidOperationException($"Your phone number registered to {dcIdx} dc. Please update settings. See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for details.");
var exception = new InvalidOperationException($"Your phone number registered to {dcIdx} dc. Please update settings. See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for details.");
exception.Data.Add("dcId", dcIdx);
throw exception;
}
else
{

View file

@ -8,12 +8,10 @@ namespace TLSharp.Core.Network
{
public class TcpTransport : IDisposable
{
private const string defaultConnectionAddress = "91.108.56.165";
private const int defaultConnectionPort = 443;
private readonly TcpClient _tcpClient;
private int sendCounter = 0;
public TcpTransport(string address = defaultConnectionAddress, int port = defaultConnectionPort)
public TcpTransport(string address, int port)
{
_tcpClient = new TcpClient();

View file

@ -49,7 +49,12 @@ namespace TLSharp.Core
public class Session
{
private const string defaultConnectionAddress = "91.108.56.165";
private const int defaultConnectionPort = 443;
public string SessionUserId { get; set; }
public string ServerAddress { get; set; }
public int Port { get; set; }
public AuthKey AuthKey { get; set; }
public ulong Id { get; set; }
public int Sequence { get; set; }
@ -78,6 +83,9 @@ namespace TLSharp.Core
writer.Write(Salt);
writer.Write(LastMessageId);
writer.Write(TimeOffset);
Serializers.String.write(writer, ServerAddress);
writer.Write(Port);
if (User != null)
{
writer.Write(1);
@ -105,6 +113,8 @@ namespace TLSharp.Core
var salt = reader.ReadUInt64();
var lastMessageId = reader.ReadInt64();
var timeOffset = reader.ReadInt32();
var serverAddress = Serializers.String.read(reader);
var port = reader.ReadInt32();
var isAuthExsist = reader.ReadInt32() == 1;
int sessionExpires = 0;
@ -127,7 +137,9 @@ namespace TLSharp.Core
TimeOffset = timeOffset,
SessionExpires = sessionExpires,
User = user,
SessionUserId = sessionUserId
SessionUserId = sessionUserId,
ServerAddress = serverAddress,
Port = port
};
}
}
@ -147,7 +159,13 @@ namespace TLSharp.Core
}
catch
{
session = new Session(store) { Id = GenerateRandomUlong(), SessionUserId = sessionUserId };
session = new Session(store)
{
Id = GenerateRandomUlong(),
SessionUserId = sessionUserId,
ServerAddress = defaultConnectionAddress,
Port = defaultConnectionPort
};
}
return session;

View file

@ -8,6 +8,7 @@ using TLSharp.Core.MTProto;
using TLSharp.Core.MTProto.Crypto;
using TLSharp.Core.Network;
using TLSharp.Core.Requests;
using MD5 = System.Security.Cryptography.MD5;
namespace TLSharp.Core
{
@ -15,17 +16,18 @@ namespace TLSharp.Core
{
private MtProtoSender _sender;
private AuthKey _key;
private readonly TcpTransport _transport;
private TcpTransport _transport;
private string _apiHash = "a2514f96431a228e4b9ee473f6c51945";
private int _apiId = 19474;
private Session _session;
private List<DcOption> dcOptions;
public User loggedUser { get { return _session.User; } }
public User loggedUser { get { return _session.User; } }
public List<Chat> chats;
public List<User> users;
public List<Chat> chats;
public List<User> users;
public TelegramClient(ISessionStore store, string sessionUserId)
public TelegramClient(ISessionStore store, string sessionUserId)
{
if (_apiId == 0)
throw new InvalidOperationException("Your API_ID is invalid. Do a configuration first https://github.com/sochix/TLSharp#quick-configuration");
@ -33,30 +35,49 @@ namespace TLSharp.Core
if (string.IsNullOrEmpty(_apiHash))
throw new InvalidOperationException("Your API_ID is invalid. Do a configuration first https://github.com/sochix/TLSharp#quick-configuration");
_transport = new TcpTransport();
_session = Session.TryLoadOrCreateNew(store, sessionUserId);
_transport = new TcpTransport(_session.ServerAddress, _session.Port);
}
public async Task<bool> Connect()
public async Task<bool> Connect(bool reconnect = false)
{
if (_session.AuthKey == null)
if (_session.AuthKey == null || reconnect)
{
var result = await Authenticator.DoAuthentication(_transport);
_session.AuthKey = result.AuthKey;
_session.TimeOffset = result.TimeOffset;
}
_sender = new MtProtoSender(_transport, _session);
var request = new InitConnectionRequest(_apiId);
if (!reconnect)
{
var request = new InitConnectionRequest(_apiId);
await _sender.Send(request);
await _sender.Recieve(request);
await _sender.Send(request);
await _sender.Recieve(request);
dcOptions = request.ConfigConstructor.dc_options;
}
return true;
}
private async Task ReconnectToDc(int dcId)
{
if (dcOptions == null || !dcOptions.Any())
throw new InvalidOperationException($"Can't reconnect. Establish initial connection first.");
var dc = dcOptions.Cast<DcOptionConstructor>().First(d => d.id == dcId);
_transport = new TcpTransport(dc.ip_address, dc.port);
_session.ServerAddress = dc.ip_address;
_session.Port = dc.port;
await Connect(true);
}
public bool IsUserAuthorized()
{
return _session.User != null;
@ -76,9 +97,34 @@ namespace TLSharp.Core
public async Task<string> SendCodeRequest(string phoneNumber)
{
var request = new AuthSendCodeRequest(phoneNumber, 5, _apiId, _apiHash, "en");
await _sender.Send(request);
await _sender.Recieve(request);
var completed = false;
AuthSendCodeRequest request = null;
while (!completed)
{
request = new AuthSendCodeRequest(phoneNumber, 5, _apiId, _apiHash, "en");
try
{
await _sender.Send(request);
await _sender.Recieve(request);
completed = true;
}
catch (InvalidOperationException ex)
{
if (ex.Message.StartsWith("Your phone number registered to") && ex.Data["dcId"] != null)
{
await ReconnectToDc((int) ex.Data["dcId"]);
}
else
{
throw;
}
}
}
return request._phoneCodeHash;
}
@ -96,101 +142,100 @@ namespace TLSharp.Core
return request.user;
}
public async Task<InputFile> UploadFile(string name, byte[] data)
{
var partSize = 65536;
public async Task<InputFile> UploadFile(string name, byte[] data)
{
var partSize = 65536;
var file_id = DateTime.Now.Ticks;
var file_id = DateTime.Now.Ticks;
var partedData = new Dictionary<int, byte[]>();
var parts = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(data.Length) / Convert.ToDouble(partSize)));
var remainBytes = data.Length;
for (int i = 0; i < parts; i++)
{
partedData.Add(i, data
.Skip(i * partSize)
.Take(remainBytes < partSize ? remainBytes : partSize)
.ToArray());
var partedData = new Dictionary<int, byte[]>();
var parts = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(data.Length) / Convert.ToDouble(partSize)));
var remainBytes = data.Length;
for (int i = 0; i < parts; i++)
{
partedData.Add(i, data
.Skip(i * partSize)
.Take(remainBytes < partSize ? remainBytes : partSize)
.ToArray());
remainBytes -= partSize;
}
remainBytes -= partSize;
}
for(int i =0;i<parts;i++)
{
var saveFilePartRequest = new Upload_SaveFilePartRequest(file_id, i, partedData[i]);
await _sender.Send(saveFilePartRequest);
await _sender.Recieve(saveFilePartRequest);
for (int i = 0; i < parts; i++)
{
var saveFilePartRequest = new Upload_SaveFilePartRequest(file_id, i, partedData[i]);
await _sender.Send(saveFilePartRequest);
await _sender.Recieve(saveFilePartRequest);
if (saveFilePartRequest.Done == false)
throw new InvalidOperationException($"File part {i} does not uploaded");
}
if (saveFilePartRequest.Done == false)
throw new InvalidOperationException($"File part {i} does not uploaded");
}
string md5_checksum;
using (var md5 = System.Security.Cryptography.MD5.Create())
{
var hash = md5.ComputeHash(data);
var hashResult = new StringBuilder(hash.Length * 2);
string md5_checksum;
using (var md5 = MD5.Create())
{
var hash = md5.ComputeHash(data);
var hashResult = new StringBuilder(hash.Length * 2);
for (int i = 0; i < hash.Length; i++)
hashResult.Append(hash[i].ToString("x2"));
for (int i = 0; i < hash.Length; i++)
hashResult.Append(hash[i].ToString("x2"));
md5_checksum = hashResult.ToString();
}
md5_checksum = hashResult.ToString();
}
var inputFile = new InputFileConstructor(file_id, parts, name, md5_checksum);
var inputFile = new InputFileConstructor(file_id, parts, name, md5_checksum);
return inputFile;
}
return inputFile;
}
public async Task<messages_StatedMessage> SendMediaMessage(InputPeer inputPeer, InputMedia inputMedia)
{
var request = new Message_SendMediaRequest(inputPeer, inputMedia);
await _sender.Send(request);
await _sender.Recieve(request);
public async Task<messages_StatedMessage> SendMediaMessage(InputPeer inputPeer, InputMedia inputMedia)
{
var request = new Message_SendMediaRequest(inputPeer, inputMedia);
await _sender.Send(request);
await _sender.Recieve(request);
return request.StatedMessage;
}
return request.StatedMessage;
}
public async Task<int?> ImportContact(string phoneNumber)
{
var request = new ImportContactRequest(new InputPhoneContactConstructor(0, phoneNumber, "My Test Name", string.Empty));
var request = new ImportContactRequest(new InputPhoneContactConstructor(0, phoneNumber, "My Test Name", String.Empty));
await _sender.Send(request);
await _sender.Recieve(request);
var importedUser = request.users.FirstOrDefault();
return importedUser == null ? (int?) null : ((UserContactConstructor) importedUser).id;
return importedUser == null ? (int?)null : ((UserContactConstructor)importedUser).id;
}
public async Task SendMessage(int id, string message)
{
var request = new SendMessageRequest(new InputPeerContactConstructor(id), message );
var request = new SendMessageRequest(new InputPeerContactConstructor(id), message);
await _sender.Send(request);
await _sender.Recieve(request);
}
public async Task LoadChatsAndUsers(int offset, int max_id, int limit)
{
// GetDialogs
var request = new GetDialogsRequest(offset, max_id, limit);
await _sender.Send(request);
await _sender.Recieve(request);
public async Task LoadChatsAndUsers(int offset, int max_id, int limit)
{
// GetDialogs
var request = new GetDialogsRequest(offset, max_id, limit);
await _sender.Send(request);
await _sender.Recieve(request);
chats = request.chats;
users = request.users;
}
chats = request.chats;
users = request.users;
}
public async Task<List<Message>> GetHistory(int user_id, int offset, int max_id, int limit)
{
var request = new GetHistoryRequest(new InputPeerContactConstructor(user_id), offset, max_id, limit);
await _sender.Send(request);
await _sender.Recieve(request);
public async Task<List<Message>> GetHistory(int user_id, int offset, int max_id, int limit)
{
var request = new GetHistoryRequest(new InputPeerContactConstructor(user_id), offset, max_id, limit);
await _sender.Send(request);
await _sender.Recieve(request);
return request.messages;
}
}
return request.messages;
}
}
}

View file

@ -12,9 +12,9 @@ namespace TLSharp.Tests
[TestClass]
public class NotificatioClientTests
{
public string NumberToAuthenticate { get; set; }
public string RegisteredNumber { get; set; }
public string UnregisteredNumber { get; set; }
private string NumberToSendMessage { get; set; }
private string NumberToAuthenticate { get; set; }
[TestInitialize]
public void Init()
@ -24,19 +24,9 @@ namespace TLSharp.Tests
if (string.IsNullOrEmpty(NumberToAuthenticate))
throw new InvalidOperationException("NumberToAuthenticate is null");
RegisteredNumber = ConfigurationManager.AppSettings["registeredNumber"];
if (string.IsNullOrEmpty(RegisteredNumber))
throw new InvalidOperationException("RegisteredNumber is null");
UnregisteredNumber = ConfigurationManager.AppSettings["unregisteredNumber"];
if (string.IsNullOrEmpty(UnregisteredNumber))
throw new InvalidOperationException("UnregisteredNumber is null");
if (NumberToAuthenticate == UnregisteredNumber)
throw new InvalidOperationException("NumberToAuthenticate eqauls UnregisteredNumber but shouldn't!");
if (RegisteredNumber == UnregisteredNumber)
throw new InvalidOperationException("RegisteredNumber eqauls UnregisteredNumber but shouldn't!");
NumberToSendMessage = ConfigurationManager.AppSettings["numberToSendMessage"];
if (string.IsNullOrEmpty(NumberToSendMessage))
throw new InvalidOperationException("NumberToSendMessage is null");
}
[TestMethod]
@ -62,20 +52,8 @@ namespace TLSharp.Tests
var client = new Core.TelegramClient(store, "session");
await client.Connect();
var phoneList = new string[]
{
RegisteredNumber,
NumberToAuthenticate
};
var rand = new Random();
foreach (var phone in phoneList)
{
var result = await client.IsPhoneRegistered(phone);
Thread.Sleep(rand.Next(9) * 1000);
if (result)
Console.WriteLine($"{phone} - OK");
}
var result = await client.IsPhoneRegistered(NumberToAuthenticate);
Assert.IsTrue(result);
}
[TestMethod]
@ -84,16 +62,15 @@ namespace TLSharp.Tests
// User should be already authenticated!
var store = new FileSessionStore();
var client = new Core.TelegramClient(store, "session");
var client = new TelegramClient(store, "session");
await client.Connect();
Assert.IsTrue(client.IsUserAuthorized());
var res = await client.ImportContact(RegisteredNumber);
var res = await client.ImportContact(NumberToSendMessage);
Assert.IsNotNull(res);
}
[TestMethod]
@ -102,12 +79,12 @@ namespace TLSharp.Tests
// User should be already authenticated!
var store = new FileSessionStore();
var client = new Core.TelegramClient(store, "session");
var client = new TelegramClient(store, "session");
await client.Connect();
Assert.IsTrue(client.IsUserAuthorized());
var res = await client.ImportContact(RegisteredNumber);
var res = await client.ImportContact(NumberToSendMessage);
Assert.IsNotNull(res);
@ -118,7 +95,7 @@ namespace TLSharp.Tests
public async Task TestConnection()
{
var store = new FakeSessionStore();
var client = new Core.TelegramClient(store, "");
var client = new TelegramClient(store, "");
Assert.IsTrue(await client.Connect());
}
@ -126,7 +103,7 @@ namespace TLSharp.Tests
[TestMethod]
public async Task AuthenticationWorks()
{
using (var transport = new TcpTransport())
using (var transport = new TcpTransport("91.108.56.165", 443))
{
var authKey = await Authenticator.DoAuthentication(transport);

View file

@ -2,7 +2,6 @@
<configuration>
<appSettings>
<add key="numberToAuthenticate" value="" />
<add key="registeredNumber" value="" />
<add key="unregisteredNumber" value="" />
<add key="numberToSendMessage" value=""/>
</appSettings>
</configuration>