mirror of
https://github.com/wiz0u/WTelegramClient.git
synced 2025-12-06 06:52:01 +01:00
Added logger system
This commit is contained in:
parent
39b0a79f0f
commit
85a3ea997f
29
Client.cs
29
Client.cs
|
|
@ -74,7 +74,7 @@ namespace WTelegram
|
||||||
{
|
{
|
||||||
var endpoint = _session.DataCenter == null ? IPEndPoint.Parse(_config("server_address"))
|
var endpoint = _session.DataCenter == null ? IPEndPoint.Parse(_config("server_address"))
|
||||||
: new IPEndPoint(IPAddress.Parse(_session.DataCenter.ip_address), _session.DataCenter.port);
|
: new IPEndPoint(IPAddress.Parse(_session.DataCenter.ip_address), _session.DataCenter.port);
|
||||||
Console.WriteLine($"Connecting to {endpoint}...");
|
Helpers.Log(2, $"Connecting to {endpoint}...");
|
||||||
_tcpClient = new TcpClient(endpoint.AddressFamily);
|
_tcpClient = new TcpClient(endpoint.AddressFamily);
|
||||||
await _tcpClient.ConnectAsync(endpoint.Address, endpoint.Port);
|
await _tcpClient.ConnectAsync(endpoint.Address, endpoint.Port);
|
||||||
_networkStream = _tcpClient.GetStream();
|
_networkStream = _tcpClient.GetStream();
|
||||||
|
|
@ -102,7 +102,7 @@ namespace WTelegram
|
||||||
|
|
||||||
private async Task MigrateDCAsync(int dcId)
|
private async Task MigrateDCAsync(int dcId)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Migrate to DC {dcId}...");
|
Helpers.Log(2, $"Migrate to DC {dcId}...");
|
||||||
//TODO: Export/Import client authorization?
|
//TODO: Export/Import client authorization?
|
||||||
var prevFamily = _tcpClient.Client.RemoteEndPoint.AddressFamily;
|
var prevFamily = _tcpClient.Client.RemoteEndPoint.AddressFamily;
|
||||||
_tcpClient.Close();
|
_tcpClient.Close();
|
||||||
|
|
@ -137,7 +137,7 @@ namespace WTelegram
|
||||||
|
|
||||||
if (_session.AuthKeyID == 0) // send unencrypted message
|
if (_session.AuthKeyID == 0) // send unencrypted message
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Sending {msg.GetType().Name}...");
|
Helpers.Log(1, $"Sending {msg.GetType().Name}...");
|
||||||
writer.Write(0L); // int64 auth_key_id = 0 (Unencrypted)
|
writer.Write(0L); // int64 auth_key_id = 0 (Unencrypted)
|
||||||
writer.Write(msgId); // int64 message_id
|
writer.Write(msgId); // int64 message_id
|
||||||
writer.Write(0); // int32 message_data_length (to be patched)
|
writer.Write(0); // int32 message_data_length (to be patched)
|
||||||
|
|
@ -146,7 +146,7 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Sending {msg.GetType().Name}... (seqno {seqno})");
|
Helpers.Log(1, $"Sending {msg.GetType().Name}... (seqno {seqno})");
|
||||||
//TODO: Implement MTProto 2.0
|
//TODO: Implement MTProto 2.0
|
||||||
using var clearStream = new MemoryStream(1024); //TODO: choose a useful capacity
|
using var clearStream = new MemoryStream(1024); //TODO: choose a useful capacity
|
||||||
using var clearWriter = new BinaryWriter(clearStream, Encoding.UTF8);
|
using var clearWriter = new BinaryWriter(clearStream, Encoding.UTF8);
|
||||||
|
|
@ -183,7 +183,7 @@ namespace WTelegram
|
||||||
_lastSentMsg = msg;
|
_lastSentMsg = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<object> RecvInternalAsync()
|
internal async Task<ITLObject> RecvInternalAsync()
|
||||||
{
|
{
|
||||||
var data = await RecvFrameAsync();
|
var data = await RecvFrameAsync();
|
||||||
if (data.Length == 4 && data[3] == 0xFF)
|
if (data.Length == 4 && data[3] == 0xFF)
|
||||||
|
|
@ -204,7 +204,7 @@ namespace WTelegram
|
||||||
var ctorNb = reader.ReadUInt32();
|
var ctorNb = reader.ReadUInt32();
|
||||||
if (!Schema.Mappings.TryGetValue(ctorNb, out var realType))
|
if (!Schema.Mappings.TryGetValue(ctorNb, out var realType))
|
||||||
throw new ApplicationException($"Cannot find type for ctor #{ctorNb:x}");
|
throw new ApplicationException($"Cannot find type for ctor #{ctorNb:x}");
|
||||||
Console.WriteLine($"Receiving {realType.Name,-50} timestamp={_session.MsgIdToStamp(msgId)} isResponse={(msgId & 2) != 0} unencrypted");
|
Helpers.Log(1, $"Receiving {realType.Name,-50} timestamp={_session.MsgIdToStamp(msgId)} isResponse={(msgId & 2) != 0} unencrypted");
|
||||||
return Schema.DeserializeObject(reader, realType);
|
return Schema.DeserializeObject(reader, realType);
|
||||||
}
|
}
|
||||||
else if (authKeyId != _session.AuthKeyID)
|
else if (authKeyId != _session.AuthKeyID)
|
||||||
|
|
@ -234,7 +234,7 @@ namespace WTelegram
|
||||||
var ctorNb = reader.ReadUInt32();
|
var ctorNb = reader.ReadUInt32();
|
||||||
if (!Schema.Mappings.TryGetValue(ctorNb, out var realType))
|
if (!Schema.Mappings.TryGetValue(ctorNb, out var realType))
|
||||||
throw new ApplicationException($"Cannot find type for ctor #{ctorNb:x}");
|
throw new ApplicationException($"Cannot find type for ctor #{ctorNb:x}");
|
||||||
Console.WriteLine($"Receiving {realType.Name,-50} timestamp={_session.MsgIdToStamp(msgId)} isResponse={(msgId & 2) != 0} {(seqno == -1 ? "clearText" : "isContent")}={(seqno & 1) != 0}");
|
Helpers.Log(1, $"Receiving {realType.Name,-50} timestamp={_session.MsgIdToStamp(msgId)} isResponse={(msgId & 2) != 0} {(seqno == -1 ? "clearText" : "isContent")}={(seqno & 1) != 0}");
|
||||||
if (realType == typeof(RpcResult))
|
if (realType == typeof(RpcResult))
|
||||||
return DeserializeRpcResult(reader); // hack necessary because some RPC return bare types like bool or int[]
|
return DeserializeRpcResult(reader); // hack necessary because some RPC return bare types like bool or int[]
|
||||||
else
|
else
|
||||||
|
|
@ -282,14 +282,14 @@ namespace WTelegram
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private object DeserializeRpcResult(BinaryReader reader)
|
private RpcResult DeserializeRpcResult(BinaryReader reader)
|
||||||
{
|
{
|
||||||
long reqMsgId = reader.ReadInt64();
|
long reqMsgId = reader.ReadInt64();
|
||||||
var rpcResult = new RpcResult { req_msg_id = reqMsgId };
|
var rpcResult = new RpcResult { req_msg_id = reqMsgId };
|
||||||
if (reqMsgId == _session.LastSentMsgId)
|
if (reqMsgId == _session.LastSentMsgId)
|
||||||
rpcResult.result = Schema.DeserializeValue(reader, _lastRpcResultType);
|
rpcResult.result = Schema.DeserializeValue(reader, _lastRpcResultType);
|
||||||
else
|
else
|
||||||
rpcResult.result = Schema.Deserialize<object>(reader);
|
rpcResult.result = Schema.Deserialize<ITLObject>(reader);
|
||||||
return rpcResult;
|
return rpcResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -343,15 +343,14 @@ namespace WTelegram
|
||||||
await SendAsync(msgsAck, false);
|
await SendAsync(msgsAck, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleMessageAsync(object obj)
|
private async Task HandleMessageAsync(ITLObject obj)
|
||||||
{
|
{
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
case MsgContainer container:
|
case MsgContainer container:
|
||||||
foreach (var msg in container.messages)
|
foreach (var msg in container.messages)
|
||||||
{
|
{
|
||||||
Console.Write($" → {msg.body?.GetType().Name}");
|
Helpers.Log(1, $" → {msg.body?.GetType().Name,-48} timestamp={_session.MsgIdToStamp(msg.msg_id)} isResponse={(msg.msg_id & 2) != 0} {(msg.seqno == -1 ? "clearText" : "isContent")}={(msg.seqno & 1) != 0}");
|
||||||
Console.WriteLine($"{new string(' ', Math.Max(0, 60 - Console.CursorLeft))} timestamp={_session.MsgIdToStamp(msg.msg_id)} isResponse={(msg.msg_id & 2) != 0} {(msg.seqno == -1 ? "clearText" : "isContent")}={(msg.seqno & 1) != 0}");
|
|
||||||
if ((msg.seqno & 1) != 0) lock (_msgsToAck) _msgsToAck.Add(msg.msg_id);
|
if ((msg.seqno & 1) != 0) lock (_msgsToAck) _msgsToAck.Add(msg.msg_id);
|
||||||
if (msg.body != null) await HandleMessageAsync(msg.body);
|
if (msg.body != null) await HandleMessageAsync(msg.body);
|
||||||
}
|
}
|
||||||
|
|
@ -362,14 +361,14 @@ namespace WTelegram
|
||||||
await SendAsync(_lastSentMsg);
|
await SendAsync(_lastSentMsg);
|
||||||
break;
|
break;
|
||||||
case BadMsgNotification badMsgNotification:
|
case BadMsgNotification badMsgNotification:
|
||||||
Console.WriteLine($"BadMsgNotification {badMsgNotification.error_code} for msg {badMsgNotification.bad_msg_seqno}");
|
Helpers.Log(3, $"BadMsgNotification {badMsgNotification.error_code} for msg {badMsgNotification.bad_msg_seqno}");
|
||||||
break;
|
break;
|
||||||
case RpcResult rpcResult:
|
case RpcResult rpcResult:
|
||||||
if (_session.MsgIdToStamp(rpcResult.req_msg_id) >= _session.SessionStart)
|
if (_session.MsgIdToStamp(rpcResult.req_msg_id) >= _session.SessionStart)
|
||||||
throw new ApplicationException($"Got RpcResult({rpcResult.result.GetType().Name}) for unknown msgId {rpcResult.req_msg_id}");
|
throw new ApplicationException($"Got RpcResult({rpcResult.result.GetType().Name}) for unknown msgId {rpcResult.req_msg_id}");
|
||||||
break; // silently ignore results for msg_id from previous sessions
|
break; // silently ignore results for msg_id from previous sessions
|
||||||
default:
|
default:
|
||||||
//_updateHandler?.Invoke(obj);
|
_updateHandler?.Invoke(obj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -386,7 +385,7 @@ namespace WTelegram
|
||||||
api_hash = _apiHash,
|
api_hash = _apiHash,
|
||||||
settings = settings ?? new()
|
settings = settings ?? new()
|
||||||
});
|
});
|
||||||
Console.WriteLine($"A verification code has been sent via {sentCode.type.GetType().Name[17..]}");
|
Helpers.Log(3, $"A verification code has been sent via {sentCode.type.GetType().Name[17..]}");
|
||||||
var verification_code = _config("verification_code");
|
var verification_code = _config("verification_code");
|
||||||
var authorization = await CallAsync(new Auth_SignIn
|
var authorization = await CallAsync(new Auth_SignIn
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace WTelegram
|
||||||
if (resPQ.nonce != reqPQ.nonce) throw new ApplicationException("Nonce mismatch");
|
if (resPQ.nonce != reqPQ.nonce) throw new ApplicationException("Nonce mismatch");
|
||||||
var fingerprint = resPQ.server_public_key_fingerprints.FirstOrDefault(PublicKeys.ContainsKey);
|
var fingerprint = resPQ.server_public_key_fingerprints.FirstOrDefault(PublicKeys.ContainsKey);
|
||||||
if (fingerprint == 0) throw new ApplicationException("Couldn't match any server_public_key_fingerprints");
|
if (fingerprint == 0) throw new ApplicationException("Couldn't match any server_public_key_fingerprints");
|
||||||
Console.WriteLine($"Selected public key with fingerprint {fingerprint:X}");
|
Helpers.Log(2, $"Selected public key with fingerprint {fingerprint:X}");
|
||||||
//3)
|
//3)
|
||||||
long retry_id = 0;
|
long retry_id = 0;
|
||||||
ulong pq = Helpers.FromBigEndian(resPQ.pq);
|
ulong pq = Helpers.FromBigEndian(resPQ.pq);
|
||||||
|
|
@ -69,7 +69,7 @@ namespace WTelegram
|
||||||
var g_a = new BigInteger(serverDHinnerData.g_a, true, true);
|
var g_a = new BigInteger(serverDHinnerData.g_a, true, true);
|
||||||
var dh_prime = new BigInteger(serverDHinnerData.dh_prime, true, true);
|
var dh_prime = new BigInteger(serverDHinnerData.dh_prime, true, true);
|
||||||
ValidityChecks(dh_prime, serverDHinnerData.g);
|
ValidityChecks(dh_prime, serverDHinnerData.g);
|
||||||
Console.WriteLine($"Server time: {serverDHinnerData.server_time} UTC");
|
Helpers.Log(1, $"Server time: {serverDHinnerData.server_time} UTC");
|
||||||
session.ServerTicksOffset = (serverDHinnerData.server_time - localTime).Ticks;
|
session.ServerTicksOffset = (serverDHinnerData.server_time - localTime).Ticks;
|
||||||
|
|
||||||
//6)
|
//6)
|
||||||
|
|
@ -210,7 +210,7 @@ namespace WTelegram
|
||||||
var bareData = Schema.Serialize(publicKey).AsSpan(4); // bare serialization
|
var bareData = Schema.Serialize(publicKey).AsSpan(4); // bare serialization
|
||||||
var fingerprint = BitConverter.ToInt64(SHA1.HashData(bareData), 12); // 64 lower-order bits of SHA1
|
var fingerprint = BitConverter.ToInt64(SHA1.HashData(bareData), 12); // 64 lower-order bits of SHA1
|
||||||
PublicKeys[fingerprint] = publicKey;
|
PublicKeys[fingerprint] = publicKey;
|
||||||
Console.WriteLine($"Loaded a public key with fingerprint {fingerprint:X}");
|
Helpers.Log(1, $"Loaded a public key with fingerprint {fingerprint:X}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LoadDefaultPublicKey() // fingerprint C3B42B026CE86B21
|
private static void LoadDefaultPublicKey() // fingerprint C3B42B026CE86B21
|
||||||
|
|
|
||||||
11
Helpers.cs
11
Helpers.cs
|
|
@ -10,6 +10,17 @@ namespace WTelegram
|
||||||
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();
|
||||||
|
|
||||||
|
public static Action<int, string> Log { get; set; } = DefaultLogger;
|
||||||
|
|
||||||
|
private static readonly ConsoleColor[] LogLevelToColor = new[] { ConsoleColor.DarkGray, ConsoleColor.DarkCyan, ConsoleColor.Cyan,
|
||||||
|
ConsoleColor.Yellow, ConsoleColor.Red, ConsoleColor.Magenta, ConsoleColor.DarkBlue };
|
||||||
|
private static void DefaultLogger(int level, string message)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = LogLevelToColor[level];
|
||||||
|
Console.WriteLine(message);
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
|
|
||||||
public static void LittleEndian(byte[] buffer, int offset, int value)
|
public static void LittleEndian(byte[] buffer, int offset, int value)
|
||||||
{
|
{
|
||||||
buffer[offset + 0] = (byte)value;
|
buffer[offset + 0] = (byte)value;
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,12 @@ namespace WTelegram
|
||||||
var json = File.ReadAllText(pathname);
|
var json = File.ReadAllText(pathname);
|
||||||
var session = JsonSerializer.Deserialize<Session>(json, Helpers.JsonOptions);
|
var session = JsonSerializer.Deserialize<Session>(json, Helpers.JsonOptions);
|
||||||
session._pathname = pathname;
|
session._pathname = pathname;
|
||||||
Console.WriteLine("Loaded previous session");
|
Helpers.Log(2, "Loaded previous session");
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Exception while reading session file: {ex.Message}");
|
Helpers.Log(4, $"Exception while reading session file: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sessionId = new byte[8];
|
var sessionId = new byte[8];
|
||||||
|
|
|
||||||
19
TL.cs
19
TL.cs
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using WTelegram;
|
||||||
|
|
||||||
namespace TL
|
namespace TL
|
||||||
{
|
{
|
||||||
|
|
@ -35,7 +36,7 @@ namespace TL
|
||||||
return memStream.ToArray();
|
return memStream.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static T Deserialize<T>(byte[] bytes)
|
internal static T Deserialize<T>(byte[] bytes) where T : ITLObject
|
||||||
{
|
{
|
||||||
using var memStream = new MemoryStream(bytes);
|
using var memStream = new MemoryStream(bytes);
|
||||||
using var reader = new BinaryReader(memStream);
|
using var reader = new BinaryReader(memStream);
|
||||||
|
|
@ -50,7 +51,7 @@ namespace TL
|
||||||
SerializeObject(writer, msg);
|
SerializeObject(writer, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static T Deserialize<T>(BinaryReader reader)
|
internal static T Deserialize<T>(BinaryReader reader) where T : ITLObject
|
||||||
{
|
{
|
||||||
var ctorNb = reader.ReadUInt32();
|
var ctorNb = reader.ReadUInt32();
|
||||||
if (!Mappings.TryGetValue(ctorNb, out var realType))
|
if (!Mappings.TryGetValue(ctorNb, out var realType))
|
||||||
|
|
@ -72,7 +73,7 @@ namespace TL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static object DeserializeObject(BinaryReader reader, Type type)
|
internal static ITLObject DeserializeObject(BinaryReader reader, Type type)
|
||||||
{
|
{
|
||||||
var obj = Activator.CreateInstance(type);
|
var obj = Activator.CreateInstance(type);
|
||||||
var fields = obj.GetType().GetFields().GroupBy(f => f.DeclaringType).Reverse().SelectMany(g => g);
|
var fields = obj.GetType().GetFields().GroupBy(f => f.DeclaringType).Reverse().SelectMany(g => g);
|
||||||
|
|
@ -85,7 +86,7 @@ namespace TL
|
||||||
field.SetValue(obj, value);
|
field.SetValue(obj, value);
|
||||||
if (field.Name.Equals("Flags", StringComparison.OrdinalIgnoreCase)) flags = (int)value;
|
if (field.Name.Equals("Flags", StringComparison.OrdinalIgnoreCase)) flags = (int)value;
|
||||||
}
|
}
|
||||||
return type == typeof(GzipPacked) ? UnzipPacket((GzipPacked)obj) : obj;
|
return type == typeof(GzipPacked) ? UnzipPacket((GzipPacked)obj) : (ITLObject)obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void SerializeValue(BinaryWriter writer, object value)
|
internal static void SerializeValue(BinaryWriter writer, object value)
|
||||||
|
|
@ -161,7 +162,7 @@ namespace TL
|
||||||
else if (type.IsValueType)
|
else if (type.IsValueType)
|
||||||
return DeserializeObject(reader, type);
|
return DeserializeObject(reader, type);
|
||||||
else
|
else
|
||||||
return Deserialize<object>(reader);
|
return Deserialize<ITLObject>(reader);
|
||||||
default:
|
default:
|
||||||
ShouldntBeHere();
|
ShouldntBeHere();
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -239,18 +240,18 @@ namespace TL
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ex);
|
Helpers.Log(4, ex.ToString());
|
||||||
}
|
}
|
||||||
reader.BaseStream.Position = pos + array[i].bytes;
|
reader.BaseStream.Position = pos + array[i].bytes;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object UnzipPacket(GzipPacked obj)
|
private static ITLObject UnzipPacket(GzipPacked obj)
|
||||||
{
|
{
|
||||||
using var reader = new BinaryReader(new GZipStream(new MemoryStream(obj.packed_data), CompressionMode.Decompress));
|
using var reader = new BinaryReader(new GZipStream(new MemoryStream(obj.packed_data), CompressionMode.Decompress));
|
||||||
var result = DeserializeValue(reader, typeof(object));
|
var result = Deserialize<ITLObject>(reader);
|
||||||
Console.WriteLine($" → {result.GetType().Name}");
|
Helpers.Log(1, $" → {result.GetType().Name}");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,11 @@
|
||||||
<RootNamespace>WTelegram</RootNamespace>
|
<RootNamespace>WTelegram</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove=".gitattributes" />
|
||||||
|
<None Remove=".gitignore" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Crc32.NET" Version="1.2.0" />
|
<PackageReference Include="Crc32.NET" Version="1.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue