Get rid of ITLFunction lambda writer and use declared ITLMethod<Ret> classes instead

This commit is contained in:
Wizou 2021-11-07 16:50:59 +01:00
parent d0be053707
commit fd9ec2eaed
5 changed files with 6018 additions and 2362 deletions

View file

@ -44,7 +44,7 @@ namespace WTelegram
private static readonly byte[] IntermediateHeader = new byte[4] { 0xee, 0xee, 0xee, 0xee };
private TcpClient _tcpClient;
private NetworkStream _networkStream;
private ITLFunction _lastSentMsg;
private ITLObject _lastSentMsg;
private long _lastRecvMsgId;
private readonly List<long> _msgsToAck = new();
private readonly Random _random = new();
@ -143,7 +143,7 @@ namespace WTelegram
try
{
if (CheckMsgsToAck() is MsgsAck msgsAck)
SendAsync(MakeFunction(msgsAck), false).Wait(1000);
SendAsync(msgsAck, false).Wait(1000);
}
catch (Exception)
{
@ -260,15 +260,18 @@ namespace WTelegram
await CreateAuthorizationKey(this, _dcSession);
var keepAliveTask = KeepAlive(_cts.Token);
TLConfig = await this.InvokeWithLayer<Config>(Layer.Version,
Schema.InitConnection(_apiId,
Config("device_model"),
Config("system_version"),
Config("app_version"),
Config("system_lang_code"),
Config("lang_pack"),
Config("lang_code"),
Schema.Help_GetConfig));
TLConfig = await this.InvokeWithLayer(Layer.Version,
new Schema.InitConnection_<Config>
{
api_id = _apiId,
device_model = Config("device_model"),
system_version = Config("system_version"),
app_version = Config("app_version"),
system_lang_code = Config("system_lang_code"),
lang_pack = Config("lang_pack"),
lang_code = Config("lang_code"),
query = new Schema.Help_GetConfig_()
});
_session.DcOptions = TLConfig.dc_options;
_saltChangeCounter = 0;
if (_dcSession.DataCenter == null)
@ -551,13 +554,13 @@ namespace WTelegram
return length;
}
private async Task<long> SendAsync(ITLFunction func, bool isContent)
private async Task<long> SendAsync(ITLObject msg, bool isContent)
{
if (_dcSession.AuthKeyID != 0 && isContent && CheckMsgsToAck() is MsgsAck msgsAck)
{
var ackMsg = NewMsgId(false);
var mainMsg = NewMsgId(true);
await SendAsync(MakeContainer((MakeFunction(msgsAck), ackMsg), (func, mainMsg)), false);
await SendAsync(MakeContainer((msgsAck, ackMsg), (msg, mainMsg)), false);
return mainMsg.msgId;
}
(long msgId, int seqno) = NewMsgId(isContent && _dcSession.AuthKeyID != 0);
@ -573,8 +576,8 @@ namespace WTelegram
writer.Write(0L); // int64 auth_key_id = 0 (Unencrypted)
writer.Write(msgId); // int64 message_id
writer.Write(0); // int32 message_data_length (to be patched)
var typeName = func(writer); // bytes message_data
Helpers.Log(1, $"{_dcSession.DcID}>Sending {typeName}...");
writer.WriteTLObject(msg); // bytes message_data
Helpers.Log(1, $"{_dcSession.DcID}>Sending {msg.GetType().Name}...");
BinaryPrimitives.WriteInt32LittleEndian(memStream.GetBuffer().AsSpan(20), (int)memStream.Length - 24); // patch message_data_length
}
else
@ -592,11 +595,11 @@ namespace WTelegram
clearWriter.Write(msgId); // int64 message_id
clearWriter.Write(seqno); // int32 msg_seqno
clearWriter.Write(0); // int32 message_data_length (to be patched)
var typeName = func(clearWriter); // bytes message_data
clearWriter.WriteTLObject(msg); // bytes message_data
if ((seqno & 1) != 0)
Helpers.Log(1, $"{_dcSession.DcID}>Sending {typeName,-40} #{(short)msgId.GetHashCode():X4}");
Helpers.Log(1, $"{_dcSession.DcID}>Sending {msg.GetType().Name,-40} #{(short)msgId.GetHashCode():X4}");
else
Helpers.Log(1, $"{_dcSession.DcID}>Sending {typeName,-40} {MsgIdToStamp(msgId):u} (svc)");
Helpers.Log(1, $"{_dcSession.DcID}>Sending {msg.GetType().Name,-40} {MsgIdToStamp(msgId):u} (svc)");
int clearLength = (int)clearStream.Length - prepend; // length before padding (= 32 + message_data_length)
int padding = (0x7FFFFFF0 - clearLength) % 16;
#if !MTPROTO1
@ -626,7 +629,7 @@ namespace WTelegram
//TODO: support Transport obfuscation?
await _networkStream.WriteAsync(memStream.GetBuffer(), 0, frameLength);
_lastSentMsg = func;
_lastSentMsg = msg;
}
finally
{
@ -635,13 +638,6 @@ namespace WTelegram
return msgId;
}
private static ITLFunction MakeFunction(ITLObject msg)
=> writer =>
{
writer.WriteTLObject(msg);
return msg.GetType().Name;
};
internal MsgContainer ReadMsgContainer(TL.BinaryReader reader)
{
int count = reader.ReadInt32();
@ -727,7 +723,7 @@ namespace WTelegram
return request;
}
internal async Task<X> CallBareAsync<X>(ITLFunction request)
internal async Task<X> CallBareAsync<X>(ITLMethod<X> request)
{
if (_bareRequest != 0) throw new ApplicationException("A bare request is already undergoing");
var msgId = await SendAsync(request, false);
@ -740,9 +736,9 @@ namespace WTelegram
/// <summary>Call the given TL method <i>(You shouldn't need to call this, usually)</i></summary>
/// <typeparam name="X">Expected type of the returned object</typeparam>
/// <param name="request">TL method serializer</param>
/// <param name="request">TL method object</param>
/// <returns>Wait for the reply and return the resulting object, or throws an RpcException if an error was replied</returns>
public async Task<X> CallAsync<X>(ITLFunction request)
public async Task<X> CallAsync<X>(ITLMethod<X> request)
{
retry:
var msgId = await SendAsync(request, true);
@ -806,27 +802,15 @@ namespace WTelegram
}
}
private ITLFunction MakeContainer(params (ITLFunction func, (long msgId, int seqno))[] msgs)
=> writer =>
private static MsgContainer MakeContainer(params (ITLObject obj, (long msgId, int seqno))[] msgs)
=> new()
{
writer.Write(0x73F1F8DC);
writer.Write(msgs.Length);
foreach (var (func, (msgId, seqno)) in msgs)
messages = msgs.Select(msg => new _Message
{
writer.Write(msgId);
writer.Write(seqno);
var patchPos = writer.BaseStream.Position;
writer.Write(0);
var typeName = func(writer);
if ((seqno & 1) != 0)
Helpers.Log(1, $" Sending → {typeName,-40} #{(short)msgId.GetHashCode():X4}");
else
Helpers.Log(1, $" Sending → {typeName,-40} {MsgIdToStamp(msgId):u} (svc)");
writer.BaseStream.Position = patchPos;
writer.Write((int)(writer.BaseStream.Length - patchPos - 4)); // patch bytes field
writer.Seek(0, SeekOrigin.End);
}
return "as MsgContainer";
msg_id = msg.Item2.msgId,
seqno = msg.Item2.seqno,
body = msg.obj
}).ToArray()
};
private async Task HandleMessageAsync(ITLObject obj)
@ -855,8 +839,8 @@ namespace WTelegram
}
}
break;
case Ping ping:
_ = SendAsync(MakeFunction(new Pong { msg_id = _lastRecvMsgId, ping_id = ping.ping_id }), false);
case MTProto.Ping_ ping:
_ = SendAsync(new Pong { msg_id = _lastRecvMsgId, ping_id = ping.ping_id }, false);
break;
case Pong pong:
SetResult(pong.msg_id, pong);

View file

@ -293,111 +293,130 @@ namespace TL
public AccessPointRule[] rules;
}
[TLDef(0x7ABE77EC)] //ping#7abe77ec ping_id:long = Pong
public partial class Ping : ITLObject
{
public long ping_id;
}
// ---functions---
public static class MTProto
{
//req_pq#60469778 nonce:int128 = ResPQ
[TLDef(0x60469778)] //req_pq#60469778 nonce:int128 = ResPQ
public partial class ReqPq_ : ITLMethod<ResPQ>
{
public Int128 nonce;
}
public static Task<ResPQ> ReqPq(this Client client, Int128 nonce)
=> client.CallBareAsync<ResPQ>(writer =>
=> client.CallBareAsync(new ReqPq_
{
writer.Write(0x60469778);
writer.Write(nonce);
return "ReqPq";
nonce = nonce,
});
//req_pq_multi#be7e8ef1 nonce:int128 = ResPQ
[TLDef(0xBE7E8EF1)] //req_pq_multi#be7e8ef1 nonce:int128 = ResPQ
public partial class ReqPqMulti_ : ITLMethod<ResPQ>
{
public Int128 nonce;
}
public static Task<ResPQ> ReqPqMulti(this Client client, Int128 nonce)
=> client.CallBareAsync<ResPQ>(writer =>
=> client.CallBareAsync(new ReqPqMulti_
{
writer.Write(0xBE7E8EF1);
writer.Write(nonce);
return "ReqPqMulti";
nonce = nonce,
});
//req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params
[TLDef(0xD712E4BE)] //req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params
public partial class ReqDHParams_ : ITLMethod<ServerDHParams>
{
public Int128 nonce;
public Int128 server_nonce;
public byte[] p;
public byte[] q;
public long public_key_fingerprint;
public byte[] encrypted_data;
}
public static Task<ServerDHParams> ReqDHParams(this Client client, Int128 nonce, Int128 server_nonce, byte[] p, byte[] q, long public_key_fingerprint, byte[] encrypted_data)
=> client.CallBareAsync<ServerDHParams>(writer =>
=> client.CallBareAsync(new ReqDHParams_
{
writer.Write(0xD712E4BE);
writer.Write(nonce);
writer.Write(server_nonce);
writer.WriteTLBytes(p);
writer.WriteTLBytes(q);
writer.Write(public_key_fingerprint);
writer.WriteTLBytes(encrypted_data);
return "ReqDHParams";
nonce = nonce,
server_nonce = server_nonce,
p = p,
q = q,
public_key_fingerprint = public_key_fingerprint,
encrypted_data = encrypted_data,
});
//set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer
[TLDef(0xF5045F1F)] //set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:bytes = Set_client_DH_params_answer
public partial class SetClientDHParams_ : ITLMethod<SetClientDHParamsAnswer>
{
public Int128 nonce;
public Int128 server_nonce;
public byte[] encrypted_data;
}
public static Task<SetClientDHParamsAnswer> SetClientDHParams(this Client client, Int128 nonce, Int128 server_nonce, byte[] encrypted_data)
=> client.CallBareAsync<SetClientDHParamsAnswer>(writer =>
=> client.CallBareAsync(new SetClientDHParams_
{
writer.Write(0xF5045F1F);
writer.Write(nonce);
writer.Write(server_nonce);
writer.WriteTLBytes(encrypted_data);
return "SetClientDHParams";
nonce = nonce,
server_nonce = server_nonce,
encrypted_data = encrypted_data,
});
//destroy_auth_key#d1435160 = DestroyAuthKeyRes
[TLDef(0xD1435160)] //destroy_auth_key#d1435160 = DestroyAuthKeyRes
public partial class DestroyAuthKey_ : ITLMethod<DestroyAuthKeyRes> { }
public static Task<DestroyAuthKeyRes> DestroyAuthKey(this Client client)
=> client.CallBareAsync<DestroyAuthKeyRes>(writer =>
=> client.CallBareAsync(new DestroyAuthKey_
{
writer.Write(0xD1435160);
return "DestroyAuthKey";
});
//rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer
[TLDef(0x58E4A740)] //rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer
public partial class RpcDropAnswer_ : ITLMethod<RpcDropAnswer>
{
public long req_msg_id;
}
public static Task<RpcDropAnswer> RpcDropAnswer(this Client client, long req_msg_id)
=> client.CallBareAsync<RpcDropAnswer>(writer =>
=> client.CallBareAsync(new RpcDropAnswer_
{
writer.Write(0x58E4A740);
writer.Write(req_msg_id);
return "RpcDropAnswer";
req_msg_id = req_msg_id,
});
//get_future_salts#b921bd04 num:int = FutureSalts
[TLDef(0xB921BD04)] //get_future_salts#b921bd04 num:int = FutureSalts
public partial class GetFutureSalts_ : ITLMethod<FutureSalts>
{
public int num;
}
public static Task<FutureSalts> GetFutureSalts(this Client client, int num)
=> client.CallAsync<FutureSalts>(writer =>
=> client.CallAsync(new GetFutureSalts_
{
writer.Write(0xB921BD04);
writer.Write(num);
return "GetFutureSalts";
num = num,
});
//ping#7abe77ec ping_id:long = Pong
[TLDef(0x7ABE77EC)] //ping#7abe77ec ping_id:long = Pong
public partial class Ping_ : ITLMethod<Pong>
{
public long ping_id;
}
public static Task<Pong> Ping(this Client client, long ping_id)
=> client.CallAsync<Pong>(writer =>
=> client.CallAsync(new Ping_
{
writer.Write(0x7ABE77EC);
writer.Write(ping_id);
return "Ping";
ping_id = ping_id,
});
//ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong
[TLDef(0xF3427B8C)] //ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong
public partial class PingDelayDisconnect_ : ITLMethod<Pong>
{
public long ping_id;
public int disconnect_delay;
}
public static Task<Pong> PingDelayDisconnect(this Client client, long ping_id, int disconnect_delay)
=> client.CallAsync<Pong>(writer =>
=> client.CallAsync(new PingDelayDisconnect_
{
writer.Write(0xF3427B8C);
writer.Write(ping_id);
writer.Write(disconnect_delay);
return "PingDelayDisconnect";
ping_id = ping_id,
disconnect_delay = disconnect_delay,
});
//destroy_session#e7512126 session_id:long = DestroySessionRes
[TLDef(0xE7512126)] //destroy_session#e7512126 session_id:long = DestroySessionRes
public partial class DestroySession_ : ITLMethod<DestroySessionRes>
{
public long session_id;
}
public static Task<DestroySessionRes> DestroySession(this Client client, long session_id)
=> client.CallBareAsync<DestroySessionRes>(writer =>
=> client.CallBareAsync(new DestroySession_
{
writer.Write(0xE7512126);
writer.Write(session_id);
return "DestroySession";
session_id = session_id,
});
}
}

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,7 @@ namespace TL
[0x3BCBF734] = typeof(DhGenOk),
[0x46DC1FB9] = typeof(DhGenRetry),
[0xA69DAE02] = typeof(DhGenFail),
[0x7ABE77EC] = typeof(Ping),
[0x7ABE77EC] = typeof(MTProto.Ping_),
[0x62D6B459] = typeof(MsgsAck),
[0xA7EFF811] = typeof(BadMsgNotification),
[0xEDAB447B] = typeof(BadServerSalt),

View file

@ -10,7 +10,7 @@ using System.Text;
namespace TL
{
public interface ITLObject { }
public delegate string ITLFunction(BinaryWriter writer);
public interface ITLMethod<ReturnType> : ITLObject { }
public static class Serialization
{
@ -94,6 +94,8 @@ namespace TL
if (type.IsArray)
if (value is byte[] bytes)
writer.WriteTLBytes(bytes);
else if (value is _Message[] messages)
writer.WriteTLMessages(messages);
else
writer.WriteTLVector((Array)value);
else if (value is Int128 int128)
@ -164,6 +166,26 @@ namespace TL
writer.WriteTLValue(array.GetValue(i), elementType);
}
internal static void WriteTLMessages(this BinaryWriter writer, _Message[] messages)
{
writer.Write(messages.Length);
foreach (var msg in messages)
{
writer.Write(msg.msg_id);
writer.Write(msg.seqno);
var patchPos = writer.BaseStream.Position;
writer.Write(0); // patched below
writer.WriteTLObject(msg.body);
if ((msg.seqno & 1) != 0)
WTelegram.Helpers.Log(1, $" Sending → {msg.body.GetType().Name,-40} #{(short)msg.msg_id.GetHashCode():X4}");
else
WTelegram.Helpers.Log(1, $" Sending → {msg.body.GetType().Name,-40}");
writer.BaseStream.Position = patchPos;
writer.Write((int)(writer.BaseStream.Length - patchPos - 4)); // patch bytes field
writer.Seek(0, SeekOrigin.End);
}
}
internal static Array ReadTLVector(this BinaryReader reader, Type type)
{
var elementType = type.GetElementType();