errors.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. const fs = require('fs')
  2. const csvParse = require('csv-parse/lib/sync')
  3. const { snakeToCamelCase, variableSnakeToCamelCase } = require('../utils')
  4. const KNOWN_BASE_CLASSES = {
  5. 303: 'InvalidDCError',
  6. 400: 'BadRequestError',
  7. 401: 'UnauthorizedError',
  8. 403: 'ForbiddenError',
  9. 404: 'NotFoundError',
  10. 406: 'AuthKeyError',
  11. 420: 'FloodError',
  12. 500: 'ServerError',
  13. 503: 'TimedOutError',
  14. }
  15. /**
  16. * Gets the corresponding class name for the given error code,
  17. * this either being an integer (thus base error name) or str.
  18. */
  19. const getClassName = (errorCode) => {
  20. if (typeof errorCode === 'number') {
  21. return KNOWN_BASE_CLASSES[Math.abs(errorCode)] || 'RPCError' + errorCode.toString().replace('-', 'Neg')
  22. }
  23. return snakeToCamelCase(
  24. errorCode
  25. .replace('FIRSTNAME', 'FIRST_NAME')
  26. .replace('SLOWMODE', 'SLOW_MODE')
  27. .toLowerCase(),
  28. 'Error',
  29. )
  30. }
  31. class TelegramError {
  32. constructor(codes, name, description) {
  33. // TODO Some errors have the same name but different integer codes
  34. // Should these be split into different files or doesn't really matter?
  35. // Telegram isn't exactly consistent with returned errors anyway.
  36. [this.intCode] = codes
  37. this.stringCode = name
  38. this.subclass = getClassName(codes[0])
  39. this.subclassExists = Math.abs(codes[0]) in KNOWN_BASE_CLASSES
  40. this.description = description
  41. this.hasCaptures = name.includes('_X')
  42. if (this.hasCaptures) {
  43. this.name = variableSnakeToCamelCase(getClassName(name.replace('_X', '')))
  44. this.pattern = variableSnakeToCamelCase(name.replace('_X', '_(\\d+)'))
  45. this.captureName = variableSnakeToCamelCase(description.match(/{(\w+)}/)[1])
  46. } else {
  47. this.name = variableSnakeToCamelCase(getClassName(name))
  48. this.pattern = variableSnakeToCamelCase(name)
  49. this.captureName = null
  50. }
  51. }
  52. }
  53. /**
  54. * Parses the input CSV file with columns (name, error codes, description)
  55. * and yields `Error` instances as a result.
  56. */
  57. const parseErrors = function* (csvFile) {
  58. const f = csvParse(fs.readFileSync(csvFile, { encoding: 'utf-8' })).slice(1)
  59. for (let line = 0; line < f.length; line++) {
  60. if (f[line].length !== 3) {
  61. throw new Error(`Columns count mismatch, unquoted comma in desc? (line ${line + 2})`)
  62. }
  63. let [name, codes, description] = f[line]
  64. codes =
  65. codes === '' ?
  66. [400] :
  67. codes.split(' ').map((x) => {
  68. if (isNaN(x)) {
  69. throw new Error(`Not all codes are integers (line ${line + 2})`)
  70. }
  71. return Number(x)
  72. })
  73. yield new TelegramError(codes, name, description)
  74. }
  75. }
  76. module.exports = {
  77. KNOWN_BASE_CLASSES,
  78. TelegramError,
  79. parseErrors,
  80. }