feat(tl): added types for reactions, also added ability to augment schema from file
This commit is contained in:
parent
2f1c8548a0
commit
d4f07aa07c
9 changed files with 190 additions and 109 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
> TL schema and related utils used for mtqt.
|
> TL schema and related utils used for mtqt.
|
||||||
|
|
||||||
Generated from TL layer **131** (last updated on 24.07.2021).
|
Generated from TL layer **131** (last updated on 25.07.2021).
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
24
packages/tl/scripts/_prepend.tl
Normal file
24
packages/tl/scripts/_prepend.tl
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// this file is sourced *before* actual schema.
|
||||||
|
// should be used for types that are not currently added to the schema,
|
||||||
|
// but seem to work
|
||||||
|
// in case of conflict, type from main schema is preferred
|
||||||
|
|
||||||
|
// reactions
|
||||||
|
// taken from official docs coz why not lol
|
||||||
|
// ctor ids will be generated by the codegen
|
||||||
|
// *does* work with layer 131 (as of 25.07.21)
|
||||||
|
|
||||||
|
---types---
|
||||||
|
|
||||||
|
updateMessageReactions peer:Peer msg_id:int reactions:MessageReactions = Update;
|
||||||
|
messageReactions flags:# min:flags.0?true results:Vector<ReactionCount> = MessageReactions;
|
||||||
|
reactionCount flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
|
||||||
|
|
||||||
|
messageReactionsList flags:# count:int reactions:Vector<MessageUserReaction> users:Vector<User> next_offset:flags.0?string = MessageReactionsList;
|
||||||
|
messageUserReaction user_id:int reaction:string = MessageUserReaction;
|
||||||
|
|
||||||
|
---functions---
|
||||||
|
|
||||||
|
messages.sendReaction flags:# peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
|
||||||
|
messages.getMessagesReactions peer:InputPeer id:Vector<int> = Updates;
|
||||||
|
messages.getMessageReactionsList flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = MessageReactionsList;
|
5
packages/tl/scripts/append.tl
Normal file
5
packages/tl/scripts/append.tl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// this file is sourced *after* actual schema.
|
||||||
|
// should be used for types that *are* in the schema,
|
||||||
|
// but for one reason or another incorrect or somehow invalid in schema.
|
||||||
|
// (i.e. should be used very rarely)
|
||||||
|
// in case of conflict, type from this schema is preferred
|
|
@ -48,7 +48,7 @@ const camelToSnake = (s) => {
|
||||||
}
|
}
|
||||||
const snakeToPascal = (s) => camelToPascal(snakeToCamel(s))
|
const snakeToPascal = (s) => camelToPascal(snakeToCamel(s))
|
||||||
|
|
||||||
const signedInt32ToUnsigned = (val) => (val < 0 ? val + 0x100000000 : val)
|
const signedInt32ToUnsigned = (val) => val >>> 0
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createWriter,
|
createWriter,
|
||||||
|
|
|
@ -12,7 +12,7 @@ const { applyDescriptionsFile } = require('./process-descriptions-yaml')
|
||||||
const yaml = require('js-yaml')
|
const yaml = require('js-yaml')
|
||||||
const { snakeToCamel, signedInt32ToUnsigned } = require('./common')
|
const { snakeToCamel, signedInt32ToUnsigned } = require('./common')
|
||||||
const { asyncPool } = require('eager-async-pool')
|
const { asyncPool } = require('eager-async-pool')
|
||||||
const { mergeSchemas } = require('./merge-schemas')
|
const { mergeSchemas, stringifyType } = require('./merge-schemas')
|
||||||
const CRC32 = require('crc-32')
|
const CRC32 = require('crc-32')
|
||||||
|
|
||||||
const SingleRegex = /^(.+?)(?:#([0-f]{1,8}))?(?: \?)?(?: {(.+?:.+?)})? ((?:.+? )*)= (.+);$/
|
const SingleRegex = /^(.+?)(?:#([0-f]{1,8}))?(?: \?)?(?: {(.+?:.+?)})? ((?:.+? )*)= (.+);$/
|
||||||
|
@ -87,7 +87,16 @@ function getJSType(typ, argName) {
|
||||||
return normalizeGenerics(typ)
|
return normalizeGenerics(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertTlToJson(tlText, tlType, silent = false) {
|
async function convertTlToJson(tlText, tlType, silent = false) {
|
||||||
|
if (tlType === 'api') {
|
||||||
|
tlText =
|
||||||
|
fs.readFileSync(path.join(__dirname, '_prepend.tl'), 'utf8')
|
||||||
|
+ '\n---$start-main---\n'
|
||||||
|
+ tlText
|
||||||
|
+ '\n---$start-append---\n'
|
||||||
|
+ fs.readFileSync(path.join(__dirname, 'append.tl'), 'utf8')
|
||||||
|
}
|
||||||
|
|
||||||
let lines = tlText.split('\n')
|
let lines = tlText.split('\n')
|
||||||
let pos = 0
|
let pos = 0
|
||||||
let line = lines[0].trim()
|
let line = lines[0].trim()
|
||||||
|
@ -111,6 +120,8 @@ function convertTlToJson(tlText, tlType, silent = false) {
|
||||||
extends: null,
|
extends: null,
|
||||||
blankLines: 0,
|
blankLines: 0,
|
||||||
stop: false,
|
stop: false,
|
||||||
|
part: 'prepend',
|
||||||
|
source: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unions = {}
|
const unions = {}
|
||||||
|
@ -133,6 +144,11 @@ function convertTlToJson(tlText, tlType, silent = false) {
|
||||||
state.type = 'class'
|
state.type = 'class'
|
||||||
return nextLine()
|
return nextLine()
|
||||||
}
|
}
|
||||||
|
if (line && line.startsWith('---$start-')) {
|
||||||
|
state.part = line.split('$start-')[1].split('-')[0]
|
||||||
|
state.type = 'class'
|
||||||
|
return nextLine()
|
||||||
|
}
|
||||||
if (!silent)
|
if (!silent)
|
||||||
process.stdout.write(
|
process.stdout.write(
|
||||||
`[${pad(pos)}/${lines.length}] Processing ${tlType}.tl..\r`
|
`[${pad(pos)}/${lines.length}] Processing ${tlType}.tl..\r`
|
||||||
|
@ -166,7 +182,7 @@ function convertTlToJson(tlText, tlType, silent = false) {
|
||||||
|
|
||||||
const match = SingleRegex.exec(line)
|
const match = SingleRegex.exec(line)
|
||||||
if (!match) {
|
if (!match) {
|
||||||
console.warn('Regex failed on:\n"' + line + '"')
|
console.warn(`Regex failed on:\n${line}`)
|
||||||
} else {
|
} else {
|
||||||
let [, fullName, typeId, generics, args, type] = match
|
let [, fullName, typeId, generics, args, type] = match
|
||||||
if (fullName in _types || fullName === 'vector') {
|
if (fullName in _types || fullName === 'vector') {
|
||||||
|
@ -189,7 +205,7 @@ function convertTlToJson(tlText, tlType, silent = false) {
|
||||||
.replace(/ +/g, ' ')
|
.replace(/ +/g, ' ')
|
||||||
.trim()
|
.trim()
|
||||||
)
|
)
|
||||||
) + ''
|
).toString(16)
|
||||||
}
|
}
|
||||||
|
|
||||||
args = args.trim()
|
args = args.trim()
|
||||||
|
@ -198,22 +214,23 @@ function convertTlToJson(tlText, tlType, silent = false) {
|
||||||
? args.split(' ').map((j) => j.split(':'))
|
? args.split(' ').map((j) => j.split(':'))
|
||||||
: []
|
: []
|
||||||
|
|
||||||
if (state.type === 'class') {
|
|
||||||
let [namespace, name] = fullName.split('.')
|
let [namespace, name] = fullName.split('.')
|
||||||
if (!name) {
|
if (!name) {
|
||||||
name = namespace
|
name = namespace
|
||||||
namespace = '$root'
|
namespace = '$root'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.type === 'class') {
|
||||||
if (!unions[type]) unions[type] = []
|
if (!unions[type]) unions[type] = []
|
||||||
unions[type].push(
|
unions[type].push(
|
||||||
namespace === '$root' ? name : namespace + '.' + name
|
namespace === '$root' ? name : namespace + '.' + name
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let r = {
|
let r = {
|
||||||
name,
|
name: state.type === 'class' ? name : snakeToCamel(name),
|
||||||
id: parseInt(typeId, 16),
|
id: parseInt(typeId, 16),
|
||||||
type: getJSType(type),
|
[state.type === 'class' ? 'type' : 'returns']: getJSType(type),
|
||||||
arguments: [],
|
arguments: [],
|
||||||
}
|
}
|
||||||
if (generics) {
|
if (generics) {
|
||||||
|
@ -250,55 +267,90 @@ function convertTlToJson(tlText, tlType, silent = false) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getNamespace(namespace).classes.push(r)
|
// check for overrides/conflicts
|
||||||
|
if (fullName in state.source) {
|
||||||
|
// this object was already met
|
||||||
|
const source = state.source[fullName]
|
||||||
|
const collection = getNamespace(namespace)[
|
||||||
|
state.type === 'class' ? 'classes' : 'methods'
|
||||||
|
]
|
||||||
|
const oldIdx = collection.findIndex(
|
||||||
|
(it) => it.name === r.name
|
||||||
|
)
|
||||||
|
|
||||||
|
if (source === state.part) {
|
||||||
|
console.log(
|
||||||
|
'warn: %s was met >1 times in %s part, was overridden',
|
||||||
|
fullName,
|
||||||
|
source
|
||||||
|
)
|
||||||
|
collection.splice(oldIdx, 1)
|
||||||
} else {
|
} else {
|
||||||
let [namespace, name] = fullName.split('.')
|
const former = collection[oldIdx]
|
||||||
if (!name) {
|
|
||||||
name = namespace
|
const latter = r
|
||||||
namespace = '$root'
|
|
||||||
|
if (former.id === latter.id) {
|
||||||
|
console.log(
|
||||||
|
'warn: %s was met in %s, then in %s, was overridden',
|
||||||
|
fullName,
|
||||||
|
source,
|
||||||
|
state.part
|
||||||
|
)
|
||||||
|
collection.splice(oldIdx, 1)
|
||||||
|
} else {
|
||||||
|
const rl = require('readline').createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
})
|
||||||
|
|
||||||
|
const input = (q) =>
|
||||||
|
new Promise((res) => rl.question(q, res))
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`!! Conflict on %s !! First met in %s, then in %s.`,
|
||||||
|
fullName,
|
||||||
|
source,
|
||||||
|
state.part
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
'Option A (%s): %s',
|
||||||
|
source,
|
||||||
|
stringifyType(former, namespace)
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
'Option B (%s): %s',
|
||||||
|
state.part,
|
||||||
|
stringifyType(latter, namespace)
|
||||||
|
)
|
||||||
|
|
||||||
|
let keep
|
||||||
|
while (true) {
|
||||||
|
keep = await input('Which to keep? [A/B] > ')
|
||||||
|
keep = keep.toUpperCase()
|
||||||
|
|
||||||
|
if (keep !== 'A' && keep !== 'B') {
|
||||||
|
console.log('Invalid input! Please type A or B')
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = {
|
break
|
||||||
name: snakeToCamel(name),
|
|
||||||
id: parseInt(typeId, 16),
|
|
||||||
returns: getJSType(type),
|
|
||||||
arguments: [],
|
|
||||||
}
|
}
|
||||||
if (generics) {
|
|
||||||
r.generics = generics.split(',').map((it) => {
|
rl.close()
|
||||||
let [name, superClass] = it.split(':')
|
|
||||||
return { name, super: getJSType(superClass) }
|
if (keep === 'A') {
|
||||||
})
|
nextLine()
|
||||||
}
|
continue
|
||||||
if (args.length) {
|
} else {
|
||||||
r.arguments = args.map(([name, typ]) => {
|
collection.splice(oldIdx, 1)
|
||||||
let [predicate, type] = typ.split('?')
|
|
||||||
if (!type) {
|
|
||||||
return {
|
|
||||||
name: snakeToCamel(name),
|
|
||||||
type: getJSType(
|
|
||||||
typ,
|
|
||||||
tlType === 'mtproto'
|
|
||||||
? `mt_${fullName}#${name}`
|
|
||||||
: ''
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
name: snakeToCamel(name),
|
|
||||||
type: getJSType(
|
|
||||||
type,
|
|
||||||
tlType === 'mtproto'
|
|
||||||
? `mt_${fullName}#${name}`
|
|
||||||
: ''
|
|
||||||
),
|
|
||||||
optional: true,
|
|
||||||
predicate,
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
getNamespace(namespace).methods.push(r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.source[fullName] = state.part
|
||||||
|
getNamespace(namespace)[state.type === 'class' ? 'classes' : 'methods'].push(r)
|
||||||
}
|
}
|
||||||
nextLine()
|
nextLine()
|
||||||
}
|
}
|
||||||
|
@ -518,7 +570,7 @@ async function main() {
|
||||||
.then((json) => convertJsonToTl(json))
|
.then((json) => convertJsonToTl(json))
|
||||||
let ret = {}
|
let ret = {}
|
||||||
|
|
||||||
ret.mtproto = convertTlToJson(mtprotoTl, 'mtproto')
|
ret.mtproto = await convertTlToJson(mtprotoTl, 'mtproto')
|
||||||
|
|
||||||
console.log('[i] Fetching api.tl from tdesktop')
|
console.log('[i] Fetching api.tl from tdesktop')
|
||||||
const apiTlDesktop = await fetch(
|
const apiTlDesktop = await fetch(
|
||||||
|
@ -555,12 +607,12 @@ async function main() {
|
||||||
apiDesktopLayer > apiTdlibLayer ? apiDesktopLayer : apiTdlibLayer
|
apiDesktopLayer > apiTdlibLayer ? apiDesktopLayer : apiTdlibLayer
|
||||||
|
|
||||||
ret.apiLayer = newerLayer + ''
|
ret.apiLayer = newerLayer + ''
|
||||||
ret.api = convertTlToJson(newer, 'api')
|
ret.api = await convertTlToJson(newer, 'api')
|
||||||
} else {
|
} else {
|
||||||
console.log('[i] Merging schemas...')
|
console.log('[i] Merging schemas...')
|
||||||
|
|
||||||
const first = convertTlToJson(apiTlTdlib, 'api')
|
const first = await convertTlToJson(apiTlTdlib, 'api')
|
||||||
const second = convertTlToJson(apiTlDesktop, 'api')
|
const second = await convertTlToJson(apiTlDesktop, 'api')
|
||||||
|
|
||||||
const onConflict =
|
const onConflict =
|
||||||
apiDesktopLayer === apiTdlibLayer
|
apiDesktopLayer === apiTdlibLayer
|
||||||
|
|
|
@ -196,7 +196,7 @@ async function mergeSchemas(a, b, conflict = null) {
|
||||||
if (rl) rl.close()
|
if (rl) rl.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { mergeSchemas }
|
module.exports = { mergeSchemas, stringifyType }
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const { expect } = require('chai')
|
const { expect } = require('chai')
|
||||||
|
|
|
@ -121,7 +121,7 @@ async function parseRemoteTl(file, commit) {
|
||||||
return {
|
return {
|
||||||
layer,
|
layer,
|
||||||
content,
|
content,
|
||||||
tl: convertToArrays(convertTlToJson(content, 'api', true)),
|
tl: convertToArrays(await convertTlToJson(content, 'api', true)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ async function main() {
|
||||||
|
|
||||||
for (const l of layers) {
|
for (const l of layers) {
|
||||||
const tl = await fetchFromLayer(l)
|
const tl = await fetchFromLayer(l)
|
||||||
const data = convertTlToJson(tl, 'api', true)
|
const data = await convertTlToJson(tl, 'api', true)
|
||||||
|
|
||||||
await fs.promises.writeFile(
|
await fs.promises.writeFile(
|
||||||
path.join(
|
path.join(
|
||||||
|
|
Loading…
Reference in a new issue