From 7c3e790eacea997343369f0b34edf582f1a585cf Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sun, 13 Nov 2022 17:06:30 +0400 Subject: [PATCH] Introduce Web Token login (#2129) --- src/App.tsx | 13 ++++++ src/api/gramjs/methods/auth.ts | 6 +++ src/api/gramjs/methods/client.ts | 7 ++- src/api/types/misc.ts | 2 + src/api/types/updates.ts | 6 ++- src/components/main/Main.tsx | 21 +++++++-- src/global/actions/api/initial.ts | 7 ++- src/global/actions/apiUpdaters/initial.ts | 14 ++++++ src/global/types.ts | 1 + src/lib/gramjs/client/TelegramClient.js | 4 +- src/lib/gramjs/client/auth.ts | 56 +++++++++++++++++++---- src/lib/gramjs/tl/AllTLObjects.js | 2 +- src/lib/gramjs/tl/api.d.ts | 25 +++++++++- src/lib/gramjs/tl/apiTl.js | 8 ++-- src/lib/gramjs/tl/static/api.json | 1 + src/lib/gramjs/tl/static/api.tl | 11 +++-- src/util/routing.ts | 44 +++++++++++++++++- 17 files changed, 201 insertions(+), 27 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 4b759a269..393346f4b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,12 +20,14 @@ import LockScreen from './components/main/LockScreen.async'; import AppInactive from './components/main/AppInactive'; import Transition from './components/ui/Transition'; import UiLoader from './components/common/UiLoader'; +import { parseInitialLocationHash } from './util/routing'; // import Test from './components/test/TestNoRedundancy'; type StateProps = { authState: GlobalState['authState']; isScreenLocked?: boolean; hasPasscode?: boolean; + hasWebAuthTokenFailed?: boolean; }; enum AppScreens { @@ -39,6 +41,7 @@ const App: FC = ({ authState, isScreenLocked, hasPasscode, + hasWebAuthTokenFailed, }) => { const { disconnect } = getActions(); @@ -130,6 +133,15 @@ const App: FC = ({ activeKey = AppScreens.auth; } + if (activeKey !== AppScreens.lock + && activeKey !== AppScreens.inactive + && activeKey !== AppScreens.main + && parseInitialLocationHash()?.tgWebAuthToken + && !hasWebAuthTokenFailed) { + page = 'main'; + activeKey = AppScreens.main; + } + const prevActiveKey = usePrevious(activeKey); // eslint-disable-next-line consistent-return @@ -169,6 +181,7 @@ export default withGlobal( authState: global.authState, isScreenLocked: global.passcode?.isScreenLocked, hasPasscode: global.passcode?.hasPasscode, + hasWebAuthTokenFailed: global.hasWebAuthTokenFailed, }; }, )(App); diff --git a/src/api/gramjs/methods/auth.ts b/src/api/gramjs/methods/auth.ts index 5375aa555..c540fecc3 100644 --- a/src/api/gramjs/methods/auth.ts +++ b/src/api/gramjs/methods/auth.ts @@ -27,6 +27,12 @@ export function init(_onUpdate: OnApiUpdate) { onUpdate = _onUpdate; } +export function onWebAuthTokenFailed() { + onUpdate({ + '@type': 'updateWebAuthTokenFailed', + }); +} + export function onRequestPhoneNumber() { onUpdate(buildAuthStateUpdate('authorizationStateWaitPhoneNumber')); diff --git a/src/api/gramjs/methods/client.ts b/src/api/gramjs/methods/client.ts index 0fa20bc58..8b4c1558e 100644 --- a/src/api/gramjs/methods/client.ts +++ b/src/api/gramjs/methods/client.ts @@ -19,7 +19,7 @@ import { } from '../../../config'; import { onRequestPhoneNumber, onRequestCode, onRequestPassword, onRequestRegistration, - onAuthError, onAuthReady, onCurrentUserUpdate, onRequestQrCode, + onAuthError, onAuthReady, onCurrentUserUpdate, onRequestQrCode, onWebAuthTokenFailed, } from './auth'; import { updater } from '../updater'; import { setMessageBuilderCurrentUserId } from '../apiBuilders/messages'; @@ -51,7 +51,7 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) onUpdate = _onUpdate; const { - userAgent, platform, sessionData, isTest, isMovSupported, isWebmSupported, maxBufferSize, + userAgent, platform, sessionData, isTest, isMovSupported, isWebmSupported, maxBufferSize, webAuthToken, dcId, } = initialArgs; const session = new sessions.CallbackSession(sessionData, onSessionUpdate); @@ -75,6 +75,7 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) useWSS: true, additionalDcsDisabled: IS_TEST, testServers: isTest, + dcId, } as any, ); @@ -101,6 +102,8 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) onError: onAuthError, initialMethod: platform === 'iOS' || platform === 'Android' ? 'phoneNumber' : 'qrCode', shouldThrowIfUnauthorized: Boolean(sessionData), + webAuthToken, + webAuthTokenFailed: onWebAuthTokenFailed, }); } catch (err: any) { // eslint-disable-next-line no-console diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index 8a3aabf6f..70581c66d 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -10,6 +10,8 @@ export interface ApiInitialArgs { isMovSupported?: boolean; isWebmSupported?: boolean; maxBufferSize?: number; + webAuthToken?: string; + dcId?: number; } export interface ApiOnProgress { diff --git a/src/api/types/updates.ts b/src/api/types/updates.ts index f16f870c5..244ed5f66 100644 --- a/src/api/types/updates.ts +++ b/src/api/types/updates.ts @@ -64,6 +64,10 @@ export type ApiUpdateAuthorizationState = { qrCode?: { token: string; expires: number }; }; +export type ApiUpdateWebAuthTokenFailed = { + '@type': 'updateWebAuthTokenFailed'; +}; + export type ApiUpdateSession = { '@type': 'updateSession'; sessionData?: ApiSessionData; @@ -549,7 +553,7 @@ export type ApiUpdateTranscribedAudio = { }; export type ApiUpdate = ( - ApiUpdateReady | ApiUpdateSession | + ApiUpdateReady | ApiUpdateSession | ApiUpdateWebAuthTokenFailed | ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser | ApiUpdateChat | ApiUpdateChatInbox | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds | ApiUpdateChatMembers | ApiUpdateChatJoin | ApiUpdateChatLeave | ApiUpdateChatPinned | ApiUpdatePinnedMessageIds | diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 53a080c10..d4783672b 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -36,10 +36,10 @@ import useBeforeUnload from '../../hooks/useBeforeUnload'; import useOnChange from '../../hooks/useOnChange'; import usePreventPinchZoomGesture from '../../hooks/usePreventPinchZoomGesture'; import useForceUpdate from '../../hooks/useForceUpdate'; -import { LOCATION_HASH } from '../../hooks/useHistoryBack'; import useShowTransition from '../../hooks/useShowTransition'; import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useInterval from '../../hooks/useInterval'; +import { parseInitialLocationHash, parseLocationHash } from '../../util/routing'; import StickerSetModal from '../common/StickerSetModal.async'; import UnreadCount from '../common/UnreadCounter'; @@ -193,6 +193,7 @@ const Main: FC = ({ closePaymentModal, clearReceipt, checkAppVersion, + openChat, } = getActions(); if (DEBUG && !DEBUG_isLogged) { @@ -276,11 +277,25 @@ const Main: FC = ({ // Parse deep link useEffect(() => { - if (lastSyncTime && LOCATION_HASH.startsWith('#?tgaddr=')) { - processDeepLink(decodeURIComponent(LOCATION_HASH.substr('#?tgaddr='.length))); + const parsedInitialLocationHash = parseInitialLocationHash(); + if (lastSyncTime && parsedInitialLocationHash?.tgaddr) { + processDeepLink(decodeURIComponent(parsedInitialLocationHash.tgaddr)); } }, [lastSyncTime]); + useEffectWithPrevDeps(([prevLastSyncTime]) => { + const parsedLocationHash = parseLocationHash(); + if (!parsedLocationHash) return; + + if (!prevLastSyncTime && lastSyncTime) { + openChat({ + id: parsedLocationHash.chatId, + threadId: parsedLocationHash.threadId, + type: parsedLocationHash.type, + }); + } + }, [lastSyncTime] as const); + // Prevent refresh by accidentally rotating device when listening to a voice chat useEffect(() => { if (!activeGroupCallId && !isPhoneCallActive) { diff --git a/src/global/actions/api/initial.ts b/src/global/actions/api/initial.ts index 00c200e8c..1c04abc40 100644 --- a/src/global/actions/api/initial.ts +++ b/src/global/actions/api/initial.ts @@ -30,6 +30,7 @@ import { forceWebsync } from '../../../util/websync'; import { clearGlobalForLockScreen, updatePasscodeSettings } from '../../reducers'; import { clearEncryptedSession, encryptSession, forgetPasscode } from '../../../util/passcode'; import { serializeGlobal } from '../../cache'; +import { parseInitialLocationHash } from '../../../util/routing'; addActionHandler('initApi', async (global, actions) => { if (!IS_TEST) { @@ -37,14 +38,18 @@ addActionHandler('initApi', async (global, actions) => { void clearLegacySessions(); } + const initialLocationHash = parseInitialLocationHash(); + void initApi(actions.apiUpdate, { userAgent: navigator.userAgent, platform: PLATFORM_ENV, sessionData: loadStoredSession(), - isTest: window.location.search.includes('test'), + isTest: window.location.search.includes('test') || initialLocationHash?.tgWebAuthTest === '1', isMovSupported: IS_MOV_SUPPORTED, isWebmSupported: IS_WEBM_SUPPORTED, maxBufferSize: MAX_BUFFER_SIZE, + webAuthToken: initialLocationHash?.tgWebAuthToken, + dcId: initialLocationHash?.tgWebAuthDcId ? Number(initialLocationHash?.tgWebAuthDcId) : undefined, }); }); diff --git a/src/global/actions/apiUpdaters/initial.ts b/src/global/actions/apiUpdaters/initial.ts index feef244ef..742a34bb6 100644 --- a/src/global/actions/apiUpdaters/initial.ts +++ b/src/global/actions/apiUpdaters/initial.ts @@ -18,6 +18,7 @@ import { setLanguage } from '../../../util/langProvider'; import { selectNotifySettings } from '../../selectors'; import { forceWebsync } from '../../../util/websync'; import { getShippingError, shouldClosePaymentModal } from '../../../util/getReadableErrorText'; +import { clearWebTokenAuth } from '../../../util/routing'; addActionHandler('apiUpdate', (global, actions, update) => { switch (update['@type']) { @@ -33,6 +34,10 @@ addActionHandler('apiUpdate', (global, actions, update) => { onUpdateAuthorizationError(update); break; + case 'updateWebAuthTokenFailed': + onUpdateWebAuthTokenFailed(); + break; + case 'updateConnectionState': onUpdateConnectionState(update); break; @@ -142,6 +147,15 @@ function onUpdateAuthorizationError(update: ApiUpdateAuthorizationError) { }); } +function onUpdateWebAuthTokenFailed() { + clearWebTokenAuth(); + + setGlobal({ + ...getGlobal(), + hasWebAuthTokenFailed: true, + }); +} + function onUpdateConnectionState(update: ApiUpdateConnectionState) { const { connectionState } = update; const global = getGlobal(); diff --git a/src/global/types.ts b/src/global/types.ts index a4b7cb795..2b9eff0c6 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -140,6 +140,7 @@ export type ApiLimitTypeWithModal = Exclude Promise); + webAuthTokenFailed: VoidFunction; phoneCode: (isCodeViaApp?: boolean) => Promise; password: (hint?: string) => Promise; firstAndLastNames: () => Promise<[string, string?]>; @@ -14,6 +15,7 @@ export interface UserAuthParams { forceSMS?: boolean; initialMethod?: 'phoneNumber' | 'qrCode'; shouldThrowIfUnauthorized?: boolean; + webAuthToken?: string; } export interface BotAuthParams { @@ -37,19 +39,27 @@ export async function authFlow( if ('botAuthToken' in authParams) { me = await signInBot(client, apiCredentials, authParams); + } else if ('webAuthToken' in authParams && authParams.webAuthToken) { + me = await signInUserWithWebToken(client, apiCredentials, authParams); } else { - const { initialMethod = DEFAULT_INITIAL_METHOD } = authParams; - - if (initialMethod === 'phoneNumber') { - me = await signInUser(client, apiCredentials, authParams); - } else { - me = await signInUserWithQrCode(client, apiCredentials, authParams); - } + me = await signInUserWithPreferredMethod(client, apiCredentials, authParams); } client._log.info('Signed in successfully as', utils.getDisplayName(me)); } +export async function signInUserWithPreferredMethod( + client: TelegramClient, apiCredentials: ApiCredentials, authParams: UserAuthParams, +): Promise { + const { initialMethod = DEFAULT_INITIAL_METHOD } = authParams; + + if (initialMethod === 'phoneNumber') { + return signInUser(client, apiCredentials, authParams); + } else { + return signInUserWithQrCode(client, apiCredentials, authParams); + } +} + export async function checkAuthorization(client: TelegramClient, shouldThrow = false) { try { await client.invoke(new Api.updates.GetState()); @@ -60,6 +70,36 @@ export async function checkAuthorization(client: TelegramClient, shouldThrow = f } } +async function signInUserWithWebToken( + client: TelegramClient, apiCredentials: ApiCredentials, authParams: UserAuthParams, +): Promise { + try { + const { apiId, apiHash } = apiCredentials; + const sendResult = await client.invoke(new Api.auth.ImportWebTokenAuthorization({ + webAuthToken: authParams.webAuthToken, + apiId, + apiHash, + })); + + if (sendResult instanceof Api.auth.Authorization) { + return sendResult.user; + } else { + throw new Error('SIGN_UP_REQUIRED'); + } + } catch (err: any) { + authParams.webAuthTokenFailed(); + client._log.error('Failed to login with web token', err); + if (err.message === 'SESSION_PASSWORD_NEEDED') { + return signInWithPassword(client, apiCredentials, authParams); + } else { + return signInUserWithPreferredMethod(client, apiCredentials, { + ...authParams, + webAuthToken: undefined, + }); + } + } +} + async function signInUser( client: TelegramClient, apiCredentials: ApiCredentials, authParams: UserAuthParams, ): Promise { diff --git a/src/lib/gramjs/tl/AllTLObjects.js b/src/lib/gramjs/tl/AllTLObjects.js index 48dbce211..b07facb69 100644 --- a/src/lib/gramjs/tl/AllTLObjects.js +++ b/src/lib/gramjs/tl/AllTLObjects.js @@ -1,6 +1,6 @@ const api = require('./api'); -const LAYER = 146; +const LAYER = 147; const tlobjects = {}; for (const tl of Object.values(api)) { diff --git a/src/lib/gramjs/tl/api.d.ts b/src/lib/gramjs/tl/api.d.ts index 524057b12..2b04a08c2 100644 --- a/src/lib/gramjs/tl/api.d.ts +++ b/src/lib/gramjs/tl/api.d.ts @@ -293,6 +293,7 @@ namespace Api { export type TypePremiumSubscriptionOption = PremiumSubscriptionOption; export type TypeSendAsPeer = SendAsPeer; export type TypeMessageExtendedMedia = MessageExtendedMediaPreview | MessageExtendedMedia; + export type TypeStickerKeyword = StickerKeyword; export type TypeResPQ = ResPQ; export type TypeP_Q_inner_data = PQInnerData | PQInnerDataDc | PQInnerDataTemp | PQInnerDataTempDc; export type TypeServer_DH_Params = ServerDHParamsFail | ServerDHParamsOk; @@ -4772,10 +4773,12 @@ namespace Api { export class StickerSetFullCovered extends VirtualClass<{ set: Api.TypeStickerSet; packs: Api.TypeStickerPack[]; + keywords: Api.TypeStickerKeyword[]; documents: Api.TypeDocument[]; }> { set: Api.TypeStickerSet; packs: Api.TypeStickerPack[]; + keywords: Api.TypeStickerKeyword[]; documents: Api.TypeDocument[]; }; export class MaskCoords extends VirtualClass<{ @@ -5846,6 +5849,7 @@ namespace Api { groupCall?: true; invites?: true; send?: true; + forums?: true; } | void> { // flags: undefined; join?: true; @@ -5865,6 +5869,7 @@ namespace Api { groupCall?: true; invites?: true; send?: true; + forums?: true; }; export class PopularContact extends VirtualClass<{ clientId: long; @@ -7587,6 +7592,13 @@ namespace Api { }> { media: Api.TypeMessageMedia; }; + export class StickerKeyword extends VirtualClass<{ + documentId: long; + keyword: string[]; + }> { + documentId: long; + keyword: string[]; + }; export class ResPQ extends VirtualClass<{ nonce: int128; serverNonce: int128; @@ -8321,10 +8333,12 @@ namespace Api { export class StickerSet extends VirtualClass<{ set: Api.TypeStickerSet; packs: Api.TypeStickerPack[]; + keywords: Api.TypeStickerKeyword[]; documents: Api.TypeDocument[]; }> { set: Api.TypeStickerSet; packs: Api.TypeStickerPack[]; + keywords: Api.TypeStickerKeyword[]; documents: Api.TypeDocument[]; }; export class StickerSetNotModified extends VirtualClass {}; @@ -9840,6 +9854,15 @@ namespace Api { }>, Bool> { code: string; }; + export class ImportWebTokenAuthorization extends Request, auth.TypeAuthorization> { + apiId: int; + apiHash: string; + webAuthToken: string; + }; } export namespace account { @@ -13355,7 +13378,7 @@ namespace Api { } export type AnyRequest = InvokeAfterMsg | InvokeAfterMsgs | InitConnection | InvokeWithLayer | InvokeWithoutUpdates | InvokeWithMessagesRange | InvokeWithTakeout | ReqPq | ReqPqMulti | ReqPqMultiNew | ReqDHParams | SetClientDHParams | DestroyAuthKey | RpcDropAnswer | GetFutureSalts | Ping | PingDelayDisconnect | DestroySession - | auth.SendCode | auth.SignUp | auth.SignIn | auth.LogOut | auth.ResetAuthorizations | auth.ExportAuthorization | auth.ImportAuthorization | auth.BindTempAuthKey | auth.ImportBotAuthorization | auth.CheckPassword | auth.RequestPasswordRecovery | auth.RecoverPassword | auth.ResendCode | auth.CancelCode | auth.DropTempAuthKeys | auth.ExportLoginToken | auth.ImportLoginToken | auth.AcceptLoginToken | auth.CheckRecoveryPassword + | auth.SendCode | auth.SignUp | auth.SignIn | auth.LogOut | auth.ResetAuthorizations | auth.ExportAuthorization | auth.ImportAuthorization | auth.BindTempAuthKey | auth.ImportBotAuthorization | auth.CheckPassword | auth.RequestPasswordRecovery | auth.RecoverPassword | auth.ResendCode | auth.CancelCode | auth.DropTempAuthKeys | auth.ExportLoginToken | auth.ImportLoginToken | auth.AcceptLoginToken | auth.CheckRecoveryPassword | auth.ImportWebTokenAuthorization | account.RegisterDevice | account.UnregisterDevice | account.UpdateNotifySettings | account.GetNotifySettings | account.ResetNotifySettings | account.UpdateProfile | account.UpdateStatus | account.GetWallPapers | account.ReportPeer | account.CheckUsername | account.UpdateUsername | account.GetPrivacy | account.SetPrivacy | account.DeleteAccount | account.GetAccountTTL | account.SetAccountTTL | account.SendChangePhoneCode | account.ChangePhone | account.UpdateDeviceLocked | account.GetAuthorizations | account.ResetAuthorization | account.GetPassword | account.GetPasswordSettings | account.UpdatePasswordSettings | account.SendConfirmPhoneCode | account.ConfirmPhone | account.GetTmpPassword | account.GetWebAuthorizations | account.ResetWebAuthorization | account.ResetWebAuthorizations | account.GetAllSecureValues | account.GetSecureValue | account.SaveSecureValue | account.DeleteSecureValue | account.GetAuthorizationForm | account.AcceptAuthorization | account.SendVerifyPhoneCode | account.VerifyPhone | account.SendVerifyEmailCode | account.VerifyEmail | account.InitTakeoutSession | account.FinishTakeoutSession | account.ConfirmPasswordEmail | account.ResendPasswordEmail | account.CancelPasswordEmail | account.GetContactSignUpNotification | account.SetContactSignUpNotification | account.GetNotifyExceptions | account.GetWallPaper | account.UploadWallPaper | account.SaveWallPaper | account.InstallWallPaper | account.ResetWallPapers | account.GetAutoDownloadSettings | account.SaveAutoDownloadSettings | account.UploadTheme | account.CreateTheme | account.UpdateTheme | account.SaveTheme | account.InstallTheme | account.GetTheme | account.GetThemes | account.SetContentSettings | account.GetContentSettings | account.GetMultiWallPapers | account.GetGlobalPrivacySettings | account.SetGlobalPrivacySettings | account.ReportProfilePhoto | account.ResetPassword | account.DeclinePasswordReset | account.GetChatThemes | account.SetAuthorizationTTL | account.ChangeAuthorizationSettings | account.GetSavedRingtones | account.SaveRingtone | account.UploadRingtone | account.UpdateEmojiStatus | account.GetDefaultEmojiStatuses | account.GetRecentEmojiStatuses | account.ClearRecentEmojiStatuses | users.GetUsers | users.GetFullUser | users.SetSecureValueErrors | contacts.GetContactIDs | contacts.GetStatuses | contacts.GetContacts | contacts.ImportContacts | contacts.DeleteContacts | contacts.DeleteByPhones | contacts.Block | contacts.Unblock | contacts.GetBlocked | contacts.Search | contacts.ResolveUsername | contacts.GetTopPeers | contacts.ResetTopPeerRating | contacts.ResetSaved | contacts.GetSaved | contacts.ToggleTopPeers | contacts.AddContact | contacts.AcceptContact | contacts.GetLocated | contacts.BlockFromReplies | contacts.ResolvePhone diff --git a/src/lib/gramjs/tl/apiTl.js b/src/lib/gramjs/tl/apiTl.js index 7281bfb3e..18812807b 100644 --- a/src/lib/gramjs/tl/apiTl.js +++ b/src/lib/gramjs/tl/apiTl.js @@ -460,7 +460,7 @@ inputStickerSetPremiumGifts#c88b3b02 = InputStickerSet; inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet; inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet; stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet; -messages.stickerSet#b60a24a6 set:StickerSet packs:Vector documents:Vector = messages.StickerSet; +messages.stickerSet#6e153f16 set:StickerSet packs:Vector keywords:Vector documents:Vector = messages.StickerSet; messages.stickerSetNotModified#d3f924eb = messages.StickerSet; botCommand#c27ac8c7 command:string description:string = BotCommand; botInfo#8f300b57 flags:# user_id:flags.0?long description:flags.1?string description_photo:flags.4?Photo description_document:flags.5?Document commands:flags.2?Vector menu_button:flags.3?BotMenuButton = BotInfo; @@ -598,7 +598,7 @@ messages.stickerSetInstallResultSuccess#38641628 = messages.StickerSetInstallRes messages.stickerSetInstallResultArchive#35e410a8 sets:Vector = messages.StickerSetInstallResult; stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered; stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector = StickerSetCovered; -stickerSetFullCovered#1aed5ee5 set:StickerSet packs:Vector documents:Vector = StickerSetCovered; +stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector keywords:Vector documents:Vector = StickerSetCovered; maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords; inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia; inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia; @@ -741,7 +741,7 @@ channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLog channelAdminLogEventActionChangeAvailableReactions#be4e0ef8 prev_value:ChatReactions new_value:ChatReactions = ChannelAdminLogEventAction; channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent; channels.adminLogResults#ed8af74d events:Vector chats:Vector users:Vector = channels.AdminLogResults; -channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true = ChannelAdminLogEventsFilter; +channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter; popularContact#5ce14175 client_id:long importers:int = PopularContact; messages.favedStickersNotModified#9e8fa6d3 = messages.FavedStickers; messages.favedStickers#2cb51097 hash:long packs:Vector stickers:Vector = messages.FavedStickers; @@ -1045,6 +1045,7 @@ premiumSubscriptionOption#b6f11ebe flags:# current:flags.1?true can_purchase_upg sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer; messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia; messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia; +stickerKeyword#fcfeb29c document_id:long keyword:Vector = StickerKeyword; ---functions--- initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X; invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X; @@ -1063,6 +1064,7 @@ auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool; auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector = Bool; auth.exportLoginToken#b7e085fe api_id:int api_hash:string except_ids:Vector = auth.LoginToken; auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken; +auth.importWebTokenAuthorization#2db873a9 api_id:int api_hash:string web_auth_token:string = auth.Authorization; account.registerDevice#ec86017a flags:# no_muted:flags.0?true token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector = Bool; account.unregisterDevice#6a0d3206 token_type:int token:string other_uids:Vector = Bool; account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool; diff --git a/src/lib/gramjs/tl/static/api.json b/src/lib/gramjs/tl/static/api.json index 16df43b35..8f7ad5005 100644 --- a/src/lib/gramjs/tl/static/api.json +++ b/src/lib/gramjs/tl/static/api.json @@ -8,6 +8,7 @@ "auth.resetAuthorizations", "auth.exportAuthorization", "auth.importAuthorization", + "auth.importWebTokenAuthorization", "auth.bindTempAuthKey", "auth.checkPassword", "auth.requestPasswordRecovery", diff --git a/src/lib/gramjs/tl/static/api.tl b/src/lib/gramjs/tl/static/api.tl index 5f22738e3..90de76067 100644 --- a/src/lib/gramjs/tl/static/api.tl +++ b/src/lib/gramjs/tl/static/api.tl @@ -561,7 +561,7 @@ inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet; stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true videos:flags.6?true emojis:flags.7?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet; -messages.stickerSet#b60a24a6 set:StickerSet packs:Vector documents:Vector = messages.StickerSet; +messages.stickerSet#6e153f16 set:StickerSet packs:Vector keywords:Vector documents:Vector = messages.StickerSet; messages.stickerSetNotModified#d3f924eb = messages.StickerSet; botCommand#c27ac8c7 command:string description:string = BotCommand; @@ -740,7 +740,7 @@ messages.stickerSetInstallResultArchive#35e410a8 sets:Vector stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered; stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector = StickerSetCovered; -stickerSetFullCovered#1aed5ee5 set:StickerSet packs:Vector documents:Vector = StickerSetCovered; +stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector keywords:Vector documents:Vector = StickerSetCovered; maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords; @@ -927,7 +927,7 @@ channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminL channels.adminLogResults#ed8af74d events:Vector chats:Vector users:Vector = channels.AdminLogResults; -channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true = ChannelAdminLogEventsFilter; +channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true forums:flags.17?true = ChannelAdminLogEventsFilter; popularContact#5ce14175 client_id:long importers:int = PopularContact; @@ -1418,6 +1418,8 @@ sendAsPeer#b81c7034 flags:# premium_required:flags.0?true peer:Peer = SendAsPeer messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int thumb:flags.1?PhotoSize video_duration:flags.2?int = MessageExtendedMedia; messageExtendedMedia#ee479c64 media:MessageMedia = MessageExtendedMedia; +stickerKeyword#fcfeb29c document_id:long keyword:Vector = StickerKeyword; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1447,6 +1449,7 @@ auth.exportLoginToken#b7e085fe api_id:int api_hash:string except_ids:Vector = Bool; account.unregisterDevice#6a0d3206 token_type:int token:string other_uids:Vector = Bool; @@ -1894,4 +1897,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; -// LAYER 146 +// LAYER 147 diff --git a/src/util/routing.ts b/src/util/routing.ts index d0643a376..2a2a48da1 100644 --- a/src/util/routing.ts +++ b/src/util/routing.ts @@ -2,6 +2,10 @@ import type { MessageListType } from '../global/types'; import { MAIN_THREAD_ID } from '../api/types'; import { LOCATION_HASH } from '../hooks/useHistoryBack'; +let parsedInitialLocationHash: Record | undefined; +let messageHash: string | undefined; +let isAlreadyParsed = false; + export const createMessageHash = (chatId: string, type: string, threadId: number): string => ( chatId.toString() + (type !== 'thread' ? `_${type}` @@ -9,9 +13,11 @@ export const createMessageHash = (chatId: string, type: string, threadId: number ); export function parseLocationHash() { - if (!LOCATION_HASH) return undefined; + parseInitialLocationHash(); - const [chatId, typeOrThreadId] = LOCATION_HASH.replace(/^#/, '').split('_'); + if (!messageHash) return undefined; + + const [chatId, typeOrThreadId] = messageHash.split('_'); if (!chatId?.match(/^-?\d+$/)) return undefined; const isType = ['thread', 'pinned', 'scheduled'].includes(typeOrThreadId); @@ -22,3 +28,37 @@ export function parseLocationHash() { threadId: Boolean(typeOrThreadId) && !isType ? Number(typeOrThreadId) : MAIN_THREAD_ID, }; } + +export function parseInitialLocationHash() { + if (parsedInitialLocationHash) return parsedInitialLocationHash; + + if (isAlreadyParsed) return undefined; + + if (!LOCATION_HASH) return undefined; + + let parsedHash = LOCATION_HASH ? LOCATION_HASH.replace(/^#/, '') : undefined; + if (parsedHash?.includes('?')) { + [messageHash, parsedHash] = parsedHash.split('?'); + window.location.hash = messageHash; + } else if (parsedHash?.includes('=')) { + window.location.hash = ''; + } + + parsedInitialLocationHash = parsedHash?.includes('=') ? parsedHash?.split('&').reduce((acc, cur) => { + const [key, value] = cur.split('='); + acc[key] = value; + return acc; + }, {} as Record) : undefined; + isAlreadyParsed = true; + if (!parsedInitialLocationHash) { + messageHash = parsedHash; + } + + return parsedInitialLocationHash; +} + +export function clearWebTokenAuth() { + if (!parsedInitialLocationHash) return; + + delete parsedInitialLocationHash.tgWebAuthToken; +}