123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- const { crc32 } = require('crc')
- const struct = require('python-struct')
- const { snakeToCamelCase } = require('../../utils')
- // https://github.com/telegramdesktop/tdesktop/blob/4bf66cb6e93f3965b40084771b595e93d0b11bcd/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py#L57-L62
- const WHITELISTED_MISMATCHING_IDS = {
- // 0 represents any layer
- 0: new Set([
- 'channel', // Since layer 77, there seems to be no going back...
- 'ipPortSecret',
- 'accessPointRule',
- 'help.configSimple',
- ]),
- }
- /**
- * Initializes a new TLObject, given its properties.
- *
- * :param fullname: The fullname of the TL object (namespace.name)
- * The namespace can be omitted.
- * :param object_id: The hexadecimal string representing the object ID
- * :param args: The arguments, if any, of the TL object
- * :param result: The result type of the TL object
- * :param is_function: Is the object a function or a type?
- * :param usability: The usability for this method.
- * :param friendly: A tuple (namespace, friendly method name) if known.
- * :param layer: The layer this TLObject belongs to.
- */
- class TLObject {
- constructor(fullname, objectId, args, result, isFunction, usability, friendly, layer) {
- // The name can or not have a namespace
- this.fullname = fullname
- if (fullname.includes('.')) {
- [this.namespace, this.name] = fullname.split(/\.(.+)/)
- // console.log(fullname.split(/\.(.+)/));
- // const [namespace, ...name] = fullname.split('.');
- // this.namespace = namespace;
- // this.name = name.join('.');
- } else {
- this.namespace = null
- this.name = fullname
- }
- this.args = args
- this.result = result
- this.isFunction = isFunction
- this.usability = usability
- this.friendly = friendly
- this.id = null
- if (!objectId) {
- this.id = this.inferId()
- } else {
- this.id = parseInt(objectId, 16)
- const whitelist = new Set([
- ...WHITELISTED_MISMATCHING_IDS[0],
- ...(WHITELISTED_MISMATCHING_IDS[layer] || []),
- ])
- if (!whitelist.has(this.fullname)) {
- if (this.id !== this.inferId()) {
- throw new Error(`Invalid inferred ID for ${this.repr()}`)
- }
- }
- }
- this.className = snakeToCamelCase(this.name, this.isFunction ? 'Request' : '')
- this.realArgs = this.sortedArgs().filter((a) => !(a.flagIndicator || a.genericDefinition))
- }
- get innermostResult() {
- const index = this.result.indexOf('<')
- return index === -1 ? this.result : this.result.slice(index + 1, -1)
- }
- /**
- * Returns the arguments properly sorted and ready to plug-in
- * into a Python's method header (i.e., flags and those which
- * can be inferred will go last so they can default =None)
- */
- sortedArgs() {
- return this.args.sort((x) => x.isFlag || x.canBeInferred)
- }
- repr(ignoreId) {
- let hexId
- let args
- if (this.id === null || ignoreId) {
- hexId = ''
- } else {
- hexId = `#${this.id.toString(16).padStart(8, '0')}`
- }
- if (this.args.length) {
- args = ` ${this.args.map((arg) => arg.toString()).join(' ')}`
- } else {
- args = ''
- }
- return `${this.fullname}${hexId}${args} = ${this.result}`
- }
- inferId() {
- const representation = this.repr(true)
- .replace(/(:|\?)bytes /g, '$1string ')
- .replace(/</g, ' ')
- .replace(/>|{|}/g, '')
- .replace(/ \w+:flags\.\d+\?true/g, '')
- if (this.fullname === 'inputMediaInvoice') {
- // eslint-disable-next-line no-empty
- if (this.fullname === 'inputMediaInvoice') {
- }
- }
- return crc32(Buffer.from(representation, 'utf8'))
- }
- toJson() {
- return {
- id: struct.unpack('i', struct.pack('I', this.id))[0].toString(),
- [this.isFunction ? 'method' : 'predicate']: this.fullname,
- param: this.args.filter((x) => !x.genericDefinition).map((x) => x.toJson()),
- type: this.result,
- }
- }
- isGoodExample() {
- return !this.className.endsWith('Empty')
- }
- asExample(f, indent) {
- f.write('<strong>new</strong> ')
- f.write(this.isFunction ? 'functions' : 'types')
- if (this.namespace) {
- f.write('.')
- f.write(this.namespace)
- }
- f.write('.')
- f.write(this.className)
- f.write('(')
- const args = this.realArgs.filter((arg) => !arg.omitExample())
- if (!args.length) {
- f.write(')')
- return
- }
- f.write('\n')
- indent++
- let remaining = args.length
- for (const arg of args) {
- remaining--
- f.write(' '.repeat(indent))
- f.write(arg.name)
- f.write('=')
- if (arg.isVector) {
- f.write('[')
- }
- arg.asExample(f, indent || 0)
- if (arg.isVector) {
- f.write(']')
- }
- if (remaining) {
- f.write(',')
- }
- f.write('\n')
- }
- indent--
- f.write(' '.repeat(indent))
- f.write(')')
- }
- }
- module.exports = {
- TLObject,
- }
|