tlobject.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. const fs = require('fs');
  2. const util = require('util');
  3. const { crc32 } = require('crc');
  4. const SourceBuilder = require('../sourcebuilder');
  5. const AUTO_GEN_NOTICE =
  6. "/*! File generated by TLObjects' generator. All changes will be ERASED !*/";
  7. const AUTO_CASTS = {
  8. InputPeer: 'utils.get_input_peer(await client.get_input_entity(%s))',
  9. InputChannel: 'utils.get_input_channel(await client.get_input_entity(%s))',
  10. InputUser: 'utils.get_input_user(await client.get_input_entity(%s))',
  11. InputDialogPeer: 'await client._get_input_dialog(%s)',
  12. InputNotifyPeer: 'await client._get_input_notify(%s)',
  13. InputMedia: 'utils.get_input_media(%s)',
  14. InputPhoto: 'utils.get_input_photo(%s)',
  15. InputMessage: 'utils.get_input_message(%s)',
  16. InputDocument: 'utils.get_input_document(%s)',
  17. InputChatPhoto: 'utils.get_input_chat_photo(%s)',
  18. };
  19. const NAMED_AUTO_CASTS = {
  20. 'chat_id,int': 'await client.get_peer_id(%s, add_mark=False)',
  21. };
  22. // Secret chats have a chat_id which may be negative.
  23. // With the named auto-cast above, we would break it.
  24. // However there are plenty of other legit requests
  25. // with `chat_id:int` where it is useful.
  26. //
  27. // NOTE: This works because the auto-cast is not recursive.
  28. // There are plenty of types that would break if we
  29. // did recurse into them to resolve them.
  30. const NAMED_BLACKLIST = new Set(['messages.discardEncryption']);
  31. const BASE_TYPES = [
  32. 'string',
  33. 'bytes',
  34. 'int',
  35. 'long',
  36. 'int128',
  37. 'int256',
  38. 'double',
  39. 'Bool',
  40. 'true',
  41. ];
  42. // Patched types {fullname: custom.ns.Name}
  43. const PATCHED_TYPES = {
  44. messageEmpty: 'message.Message',
  45. message: 'message.Message',
  46. messageService: 'message.Message',
  47. };
  48. const writeModules = (
  49. outDir,
  50. depth,
  51. kind,
  52. namespaceTlobjects,
  53. typeConstructors
  54. ) => {
  55. // namespace_tlobjects: {'namespace', [TLObject]}
  56. fs.mkdirSync(outDir, { recursive: true });
  57. for (const [ns, tlobjects] of Object.entries(namespaceTlobjects)) {
  58. const file = `${outDir}/${ns === 'null' ? 'index' : ns}.js`;
  59. const stream = fs.createWriteStream(file);
  60. const builder = new SourceBuilder(stream);
  61. const dotDepth = '.'.repeat(depth || 1);
  62. builder.writeln(AUTO_GEN_NOTICE);
  63. builder.writeln(
  64. `const { TLObject } = require('${dotDepth}/tlobject');`
  65. );
  66. if (kind !== 'TLObject') {
  67. builder.writeln(
  68. `const { ${kind} } = require('${dotDepth}/tlobject');`
  69. );
  70. }
  71. // Add the relative imports to the namespaces,
  72. // unless we already are in a namespace.
  73. if (!ns) {
  74. const imports = Object.keys(namespaceTlobjects)
  75. .filter(Boolean)
  76. .join(`, `);
  77. builder.writeln(`const { ${imports} } = require('.');`);
  78. }
  79. // Import struct for the .__bytes__(self) serialization
  80. builder.writeln("const struct = require('python-struct');");
  81. const typeNames = new Set();
  82. const typeDefs = [];
  83. // Find all the types in this file and generate type definitions
  84. // based on the types. The type definitions are written to the
  85. // file at the end.
  86. for (const t of tlobjects) {
  87. if (!t.isFunction) {
  88. let typeName = t.result;
  89. if (typeName.includes('.')) {
  90. typeName = typeName.slice(typeName.lastIndexOf('.'));
  91. }
  92. if (typeNames.has(typeName)) {
  93. continue;
  94. }
  95. typeNames.add(typeName);
  96. const constructors = typeConstructors[typeName];
  97. if (!constructors) {
  98. } else if (constructors.length === 1) {
  99. typeDefs.push(
  100. `Type${typeName} = ${constructors[0].className}`
  101. );
  102. } else {
  103. typeDefs.push(
  104. `Type${typeName} = Union[${constructors
  105. .map(x => constructors.className)
  106. .join(',')}]`
  107. );
  108. }
  109. }
  110. }
  111. const imports = {};
  112. const primitives = new Set([
  113. 'int',
  114. 'long',
  115. 'int128',
  116. 'int256',
  117. 'double',
  118. 'string',
  119. 'bytes',
  120. 'Bool',
  121. 'true',
  122. ]);
  123. // Find all the types in other files that are used in this file
  124. // and generate the information required to import those types.
  125. for (const t of tlobjects) {
  126. for (const arg of t.args) {
  127. let name = arg.type;
  128. if (!name || primitives.has(name)) {
  129. continue;
  130. }
  131. let importSpace = `${dotDepth}/tl/types`;
  132. if (name.includes('.')) {
  133. const [namespace] = name.split('.');
  134. name = name.split('.');
  135. importSpace += `/${namespace}`;
  136. }
  137. if (!typeNames.has(name)) {
  138. typeNames.add(name);
  139. if (name === 'date') {
  140. imports.datetime = ['datetime'];
  141. continue;
  142. } else if (!(importSpace in imports)) {
  143. imports[importSpace] = new Set();
  144. }
  145. imports[importSpace].add(`Type${name}`);
  146. }
  147. }
  148. }
  149. // Add imports required for type checking
  150. if (imports) {
  151. builder.writeln('if (false) { // TYPE_CHECKING {');
  152. for (const [namespace, names] of Object.entries(imports)) {
  153. builder.writeln(
  154. `const { ${[...names.values()].join(
  155. ', '
  156. )} } = require('${namespace}');`
  157. );
  158. }
  159. builder.endBlock();
  160. }
  161. // Generate the class for every TLObject
  162. for (const t of tlobjects) {
  163. if (t.fullname in PATCHED_TYPES) {
  164. builder.writeln(`const ${t.className} = null; // Patched`);
  165. } else {
  166. writeSourceCode(t, kind, builder, typeConstructors);
  167. builder.currentIndent = 0;
  168. }
  169. }
  170. // Write the type definitions generated earlier.
  171. builder.writeln();
  172. for (const line of typeDefs) {
  173. builder.writeln(line);
  174. }
  175. writeModuleExports(tlobjects, builder);
  176. }
  177. };
  178. /**
  179. * Writes the source code corresponding to the given TLObject
  180. * by making use of the ``builder`` `SourceBuilder`.
  181. *
  182. * Additional information such as file path depth and
  183. * the ``Type: [Constructors]`` must be given for proper
  184. * importing and documentation strings.
  185. */
  186. const writeSourceCode = (tlobject, kind, builder, typeConstructors) => {
  187. writeClassConstructor(tlobject, kind, typeConstructors, builder);
  188. writeResolve(tlobject, builder);
  189. writeToJson(tlobject, builder);
  190. writeToBytes(tlobject, builder);
  191. builder.currentIndent--;
  192. builder.writeln('}');
  193. // writeFromReader(tlobject, builder);
  194. // writeReadResult(tlobject, builder);
  195. };
  196. const writeClassConstructor = (tlobject, kind, typeConstructors, builder) => {
  197. builder.writeln();
  198. builder.writeln();
  199. builder.writeln(`class ${tlobject.className} extends ${kind} {`);
  200. // Convert the args to string parameters, flags having =None
  201. const args = tlobject.realArgs.map(
  202. a =>
  203. `${a.name}: ${a.typeHint()}${
  204. a.isFlag || a.canBeInferred ? `=None` : ''
  205. }`
  206. );
  207. // Write the __init__ function if it has any argument
  208. if (!tlobject.realArgs.length) {
  209. return;
  210. }
  211. builder.writeln('/**');
  212. if (tlobject.isFunction) {
  213. builder.write(`:returns ${tlobject.result}: `);
  214. } else {
  215. builder.write(`Constructor for ${tlobject.result}: `);
  216. }
  217. const constructors = typeConstructors[tlobject.result];
  218. if (!constructors) {
  219. builder.writeln('This type has no constructors.');
  220. } else if (constructors.length === 1) {
  221. builder.writeln(`Instance of ${constructors[0].className}`);
  222. } else {
  223. builder.writeln(
  224. `Instance of either ${constructors
  225. .map(c => c.className)
  226. .join(', ')}`
  227. );
  228. }
  229. builder.writeln('*/');
  230. builder.writeln(`constructor(args) {`);
  231. builder.writeln(`super();`);
  232. // Class-level variable to store its Telegram's constructor ID
  233. builder.writeln(
  234. `this.CONSTRUCTOR_ID = 0x${tlobject.id.toString(16).padStart(8, '0')};`
  235. );
  236. builder.writeln(`this.SUBCLASS_OF_ID = 0x${crc32(tlobject.result)};`);
  237. builder.writeln();
  238. // Set the arguments
  239. for (const arg of tlobject.realArgs) {
  240. if (!arg.canBeInferred) {
  241. builder.writeln(`this.${arg.name} = args.${arg.name};`);
  242. }
  243. // Currently the only argument that can be
  244. // inferred are those called 'random_id'
  245. else if (arg.name === 'random_id') {
  246. // Endianness doesn't really matter, and 'big' is shorter
  247. let code = `int.from_bytes(os.urandom(${
  248. arg.type === 'long' ? 8 : 4
  249. }), 'big', signed=True)`;
  250. if (arg.isVector) {
  251. // Currently for the case of "messages.forwardMessages"
  252. // Ensure we can infer the length from id:Vector<>
  253. if (!tlobject.realArgs.find(a => a.name === 'id').isVector) {
  254. throw new Error(
  255. `Cannot infer list of random ids for ${tlobject}`
  256. );
  257. }
  258. code = `new Array(id.length).fill().map(_ => ${code})`;
  259. }
  260. builder.writeln(
  261. `this.random_id = random_id !== null ? random_id : ${code}`
  262. );
  263. } else {
  264. throw new Error(`Cannot infer a value for ${arg}`);
  265. }
  266. }
  267. builder.endBlock();
  268. };
  269. const writeResolve = (tlobject, builder) => {
  270. if (
  271. tlobject.isFunction &&
  272. tlobject.realArgs.some(
  273. arg =>
  274. arg.type in AUTO_CASTS ||
  275. (`${arg.name},${arg.type}` in NAMED_AUTO_CASTS &&
  276. !NAMED_BLACKLIST.has(tlobject.fullname))
  277. )
  278. ) {
  279. builder.writeln('async resolve(client, utils) {');
  280. for (const arg of tlobject.realArgs) {
  281. let ac = AUTO_CASTS[arg.type];
  282. if (!ac) {
  283. ac = NAMED_AUTO_CASTS[`${arg.name},${arg.type}`];
  284. if (!ac) {
  285. continue;
  286. }
  287. }
  288. if (arg.isFlag) {
  289. builder.writeln(`if (this.${arg.name}) {`);
  290. }
  291. if (arg.isVector) {
  292. builder.write(`const _tmp = [];`);
  293. builder.writeln(`for (const _x of this.${arg.name}) {`);
  294. builder.writeln(`_tmp.push(%s);`, util.format(ac, '_x'));
  295. builder.endBlock();
  296. builder.writeln(`this.${arg.name} = _tmp;`);
  297. } else {
  298. builder.writeln(
  299. `this.${arg.name} = %s`,
  300. util.format(ac, `this.${arg.name}`)
  301. );
  302. }
  303. if (arg.isFlag) {
  304. builder.currentIndent--;
  305. builder.writeln('}');
  306. }
  307. }
  308. builder.endBlock();
  309. }
  310. };
  311. const writeToJson = (tlobject, builder) => {
  312. builder.writeln('toJson() {');
  313. builder.writeln('return {');
  314. builder.write("_: '%s'", tlobject.className);
  315. for (const arg of tlobject.realArgs) {
  316. builder.writeln(',');
  317. builder.write('%s: ', arg.name);
  318. if (BASE_TYPES.includes(arg.type)) {
  319. if (arg.isVector) {
  320. builder.write(
  321. 'this.%s === null ? [] : this.%s.slice()',
  322. arg.name,
  323. arg.name
  324. );
  325. } else {
  326. builder.write('this.%s', arg.name);
  327. }
  328. } else {
  329. if (arg.isVector) {
  330. builder.write(
  331. 'this.%s === null ? [] : this.%s.map(x => x instanceof TLObject ? x.toJson() : x)',
  332. arg.name,
  333. arg.name
  334. );
  335. } else {
  336. builder.write(
  337. 'this.%s instanceof TLObject ? this.%s.toJson() : this.%s',
  338. arg.name,
  339. arg.name,
  340. arg.name
  341. );
  342. }
  343. }
  344. }
  345. builder.writeln();
  346. builder.endBlock();
  347. builder.currentIndent--;
  348. builder.writeln('}');
  349. };
  350. const writeToBytes = (tlobject, builder) => {
  351. builder.writeln('get bytes() {');
  352. // Some objects require more than one flag parameter to be set
  353. // at the same time. In this case, add an assertion.
  354. const repeatedArgs = {};
  355. for (const arg of tlobject.args) {
  356. if (arg.isFlag) {
  357. if (!repeatedArgs[arg.flagIndex]) {
  358. repeatedArgs[arg.flagIndex] = [];
  359. }
  360. repeatedArgs[arg.flagIndex].push(arg);
  361. }
  362. }
  363. for (const ra of Object.values(repeatedArgs)) {
  364. if (ra.length > 1) {
  365. const cnd1 = ra.map(
  366. a => `(this.${a.name} || this.${a.name} !== null)`
  367. );
  368. const cnd2 = ra.map(
  369. a => `(this.${a.name} === null || this.${a.name} === false)`
  370. );
  371. builder.writeln(
  372. 'if (!(%s || %s)) {',
  373. cnd1.join(' && '),
  374. cnd2.join(' && ')
  375. );
  376. builder.writeln(
  377. "throw new Error('%s parameters must all be false-y (like null) or all me true-y');",
  378. ra.map(a => a.name).join(', ')
  379. );
  380. builder.endBlock();
  381. }
  382. }
  383. const bytes = Buffer.from(
  384. parseInt(tlobject.id)
  385. .toString(16)
  386. .padStart(8, `0`),
  387. `hex`
  388. )
  389. .readUInt32LE()
  390. .toString(16)
  391. .padStart(8, `0`);
  392. builder.writeln('return parseInt([');
  393. builder.currentIndent++;
  394. builder.writeln("'%s',", bytes);
  395. for (const arg of tlobject.args) {
  396. if (writeArgToBytes(builder, arg, tlobject.args)) {
  397. builder.writeln(',');
  398. }
  399. }
  400. builder.currentIndent--;
  401. builder.writeln(']);');
  402. builder.endBlock();
  403. };
  404. // writeFromReader
  405. // writeReadResult
  406. /**
  407. * Writes the .__bytes__() code for the given argument
  408. * :param builder: The source code builder
  409. * :param arg: The argument to write
  410. * :param args: All the other arguments in TLObject same __bytes__.
  411. * This is required to determine the flags value
  412. * :param name: The name of the argument. Defaults to "self.argname"
  413. * This argument is an option because it's required when
  414. * writing Vectors<>
  415. */
  416. const writeArgToBytes = (builder, arg, args, name = null) => {
  417. if (arg.genericDefinition) {
  418. return; // Do nothing, this only specifies a later type
  419. }
  420. if (name === null) {
  421. name = `this.${arg.name}`;
  422. }
  423. // The argument may be a flag, only write if it's not None AND
  424. // if it's not a True type.
  425. // True types are not actually sent, but instead only used to
  426. // determine the flags.
  427. if (arg.isFlag) {
  428. if (arg.type === 'true') {
  429. return; // Exit, since true type is never written
  430. } else if (arg.isVector) {
  431. // Vector flags are special since they consist of 3 values,
  432. // so we need an extra join here. Note that empty vector flags
  433. // should NOT be sent either!
  434. builder.write(
  435. "%s === null || %s === false ? b'' : b''.join([",
  436. name,
  437. name
  438. );
  439. } else {
  440. builder.write("%s === null || %s === false ? b'' : [", name, name);
  441. }
  442. }
  443. if (arg.isVector) {
  444. if (arg.useVectorId) {
  445. builder.write("'15c4b51c',");
  446. }
  447. builder.write("struct.pack('<i', %s.length),", name);
  448. // Cannot unpack the values for the outer tuple through *[(
  449. // since that's a Python >3.5 feature, so add another join.
  450. builder.write('Buffer.concat(%s.map(x => ', name);
  451. // Temporary disable .is_vector, not to enter this if again
  452. // Also disable .is_flag since it's not needed per element
  453. const oldFlag = arg.isFlag;
  454. arg.isVector = arg.isFlag = false;
  455. writeArgToBytes(builder, arg, args, 'x');
  456. arg.isVector = true;
  457. arg.isFlag = oldFlag;
  458. builder.write('))', name);
  459. } else if (arg.flagIndicator) {
  460. // Calculate the flags with those items which are not None
  461. if (!args.some(f => f.isFlag)) {
  462. // There's a flag indicator, but no flag arguments so it's 0
  463. builder.write('Buffer.alloc(4)');
  464. } else {
  465. builder.write("struct.pack('<I', ");
  466. builder.write(
  467. args
  468. .filter(flag => flag.isFlag)
  469. .map(
  470. flag =>
  471. `(this.${flag.name} === null || this.${
  472. flag.name
  473. } === false ? 0 : ${1 << flag.flagIndex})`
  474. )
  475. .join(' | ')
  476. );
  477. builder.write(')');
  478. }
  479. } else if (arg.type === 'int') {
  480. builder.write("struct.pack('<i', %s)", name);
  481. } else if (arg.type === 'long') {
  482. builder.write("struct.pack('<q', %s)", name);
  483. } else if (arg.type === 'int128') {
  484. builder.write("%s.to_bytes(16, 'little', signed=True)", name);
  485. } else if (arg.type === 'int256') {
  486. builder.write("%s.to_bytes(32, 'little', signed=True)", name);
  487. } else if (arg.type === 'double') {
  488. builder.write("struct.pack('<d', %s)", name);
  489. } else if (arg.type === 'string') {
  490. builder.write('this.serializeBytes(%s)', name);
  491. } else if (arg.type === 'Bool') {
  492. builder.write('%s ? 0xb5757299 : 0x379779bc', name);
  493. } else if (arg.type === 'true') {
  494. // These are actually NOT written! Only used for flags
  495. } else if (arg.type === 'bytes') {
  496. builder.write('this.serializeBytes(%s)', name);
  497. } else if (arg.type === 'date') {
  498. builder.write('this.serializeDatetime(%s)', name);
  499. } else {
  500. // Else it may be a custom type
  501. builder.write('bytes(%s)', name);
  502. // If the type is not boxed (i.e. starts with lowercase) we should
  503. // not serialize the constructor ID (so remove its first 4 bytes).
  504. let boxed = arg.type.charAt(arg.type.indexOf('.') + 1);
  505. boxed = boxed === boxed.toUpperCase();
  506. if (!boxed) {
  507. builder.write('.slice(4)');
  508. }
  509. }
  510. if (arg.isFlag) {
  511. builder.write(']');
  512. if (arg.isVector) {
  513. builder.write(']');
  514. }
  515. }
  516. return true;
  517. };
  518. const writePatched = (outDir, namespaceTlobjects) => {
  519. fs.mkdirSync(outDir, { recursive: true });
  520. for (const [ns, tlobjects] of Object.entries(namespaceTlobjects)) {
  521. const file = `${outDir}/${ns === 'null' ? 'index' : ns}.js`;
  522. const stream = fs.createWriteStream(file);
  523. const builder = new SourceBuilder(stream);
  524. builder.writeln(AUTO_GEN_NOTICE);
  525. builder.writeln("const struct = require('python-struct');");
  526. builder.writeln(`const { TLObject, types, custom } = require('..');`);
  527. builder.writeln();
  528. for (const t of tlobjects) {
  529. builder.writeln(
  530. 'class %s extends custom.%s {',
  531. t.className,
  532. PATCHED_TYPES[t.fullname]
  533. );
  534. builder.writeln('constructor() {');
  535. builder.writeln('super();');
  536. builder.writeln(`this.CONSTRUCTOR_ID = 0x${t.id.toString(16)}`);
  537. builder.writeln(`this.SUBCLASS_OF_ID = 0x${crc32(t.result)}`);
  538. builder.endBlock();
  539. writeToJson(t, builder);
  540. writeToBytes(t, builder);
  541. // writeFromReader(t, builder);
  542. builder.writeln();
  543. builder.writeln(
  544. 'types.%s%s = %s',
  545. t.namespace ? `${t.namespace}.` : '',
  546. t.className,
  547. t.className
  548. );
  549. builder.writeln();
  550. }
  551. }
  552. };
  553. const writeAllTlobjects = (tlobjects, layer, builder = new SourceBuilder()) => {
  554. builder.writeln(AUTO_GEN_NOTICE);
  555. builder.writeln();
  556. builder.writeln("const { types, functions, patched } = require('.');");
  557. builder.writeln();
  558. // Create a constant variable to indicate which layer this is
  559. builder.writeln(`const LAYER = %s;`, layer);
  560. builder.writeln();
  561. // Then create the dictionary containing constructor_id: class
  562. builder.writeln('const tlobjects = {');
  563. // Fill the dictionary (0x1a2b3c4f: tl.full.type.path.Class)
  564. for (const tlobject of tlobjects) {
  565. builder.write('0x0%s: ', tlobject.id.toString(16).padStart(8, '0'));
  566. if (tlobject.fullname in PATCHED_TYPES) {
  567. builder.write('patched');
  568. } else {
  569. builder.write(tlobject.isFunction ? 'functions' : 'types');
  570. }
  571. if (tlobject.namespace) {
  572. builder.write('.%s', tlobject.namespace);
  573. }
  574. builder.writeln('.%s,', tlobject.className);
  575. }
  576. builder.endBlock(true);
  577. builder.writeln('');
  578. builder.writeln('module.exports = {');
  579. builder.writeln('LAYER,');
  580. builder.writeln('tlobjects');
  581. builder.endBlock(true);
  582. };
  583. const generateTlobjects = (tlobjects, layer, importDepth, outputDir) => {
  584. const namespaceFunctions = {};
  585. const namespaceTypes = {};
  586. const namespacePatched = {};
  587. const typeConstructors = {};
  588. for (const tlobject of tlobjects) {
  589. if (tlobject.isFunction) {
  590. if (!namespaceFunctions[tlobject.namespace]) {
  591. namespaceFunctions[tlobject.namespace] = [];
  592. }
  593. namespaceFunctions[tlobject.namespace].push(tlobject);
  594. } else {
  595. if (!namespaceTypes[tlobject.namespace]) {
  596. namespaceTypes[tlobject.namespace] = [];
  597. }
  598. if (!typeConstructors[tlobject.result]) {
  599. typeConstructors[tlobject.result] = [];
  600. }
  601. namespaceTypes[tlobject.namespace].push(tlobject);
  602. typeConstructors[tlobject.result].push(tlobject);
  603. if (tlobject.fullname in PATCHED_TYPES) {
  604. if (!namespacePatched[tlobject.namespace]) {
  605. namespacePatched[tlobject.namespace] = [];
  606. }
  607. namespacePatched[tlobject.namespace].push(tlobject);
  608. }
  609. }
  610. }
  611. writeModules(
  612. `${outputDir}/functions`,
  613. importDepth,
  614. 'TLRequest',
  615. namespaceFunctions,
  616. typeConstructors
  617. );
  618. writeModules(
  619. `${outputDir}/types`,
  620. importDepth,
  621. 'TLObject',
  622. namespaceTypes,
  623. typeConstructors
  624. );
  625. writePatched(`${outputDir}/pathced`, namespacePatched);
  626. const filename = `${outputDir}/alltlobjects.js`;
  627. const stream = fs.createWriteStream(filename);
  628. const builder = new SourceBuilder(stream);
  629. writeAllTlobjects(tlobjects, layer, builder);
  630. };
  631. const cleanTlobjects = outputDir => {
  632. for (let d in ['functions', 'types', 'patched']) {
  633. d = `${outputDir}/d`;
  634. if (fs.statSync(d).isDirectory()) {
  635. fs.rmdirSync(d);
  636. }
  637. }
  638. const tl = `${outputDir}/alltlobjects.js`;
  639. if (fs.statSync(tl).isFile()) {
  640. fs.unlinkSync(tl);
  641. }
  642. };
  643. const writeModuleExports = (tlobjects, builder) => {
  644. builder.writeln('module.exports = {');
  645. for (const t of tlobjects) {
  646. builder.writeln(`${t.className},`);
  647. }
  648. builder.currentIndent--;
  649. builder.writeln('};');
  650. };
  651. module.exports = {
  652. generateTlobjects,
  653. cleanTlobjects,
  654. };