import { Api } from "../tl" ; import * as utils from "../Utils" ; import { sleep } from "../Helpers" ; import { computeCheck as computePasswordSrpCheck } from "../Password" ; import type { TelegramClient } from "./TelegramClient" ; /**
 * For when you want to login as a  Api . User <br/>
 * this should handle all needed steps for authorization as a user.<br/>
 * to stop the operation at any point just raise and error with the message `AUTH_USER_CANCEL`.
  export interface UserAuthParams { /** Either a string or a callback that returns a string for the phone to use to login. */ phoneNumber : string | ( ( ) => Promise < string > ) ; /** callback that should return the login code that telegram sent.<br/>
     *  has optional bool `isCodeViaApp` param for whether the code was sent through the app (true) or an SMS (false). */ phoneCode : ( isCodeViaApp ? : boolean ) => Promise < string > ; /** optional string or callback that should return the 2FA password if present.<br/>
     *  the password hint will be sent in the hint param */ password ? : ( hint ? : string ) => Promise < string > ; /** in case of a new account creation this callback should return a first name and last name `[first,last]`. */ firstAndLastNames ? : ( ) => Promise < [ string , string ? ] > ; /** a qrCode token for login through qrCode.<br/>
     *  this would need a QR code that you should scan with another app to login with. */ qrCode ? : ( qrCode : { token : Buffer ; expires : number ; } ) => Promise < void > ; /** when an error happens during auth this function will be called with the error.<br/>
     *  if this returns true the auth operation will stop. */ onError : ( err : Error ) => Promise < boolean > | void ; /** whether to send the code through SMS or not. */ forceSMS ? : boolean ; } export interface UserPasswordAuthParams { /** optional string or callback that should return the 2FA password if present.<br/>
     *  the password hint will be sent in the hint param */ password ? : ( hint ? : string ) => Promise < string > ; /** when an error happens during auth this function will be called with the error.<br/>
     *  if this returns true the auth operation will stop. */ onError : ( err : Error ) => Promise < boolean > | void ; } export interface QrCodeAuthParams extends UserPasswordAuthParams { /** a qrCode token for login through qrCode.<br/>
     *  this would need a QR code that you should scan with another app to login with. */ qrCode ? : ( qrCode : { token : Buffer ; expires : number ; } ) => Promise < void > ; /** when an error happens during auth this function will be called with the error.<br/>
     *  if this returns true the auth operation will stop. */ onError : ( err : Error ) => Promise < boolean > | void ; } interface ReturnString { ( ) : string ; } /**
 * For when you want as a normal bot created by https://t.me/Botfather.<br/>
 * Logging in as bot is simple and requires no callbacks
 */ export interface BotAuthParams { /**
     * the bot token to use.
     */ botAuthToken : string | ReturnString ; } /**
 * Credential needed for the authentication. you can get theses from https://my.telegram.org/auth<br/>
 * Note: This is required for both logging in as a bot and a user.<br/>
 */ export interface ApiCredentials { /** The app api id. */ apiId : number ; /** the app api hash */ apiHash : string ; } const QR_CODE_TIMEOUT = 30000 ; hidden export async function start ( client , authParams ) { if ( ! client . connected ) { await client . connect ( ) ; } if ( await client . checkAuthorization ( ) ) { return ; } const apiCredentials = { apiId : client . apiId , apiHash : client . apiHash , } ; await _authFlow ( client , apiCredentials , authParams ) ; } hidden export async function checkAuthorization ( client ) { try { await client . invoke ( new Api . updates . GetState ( ) ) ; return true ; } catch ( e ) { return false ; } } hidden export async function signInUser ( client , apiCredentials , authParams ) { let phoneNumber ; let phoneCodeHash ; let isCodeViaApp = false ; while ( 1 ) { try { if ( typeof authParams . phoneNumber === "function" ) { try { phoneNumber = await authParams . phoneNumber ( ) ; } catch ( err ) { if ( err . errorMessage === "RESTART_AUTH_WITH_QR" ) { return client . signInUserWithQrCode ( apiCredentials , authParams ) ; } throw err ; } } else { phoneNumber = authParams . phoneNumber ; } const sendCodeResult = await client . sendCode ( apiCredentials , phoneNumber , authParams . forceSMS ) ; phoneCodeHash = sendCodeResult . phoneCodeHash ; isCodeViaApp = sendCodeResult . isCodeViaApp ; if ( typeof phoneCodeHash !== "string" ) { throw new Error ( "Failed to retrieve phone code hash" ) ; } break ; } catch ( err ) { if ( typeof authParams . phoneNumber !== "function" ) { throw err ; } const shouldWeStop = await authParams . onError ( err ) ; if ( shouldWeStop ) { throw new Error ( "AUTH_USER_CANCEL" ) ; } } } let phoneCode ; let isRegistrationRequired = false ; let termsOfService ; while ( 1 ) { try { try { phoneCode = await authParams . phoneCode ( isCodeViaApp ) ; } catch ( err ) { if ( err . errorMessage === "RESTART_AUTH" ) { return client . signInUser ( apiCredentials , authParams ) ; } } if ( ! phoneCode ) { throw new Error ( "Code is empty" ) ; } const result = await client . invoke ( new Api . auth . SignIn ( { phoneNumber , phoneCodeHash , phoneCode , } ) ) ; if ( result instanceof Api . auth . AuthorizationSignUpRequired ) { isRegistrationRequired = true ; termsOfService = result . termsOfService ; break ; } return result . user ; } catch ( err ) { if ( err . errorMessage === "SESSION_PASSWORD_NEEDED" ) { return client . signInWithPassword ( apiCredentials , authParams ) ; } else { const shouldWeStop = await authParams . onError ( err ) ; if ( shouldWeStop ) { throw new Error ( "AUTH_USER_CANCEL" ) ; } } } } if ( isRegistrationRequired ) { while ( 1 ) { try { let lastName ; let firstName = "first name" ; if ( authParams . firstAndLastNames ) { const result = await authParams . firstAndLastNames ( ) ; firstName = result [ 0 ] ; lastName = result [ 1 ] ; } if ( ! firstName ) { throw new Error ( "First name is required" ) ; } const { user } = ( await client . invoke ( new Api . auth . SignUp ( { phoneNumber , phoneCodeHash , firstName , lastName , } ) ) ) as Api . auth . Authorization ; if ( termsOfService ) { await client . invoke ( new Api . help . AcceptTermsOfService ( { id : termsOfService . id , } ) ) ; } return user ; } catch ( err ) { const shouldWeStop = await authParams . onError ( err ) ; if ( shouldWeStop ) { throw new Error ( "AUTH_USER_CANCEL" ) ; } } } } await authParams . onError ( new Error ( "Auth failed" ) ) ; return client . signInUser ( apiCredentials , authParams ) ; } hidden export async function signInUserWithQrCode ( client , apiCredentials , authParams ) { const inputPromise = ( async ( ) => { while ( 1 ) { const result = await client . invoke ( new Api . auth . ExportLoginToken ( { apiId : Number ( apiCredentials . apiId ) , apiHash : apiCredentials . apiHash , exceptIds : [ ] , } ) ) ; if ( ! ( result instanceof Api . auth . LoginToken ) ) { throw new Error ( "Unexpected" ) ; } const { token , expires } = result ; if ( authParams . qrCode ) { await Promise . race ( [ authParams . qrCode ( { token , expires } ) , sleep ( QR_CODE_TIMEOUT ) , ] ) ; } await sleep ( QR_CODE_TIMEOUT ) ; } } ) ( ) ; const updatePromise = new Promise ( ( resolve ) => { client . addEventHandler ( ( update ) => { if ( update instanceof Api . UpdateLoginToken ) { resolve ( undefined ) ; } } ) ; } ) ; try { await Promise . race ( [ updatePromise , inputPromise ] ) ; } catch ( err ) { throw err ; } try { const result2 = await client . invoke ( new Api . auth . ExportLoginToken ( { apiId : Number ( apiCredentials . apiId ) , apiHash : apiCredentials . apiHash , exceptIds : [ ] , } ) ) ; if ( result2 instanceof Api . auth . LoginTokenSuccess && result2 . authorization instanceof Api . auth . Authorization ) { return result2 . authorization . user ; } else if ( result2 instanceof Api . auth . LoginTokenMigrateTo ) { await client . _switchDC ( result2 . dcId ) ; const migratedResult = await client . invoke ( new Api . auth . ImportLoginToken ( { token : result2 . token , } ) ) ; if ( migratedResult instanceof Api . auth . LoginTokenSuccess && migratedResult . authorization instanceof Api . auth . Authorization ) { return migratedResult . authorization . user ; } } } catch ( err ) { if ( err . errorMessage === "SESSION_PASSWORD_NEEDED" ) { return client . signInWithPassword ( apiCredentials , authParams ) ; } } await authParams . onError ( new Error ( "QR auth failed" ) ) ; throw new Error ( "QR auth failed" ) ; } hidden export async function sendCode ( client , apiCredentials , phoneNumber , forceSMS = false ) { try { const { apiId , apiHash } = apiCredentials ; const sendResult = await client . invoke ( new Api . auth . SendCode ( { phoneNumber , apiId , apiHash , settings : new Api . CodeSettings ( { } ) , } ) ) ; if ( ! forceSMS || sendResult . type instanceof Api . auth . SentCodeTypeSms ) { return { phoneCodeHash : sendResult . phoneCodeHash , isCodeViaApp : sendResult . type instanceof Api . auth . SentCodeTypeApp , } ; } const resendResult = await client . invoke ( new Api . auth . ResendCode ( { phoneNumber , phoneCodeHash : sendResult . phoneCodeHash , } ) ) ; return { phoneCodeHash : resendResult . phoneCodeHash , isCodeViaApp : resendResult . type instanceof Api . auth . SentCodeTypeApp , } ; } catch ( err ) { if ( err . errorMessage === "AUTH_RESTART" ) { return client . sendCode ( apiCredentials , phoneNumber , forceSMS ) ; } else { throw err ; } } } hidden export async function signInWithPassword ( client , apiCredentials , authParams ) { let emptyPassword = false ; while ( 1 ) { try { const passwordSrpResult = await client . invoke ( new Api . account . GetPassword ( ) ) ; if ( ! authParams . password ) { emptyPassword = true ; break ; } const password = await authParams . password ( passwordSrpResult . hint ) ; if ( ! password ) { throw new Error ( "Password is empty" ) ; } const passwordSrpCheck = await computePasswordSrpCheck ( passwordSrpResult , password ) ; const { user } = ( await client . invoke ( new Api . auth . CheckPassword ( { password : passwordSrpCheck , } ) ) ) as Api . auth . Authorization ; return user ; } catch ( err ) { const shouldWeStop = await authParams . onError ( err ) ; if ( shouldWeStop ) { throw new Error ( "AUTH_USER_CANCEL" ) ; } } } if ( emptyPassword ) { throw new Error ( "Account has 2FA enabled." ) ; } return undefined ! ; } hidden export async function signInBot ( client , apiCredentials , authParams ) { const { apiId , apiHash } = apiCredentials ; let { botAuthToken } = authParams ; if ( ! botAuthToken ) { throw new Error ( "a valid BotToken is required" ) ; } if ( typeof botAuthToken === "function" ) { let token ; while ( true ) { token = await botAuthToken ( ) ; if ( token ) { botAuthToken = token ; break ; } } } const { user } = ( await client . invoke ( new Api . auth . ImportBotAuthorization ( { apiId , apiHash , botAuthToken , } ) ) ) as Api . auth . Authorization ; return user ; } hidden export async function _authFlow ( client , apiCredentials , authParams ) { const me = "phoneNumber" in authParams ? await client . signInUser ( apiCredentials , authParams ) : await client . signInBot ( apiCredentials , authParams ) ; client . _log . info ( "Signed in successfully as " + utils . getDisplayName ( me ) ) ; }