From 8d942e62bbfac0727dc3c9b4dc3835c7abd86200 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 19 Apr 2024 13:38:32 +0400 Subject: [PATCH] Show notification for FLOOD_PREMIUM_WAIT (#4471) --- src/api/gramjs/apiBuilders/appConfig.ts | 7 ++++ .../gramjs/updates/UpdatePremiumFloodWait.ts | 3 ++ src/api/gramjs/updates/updater.ts | 8 +++- src/api/types/misc.ts | 3 ++ src/api/types/updates.ts | 7 +++- src/global/actions/apiUpdaters/misc.ts | 6 +++ src/global/actions/ui/misc.ts | 38 +++++++++++++++++++ src/global/types.ts | 4 ++ src/lib/gramjs/client/TelegramClient.d.ts | 8 ++-- src/lib/gramjs/client/TelegramClient.js | 1 + src/lib/gramjs/client/downloadFile.ts | 8 ++++ src/lib/gramjs/client/uploadFile.ts | 18 ++++++--- src/lib/gramjs/errors/RPCErrorList.js | 1 + 13 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 src/api/gramjs/updates/UpdatePremiumFloodWait.ts diff --git a/src/api/gramjs/apiBuilders/appConfig.ts b/src/api/gramjs/apiBuilders/appConfig.ts index 39aa539b4..648987107 100644 --- a/src/api/gramjs/apiBuilders/appConfig.ts +++ b/src/api/gramjs/apiBuilders/appConfig.ts @@ -73,6 +73,10 @@ export interface GramJsAppConfig extends LimitsConfig { // Boosts group_transcribe_level_min?: number; new_noncontact_peers_require_premium_without_ownpremium?: boolean; + // Upload premium notifications + upload_premium_speedup_notify_period?: number; + upload_premium_speedup_download?: number; + upload_premium_speedup_upload?: number; } function buildEmojiSounds(appConfig: GramJsAppConfig) { @@ -147,5 +151,8 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp storyChangelogUserId: appConfig.stories_changelog_user_id?.toString() ?? SERVICE_NOTIFICATIONS_USER_ID, groupTranscribeLevelMin: appConfig.group_transcribe_level_min, canLimitNewMessagesWithoutPremium: appConfig.new_noncontact_peers_require_premium_without_ownpremium, + bandwidthPremiumNotifyPeriod: appConfig.upload_premium_speedup_notify_period, + bandwidthPremiumUploadSpeedup: appConfig.upload_premium_speedup_upload, + bandwidthPremiumDownloadSpeedup: appConfig.upload_premium_speedup_download, }; } diff --git a/src/api/gramjs/updates/UpdatePremiumFloodWait.ts b/src/api/gramjs/updates/UpdatePremiumFloodWait.ts new file mode 100644 index 000000000..e22aeb535 --- /dev/null +++ b/src/api/gramjs/updates/UpdatePremiumFloodWait.ts @@ -0,0 +1,3 @@ +export default class LocalUpdatePremiumFloodWait { + constructor(public isUpload: boolean) {} +} diff --git a/src/api/gramjs/updates/updater.ts b/src/api/gramjs/updates/updater.ts index 16bc709da..b852c2400 100644 --- a/src/api/gramjs/updates/updater.ts +++ b/src/api/gramjs/updates/updater.ts @@ -77,11 +77,12 @@ import { import localDb from '../localDb'; import { scheduleMutedChatUpdate, scheduleMutedTopicUpdate } from '../scheduleUnmute'; +import LocalUpdatePremiumFloodWait from './UpdatePremiumFloodWait'; import { LocalUpdateChannelPts, LocalUpdatePts, type UpdatePts } from './UpdatePts'; export type Update = ( (GramJs.TypeUpdate | GramJs.TypeUpdates) & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] } -) | typeof connection.UpdateConnectionState | UpdatePts; +) | typeof connection.UpdateConnectionState | UpdatePts | LocalUpdatePremiumFloodWait; const DELETE_MISSING_CHANNEL_MESSAGE_DELAY = 1000; @@ -1193,6 +1194,11 @@ export function updater(update: Update) { chatId: buildApiPeerId(update.channelId, 'channel'), isEnabled: update.enabled ? true : undefined, }); + } else if (update instanceof LocalUpdatePremiumFloodWait) { + onUpdate({ + '@type': 'updatePremiumFloodWait', + isUpload: update.isUpload, + }); } else if (update instanceof LocalUpdatePts || update instanceof LocalUpdateChannelPts) { // Do nothing, handled on the manager side } else if (DEBUG) { diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index 42b644512..9a5b512de 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -204,6 +204,9 @@ export interface ApiAppConfig { storyChangelogUserId: string; groupTranscribeLevelMin?: number; canLimitNewMessagesWithoutPremium?: boolean; + bandwidthPremiumNotifyPeriod?: number; + bandwidthPremiumUploadSpeedup?: number; + bandwidthPremiumDownloadSpeedup?: number; } export interface ApiConfig { diff --git a/src/api/types/updates.ts b/src/api/types/updates.ts index 4006ce012..921912960 100644 --- a/src/api/types/updates.ts +++ b/src/api/types/updates.ts @@ -733,6 +733,11 @@ export type ApiUpdateSavedReactionTags = { '@type': 'updateSavedReactionTags'; }; +export type ApiUpdatePremiumFloodWait = { + '@type': 'updatePremiumFloodWait'; + isUpload?: boolean; +}; + export type ApiUpdate = ( ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateRequestUserUpdate | ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser | @@ -763,7 +768,7 @@ export type ApiUpdate = ( ApiRequestReconnectApi | ApiRequestSync | ApiUpdateFetchingDifference | ApiUpdateChannelMessages | ApiUpdateStealthMode | ApiUpdateAttachMenuBots | ApiUpdateNewAuthorization | ApiUpdateGroupInvitePrivacyForbidden | ApiUpdateViewForumAsMessages | ApiUpdateSavedDialogPinned | ApiUpdatePinnedSavedDialogIds | ApiUpdateChatLastMessage | - ApiUpdateDeleteSavedHistory | + ApiUpdateDeleteSavedHistory | ApiUpdatePremiumFloodWait | ApiUpdateQuickReplyMessage | ApiUpdateQuickReplies | ApiDeleteQuickReply | ApiUpdateDeleteQuickReplyMessages ); diff --git a/src/global/actions/apiUpdaters/misc.ts b/src/global/actions/apiUpdaters/misc.ts index c7a6597ac..e7d6ed782 100644 --- a/src/global/actions/apiUpdaters/misc.ts +++ b/src/global/actions/apiUpdaters/misc.ts @@ -155,6 +155,12 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => { case 'updateAttachMenuBots': actions.loadAttachBots({ hash: global.attachMenu.hash }); break; + + case 'updatePremiumFloodWait': { + actions.processPremiumFloodWait({ + isUpload: update.isUpload, + }); + } } return undefined; diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index 84790d1e2..7cd44a83f 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -35,6 +35,7 @@ import { import { getIsMobile, getIsTablet } from '../../../hooks/useAppLayout'; export const APP_VERSION_URL = 'version.txt'; +const FLOOD_PREMIUM_WAIT_NOTIFICATION_DURATION = 6000; const MAX_STORED_EMOJIS = 8 * 4; // Represents four rows of recent emojis addActionHandler('toggleChatInfo', (global, actions, payload): ActionReturnType => { @@ -750,6 +751,43 @@ addActionHandler('setShouldCloseRightColumn', (global, actions, payload): Action }, tabId); }); +addActionHandler('processPremiumFloodWait', (global, actions, payload): ActionReturnType => { + const { isUpload } = payload; + const { + bandwidthPremiumDownloadSpeedup, + bandwidthPremiumUploadSpeedup, + bandwidthPremiumNotifyPeriod, + } = global.appConfig || {}; + const { lastPremiumBandwithNotificationDate: lastNotifiedAt } = global.settings; + + if (!bandwidthPremiumDownloadSpeedup || !bandwidthPremiumUploadSpeedup || !bandwidthPremiumNotifyPeriod) { + return undefined; + } + if (lastNotifiedAt && Date.now() < lastNotifiedAt + bandwidthPremiumNotifyPeriod * 1000) return undefined; + + const unblurredTabIds = Object.values(global.byTabId).filter((l) => !l.isBlurred).map((l) => l.id); + + unblurredTabIds.forEach((tabId) => { + actions.showNotification({ + title: langProvider.translate(isUpload ? 'UploadSpeedLimited' : 'DownloadSpeedLimited'), + message: langProvider.translate( + isUpload ? 'UploadSpeedLimitedMessage' : 'DownloadSpeedLimitedMessage', + isUpload ? bandwidthPremiumUploadSpeedup : bandwidthPremiumDownloadSpeedup, + ), + duration: FLOOD_PREMIUM_WAIT_NOTIFICATION_DURATION, + tabId, + }); + }); + + return { + ...global, + settings: { + ...global.settings, + lastPremiumBandwithNotificationDate: Date.now(), + }, + }; +}); + let prevIsScreenLocked: boolean | undefined; let prevBlurredTabsCount: number = 0; let onlineTimeout: number | undefined; diff --git a/src/global/types.ts b/src/global/types.ts index c071eae14..ed22b5cd4 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -1009,6 +1009,7 @@ export type GlobalState = { themes: Partial>; privacy: Partial>; notifyExceptions?: Record; + lastPremiumBandwithNotificationDate?: number; }; push?: { @@ -1852,6 +1853,9 @@ export interface ActionPayloads { setGlobalSearchClosing: ({ isClosing?: boolean; } & WithTabId) | undefined; + processPremiumFloodWait: { + isUpload?: boolean; + }; // Accounts reportPeer: { diff --git a/src/lib/gramjs/client/TelegramClient.d.ts b/src/lib/gramjs/client/TelegramClient.d.ts index af6f1a8d3..fe0c1d5a6 100644 --- a/src/lib/gramjs/client/TelegramClient.d.ts +++ b/src/lib/gramjs/client/TelegramClient.d.ts @@ -1,9 +1,9 @@ -import type { Api } from '..'; - +import type { TmpPasswordResult, TwoFaParams, updateTwoFaSettings } from './2fa'; import type { BotAuthParams, UserAuthParams } from './auth'; -import type { uploadFile, UploadFileParams } from './uploadFile'; import type { downloadFile, DownloadFileParams } from './downloadFile'; -import type { TwoFaParams, updateTwoFaSettings, TmpPasswordResult } from './2fa'; +import type { uploadFile, UploadFileParams } from './uploadFile'; + +import type { Api } from '..'; declare class TelegramClient { constructor(...args: any); diff --git a/src/lib/gramjs/client/TelegramClient.js b/src/lib/gramjs/client/TelegramClient.js index 7591d8aa5..1ad80a964 100644 --- a/src/lib/gramjs/client/TelegramClient.js +++ b/src/lib/gramjs/client/TelegramClient.js @@ -634,6 +634,7 @@ class TelegramClient { authKeyCallback: this._authKeyCallback.bind(this), isMainSender: dcId === this.session.dcId, isExported: true, + updateCallback: this._handleUpdate.bind(this), getShouldDebugExportedSenders: this.getShouldDebugExportedSenders.bind(this), onConnectionBreak: () => this._cleanupExportedSender(dcId, index), }); diff --git a/src/lib/gramjs/client/downloadFile.ts b/src/lib/gramjs/client/downloadFile.ts index 2440b2bb4..e22d4f5fe 100644 --- a/src/lib/gramjs/client/downloadFile.ts +++ b/src/lib/gramjs/client/downloadFile.ts @@ -7,6 +7,7 @@ import { Foreman } from '../../../util/foreman'; import errors from '../errors'; import Api from '../tl/api'; +import LocalUpdatePremiumFloodWait from '../../../api/gramjs/updates/UpdatePremiumFloodWait'; import { sleep } from '../Helpers'; import { getDownloadPartSize } from '../Utils'; @@ -185,6 +186,9 @@ async function downloadFile2( progressCallback(progress); } + // Limit updates to one per file + let isPremiumFloodWaitSent = false; + // Allocate memory await fileView.init(); @@ -292,6 +296,10 @@ async function downloadFile2( await sleep(DISCONNECT_SLEEP); continue; } else if (err instanceof errors.FloodWaitError) { + if (err instanceof errors.FloodPremiumWaitError && !isPremiumFloodWaitSent) { + sender?._updateCallback(new LocalUpdatePremiumFloodWait(false)); + isPremiumFloodWaitSent = true; + } await sleep(err.seconds * 1000); continue; } diff --git a/src/lib/gramjs/client/uploadFile.ts b/src/lib/gramjs/client/uploadFile.ts index 7c2e2a715..bbcbe29d9 100644 --- a/src/lib/gramjs/client/uploadFile.ts +++ b/src/lib/gramjs/client/uploadFile.ts @@ -1,11 +1,12 @@ -// eslint-disable-next-line import/no-named-default -import { default as Api } from '../tl/api'; - import type TelegramClient from './TelegramClient'; + +import { Foreman } from '../../../util/foreman'; +import errors from '../errors'; +import Api from '../tl/api'; + +import LocalUpdatePremiumFloodWait from '../../../api/gramjs/updates/UpdatePremiumFloodWait'; import { generateRandomBytes, readBigIntFromBuffer, sleep } from '../Helpers'; import { getUploadPartSize } from '../Utils'; -import errors from '../errors'; -import { Foreman } from '../../../util/foreman'; interface OnProgress { isCanceled?: boolean; @@ -64,6 +65,9 @@ export async function uploadFile( onProgress(progress); } + // Limit updates to one per file + let isPremiumFloodWaitSent = false; + const promises: Promise[] = []; for (let i = 0; i < partCount; i++) { @@ -130,6 +134,10 @@ export async function uploadFile( await sleep(DISCONNECT_SLEEP); continue; } else if (err instanceof errors.FloodWaitError) { + if (err instanceof errors.FloodPremiumWaitError && !isPremiumFloodWaitSent) { + sender?._updateCallback(new LocalUpdatePremiumFloodWait(true)); + isPremiumFloodWaitSent = true; + } await sleep(err.seconds * 1000); continue; } diff --git a/src/lib/gramjs/errors/RPCErrorList.js b/src/lib/gramjs/errors/RPCErrorList.js index edf934023..fd980a07a 100644 --- a/src/lib/gramjs/errors/RPCErrorList.js +++ b/src/lib/gramjs/errors/RPCErrorList.js @@ -119,6 +119,7 @@ module.exports = { FileMigrateError, FloodTestPhoneWaitError, FloodWaitError, + FloodPremiumWaitError, PhoneMigrateError, SlowModeWaitError, UserMigrateError,