Generator: fetch from Web ; supports Layer in end-to-end messages

This commit is contained in:
Wizou 2021-08-07 03:44:11 +02:00
parent 498aa2c319
commit 8ecf2e1a53
5 changed files with 925 additions and 1028 deletions

View file

@ -2,37 +2,76 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace WTelegram
{
public static class Generator
public class Generator
{
//TODO: generate BinaryReader/Writer serialization directly to avoid using Reflection
//TODO: generate partial class with methods for functions instead of exposing request classes
readonly Dictionary<int, string> ctorToTypes = new();
readonly HashSet<string> allTypes = new();
readonly Dictionary<int, Dictionary<string, TypeInfo>> typeInfosByLayer = new();
Dictionary<string, TypeInfo> typeInfos;
int currentLayer;
string tabIndent;
public static void FromJson(string jsonPath, string outputCs, string tableCs = null)
public async Task FromWeb()
{
Console.WriteLine("Fetch web pages...");
//using var http = new HttpClient();
//var html = await http.GetStringAsync("https://core.telegram.org/api/layers");
var html = await Task.FromResult("#layer-121");
currentLayer = int.Parse(Regex.Match(html, @"#layer-(\d+)").Groups[1].Value);
//await File.WriteAllBytesAsync("TL.MTProto.json", await http.GetByteArrayAsync("https://core.telegram.org/schema/mtproto-json"));
//await File.WriteAllBytesAsync("TL.Schema.json", await http.GetByteArrayAsync("https://core.telegram.org/schema/json"));
//await File.WriteAllBytesAsync("TL.Secret.json", await http.GetByteArrayAsync("https://core.telegram.org/schema/end-to-end-json"));
FromJson("TL.MTProto.json", "TL.MTProto.cs", @"TL.Table.cs");
FromJson("TL.Schema.json", "TL.Schema.cs", @"TL.Table.cs");
FromJson("TL.Secret.json", "TL.Secret.cs", @"TL.Table.cs");
}
public void FromJson(string jsonPath, string outputCs, string tableCs = null)
{
Console.WriteLine("Parsing " + jsonPath);
var schema = JsonSerializer.Deserialize<SchemaJson>(File.ReadAllText(jsonPath));
using var sw = File.CreateText(outputCs);
sw.WriteLine("using System;");
sw.WriteLine();
sw.WriteLine("namespace TL");
sw.Write("{");
string tabIndent = "\t";
Dictionary<string, TypeInfo> typeInfos = new();
foreach (var ctor in schema.constructors)
tabIndent = "\t";
var layers = schema.constructors.GroupBy(c => c.layer).OrderBy(g => g.Key);
foreach (var layer in layers)
{
typeInfos = typeInfosByLayer.GetOrCreate(layer.Key);
if (layer.Key != 0)
{
sw.WriteLine();
sw.WriteLine("\tnamespace Layer" + layer.Key);
sw.Write("\t{");
tabIndent += "\t";
}
string layerPrefix = layer.Key == 0 ? "" : $"Layer{layer.Key}.";
foreach (var ctor in layer)
{
if (ctorToTypes.ContainsKey(ctor.ID)) continue;
if (ctor.type is "Bool" or "Vector t") continue;
var structName = CSharpName(ctor.predicate);
ctorToTypes[ctor.ID] = layerPrefix + structName;
var typeInfo = typeInfos.GetOrCreate(ctor.type);
if (ctor.ID == 0x5BB8E511) { ctorToTypes[ctor.ID] = structName = ctor.predicate = ctor.type = "_Message"; }
if (typeInfo.ReturnName == null) typeInfo.ReturnName = CSharpName(ctor.type);
typeInfo.Structs.Add(ctor);
if (structName == typeInfo.ReturnName) typeInfo.SameName = ctor;
}
typeInfos.Remove("Bool");
typeInfos.Remove("Vector t");
foreach (var (name, typeInfo) in typeInfos)
{
if (allTypes.Contains(typeInfo.ReturnName)) { typeInfo.NeedAbstract = -2; continue; }
if (typeInfo.SameName == null)
{
typeInfo.NeedAbstract = -1;
@ -67,44 +106,62 @@ namespace WTelegram
}
}
foreach (var typeInfo in typeInfos.Values)
WriteTypeInfo(sw, typeInfo);
WriteTypeInfo(sw, typeInfo, jsonPath, layerPrefix, false);
if (layer.Key != 0)
{
sw.WriteLine("\t}");
tabIndent = tabIndent[1..];
}
}
if (typeInfosByLayer[0]["Message"].SameName.ID == 0x5BB8E511) typeInfosByLayer[0].Remove("Message");
sw.WriteLine();
var methods = new List<TypeInfo>();
if (schema.methods.Length != 0)
{
typeInfos = typeInfosByLayer[0];
sw.WriteLine("\tpublic static partial class Fn // ---functions---");
sw.Write("\t{");
tabIndent = "\t\t";
var methods = new List<TypeInfo>();
foreach (var method in schema.methods)
{
var typeInfo = new TypeInfo { ReturnName = method.type };
typeInfo.Structs.Add(new Constructor { id = method.id, @params = method.@params, predicate = method.method, type = method.type });
methods.Add(typeInfo);
WriteTypeInfo(sw, typeInfo, true);
WriteTypeInfo(sw, typeInfo, jsonPath, "", true);
}
sw.WriteLine("\t}");
}
sw.WriteLine("}");
if (tableCs != null) UpdateTable();
if (tableCs != null) UpdateTable(jsonPath, tableCs, methods);
}
void WriteTypeInfo(StreamWriter sw, TypeInfo typeInfo, bool isMethod = false)
void WriteTypeInfo(StreamWriter sw, TypeInfo typeInfo, string definedIn, string layerPrefix, bool isMethod)
{
var parentClass = typeInfo.NeedAbstract != 0 ? typeInfo.ReturnName : "ITLObject";
var genericType = typeInfo.ReturnName.Length == 1 ? $"<{typeInfo.ReturnName}>" : null;
if (isMethod)
parentClass = $"ITLFunction<{MapType(typeInfo.ReturnName, "")}>";
if (isMethod) parentClass = $"ITLFunction<{MapType(typeInfo.ReturnName, "")}>";
bool needNewLine = true;
if (typeInfo.NeedAbstract == -1 && allTypes.Add(layerPrefix + parentClass))
{
needNewLine = false;
sw.WriteLine();
if (typeInfo.NeedAbstract == -1)
sw.WriteLine($"{tabIndent}public abstract class {parentClass} : ITLObject {{ }}");
}
int skipParams = 0;
foreach (var ctor in typeInfo.Structs)
{
string className = CSharpName(ctor.predicate) + genericType;
//if (typeInfo.ReturnName == "SendMessageAction") System.Diagnostics.Debugger.Break();
if (layerPrefix != "" && className == parentClass) { className += "_"; ctorToTypes[ctor.ID] = layerPrefix + className; }
if (!allTypes.Add(layerPrefix + className)) continue;
if (needNewLine) { needNewLine = false; sw.WriteLine(); }
if (ctor.id == null)
sw.Write($"{tabIndent}public abstract class {className} : ITLObject");
else
{
int ctorId = int.Parse(ctor.id);
sw.Write($"{tabIndent}[TLDef(0x{ctorId:X8})] //{ctor.predicate}#{ctorId:x8} ");
sw.Write($"{tabIndent}[TLDef(0x{ctor.ID:X8})] //{ctor.predicate}#{ctor.ID:x8} ");
if (genericType != null) sw.Write($"{{{typeInfo.ReturnName}:Type}} ");
foreach (var parm in ctor.@params) sw.Write($"{parm.name}:{parm.type} ");
sw.WriteLine($"= {ctor.type}");
@ -225,38 +282,36 @@ namespace WTelegram
}
}
void UpdateTable()
void UpdateTable(string jsonPath, string tableCs, List<TypeInfo> methods)
{
var myTag = $"\t\t\t// from {Path.GetFileNameWithoutExtension(jsonPath)}:";
var seen_ids = new HashSet<int>();
using (var sr = new StreamReader(tableCs))
using (var sw = new StreamWriter(tableCs + ".new"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (currentLayer != 0 && line.StartsWith("\t\tpublic const int Layer"))
sw.WriteLine($"\t\tpublic const int Layer = {currentLayer};\t\t\t\t// fetched {DateTime.UtcNow}");
else
sw.WriteLine(line);
if (line == myTag)
{
foreach (var typeInfo in typeInfos.Values)
foreach (var ctor in typeInfo.Structs)
if (ctor.id != null)
sw.WriteLine($"\t\t\t[0x{int.Parse(ctor.id):X8}] = typeof({CSharpName(ctor.predicate)}),");
foreach (var typeInfo in methods)
foreach (var ctor in typeInfo.Structs)
{
var generic = typeInfo.ReturnName.Length == 1 ? "<>" : "";
sw.WriteLine($"\t\t\t[0x{int.Parse(ctor.id):X8}] = typeof(Fn.{CSharpName(ctor.predicate)}{generic}),");
}
foreach (var ctor in ctorToTypes)
if (seen_ids.Add(ctor.Key))
sw.WriteLine($"\t\t\t[0x{ctor.Key:X8}] = typeof({ctor.Value}),");
while ((line = sr.ReadLine()) != null)
if (line.StartsWith("\t\t\t// "))
break;
sw.WriteLine(line);
}
else if (line.StartsWith("\t\t\t[0x"))
seen_ids.Add(int.Parse(line[6..14], System.Globalization.NumberStyles.HexNumber));
}
}
File.Replace(tableCs + ".new", tableCs, null);
}
}
static readonly HashSet<string> ctorNeedClone = new() { /*"User"*/ };
@ -301,6 +356,9 @@ namespace WTelegram
public string predicate { get; set; }
public Param[] @params { get; set; }
public string type { get; set; }
public int layer { get; set; }
public int ID => int.Parse(id);
}
public class Param

View file

@ -113,7 +113,7 @@ namespace TL
public int bytes;
}
[TLDef(0x949D9DC)] //future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt
[TLDef(0x0949D9DC)] //future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt
public class FutureSalt : ITLObject
{
public DateTime valid_since;

View file

@ -1,6 +1,8 @@
using System;
namespace TL
{
namespace Layer8
{
public abstract class DecryptedMessageBase : ITLObject { }
[TLDef(0x1F814F1F)] //decryptedMessage#1f814f1f random_id:long random_bytes:bytes message:string media:DecryptedMessageMedia = DecryptedMessage
@ -81,30 +83,6 @@ namespace TL
public byte[] key;
public byte[] iv;
}
[TLDef(0xFA95B0DD)] //decryptedMessageMediaExternalDocument#fa95b0dd id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = DecryptedMessageMedia
public class DecryptedMessageMediaExternalDocument : DecryptedMessageMedia
{
public long id;
public long access_hash;
public DateTime date;
public string mime_type;
public int size;
public PhotoSizeBase thumb;
public int dc_id;
public DocumentAttribute[] attributes;
}
[TLDef(0x8A0DF56F)] //decryptedMessageMediaVenue#8a0df56f lat:double long:double title:string address:string provider:string venue_id:string = DecryptedMessageMedia
public class DecryptedMessageMediaVenue : DecryptedMessageMedia
{
public double lat;
public double long_;
public string title;
public string address;
public string provider;
public string venue_id;
}
[TLDef(0xE50511D8)] //decryptedMessageMediaWebPage#e50511d8 url:string = DecryptedMessageMedia
public class DecryptedMessageMediaWebPage : DecryptedMessageMedia { public string url; }
public abstract class DecryptedMessageAction : ITLObject { }
[TLDef(0xA1733AEC)] //decryptedMessageActionSetMessageTTL#a1733aec ttl_seconds:int = DecryptedMessageAction
@ -117,6 +95,71 @@ namespace TL
public class DecryptedMessageActionScreenshotMessages : DecryptedMessageAction { public long[] random_ids; }
[TLDef(0x6719E45C)] //decryptedMessageActionFlushHistory#6719e45c = DecryptedMessageAction
public class DecryptedMessageActionFlushHistory : DecryptedMessageAction { }
}
namespace Layer17
{
public abstract class DecryptedMessageBase : ITLObject { }
[TLDef(0x204D3878)] //decryptedMessage#204d3878 random_id:long ttl:int message:string media:DecryptedMessageMedia = DecryptedMessage
public class DecryptedMessage : DecryptedMessageBase
{
public long random_id;
public int ttl;
public string message;
public DecryptedMessageMedia media;
}
[TLDef(0x73164160)] //decryptedMessageService#73164160 random_id:long action:DecryptedMessageAction = DecryptedMessage
public class DecryptedMessageService : DecryptedMessageBase
{
public long random_id;
public DecryptedMessageAction action;
}
public abstract class DecryptedMessageMedia : ITLObject { }
[TLDef(0x524A415D)] //decryptedMessageMediaVideo#524a415d thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes = DecryptedMessageMedia
public class DecryptedMessageMediaVideo : DecryptedMessageMedia
{
public byte[] thumb;
public int thumb_w;
public int thumb_h;
public int duration;
public string mime_type;
public int w;
public int h;
public int size;
public byte[] key;
public byte[] iv;
}
[TLDef(0x57E0A9CB)] //decryptedMessageMediaAudio#57e0a9cb duration:int mime_type:string size:int key:bytes iv:bytes = DecryptedMessageMedia
public class DecryptedMessageMediaAudio : DecryptedMessageMedia
{
public int duration;
public string mime_type;
public int size;
public byte[] key;
public byte[] iv;
}
[TLDef(0x1BE31789)] //decryptedMessageLayer#1be31789 random_bytes:bytes layer:int in_seq_no:int out_seq_no:int message:DecryptedMessage = DecryptedMessageLayer
public class DecryptedMessageLayer : ITLObject
{
public byte[] random_bytes;
public int layer;
public int in_seq_no;
public int out_seq_no;
public DecryptedMessageBase message;
}
[TLDef(0x92042FF7)] //sendMessageUploadVideoAction#92042ff7 = SendMessageAction
public class SendMessageUploadVideoAction : SendMessageAction { }
[TLDef(0xE6AC8A6F)] //sendMessageUploadAudioAction#e6ac8a6f = SendMessageAction
public class SendMessageUploadAudioAction : SendMessageAction { }
[TLDef(0x990A3C1A)] //sendMessageUploadPhotoAction#990a3c1a = SendMessageAction
public class SendMessageUploadPhotoAction : SendMessageAction { }
[TLDef(0x8FAEE98E)] //sendMessageUploadDocumentAction#8faee98e = SendMessageAction
public class SendMessageUploadDocumentAction : SendMessageAction { }
public abstract class DecryptedMessageAction : ITLObject { }
[TLDef(0x511110B0)] //decryptedMessageActionResend#511110b0 start_seq_no:int end_seq_no:int = DecryptedMessageAction
public class DecryptedMessageActionResend : DecryptedMessageAction
{
@ -127,6 +170,11 @@ namespace TL
public class DecryptedMessageActionNotifyLayer : DecryptedMessageAction { public int layer; }
[TLDef(0xCCB27641)] //decryptedMessageActionTyping#ccb27641 action:SendMessageAction = DecryptedMessageAction
public class DecryptedMessageActionTyping : DecryptedMessageAction { public SendMessageAction action; }
}
namespace Layer20
{
public abstract class DecryptedMessageAction : ITLObject { }
[TLDef(0xF3C9611B)] //decryptedMessageActionRequestKey#f3c9611b exchange_id:long g_a:bytes = DecryptedMessageAction
public class DecryptedMessageActionRequestKey : DecryptedMessageAction
{
@ -150,17 +198,22 @@ namespace TL
}
[TLDef(0xA82FDD63)] //decryptedMessageActionNoop#a82fdd63 = DecryptedMessageAction
public class DecryptedMessageActionNoop : DecryptedMessageAction { }
[TLDef(0x1BE31789)] //decryptedMessageLayer#1be31789 random_bytes:bytes layer:int in_seq_no:int out_seq_no:int message:DecryptedMessage = DecryptedMessageLayer
public class DecryptedMessageLayer : ITLObject
{
public byte[] random_bytes;
public int layer;
public int in_seq_no;
public int out_seq_no;
public DecryptedMessageBase message;
}
namespace Layer23
{
[TLDef(0xFB0A5727)] //documentAttributeSticker#fb0a5727 = DocumentAttribute
public class DocumentAttributeSticker : DocumentAttribute { }
[TLDef(0x5910CCCB)] //documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute
public class DocumentAttributeVideo : DocumentAttribute
{
public int duration;
public int w;
public int h;
}
[TLDef(0x051448E5)] //documentAttributeAudio#051448e5 duration:int = DocumentAttribute
public class DocumentAttributeAudio : DocumentAttribute { public int duration; }
[TLDef(0x7C596B46)] //fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation
public class FileLocationUnavailable : FileLocation
{
@ -177,6 +230,133 @@ namespace TL
public long secret;
}
public static partial class Fn // ---functions---
{ }
public abstract class DecryptedMessageMedia : ITLObject { }
[TLDef(0xFA95B0DD)] //decryptedMessageMediaExternalDocument#fa95b0dd id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = DecryptedMessageMedia
public class DecryptedMessageMediaExternalDocument : DecryptedMessageMedia
{
public long id;
public long access_hash;
public DateTime date;
public string mime_type;
public int size;
public PhotoSize thumb;
public int dc_id;
public DocumentAttribute[] attributes;
}
}
namespace Layer45
{
[TLDef(0x36B091DE)] //decryptedMessage#36b091de flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long = DecryptedMessage
public class DecryptedMessage : ITLObject
{
[Flags] public enum Flags { has_reply_to_random_id = 0x8, has_entities = 0x80, has_media = 0x200, has_via_bot_name = 0x800 }
public Flags flags;
public long random_id;
public int ttl;
public string message;
[IfFlag(9)] public DecryptedMessageMedia media;
[IfFlag(7)] public MessageEntity[] entities;
[IfFlag(11)] public string via_bot_name;
[IfFlag(3)] public long reply_to_random_id;
}
public abstract class DecryptedMessageMedia : ITLObject { }
[TLDef(0xF1FA8D78)] //decryptedMessageMediaPhoto#f1fa8d78 thumb:bytes thumb_w:int thumb_h:int w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia
public class DecryptedMessageMediaPhoto : DecryptedMessageMedia
{
public byte[] thumb;
public int thumb_w;
public int thumb_h;
public int w;
public int h;
public int size;
public byte[] key;
public byte[] iv;
public string caption;
}
[TLDef(0x970C8C0E)] //decryptedMessageMediaVideo#970c8c0e thumb:bytes thumb_w:int thumb_h:int duration:int mime_type:string w:int h:int size:int key:bytes iv:bytes caption:string = DecryptedMessageMedia
public class DecryptedMessageMediaVideo : DecryptedMessageMedia
{
public byte[] thumb;
public int thumb_w;
public int thumb_h;
public int duration;
public string mime_type;
public int w;
public int h;
public int size;
public byte[] key;
public byte[] iv;
public string caption;
}
[TLDef(0x7AFE8AE2)] //decryptedMessageMediaDocument#7afe8ae2 thumb:bytes thumb_w:int thumb_h:int mime_type:string size:int key:bytes iv:bytes attributes:Vector<DocumentAttribute> caption:string = DecryptedMessageMedia
public class DecryptedMessageMediaDocument : DecryptedMessageMedia
{
public byte[] thumb;
public int thumb_w;
public int thumb_h;
public string mime_type;
public int size;
public byte[] key;
public byte[] iv;
public DocumentAttribute[] attributes;
public string caption;
}
[TLDef(0x8A0DF56F)] //decryptedMessageMediaVenue#8a0df56f lat:double long:double title:string address:string provider:string venue_id:string = DecryptedMessageMedia
public class DecryptedMessageMediaVenue : DecryptedMessageMedia
{
public double lat;
public double long_;
public string title;
public string address;
public string provider;
public string venue_id;
}
[TLDef(0xE50511D8)] //decryptedMessageMediaWebPage#e50511d8 url:string = DecryptedMessageMedia
public class DecryptedMessageMediaWebPage : DecryptedMessageMedia { public string url; }
[TLDef(0x3A556302)] //documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute
public class DocumentAttributeSticker : DocumentAttribute
{
public string alt;
public InputStickerSet stickerset;
}
[TLDef(0xDED218E0)] //documentAttributeAudio#ded218e0 duration:int title:string performer:string = DocumentAttribute
public class DocumentAttributeAudio : DocumentAttribute
{
public int duration;
public string title;
public string performer;
}
}
namespace Layer46
{ }
namespace Layer66
{
[TLDef(0xBB718624)] //sendMessageUploadRoundAction#bb718624 = SendMessageAction
public class SendMessageUploadRoundAction : SendMessageAction { }
}
namespace Layer73
{
[TLDef(0x91CC4674)] //decryptedMessage#91cc4674 flags:# random_id:long ttl:int message:string media:flags.9?DecryptedMessageMedia entities:flags.7?Vector<MessageEntity> via_bot_name:flags.11?string reply_to_random_id:flags.3?long grouped_id:flags.17?long = DecryptedMessage
public class DecryptedMessage : ITLObject
{
[Flags] public enum Flags { has_reply_to_random_id = 0x8, has_entities = 0x80, has_media = 0x200, has_via_bot_name = 0x800,
has_grouped_id = 0x20000 }
public Flags flags;
public long random_id;
public int ttl;
public string message;
[IfFlag(9)] public Layer45.DecryptedMessageMedia media;
[IfFlag(7)] public MessageEntity[] entities;
[IfFlag(11)] public string via_bot_name;
[IfFlag(3)] public long reply_to_random_id;
[IfFlag(17)] public long grouped_id;
}
}
}

File diff suppressed because it is too large Load diff

3
TL.cs
View file

@ -15,9 +15,6 @@ namespace TL
public static partial class Schema
{
public const int Layer = 121;
public const int VectorCtor = 0x1CB5C415;
internal static byte[] Serialize(ITLObject msg)
{
using var memStream = new MemoryStream(1024);