morethanwords
4 years ago
55 changed files with 11549 additions and 853 deletions
@ -0,0 +1,241 @@
@@ -0,0 +1,241 @@
|
||||
// @ts-check
|
||||
const schema = require('./in/schema.json'); |
||||
const additional = require('./in/schema_additional_params.json'); |
||||
|
||||
const mtproto = schema.API; |
||||
|
||||
for(const constructor of additional) { |
||||
constructor.params.forEach(param => { |
||||
param.type = 'flags.-1?' + param.type; |
||||
}); |
||||
|
||||
const realConstructor = mtproto.constructors.find(c => c.predicate == constructor.predicate); |
||||
realConstructor.params.splice(realConstructor.params.length, 0, ...constructor.params); |
||||
} |
||||
|
||||
['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) => any} */ |
||||
const processParamType = (type) => { |
||||
const isAdditional = type.indexOf('flags.-1?') === 0; |
||||
if(type.includes('?')) { |
||||
type = type.split('?')[1]; |
||||
} |
||||
|
||||
if(type.includes('Vector')) { |
||||
return `Array<${processParamType(type.slice(7, -1))}>`; |
||||
} |
||||
|
||||
switch(type) { |
||||
case '#': |
||||
case 'int': |
||||
return 'number'; |
||||
|
||||
case 'true': |
||||
return 'true'; |
||||
|
||||
case 'Bool': |
||||
return 'boolean'; |
||||
|
||||
case 'double': |
||||
return 'number'; |
||||
|
||||
case 'long': |
||||
return 'string'; |
||||
|
||||
case 'bytes': |
||||
return 'Uint8Array | number[]'; |
||||
|
||||
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 : camelizeName(type, true); |
||||
} |
||||
}; |
||||
|
||||
/** @type {(params: {name: string, type: string}[], object: any, parseBooleanFlags: boolean) => any} */ |
||||
const processParams = (params, object = {}, parseBooleanFlags = true) => { |
||||
for(const param of params) { |
||||
let {name, type} = param; |
||||
|
||||
if(type.includes('?') || name == 'flags') { |
||||
name += '?'; |
||||
} |
||||
|
||||
const processed = processParamType(type); |
||||
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 + '\t'); |
||||
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)} = {\n`; |
||||
|
||||
const params = serializeObject(constructors[name], [], '\t\t'); |
||||
|
||||
return str + params.join(',\n').replace(/\{,/g, '{') + '\n\t};'; |
||||
}); |
||||
|
||||
|
||||
out += `/**
|
||||
* @link https://core.telegram.org/type/${type}
|
||||
*/ |
||||
export type ${camelizedType} = ${cs.map(name => camelizedType + '.' + camelizeName(name, false)).join(' | ')}; |
||||
|
||||
export namespace ${camelizedType} { |
||||
${csTypes.join('\n\n\t')} |
||||
} |
||||
|
||||
`;
|
||||
|
||||
} |
||||
|
||||
//console.log(types['InputUser']);
|
||||
|
||||
out += `export interface ConstructorDeclMap {\n`; |
||||
for(const predicate in constructorsTypes) { |
||||
out += `\t'${predicate}': ${constructorsTypes[predicate]},\n`; |
||||
} |
||||
out += `}\n\n`; |
||||
|
||||
/** @type {{[method: string]: {req: string, res: string}}} */ |
||||
const methodsMap = {}; |
||||
mtproto.methods.forEach((_method) => { |
||||
const {method, type, params} = _method; |
||||
|
||||
const camelizedMethod = camelizeName(method, true, true); |
||||
|
||||
methodsMap[method] = {req: camelizedMethod, res: processParamType(type)}; |
||||
|
||||
let str = `export type ${camelizedMethod} = {\n`; |
||||
|
||||
const object = processParams(params, {}, false); |
||||
|
||||
const serialized = serializeObject(object, [], '\t'); |
||||
|
||||
str += serialized.join(',\n').replace(/\{,/g, '{') + '\n};\n\n'; |
||||
out += str; |
||||
}); |
||||
|
||||
out += `export interface MethodDeclMap {\n`; |
||||
for(const method in methodsMap) { |
||||
out += `\t'${method}': {req: ${methodsMap[method].req}, res: ${methodsMap[method].res}},\n`; |
||||
} |
||||
out += `}\n\n`; |
||||
|
||||
require('fs').writeFileSync('./out/layer.d.ts', out); |
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
[{ |
||||
"predicate": "document", |
||||
"params": [ |
||||
{"name": "thumbs", "type": "Array<PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize>"}, |
||||
{"name": "type", "type": "'gif' | 'sticker' | 'audio' | 'voice' | 'video' | 'round' | 'photo'"}, |
||||
{"name": "h", "type": "number"}, |
||||
{"name": "w", "type": "number"}, |
||||
{"name": "file_name", "type": "string"}, |
||||
{"name": "file", "type": "File"}, |
||||
{"name": "duration", "type": "number"}, |
||||
{"name": "downloaded", "type": "boolean"}, |
||||
{"name": "url", "type": "string"}, |
||||
{"name": "audioTitle", "type": "string"}, |
||||
{"name": "audioPerformer", "type": "string"}, |
||||
{"name": "sticker", "type": "number"}, |
||||
{"name": "stickerEmoji", "type": "string"}, |
||||
{"name": "stickerEmojiRaw", "type": "string"}, |
||||
{"name": "stickerSetInput", "type": "InputStickerSet.inputStickerSetID"}, |
||||
{"name": "stickerThumbConverted", "type": "true"}, |
||||
{"name": "animated", "type": "boolean"}, |
||||
{"name": "supportsStreaming", "type": "boolean"} |
||||
] |
||||
}, { |
||||
"predicate": "photo", |
||||
"params": [ |
||||
{"name": "downloaded", "type": "boolean | number"}, |
||||
{"name": "url", "type": "string"} |
||||
] |
||||
}, { |
||||
"predicate": "photoSize", |
||||
"params": [ |
||||
{"name": "url", "type": "string"} |
||||
] |
||||
}, { |
||||
"predicate": "photoCachedSize", |
||||
"params": [ |
||||
{"name": "url", "type": "string"} |
||||
] |
||||
}, { |
||||
"predicate": "photoStrippedSize", |
||||
"params": [ |
||||
{"name": "url", "type": "string"} |
||||
] |
||||
}] |
Loading…
Reference in new issue