Signing in
WARNING
Before we start, please do not use mtcute to abuse Telegram services or harm other users by any means (including spamming, scraping, scamming, etc.)
Bots are meant to help people, not hurt them, so please make sure you don't 😃
API Keys
Before you can use this library (or any other MTProto library, for that matter), you need to obtain API ID and API Hash from Telegram:
- Go to https://my.telegram.org/apps and log in with your Telegram account
- Fill out the form to create a new application
TIP
You can leave URL field empty. App name and short name can (currently) be changed later.
Note that you will not be able to create another application using the same account.
- Press Create Application, and you'll see your
api_id
andapi_hash
.
WARNING
Be careful with your API hash. It can not be revoked.
Signing in
Now that we have got our API keys, we can sign in into our account:
// Replace with your own values
const tg = new TelegramClient({
apiId: API_ID,
apiHash: 'API_HASH'
})
const self = await tg.start({
phone: () => tg.input('Phone > '),
code: () => tg.input('Code > '),
password: () => tg.input('Password > ')
})
console.log(`Logged in as ${self.displayName}`)
// Replace with your own values
const tg = new TelegramClient({
apiId: API_ID,
apiHash: 'API_HASH'
})
const self = await tg.start({
phone: () => tg.input('Phone > '),
code: () => tg.input('Code > '),
password: () => tg.input('Password > ')
})
console.log(`Logged in as ${self.displayName}`)
TIP
tg.input
is a tiny wrapper over readline
module in Node.js, that will ask you for input in the console.
It's not available in @mtcute/core
, since it is platform-agnostic
Signing in as a bot
You can also use mtcute for bots (created via @BotFather). You will still need API ID and Hash, though:
// Replace with your own values
const tg = new TelegramClient({
apiId: API_ID,
apiHash: 'API_HASH'
})
const self = await tg.start({
botToken: '12345678:0123456789abcdef0123456789abcdef'
})
console.log(`Logged in as ${self.displayName}`)
// Replace with your own values
const tg = new TelegramClient({
apiId: API_ID,
apiHash: 'API_HASH'
})
const self = await tg.start({
botToken: '12345678:0123456789abcdef0123456789abcdef'
})
console.log(`Logged in as ${self.displayName}`)
Storing your API ID and Hash
In the examples above, we hard-coded the API keys. It works fine, but it is better to not keep that kind of stuff in the code, let alone publish them to public repositories.
Instead, it is a good practice to use environment variables and a .env
that will contain them.
You can load it then using dotenv-cli:
# .env
API_ID=123456
API_HASH=0123456789abcdef0123456789abcdef
# .env
API_ID=123456
API_HASH=0123456789abcdef0123456789abcdef
const tg = new TelegramClient({
apiId: process.env. API_ID,
apiHash: process.env. API_HASH
})
const tg = new TelegramClient({
apiId: process.env. API_ID,
apiHash: process.env. API_HASH
})
dotenv ts-node your-file.ts
dotenv ts-node your-file.ts
Using a proxy
When using Node.js, you can also connect to Telegram through proxy. This is particularly useful in countries like Iran or Russia, where Telegram might be limited.
To learn how to set up a connection through proxy, refer to Transport documentation
Manual sign in
So far we've only discussed the .start
helper method.
While they do provide some flexibility and convenience, they are not always suitable for every use case. For example, when building a web application, you might want to use a different way to ask for user input.
In that case, you can use the underlying sign-in methods directly
First, check if you are already signed in:
async function checkSignedIn() {
try {
// Try calling any method that requires authorization
// (getMe is the simplest one and likely the most useful,
// but you can use any other)
return await tg.getMe()
} catch (e) {
if (tl.RpcError.is(e, 'AUTH_KEY_UNREGISTERED')) {
// Not signed in, continue
return null
} else {
// Some other error, rethrow
throw e
}
}
}
async function checkSignedIn() {
try {
// Try calling any method that requires authorization
// (getMe is the simplest one and likely the most useful,
// but you can use any other)
return await tg.getMe()
} catch (e) {
if (tl.RpcError.is(e, 'AUTH_KEY_UNREGISTERED')) {
// Not signed in, continue
return null
} else {
// Some other error, rethrow
throw e
}
}
}
If you are not signed in, you should first use .sendCode
method:
// phone can be in pretty much any format,
// mtcute will automatically normalize it
const phone = '+7 999 123 4567'
const code = await tg.sendCode({ phone })
// phone can be in pretty much any format,
// mtcute will automatically normalize it
const phone = '+7 999 123 4567'
const code = await tg.sendCode({ phone })
The code
object will contain information about the sent code, including the phoneCodeHash
that you will need to use later.
Now, you need to ask the user for the code and call .signIn
method:
const code = '12345' // code from user input
const user = await tg.signIn({
phone,
phoneCodeHash: code.phoneCodeHash,
phoneCode: code
})
const code = '12345' // code from user input
const user = await tg.signIn({
phone,
phoneCodeHash: code.phoneCodeHash,
phoneCode: code
})
This method may either return right away, or throw one of:
SESSION_PASSWORD_NEEDED
if the account has 2FA enabled, and you should enter the passwordPHONE_CODE_INVALID
if the code entered was invalidPHONE_CODE_EXPIRED
if the code has expired, and you should resend it
Handling 2FA
If the account has 2FA enabled, you will need to ask the user for the password, and then call .checkPassword
:
const password = 'hunter2' // password from user input
const user = await tg.checkPassword(password)
const password = 'hunter2' // password from user input
const user = await tg.checkPassword(password)
In case of invalid password, this method will throw PASSWORD_HASH_INVALID
.
Resending code
In some cases, the code may expire before the user enters it, or it may never arrive, so you may want to resend it. To do that, you can use .resendCode
method:
code = await tg.resendCode({
phone,
phoneCodeHash: code.phoneCodeHash
})
code = await tg.resendCode({
phone,
phoneCodeHash: code.phoneCodeHash
})
TIP
You can know beforehand the type of the next code that will be sent using the code.nextType
field
Updates
If you want the updates to be processed, you should manually use startUpdatesLoop
once you are signed in:
if (await checkSignedIn()) {
tg.startUpdatesLoop()
} else {
// some sign in logic
tg.startUpdatesLoop()
}
if (await checkSignedIn()) {
tg.startUpdatesLoop()
} else {
// some sign in logic
tg.startUpdatesLoop()
}
tg.start
method does this automatically for you.