Added logger system

This commit is contained in:
Wizou 2021-08-04 10:11:07 +02:00
parent 39b0a79f0f
commit 85a3ea997f
6 changed files with 45 additions and 29 deletions

View file

@ -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
{ {

View file

@ -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

View file

@ -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;

View file

@ -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
View file

@ -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;
} }

View file

@ -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>