Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
8.2 KiB
299 lines
8.2 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
// @ts-check |
|
const schema = require(__dirname + '/in/schema.json'); |
|
const additional = require(__dirname + '/in/schema_additional_params.json'); |
|
const replace = require(__dirname + '/in/schema_replace_types.json'); |
|
|
|
const mtproto = schema.API; |
|
|
|
const TABULATION = ' '; |
|
const NEW_LINE = '\n'; |
|
const FLAGS_KEYS = new Set(['flags', 'flags2']); |
|
|
|
for(const constructor of additional) { |
|
const additionalParams = constructor.params || (constructor.params = []); |
|
additionalParams.forEach(param => { |
|
param.type = 'flags.-1?' + param.type; |
|
}); |
|
|
|
if(constructor.properties) { |
|
additionalParams.push(...constructor.properties); |
|
} |
|
|
|
if(constructor.type) { |
|
mtproto.constructors.push(constructor); |
|
} |
|
|
|
const realConstructor = constructor.type ? constructor : mtproto.constructors.find(c => c.predicate == constructor.predicate); |
|
|
|
if(!constructor.type) { |
|
if(!realConstructor) { |
|
console.log(realConstructor, constructor); |
|
} |
|
|
|
for(let i = realConstructor.params.length - 1; i >= 0; --i) { |
|
const param = realConstructor.params[i]; |
|
if(additionalParams.find(newParam => newParam.name === param.name)) { |
|
realConstructor.params.splice(i, 1); |
|
} |
|
} |
|
} |
|
|
|
/* constructor.params.forEach(param => { |
|
const index = realConstructor.params.findIndex(_param => _param.predicate == param.predicate); |
|
if(index !== -1) { |
|
realConstructor.params.splice(index, 1); |
|
} |
|
}); */ |
|
realConstructor.params.splice(realConstructor.params.length, 0, ...additionalParams); |
|
} |
|
|
|
['Vector t', 'Bool', 'True', 'Null'].forEach(key => { |
|
let idx = -1; |
|
do { |
|
idx = mtproto.constructors.findIndex(c => c.type == key); |
|
if(idx !== -1) { |
|
mtproto.constructors.splice(idx, 1); |
|
} else { |
|
break; |
|
} |
|
} while(true); |
|
|
|
// delete types[key]; |
|
}); |
|
|
|
/** @type {(string: string) => string} */ |
|
function capitalizeFirstLetter(string) { |
|
return string[0].toUpperCase() + string.slice(1); |
|
} |
|
|
|
/** @type {(string: string, camelizeFirstLetterIfFound: boolean, camelizeFirstLetterIfNotFound: boolean) => string} */ |
|
function camelizeName(string, camelizeFirstLetterIfFound, camelizeFirstLetterIfNotFound = false) { |
|
if(!string.includes('.')) { |
|
if(camelizeFirstLetterIfNotFound) { |
|
string = capitalizeFirstLetter(string); |
|
} |
|
|
|
return string; |
|
} |
|
|
|
if(camelizeFirstLetterIfFound) { |
|
string = capitalizeFirstLetter(string); |
|
} |
|
|
|
return string.replace(/\../g, (match, index) => { |
|
return match[1].toUpperCase(); |
|
}); |
|
} |
|
|
|
/** @type {(type: string, parseBooleanFlags: boolean, overrideTypes?: {[type: string]: string}) => any} */ |
|
const processParamType = (type, parseBooleanFlags, overrideTypes) => { |
|
const isAdditional = type.indexOf('flags.-1?') === 0; |
|
const isFlag = type.includes('?'); |
|
if(isFlag) { |
|
type = type.split('?')[1]; |
|
} |
|
|
|
if(type.includes('Vector')) { |
|
return `Array<${processParamType(type.slice(7, -1), parseBooleanFlags, overrideTypes)}>`; |
|
} |
|
|
|
const overridden = overrideTypes && overrideTypes[type]; |
|
if(overridden) { |
|
return overridden; |
|
} |
|
|
|
switch(type) { |
|
case '#': |
|
case 'int': |
|
return 'number'; |
|
|
|
case 'true': |
|
return parseBooleanFlags ? 'true' : 'boolean'; |
|
|
|
case 'Bool': |
|
return 'boolean'; |
|
|
|
case 'double': |
|
return 'number'; |
|
|
|
case 'long': |
|
return 'string | number'; |
|
|
|
case 'bytes': |
|
return 'Uint8Array'; |
|
|
|
case 'string': |
|
return 'string'; |
|
|
|
case 'X': |
|
case '!X': |
|
return 'any'; |
|
|
|
default: |
|
// console.log('no such type', type); |
|
// throw new Error('no such type: ' + type); |
|
return isAdditional || type[0] === type[0].toUpperCase() ? type : camelizeName(type, true); |
|
} |
|
}; |
|
|
|
/** @type {(params: {name: string, type: string}[], object: any, parseBooleanFlags: boolean, overrideTypes?: {[type: string]: string}) => any} */ |
|
const processParams = (params, object = {}, parseBooleanFlags = true, overrideTypes) => { |
|
for(const param of params) { |
|
let {name, type} = param; |
|
|
|
if((type.includes('?') || FLAGS_KEYS.has(name)) && !name.includes('`')) { |
|
name += '?'; |
|
} |
|
|
|
if(replace[name]) { |
|
type = replace[name]; |
|
} |
|
|
|
const processed = processParamType(type, parseBooleanFlags, overrideTypes); |
|
if(type.includes('?true') && parseBooleanFlags) { |
|
if(!object.pFlags) object.pFlags = {}; |
|
object.pFlags[name] = processed; |
|
} else { |
|
object[name] = processed; |
|
} |
|
} |
|
|
|
return object; |
|
}; |
|
|
|
/** @type {(object: any) => boolean} */ |
|
function isObject(object) { |
|
return typeof(object) === 'object' && object !== null; |
|
} |
|
|
|
/** @type {(object: any, outArray: string[], space: string) => string[]} */ |
|
function serializeObject(object, outArray, space) { |
|
for(const key in object) { |
|
const value = object[key]; |
|
|
|
if(isObject(value)) { // only pFlags |
|
outArray.push(`${space}${key}: Partial<{`); |
|
serializeObject(value, outArray, space + TABULATION); |
|
outArray.push(`${space}}>`); |
|
} else { |
|
outArray.push(`${space}${key}: ${value}`); |
|
} |
|
} |
|
|
|
return outArray; |
|
} |
|
|
|
let out = ''; |
|
/** @type {Array<{key: 'predicate' | 'method', instanceKey: 'constructors' | 'methods', name: string}>} */ |
|
/* const lol = [{key: 'predicate', instanceKey: 'constructors', name: 'Constructor'}, {key: 'method', instanceKey: 'methods', name: 'Method'}]; |
|
lol.forEach(info => { |
|
const {key: keyName, instanceKey, name: mapName} = info; */ |
|
|
|
/** @type {{[type: string]: string[]}} */ |
|
const types = {}; |
|
/** @type {{[predicate: string]: any}} */ |
|
const constructors = {}; |
|
/** @type {{[predicate: string]: string}} */ |
|
const constructorsTypes = {}; |
|
|
|
mtproto.constructors.forEach((constructor) => { |
|
const {type, predicate, params} = constructor; |
|
|
|
if(!types.hasOwnProperty(type)) { |
|
types[type] = []; |
|
} |
|
|
|
types[type].push(predicate); |
|
|
|
constructorsTypes[predicate] = camelizeName(type, true) + '.' + camelizeName(predicate, false); |
|
|
|
// type end |
|
|
|
/** @type {any} */ |
|
const c = { |
|
_: `'${predicate}'` |
|
}; |
|
constructors[predicate] = c; |
|
|
|
processParams(params, c, true); |
|
|
|
/* if(predicate == 'inputFileLocation') { |
|
console.log(c); |
|
} */ |
|
}); |
|
|
|
for(const type in types) { |
|
const cs = types[type]; |
|
|
|
const camelizedType = camelizeName(type, true); |
|
|
|
const csTypes = cs.map(name => { |
|
const str = `export type ${camelizeName(name, false)} = {${NEW_LINE}`; |
|
|
|
const params = serializeObject(constructors[name], [], TABULATION + TABULATION); |
|
|
|
return str + params.join(`,${NEW_LINE}`).replace(/\{,/g, '{') + `${NEW_LINE}${TABULATION}};`; |
|
}); |
|
|
|
|
|
out += `/** |
|
* @link https://core.telegram.org/type/${type} |
|
*/ |
|
export type ${camelizedType} = ${cs.map(name => camelizedType + '.' + camelizeName(name, false)).join(' | ')}; |
|
|
|
export namespace ${camelizedType} { |
|
${csTypes.join(`${NEW_LINE}${NEW_LINE}${TABULATION}`)} |
|
} |
|
|
|
`; |
|
} |
|
|
|
// console.log(types['InputUser']); |
|
|
|
out += `export interface ConstructorDeclMap {${NEW_LINE}`; |
|
for(const predicate in constructorsTypes) { |
|
out += `${TABULATION}'${predicate}': ${constructorsTypes[predicate]},${NEW_LINE}`; |
|
} |
|
out += `}${NEW_LINE}${NEW_LINE}`; |
|
|
|
/** @type {{[method: string]: {req: string, res: string}}} */ |
|
const methodsMap = {}; |
|
// const overrideMethodTypes = { |
|
// long: 'string | number' |
|
// }; |
|
mtproto.methods.forEach((_method) => { |
|
const {method, type, params} = _method; |
|
|
|
const camelizedMethod = camelizeName(method, true, true); |
|
|
|
methodsMap[method] = { |
|
req: camelizedMethod, |
|
res: processParamType(type, false, {'JSONValue': 'any'}/* , overrideMethodTypes */) |
|
}; |
|
|
|
let str = `export type ${camelizedMethod} = {${NEW_LINE}`; |
|
|
|
const object = processParams(params, {}, false/* , overrideMethodTypes */); |
|
|
|
const serialized = serializeObject(object, [], TABULATION); |
|
|
|
str += serialized.join(`,${NEW_LINE}`).replace(/\{,/g, '{') + `${NEW_LINE}};${NEW_LINE}${NEW_LINE}`; |
|
out += str; |
|
}); |
|
|
|
out += `export interface MethodDeclMap {${NEW_LINE}`; |
|
for(const method in methodsMap) { |
|
out += `${TABULATION}'${method}': {req: ${methodsMap[method].req}, res: ${methodsMap[method].res}},${NEW_LINE}`; |
|
} |
|
out += `}${NEW_LINE}${NEW_LINE}`; |
|
|
|
const path = process.argv[2]; |
|
const writePathTo = (path || __dirname + '/out/') + 'layer.d.ts'; |
|
console.log('Writing layer to:', writePathTo); |
|
require('fs').writeFileSync(writePathTo, out);
|
|
|