Introduce Web Token login (#2129)

This commit is contained in:
Alexander Zinchuk 2022-11-13 17:06:30 +04:00
parent 7cc64ed6bb
commit 7c3e790eac
17 changed files with 201 additions and 27 deletions

View File

@ -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<StateProps> = ({
authState,
isScreenLocked,
hasPasscode,
hasWebAuthTokenFailed,
}) => {
const { disconnect } = getActions();
@ -130,6 +133,15 @@ const App: FC<StateProps> = ({
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);

View File

@ -27,6 +27,12 @@ export function init(_onUpdate: OnApiUpdate) {
onUpdate = _onUpdate;
}
export function onWebAuthTokenFailed() {
onUpdate({
'@type': 'updateWebAuthTokenFailed',
});
}
export function onRequestPhoneNumber() {
onUpdate(buildAuthStateUpdate('authorizationStateWaitPhoneNumber'));

View File

@ -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

View File

@ -10,6 +10,8 @@ export interface ApiInitialArgs {
isMovSupported?: boolean;
isWebmSupported?: boolean;
maxBufferSize?: number;
webAuthToken?: string;
dcId?: number;
}
export interface ApiOnProgress {

View File

@ -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 |

View File

@ -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<StateProps> = ({
closePaymentModal,
clearReceipt,
checkAppVersion,
openChat,
} = getActions();
if (DEBUG && !DEBUG_isLogged) {
@ -276,11 +277,25 @@ const Main: FC<StateProps> = ({
// 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) {

View File

@ -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,
});
});

View File

@ -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();

View File

@ -140,6 +140,7 @@ export type ApiLimitTypeWithModal = Exclude<ApiLimitType, (
export type GlobalState = {
appConfig?: ApiAppConfig;
canInstall?: boolean;
hasWebAuthTokenFailed?: boolean;
isChatInfoShown: boolean;
isStatisticsShown?: boolean;
isLeftColumnShown: boolean;

View File

@ -70,6 +70,7 @@ class TelegramClient {
useWSS: false,
additionalDcsDisabled: false,
testServers: false,
dcId: DEFAULT_DC_ID,
};
/**
@ -86,6 +87,7 @@ class TelegramClient {
const args = { ...TelegramClient.DEFAULT_OPTIONS, ...opts };
this.apiId = apiId;
this.apiHash = apiHash;
this.defaultDcId = args.dcId || DEFAULT_DC_ID;
this._useIPV6 = args.useIPV6;
// this._entityCache = new Set()
if (typeof args.baseLogger === 'string') {
@ -221,7 +223,7 @@ class TelegramClient {
await this.session.load();
if (!this.session.serverAddress || (this.session.serverAddress.includes(':') !== this._useIPV6)) {
this.session.setDC(DEFAULT_DC_ID, this._useIPV6
this.session.setDC(this.defaultDcId, this._useIPV6
? DEFAULT_IPV6_IP : DEFAULT_IPV4_IP, this._args.useWSS ? 443 : 80);
}
}

View File

@ -1,11 +1,12 @@
import Api from '../tl/api';
import TelegramClient from './TelegramClient';
import type TelegramClient from './TelegramClient';
import utils from '../Utils';
import { sleep } from '../Helpers';
import { computeCheck as computePasswordSrpCheck } from '../Password';
export interface UserAuthParams {
phoneNumber: string | (() => Promise<string>);
webAuthTokenFailed: VoidFunction;
phoneCode: (isCodeViaApp?: boolean) => Promise<string>;
password: (hint?: string) => Promise<string>;
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<Api.TypeUser> {
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<Api.TypeUser> {
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<Api.TypeUser> {

View File

@ -1,6 +1,6 @@
const api = require('./api');
const LAYER = 146;
const LAYER = 147;
const tlobjects = {};
for (const tl of Object.values(api)) {

View File

@ -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<void> {};
@ -9840,6 +9854,15 @@ namespace Api {
}>, Bool> {
code: string;
};
export class ImportWebTokenAuthorization extends Request<Partial<{
apiId: int;
apiHash: string;
webAuthToken: string;
}>, 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

View File

@ -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<PhotoSize> 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<StickerPack> documents:Vector<Document> = messages.StickerSet;
messages.stickerSet#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = 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<BotCommand> menu_button:flags.3?BotMenuButton = BotInfo;
@ -598,7 +598,7 @@ messages.stickerSetInstallResultSuccess#38641628 = messages.StickerSetInstallRes
messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered> = messages.StickerSetInstallResult;
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
stickerSetFullCovered#1aed5ee5 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = StickerSetCovered;
stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = 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<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = 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<StickerPack> stickers:Vector<Document> = 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<string> = 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<long> = Bool;
auth.exportLoginToken#b7e085fe api_id:int api_hash:string except_ids:Vector<long> = 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<long> = Bool;
account.unregisterDevice#6a0d3206 token_type:int token:string other_uids:Vector<long> = Bool;
account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;

View File

@ -8,6 +8,7 @@
"auth.resetAuthorizations",
"auth.exportAuthorization",
"auth.importAuthorization",
"auth.importWebTokenAuthorization",
"auth.bindTempAuthKey",
"auth.checkPassword",
"auth.requestPasswordRecovery",

View File

@ -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<PhotoSize> 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<StickerPack> documents:Vector<Document> = messages.StickerSet;
messages.stickerSet#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = messages.StickerSet;
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
botCommand#c27ac8c7 command:string description:string = BotCommand;
@ -740,7 +740,7 @@ messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered>
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
stickerSetFullCovered#1aed5ee5 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = StickerSetCovered;
stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = 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<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = 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<string> = 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<long
auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken;
auth.acceptLoginToken#e894ad4d token:bytes = Authorization;
auth.checkRecoveryPassword#d36bf79 code:string = Bool;
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<long> = Bool;
account.unregisterDevice#6a0d3206 token_type:int token:string other_uids:Vector<long> = 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

View File

@ -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<string, string> | 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<string, string>) : undefined;
isAlreadyParsed = true;
if (!parsedInitialLocationHash) {
messageHash = parsedHash;
}
return parsedInitialLocationHash;
}
export function clearWebTokenAuth() {
if (!parsedInitialLocationHash) return;
delete parsedInitialLocationHash.tgWebAuthToken;
}