mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
We don't need to store full User in session file anymore
This commit is contained in:
parent
a06be4e096
commit
d0be053707
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
## _Telegram Client API library written 100% in C# and .NET Standard_
|
## _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.
|
⚠️ 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.
|
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)
|
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.
|
If you like this library, please [consider a donation](http://wizou.fr/donate.html). ❤ This will help the project keep going.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -164,7 +163,7 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (resetUser)
|
if (resetUser)
|
||||||
_session.User = null;
|
_session.UserId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Establish connection to Telegram servers</summary>
|
/// <summary>Establish connection to Telegram servers</summary>
|
||||||
|
|
@ -310,7 +309,7 @@ namespace WTelegram
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Auth_ExportedAuthorization exported = null;
|
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);
|
exported = await this.Auth_ExportAuthorization(dcId);
|
||||||
await altSession.Client.ConnectAsync();
|
await altSession.Client.ConnectAsync();
|
||||||
if (exported != null)
|
if (exported != null)
|
||||||
|
|
@ -318,7 +317,6 @@ namespace WTelegram
|
||||||
var authorization = await altSession.Client.Auth_ImportAuthorization(exported.id, exported.bytes);
|
var authorization = await altSession.Client.Auth_ImportAuthorization(exported.id, exported.bytes);
|
||||||
if (authorization is not Auth_Authorization { user: User user })
|
if (authorization is not Auth_Authorization { user: User user })
|
||||||
throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name);
|
throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name);
|
||||||
_session.User = user;
|
|
||||||
altSession.UserId = user.id;
|
altSession.UserId = user.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -419,6 +417,7 @@ namespace WTelegram
|
||||||
_pendingRequests.Clear();
|
_pendingRequests.Clear();
|
||||||
_bareRequest = 0;
|
_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
|
var udpatesState = await this.Updates_GetState(); // this call reenables incoming Updates
|
||||||
OnUpdate(udpatesState);
|
OnUpdate(udpatesState);
|
||||||
}
|
}
|
||||||
|
|
@ -785,7 +784,7 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
else if (rpcError.error_code == 500 && rpcError.error_message == "AUTH_RESTART")
|
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();
|
_session.Save();
|
||||||
}
|
}
|
||||||
throw new RpcException(rpcError.error_code, rpcError.error_message);
|
throw new RpcException(rpcError.error_code, rpcError.error_message);
|
||||||
|
|
@ -927,33 +926,30 @@ namespace WTelegram
|
||||||
{
|
{
|
||||||
await ConnectAsync();
|
await ConnectAsync();
|
||||||
string botToken = Config("bot_token");
|
string botToken = Config("bot_token");
|
||||||
var prevUser = _session.User;
|
if (_session.UserId != 0) // a user is already logged-in
|
||||||
if (prevUser != null)
|
|
||||||
{
|
{
|
||||||
try
|
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
|
_session.UserId = _dcSession.UserId = self.id;
|
||||||
var users = await this.Users_GetUsers(new[] { InputUser.Self });
|
return 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 bot_token. Logging out and in...");
|
Helpers.Log(3, $"Current logged user {self.id} mismatched bot_token. Logging out and in...");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Helpers.Log(4, $"Error while verifying current bot! ({ex.Message}) Proceeding to login...");
|
Helpers.Log(4, $"Error while verifying current bot! ({ex.Message}) Proceeding to login...");
|
||||||
}
|
}
|
||||||
await this.Auth_LogOut();
|
await this.Auth_LogOut();
|
||||||
_dcSession.UserId = 0;
|
_session.UserId = _dcSession.UserId = 0;
|
||||||
}
|
}
|
||||||
var authorization = await this.Auth_ImportBotAuthorization(0, _apiId, _apiHash, botToken);
|
var authorization = await this.Auth_ImportBotAuthorization(0, _apiId, _apiHash, botToken);
|
||||||
if (authorization is not Auth_Authorization { user: User user })
|
if (authorization is not Auth_Authorization { user: User user })
|
||||||
throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name);
|
throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name);
|
||||||
_session.User = user;
|
_session.UserId = _dcSession.UserId = user.id;
|
||||||
_dcSession.UserId = user.id;
|
|
||||||
_session.Save();
|
_session.Save();
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
@ -968,36 +964,27 @@ namespace WTelegram
|
||||||
{
|
{
|
||||||
await ConnectAsync();
|
await ConnectAsync();
|
||||||
string phone_number = null;
|
string phone_number = null;
|
||||||
var prevUser = _session.User;
|
if (_session.UserId != 0) // a user is already logged-in
|
||||||
if (prevUser != null)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool sameUser = true;
|
var users = await this.Users_GetUsers(new[] { InputUser.Self }); // this calls also reenable incoming Updates
|
||||||
var userId = _config("user_id"); // if config prefers to validate current user by its id, use it
|
var self = users[0] as User;
|
||||||
if (userId == null || !int.TryParse(userId, out int id) || id != -1 && prevUser.id != id)
|
// 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
|
_session.UserId = _dcSession.UserId = self.id;
|
||||||
if (prevUser.phone != string.Concat(phone_number.Where(char.IsDigit)))
|
return self;
|
||||||
sameUser = false;
|
|
||||||
}
|
}
|
||||||
if (sameUser)
|
Helpers.Log(3, $"Current logged user {self.id} mismatched user_id or phone_number. Logging out and in...");
|
||||||
{
|
|
||||||
// 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...");
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Helpers.Log(4, $"Error while verifying current user! ({ex.Message}) Proceeding to login...");
|
Helpers.Log(4, $"Error while verifying current user! ({ex.Message}) Proceeding to login...");
|
||||||
}
|
}
|
||||||
await this.Auth_LogOut();
|
await this.Auth_LogOut();
|
||||||
_dcSession.UserId = 0;
|
_session.UserId = _dcSession.UserId = 0;
|
||||||
}
|
}
|
||||||
phone_number ??= Config("phone_number");
|
phone_number ??= Config("phone_number");
|
||||||
Auth_SentCode sentCode;
|
Auth_SentCode sentCode;
|
||||||
|
|
@ -1043,8 +1030,7 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
if (authorization is not Auth_Authorization { user: User user })
|
if (authorization is not Auth_Authorization { user: User user })
|
||||||
throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name);
|
throw new ApplicationException("Failed to get Authorization: " + authorization.GetType().Name);
|
||||||
_session.User = user;
|
_session.UserId = _dcSession.UserId = user.id;
|
||||||
_dcSession.UserId = user.id;
|
|
||||||
_session.Save();
|
_session.Save();
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,41 +26,6 @@ namespace WTelegram
|
||||||
Console.ResetColor();
|
Console.ResetColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class PolymorphicConverter<T> : JsonConverter<T> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Get a cryptographic random 64-bit value</summary>
|
/// <summary>Get a cryptographic random 64-bit value</summary>
|
||||||
public static long RandomLong()
|
public static long RandomLong()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,19 @@ using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace WTelegram
|
namespace WTelegram
|
||||||
{
|
{
|
||||||
internal class Session
|
internal class Session
|
||||||
{
|
{
|
||||||
public TL.User User;
|
public long UserId;
|
||||||
public int MainDC;
|
public int MainDC;
|
||||||
public Dictionary<int, DCSession> DCSessions = new();
|
public Dictionary<int, DCSession> DCSessions = new();
|
||||||
public TL.DcOption[] DcOptions;
|
public TL.DcOption[] DcOptions;
|
||||||
|
|
||||||
|
public UserShim User; // obsolete, to be removed
|
||||||
|
public class UserShim { public long id; } // to be removed
|
||||||
|
|
||||||
public class DCSession
|
public class DCSession
|
||||||
{
|
{
|
||||||
public long Id;
|
public long Id;
|
||||||
|
|
@ -39,14 +41,6 @@ namespace WTelegram
|
||||||
private string _pathname;
|
private string _pathname;
|
||||||
private byte[] _apiHash; // used as AES key for encryption of session file
|
private byte[] _apiHash; // used as AES key for encryption of session file
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new(Helpers.JsonOptions)
|
|
||||||
{
|
|
||||||
Converters = {
|
|
||||||
new Helpers.PolymorphicConverter<TL.UserProfilePhoto>(),
|
|
||||||
new Helpers.PolymorphicConverter<TL.UserStatus>()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
internal static Session LoadOrCreate(string pathname, byte[] apiHash)
|
internal static Session LoadOrCreate(string pathname, byte[] apiHash)
|
||||||
{
|
{
|
||||||
if (File.Exists(pathname))
|
if (File.Exists(pathname))
|
||||||
|
|
@ -54,6 +48,7 @@ namespace WTelegram
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var session = Load(pathname, apiHash);
|
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._pathname = pathname;
|
||||||
session._apiHash = apiHash;
|
session._apiHash = apiHash;
|
||||||
Helpers.Log(2, "Loaded previous session");
|
Helpers.Log(2, "Loaded previous session");
|
||||||
|
|
@ -76,12 +71,12 @@ namespace WTelegram
|
||||||
var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16);
|
var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16);
|
||||||
if (!sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32]))
|
if (!sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32]))
|
||||||
throw new ApplicationException("Integrity check failed in session loading");
|
throw new ApplicationException("Integrity check failed in session loading");
|
||||||
return JsonSerializer.Deserialize<Session>(utf8Json.AsSpan(32), JsonOptions);
|
return JsonSerializer.Deserialize<Session>(utf8Json.AsSpan(32), Helpers.JsonOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Save()
|
internal void Save()
|
||||||
{
|
{
|
||||||
var utf8Json = JsonSerializer.SerializeToUtf8Bytes(this, JsonOptions);
|
var utf8Json = JsonSerializer.SerializeToUtf8Bytes(this, Helpers.JsonOptions);
|
||||||
var finalBlock = new byte[16];
|
var finalBlock = new byte[16];
|
||||||
var output = new byte[(16 + 32 + utf8Json.Length + 16) & ~15];
|
var output = new byte[(16 + 32 + utf8Json.Length + 16) & ~15];
|
||||||
Encryption.RNG.GetBytes(output, 0, 16);
|
Encryption.RNG.GetBytes(output, 0, 16);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue