mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
Serialize User to Json inside session file to prevent future breaking changes
This commit is contained in:
parent
f6cc00068a
commit
223d8984cf
|
|
@ -43,6 +43,7 @@ namespace WTelegram
|
||||||
private readonly Dictionary<long, (Type type, TaskCompletionSource<object> tcs)> _pendingRequests = new();
|
private readonly Dictionary<long, (Type type, TaskCompletionSource<object> tcs)> _pendingRequests = new();
|
||||||
private SemaphoreSlim _sendSemaphore = new(0);
|
private SemaphoreSlim _sendSemaphore = new(0);
|
||||||
private CancellationTokenSource _cts;
|
private CancellationTokenSource _cts;
|
||||||
|
private int _reactorReconnects = 0;
|
||||||
|
|
||||||
/// <summary>Welcome to WTelegramClient! 😀</summary>
|
/// <summary>Welcome to WTelegramClient! 😀</summary>
|
||||||
/// <param name="configProvider">Config callback, is queried for: api_id, api_hash, session_pathname</param>
|
/// <param name="configProvider">Config callback, is queried for: api_id, api_hash, session_pathname</param>
|
||||||
|
|
@ -164,7 +165,7 @@ namespace WTelegram
|
||||||
var authorization = await this.Auth_ImportAuthorization(exported.id, exported.bytes);
|
var authorization = await this.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.Serialize();
|
_session.User = user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +181,6 @@ namespace WTelegram
|
||||||
|
|
||||||
private async Task Reactor(NetworkStream stream, CancellationTokenSource cts)
|
private async Task Reactor(NetworkStream stream, CancellationTokenSource cts)
|
||||||
{
|
{
|
||||||
int reconnects = 0;
|
|
||||||
while (!cts.IsCancellationRequested)
|
while (!cts.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
ITLObject obj = null;
|
ITLObject obj = null;
|
||||||
|
|
@ -203,8 +203,8 @@ namespace WTelegram
|
||||||
_pendingRequests.Clear();
|
_pendingRequests.Clear();
|
||||||
_bareRequest = 0;
|
_bareRequest = 0;
|
||||||
}
|
}
|
||||||
reconnects = (reconnects + 1) % MaxAutoReconnects;
|
_reactorReconnects = (_reactorReconnects + 1) % MaxAutoReconnects;
|
||||||
if (reconnects != 0)
|
if (_reactorReconnects != 0)
|
||||||
{
|
{
|
||||||
Reset(false);
|
Reset(false);
|
||||||
await ConnectAsync(); // start a new reactor
|
await ConnectAsync(); // start a new reactor
|
||||||
|
|
@ -696,7 +696,7 @@ namespace WTelegram
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var prevUser = Serialization.Deserialize<User>(_session.User);
|
var prevUser = _session.User;
|
||||||
if (prevUser?.id == int.Parse(botToken.Split(':')[0]))
|
if (prevUser?.id == int.Parse(botToken.Split(':')[0]))
|
||||||
return prevUser;
|
return prevUser;
|
||||||
}
|
}
|
||||||
|
|
@ -709,7 +709,7 @@ namespace WTelegram
|
||||||
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.Serialize();
|
_session.User = user;
|
||||||
_session.Save();
|
_session.Save();
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
@ -728,7 +728,7 @@ namespace WTelegram
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var prevUser = Serialization.Deserialize<User>(_session.User);
|
var prevUser = _session.User;
|
||||||
var userId = _config("user_id"); // if config prefers to validate current user by its id, use it
|
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)
|
if (userId == null || !int.TryParse(userId, out int id) || id != -1 && prevUser.id != id)
|
||||||
{
|
{
|
||||||
|
|
@ -780,7 +780,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);
|
||||||
//TODO: find better serialization for User not subject to TL changes?
|
//TODO: find better serialization for User not subject to TL changes?
|
||||||
_session.User = user.Serialize();
|
_session.User = user;
|
||||||
_session.Save();
|
_session.Save();
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace WTelegram
|
namespace WTelegram
|
||||||
{
|
{
|
||||||
|
|
@ -9,7 +11,7 @@ namespace WTelegram
|
||||||
// int argument is the LogLevel: https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loglevel
|
// int argument is the LogLevel: https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loglevel
|
||||||
public static Action<int, string> Log { get; set; } = DefaultLogger;
|
public static Action<int, string> Log { get; set; } = DefaultLogger;
|
||||||
|
|
||||||
public static readonly System.Text.Json.JsonSerializerOptions JsonOptions = new(System.Text.Json.JsonSerializerDefaults.Web) { IncludeFields = true, WriteIndented = true };
|
public static readonly JsonSerializerOptions JsonOptions = new() { IncludeFields = true, WriteIndented = true };
|
||||||
|
|
||||||
public static V GetOrCreate<K, V>(this Dictionary<K, V> dictionary, K key) where V : new()
|
public static V GetOrCreate<K, V>(this Dictionary<K, V> dictionary, K key) where V : new()
|
||||||
=> dictionary.TryGetValue(key, out V value) ? value : dictionary[key] = new V();
|
=> dictionary.TryGetValue(key, out V value) ? value : dictionary[key] = new V();
|
||||||
|
|
@ -23,6 +25,41 @@ 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static long RandomLong()
|
public static long RandomLong()
|
||||||
{
|
{
|
||||||
#if NETCOREAPP2_1_OR_GREATER
|
#if NETCOREAPP2_1_OR_GREATER
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,21 @@ namespace WTelegram
|
||||||
public long ServerTicksOffset;
|
public long ServerTicksOffset;
|
||||||
public long LastSentMsgId;
|
public long LastSentMsgId;
|
||||||
public TL.DcOption DataCenter;
|
public TL.DcOption DataCenter;
|
||||||
public byte[] User; // serialization of TL.User
|
public TL.User User;
|
||||||
|
|
||||||
public DateTime SessionStart => _sessionStart;
|
public DateTime SessionStart => _sessionStart;
|
||||||
private readonly DateTime _sessionStart = DateTime.UtcNow;
|
private readonly DateTime _sessionStart = DateTime.UtcNow;
|
||||||
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.UserProfilePhotoBase>(),
|
||||||
|
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))
|
||||||
|
|
@ -51,12 +59,12 @@ namespace WTelegram
|
||||||
var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16);
|
var utf8Json = decryptor.TransformFinalBlock(input, 16, input.Length - 16);
|
||||||
if (!Encryption.Sha256.ComputeHash(utf8Json, 32, utf8Json.Length - 32).SequenceEqual(utf8Json[0..32]))
|
if (!Encryption.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), Helpers.JsonOptions);
|
return JsonSerializer.Deserialize<Session>(utf8Json.AsSpan(32), JsonOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Save()
|
internal void Save()
|
||||||
{
|
{
|
||||||
var utf8Json = JsonSerializer.SerializeToUtf8Bytes(this, Helpers.JsonOptions);
|
var utf8Json = JsonSerializer.SerializeToUtf8Bytes(this, 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);
|
||||||
|
|
@ -91,7 +99,8 @@ namespace WTelegram
|
||||||
{
|
{
|
||||||
DataCenter = newDC;
|
DataCenter = newDC;
|
||||||
AuthKeyID = Salt = Seqno = 0;
|
AuthKeyID = Salt = Seqno = 0;
|
||||||
AuthKey = User = null;
|
AuthKey = null;
|
||||||
|
User = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue