diff --git a/README.md b/README.md index 7292629..9f78092 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ static async Task Main(string[] _) Console.WriteLine($"We are logged-in as {user.username ?? user.first_name + " " + user.last_name} (id {user.id})"); } ``` -When run, this will prompt you interactively for your App **api_id** and **api_hash** (that you obtain through Telegram's [API development tools](https://my.telegram.org/apps) page) and try to connect to Telegram servers. +When run, this will prompt you interactively for your App **api_hash** and **api_id** (that you obtain through Telegram's [API development tools](https://my.telegram.org/apps) page) and try to connect to Telegram servers. Then it will attempt to sign-in *(login)* as a user for which you must enter the **phone_number** and the **verification_code** that will be sent to this user (for example through SMS or another Telegram client app the user is connected to). @@ -33,7 +33,7 @@ And that's it, you now have access to the **[full range of Telegram Client APIs] All those API methods are available *(with an underscore in the method name, instead of a dot)*, like this: `await client.Method_Name(...)` # Saved session -If you run this program again, you will notice that only **api_id** and **api_hash** are requested, the other prompts are gone and you are automatically logged-on and ready to go. +If you run this program again, you will notice that only **api_hash** is requested, the other prompts are gone and you are automatically logged-on and ready to go. This is because WTelegramClient saves (typically in the encrypted file **bin\WTelegram.session**) its state and the authentication keys that were negociated with Telegram so that you needn't sign-in again every time. @@ -127,7 +127,7 @@ The Client class also offers an `Update` event that is triggered when Telegram s An invalid API request can result in a `RpcException` being raised, reflecting the [error code and status text](https://revgram.github.io/errors.html) of the problem. -The other configuration items that you can override include: **session_pathname, server_address, device_model, system_version, app_version, system_lang_code, lang_pack, lang_code, user_id** +The other configuration items that you can override include: **session_pathname, session_key, server_address, device_model, system_version, app_version, system_lang_code, lang_pack, lang_code, user_id** Optional API parameters have a default value of `null` when unset. Passing `null` for a required string/array is the same as *empty* (0-length). Required API parameters/fields can sometimes be set to 0 or `null` when unused (check API documentation or experiment). diff --git a/src/Client.cs b/src/Client.cs index edf9f32..c13eb69 100644 --- a/src/Client.cs +++ b/src/Client.cs @@ -50,9 +50,8 @@ namespace WTelegram public delegate void ProgressCallback(long transmitted, long totalSize); private readonly Func _config; - private readonly int _apiId; - private readonly string _apiHash; private readonly Session _session; + private string _apiHash; private Session.DCSession _dcSession; private TcpClient _tcpClient; private Stream _networkStream; @@ -89,9 +88,10 @@ namespace WTelegram public Client(Func configProvider = null) { _config = configProvider ?? DefaultConfigOrAsk; - _apiId = int.Parse(Config("api_id")); - _apiHash = Config("api_hash"); - _session = Session.LoadOrCreate(Config("session_pathname"), Convert.FromHexString(_apiHash)); + var session_pathname = Config("session_pathname"); + var session_key = _config("session_key") ?? (_apiHash = Config("api_hash")); + _session = Session.LoadOrCreate(session_pathname, Convert.FromHexString(session_key)); + if (_session.ApiId == 0) _session.ApiId = int.Parse(Config("api_id")); if (_session.MainDC != 0) _session.DCSessions.TryGetValue(_session.MainDC, out _dcSession); _dcSession ??= new() { Id = Helpers.RandomLong() }; _dcSession.Client = this; @@ -102,8 +102,6 @@ namespace WTelegram private Client(Client cloneOf, Session.DCSession dcSession) { _config = cloneOf._config; - _apiId = cloneOf._apiId; - _apiHash = cloneOf._apiHash; _session = cloneOf._session; TcpHandler = cloneOf.TcpHandler; MTProxyUrl = cloneOf.MTProxyUrl; @@ -127,8 +125,8 @@ namespace WTelegram "server_address" => "149.154.167.50:443", // DC 2 #endif "device_model" => Environment.Is64BitOperatingSystem ? "PC 64bit" : "PC 32bit", - "system_version" => System.Runtime.InteropServices.RuntimeInformation.OSDescription, - "app_version" => (Assembly.GetEntryAssembly() ?? AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.EntryPoint != null))?.GetName().Version.ToString() ?? "0.0", + "system_version" => Helpers.GetSystemVersion(), + "app_version" => Helpers.GetAppVersion(), "system_lang_code" => CultureInfo.InstalledUICulture.TwoLetterISOLanguageName, "lang_pack" => "", "lang_code" => CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, @@ -141,7 +139,8 @@ namespace WTelegram private static string AskConfig(string config) { - if (config == "api_id") Console.WriteLine("Welcome! You can obtain your api_id/api_hash at https://my.telegram.org/apps"); + if (config == "session_key") return null; + if (config == "api_hash") Console.WriteLine("Welcome! You can obtain your api_id/api_hash at https://my.telegram.org/apps"); Console.Write($"Enter {config.Replace('_', ' ')}: "); return Console.ReadLine(); } @@ -340,7 +339,7 @@ namespace WTelegram TLConfig = await this.InvokeWithLayer(Layer.Version, new TL.Methods.InitConnection { - api_id = _apiId, + api_id = _session.ApiId, device_model = Config("device_model"), system_version = Config("system_version"), app_version = Config("app_version"), @@ -1082,7 +1081,7 @@ namespace WTelegram await this.Auth_LogOut(); _session.UserId = _dcSession.UserId = 0; } - var authorization = await this.Auth_ImportBotAuthorization(0, _apiId, _apiHash, botToken); + var authorization = await this.Auth_ImportBotAuthorization(0, _session.ApiId, _apiHash ??= Config("api_hash"), botToken); return LoginAlreadyDone(authorization); } @@ -1122,11 +1121,11 @@ namespace WTelegram Auth_SentCode sentCode; try { - sentCode = await this.Auth_SendCode(phone_number, _apiId, _apiHash, settings ??= new()); + sentCode = await this.Auth_SendCode(phone_number, _session.ApiId, _apiHash ??= Config("api_hash"), settings ??= new()); } catch (RpcException ex) when (ex.Code == 500 && ex.Message == "AUTH_RESTART") { - sentCode = await this.Auth_SendCode(phone_number, _apiId, _apiHash, settings ??= new()); + sentCode = await this.Auth_SendCode(phone_number, _session.ApiId, _apiHash, settings); } resent: var timeout = DateTime.UtcNow + TimeSpan.FromSeconds(sentCode.timeout); diff --git a/src/Helpers.cs b/src/Helpers.cs index 355275f..1dc2101 100644 --- a/src/Helpers.cs +++ b/src/Helpers.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Numerics; +using System.Reflection; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -253,6 +254,16 @@ namespace WTelegram 0x3f, 0x00 }; + internal static string GetSystemVersion() + { + var os = System.Runtime.InteropServices.RuntimeInformation.OSDescription; + int space = os.IndexOf(' ') + 1, dot = os.IndexOf('.'); + return os[(os.IndexOf(' ', space) < 0 ? 0 : space)..(dot < 0 ? os.Length : dot)]; + } + + internal static string GetAppVersion() + => (Assembly.GetEntryAssembly() ?? Array.Find(AppDomain.CurrentDomain.GetAssemblies(), a => a.EntryPoint != null))?.GetName().Version.ToString() ?? "0.0"; + public class IndirectStream : Stream { public IndirectStream(Stream innerStream) => _innerStream = innerStream; diff --git a/src/Session.cs b/src/Session.cs index 4b6c3a0..9f078b6 100644 --- a/src/Session.cs +++ b/src/Session.cs @@ -11,6 +11,7 @@ namespace WTelegram { internal class Session { + public int ApiId; public long UserId; public int MainDC; public Dictionary DCSessions = new(); diff --git a/src/TL.Schema.cs b/src/TL.Schema.cs index 5c8e697..3bb3bee 100644 --- a/src/TL.Schema.cs +++ b/src/TL.Schema.cs @@ -5572,9 +5572,9 @@ namespace TL /// App version public string app_version; /// When was the session created - public int date_created; + public DateTime date_created; /// When was the session last active - public int date_active; + public DateTime date_active; /// Last known IP public string ip; /// Country determined from IP @@ -9542,9 +9542,9 @@ namespace TL /// Platform public string platform; /// When was the web session created - public int date_created; + public DateTime date_created; /// When was the web session last active - public int date_active; + public DateTime date_active; /// IP address public string ip; /// Region, determined from IP address