mirror of
https://github.com/sochix/TLSharp.git
synced 2025-12-06 08:02:00 +01:00
Added auto dc switching
This commit is contained in:
parent
f56ffc7c6f
commit
824ba77e12
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
<configuration>
|
||||
<appSettings>
|
||||
<add key="numberToAuthenticate" value="" />
|
||||
<add key="registeredNumber" value="" />
|
||||
<add key="unregisteredNumber" value="" />
|
||||
<add key="numberToSendMessage" value=""/>
|
||||
</appSettings>
|
||||
</configuration>
|
||||
|
|
|
|||
Loading…
Reference in a new issue