1 import { isFreeSubscription } from '@proton/payments';
2 import { getSilentApi } from '@proton/shared/lib/api/helpers/customConfig';
3 import { getIsB2BAudienceFromSubscription, getPlanName } from '@proton/shared/lib/helpers/subscription';
4 import type { Subscription, UserModel, UserSettings } from '@proton/shared/lib/interfaces';
6 import { metrics } from '../api/metrics';
7 import type { TelemetryReport } from '../api/telemetry';
8 import { sendMultipleTelemetryData, sendTelemetryData } from '../api/telemetry';
9 import type { METRICS_LOG } from '../constants';
10 import { SECOND } from '../constants';
11 import type { Api } from '../interfaces';
12 import { getAccountAgeForDimension } from './metrics.helpers';
13 import { wait } from './promise';
15 // Make the metrics false by default to avoid (rare) cases where we could have sendMetricReport or sendTelemetryReport
16 // before setting this metricsEnabled value with the user setting.
17 // In that scenario we would send something but the user might not want this.
18 let metricsEnabled = false;
21 * Delay an operation by a random number of seconds between 1 second and the specified
22 * number of seconds. If none is provided, the default is 180 seconds, i.e. 3 minutes
24 export const randomDelay = async (delayInSeconds: number = 180) => {
25 await wait(SECOND * Math.floor(delayInSeconds * Math.random() + 1));
29 * Send metrics report (/metrics endpoint)
31 export const sendMetricsReport = async (api: Api, Log: METRICS_LOG, Title?: string, Data?: any) => {
32 if (!metricsEnabled) {
35 // We delay sending the metrics report because this helper is used in some privacy-sensitive
36 // use-cases, e.g. encrypted search, in which we don't want the server to be able to use the
37 // metric report as a distinguisher to correlate user actions, e.g. performing an encrypted
38 // search and fetching an email shortly after
40 void api(metrics({ Log, Title, Data }));
43 interface SendTelemetryReportArgs extends TelemetryReport {
49 * Send a telemetry report (/data/v1/stats endpoint)
51 export const sendTelemetryReport = async ({
58 }: SendTelemetryReportArgs) => {
59 const possiblySilentApi = silence ? getSilentApi(api) : api;
61 if (!metricsEnabled) {
66 void (await possiblySilentApi(
68 MeasurementGroup: measurementGroup,
71 Dimensions: dimensions,
79 interface SendTelemetryReportWithBaseDimensionArgs extends SendTelemetryReportArgs {
82 subscription?: Subscription;
83 userSettings?: UserSettings;
88 * Send a telemetry report with basic dimensions already setup uses /data/v1/stats endpoint
95 export const sendTelemetryReportWithBaseDimensions = async ({
105 }: SendTelemetryReportWithBaseDimensionArgs) => {
106 const subscriptionName = isFreeSubscription(subscription) ? 'free' : getPlanName(subscription);
108 let audience = 'free';
109 if (getIsB2BAudienceFromSubscription(subscription)) {
111 } else if (!user.isFree) {
115 void sendTelemetryReport({
123 // Base dimensions used to help get basic knowledge about the user
124 accountAge: getAccountAgeForDimension(user),
125 userLocale: userSettings?.Locale ?? 'undefined',
126 subscription: String(subscriptionName),
128 isFree: subscriptionName === 'free' ? 'true' : 'false',
133 interface SendMultipleTelemetryReportsArgs {
135 reports: TelemetryReport[];
140 * Send multiple telemetry reports (/data/v1/stats/multiple endpoint)
142 export const sendMultipleTelemetryReports = async ({
146 }: SendMultipleTelemetryReportsArgs) => {
147 const possiblySilentApi = silence ? getSilentApi(api) : api;
149 if (!metricsEnabled) {
154 void (await possiblySilentApi(sendMultipleTelemetryData({ reports })));
160 export const setMetricsEnabled = (enabled: boolean) => {
161 metricsEnabled = enabled;