2021-04-08 12:19:38 +03:00
|
|
|
const { createWriter, snakeToCamel, camelToPascal } = require('./common')
|
|
|
|
const fetch = require('node-fetch')
|
|
|
|
const csvParser = require('csv-parser')
|
2021-04-11 19:40:26 +03:00
|
|
|
const fs = require('fs')
|
|
|
|
const path = require('path')
|
2021-04-08 12:19:38 +03:00
|
|
|
|
|
|
|
const ts = createWriter('../errors.d.ts')
|
|
|
|
const js = createWriter('../errors.js')
|
|
|
|
|
|
|
|
const baseErrors = [
|
|
|
|
{
|
|
|
|
codes: '400',
|
|
|
|
name: 'BAD_REQUEST',
|
|
|
|
description:
|
|
|
|
'The query contains errors. In the event that a request was created using a form ' +
|
|
|
|
'and contains user generated data, the user should be notified that the data must ' +
|
|
|
|
'be corrected before the query is repeated',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
codes: '401',
|
|
|
|
name: 'UNAUTHORIZED',
|
|
|
|
description:
|
|
|
|
'There was an unauthorized attempt to use functionality available only to authorized users.',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
codes: '403',
|
|
|
|
name: 'FORBIDDEN',
|
|
|
|
description:
|
|
|
|
'Privacy violation. For example, an attempt to write a message ' +
|
|
|
|
'to someone who has blacklisted the current user.',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
codes: '404',
|
|
|
|
name: 'NOT_FOUND',
|
|
|
|
description:
|
|
|
|
'An attempt to invoke a non-existent object, such as a method.',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
codes: '420',
|
|
|
|
name: 'FLOOD',
|
|
|
|
description:
|
|
|
|
'The maximum allowed number of attempts to invoke the given method' +
|
|
|
|
'with the given input parameters has been exceeded. For example, in an' +
|
|
|
|
'attempt to request a large number of text messages (SMS) for the same' +
|
|
|
|
'phone number.',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
codes: '303',
|
|
|
|
name: 'SEE_OTHER',
|
|
|
|
description:
|
|
|
|
'The request must be repeated, but directed to a different data center',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
codes: '406',
|
|
|
|
name: 'NOT_ACCEPTABLE',
|
|
|
|
description:
|
|
|
|
'Similar to 400 BAD_REQUEST, but the app should not display any error messages to user ' +
|
|
|
|
'in UI as a result of this response. The error message will be delivered via ' +
|
|
|
|
'updateServiceNotification instead.',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
codes: '500',
|
|
|
|
name: 'INTERNAL',
|
|
|
|
description:
|
|
|
|
'An internal server error occurred while a request was being processed; ' +
|
|
|
|
'for example, there was a disruption while accessing a database or file storage.',
|
2021-06-08 00:37:13 +03:00
|
|
|
}
|
2021-04-08 12:19:38 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
const inheritanceTable = {
|
|
|
|
400: 'BadRequestError',
|
|
|
|
401: 'UnauthorizedError',
|
|
|
|
403: 'ForbiddenError',
|
|
|
|
404: 'NotFoundError',
|
|
|
|
420: 'FloodError',
|
|
|
|
303: 'SeeOtherError',
|
|
|
|
406: 'NotAcceptableError',
|
|
|
|
500: 'InternalError',
|
|
|
|
}
|
|
|
|
|
2021-05-16 02:50:43 +03:00
|
|
|
const customErrors = [
|
|
|
|
{
|
|
|
|
virtual: true,
|
|
|
|
name: 'RPC_TIMEOUT',
|
|
|
|
codes: '408',
|
2021-06-08 00:37:13 +03:00
|
|
|
description: 'Timeout of {ms} ms exceeded',
|
2021-07-06 02:24:58 +03:00
|
|
|
},
|
|
|
|
{
|
|
|
|
virtual: true,
|
|
|
|
name: 'MESSAGE_NOT_FOUND',
|
|
|
|
codes: '404',
|
|
|
|
description: 'Message was not found'
|
2021-05-16 02:50:43 +03:00
|
|
|
}
|
|
|
|
]
|
|
|
|
|
2021-04-08 12:19:38 +03:00
|
|
|
async function main() {
|
|
|
|
// relying on slightly more exhaustive than official docs errors.csv from telethon
|
|
|
|
const csvText = await fetch(
|
|
|
|
'https://raw.githubusercontent.com/LonamiWebs/Telethon/master/telethon_generator/data/errors.csv'
|
|
|
|
).then((i) => i.body)
|
|
|
|
|
|
|
|
const csv = await new Promise((resolve, reject) => {
|
|
|
|
const ret = []
|
|
|
|
csvText
|
|
|
|
.pipe(csvParser())
|
|
|
|
.on('data', (it) => ret.push(it))
|
|
|
|
.on('end', () => resolve(ret))
|
|
|
|
.on('error', reject)
|
|
|
|
})
|
|
|
|
|
2021-05-16 02:50:43 +03:00
|
|
|
csv.push(...customErrors)
|
|
|
|
|
2021-04-08 12:19:38 +03:00
|
|
|
js.write(`// This file was auto-generated. Do not edit!
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
class RpcError extends Error {
|
|
|
|
constructor(code, text, description) {
|
|
|
|
super(description);
|
|
|
|
this.code = code;
|
|
|
|
this.text = text;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exports.RpcError = RpcError;`)
|
|
|
|
ts.write(`// This file was auto-generated. Do not edit!
|
|
|
|
export declare class RpcError extends Error {
|
|
|
|
code: number;
|
|
|
|
text: string;
|
|
|
|
constructor (code: number, text: string, description?: string);
|
|
|
|
}`)
|
|
|
|
baseErrors.forEach((it) => (it.base = true))
|
|
|
|
const allErrors = [...baseErrors, ...csv]
|
2021-04-11 19:40:26 +03:00
|
|
|
|
2021-06-08 00:37:13 +03:00
|
|
|
fs.writeFileSync(
|
|
|
|
path.join(__dirname, '../raw-errors.json'),
|
|
|
|
JSON.stringify(allErrors)
|
|
|
|
)
|
2021-04-11 19:40:26 +03:00
|
|
|
|
2021-04-08 12:19:38 +03:00
|
|
|
allErrors.forEach((err) => {
|
|
|
|
let hasArgument =
|
|
|
|
err.description && err.description.match(/{([a-z_]+)}/i)
|
|
|
|
let argName = hasArgument ? snakeToCamel(hasArgument[1]) : ''
|
|
|
|
if (err.name[0].match(/[0-9]/)) err.clsname = '_' + err.name
|
|
|
|
else err.clsname = err.name
|
|
|
|
err.clsname = err.clsname.replace(/_X_|_X$|^X_/, (i) =>
|
|
|
|
i === '_X_' ? '_' : ''
|
|
|
|
)
|
|
|
|
const className =
|
|
|
|
camelToPascal(snakeToCamel(err.clsname.toLowerCase())) + 'Error'
|
|
|
|
err.fclsname = className
|
|
|
|
|
|
|
|
const descrCode = JSON.stringify(err.description).replace(
|
|
|
|
/{([a-z_]+)}/gi,
|
|
|
|
(_, $1) => `" + ${snakeToCamel($1)} + "`
|
|
|
|
)
|
|
|
|
err.code = err.codes.split(' ')[0]
|
|
|
|
err.inherits = err.base
|
|
|
|
? 'RpcError'
|
|
|
|
: inheritanceTable[err.code] || 'RpcError'
|
|
|
|
if (err.base) {
|
|
|
|
js.write(`class ${className} extends RpcError {
|
|
|
|
constructor(name, description) {
|
|
|
|
super(${err.code}, name, description);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exports.${className} = ${className}`)
|
|
|
|
} else {
|
|
|
|
js.write(`class ${className} extends ${err.inherits} {
|
|
|
|
constructor(${argName}) {
|
|
|
|
super(${err.inherits === 'RpcError' ? `${err.code}, ` : ''}'${
|
|
|
|
err.name
|
|
|
|
}', ${descrCode});${
|
|
|
|
hasArgument
|
|
|
|
? `
|
|
|
|
this.${argName} = ${argName};`
|
|
|
|
: ''
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exports.${className} = ${className}`)
|
|
|
|
}
|
|
|
|
ts.comment(err.description)
|
|
|
|
if (err.base) {
|
|
|
|
ts.write(`export declare class ${className} extends RpcError {
|
|
|
|
constructor (name: string, description: string);
|
|
|
|
}`)
|
|
|
|
} else {
|
|
|
|
ts.write(`export declare class ${className} extends ${
|
|
|
|
err.inherits
|
|
|
|
} {${
|
|
|
|
hasArgument
|
|
|
|
? `
|
|
|
|
${argName}: number;`
|
|
|
|
: ''
|
|
|
|
}
|
|
|
|
constructor (${hasArgument ? argName + ': number' : ''});
|
|
|
|
}`)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
ts.write(
|
|
|
|
'export declare function createRpcErrorFromTl (obj: any): RpcError;'
|
|
|
|
)
|
|
|
|
|
|
|
|
js.write('const _staticNameErrors = {')
|
|
|
|
js.tab()
|
2021-06-08 00:37:13 +03:00
|
|
|
csv.filter(
|
|
|
|
(i) => !i.virtual && !i.name.match(/_X_|_X$|^X_/)
|
|
|
|
).forEach((err) => js.write(`'${err.name}': ${err.fclsname},`))
|
2021-04-08 12:19:38 +03:00
|
|
|
js.write(`'Timeout': TimeoutError,`)
|
|
|
|
js.untab()
|
|
|
|
js.write('};')
|
2021-06-08 00:37:13 +03:00
|
|
|
|
|
|
|
js.write('const _baseCodeErrors = {')
|
|
|
|
js.tab()
|
|
|
|
Object.entries(inheritanceTable).forEach(([code, error]) => {
|
|
|
|
js.write(`${code}: ${error},`)
|
|
|
|
})
|
|
|
|
js.untab()
|
|
|
|
js.write('};')
|
|
|
|
|
2021-04-08 12:19:38 +03:00
|
|
|
js.write(`exports.createRpcErrorFromTl = function (obj) {
|
|
|
|
if (obj.errorMessage in _staticNameErrors) return new _staticNameErrors[obj.errorMessage]();
|
|
|
|
|
|
|
|
let match;
|
|
|
|
${allErrors
|
2021-05-16 02:50:43 +03:00
|
|
|
.filter((i) => !i.virtual && !!i.name.match(/_X_|_X$|^X_/))
|
2021-04-08 12:19:38 +03:00
|
|
|
.map(
|
|
|
|
(i) =>
|
|
|
|
` if ((match = obj.errorMessage.match(/${i.name.replace(
|
|
|
|
/_X_|_X$|^X_/g,
|
|
|
|
(i) => i.replace('X', '(\\d+)')
|
|
|
|
)}/)) != null) return new ${i.fclsname}(parseInt(match[1]));`
|
|
|
|
)
|
|
|
|
.join('\n')}
|
|
|
|
|
2021-06-08 00:37:13 +03:00
|
|
|
if (obj.errorCode in _baseCodeErrors) return new _baseCodeErrors[obj.errorCode](obj.errorMessage);
|
|
|
|
|
2021-04-08 12:19:38 +03:00
|
|
|
return new RpcError(obj.errorCode, obj.errorMessage);
|
|
|
|
}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
main().catch(console.error)
|