import { ArrowRightOutlined, LoginOutlined } from '@ant-design/icons' import { Button, Card, Checkbox, Col, Form, Input, Layout, notification, Row, Spin, Steps, Typography } from 'antd' import CountryPhoneInput, { ConfigProvider } from 'antd-country-phone-input' import { useForm } from 'antd/lib/form/Form' import base64url from 'base64url' import JSCookie from 'js-cookie' import React, { useEffect, useState } from 'react' import { useThemeSwitcher } from 'react-css-theme-switcher' import OtpInput from 'react-otp-input' import QRCode from 'react-qr-code' import { useHistory } from 'react-router' import useSWRImmutable from 'swr/immutable' import { Api } from 'telegram' import { generateRandomBytes } from 'telegram/Helpers' import { computeCheck } from 'telegram/Password' import en from 'world_countries_lists/data/countries/en/world.json' import { fetcher, req } from '../utils/Fetcher' import { anonymousTelegramClient, telegramClient } from '../utils/Telegram' import { getUserLocale } from 'get-user-locale' interface Props { me?: any } const Login: React.FC = ({ me }) => { const history = useHistory() const [formLogin] = useForm() const [formLoginQRCode] = useForm() const [currentStep, setCurrentStep] = useState(0) const [phoneData, setPhoneData] = useState<{ phone?: string, code?: number, short?: string }>({}) const [otp, setOtp] = useState() const [loadingSendCode, setLoadingSendCode] = useState() const [loadingLogin, setLoadingLogin] = useState() const [countdown, setCountdown] = useState() const [phoneCodeHash, setPhoneCodeHash] = useState() const [needPassword, setNeedPassword] = useState() const [method, setMethod] = useState<'phoneNumber' | 'qrCode'>('phoneNumber') const { data: _ } = useSWRImmutable('/utils/ipinfo', fetcher, { onSuccess: ({ ipinfo }) => setPhoneData(phoneData?.short ? phoneData : { short: ipinfo?.country || country }) }) const [qrCode, setQrCode] = useState<{ loginToken: string, accessToken?: string, session?: string }>() const { currentTheme } = useThemeSwitcher() const [unknownNumber, setUnknownNumber] = useState(false) const country = getUserLocale()?.substring(3, 5)?.toString() || 'ID' // useEffect(() => { // // init config // req.get('/config') // }, []) const sendCode = async (phoneNumber?: string) => { phoneNumber = phoneNumber || phoneData.phone ? `+${unknownNumber ? 888 : phoneData.code}${phoneData.phone}` : '' if (!phoneNumber) { return notification.error({ message: 'Error', description: 'Please input your valid phone number with country code' }) } const fetch = async (phoneCodeHash?: string) => { let data: any = null if (localStorage.getItem('experimental')) { const client = await anonymousTelegramClient.connect() if (phoneCodeHash) { const { phoneCodeHash: newPhoneCodeHash, timeout } = await client.invoke(new Api.auth.ResendCode({ phoneNumber, phoneCodeHash })) const session = client.session.save() as any localStorage.setItem('session', session) data = { phoneCodeHash: newPhoneCodeHash, timeout } } else { const { phoneCodeHash, timeout } = await client.invoke(new Api.auth.SendCode({ apiId: Number(process.env.REACT_APP_TG_API_ID), apiHash: process.env.REACT_APP_TG_API_HASH, phoneNumber, settings: new Api.CodeSettings({ allowFlashcall: true, currentNumber: true, allowAppHash: true, }) })) const session = client.session.save() as any localStorage.setItem('session', session) data = { phoneCodeHash, timeout } } } else { const invitationCode = location.search.replace('?code=', '') const resp = phoneCodeHash ? await req.post('/auth/reSendCode', { phoneNumber, phoneCodeHash, invitationCode }) : await req.post('/auth/sendCode', { phoneNumber, invitationCode }) data = resp.data } setPhoneCodeHash(data.phoneCodeHash) setCountdown(data.timeout) notification.info({ message: 'Sent!', description: 'Please check your Telegram app and input the code' }) } try { setLoadingSendCode(true) await fetch(phoneCodeHash) setCurrentStep(1) setLoadingSendCode(false) } catch (error: any) { setLoadingSendCode(false) notification.error({ message: 'Error', description: <> {error?.response?.data?.error || error.message || 'Something error'} {JSON.stringify(error?.response?.data || error?.data || error, null, 2)} }) if (error?.status === 400 || error?.response?.status === 400) { await fetch() } } } const login = async () => { if (!phoneCodeHash) { return sendCode() } setLoadingLogin(true) const phoneNumber = `+${unknownNumber ? 888 : phoneData.code}${phoneData.phone}` const phoneCode = otp const { password } = formLogin.getFieldsValue() try { let data: any = null if (localStorage.getItem('experimental')) { const client = await anonymousTelegramClient.connect() let signIn: any if (password) { const dataLogin = await client.invoke(new Api.account.GetPassword()) dataLogin.newAlgo['salt1'] = Buffer.concat([dataLogin.newAlgo['salt1'], generateRandomBytes(32)]) signIn = await client.invoke(new Api.auth.CheckPassword({ password: await computeCheck(dataLogin, password) })) } else { signIn = await client.invoke(new Api.auth.SignIn({ phoneNumber, phoneCode, phoneCodeHash })) } const userAuth = signIn['user'] if (!userAuth) { return notification.error({ message: 'User not found/authorized' }) } const session = client.session.save() as any localStorage.setItem('session', session) try { const resp = await req.get('/users/me') data = resp?.data } catch (error) { // ignore } } else { const invitationCode = location.search.replace('?code=', '') const resp = await req.post('/auth/login', { ...needPassword ? { password, invitationCode } : { phoneNumber, phoneCode, phoneCodeHash, invitationCode } }) data = resp.data } try { // req.post('/users/me/paymentSync') if (localStorage.getItem('files')) { notification.info({ key: 'sync', duration: null, message: 'Sync files data...', description: 'Please wait, we found your files data from another server.' }) req.post('/files/filesSync', { files: JSON.parse(localStorage.getItem('files') || '[]') }) .then(() => { notification.success({ key: 'sync', duration: 4.5, message: 'Files data synced successfully', description: 'Reload your browser to see the migrated files.', btn: }) localStorage.removeItem('files') }) } } catch (error) { // ignore } setLoadingLogin(false) notification.success({ message: 'Success', description: `Welcome back, ${data.user.name || data.user.username}! Please wait a moment...` }) if (localStorage.getItem('experimental')) { window.close() } else { history.replace('/dashboard') } } catch (error: any) { setLoadingLogin(false) let errorMessage = error?.errorMessage if (error.response && error.response.data) { errorMessage = error.response.data.details?.errorMessage } if (errorMessage === 'SESSION_PASSWORD_NEEDED') { notification.info({ message: 'Info', description: 'Please input your 2FA password' }) setCurrentStep(currentStep + 1) return setNeedPassword(true) } return notification.error({ message: 'Error', description: <> {error?.response?.data?.error || error.message || 'Something error'} {JSON.stringify(error?.response?.data || error?.data || error, null, 2)} }) } } const _qrCodeSignIn = async (password?: string) => { let data: any = null const sessionString = qrCode?.session if (password && sessionString) { const client = await telegramClient.connect(sessionString || '') const passwordData = await client.invoke(new Api.account.GetPassword()) passwordData.newAlgo['salt1'] = Buffer.concat([passwordData.newAlgo['salt1'], generateRandomBytes(32)]) const signIn = await client.invoke(new Api.auth.CheckPassword({ password: await computeCheck(passwordData, password) })) const userAuth = signIn['user'] if (!userAuth) { throw { status: 400, body: { error: 'User not found/authorized' } } } const session = client.session.save() as any localStorage.setItem('session', session) const resp = await req.get('/users/me') data = resp.data } else { // handle the second call for export login token, result case: success, need to migrate to other dc, or 2fa const client = await telegramClient.connect(sessionString || '') try { const dataLogin = await client.invoke(new Api.auth.ExportLoginToken({ apiId: Number(process.env.REACT_APP_TG_API_ID), apiHash: process.env.REACT_APP_TG_API_HASH, exceptIds: [] })) // handle to switch dc if (dataLogin instanceof Api.auth.LoginTokenMigrateTo) { await client._switchDC(dataLogin.dcId) const result = await client.invoke(new Api.auth.ImportLoginToken({ token: dataLogin.token })) // result import login token success if (result instanceof Api.auth.LoginTokenSuccess && result.authorization instanceof Api.auth.Authorization) { const userAuth = result.authorization.user if (userAuth) { const session = client.session.save() as any localStorage.setItem('session', session) const resp = await req.get('/users/me') data = resp.data } else { // ignore } } else { // ignore } // handle if success } else if (dataLogin instanceof Api.auth.LoginTokenSuccess && (dataLogin as any).authorization instanceof Api.auth.Authorization) { const userAuth = (dataLogin as any).authorization.user if (userAuth) { const session = client.session.save() as any localStorage.setItem('session', session) const resp = await req.get('/users/me') data = resp.data } else { // ignore } } data = { session: client.session.save() as any, loginToken: base64url(dataLogin['token']) } } catch (error: any) { // handle if need 2fa password if (error.errorMessage === 'SESSION_PASSWORD_NEEDED') { error.session = client.session.save() as any } throw error } } return data } const loginByQrCode = async () => { try { const { password } = formLoginQRCode.getFieldsValue() setLoadingLogin(true) const invitationCode = location.search.replace('?code=', '') const data = localStorage.getItem('experimental') ? await _qrCodeSignIn(password) : (await req.post('/auth/qrCodeSignIn', { password, session: qrCode?.session, invitationCode }))?.data try { // req.post('/users/me/paymentSync') if (localStorage.getItem('files')) { notification.info({ key: 'sync', duration: null, message: 'Sync files data...', description: 'Please wait, we found your files data from another server.' }) req.post('/files/filesSync', { files: JSON.parse(localStorage.getItem('files') || '[]') }) .then(() => { notification.success({ key: 'sync', duration: 4.5, message: 'Files data synced successfully', description: 'Reload your browser to see the migrated files.', btn: }) localStorage.removeItem('files') }) } } catch (error) { // ignore } notification.success({ message: 'Success', description: `Welcome back, ${data.user.name || data.user.username}! Please wait a moment...` }) setLoadingLogin(false) if (localStorage.getItem('experimental')) { window.close() } else { history.replace('/dashboard') } } catch (error: any) { setLoadingLogin(false) return notification.error({ message: 'Error', description: <> {error?.response?.data?.error || error.message || 'Something error'} {JSON.stringify(error?.response?.data || error?.data || error, null, 2)} }) } } useEffect(() => { if (JSCookie.get('authorization') && me?.user) { if (!localStorage.getItem('experimental')) { return history.replace('/dashboard') } } }, [me]) useEffect(() => { if (countdown) { setTimeout(() => setCountdown(countdown - 1), 1000) } else { setLoadingSendCode(false) } }, [countdown]) useEffect(() => { if (method === 'qrCode') { if (!qrCode?.loginToken) { if (localStorage.getItem('experimental')) { anonymousTelegramClient.connect() .then(client => { client.invoke(new Api.auth.ExportLoginToken({ apiId: Number(process.env.REACT_APP_TG_API_ID), apiHash: process.env.REACT_APP_TG_API_HASH, exceptIds: [] })) .then(data => { const session = client.session.save() as any localStorage.setItem('session', session) setQrCode({ session: client.session.save() as any, loginToken: base64url(data['token']) }) }) }) } else { req.get('/auth/qrCode').then(({ data }) => { setQrCode(data) }) } } } else { setQrCode(undefined) } }, [method]) useEffect(() => { if (qrCode && method === 'qrCode' && !needPassword) { const timeout = setTimeout(() => { if (method === 'qrCode' && !needPassword && qrCode?.loginToken) { new Promise((resolve, reject) => { if (localStorage.getItem('experimental')) { _qrCodeSignIn().then(resolve).catch(reject) } else { const invitationCode = location.search.replace('?code=', '') req.post('/auth/qrCodeSignIn', { invitationCode }, { headers: { 'Authorization': `Bearer ${qrCode.accessToken}` } }).then(({ data }) => resolve(data)).catch(reject) } }).then((data: any) => { // console.log(data) if (data?.user) { clearTimeout(timeout) try { // req.post('/users/me/paymentSync') if (localStorage.getItem('files')) { notification.info({ key: 'sync', duration: null, message: 'Sync files data...', description: 'Please wait, we found your files data from another server.' }) req.post('/files/filesSync', { files: JSON.parse(localStorage.getItem('files') || '[]') }) .then(() => { notification.success({ key: 'sync', duration: null, message: 'Files data synced successfully', description: 'Reload your browser to see the migrated files.', btn: }) localStorage.removeItem('files') }) } } catch (error) { // ignore } notification.success({ message: 'Success', description: `Welcome back, ${data.user.name || data.user.username}! Please wait a moment...` }) if (localStorage.getItem('experimental')) { window.close() } else { history.replace('/dashboard') } } else { setQrCode(data) } }).catch((error: any) => { let errorMessage = error?.errorMessage if (error.response && error.response.data) { errorMessage = error.response.data.details?.errorMessage } if (errorMessage === 'SESSION_PASSWORD_NEEDED') { notification.info({ message: 'Info', description: 'Please input your 2FA password' }) let session = error.session if (!session && error.response && error.response.data) { session = error.response.data.details?.session } setQrCode({ ...qrCode, session }) setNeedPassword(true) } else { // notification.error({ // message: 'Error', // description: response?.data?.error || 'Something error' // }) } }) } }, 3000) } }, [qrCode, method]) return <> Login with Telegram Please download Telegram app and login with your account. {method === 'phoneNumber' && <> {needPassword && }
{currentStep === 0 && <> { setPhoneData({ phone: e.phone, code: unknownNumber ? undefined : e.code, short: unknownNumber ? undefined : e.short }) }} /> {/* */} setUnknownNumber(target.checked)}> Use Fragment Number (+888). } {currentStep === 1 && <> Authentication code sent to +{phoneData.code}•••••••{phoneData.phone?.substring(phoneData.phone.length - 4)} {countdown ? Re-send in {countdown}s... : } } {currentStep === 2 && <> }
} {method === 'qrCode' && <> {!needPassword ? <> {qrCode?.loginToken ? : } Login to Telegram by QR Code
  1. Open Telegram on your phone
  2. Go to Settings > Devices > Link Desktop Device
  3. Point your phone at this screen to confirm login
{/* */}
{/* */} : <>
}
}
} export default Login