Search code examples
telegram-botgrammy

How to save group chat id when navigating to private chat?


I'm trying to build a telegram bot that will help the user to prepare an event creation privately and then will post it to the group.

The flow is to initialize the bot in the group, which then will show a button that will redirect the user to the private chat.

Here I will need to save in some way the group's chat id that will be used later to publish the event.

I've tried to use sessions for so but it did not worked:

Here is the command that is called in the group chat:

bot.command('event', checkIfGroup, checkIfAdmin, async (ctx) => {
    ctx.session.groupId = ctx.chat.id;

    const message = await ctx.reply('Welcome!', {
        reply_markup: {
            inline_keyboard: [
                [{ text: 'Start', url: 'https://t.me/bot_name?start=true' }]
            ]
        }
    });
});

Then the start command looks like this:

  bot.command('start', checkIfPrivate, async (ctx) => {
        await ctx.reply('...');
        await ctx.reply('Write the event name:');
    });

Then on message the event is build:

bot.on('message', checkIfPrivate, async (ctx) => {
    const builder = ctx.session.builder;
    ...
    builder.setTitle(text);
    ...

   const script = builder.formatEvent();
   await ctx.reply(script, {
                    parse_mode: 'MarkdownV2', link_preview_options: { is_disabled: true }, reply_markup: {
                        inline_keyboard: [
                            [
                                {
                                    text: 'Publish',
                                    callback_data: 'publish'
                                },
                                {
                                    text: 'Cancel',
                                    callback_data: 'cancel'
                                }
                            ]
                        ]
                    }
                });
}

Then the publish callback looks like this:

bot.callbackQuery('publish', async (ctx) => {
    if (!ctx.callbackQuery.message?.chat.id) {
        return;
    }

    const chatId = ctx.session.groupId;
    const builder = ctx.session.builder;

    const script = builder.formatEvent();
    const photoId = builder.getPhotoId();


        const message = await ctx.api.sendMessage(chatId, script, { parse_mode: 'MarkdownV2', link_preview_options: { is_disabled: true } });
     ...
});

Solution

  • I can see you can solve it two slightly different ways - but both using Sessions. I'd prefer Solution #2. Here's the detailed info.

    Solution #1

    According to grammY documentation there's a key thing you're missing probably.that is how grammY creates session keys. You can refer to this document and see the different ways to create Session Keys - while the default being different keys for group chats and personal chats. So, even if you set ctx.session.groupId from /event command in group, you probably won't get access to it in personal chat by default.

    As per the documentation you can add and specify the following method to get the session key to achieve your goal simply.

    
    bot.use(session({ initial, getSessionKey }));
    
    // Stores data per user.
    function getSessionKey(ctx) {
        // Give every user their personal session storage
        // (will be shared across groups and in their private chat)
        return ctx.from?.id.toString();
    }
    

    Now you can simply set the Context.session.groupId from the /event command that is sent to the group as the following code:

    bot.command('event', checkIfGroup, async (ctx) => {
        ctx.session.groupId = ctx.chat.id;
        await ctx.reply('Welcome!', {
            reply_markup: {
                inline_keyboard: [
                    [{ text: 'Start', url: `https://t.me/botusername?start=true` }]
                ]
            }
        });
    });
    

    Solution #2

    This is because, sometimes - if the user is anonymous or has privacy settings enabled ctx.from will be undefined hence grammy won't be able to save groupId to a shared session from the group.

    At this point, you can make use of the start parameter to handle it even more efficiently. Here's some code:

    bot.command('event', checkIfGroup, async (ctx) => {
        await ctx.reply('Welcome!', {
            reply_markup: {
                inline_keyboard: [
                    [{ text: 'Start', url: `https://t.me/botusername?start=${ctx.chat.id}` }]
                ]
            }
        });
    });
    

    Now when the Start button is pressed the ctx.match will be populated with the actual group id. You can save it to the session and continue your workflow.

    bot.command('start', checkIfPrivate, async (ctx) => {
        ctx.session.groupId = ctx.match;
        await ctx.reply('...');
        await ctx.reply('Write the event name:');
    });
    

    Hope this helps!