From 544e5a68dc8349afdec29dff1fdb37802157a3fc Mon Sep 17 00:00:00 2001 From: Alina Sireneva Date: Tue, 3 Oct 2023 02:49:53 +0300 Subject: [PATCH] feat: opt-in error reporting --- packages/core/src/base-client.ts | 13 +++++++++++ packages/core/src/network/network-manager.ts | 2 ++ .../core/src/network/session-connection.ts | 6 +++++ .../src/utils/platform/error-reporting.ts | 23 +++++++++++++++++++ packages/tl-utils/src/codegen/errors.ts | 2 ++ 5 files changed, 46 insertions(+) create mode 100644 packages/core/src/utils/platform/error-reporting.ts diff --git a/packages/core/src/base-client.ts b/packages/core/src/base-client.ts index cc3de8b0..85fdb5ab 100644 --- a/packages/core/src/base-client.ts +++ b/packages/core/src/base-client.ts @@ -131,6 +131,18 @@ export interface BaseTelegramClientOptions { */ disableUpdates?: boolean + /** + * mtcute can send all unknown RPC errors to [danog](https://github.com/danog)'s + * [error reporting service](https://rpc.pwrtelegram.xyz/). + * + * This is fully anonymous (except maybe IP) and is only used to improve the library + * and developer experience for everyone working with MTProto. This is fully opt-in, + * and if you're too paranoid, you can disable it by manually passing `enableErrorReporting: false` to the client. + * + * @default false + */ + enableErrorReporting?: boolean + /** * If true, RPC errors will have a stack trace of the initial `.call()` * or `.sendForResult()` call position, which drastically improves @@ -288,6 +300,7 @@ export class BaseTelegramClient extends EventEmitter { isPremium: false, useIpv6: Boolean(opts.useIpv6), keepAliveAction: this._keepAliveAction.bind(this), + enableErrorReporting: opts.enableErrorReporting ?? false, ...(opts.network ?? {}), }, this._config, diff --git a/packages/core/src/network/network-manager.ts b/packages/core/src/network/network-manager.ts index c71f0dbd..9a17212e 100644 --- a/packages/core/src/network/network-manager.ts +++ b/packages/core/src/network/network-manager.ts @@ -33,6 +33,7 @@ export interface NetworkManagerParams { crypto: ICryptoProvider log: Logger + enableErrorReporting: boolean apiId: number initConnectionOptions?: Partial> transport?: TransportFactory @@ -156,6 +157,7 @@ export class DcConnectionManager { isMainConnection: false, isMainDcConnection: this.isPrimary, inactivityTimeout: this.manager.params.inactivityTimeout ?? 60_000, + enableErrorReporting: this.manager.params.enableErrorReporting, }) private _log = this.manager._log.create('dc-manager') diff --git a/packages/core/src/network/session-connection.ts b/packages/core/src/network/session-connection.ts index a838a1c8..6e007956 100644 --- a/packages/core/src/network/session-connection.ts +++ b/packages/core/src/network/session-connection.ts @@ -18,6 +18,7 @@ import { removeFromLongArray, } from '../utils' import { createAesIgeForMessageOld } from '../utils/crypto/mtproto' +import { reportUnknownError } from '../utils/platform/error-reporting' import { doAuthorization } from './authorization' import { MtprotoSession, PendingMessage, PendingRpc } from './mtproto-session' import { PersistentConnection, PersistentConnectionParams } from './persistent-connection' @@ -29,6 +30,7 @@ export interface SessionConnectionParams extends PersistentConnectionParams { initConnection: tl.RawInitConnectionRequest inactivityTimeout?: number niceStacks?: boolean + enableErrorReporting: boolean layer: number disableUpdates?: boolean withUpdates?: boolean @@ -760,6 +762,10 @@ export class SessionConnection extends PersistentConnection { makeNiceStack(error, rpc.stack!, rpc.method) } + if (error.unknown && this.params.enableErrorReporting) { + reportUnknownError(this.log, error, rpc.method) + } + rpc.promise.reject(error) } else { this.log.debug('received rpc_result (%s) for request %l (%s)', result._, reqMsgId, rpc.method) diff --git a/packages/core/src/utils/platform/error-reporting.ts b/packages/core/src/utils/platform/error-reporting.ts new file mode 100644 index 00000000..1eece79d --- /dev/null +++ b/packages/core/src/utils/platform/error-reporting.ts @@ -0,0 +1,23 @@ +import { tl } from '@mtcute/tl' + +import { Logger } from '../logger' + +export function reportUnknownError(log: Logger, error: tl.RpcError, method: string): void { + if (typeof fetch !== 'function') return + + fetch(`https://rpc.pwrtelegram.xyz/?code=${error.code}&method=${method}&error=${error.text}`) + .then((r) => r.json()) + .then((r) => { + if (r.ok) { + log.info('telerpc responded with error info for %s: %s', error.text, r.result) + } else { + log.info( + 'Reported error %s to telerpc. You can disable this using `enableErrorReporting: false`', + error.text, + ) + } + }) + .catch((e) => { + log.debug('failed to report error %s to telerpc: %s', error.text, e) + }) +} diff --git a/packages/tl-utils/src/codegen/errors.ts b/packages/tl-utils/src/codegen/errors.ts index 7b434cf6..52aec98e 100644 --- a/packages/tl-utils/src/codegen/errors.ts +++ b/packages/tl-utils/src/codegen/errors.ts @@ -8,6 +8,7 @@ class RpcError extends Error { super(_descriptionsMap[text] || 'Unknown RPC error: [' + code + ':' + text + ']'); this.code = code; this.text = text; + this.unknown = !_descriptionsMap[text]; } static is(err, text) { return err.constructor === RpcError && (!text || err.text === text); } @@ -44,6 +45,7 @@ export class RpcError extends Error { readonly code: number; readonly text: MtErrorText; + readonly unknown: boolean; constructor(code: number, text: MtErrorText); is(text: T): this is RpcErrorWithArgs;