diff --git a/TLSharp.Core/DataCenterIPVersion.cs b/TLSharp.Core/DataCenterIPVersion.cs
new file mode 100644
index 0000000..9b22794
--- /dev/null
+++ b/TLSharp.Core/DataCenterIPVersion.cs
@@ -0,0 +1,30 @@
+namespace TLSharp.Core
+{
+ ///
+ /// When the Telegram server responds with a set of addresses to connect to, DataCenterIPVersion indicates a preference
+ /// for how to choose the IP address to connect to
+ ///
+ public enum DataCenterIPVersion
+ {
+ ///
+ /// Prefers IPv6 addresses if any is passed by Telegram
+ ///
+ Default = 0,
+ ///
+ /// Takes only IPv4 addresses
+ ///
+ OnlyIPv4 = 1,
+ ///
+ /// Takes only IPv6 addresses
+ ///
+ OnlyIPv6 = 2,
+ ///
+ /// Connection to IPv4 addresses is preferred to IPv6 addresses
+ ///
+ PreferIPv4 = 3,
+ ///
+ /// Connection to IPv6 addresses is preferred to IPv6 addresses
+ ///
+ PreferIPv6 = 4,
+ }
+}
diff --git a/TLSharp.Core/TelegramClient.cs b/TLSharp.Core/TelegramClient.cs
index 46e0ad4..997dfd5 100644
--- a/TLSharp.Core/TelegramClient.cs
+++ b/TLSharp.Core/TelegramClient.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
@@ -22,433 +24,459 @@ using TLAuthorization = TeleSharp.TL.Auth.TLAuthorization;
namespace TLSharp.Core
{
- public class TelegramClient : IDisposable
- {
- private MtProtoSender sender;
- private TcpTransport transport;
- private string apiHash = String.Empty;
- private int apiId = 0;
- private Session session;
- private List dcOptions;
- private TcpClientConnectionHandler handler;
-
- public Session Session
- {
- get { return session; }
- }
-
- public TelegramClient(int apiId, string apiHash,
- ISessionStore store = null, string sessionUserId = "session", TcpClientConnectionHandler handler = null)
- {
- if (apiId == default(int))
- throw new MissingApiConfigurationException("API_ID");
- if (string.IsNullOrEmpty(apiHash))
- throw new MissingApiConfigurationException("API_HASH");
-
- if (store == null)
- store = new FileSessionStore();
-
- this.apiHash = apiHash;
- this.apiId = apiId;
- this.handler = handler;
-
- session = Session.TryLoadOrCreateNew(store, sessionUserId);
- transport = new TcpTransport (session.DataCenter.Address, session.DataCenter.Port, this.handler);
- }
-
- public async Task ConnectAsync(bool reconnect = false, CancellationToken token = default(CancellationToken))
- {
- token.ThrowIfCancellationRequested();
-
- if (session.AuthKey == null || reconnect)
- {
- var result = await Authenticator.DoAuthentication(transport, token).ConfigureAwait(false);
- session.AuthKey = result.AuthKey;
- session.TimeOffset = result.TimeOffset;
- }
-
- sender = new MtProtoSender(transport, session);
-
- //set-up layer
- var config = new TLRequestGetConfig();
- var request = new TLRequestInitConnection()
- {
- ApiId = apiId,
- AppVersion = "1.0.0",
- DeviceModel = "PC",
- LangCode = "en",
- Query = config,
- SystemVersion = "Win 10.0"
- };
- var invokewithLayer = new TLRequestInvokeWithLayer() { Layer = 66, Query = request };
- await sender.Send(invokewithLayer, token).ConfigureAwait(false);
- await sender.Receive(invokewithLayer, token).ConfigureAwait(false);
-
- dcOptions = ((TLConfig)invokewithLayer.Response).DcOptions.ToList();
- }
-
- private async Task ReconnectToDcAsync(int dcId, CancellationToken token = default(CancellationToken))
- {
- token.ThrowIfCancellationRequested();
-
- if (dcOptions == null || !dcOptions.Any())
- throw new InvalidOperationException($"Can't reconnect. Establish initial connection first.");
-
- TLExportedAuthorization exported = null;
- if (session.TLUser != null)
- {
- TLRequestExportAuthorization exportAuthorization = new TLRequestExportAuthorization() { DcId = dcId };
- exported = await SendRequestAsync(exportAuthorization, token).ConfigureAwait(false);
- }
-
- var dc = dcOptions.First(d => d.Id == dcId);
- var dataCenter = new DataCenter (dcId, dc.IpAddress, dc.Port);
-
- transport = new TcpTransport(dc.IpAddress, dc.Port, handler);
- session.DataCenter = dataCenter;
-
- await ConnectAsync(true, token).ConfigureAwait(false);
-
- if (session.TLUser != null)
- {
- TLRequestImportAuthorization importAuthorization = new TLRequestImportAuthorization() { Id = exported.Id, Bytes = exported.Bytes };
- var imported = await SendRequestAsync(importAuthorization, token).ConfigureAwait(false);
- OnUserAuthenticated((TLUser)imported.User);
- }
- }
-
- private async Task RequestWithDcMigration(TLMethod request, CancellationToken token = default(CancellationToken))
- {
- if (sender == null)
- throw new InvalidOperationException("Not connected!");
-
- var completed = false;
- while(!completed)
- {
- try
- {
- await sender.Send(request, token).ConfigureAwait(false);
- await sender.Receive(request, token).ConfigureAwait(false);
- completed = true;
- }
- catch(DataCenterMigrationException e)
- {
- if (session.DataCenter.DataCenterId.HasValue &&
- session.DataCenter.DataCenterId.Value == e.DC)
- {
- throw new Exception($"Telegram server replied requesting a migration to DataCenter {e.DC} when this connection was already using this DataCenter", e);
- }
-
- await ReconnectToDcAsync(e.DC, token).ConfigureAwait(false);
- // prepare the request for another try
- request.ConfirmReceived = false;
- }
- }
- }
-
- public bool IsUserAuthorized()
- {
- return session.TLUser != null;
- }
-
- public async Task IsPhoneRegisteredAsync(string phoneNumber, CancellationToken token = default(CancellationToken))
- {
- if (String.IsNullOrWhiteSpace(phoneNumber))
- throw new ArgumentNullException(nameof(phoneNumber));
-
- var authCheckPhoneRequest = new TLRequestCheckPhone() { PhoneNumber = phoneNumber };
-
- await RequestWithDcMigration(authCheckPhoneRequest, token).ConfigureAwait(false);
-
- return authCheckPhoneRequest.Response.PhoneRegistered;
- }
-
- public async Task SendCodeRequestAsync(string phoneNumber, CancellationToken token = default(CancellationToken))
- {
- if (String.IsNullOrWhiteSpace(phoneNumber))
- throw new ArgumentNullException(nameof(phoneNumber));
-
- var request = new TLRequestSendCode() { PhoneNumber = phoneNumber, ApiId = apiId, ApiHash = apiHash };
-
- await RequestWithDcMigration(request, token).ConfigureAwait(false);
-
- return request.Response.PhoneCodeHash;
- }
-
- public async Task MakeAuthAsync(string phoneNumber, string phoneCodeHash, string code, CancellationToken token = default(CancellationToken))
- {
- if (String.IsNullOrWhiteSpace(phoneNumber))
- throw new ArgumentNullException(nameof(phoneNumber));
- if (String.IsNullOrWhiteSpace(phoneCodeHash))
- throw new ArgumentNullException(nameof(phoneCodeHash));
+ public class TelegramClient : IDisposable
+ {
+ private MtProtoSender sender;
+ private TcpTransport transport;
+ private string apiHash = String.Empty;
+ private int apiId = 0;
+ private Session session;
+ private List dcOptions;
+ private TcpClientConnectionHandler handler;
+ private DataCenterIPVersion dcIpVersion;
+
+ public Session Session
+ {
+ get { return session; }
+ }
+
+ ///
+ /// Creates a new TelegramClient
+ ///
+ /// The API ID provided by Telegram. Get one at https://my.telegram.org
+ /// The API Hash provided by Telegram. Get one at https://my.telegram.org
+ /// An ISessionStore object that will handle the session
+ /// The name of the session that tracks login info about this TelegramClient connection
+ /// A delegate to invoke when a connection is needed and that will return a TcpClient that will be used to connect
+ /// Indicates the preferred IpAddress version to use to connect to a Telegram server
+ public TelegramClient(int apiId, string apiHash,
+ ISessionStore store = null, string sessionUserId = "session", TcpClientConnectionHandler handler = null, DataCenterIPVersion dcIpVersion = DataCenterIPVersion.Default)
+ {
+ if (apiId == default(int))
+ throw new MissingApiConfigurationException("API_ID");
+ if (string.IsNullOrEmpty(apiHash))
+ throw new MissingApiConfigurationException("API_HASH");
+
+ if (store == null)
+ store = new FileSessionStore();
+
+ this.apiHash = apiHash;
+ this.apiId = apiId;
+ this.handler = handler;
+ this.dcIpVersion = dcIpVersion;
+
+ session = Session.TryLoadOrCreateNew(store, sessionUserId);
+ transport = new TcpTransport(session.DataCenter.Address, session.DataCenter.Port, this.handler);
+ }
+
+ public async Task ConnectAsync(bool reconnect = false, CancellationToken token = default(CancellationToken))
+ {
+ token.ThrowIfCancellationRequested();
+
+ if (session.AuthKey == null || reconnect)
+ {
+ var result = await Authenticator.DoAuthentication(transport, token).ConfigureAwait(false);
+ session.AuthKey = result.AuthKey;
+ session.TimeOffset = result.TimeOffset;
+ }
+
+ sender = new MtProtoSender(transport, session);
+
+ //set-up layer
+ var config = new TLRequestGetConfig();
+ var request = new TLRequestInitConnection()
+ {
+ ApiId = apiId,
+ AppVersion = "1.0.0",
+ DeviceModel = "PC",
+ LangCode = "en",
+ Query = config,
+ SystemVersion = "Win 10.0"
+ };
+ var invokewithLayer = new TLRequestInvokeWithLayer() { Layer = 66, Query = request };
+ await sender.Send(invokewithLayer, token).ConfigureAwait(false);
+ await sender.Receive(invokewithLayer, token).ConfigureAwait(false);
+
+ dcOptions = ((TLConfig)invokewithLayer.Response).DcOptions.ToList();
+ }
+
+ private async Task ReconnectToDcAsync(int dcId, CancellationToken token = default(CancellationToken))
+ {
+ token.ThrowIfCancellationRequested();
+
+ if (dcOptions == null || !dcOptions.Any())
+ throw new InvalidOperationException($"Can't reconnect. Establish initial connection first.");
+
+ TLExportedAuthorization exported = null;
+ if (session.TLUser != null)
+ {
+ TLRequestExportAuthorization exportAuthorization = new TLRequestExportAuthorization() { DcId = dcId };
+ exported = await SendRequestAsync(exportAuthorization, token).ConfigureAwait(false);
+ }
+
+ var dcs = dcOptions.Where(d => d.Id == dcId
+ && (
+ (dcIpVersion == DataCenterIPVersion.Default) // any
+ || (d.Ipv6 && dcIpVersion == DataCenterIPVersion.OnlyIPv6) // selects only ipv6 addresses
+ || (!d.Ipv6 && dcIpVersion == DataCenterIPVersion.OnlyIPv4) // selects only ipv4 addresses
+ || dcIpVersion == DataCenterIPVersion.PreferIPv4 // we can take both types of address
+ || dcIpVersion == DataCenterIPVersion.PreferIPv6 // we can take both types of address
+ )
+ ).OrderBy(d => d.Ipv6);
+
+ if (dcs.Count() == 0)
+ throw new Exception($"Telegram server didn't provide us with any IPAddress that matches your preferences. If you chose OnlyIPvX, try switch to PreferIPvX instead.");
+
+ var dc = dcIpVersion == DataCenterIPVersion.PreferIPv4 ? dcs.First() : dcs.Last(); // ipv4 addresses are at the beginning of the list because it was ordered
+
+ var dataCenter = new DataCenter(dcId, dc.IpAddress, dc.Port);
+
+ transport = new TcpTransport(dc.IpAddress, dc.Port, handler);
+ session.DataCenter = dataCenter;
+
+ await ConnectAsync(true, token).ConfigureAwait(false);
+
+ if (session.TLUser != null)
+ {
+ TLRequestImportAuthorization importAuthorization = new TLRequestImportAuthorization() { Id = exported.Id, Bytes = exported.Bytes };
+ var imported = await SendRequestAsync(importAuthorization, token).ConfigureAwait(false);
+ OnUserAuthenticated((TLUser)imported.User);
+ }
+ }
+
+ private async Task RequestWithDcMigration(TLMethod request, CancellationToken token = default(CancellationToken))
+ {
+ if (sender == null)
+ throw new InvalidOperationException("Not connected!");
+
+ var completed = false;
+ while (!completed)
+ {
+ try
+ {
+ await sender.Send(request, token).ConfigureAwait(false);
+ await sender.Receive(request, token).ConfigureAwait(false);
+ completed = true;
+ }
+ catch (DataCenterMigrationException e)
+ {
+ if (session.DataCenter.DataCenterId.HasValue &&
+ session.DataCenter.DataCenterId.Value == e.DC)
+ {
+ throw new Exception($"Telegram server replied requesting a migration to DataCenter {e.DC} when this connection was already using this DataCenter", e);
+ }
- if (String.IsNullOrWhiteSpace(code))
- throw new ArgumentNullException(nameof(code));
-
- var request = new TLRequestSignIn() { PhoneNumber = phoneNumber, PhoneCodeHash = phoneCodeHash, PhoneCode = code };
+ await ReconnectToDcAsync(e.DC, token).ConfigureAwait(false);
+ // prepare the request for another try
+ request.ConfirmReceived = false;
+ }
+ }
+ }
- await RequestWithDcMigration(request, token).ConfigureAwait(false);
+ public bool IsUserAuthorized()
+ {
+ return session.TLUser != null;
+ }
- OnUserAuthenticated(((TLUser)request.Response.User));
+ public async Task IsPhoneRegisteredAsync(string phoneNumber, CancellationToken token = default(CancellationToken))
+ {
+ if (String.IsNullOrWhiteSpace(phoneNumber))
+ throw new ArgumentNullException(nameof(phoneNumber));
- return ((TLUser)request.Response.User);
- }
-
- public async Task GetPasswordSetting(CancellationToken token = default(CancellationToken))
- {
- var request = new TLRequestGetPassword();
+ var authCheckPhoneRequest = new TLRequestCheckPhone() { PhoneNumber = phoneNumber };
- await RequestWithDcMigration(request, token).ConfigureAwait(false);
+ await RequestWithDcMigration(authCheckPhoneRequest, token).ConfigureAwait(false);
- return (TLPassword)request.Response;
- }
+ return authCheckPhoneRequest.Response.PhoneRegistered;
+ }
- public async Task MakeAuthWithPasswordAsync(TLPassword password, string password_str, CancellationToken token = default(CancellationToken))
- {
- token.ThrowIfCancellationRequested();
-
- byte[] password_Bytes = Encoding.UTF8.GetBytes(password_str);
- IEnumerable rv = password.CurrentSalt.Concat(password_Bytes).Concat(password.CurrentSalt);
-
- SHA256Managed hashstring = new SHA256Managed();
- var password_hash = hashstring.ComputeHash(rv.ToArray());
-
- var request = new TLRequestCheckPassword() { PasswordHash = password_hash };
-
- await RequestWithDcMigration(request, token).ConfigureAwait(false);
-
- OnUserAuthenticated((TLUser)request.Response.User);
-
- return (TLUser)request.Response.User;
- }
-
- public async Task SignUpAsync(string phoneNumber, string phoneCodeHash, string code, string firstName, string lastName, CancellationToken token = default(CancellationToken))
- {
- var request = new TLRequestSignUp() { PhoneNumber = phoneNumber, PhoneCode = code, PhoneCodeHash = phoneCodeHash, FirstName = firstName, LastName = lastName };
-
- await RequestWithDcMigration(request, token).ConfigureAwait(false);
-
- OnUserAuthenticated((TLUser)request.Response.User);
-
- return (TLUser)request.Response.User;
- }
-
- public async Task SendRequestAsync(TLMethod methodToExecute, CancellationToken token = default(CancellationToken))
- {
- await RequestWithDcMigration(methodToExecute, token).ConfigureAwait(false);
-
- var result = methodToExecute.GetType().GetProperty("Response").GetValue(methodToExecute);
-
- return (T)result;
- }
-
- internal async Task SendAuthenticatedRequestAsync (TLMethod methodToExecute, CancellationToken token = default(CancellationToken))
- {
- if (!IsUserAuthorized())
- throw new InvalidOperationException("Authorize user first!");
-
- return await SendRequestAsync(methodToExecute, token)
- .ConfigureAwait(false);
- }
-
- public async Task UpdateUsernameAsync(string username, CancellationToken token = default(CancellationToken))
- {
- var req = new TLRequestUpdateUsername { Username = username };
-
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- public async Task CheckUsernameAsync(string username, CancellationToken token = default(CancellationToken))
- {
- var req = new TLRequestCheckUsername { Username = username };
-
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- public async Task ImportContactsAsync(IReadOnlyList contacts, CancellationToken token = default(CancellationToken))
- {
- var req = new TLRequestImportContacts { Contacts = new TLVector(contacts)};
-
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- public async Task DeleteContactsAsync(IReadOnlyList users, CancellationToken token = default(CancellationToken))
- {
- var req = new TLRequestDeleteContacts {Id = new TLVector(users)};
-
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- public async Task DeleteContactAsync(TLAbsInputUser user, CancellationToken token = default(CancellationToken))
- {
- var req = new TLRequestDeleteContact {Id = user};
-
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- public async Task GetContactsAsync(CancellationToken token = default(CancellationToken))
- {
- var req = new TLRequestGetContacts() { Hash = "" };
-
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- public async Task SendMessageAsync(TLAbsInputPeer peer, string message, CancellationToken token = default(CancellationToken))
- {
- return await SendAuthenticatedRequestAsync(
- new TLRequestSendMessage()
- {
- Peer = peer,
- Message = message,
- RandomId = Helpers.GenerateRandomLong()
- }, token)
- .ConfigureAwait(false);
- }
-
- public async Task SendTypingAsync(TLAbsInputPeer peer, CancellationToken token = default(CancellationToken))
- {
- var req = new TLRequestSetTyping()
- {
- Action = new TLSendMessageTypingAction(),
- Peer = peer
- };
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- public async Task GetUserDialogsAsync(int offsetDate = 0, int offsetId = 0, TLAbsInputPeer offsetPeer = null, int limit = 100, CancellationToken token = default(CancellationToken))
- {
- if (offsetPeer == null)
- offsetPeer = new TLInputPeerSelf();
-
- var req = new TLRequestGetDialogs()
- {
- OffsetDate = offsetDate,
- OffsetId = offsetId,
- OffsetPeer = offsetPeer,
- Limit = limit
- };
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- public async Task SendUploadedPhoto(TLAbsInputPeer peer, TLAbsInputFile file, string caption, CancellationToken token = default(CancellationToken))
- {
- return await SendAuthenticatedRequestAsync(new TLRequestSendMedia()
- {
- RandomId = Helpers.GenerateRandomLong(),
- Background = false,
- ClearDraft = false,
- Media = new TLInputMediaUploadedPhoto() { File = file, Caption = caption },
- Peer = peer
- }, token)
- .ConfigureAwait(false);
- }
-
- public async Task SendUploadedDocument(
- TLAbsInputPeer peer, TLAbsInputFile file, string caption, string mimeType, TLVector attributes, CancellationToken token = default(CancellationToken))
- {
- return await SendAuthenticatedRequestAsync(new TLRequestSendMedia()
- {
- RandomId = Helpers.GenerateRandomLong(),
- Background = false,
- ClearDraft = false,
- Media = new TLInputMediaUploadedDocument()
- {
- File = file,
- Caption = caption,
- MimeType = mimeType,
- Attributes = attributes
- },
- Peer = peer
- }, token)
- .ConfigureAwait(false);
- }
-
- public async Task GetFile(TLAbsInputFileLocation location, int filePartSize, int offset = 0, CancellationToken token = default(CancellationToken))
- {
- TLFile result = await SendAuthenticatedRequestAsync(new TLRequestGetFile
- {
- Location = location,
- Limit = filePartSize,
- Offset = offset
- }, token)
- .ConfigureAwait(false);
- return result;
- }
-
- public async Task SendPingAsync(CancellationToken token = default(CancellationToken))
- {
- await sender.SendPingAsync(token)
- .ConfigureAwait(false);
- }
-
- public async Task GetHistoryAsync(TLAbsInputPeer peer, int offsetId = 0, int offsetDate = 0, int addOffset = 0, int limit = 100, int maxId = 0, int minId = 0, CancellationToken token = default(CancellationToken))
- {
- var req = new TLRequestGetHistory()
- {
- Peer = peer,
- OffsetId = offsetId,
- OffsetDate = offsetDate,
- AddOffset = addOffset,
- Limit = limit,
- MaxId = maxId,
- MinId = minId
- };
- return await SendAuthenticatedRequestAsync(req, token)
- .ConfigureAwait(false);
- }
-
- ///
- /// Serch user or chat. API: contacts.search#11f812d8 q:string limit:int = contacts.Found;
- ///
- /// User or chat name
- /// Max result count
- ///
- public async Task SearchUserAsync(string q, int limit = 10, CancellationToken token = default(CancellationToken))
- {
- var r = new TeleSharp.TL.Contacts.TLRequestSearch
- {
- Q = q,
- Limit = limit
- };
-
- return await SendAuthenticatedRequestAsync(r, token)
- .ConfigureAwait(false);
- }
-
- private void OnUserAuthenticated(TLUser TLUser)
- {
- session.TLUser = TLUser;
- session.SessionExpires = int.MaxValue;
-
- session.Save();
- }
-
- public bool IsConnected
- {
- get
- {
- if (transport == null)
- return false;
- return transport.IsConnected;
- }
- }
-
- public void Dispose()
- {
- if (transport != null)
- {
- transport.Dispose();
- transport = null;
- }
- }
- }
+ public async Task SendCodeRequestAsync(string phoneNumber, CancellationToken token = default(CancellationToken))
+ {
+ if (String.IsNullOrWhiteSpace(phoneNumber))
+ throw new ArgumentNullException(nameof(phoneNumber));
+
+ var request = new TLRequestSendCode() { PhoneNumber = phoneNumber, ApiId = apiId, ApiHash = apiHash };
+
+ await RequestWithDcMigration(request, token).ConfigureAwait(false);
+
+ return request.Response.PhoneCodeHash;
+ }
+
+ public async Task MakeAuthAsync(string phoneNumber, string phoneCodeHash, string code, CancellationToken token = default(CancellationToken))
+ {
+ if (String.IsNullOrWhiteSpace(phoneNumber))
+ throw new ArgumentNullException(nameof(phoneNumber));
+
+ if (String.IsNullOrWhiteSpace(phoneCodeHash))
+ throw new ArgumentNullException(nameof(phoneCodeHash));
+
+ if (String.IsNullOrWhiteSpace(code))
+ throw new ArgumentNullException(nameof(code));
+
+ var request = new TLRequestSignIn() { PhoneNumber = phoneNumber, PhoneCodeHash = phoneCodeHash, PhoneCode = code };
+
+ await RequestWithDcMigration(request, token).ConfigureAwait(false);
+
+ OnUserAuthenticated(((TLUser)request.Response.User));
+
+ return ((TLUser)request.Response.User);
+ }
+
+ public async Task GetPasswordSetting(CancellationToken token = default(CancellationToken))
+ {
+ var request = new TLRequestGetPassword();
+
+ await RequestWithDcMigration(request, token).ConfigureAwait(false);
+
+ return (TLPassword)request.Response;
+ }
+
+ public async Task MakeAuthWithPasswordAsync(TLPassword password, string password_str, CancellationToken token = default(CancellationToken))
+ {
+ token.ThrowIfCancellationRequested();
+
+ byte[] password_Bytes = Encoding.UTF8.GetBytes(password_str);
+ IEnumerable rv = password.CurrentSalt.Concat(password_Bytes).Concat(password.CurrentSalt);
+
+ SHA256Managed hashstring = new SHA256Managed();
+ var password_hash = hashstring.ComputeHash(rv.ToArray());
+
+ var request = new TLRequestCheckPassword() { PasswordHash = password_hash };
+
+ await RequestWithDcMigration(request, token).ConfigureAwait(false);
+
+ OnUserAuthenticated((TLUser)request.Response.User);
+
+ return (TLUser)request.Response.User;
+ }
+
+ public async Task SignUpAsync(string phoneNumber, string phoneCodeHash, string code, string firstName, string lastName, CancellationToken token = default(CancellationToken))
+ {
+ var request = new TLRequestSignUp() { PhoneNumber = phoneNumber, PhoneCode = code, PhoneCodeHash = phoneCodeHash, FirstName = firstName, LastName = lastName };
+
+ await RequestWithDcMigration(request, token).ConfigureAwait(false);
+
+ OnUserAuthenticated((TLUser)request.Response.User);
+
+ return (TLUser)request.Response.User;
+ }
+
+ public async Task SendRequestAsync(TLMethod methodToExecute, CancellationToken token = default(CancellationToken))
+ {
+ await RequestWithDcMigration(methodToExecute, token).ConfigureAwait(false);
+
+ var result = methodToExecute.GetType().GetProperty("Response").GetValue(methodToExecute);
+
+ return (T)result;
+ }
+
+ internal async Task SendAuthenticatedRequestAsync(TLMethod methodToExecute, CancellationToken token = default(CancellationToken))
+ {
+ if (!IsUserAuthorized())
+ throw new InvalidOperationException("Authorize user first!");
+
+ return await SendRequestAsync(methodToExecute, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task UpdateUsernameAsync(string username, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestUpdateUsername { Username = username };
+
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task CheckUsernameAsync(string username, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestCheckUsername { Username = username };
+
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task ImportContactsAsync(IReadOnlyList contacts, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestImportContacts { Contacts = new TLVector(contacts) };
+
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task DeleteContactsAsync(IReadOnlyList users, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestDeleteContacts { Id = new TLVector(users) };
+
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task DeleteContactAsync(TLAbsInputUser user, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestDeleteContact { Id = user };
+
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task GetContactsAsync(CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestGetContacts() { Hash = "" };
+
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task SendMessageAsync(TLAbsInputPeer peer, string message, CancellationToken token = default(CancellationToken))
+ {
+ return await SendAuthenticatedRequestAsync(
+ new TLRequestSendMessage()
+ {
+ Peer = peer,
+ Message = message,
+ RandomId = Helpers.GenerateRandomLong()
+ }, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task SendTypingAsync(TLAbsInputPeer peer, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestSetTyping()
+ {
+ Action = new TLSendMessageTypingAction(),
+ Peer = peer
+ };
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task GetUserDialogsAsync(int offsetDate = 0, int offsetId = 0, TLAbsInputPeer offsetPeer = null, int limit = 100, CancellationToken token = default(CancellationToken))
+ {
+ if (offsetPeer == null)
+ offsetPeer = new TLInputPeerSelf();
+
+ var req = new TLRequestGetDialogs()
+ {
+ OffsetDate = offsetDate,
+ OffsetId = offsetId,
+ OffsetPeer = offsetPeer,
+ Limit = limit
+ };
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task SendUploadedPhoto(TLAbsInputPeer peer, TLAbsInputFile file, string caption, CancellationToken token = default(CancellationToken))
+ {
+ return await SendAuthenticatedRequestAsync(new TLRequestSendMedia()
+ {
+ RandomId = Helpers.GenerateRandomLong(),
+ Background = false,
+ ClearDraft = false,
+ Media = new TLInputMediaUploadedPhoto() { File = file, Caption = caption },
+ Peer = peer
+ }, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task SendUploadedDocument(
+ TLAbsInputPeer peer, TLAbsInputFile file, string caption, string mimeType, TLVector attributes, CancellationToken token = default(CancellationToken))
+ {
+ return await SendAuthenticatedRequestAsync(new TLRequestSendMedia()
+ {
+ RandomId = Helpers.GenerateRandomLong(),
+ Background = false,
+ ClearDraft = false,
+ Media = new TLInputMediaUploadedDocument()
+ {
+ File = file,
+ Caption = caption,
+ MimeType = mimeType,
+ Attributes = attributes
+ },
+ Peer = peer
+ }, token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task GetFile(TLAbsInputFileLocation location, int filePartSize, int offset = 0, CancellationToken token = default(CancellationToken))
+ {
+ TLFile result = await SendAuthenticatedRequestAsync(new TLRequestGetFile
+ {
+ Location = location,
+ Limit = filePartSize,
+ Offset = offset
+ }, token)
+ .ConfigureAwait(false);
+ return result;
+ }
+
+ public async Task SendPingAsync(CancellationToken token = default(CancellationToken))
+ {
+ await sender.SendPingAsync(token)
+ .ConfigureAwait(false);
+ }
+
+ public async Task GetHistoryAsync(TLAbsInputPeer peer, int offsetId = 0, int offsetDate = 0, int addOffset = 0, int limit = 100, int maxId = 0, int minId = 0, CancellationToken token = default(CancellationToken))
+ {
+ var req = new TLRequestGetHistory()
+ {
+ Peer = peer,
+ OffsetId = offsetId,
+ OffsetDate = offsetDate,
+ AddOffset = addOffset,
+ Limit = limit,
+ MaxId = maxId,
+ MinId = minId
+ };
+ return await SendAuthenticatedRequestAsync(req, token)
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Serch user or chat. API: contacts.search#11f812d8 q:string limit:int = contacts.Found;
+ ///
+ /// User or chat name
+ /// Max result count
+ ///
+ public async Task SearchUserAsync(string q, int limit = 10, CancellationToken token = default(CancellationToken))
+ {
+ var r = new TeleSharp.TL.Contacts.TLRequestSearch
+ {
+ Q = q,
+ Limit = limit
+ };
+
+ return await SendAuthenticatedRequestAsync(r, token)
+ .ConfigureAwait(false);
+ }
+
+ private void OnUserAuthenticated(TLUser TLUser)
+ {
+ session.TLUser = TLUser;
+ session.SessionExpires = int.MaxValue;
+
+ session.Save();
+ }
+
+ public bool IsConnected
+ {
+ get
+ {
+ if (transport == null)
+ return false;
+ return transport.IsConnected;
+ }
+ }
+
+ public void Dispose()
+ {
+ if (transport != null)
+ {
+ transport.Dispose();
+ transport = null;
+ }
+ }
+ }
}