From d0be053707c6a6b6ed1984764e82ab0d73617e3e Mon Sep 17 00:00:00 2001 From: Wizou Date: Sun, 7 Nov 2021 09:09:15 +0100 Subject: [PATCH] We don't need to store full User in session file anymore --- README.md | 4 +++- src/Client.cs | 62 +++++++++++++++++++------------------------------- src/Helpers.cs | 35 ---------------------------- src/Session.cs | 19 ++++++---------- 4 files changed, 34 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index a1945b9..bbe31b5 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ## _Telegram Client API library written 100% in C# and .NET Standard_ -## How to use +# How to use ⚠️ This library relies on asynchronous C# programming (`async/await`) so make sure you are familiar with this before proceeding. @@ -166,6 +166,8 @@ This library can be used for any Telegram scenarios including: Secret chats (end-to-end encryption, PFS) and connection to CDN DCs have not been tested yet. +Please don't use this library for Spam or Scam. Respect Telegram [Terms of Service](https://telegram.org/tos) or you might get banned from Telegram servers. + Developers feedbacks are welcome in the Telegram channel [@WTelegramClient](https://t.me/WTelegramClient) If you like this library, please [consider a donation](http://wizou.fr/donate.html). ❤ This will help the project keep going. diff --git a/src/Client.cs b/src/Client.cs index 743c748..4abac12 100644 --- a/src/Client.cs +++ b/src/Client.cs @@ -1,7 +1,6 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -164,7 +163,7 @@ namespace WTelegram } } if (resetUser) - _session.User = null; + _session.UserId = 0; } /// Establish connection to Telegram servers @@ -310,7 +309,7 @@ namespace WTelegram try { Auth_ExportedAuthorization exported = null; - if (_session.User != null && IsMainDC && altSession.UserId != _session.User.id) + if (_session.UserId != 0 && IsMainDC && altSession.UserId != _session.UserId) exported = await this.Auth_ExportAuthorization(dcId); await altSession.Client.ConnectAsync(); if (exported != null) @@ -318,7 +317,6 @@ namespace WTelegram var authorization = await altSession.Client.Auth_ImportAuthorization(exported.id, exported.bytes); if (authorization is not Auth_Authorization { user: User user }) throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name); - _session.User = user; altSession.UserId = user.id; } } @@ -419,6 +417,7 @@ namespace WTelegram _pendingRequests.Clear(); _bareRequest = 0; } + // TODO: implement an Updates gaps handling system? https://core.telegram.org/api/updates var udpatesState = await this.Updates_GetState(); // this call reenables incoming Updates OnUpdate(udpatesState); } @@ -785,7 +784,7 @@ namespace WTelegram } else if (rpcError.error_code == 500 && rpcError.error_message == "AUTH_RESTART") { - _session.User = null; // force a full login authorization flow, next time + _session.UserId = 0; // force a full login authorization flow, next time _session.Save(); } throw new RpcException(rpcError.error_code, rpcError.error_message); @@ -927,33 +926,30 @@ namespace WTelegram { await ConnectAsync(); string botToken = Config("bot_token"); - var prevUser = _session.User; - if (prevUser != null) + if (_session.UserId != 0) // a user is already logged-in { try { - if (prevUser.id == int.Parse(botToken.Split(':')[0])) + var users = await this.Users_GetUsers(new[] { InputUser.Self }); // this calls also reenable incoming Updates + var self = users[0] as User; + if (self.id == long.Parse(botToken.Split(':')[0])) { - // Update our info about the user, and reenable incoming Updates - var users = await this.Users_GetUsers(new[] { InputUser.Self }); - if (users.Length > 0 && users[0] is User self) - _session.User = prevUser = self; - return prevUser; + _session.UserId = _dcSession.UserId = self.id; + return self; } - Helpers.Log(3, $"Current logged user {prevUser.id} mismatched bot_token. Logging out and in..."); + Helpers.Log(3, $"Current logged user {self.id} mismatched bot_token. Logging out and in..."); } catch (Exception ex) { Helpers.Log(4, $"Error while verifying current bot! ({ex.Message}) Proceeding to login..."); } await this.Auth_LogOut(); - _dcSession.UserId = 0; + _session.UserId = _dcSession.UserId = 0; } var authorization = await this.Auth_ImportBotAuthorization(0, _apiId, _apiHash, botToken); if (authorization is not Auth_Authorization { user: User user }) throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name); - _session.User = user; - _dcSession.UserId = user.id; + _session.UserId = _dcSession.UserId = user.id; _session.Save(); return user; } @@ -968,36 +964,27 @@ namespace WTelegram { await ConnectAsync(); string phone_number = null; - var prevUser = _session.User; - if (prevUser != null) + if (_session.UserId != 0) // a user is already logged-in { try { - bool sameUser = true; - var userId = _config("user_id"); // if config prefers to validate current user by its id, use it - if (userId == null || !int.TryParse(userId, out int id) || id != -1 && prevUser.id != id) + var users = await this.Users_GetUsers(new[] { InputUser.Self }); // this calls also reenable incoming Updates + var self = users[0] as User; + // check user_id or phone_number match currently logged-in user + if ((int.TryParse(_config("user_id"), out int id) && (id == -1 || self.id == id)) || + self.phone == string.Concat((phone_number = Config("phone_number")).Where(char.IsDigit))) { - phone_number = Config("phone_number"); // otherwise, validation is done by the phone number - if (prevUser.phone != string.Concat(phone_number.Where(char.IsDigit))) - sameUser = false; + _session.UserId = _dcSession.UserId = self.id; + return self; } - if (sameUser) - { - // TODO: implement a more complete Updates gaps handling system? https://core.telegram.org/api/updates - // Update our info about the user, and reenable incoming Updates - var users = await this.Users_GetUsers(new[] { InputUser.Self }); - if (users.Length > 0 && users[0] is User self) - _session.User = prevUser = self; - return prevUser; - } - Helpers.Log(3, $"Current logged user {prevUser.id} mismatched user_id or phone_number. Logging out and in..."); + Helpers.Log(3, $"Current logged user {self.id} mismatched user_id or phone_number. Logging out and in..."); } catch (Exception ex) { Helpers.Log(4, $"Error while verifying current user! ({ex.Message}) Proceeding to login..."); } await this.Auth_LogOut(); - _dcSession.UserId = 0; + _session.UserId = _dcSession.UserId = 0; } phone_number ??= Config("phone_number"); Auth_SentCode sentCode; @@ -1043,8 +1030,7 @@ namespace WTelegram } if (authorization is not Auth_Authorization { user: User user }) throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name); - _session.User = user; - _dcSession.UserId = user.id; + _session.UserId = _dcSession.UserId = user.id; _session.Save(); return user; } diff --git a/src/Helpers.cs b/src/Helpers.cs index 1b6d43b..e7d51c2 100644 --- a/src/Helpers.cs +++ b/src/Helpers.cs @@ -26,41 +26,6 @@ namespace WTelegram Console.ResetColor(); } - internal class PolymorphicConverter : JsonConverter where T : class - { - public override bool HandleNull => true; - - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) - { - if (value == null) - writer.WriteNullValue(); - else - { - writer.WriteStartObject(); - writer.WritePropertyName(value.GetType().FullName); - JsonSerializer.Serialize(writer, value, value.GetType(), JsonOptions); - writer.WriteEndObject(); - } - } - - public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) - return null; - else if (reader.TokenType == JsonTokenType.StartObject) - { - if (!reader.Read() || reader.TokenType != JsonTokenType.PropertyName) throw new JsonException(); - var returnType = typeToConvert.Assembly.GetType(reader.GetString()); - if (!typeToConvert.IsAssignableFrom(returnType)) throw new JsonException(); - var result = (T)JsonSerializer.Deserialize(ref reader, returnType, JsonOptions); - if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject) throw new JsonException(); - return result; - } - else - throw new JsonException(); - } - } - /// Get a cryptographic random 64-bit value public static long RandomLong() { diff --git a/src/Session.cs b/src/Session.cs index 94995b9..89a2082 100644 --- a/src/Session.cs +++ b/src/Session.cs @@ -5,17 +5,19 @@ using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text.Json; -using System.Threading; namespace WTelegram { internal class Session { - public TL.User User; + public long UserId; public int MainDC; public Dictionary DCSessions = new(); public TL.DcOption[] DcOptions; + public UserShim User; // obsolete, to be removed + public class UserShim { public long id; } // to be removed + public class DCSession { public long Id; @@ -39,14 +41,6 @@ namespace WTelegram private string _pathname; private byte[] _apiHash; // used as AES key for encryption of session file - private static readonly JsonSerializerOptions JsonOptions = new(Helpers.JsonOptions) - { - Converters = { - new Helpers.PolymorphicConverter(), - new Helpers.PolymorphicConverter() - } - }; - internal static Session LoadOrCreate(string pathname, byte[] apiHash) { if (File.Exists(pathname)) @@ -54,6 +48,7 @@ namespace WTelegram try { var session = Load(pathname, apiHash); + if (session.User != null) { session.UserId = session.User.id; session.User.id = 0; session.User = null; } session._pathname = pathname; session._apiHash = apiHash; Helpers.Log(2, "Loaded previous session"); @@ -76,12 +71,12 @@ namespace WTelegram var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16); if (!sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32])) throw new ApplicationException("Integrity check failed in session loading"); - return JsonSerializer.Deserialize(utf8Json.AsSpan(32), JsonOptions); + return JsonSerializer.Deserialize(utf8Json.AsSpan(32), Helpers.JsonOptions); } internal void Save() { - var utf8Json = JsonSerializer.SerializeToUtf8Bytes(this, JsonOptions); + var utf8Json = JsonSerializer.SerializeToUtf8Bytes(this, Helpers.JsonOptions); var finalBlock = new byte[16]; var output = new byte[(16 + 32 + utf8Json.Length + 16) & ~15]; Encryption.RNG.GetBytes(output, 0, 16);