import { crc32 } from "../Helpers" ; import type { DateLike } from "../define" ; const snakeToCamelCase = ( name ) => { const result = name . replace ( /(?:^|_)([a-z])/g , ( _ , g ) => g . toUpperCase ( ) ) ; return result . replace ( /_/g , "" ) ; } ; const variableSnakeToCamelCase = ( str ) => str . replace ( /([-_][a-z])/g , ( group ) => group . toUpperCase ( ) . replace ( "-" , "" ) . replace ( "_" , "" ) ) ; const CORE_TYPES = new Set ( [ 0xbc799737 , 0x997275b5 , 0x3fedd339 , 0xc4b9f9bb , 0x56730bcc , ] ) ; const AUTH_KEY_TYPES = new Set ( [ 0x05162463 , 0x83c95aec , 0xa9f55f95 , 0x3c6a84d4 , 0x56fddf88 , 0xd0e8075c , 0xb5890dba , 0x6643b654 , 0xd712e4be , 0xf5045f1f , 0x3072cfa1 , ] ) ; const fromLine = ( line , isFunction ) => { const match = line . match ( /([\w.]+)(?:#([0-9a-fA-F]+))?(?:\s{?\w+:[\w\d<>#.?!]+}?)*\s=\s([\w\d<>#.?]+);$/ ) ; if ( ! match ) { throw new Error ( `Cannot parse TLObject ${ line }` ) ; } const argsMatch = findAll ( /({)?(\w+):([\w\d<>#.?!]+)}?/ , line ) ; const currentConfig = { name : match [ 1 ] , constructorId : parseInt ( match [ 2 ] , 16 ) , argsConfig : { } , subclassOfId : crc32 ( match [ 3 ] ) , result : match [ 3 ] , isFunction : isFunction , namespace : undefined , } ; if ( ! currentConfig . constructorId ) { const hexId = "" ; let args ; if ( Object . values ( currentConfig . argsConfig ) . length ) { args = ` ${ Object . keys ( currentConfig . argsConfig ) . map ( ( arg ) => arg . toString ( ) ) . join ( " " ) }` ; } else { args = "" ; } const representation = `${ currentConfig . name }${ hexId }${ args } = ${ currentConfig . result }` . replace ( /(:|\?)bytes /g , "$1string " ) . replace ( /</g , " " ) . replace ( />|{|}/g , "" ) . replace ( / \w+:flags\.\d+\?true/g , "" ) ; if ( currentConfig . name === "inputMediaInvoice" ) { if ( currentConfig . name === "inputMediaInvoice" ) { } } currentConfig . constructorId = crc32 ( Buffer . from ( representation , "utf8" ) ) ; } for ( const [ brace , name , argType ] of argsMatch ) { if ( brace === undefined ) { currentConfig . argsConfig [ variableSnakeToCamelCase ( name ) ] = buildArgConfig ( name , argType ) ; } } if ( currentConfig . name . includes ( "." ) ) { [ currentConfig . namespace , currentConfig . name ] = currentConfig . name . split ( /\.(.+)/ ) ; } currentConfig . name = snakeToCamelCase ( currentConfig . name ) ; return currentConfig ; } ; function buildArgConfig ( name , argType ) { name = name === "self" ? "is_self" : name ; const currentConfig = { isVector : false , isFlag : false , skipConstructorId : false , flagIndex : - 1 , flagIndicator : true , type : null , useVectorId : null , } ; const canBeInferred = name === "random_id" ; if ( argType !== "#" ) { currentConfig . flagIndicator = false ; currentConfig . type = argType . replace ( /^!+/ , "" ) ; const flagMatch = currentConfig . type . match ( /flags.(\d+)\?([\w<>.]+)/ ) ; if ( flagMatch ) { currentConfig . isFlag = true ; currentConfig . flagIndex = Number ( flagMatch [ 1 ] ) ; [ , , currentConfig . type ] = flagMatch ; } const vectorMatch = currentConfig . type . match ( /[Vv]ector<([\w\d.]+)>/ ) ; if ( vectorMatch ) { currentConfig . isVector = true ; currentConfig . useVectorId = currentConfig . type . charAt ( 0 ) === "V" ; [ , currentConfig . type ] = vectorMatch ; } if ( /^[a-z]$/ . test ( currentConfig . type . split ( "." ) . pop ( ) . charAt ( 0 ) ) ) { currentConfig . skipConstructorId = true ; } } if ( currentConfig . type == "future_salt" ) { currentConfig . type = "FutureSalt" ; } return currentConfig ; } const parseTl = function * ( content , layer , methods = [ ] , ignoreIds = CORE_TYPES ) { const methodInfo = ( methods || [ ] ) . reduce ( ( o , m ) => ( { ... o , [ m . name ] : m } ) , { } ) ; const objAll = [ ] ; const objByName = { } ; const objByType = { } ; const file = content ; let isFunction = false ; for ( let line of file . split ( "\n" ) ) { const commentIndex = line . indexOf ( "//" ) ; if ( commentIndex !== - 1 ) { line = line . slice ( 0 , commentIndex ) ; } line = line . trim ( ) ; if ( ! line ) { continue ; } const match = line . match ( /---(\w+)---/ ) ; if ( match ) { const [ , followingTypes ] = match ; isFunction = followingTypes === "functions" ; continue ; } try { const result = fromLine ( line , isFunction ) ; if ( ignoreIds . has ( result . constructorId ) ) { continue ; } objAll . push ( result ) ; if ( ! result . isFunction ) { if ( ! objByType [ result . result ] ) { objByType [ result . result ] = [ ] ; } objByName [ result . name ] = result ; objByType [ result . result ] . push ( result ) ; } } catch ( e ) { if ( ! e . toString ( ) . includes ( "vector#1cb5c415" ) ) { throw e ; } } } for ( const obj of objAll ) { if ( AUTH_KEY_TYPES . has ( obj . constructorId ) ) { for ( const arg in obj . argsConfig ) { if ( obj . argsConfig [ arg ] . type === "string" ) { obj . argsConfig [ arg ] . type = "bytes" ; } } } } for ( const obj of objAll ) { yield obj ; } } ; const findAll = ( regex , str , matches = [ ] ) => { if ( ! regex . flags . includes ( "g" ) ) { regex = new RegExp ( regex . source , "g" ) ; } const res = regex . exec ( str ) ; if ( res ) { matches . push ( res . slice ( 1 ) ) ; findAll ( regex , str , matches ) ; } return matches ; } ; export function serializeBytes ( data ) { if ( ! ( data instanceof Buffer ) ) { if ( typeof data == "string" ) { data = Buffer . from ( data ) ; } else { throw Error ( `Bytes or str expected, not ${ data . constructor . name }` ) ; } } const r = [ ] ; let padding ; if ( data . length < 254 ) { padding = ( data . length + 1 ) % 4 ; if ( padding !== 0 ) { padding = 4 - padding ; } r . push ( Buffer . from ( [ data . length ] ) ) ; r . push ( data ) ; } else { padding = data . length % 4 ; if ( padding !== 0 ) { padding = 4 - padding ; } r . push ( Buffer . from ( [ 254 , data . length % 256 , ( data . length >> 8 ) % 256 , ( data . length >> 16 ) % 256 , ] ) ) ; r . push ( data ) ; } r . push ( Buffer . alloc ( padding ) . fill ( 0 ) ) ; return Buffer . concat ( r ) ; } export function serializeDate ( dt ) { if ( ! dt ) { return Buffer . alloc ( 4 ) . fill ( 0 ) ; } if ( dt instanceof Date ) { dt = Math . floor ( ( Date . now ( ) - dt . getTime ( ) ) / 1000 ) ; } if ( typeof dt == "number" ) { const t = Buffer . alloc ( 4 ) ; t . writeInt32LE ( dt , 0 ) ; return t ; } throw Error ( `Cannot interpret "${ dt }" as a date` ) ; } export { findAll , parseTl , buildArgConfig , fromLine , CORE_TYPES , snakeToCamelCase , variableSnakeToCamelCase , } ;