diff --git a/TLSharp.Core/Network/MtProtoSender.cs b/TLSharp.Core/Network/MtProtoSender.cs
index 4bc8e84..107408a 100644
--- a/TLSharp.Core/Network/MtProtoSender.cs
+++ b/TLSharp.Core/Network/MtProtoSender.cs
@@ -66,7 +66,7 @@ namespace TLSharp.Core.Network
byte[] msgKey;
byte[] ciphertext;
- using (MemoryStream plaintextPacket = makeMemory(8 + 8 + 8 + 4 + 4 + packet.Length))
+ using (MemoryStream plaintextPacket = MakeMemory(8 + 8 + 8 + 4 + 4 + packet.Length))
{
using (BinaryWriter plaintextWriter = new BinaryWriter(plaintextPacket))
{
@@ -82,7 +82,7 @@ namespace TLSharp.Core.Network
}
}
- using (MemoryStream ciphertextPacket = makeMemory(8 + 16 + ciphertext.Length))
+ using (MemoryStream ciphertextPacket = MakeMemory(8 + 16 + ciphertext.Length))
{
using (BinaryWriter writer = new BinaryWriter(ciphertextPacket))
{
@@ -136,7 +136,7 @@ namespace TLSharp.Core.Network
using (var messageStream = new MemoryStream(result.Item1, false))
using (var messageReader = new BinaryReader(messageStream))
{
- processMessage(result.Item2, result.Item3, messageReader, request);
+ ProcessMessage(result.Item2, result.Item3, messageReader, request);
}
}
@@ -156,7 +156,7 @@ namespace TLSharp.Core.Network
await Receive(pingRequest);
}
- private bool processMessage(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request)
+ private bool ProcessMessage(ulong messageId, int sequence, BinaryReader messageReader, TeleSharp.TL.TLMethod request)
{
// TODO: check salt
// TODO: check sessionid
@@ -240,7 +240,7 @@ namespace TLSharp.Core.Network
using (MemoryStream packedStream = new MemoryStream(packedData, false))
using (BinaryReader compressedReader = new BinaryReader(packedStream))
{
- processMessage(messageId, sequence, compressedReader, request);
+ ProcessMessage(messageId, sequence, compressedReader, request);
}
return true;
@@ -503,7 +503,7 @@ namespace TLSharp.Core.Network
long beginPosition = messageReader.BaseStream.Position;
try
{
- if (!processMessage(innerMessageId, sequence, messageReader, request))
+ if (!ProcessMessage(innerMessageId, sequence, messageReader, request))
{
messageReader.BaseStream.Position = beginPosition + innerLength;
}
@@ -518,7 +518,7 @@ namespace TLSharp.Core.Network
return false;
}
- private MemoryStream makeMemory(int len)
+ private MemoryStream MakeMemory(int len)
{
return new MemoryStream(new byte[len], 0, len, true, true);
}
diff --git a/TLSharp.Core/TLSharp.Core.csproj b/TLSharp.Core/TLSharp.Core.csproj
index fbef942..7bad699 100644
--- a/TLSharp.Core/TLSharp.Core.csproj
+++ b/TLSharp.Core/TLSharp.Core.csproj
@@ -9,8 +9,9 @@
Properties
TLSharp.Core
TLSharp.Core
- v4.5
+ v4.6.1
512
+
true
diff --git a/TLSharp.Core/TelegramClient.cs b/TLSharp.Core/TelegramClient.cs
index 8ad5cfb..247daf8 100644
--- a/TLSharp.Core/TelegramClient.cs
+++ b/TLSharp.Core/TelegramClient.cs
@@ -1,408 +1,421 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading.Tasks;
-using TeleSharp.TL;
-using TeleSharp.TL.Account;
-using TeleSharp.TL.Auth;
-using TeleSharp.TL.Contacts;
-using TeleSharp.TL.Help;
-using TeleSharp.TL.Messages;
-using TeleSharp.TL.Upload;
-using TLSharp.Core.Auth;
-using TLSharp.Core.MTProto.Crypto;
-using TLSharp.Core.Network;
-using TLSharp.Core.Utils;
-using TLAuthorization = TeleSharp.TL.Auth.TLAuthorization;
-
-namespace TLSharp.Core
-{
- public class TelegramClient : IDisposable
- {
- private MtProtoSender _sender;
- private AuthKey _key;
- private TcpTransport _transport;
- private string _apiHash = "";
- private int _apiId = 0;
- private Session _session;
- private List dcOptions;
- private TcpClientConnectionHandler _handler;
-
- 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();
-
- _apiHash = apiHash;
- _apiId = apiId;
- _handler = handler;
-
- _session = Session.TryLoadOrCreateNew(store, sessionUserId);
- _transport = new TcpTransport(_session.ServerAddress, _session.Port, _handler);
- }
-
- public async Task ConnectAsync(bool reconnect = false)
- {
- if (_session.AuthKey == null || reconnect)
- {
- var result = await Authenticator.DoAuthentication(_transport);
- _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);
- await _sender.Receive(invokewithLayer);
-
- dcOptions = ((TLConfig)invokewithLayer.Response).DcOptions.ToList();
- }
-
- private async Task ReconnectToDcAsync(int dcId)
- {
- 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);
- }
-
- var dc = dcOptions.First(d => d.Id == dcId);
-
- _transport = new TcpTransport(dc.IpAddress, dc.Port, _handler);
- _session.ServerAddress = dc.IpAddress;
- _session.Port = dc.Port;
-
- await ConnectAsync(true);
-
- if (_session.TLUser != null)
- {
- TLRequestImportAuthorization importAuthorization = new TLRequestImportAuthorization() { Id = exported.Id, Bytes = exported.Bytes };
- var imported = await SendRequestAsync(importAuthorization);
- OnUserAuthenticated(((TLUser)imported.User));
- }
- }
-
- private async Task RequestWithDcMigration(TLMethod request)
- {
- if (_sender == null)
- throw new InvalidOperationException("Not connected!");
-
- var completed = false;
- while(!completed)
- {
- try
- {
- await _sender.Send(request);
- await _sender.Receive(request);
- completed = true;
- }
- catch(DataCenterMigrationException e)
- {
- await ReconnectToDcAsync(e.DC);
- // prepare the request for another try
- request.ConfirmReceived = false;
- }
- }
- }
-
- public bool IsUserAuthorized()
- {
- return _session.TLUser != null;
- }
-
- public async Task IsPhoneRegisteredAsync(string phoneNumber)
- {
- if (String.IsNullOrWhiteSpace(phoneNumber))
- throw new ArgumentNullException(nameof(phoneNumber));
-
- var authCheckPhoneRequest = new TLRequestCheckPhone() { PhoneNumber = phoneNumber };
-
- await RequestWithDcMigration(authCheckPhoneRequest);
-
- return authCheckPhoneRequest.Response.PhoneRegistered;
- }
-
- public async Task SendCodeRequestAsync(string phoneNumber)
- {
- if (String.IsNullOrWhiteSpace(phoneNumber))
- throw new ArgumentNullException(nameof(phoneNumber));
-
- var request = new TLRequestSendCode() { PhoneNumber = phoneNumber, ApiId = _apiId, ApiHash = _apiHash };
-
- await RequestWithDcMigration(request);
-
- return request.Response.PhoneCodeHash;
- }
-
- public async Task MakeAuthAsync(string phoneNumber, string phoneCodeHash, string code)
- {
- 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);
-
- OnUserAuthenticated(((TLUser)request.Response.User));
-
- return ((TLUser)request.Response.User);
- }
-
- public async Task GetPasswordSetting()
- {
- var request = new TLRequestGetPassword();
-
- await RequestWithDcMigration(request);
-
- return ((TLPassword)request.Response);
- }
-
- public async Task MakeAuthWithPasswordAsync(TLPassword password, string password_str)
- {
-
- 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);
-
- OnUserAuthenticated(((TLUser)request.Response.User));
-
- return ((TLUser)request.Response.User);
- }
-
- public async Task SignUpAsync(string phoneNumber, string phoneCodeHash, string code, string firstName, string lastName)
- {
- var request = new TLRequestSignUp() { PhoneNumber = phoneNumber, PhoneCode = code, PhoneCodeHash = phoneCodeHash, FirstName = firstName, LastName = lastName };
-
- await RequestWithDcMigration(request);
-
- OnUserAuthenticated(((TLUser)request.Response.User));
-
- return ((TLUser)request.Response.User);
- }
- public async Task SendRequestAsync(TLMethod methodToExecute)
- {
- await RequestWithDcMigration(methodToExecute);
-
- var result = methodToExecute.GetType().GetProperty("Response").GetValue(methodToExecute);
-
- return (T)result;
- }
-
- public async Task GetContactsAsync()
- {
- if (!IsUserAuthorized())
- throw new InvalidOperationException("Authorize user first!");
-
- var req = new TLRequestGetContacts() { Hash = "" };
-
- return await SendRequestAsync(req);
- }
-
- public async Task SendMessageAsync(TLAbsInputPeer peer, string message)
- {
- if (!IsUserAuthorized())
- throw new InvalidOperationException("Authorize user first!");
-
- return await SendRequestAsync(
- new TLRequestSendMessage()
- {
- Peer = peer,
- Message = message,
- RandomId = Helpers.GenerateRandomLong()
- });
- }
-
- public async Task SendTypingAsync(TLAbsInputPeer peer)
- {
- var req = new TLRequestSetTyping()
- {
- Action = new TLSendMessageTypingAction(),
- Peer = peer
- };
- return await SendRequestAsync(req);
- }
-
- public async Task GetUserDialogsAsync(int offsetDate = 0, int offsetId = 0, TLAbsInputPeer offsetPeer = null, int limit = 100)
- {
- if (!IsUserAuthorized())
- throw new InvalidOperationException("Authorize user first!");
-
- if (offsetPeer == null)
- offsetPeer = new TLInputPeerSelf();
-
- var req = new TLRequestGetDialogs()
- {
- OffsetDate = offsetDate,
- OffsetId = offsetId,
- OffsetPeer = offsetPeer,
- Limit = limit
- };
- return await SendRequestAsync(req);
- }
-
- public async Task SendUploadedPhoto(TLAbsInputPeer peer, TLAbsInputFile file, string caption)
- {
- return await SendRequestAsync(new TLRequestSendMedia()
- {
- RandomId = Helpers.GenerateRandomLong(),
- Background = false,
- ClearDraft = false,
- Media = new TLInputMediaUploadedPhoto() { File = file, Caption = caption },
- Peer = peer
- });
- }
-
- public async Task SendUploadedDocument(
- TLAbsInputPeer peer, TLAbsInputFile file, string caption, string mimeType, TLVector attributes)
- {
- return await SendRequestAsync(new TLRequestSendMedia()
- {
- RandomId = Helpers.GenerateRandomLong(),
- Background = false,
- ClearDraft = false,
- Media = new TLInputMediaUploadedDocument()
- {
- File = file,
- Caption = caption,
- MimeType = mimeType,
- Attributes = attributes
- },
- Peer = peer
- });
- }
-
- public async Task GetFile(TLAbsInputFileLocation location, int filePartSize, int offset = 0)
- {
- TLFile result = null;
- result = await SendRequestAsync(new TLRequestGetFile()
- {
- Location = location,
- Limit = filePartSize,
- Offset = offset
- });
- return result;
- }
-
- public async Task SendPingAsync()
- {
- await _sender.SendPingAsync();
- }
-
- public async Task GetHistoryAsync(TLAbsInputPeer peer, int offsetId = 0, int offsetDate = 0, int addOffset = 0, int limit = 100, int maxId = 0, int minId = 0)
- {
- if (!IsUserAuthorized())
- throw new InvalidOperationException("Authorize user first!");
-
- var req = new TLRequestGetHistory()
- {
- Peer = peer,
- OffsetId = offsetId,
- OffsetDate = offsetDate,
- AddOffset = addOffset,
- Limit = limit,
- MaxId = maxId,
- MinId = minId
- };
- return await SendRequestAsync(req);
- }
-
- ///
- /// 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)
- {
- var r = new TeleSharp.TL.Contacts.TLRequestSearch
- {
- Q = q,
- Limit = limit
- };
-
- return await SendRequestAsync(r);
- }
-
- 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 class MissingApiConfigurationException : Exception
- {
- public const string InfoUrl = "https://github.com/sochix/TLSharp#quick-configuration";
-
- internal MissingApiConfigurationException(string invalidParamName) :
- base($"Your {invalidParamName} setting is missing. Adjust the configuration first, see {InfoUrl}")
- {
- }
- }
-
- public class InvalidPhoneCodeException : Exception
- {
- internal InvalidPhoneCodeException(string msg) : base(msg) { }
- }
- public class CloudPasswordNeededException : Exception
- {
- internal CloudPasswordNeededException(string msg) : base(msg) { }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using TeleSharp.TL;
+using TeleSharp.TL.Account;
+using TeleSharp.TL.Auth;
+using TeleSharp.TL.Contacts;
+using TeleSharp.TL.Help;
+using TeleSharp.TL.Messages;
+using TeleSharp.TL.Upload;
+using TLSharp.Core.Auth;
+using TLSharp.Core.MTProto.Crypto;
+using TLSharp.Core.Network;
+using TLSharp.Core.Utils;
+using TLAuthorization = TeleSharp.TL.Auth.TLAuthorization;
+
+namespace TLSharp.Core
+{
+ public class TelegramClient : IDisposable
+ {
+ private MtProtoSender _sender;
+ private readonly AuthKey _key;
+ private TcpTransport _transport;
+ private string _apiHash = "";
+ private int _apiId = 0;
+ private Session _session;
+ private List dcOptions;
+ private readonly TcpClientConnectionHandler _handler;
+
+ 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();
+
+ _apiHash = apiHash;
+ _apiId = apiId;
+ _handler = handler;
+
+ _session = Session.TryLoadOrCreateNew(store, sessionUserId);
+ _transport = new TcpTransport(_session.ServerAddress, _session.Port, _handler);
+ }
+
+ public async Task ConnectAsync(bool reconnect = false)
+ {
+ if (_session.AuthKey == null || reconnect)
+ {
+ var result = await Authenticator.DoAuthentication(_transport);
+ _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);
+ await _sender.Receive(invokewithLayer);
+
+ dcOptions = ((TLConfig)invokewithLayer.Response).DcOptions.ToList();
+ }
+
+ private async Task ReconnectToDcAsync(int dcId, int times)
+ {
+ 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,times);
+ }
+
+ var dc = dcOptions.First(d => d.Id == dcId);
+
+ _transport = new TcpTransport(dc.IpAddress, dc.Port, _handler);
+ _session.ServerAddress = dc.IpAddress;
+ _session.Port = dc.Port;
+
+ await ConnectAsync(true);
+
+ if (_session.TLUser != null)
+ {
+ TLRequestImportAuthorization importAuthorization = new TLRequestImportAuthorization() { Id = exported.Id, Bytes = exported.Bytes };
+ var imported = await SendRequestAsync(importAuthorization,times);
+ OnUserAuthenticated(((TLUser)imported.User));
+ }
+
+
+ }
+
+ private async Task RequestWithDcMigration(TLMethod request, int times)
+ {
+ if (_sender == null)
+ throw new InvalidOperationException("Not connected!");
+
+ var completed = false;
+ while(!completed)
+ {
+ try
+ {
+ await _sender.Send(request);
+ await _sender.Receive(request);
+ completed = true;
+ }
+ catch(DataCenterMigrationException e)
+ {
+ if (times <= 1)
+ {
+ await ReconnectToDcAsync(e.DC, times + 1);
+ // prepare the request for another try
+ request.ConfirmReceived = false;
+ }
+ else
+ {
+ throw e;
+ }
+ }
+ catch (TLSharp.Core.Network.FloodException e)
+ {
+ throw e;
+ }
+ }
+ }
+
+ public bool IsUserAuthorized()
+ {
+ return _session.TLUser != null;
+ }
+
+ public async Task IsPhoneRegisteredAsync(string phoneNumber)
+ {
+ if (String.IsNullOrWhiteSpace(phoneNumber))
+ throw new ArgumentNullException(nameof(phoneNumber));
+
+ var authCheckPhoneRequest = new TLRequestCheckPhone() { PhoneNumber = phoneNumber };
+
+ await RequestWithDcMigration(authCheckPhoneRequest,0);
+
+ return authCheckPhoneRequest.Response.PhoneRegistered;
+ }
+
+ public async Task SendCodeRequestAsync(string phoneNumber)
+ {
+ if (String.IsNullOrWhiteSpace(phoneNumber))
+ throw new ArgumentNullException(nameof(phoneNumber));
+
+ var request = new TLRequestSendCode() { PhoneNumber = phoneNumber, ApiId = _apiId, ApiHash = _apiHash };
+
+ await RequestWithDcMigration(request,0);
+
+ return request.Response.PhoneCodeHash;
+ }
+
+ public async Task MakeAuthAsync(string phoneNumber, string phoneCodeHash, string code)
+ {
+ 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,0);
+
+ OnUserAuthenticated(((TLUser)request.Response.User));
+
+ return ((TLUser)request.Response.User);
+ }
+
+ public async Task GetPasswordSetting()
+ {
+ var request = new TLRequestGetPassword();
+
+ await RequestWithDcMigration(request,0);
+
+ return ((TLPassword)request.Response);
+ }
+
+ public async Task MakeAuthWithPasswordAsync(TLPassword password, string password_str)
+ {
+
+ 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,0);
+
+ OnUserAuthenticated(((TLUser)request.Response.User));
+
+ return ((TLUser)request.Response.User);
+ }
+
+ public async Task SignUpAsync(string phoneNumber, string phoneCodeHash, string code, string firstName, string lastName)
+ {
+ var request = new TLRequestSignUp() { PhoneNumber = phoneNumber, PhoneCode = code, PhoneCodeHash = phoneCodeHash, FirstName = firstName, LastName = lastName };
+
+ await RequestWithDcMigration(request,0);
+
+ OnUserAuthenticated(((TLUser)request.Response.User));
+
+ return ((TLUser)request.Response.User);
+ }
+ public async Task SendRequestAsync(TLMethod methodToExecute, int times)
+ {
+ await RequestWithDcMigration(methodToExecute, times);
+
+ var result = methodToExecute.GetType().GetProperty("Response").GetValue(methodToExecute);
+
+ return (T)result;
+ }
+
+ public async Task GetContactsAsync()
+ {
+ if (!IsUserAuthorized())
+ throw new InvalidOperationException("Authorize user first!");
+
+ var req = new TLRequestGetContacts() { Hash = "" };
+
+ return await SendRequestAsync(req,0);
+ }
+
+ public async Task SendMessageAsync(TLAbsInputPeer peer, string message)
+ {
+ if (!IsUserAuthorized())
+ throw new InvalidOperationException("Authorize user first!");
+
+ var x = new TLRequestSendMessage()
+ {
+ Peer = peer,
+ Message = message,
+ RandomId = Helpers.GenerateRandomLong()
+ };
+ return await SendRequestAsync(x, 0);
+ }
+
+ public async Task SendTypingAsync(TLAbsInputPeer peer)
+ {
+ var req = new TLRequestSetTyping()
+ {
+ Action = new TLSendMessageTypingAction(),
+ Peer = peer
+ };
+ return await SendRequestAsync(req,0);
+ }
+
+ public async Task GetUserDialogsAsync(int offsetDate = 0, int offsetId = 0, TLAbsInputPeer offsetPeer = null, int limit = 100)
+ {
+ if (!IsUserAuthorized())
+ throw new InvalidOperationException("Authorize user first!");
+
+ if (offsetPeer == null)
+ offsetPeer = new TLInputPeerSelf();
+
+ var req = new TLRequestGetDialogs()
+ {
+ OffsetDate = offsetDate,
+ OffsetId = offsetId,
+ OffsetPeer = offsetPeer,
+ Limit = limit
+ };
+ return await SendRequestAsync(req,0);
+ }
+
+ public async Task SendUploadedPhoto(TLAbsInputPeer peer, TLAbsInputFile file, string caption)
+ {
+ return await SendRequestAsync(new TLRequestSendMedia()
+ {
+ RandomId = Helpers.GenerateRandomLong(),
+ Background = false,
+ ClearDraft = false,
+ Media = new TLInputMediaUploadedPhoto() { File = file, Caption = caption },
+ Peer = peer
+ },0);
+ }
+
+ public async Task SendUploadedDocument(
+ TLAbsInputPeer peer, TLAbsInputFile file, string caption, string mimeType, TLVector attributes)
+ {
+ return await SendRequestAsync(new TLRequestSendMedia()
+ {
+ RandomId = Helpers.GenerateRandomLong(),
+ Background = false,
+ ClearDraft = false,
+ Media = new TLInputMediaUploadedDocument()
+ {
+ File = file,
+ Caption = caption,
+ MimeType = mimeType,
+ Attributes = attributes
+ },
+ Peer = peer
+ },0);
+ }
+
+ public async Task GetFile(TLAbsInputFileLocation location, int filePartSize, int offset = 0)
+ {
+ TLFile result = null;
+ result = await SendRequestAsync(new TLRequestGetFile()
+ {
+ Location = location,
+ Limit = filePartSize,
+ Offset = offset
+ },0);
+ return result;
+ }
+
+ public async Task SendPingAsync()
+ {
+ await _sender.SendPingAsync();
+ }
+
+ public async Task GetHistoryAsync(TLAbsInputPeer peer, int offsetId = 0, int offsetDate = 0, int addOffset = 0, int limit = 100, int maxId = 0, int minId = 0)
+ {
+ if (!IsUserAuthorized())
+ throw new InvalidOperationException("Authorize user first!");
+
+ var req = new TLRequestGetHistory()
+ {
+ Peer = peer,
+ OffsetId = offsetId,
+ OffsetDate = offsetDate,
+ AddOffset = addOffset,
+ Limit = limit,
+ MaxId = maxId,
+ MinId = minId
+ };
+ return await SendRequestAsync(req,0);
+ }
+
+ ///
+ /// 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)
+ {
+ var r = new TeleSharp.TL.Contacts.TLRequestSearch
+ {
+ Q = q,
+ Limit = limit
+ };
+
+ return await SendRequestAsync(r,0);
+ }
+
+ 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 class MissingApiConfigurationException : Exception
+ {
+ public const string InfoUrl = "https://github.com/sochix/TLSharp#quick-configuration";
+
+ internal MissingApiConfigurationException(string invalidParamName) :
+ base($"Your {invalidParamName} setting is missing. Adjust the configuration first, see {InfoUrl}")
+ {
+ }
+ }
+
+ public class InvalidPhoneCodeException : Exception
+ {
+ internal InvalidPhoneCodeException(string msg) : base(msg) { }
+ }
+ public class CloudPasswordNeededException : Exception
+ {
+ internal CloudPasswordNeededException(string msg) : base(msg) { }
+ }
+}
diff --git a/TLSharp.Core/Utils/UploadHelper.cs b/TLSharp.Core/Utils/UploadHelper.cs
index 3471c51..0581b05 100644
--- a/TLSharp.Core/Utils/UploadHelper.cs
+++ b/TLSharp.Core/Utils/UploadHelper.cs
@@ -97,7 +97,7 @@ namespace TLSharp.Core.Utils
FilePart = partNumber,
Bytes = part,
FileTotalParts = partsCount
- });
+ },0);
}
else
{
@@ -106,7 +106,7 @@ namespace TLSharp.Core.Utils
FileId = file_id,
FilePart = partNumber,
Bytes = part
- });
+ },0);
}
partNumber++;
}
diff --git a/TLSharp.Core/packages.config b/TLSharp.Core/packages.config
index 0051856..4966d6d 100644
--- a/TLSharp.Core/packages.config
+++ b/TLSharp.Core/packages.config
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/TLSharp.Tests.NUnit/TLSharp.Tests.NUnit.csproj b/TLSharp.Tests.NUnit/TLSharp.Tests.NUnit.csproj
index e947f4e..f7f6430 100644
--- a/TLSharp.Tests.NUnit/TLSharp.Tests.NUnit.csproj
+++ b/TLSharp.Tests.NUnit/TLSharp.Tests.NUnit.csproj
@@ -1,54 +1,55 @@
-
-
-
- Debug
- AnyCPU
- {E90B705B-19FA-43BA-B952-69957976D12C}
- Library
- TLSharp.Tests.NUnit
- TLSharp.Tests.NUnit
- v4.5
-
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- false
-
-
- full
- true
- bin\Release
- prompt
- 4
- false
-
-
-
-
- ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
-
-
-
-
-
-
-
- app.config
-
-
-
-
-
-
-
-
- {DE5C0467-EE99-4734-95F2-EFF7A0B99924}
- TLSharp.Tests
-
-
-
+
+
+
+ Debug
+ AnyCPU
+ {E90B705B-19FA-43BA-B952-69957976D12C}
+ Library
+ TLSharp.Tests.NUnit
+ TLSharp.Tests.NUnit
+ v4.6.1
+
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ false
+
+
+ full
+ true
+ bin\Release
+ prompt
+ 4
+ false
+
+
+
+
+ ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
+
+
+
+
+
+
+
+ app.config
+
+
+
+
+
+
+
+
+ {DE5C0467-EE99-4734-95F2-EFF7A0B99924}
+ TLSharp.Tests
+
+
+
\ No newline at end of file
diff --git a/TLSharp.Tests.VS/TLSharp.Tests.VS.csproj b/TLSharp.Tests.VS/TLSharp.Tests.VS.csproj
index a44fff4..cd4d603 100644
--- a/TLSharp.Tests.VS/TLSharp.Tests.VS.csproj
+++ b/TLSharp.Tests.VS/TLSharp.Tests.VS.csproj
@@ -1,103 +1,104 @@
-
-
-
- Debug
- AnyCPU
- {AFFC3B00-3E4D-4327-8F7A-08EE41E0C8B7}
- Library
- Properties
- TLSharp.Tests.VS
- TLSharp.Tests.VS
- v4.5
- 512
- {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- 10.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
- $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
- False
- UnitTest
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {d6144517-91d2-4880-86df-e9ff5d7f383a}
- TeleSharp.TL
-
-
- {400d2544-1cc6-4d8a-a62c-2292d9947a16}
- TLSharp.Core
-
-
- {de5c0467-ee99-4734-95f2-eff7a0b99924}
- TLSharp.Tests
-
-
-
-
- app.config
-
-
-
-
-
-
-
- False
-
-
- False
-
-
- False
-
-
- False
-
-
-
-
-
-
+
+
+
+ Debug
+ AnyCPU
+ {AFFC3B00-3E4D-4327-8F7A-08EE41E0C8B7}
+ Library
+ Properties
+ TLSharp.Tests.VS
+ TLSharp.Tests.VS
+ v4.6.1
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {d6144517-91d2-4880-86df-e9ff5d7f383a}
+ TeleSharp.TL
+
+
+ {400d2544-1cc6-4d8a-a62c-2292d9947a16}
+ TLSharp.Core
+
+
+ {de5c0467-ee99-4734-95f2-eff7a0b99924}
+ TLSharp.Tests
+
+
+
+
+ app.config
+
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+ -->
\ No newline at end of file
diff --git a/TLSharp.Tests/TLSharp.Tests.csproj b/TLSharp.Tests/TLSharp.Tests.csproj
index e2277f1..a403928 100644
--- a/TLSharp.Tests/TLSharp.Tests.csproj
+++ b/TLSharp.Tests/TLSharp.Tests.csproj
@@ -1,67 +1,68 @@
-
-
-
-
- Debug
- AnyCPU
- {DE5C0467-EE99-4734-95F2-EFF7A0B99924}
- Library
- Properties
- TLSharp.Tests
- TLSharp.Tests
- v4.5
- 512
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {d6144517-91d2-4880-86df-e9ff5d7f383a}
- TeleSharp.TL
-
-
- {400d2544-1cc6-4d8a-a62c-2292d9947a16}
- TLSharp.Core
-
-
-
-
- Always
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+ {DE5C0467-EE99-4734-95F2-EFF7A0B99924}
+ Library
+ Properties
+ TLSharp.Tests
+ TLSharp.Tests
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {d6144517-91d2-4880-86df-e9ff5d7f383a}
+ TeleSharp.TL
+
+
+ {400d2544-1cc6-4d8a-a62c-2292d9947a16}
+ TLSharp.Core
+
+
+
+
+ Always
+
+
+
+ -->
\ No newline at end of file
diff --git a/TLSharp.Tests/app.config b/TLSharp.Tests/app.config
index 490261d..41d4dc7 100644
--- a/TLSharp.Tests/app.config
+++ b/TLSharp.Tests/app.config
@@ -1,15 +1,15 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TeleSharp.Generator/App.config b/TeleSharp.Generator/App.config
index 88fa402..4543795 100644
--- a/TeleSharp.Generator/App.config
+++ b/TeleSharp.Generator/App.config
@@ -1,6 +1,6 @@
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
diff --git a/TeleSharp.Generator/TeleSharp.Generator.csproj b/TeleSharp.Generator/TeleSharp.Generator.csproj
index 94f3e9b..4338b02 100644
--- a/TeleSharp.Generator/TeleSharp.Generator.csproj
+++ b/TeleSharp.Generator/TeleSharp.Generator.csproj
@@ -1,80 +1,81 @@
-
-
-
-
- Debug
- AnyCPU
- {9BE3B9D4-9FF6-4DC8-B9CC-EB2E3F390129}
- Exe
- Properties
- TeleSharp.Generator
- TeleSharp.Generator
- v4.5
- 512
- true
-
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
- ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Always
-
-
-
-
-
-
-
-
- Always
-
-
- Always
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+ {9BE3B9D4-9FF6-4DC8-B9CC-EB2E3F390129}
+ Exe
+ Properties
+ TeleSharp.Generator
+ TeleSharp.Generator
+ v4.6.1
+ 512
+ true
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+ -->
\ No newline at end of file
diff --git a/TeleSharp.TL/TeleSharp.TL.csproj b/TeleSharp.TL/TeleSharp.TL.csproj
index 7c6b30c..b5e6b02 100644
--- a/TeleSharp.TL/TeleSharp.TL.csproj
+++ b/TeleSharp.TL/TeleSharp.TL.csproj
@@ -1,938 +1,939 @@
-
-
-
-
- Debug
- AnyCPU
- {D6144517-91D2-4880-86DF-E9FF5D7F383A}
- Library
- Properties
- TeleSharp.TL
- TeleSharp.TL
- v4.5
- 512
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
- ..\packages\BigMath.0.5.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\BigMath.dll
- True
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+ {D6144517-91D2-4880-86DF-E9FF5D7F383A}
+ Library
+ Properties
+ TeleSharp.TL
+ TeleSharp.TL
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\BigMath.0.5.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\BigMath.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -->
\ No newline at end of file