123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981 |
- const fs = require('fs');
- const util = require('util');
- const {crc32} = require('crc');
- const SourceBuilder = require('../sourcebuilder');
- const {snakeToCamelCase, variableSnakeToCamelCase} = require("../utils");
- const AUTO_GEN_NOTICE =
- "/*! File generated by TLObjects' generator. All changes will be ERASED !*/";
- const AUTO_CASTS = {
- InputPeer: 'utils.get_input_peer(await client.get_input_entity(%s))',
- InputChannel: 'utils.get_input_channel(await client.get_input_entity(%s))',
- InputUser: 'utils.get_input_user(await client.get_input_entity(%s))',
- InputDialogPeer: 'await client._get_input_dialog(%s)',
- InputNotifyPeer: 'await client._get_input_notify(%s)',
- InputMedia: 'utils.get_input_media(%s)',
- InputPhoto: 'utils.get_input_photo(%s)',
- InputMessage: 'utils.get_input_message(%s)',
- InputDocument: 'utils.get_input_document(%s)',
- InputChatPhoto: 'utils.get_input_chat_photo(%s)',
- };
- const NAMED_AUTO_CASTS = {
- 'chat_id,int': 'await client.get_peer_id(%s, add_mark=False)',
- };
- // Secret chats have a chat_id which may be negative.
- // With the named auto-cast above, we would break it.
- // However there are plenty of other legit requests
- // with `chat_id:int` where it is useful.
- //
- // NOTE: This works because the auto-cast is not recursive.
- // There are plenty of types that would break if we
- // did recurse into them to resolve them.
- const NAMED_BLACKLIST = new Set(['messages.discardEncryption']);
- const BASE_TYPES = [
- 'string',
- 'bytes',
- 'int',
- 'long',
- 'int128',
- 'int256',
- 'double',
- 'Bool',
- 'true',
- ];
- // Patched types {fullname: custom.ns.Name}
- //No patches currently
- /**
- const PATCHED_TYPES = {
- messageEmpty: 'message.Message',
- message: 'message.Message',
- messageService: 'message.Message',
- };*/
- const PATCHED_TYPES = {};
- const writeModules = (
- outDir,
- depth,
- kind,
- namespaceTlobjects,
- typeConstructors
- ) => {
- // namespace_tlobjects: {'namespace', [TLObject]}
- fs.mkdirSync(outDir, {recursive: true});
- for (const [ns, tlobjects] of Object.entries(namespaceTlobjects)) {
- const file = `${outDir}/${ns === 'null' ? 'index' : ns}.js`;
- const stream = fs.createWriteStream(file);
- const builder = new SourceBuilder(stream);
- const dotDepth = '.'.repeat(depth || 1);
- builder.writeln(AUTO_GEN_NOTICE);
- builder.writeln(
- `const { TLObject } = require('${dotDepth}/tlobject');`
- );
- if (kind !== 'TLObject') {
- builder.writeln(
- `const { ${kind} } = require('${dotDepth}/tlobject');`
- );
- }
- // Add the relative imports to the namespaces,
- // unless we already are in a namespace.
- if (!ns) {
- const imports = Object.keys(namespaceTlobjects)
- .filter(Boolean)
- .join(`, `);
- builder.writeln(`const { ${imports} } = require('.');`);
- }
- // Import struct for the .__bytes__(self) serialization
- builder.writeln("const struct = require('python-struct');");
- builder.writeln(`const Helpers = require('../../utils/Helpers');`);
- const typeNames = new Set();
- const typeDefs = [];
- /*
- // Find all the types in this file and generate type definitions
- // based on the types. The type definitions are written to the
- // file at the end.
- for (const t of tlobjects) {
- if (!t.isFunction) {
- let typeName = t.result;
- if (typeName.includes('.')) {
- typeName = typeName.slice(typeName.lastIndexOf('.'));
- }
- if (typeNames.has(typeName)) {
- continue;
- }
- typeNames.add(typeName);
- const constructors = typeConstructors[typeName];
- if (!constructors) {
- } else if (constructors.length === 1) {
- typeDefs.push(
- `Type${typeName} = ${constructors[0].className}`
- );
- } else {
- typeDefs.push(
- `Type${typeName} = Union[${constructors
- .map(x => constructors.className)
- .join(',')}]`
- );
- }
- }
- }*/
- const imports = {};
- const primitives = new Set([
- 'int',
- 'long',
- 'int128',
- 'int256',
- 'double',
- 'string',
- 'bytes',
- 'Bool',
- 'true',
- ]);
- // Find all the types in other files that are used in this file
- // and generate the information required to import those types.
- for (const t of tlobjects) {
- for (const arg of t.args) {
- let name = arg.type;
- if (!name || primitives.has(name)) {
- continue;
- }
- let importSpace = `${dotDepth}/tl/types`;
- if (name.includes('.')) {
- const [namespace] = name.split('.');
- name = name.split('.');
- importSpace += `/${namespace}`;
- }
- if (!typeNames.has(name)) {
- typeNames.add(name);
- if (name === 'date') {
- imports.datetime = ['datetime'];
- continue;
- } else if (!(importSpace in imports)) {
- imports[importSpace] = new Set();
- }
- imports[importSpace].add(`Type${name}`);
- }
- }
- }
- // Add imports required for type checking
- /**
- if (imports) {
- builder.writeln('if (false) { // TYPE_CHECKING {');
- for (const [namespace, names] of Object.entries(imports)) {
- builder.writeln(
- `const { ${[...names.values()].join(
- ', '
- )} } = require('${namespace}');`
- );
- }
- builder.endBlock();
- }*/
- // Generate the class for every TLObject
- for (const t of tlobjects) {
- if (t.fullname in PATCHED_TYPES) {
- builder.writeln(`const ${t.className} = null; // Patched`);
- } else {
- writeSourceCode(t, kind, builder, typeConstructors);
- builder.currentIndent = 0;
- }
- }
- // Write the type definitions generated earlier.
- builder.writeln();
- for (const line of typeDefs) {
- builder.writeln(line);
- }
- writeModuleExports(tlobjects, builder);
- if (file.indexOf("index.js") > 0) {
- for (const [ns, tlobjects] of Object.entries(namespaceTlobjects)) {
- if (ns !== 'null') {
- builder.writeln("let %s = require('./%s');", ns, ns);
- }
- }
- for (const [ns, tlobjects] of Object.entries(namespaceTlobjects)) {
- if (ns !== 'null') {
- builder.writeln("module.exports.%s = %s;", ns, ns);
- }
- }
- }
- }
- };
- const writeReadResult = (tlobject, builder) => {
- // Only requests can have a different response that's not their
- // serialized body, that is, we'll be setting their .result.
- //
- // The default behaviour is reading a TLObject too, so no need
- // to override it unless necessary.
- if (!tlobject.isFunction)
- return;
- // https://core.telegram.org/mtproto/serialize#boxed-and-bare-types
- // TL;DR; boxed types start with uppercase always, so we can use
- // this to check whether everything in it is boxed or not.
- //
- // Currently only un-boxed responses are Vector<int>/Vector<long>.
- // If this weren't the case, we should check upper case after
- // max(index('<'), index('.')) (and if it is, it's boxed, so return).
- let m = tlobject.result.match(/Vector<(int|long)>/);
- if (!m) {
- return
- }
- //builder.endBlock();
- builder.writeln('readResult(reader){');
- builder.writeln('reader.readInt(); // Vector ID');
- builder.writeln('let temp = [];');
- builder.writeln("let len = reader.readInt(); //fix this");
- builder.writeln('for (let i=0;i<len;i++){');
- let read = m[1][0].toUpperCase() + m[1].slice(1);
- builder.writeln('temp.push(reader.read%s())', read);
- builder.endBlock();
- builder.writeln('return temp');
- builder.endBlock();
- };
- /**
- * Writes the source code corresponding to the given TLObject
- * by making use of the ``builder`` `SourceBuilder`.
- *
- * Additional information such as file path depth and
- * the ``Type: [Constructors]`` must be given for proper
- * importing and documentation strings.
- */
- const writeSourceCode = (tlobject, kind, builder, typeConstructors) => {
- writeClassConstructor(tlobject, kind, typeConstructors, builder);
- writeResolve(tlobject, builder);
- //writeToJson(tlobject, builder);
- writeToBytes(tlobject, builder);
- builder.currentIndent--;
- writeFromReader(tlobject, builder);
- writeReadResult(tlobject, builder);
- builder.currentIndent--;
- builder.writeln('}');
- };
- const writeClassConstructor = (tlobject, kind, typeConstructors, builder) => {
- builder.writeln();
- builder.writeln();
- builder.writeln(`class ${tlobject.className} extends ${kind} {`);
- // Convert the args to string parameters, flags having =None
- const args = tlobject.realArgs.map(
- a =>
- `${a.name}: ${a.typeHint()}${
- a.isFlag || a.canBeInferred ? `=None` : ''
- }`
- );
- // Write the __init__ function if it has any argument
- if (!tlobject.realArgs.length) {
- return;
- }
- // Note : this is needed to be able to access them
- // with or without an instance
- builder.writeln(
- `static CONSTRUCTOR_ID = 0x${tlobject.id.toString(16).padStart(8, '0')};`
- );
- builder.writeln(`static SUBCLASS_OF_ID = 0x${crc32(tlobject.result).toString("16")};`);
- builder.writeln();
- builder.writeln('/**');
- if (tlobject.isFunction) {
- builder.write(`:returns ${tlobject.result}: `);
- } else {
- builder.write(`Constructor for ${tlobject.result}: `);
- }
- const constructors = typeConstructors[tlobject.result];
- if (!constructors) {
- builder.writeln('This type has no constructors.');
- } else if (constructors.length === 1) {
- builder.writeln(`Instance of ${constructors[0].className}`);
- } else {
- builder.writeln(
- `Instance of either ${constructors
- .map(c => c.className)
- .join(', ')}`
- );
- }
- builder.writeln('*/');
- builder.writeln(`constructor(args) {`);
- builder.writeln(`super();`);
- // Class-level variable to store its Telegram's constructor ID
- builder.writeln(
- `this.CONSTRUCTOR_ID = 0x${tlobject.id.toString(16).padStart(8, '0')};`
- );
- builder.writeln(`this.SUBCLASS_OF_ID = 0x${crc32(tlobject.result).toString("16")};`);
- builder.writeln();
- // Set the arguments
- for (const arg of tlobject.realArgs) {
- if (!arg.canBeInferred) {
- builder.writeln(`this.${variableSnakeToCamelCase(arg.name)} = args.${variableSnakeToCamelCase(arg.name)};`);
- }
- // Currently the only argument that can be
- // inferred are those called 'random_id'
- else if (arg.name === 'random_id') {
- // Endianness doesn't really matter, and 'big' is shorter
- let code = `Helpers.readBigIntFromBuffer(Helpers.generateRandomBytes(${
- arg.type === 'long' ? 8 : 4
- }),false,true)`;
- if (arg.isVector) {
- // Currently for the case of "messages.forwardMessages"
- // Ensure we can infer the length from id:Vector<>
- if (!tlobject.realArgs.find(a => a.name === 'id').isVector) {
- throw new Error(
- `Cannot infer list of random ids for ${tlobject}`
- );
- }
- code = `new Array(id.length).fill().map(_ => ${code})`;
- }
- builder.writeln(
- `this.randomId = randomId !== null ? randomId : ${code}`
- );
- } else {
- throw new Error(`Cannot infer a value for ${arg}`);
- }
- }
- builder.endBlock();
- };
- const writeResolve = (tlobject, builder) => {
- if (
- tlobject.isFunction &&
- tlobject.realArgs.some(
- arg =>
- arg.type in AUTO_CASTS ||
- (`${arg.name},${arg.type}` in NAMED_AUTO_CASTS &&
- !NAMED_BLACKLIST.has(tlobject.fullname))
- )
- ) {
- builder.writeln('async resolve(client, utils) {');
- for (const arg of tlobject.realArgs) {
- let ac = AUTO_CASTS[arg.type];
- if (!ac) {
- ac = NAMED_AUTO_CASTS[`${arg.name},${arg.type}`];
- if (!ac) {
- continue;
- }
- }
- if (arg.isFlag) {
- builder.writeln(`if (this.${arg.name}) {`);
- }
- if (arg.isVector) {
- builder.write(`const _tmp = [];`);
- builder.writeln(`for (const _x of this.${arg.name}) {`);
- builder.writeln(`_tmp.push(%s);`, util.format(ac, '_x'));
- builder.endBlock();
- builder.writeln(`this.${arg.name} = _tmp;`);
- } else {
- builder.writeln(
- `this.${arg.name} = %s`,
- util.format(ac, `this.${arg.name}`)
- );
- }
- if (arg.isFlag) {
- builder.currentIndent--;
- builder.writeln('}');
- }
- }
- builder.endBlock();
- }
- };
- /**
- const writeToJson = (tlobject, builder) => {
- builder.writeln('toJson() {');
- builder.writeln('return {');
- builder.write("_: '%s'", tlobject.className);
- for (const arg of tlobject.realArgs) {
- builder.writeln(',');
- builder.write('%s: ', arg.name);
- if (BASE_TYPES.includes(arg.type)) {
- if (arg.isVector) {
- builder.write(
- 'this.%s === null ? [] : this.%s.slice()',
- arg.name,
- arg.name
- );
- } else {
- builder.write('this.%s', arg.name);
- }
- } else {
- if (arg.isVector) {
- builder.write(
- 'this.%s === null ? [] : this.%s.map(x => x instanceof TLObject ? x.toJson() : x)',
- arg.name,
- arg.name
- );
- } else {
- builder.write(
- 'this.%s instanceof TLObject ? this.%s.toJson() : this.%s',
- arg.name,
- arg.name,
- arg.name
- );
- }
- }
- }
- builder.writeln();
- builder.endBlock();
- builder.currentIndent--;
- builder.writeln('}');
- };
- */
- const writeToBytes = (tlobject, builder) => {
- builder.writeln('get bytes() {');
- // Some objects require more than one flag parameter to be set
- // at the same time. In this case, add an assertion.
- const repeatedArgs = {};
- for (let arg of tlobject.args) {
- if (arg.isFlag) {
- if (!repeatedArgs[arg.flagIndex]) {
- repeatedArgs[arg.flagIndex] = [];
- }
- repeatedArgs[arg.flagIndex].push(arg);
- }
- }
- for (let ra of Object.values(repeatedArgs)) {
- if (ra.length > 1) {
- let cnd1 = [];
- let cnd2 = [];
- let names = [];
- for (let a of ra) {
- cnd1.push(`this.${a.name} || this.${a.name}!==null`);
- cnd2.push(`this.${a.name}===null || this.${a.name}===false`);
- names.push(a.name);
- }
- builder.writeln("if (!((%s) && (%s)))\n\t throw new Error('%s paramaters must all"
- + " be false-y or all true')", cnd1.join(" && "), cnd2.join(" && "), names.join(", "));
- }
- }
- builder.writeln("return Buffer.concat([");
- builder.currentIndent++;
- let b = Buffer.alloc(4);
- b.writeUInt32LE(tlobject.id, 0);
- // First constructor code, we already know its bytes
- builder.writeln('Buffer.from("%s","hex"),', b.toString("hex"));
- for (let arg of tlobject.args) {
- if (writeArgToBytes(builder, arg, tlobject.args)) {
- builder.writeln(',');
- }
- }
- builder.writeln("])");
- builder.endBlock();
- };
- // writeFromReader
- const writeFromReader = (tlobject, builder) => {
- builder.writeln("static fromReader(reader) {");
- for (const arg of tlobject.args) {
- if (arg.name !== "flag") {
- if (arg.name !== "x") {
- builder.writeln("let %s", "_" + arg.name + ";");
- }
- }
- }
- // TODO fix this really
- builder.writeln("let _x;");
- builder.writeln("let len;");
- for (const arg of tlobject.args) {
- writeArgReadCode(builder, arg, tlobject.args, "_" + arg.name);
- }
- let temp = [];
- for (let a of tlobject.realArgs) {
- temp.push(`${variableSnakeToCamelCase(a.name)}:_${a.name}`)
- }
- builder.writeln("return new this({%s})", temp.join(",\n\t"));
- builder.endBlock();
- };
- // writeReadResult
- /**
- * Writes the .__bytes__() code for the given argument
- * @param builder: The source code builder
- * @param arg: The argument to write
- * @param args: All the other arguments in TLObject same __bytes__.
- * This is required to determine the flags value
- * @param name: The name of the argument. Defaults to "self.argname"
- * This argument is an option because it's required when
- * writing Vectors<>
- */
- const writeArgToBytes = (builder, arg, args, name = null) => {
- if (arg.genericDefinition) {
- return; // Do nothing, this only specifies a later type
- }
- if (name === null) {
- name = `this.${arg.name}`;
- }
- if (name =="this.msg_ids"){
- console.log(name)
- }
- name = variableSnakeToCamelCase(name);
- // The argument may be a flag, only write if it's not None AND
- // if it's not a True type.
- // True types are not actually sent, but instead only used to
- // determine the flags.
- if (arg.isFlag) {
- if (arg.type === 'true') {
- return; // Exit, since true type is never written
- } else if (arg.isVector) {
- // Vector flags are special since they consist of 3 values,
- // so we need an extra join here. Note that empty vector flags
- // should NOT be sent either!
- builder.write(
- "%s === null || %s === false ? Buffer.alloc(0) :Buffer.concat([",
- name,
- name
- );
- } else {
- builder.write("%s === null || %s === false ? Buffer.alloc(0) : [", name, name);
- }
- }
- if (arg.isVector) {
- if (arg.useVectorId) {
- builder.write("Buffer.from('15c4b51c','hex'),");
- }
- builder.write("struct.pack('<i', %s.length),", name);
- // Cannot unpack the values for the outer tuple through *[(
- // since that's a Python >3.5 feature, so add another join.
- builder.write('Buffer.concat(%s.map(x => ', name);
- // Temporary disable .is_vector, not to enter this if again
- // Also disable .is_flag since it's not needed per element
- const oldFlag = arg.isFlag;
- arg.isVector = arg.isFlag = false;
- writeArgToBytes(builder, arg, args, 'x');
- arg.isVector = true;
- arg.isFlag = oldFlag;
- builder.write('))');
- } else if (arg.flagIndicator) {
- // Calculate the flags with those items which are not None
- if (!args.some(f => f.isFlag)) {
- // There's a flag indicator, but no flag arguments so it's 0
- builder.write('Buffer.alloc(4)');
- } else {
- builder.write("struct.pack('<I', ");
- builder.write(
- args
- .filter(flag => flag.isFlag)
- .map(
- flag =>
- `(this.${flag.name} === null || this.${
- flag.name
- } === false ? 0 : ${1 << flag.flagIndex})`
- )
- .join(' | ')
- );
- builder.write(')');
- }
- } else if (arg.type === 'int') {
- builder.write("struct.pack('<i', %s)", name);
- } else if (arg.type === 'long') {
- builder.write("Helpers.readBufferFromBigInt(%s,8,true,true)", name);
- } else if (arg.type === 'int128') {
- builder.write("Helpers.readBufferFromBigInt(%s,16,true,true)", name);
- } else if (arg.type === 'int256') {
- builder.write("Helpers.readBufferFromBigInt(%s,32,true,true)", name);
- } else if (arg.type === 'double') {
- builder.write("struct.pack('<d', %s.toString())", name);
- } else if (arg.type === 'string') {
- builder.write('TLObject.serializeBytes(%s)', name);
- } else if (arg.type === 'Bool') {
- builder.write('%s ? 0xb5757299 : 0x379779bc', name);
- } else if (arg.type === 'true') {
- // These are actually NOT written! Only used for flags
- } else if (arg.type === 'bytes') {
- builder.write('TLObject.serializeBytes(%s)', name);
- } else if (arg.type === 'date') {
- builder.write('TLObject.serializeDatetime(%s)', name);
- } else {
- // Else it may be a custom type
- builder.write('%s.bytes', name);
- // If the type is not boxed (i.e. starts with lowercase) we should
- // not serialize the constructor ID (so remove its first 4 bytes).
- let boxed = arg.type.charAt(arg.type.indexOf('.') + 1);
- boxed = boxed === boxed.toUpperCase();
- if (!boxed) {
- builder.write('.slice(4)');
- }
- }
- if (arg.isFlag) {
- builder.write(']');
- if (arg.isVector) {
- builder.write(')');
- }
- }
- return true;
- };
- /**
- * Writes the read code for the given argument, setting the
- * arg.name variable to its read value.
- * @param builder The source code builder
- * @param arg The argument to write
- * @param args All the other arguments in TLObject same on_send.
- * This is required to determine the flags value
- * @param name The name of the argument. Defaults to "self.argname"
- * This argument is an option because it's required when
- * writing Vectors<>
- */
- const writeArgReadCode = (builder, arg, args, name) => {
- if (arg.genericDefinition) {
- return // Do nothing, this only specifies a later type
- }
- //The argument may be a flag, only write that flag was given!
- let wasFlag = false;
- if (arg.isFlag) {
- // Treat 'true' flags as a special case, since they're true if
- // they're set, and nothing else needs to actually be read.
- if (arg.type === "true") {
- builder.writeln("%s = Boolean(flags & %s);", name, 1 << arg.flagIndex);
- return;
- }
- wasFlag = true;
- builder.writeln("if (flags & %s) {", 1 << arg.flagIndex);
- // Temporary disable .is_flag not to enter this if
- // again when calling the method recursively
- arg.isFlag = false;
- }
- if (arg.isVector) {
- if (arg.useVectorId) {
- // We have to read the vector's constructor ID
- builder.writeln("reader.readInt();");
- }
- builder.writeln("%s = [];", name);
- builder.writeln("len = reader.readInt();");
- builder.writeln('for (let i=0;i<len;i++){');
- // Temporary disable .is_vector, not to enter this if again
- arg.isVector = false;
- writeArgReadCode(builder, arg, args, "_x");
- builder.writeln("%s.push(_x);", name);
- arg.isVector = true;
- } else if (arg.flagIndicator) {
- //Read the flags, which will indicate what items we should read next
- builder.writeln("let flags = reader.readInt();");
- builder.writeln();
- } else if (arg.type === "int") {
- builder.writeln("%s = reader.readInt();", name)
- } else if (arg.type === "long") {
- builder.writeln("%s = reader.readLong();", name);
- } else if (arg.type === "int128") {
- builder.writeln('%s = reader.readLargeInt(128);', name);
- } else if (arg.type === "int256") {
- builder.writeln('%s = reader.readLargeInt(256);', name);
- } else if (arg.type === "double") {
- builder.writeln('%s = reader.readDouble();', name);
- } else if (arg.type === "string") {
- builder.writeln('%s = reader.tgReadString();', name);
- } else if (arg.type === "Bool") {
- builder.writeln('%s = reader.tgReadBool();', name);
- } else if (arg.type === "true") {
- builder.writeln('%s = true;', name);
- } else if (arg.type === "bytes") {
- builder.writeln('%s = reader.tgReadBytes();', name);
- } else if (arg.type === "date") {
- builder.writeln('%s = reader.tgReadDate();', name);
- } else {
- // Else it may be a custom type
- if (!arg.skipConstructorId) {
- builder.writeln('%s = reader.tgReadObject();', name);
- } else {
- // Import the correct type inline to avoid cyclic imports.
- // There may be better solutions so that we can just access
- // all the types before the files have been parsed, but I
- // don't know of any.
- let sepIndex = arg.type.indexOf(".");
- let ns, t;
- if (sepIndex === -1) {
- ns = ".";
- t = arg.type;
- } else {
- ns = "." + arg.type.slice(0, sepIndex);
- t = arg.type.slice(sepIndex + 1);
- }
- let className = snakeToCamelCase(t);
- // There would be no need to import the type if we're in the
- // file with the same namespace, but since it does no harm
- // and we don't have information about such thing in the
- // method we just ignore that case.
- builder.writeln('let %s = require("%s");', className, ns);
- builder.writeln("%s = %s.fromReader(reader);", name, className);
- }
- }
- // End vector and flag blocks if required (if we opened them before)
- if (arg.isVector) {
- builder.writeln("}");
- }
- if (wasFlag) {
- builder.endBlock();
- builder.writeln("else {");
- builder.writeln("%s = null", name);
- builder.endBlock();
- // Restore .isFlag;
- arg.isFlag = true;
- }
- };
- const writePatched = (outDir, namespaceTlobjects) => {
- fs.mkdirSync(outDir, {recursive: true});
- for (const [ns, tlobjects] of Object.entries(namespaceTlobjects)) {
- const file = `${outDir}/${ns === 'null' ? 'index' : ns}.js`;
- const stream = fs.createWriteStream(file);
- const builder = new SourceBuilder(stream);
- builder.writeln(AUTO_GEN_NOTICE);
- builder.writeln("const struct = require('python-struct');");
- builder.writeln(`const { TLObject, types, custom } = require('..');`);
- builder.writeln(`const Helpers = require('../../utils/Helpers');`);
- builder.writeln();
- for (const t of tlobjects) {
- builder.writeln(
- 'class %s extends custom.%s {',
- t.className,
- PATCHED_TYPES[t.fullname]
- );
- builder.writeln(`static CONSTRUCTOR_ID = 0x${t.id.toString(16)}`);
- builder.writeln(`static SUBCLASS_OF_ID = 0x${crc32(t.result).toString("16")}`);
- builder.writeln();
- builder.writeln('constructor() {');
- builder.writeln('super();');
- builder.writeln(`this.CONSTRUCTOR_ID = 0x${t.id.toString(16)}`);
- builder.writeln(`this.SUBCLASS_OF_ID = 0x${crc32(t.result).toString("16")}`);
- builder.endBlock();
- //writeToJson(t, builder);
- writeToBytes(t, builder);
- writeFromReader(t, builder);
- builder.writeln();
- builder.endBlock();
- builder.currentIndent = 0;
- builder.writeln(
- 'types.%s%s = %s',
- t.namespace ? `${t.namespace}.` : '',
- t.className,
- t.className
- );
- builder.writeln();
- }
- }
- };
- const writeAllTLObjects = (tlobjects, layer, builder) => {
- builder.writeln(AUTO_GEN_NOTICE);
- builder.writeln();
- builder.writeln("const { types, functions, patched } = require('.');");
- builder.writeln();
- // Create a constant variable to indicate which layer this is
- builder.writeln(`const LAYER = %s;`, layer);
- builder.writeln();
- // Then create the dictionary containing constructor_id: class
- builder.writeln('const tlobjects = {');
- // Fill the dictionary (0x1a2b3c4f: tl.full.type.path.Class)
- for (const tlobject of tlobjects) {
- builder.write('0x0%s: ', tlobject.id.toString(16).padStart(8, '0'));
- if (tlobject.fullname in PATCHED_TYPES) {
- builder.write('patched');
- } else {
- builder.write(tlobject.isFunction ? 'functions' : 'types');
- }
- if (tlobject.namespace) {
- builder.write('.%s', tlobject.namespace);
- }
- builder.writeln('.%s,', tlobject.className);
- }
- builder.endBlock(true);
- builder.writeln('');
- builder.writeln('module.exports = {');
- builder.writeln('LAYER,');
- builder.writeln('tlobjects');
- builder.endBlock(true);
- };
- const generateTLObjects = (tlobjects, layer, importDepth, outputDir) => {
- // Group everything by {namespace :[tlobjects]} to generate index.js
- const namespaceFunctions = {};
- const namespaceTypes = {};
- const namespacePatched = {};
- // Group {type: [constructors]} to generate the documentation
- const typeConstructors = {};
- for (const tlobject of tlobjects) {
- if (tlobject.isFunction) {
- if (!namespaceFunctions[tlobject.namespace]) {
- namespaceFunctions[tlobject.namespace] = [];
- }
- namespaceFunctions[tlobject.namespace].push(tlobject);
- } else {
- if (!namespaceTypes[tlobject.namespace]) {
- namespaceTypes[tlobject.namespace] = [];
- }
- if (!typeConstructors[tlobject.result]) {
- typeConstructors[tlobject.result] = [];
- }
- namespaceTypes[tlobject.namespace].push(tlobject);
- typeConstructors[tlobject.result].push(tlobject);
- if (tlobject.fullname in PATCHED_TYPES) {
- if (!namespacePatched[tlobject.namespace]) {
- namespacePatched[tlobject.namespace] = [];
- }
- namespacePatched[tlobject.namespace].push(tlobject);
- }
- }
- }
- writeModules(
- `${outputDir}/functions`,
- importDepth,
- 'TLRequest',
- namespaceFunctions,
- typeConstructors
- );
- writeModules(
- `${outputDir}/types`,
- importDepth,
- 'TLObject',
- namespaceTypes,
- typeConstructors
- );
- writePatched(`${outputDir}/patched`, namespacePatched);
- const filename = `${outputDir}/alltlobjects.js`;
- const stream = fs.createWriteStream(filename);
- const builder = new SourceBuilder(stream);
- writeAllTLObjects(tlobjects, layer, builder);
- };
- const cleanTLObjects = outputDir => {
- for (let d in ['functions', 'types', 'patched']) {
- d = `${outputDir}/d`;
- if (fs.statSync(d).isDirectory()) {
- fs.rmdirSync(d);
- }
- }
- const tl = `${outputDir}/alltlobjects.js`;
- if (fs.statSync(tl).isFile()) {
- fs.unlinkSync(tl);
- }
- };
- const writeModuleExports = (tlobjects, builder) => {
- builder.writeln('module.exports = {');
- for (const t of tlobjects) {
- builder.writeln(`${t.className},`);
- }
- builder.currentIndent--;
- builder.writeln('};');
- };
- module.exports = {
- generateTLObjects,
- cleanTLObjects,
- };
|