skip to Main Content
const { TelegramClient } = require('telegram')
const { StringSession } = require('telegram/sessions')
const input = require('input') // npm i input

const apiId = 123456
const apiHash = '123456abcdfg'
const stringSession = new StringSession(''); // fill this later with the value from session.save()
(async () => {
    console.log('Loading interactive example...')
    const client = new TelegramClient(stringSession, apiId, apiHash, { connectionRetries: 5 })
    await client.start({
        phoneNumber: async () => await input.text('number ?'),
        password: async () => await input.text('password?'),
        phoneCode: async () => await input.text('Code ?'),   //<<<<<<This line
        onError: (err) => console.log(err),
    });
    console.log('You should now be connected.')
    console.log(client.session.save()) // Save this string to avoid logging in again
    await client.sendMessage('me', { message: 'Hello!' });
})()

This is the example of authorization with gramjs (https://painor.gitbook.io/gramjs/getting-started/authorization)

I’m trying to setup a nodejs-express environment to render pages that can help the user to setup credentials by submitting through a webpage and save/process it back-end.

Since the above code will wait for user input when calling client.start(), is there any way to break it down to 2 http request that posts the phoneNumber and password, then another http post to post the phoneCode from telegram for authentication?

I tried some Promise but it won’t work..
The idea is to have 2 steps, 1st one is to call /setup and post the initial credentials, then hope the client will call the telegram api to send a phone code to myself. After that I can call /setup API once more and submit the code as well.

The behavior of the following code is that, it can call and receive telegram’s phone code. But the second call to POST /setup API wouldn’t work and got stuck. So I can’t resolve the phone code for the client.

Is there any way that I could ignore the first Promise in the first http post, return a response, then call the API second time or a different route/url and send the data as the first Promise’s resolve?

let code = null;
async function waitForPhoneCode() {
    return new Promise(function(resolve, reject) {
        while(code==null){
            //wait for code to be set on next http post
        }
        resolve(code);
    });
}

app.post('/setup', async (req, res) => {
    const setupStep = req.body.setupStep;
    const apiId = req.body.apiId;
    const apiHash = req.body.apiHash;
    const phoneNumber = req.body.phoneNumber;
    const password = req.body.password;
    const obtainedCode = req.body.code;

    credentials = {
        "apiId": apiId,
        "apiHash": apiHash
    }

    if(setupStep == 1){
        client = new TelegramClient(new StringSession(""), credentials.apiId, credentials.apiHash, {
            connectionRetries: 5,
        });
        res.json({
            "status": "ok",
        })
        client.start({
            phoneNumber: phoneNumber,
            password: password,
            phoneCode: waitForPhoneCode(),
            onError: (err) => console.log(err),
        });
    } else if(setupStep == 2){
        code = obtainedCode;
        console.log("You should now be connected.");
        credentials = {
            apiId: apiId,
            apiHash: apiHash,
            stringSession: new sessionStorage(client.session.save())
        }
        fs.writeFileSync(credentialsPath, JSON.stringify({
            apiId: apiId,
            apiHash: apiHash,
            stringSession: client.session.save(),
        }));
        await client.sendMessage("me", { message: "Setup Completed!" });
        res.redirect('/');
    }
})

2

Answers


  1. Chosen as BEST ANSWER

    Here's the full working example that has @Coxxs's promise implemented. Sleep a few seconds worked too. I tried with some more promise for the client.start callbacks and here's the working result. I know I could chain the then() method but I think this looked more logically appealing.

    Thank you so much for your help! I really appreciate that!

    let globalPhoneCodePromise;
    let clientStartPromise;
    function generatePromise() {
        let resolve, reject;
        let promise = new Promise((_resolve, _reject) => {
            resolve = _resolve;
            reject = _reject;
        })
        return { resolve, reject, promise };
    }
    
    app.post('/setup', async (req, res) => {
        const setupStep = req.body.setupStep;
        const apiIdStr = req.body.apiId;
        const apiHash = req.body.apiHash;
        const phoneNumber = req.body.phoneNumber;
        const password = req.body.password;
        const obtainedCode = req.body.code;
    
        if(Number.isNaN(Number(apiIdStr))){
            res.json({
                "status": "error",
                "message": "apiId is not a number"
            })
        }
        let apiId = Number(apiIdStr) // Convert apiId to number
    
        credentials = {
            "apiId": apiId,
            "apiHash": apiHash
        }
    
        if(setupStep == 1){
            client = new TelegramClient(new StringSession(""), credentials.apiId, credentials.apiHash, {
                connectionRetries: 5,
            });
            
            globalPhoneCodePromise = generatePromise()
            clientStartPromise = client.start({
                phoneNumber: phoneNumber,
                password: async () => {return password},
                phoneCode: async () => {
                    let code = await globalPhoneCodePromise.promise;
                    globalPhoneCodePromise = generatePromise();
                    return code;
                },
                onError: (err) => {
                    console.log(err)
                    res.json({
                        "status": "error",
                        "setupStep": 9,
                        "message": err
                    })
                },
            });
            
            res.json({
                "status": "ok",
                "setupStep": 1
            })
        } else if(setupStep == 2){
            globalPhoneCodePromise.resolve(obtainedCode);
    
            clientStartPromise.then(async () => {
                console.log(client.session.save());
                console.log("You should now be connected.");
                credentials = {
                    "apiId": apiId,
                    "apiHash": apiHash,
                    "stringSession": client.session.save()
                }
                fs.writeFileSync(credentialsPath, JSON.stringify(credentials));
                await client.sendMessage("me", { message: "Setup Completed!" });
    
                res.json({
                    "status": "ok",
                    "setupStep": 2
                })
    
            }).catch(err => {
                res.json({
                    "status": "error",
                    "setupStep": 2,
                    "message": err
                })
            })
        }
    })
    

    Note: If you had tried a lot of times authenticating (testing with a wrong code?), you might get this error and have to wait for a day before testing the code again

    FloodWaitError: A wait of 78660 seconds is required (caused by auth.SendCode)

    {
      code: 420,
      errorMessage: 'FLOOD',
      seconds: 78660
    }
    

  2. We can save the resolve provided by the Promise to a global variable, and resolve it later.

    Here’s the fixed code:

    let globalPhoneCodePromise
    
    function generatePromise() {
        let resolve
        let reject
        let promise = new Promise((_resolve, _reject) => {
            resolve = _resolve
            reject = _reject
        })
    
        return { resolve, reject, promise }
    }
    
    // step 1
    globalPhoneCodePromise = generatePromise()
    await client.start({
        // ...
        phoneCode: async () => {
            let code = await globalPhoneCodePromise.promise
    
            // In case the user provided a wrong code, gram.js will try to call this function again
            // We generate a new promise here to allow user enter a new code later
            globalPhoneCodePromise = generatePromise()
    
            return code
        },
        // ...
    })
    
    // step 2
    globalPhoneCodePromise.resolve('12345')
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search