Alexander Zinchuk 3afcde3217 Initial commit
2021-04-09 14:11:51 +03:00

384 lines
13 KiB
JavaScript

const {
parseTl,
serializeBytes,
serializeDate
} = require('./generationHelpers')
const { readBufferFromBigInt,toSignedLittleBuffer } = require('../Helpers')
const tlContent = require('./apiTl.js')
const schemeContent = require('./schemaTl.js')
/*CONTEST
const NAMED_AUTO_CASTS = new Set([
'chatId,int'
])
const NAMED_BLACKLIST = new Set([
'discardEncryption'
])
const AUTO_CASTS = new Set([
'InputPeer',
'InputChannel',
'InputUser',
'InputDialogPeer',
'InputNotifyPeer',
'InputMedia',
'InputPhoto',
'InputMessage',
'InputDocument',
'InputChatPhoto'
])
*/
const CACHING_SUPPORTED = typeof self !== 'undefined' && self.localStorage !== undefined
const CACHE_KEY = 'GramJs:apiCache'
function buildApiFromTlSchema() {
let definitions;
const fromCache = CACHING_SUPPORTED && loadFromCache()
if (fromCache) {
definitions = fromCache
} else {
definitions = loadFromTlSchemas()
if (CACHING_SUPPORTED) {
localStorage.setItem(CACHE_KEY, JSON.stringify(definitions))
}
}
return mergeWithNamespaces(
createClasses('constructor', definitions.constructors),
createClasses('request', definitions.requests)
)
}
function loadFromCache() {
const jsonCache = localStorage.getItem(CACHE_KEY)
return jsonCache && JSON.parse(jsonCache)
}
function loadFromTlSchemas() {
const [constructorParamsApi, functionParamsApi] = extractParams(tlContent)
const [constructorParamsSchema, functionParamsSchema] = extractParams(schemeContent)
const constructors = [].concat(constructorParamsApi, constructorParamsSchema)
const requests = [].concat(functionParamsApi, functionParamsSchema)
return { constructors, requests }
}
function mergeWithNamespaces(obj1, obj2) {
const result = { ...obj1 }
Object.keys(obj2).forEach((key) => {
if (typeof obj2[key] === 'function' || !result[key]) {
result[key] = obj2[key]
} else {
Object.assign(result[key], obj2[key])
}
})
return result
}
function extractParams(fileContent) {
const f = parseTl(fileContent, 109)
const constructors = []
const functions = []
for (const d of f) {
d.isFunction ? functions.push(d) : constructors.push(d)
}
return [constructors, functions]
}
function argToBytes(x, type) {
switch (type) {
case 'int':
const i = Buffer.alloc(4)
i.writeInt32LE(x, 0)
return i
case 'long':
return toSignedLittleBuffer(x, 8)
case 'int128':
return toSignedLittleBuffer(x, 16)
case 'int256':
return toSignedLittleBuffer(x, 32)
case 'double':
const d = Buffer.alloc(8)
d.writeDoubleLE(x, 0)
return d
case 'string':
return serializeBytes(x)
case 'Bool':
return x ? Buffer.from('b5757299', 'hex') : Buffer.from('379779bc', 'hex')
case 'true':
return Buffer.alloc(0)
case 'bytes':
return serializeBytes(x)
case 'date':
return serializeDate(x)
default:
return x.getBytes()
}
}
/*
CONTEST
async function getInputFromResolve(utils, client, peer, peerType) {
switch (peerType) {
case 'InputPeer':
return utils.getInputPeer(await client.getInputEntity(peer))
case 'InputChannel':
return utils.getInputChannel(await client.getInputEntity(peer))
case 'InputUser':
return utils.getInputUser(await client.getInputEntity(peer))
case 'InputDialogPeer':
return await client._getInputDialog(peer)
case 'InputNotifyPeer':
return await client._getInputNotify(peer)
case 'InputMedia':
return utils.getInputMedia(peer)
case 'InputPhoto':
return utils.getInputPhoto(peer)
case 'InputMessage':
return utils.getInputMessage(peer)
case 'InputDocument':
return utils.getInputDocument(peer)
case 'InputChatPhoto':
return utils.getInputChatPhoto(peer)
case 'chatId,int' :
return await client.getPeerId(peer, false)
default:
throw new Error('unsupported peer type : ' + peerType)
}
}
*/
function getArgFromReader(reader, arg) {
if (arg.isVector) {
if (arg.useVectorId) {
reader.readInt()
}
const temp = []
const len = reader.readInt()
arg.isVector = false
for (let i = 0; i < len; i++) {
temp.push(getArgFromReader(reader, arg))
}
arg.isVector = true
return temp
} else if (arg.flagIndicator) {
return reader.readInt()
} else {
switch (arg.type) {
case 'int':
return reader.readInt()
case 'long':
return reader.readLong()
case 'int128':
return reader.readLargeInt(128)
case 'int256':
return reader.readLargeInt(256)
case 'double':
return reader.readDouble()
case 'string':
return reader.tgReadString()
case 'Bool':
return reader.tgReadBool()
case 'true':
return true
case 'bytes':
return reader.tgReadBytes()
case 'date':
return reader.tgReadDate()
default:
if (!arg.skipConstructorId) {
return reader.tgReadObject()
} else {
return api.constructors[arg.type].fromReader(reader)
}
}
}
}
function createClasses(classesType, params) {
const classes = {}
for (const classParams of params) {
const { name, constructorId, subclassOfId, argsConfig, namespace, result } = classParams
const fullName = [namespace, name].join('.').replace(/^\./, '')
class VirtualClass {
static CONSTRUCTOR_ID = constructorId
static SUBCLASS_OF_ID = subclassOfId
static className = fullName
static classType = classesType
CONSTRUCTOR_ID = constructorId
SUBCLASS_OF_ID = subclassOfId
className = fullName
classType = classesType
constructor(args) {
args = args || {}
Object.keys(args)
.forEach((argName) => {
this[argName] = args[argName]
})
}
static fromReader(reader) {
const args = {}
for (const argName in argsConfig) {
if (argsConfig.hasOwnProperty(argName)) {
const arg = argsConfig[argName]
if (arg.isFlag) {
if (arg.type === 'true') {
args[argName] = Boolean(args['flags'] & 1 << arg.flagIndex)
continue
}
if (args['flags'] & 1 << arg.flagIndex) {
args[argName] = getArgFromReader(reader, arg)
} else {
args[argName] = null
}
} else {
if (arg.flagIndicator) {
arg.name = 'flags'
}
args[argName] = getArgFromReader(reader, arg)
}
}
}
return new VirtualClass(args)
}
getBytes() {
// The next is pseudo-code:
const idForBytes = this.CONSTRUCTOR_ID
const c = Buffer.alloc(4)
c.writeUInt32LE(idForBytes, 0)
const buffers = [c]
for (const arg in argsConfig) {
if (argsConfig.hasOwnProperty(arg)) {
if (argsConfig[arg].isFlag) {
if ((this[arg]===false&&argsConfig[arg].type==="true") || this[arg]===null || this[arg]===undefined){
continue
}
}
if (argsConfig[arg].isVector) {
if (argsConfig[arg].useVectorId) {
buffers.push(Buffer.from('15c4b51c', 'hex'))
}
const l = Buffer.alloc(4)
l.writeInt32LE(this[arg].length, 0)
buffers.push(l, Buffer.concat(this[arg].map(x => argToBytes(x, argsConfig[arg].type))))
} else if (argsConfig[arg].flagIndicator) {
if (!Object.values(argsConfig)
.some((f) => f.isFlag)) {
buffers.push(Buffer.alloc(4))
} else {
let flagCalculate = 0
for (const f in argsConfig) {
if (argsConfig[f].isFlag) {
if ((this[f]===false&&argsConfig[f].type==="true") || this[f]===undefined || this[f]===null) {
flagCalculate |= 0
} else {
flagCalculate |= 1 << argsConfig[f].flagIndex
}
}
}
const f = Buffer.alloc(4)
f.writeUInt32LE(flagCalculate, 0)
buffers.push(f)
}
} else {
buffers.push(argToBytes(this[arg], argsConfig[arg].type))
if (this[arg] && typeof this[arg].getBytes === 'function') {
let boxed = (argsConfig[arg].type.charAt(argsConfig[arg].type.indexOf('.') + 1))
boxed = boxed === boxed.toUpperCase()
if (!boxed) {
buffers.shift()
}
}
}
}
}
return Buffer.concat(buffers)
}
readResult(reader) {
if (classesType !== 'request') {
throw new Error('`readResult()` called for non-request instance')
}
const m = result.match(/Vector<(int|long)>/)
if (m) {
reader.readInt()
let temp = []
let len = reader.readInt()
if (m[1] === 'int') {
for (let i = 0; i < len; i++) {
temp.push(reader.readInt())
}
} else {
for (let i = 0; i < len; i++) {
temp.push(reader.readLong())
}
}
return temp
} else {
return reader.tgReadObject()
}
}
/*CONTEST
async resolve(client, utils) {
if (classesType !== 'request') {
throw new Error('`resolve()` called for non-request instance')
}
for (const arg in argsConfig) {
if (argsConfig.hasOwnProperty(arg)) {
if (!AUTO_CASTS.has(argsConfig[arg].type)) {
if (!NAMED_AUTO_CASTS.has(`${argsConfig[arg].name},${argsConfig[arg].type}`)) {
continue
}
}
if (argsConfig[arg].isFlag) {
if (!this[arg]) {
continue
}
}
if (argsConfig[arg].isVector) {
const temp = []
for (const x of this[arg]) {
temp.push(await getInputFromResolve(utils, client, x, argsConfig[arg].type))
}
this[arg] = temp
} else {
this[arg] = await getInputFromResolve(utils, client, this[arg], argsConfig[arg].type)
}
}
}
}*/
}
if (namespace) {
if (!classes[namespace]) {
classes[namespace] = {}
}
classes[namespace][name] = VirtualClass
} else {
classes[name] = VirtualClass
}
}
return classes
}
module.exports = buildApiFromTlSchema()