Help in creating another Common Voice Telegram bot

Hello everyone! It has been 3 years since I originally planned to run a public Common Voice Telegram bot, especially when Common Voice was badly supported in iOS web browsers. I’ve lost the original source code behind it, and that’s why I stopped my development without any further announcement. It was full of callback hell and JavaScript mess.

This year I decided to develop this project from scratch, as I do notice that many non-English languages are starting to lack of contributors than it was in 3 years ago. Sure, while the Common Voice website already allows people to set their goals and be reminded via email, utilizing a Telegram bot with periodic notification (see OSM Streak for example) could increase participation from even more users from Telegram.

The project source code is on https://gitlab.com/reinhart1010/mozilla-common-voice-telegram-bot, which is now built with TypeScript and a better knowledge of async functions. Hooray!

However, the biggest issue of all is communicating with the REST API, as things, of course, have already changed over the years. Sending clip validation status (i.e. the “yes” and “no” buttons in validating clips) now always returns a 401, as shown in the Axios error dump below.

[AxiosError: Request failed with status code 401] {
  code: 'ERR_BAD_REQUEST',
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [Function: httpAdapter],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function] },
    validateStatus: [Function: validateStatus],
    headers: {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/json',
      'User-Agent': 'CapsCommonVoiceBot/1.0 (+https://reinhart1010.id)',
      Authorization: 'Basic MWQxNDFkM2ItNmVlYy00ZDY4LWI0MTUtZDk2MzY5N2Q1ZTgzOjAyMGZiMDUxMGVlMDQ4MDkxMGZjMDkzMDdkMDhhMGU0MDRmMDllMDM3MDY0MGYzMDQzMGZkMDU2MGY2',
      'Content-Length': 16
    },
    url: 'https://commonvoice.mozilla.org/api/v1/en/clips/31148933/votes',
    method: 'post',
    data: '{"isValid":true}'
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      prefinish: [Function: requestOnPrefinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    _contentLength: null,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: false,
    socket: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: 'commonvoice.mozilla.org',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 10,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'commonvoice.mozilla.org',
      _readableState: [ReadableState],
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [Circular *1],
      [Symbol(res)]: [TLSWrap],
      [Symbol(verified)]: true,
      [Symbol(pendingSession)]: null,
      [Symbol(async_id_symbol)]: 10713,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: false,
      [Symbol(kSetKeepAlive)]: true,
      [Symbol(kSetKeepAliveInitialDelay)]: 60,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(connect-options)]: [Object]
    },
    _header: 'POST /api/v1/en/clips/31148933/votes HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Content-Type: application/json\r\n' +
      'User-Agent: CapsCommonVoiceBot/1.0 (+https://reinhart1010.id)\r\n' +
      'Authorization: Basic MWQxNDFkM2ItNmVlYy00ZDY4LWI0MTUtZDk2MzY5N2Q1ZTgzOjAyMGZiMDUxMGVlMDQ4MDkxMGZjMDkzMDdkMDhhMGU0MDRmMDllMDM3MDY0MGYzMDQzMGZkMDU2MGY2\r\n' +
      'Content-Length: 16\r\n' +
      'Host: commonvoice.mozilla.org\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: nop],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype],
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 2,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    path: '/api/v1/en/clips/31148933/votes',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 4,
      _maxListeners: undefined,
      socket: [TLSSocket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      rawHeaders: [Array],
      rawTrailers: [],
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 401,
      statusMessage: 'Unauthorized',
      client: [TLSSocket],
      _consuming: false,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'https://commonvoice.mozilla.org/api/v1/en/clips/31148933/votes',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(kHeaders)]: [Object],
      [Symbol(kHeadersCount)]: 22,
      [Symbol(kTrailers)]: null,
      [Symbol(kTrailersCount)]: 0
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: 'commonvoice.mozilla.org',
    protocol: 'https:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 16,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'https://commonvoice.mozilla.org/api/v1/en/clips/31148933/votes',
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      'user-agent': [Array],
      authorization: [Array],
      'content-length': [Array],
      host: [Array]
    },
    [Symbol(kUniqueHeaders)]: null
  },
  response: {
    status: 401,
    statusText: 'Unauthorized',
    headers: {
      'content-type': 'text/plain; charset=utf-8',
      date: 'Tue, 09 Aug 2022 14:48:57 GMT',
      etag: 'W/"c-dAuDFQrdjS3hezqxDTNgW7AOlYk"',
      'strict-transport-security': 'max-age=31536000',
      vary: 'Accept-Encoding',
      'x-content-type-options': 'nosniff',
      'x-frame-options': 'DENY',
      'x-powered-by': 'Express',
      'x-xss-protection': '1; mode=block',
      'content-length': '12',
      connection: 'Close'
    },
    config: {
      transitional: [Object],
      adapter: [Function: httpAdapter],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: [Object],
      validateStatus: [Function: validateStatus],
      headers: [Object],
      url: 'https://commonvoice.mozilla.org/api/v1/en/clips/31148933/votes',
      method: 'post',
      data: '{"isValid":true}'
    },
    request: <ref *1> ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: null,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [TLSSocket],
      _header: 'POST /api/v1/en/clips/31148933/votes HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/json\r\n' +
        'User-Agent: CapsCommonVoiceBot/1.0 (+https://reinhart1010.id)\r\n' +
        'Authorization: Basic MWQxNDFkM2ItNmVlYy00ZDY4LWI0MTUtZDk2MzY5N2Q1ZTgzOjAyMGZiMDUxMGVlMDQ4MDkxMGZjMDkzMDdkMDhhMGU0MDRmMDllMDM3MDY0MGYzMDQzMGZkMDU2MGY2\r\n' +
        'Content-Length: 16\r\n' +
        'Host: commonvoice.mozilla.org\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/api/v1/en/clips/31148933/votes',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'commonvoice.mozilla.org',
      protocol: 'https:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(kUniqueHeaders)]: null
    },
    data: 'Unauthorized'
  }
}

Oh right, I do intend to pseudo-randomly generate user IDs (based on UUIDv5) and authentication tokens for each Telegram user. However, no matter what I do (including trying to copy exactly as the Common Voice web source code does) still do not solve the problem.

Are there any solution to this authentication errors?

1 Like

AFAIK, it is a security feature. You should contact the team for this (@heyhillary).

Welcome back to the community,

My name is Hillary - I’m the community manager here!

I’m going to share this with our developer and product lead to assess the legal , governance and technical requirements to understand if we can implement your idea.

We appreciate the time and idea you have invested in !