Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to debug when bot stops? #524

Closed
Roma-Kyrnis opened this issue Jan 25, 2024 · 17 comments
Closed

How to debug when bot stops? #524

Roma-Kyrnis opened this issue Jan 25, 2024 · 17 comments

Comments

@Roma-Kyrnis
Copy link

Hi, I'm using TDLib with Grammy bot. Flow:

  1. User sends /start command to the bot
  2. Bot send auth request to TDLib
  3. Bot asks some questions. TDLib handlers sendBotMessages to user when TDL receive update with auth state
  4. After user is loginned. Bot respond with Custom keyboard.
  5. User click button in the keyborad go to another keyboard "chats" than click on button "add"
  6. Button will trigger Hear "chatsAddHears".
  7. In this process I get Grammy logs but nothing specials. After a 3-5 times of repeating clicking "chatsAddHears" button - bot stops to respond
  8. It happend on the customer side. I don't have this issue. But after bot stops on customer side, bot stops to respond to every one.

Screenshot from 2024-01-24 15-01-39

As I understand my function has stopped and bot is waiting for this function. How to solve this?

I've used:
export DEBUG = "grammy*".

Can you help to find a reason of the bot stop?

@Roma-Kyrnis
Copy link
Author

chatAddHears export const chatsAddHears: HearsMiddleware = async (ctx: MyContext): Promise => { const telegramId = ctx.from?.id; if (!telegramId) { await ctx.reply('You does not have permission to enter the bot without ID.'); return; }

const client = getTDLibInstanceDB(telegramId);
if (!client) {
await ctx.reply(Будь-ласка перевірте чи ви увійшли в додаток Телеграму);
return;
}

console.log('client', client);
const chats = await client.getChatsWithInfo();
console.log('client.getChatsWithInfo', chats);
const sortedChats = chats.map(chat => [
chat.title,
constantsConfig.CHATS.CALLBACK_QUERY.CHOOSE_CHAT + chat.id.toString(),
]);
console.log('chats.map', sortedChats);
const keyboard = chooseChatKeyboard(sortedChats);
console.log('chooseChatKeyboard', keyboard);

await ctx.reply(stringToMarkdownV2(ua.RESULT_TEXT.CHATS.GET_BUTTONS_KEYBOARD), {
reply_markup: keyboard,
parse_mode: 'MarkdownV2',
});
};

Last crash with more logs:
image

Overall code execution
const bot = new Bot<MyContext>(env.TELEGRAM_BOT_TOKEN);

bot.use(
  session({
    initial() {
      // return empty object for now
      return {};
    },
  }),
);
bot.hears(ua.HEARS.CHATS.ADD, chatsAddHears);

/** import { getTDLibInstanceDB } from '../../db/memory/tdlib-instances.ts'; */
import { TDLibInstance } from '../../tdl/main.ts';

const instances = new Map<number, TDLibInstance>();

export const addTDLibInstanceDB = (telegramId: number, instance: TDLibInstance): void => {
  console.log('addTDLibInstanceDB');

  instances.set(telegramId, instance);
};

export const getTDLibInstanceDB = (telegramId: number): TDLibInstance | undefined => {
  console.log('getTDLibInstanceDB');

  return instances.get(telegramId);
};


/** getChatsWithInfo */
async function getChatsWithInfo(client: tdlClient, limit?: number): Promise<Td.chat[]> {
  const chats = await getChats(client, limit);
  console.log('getChatsWithInfo', chats);

  const chatsWithInfo = [];
  for (const chatId of chats.chat_ids) {
    const chat = await getChat(client, chatId);
    chatsWithInfo.push(chat);
  }

  return chatsWithInfo;
}

/** import { chatsKeyboard, chooseChatKeyboard } from '../keyboards/chats.ts'; */
import { InlineKeyboard, Keyboard } from 'grammy';

import constantsConfig from '../../config/constants.config.ts';
import ua from '../../config/locales/ua.ts';

export const chatsKeyboard = new Keyboard()
  .text(ua.HEARS.CHATS.ADD)
  .row()
  .text(ua.HEARS.CHATS.LIST)
  .row()
  .text(ua.HEARS.CHATS.REMOVE)
  .row()
  .text(ua.HEARS.BACK_TO_MAIN_MENU)
  .resized();

/**
 * const labelDataPairs = [
  ['« 1', 'first'],
  ['‹ 3', 'prev'],
  ['· 4 ·', 'stay'],
  ['5 ›', 'next'],
  ['31 »', 'last'],
];
 *
 * Limit is 100 buttons in telegram
 *
 * @param labelDataPairs array of buttons where label is what user see and data is what you will get on user's callback. For label better user Name of the Chat and for Data ChatId
 * @returns InlineKeyboard that you can reply to user
 */
export const chooseChatKeyboard = (labelDataPairs: string[][]): InlineKeyboard => {
  const buttonRow = labelDataPairs.map(([label, data]) => [InlineKeyboard.text(label, data)]);
  console.debug('buttons.length', buttonRow.length);

  if (buttonRow.length <= constantsConfig.TDLIB.REPLY_MARKUP_LIMIT_BUTTONS) {
    return InlineKeyboard.from(buttonRow);
  }

  return InlineKeyboard.from(
    buttonRow.slice(0, constantsConfig.TDLIB.REPLY_MARKUP_LIMIT_BUTTONS - 1),
  );
}

@Roma-Kyrnis
Copy link
Author

I understand maybe it's a problem with TDL, but why grammy stops to respond?

@KnorpelSenf
Copy link
Member

Do you use several instances of TDLib?

@Roma-Kyrnis
Copy link
Author

Yes, There are many users that connects to this bot (connect to TDLib through this bot)

@KnorpelSenf
Copy link
Member

That does not mean that you need several instances of TDLib. A bot isn't allowed to connect to different instances. You first need to explicitly log out of one and then start contacting another instance. Otherwise, updates may get lost.

TDLib can easily handle 500,000,000 messages per day with a single instance. If you don't have substantially more traffic than that, then your setup is overkill (=harmful). Most likely, you should only be using a single instance..

@Roma-Kyrnis
Copy link
Author

If I'll use one TDLib instance I need to login different users.

Maybe you can recommend a topic what to read?

If I need to send message, receive chats for specific user than I need to use telegram ID on this instance, as I understand it.

image

I found this PR, it recommends to create a lot of instances.

I understand you try to help. Can you point me where to look?

@KnorpelSenf
Copy link
Member

It has nothing to do with bots.

What are you even trying to do?

@Roma-Kyrnis
Copy link
Author

I make an application to send regular messages from different accounts to the chats, like ads or notifications.

I've tested that TDLib is still receiving updates when bot stops to work. But not is not responding on other calls, bot is not calling getUpdates function to pull messages from Telegram. That's why I'm here

I want to test why it happens and solve the issue

Could you help?

@KnorpelSenf
Copy link
Member

So your bot doesn't actually use the TDLib instances? In other words, your bot still connects to api.telegram.org?

Usually, if the bot stops, that's because a handler never completes. Do you have any handlers that could take a very long time, potentially forever?

@Roma-Kyrnis
Copy link
Author

Yes, I send a request to get users chats.

I'm using "tdl" npm lib. User enter app_ia and app_hash etc. And then I can send messages from a users account to chats

Thank you! I think that the issue.
But I don't get the logic here: bot waits until long request to get Chats from TDLib for example, but never receive. Does not telegram bot should work asynchronous and handle other requests while this one is processing?

Oh, maybe it's because you run a loop and wait for getUpdate function to resolve. And while you waiting you cannot send a new getUpdate?

I've seen this behaviour on the tdl lib:
Here https://github.com/Bannerets/tdl
In packages/tdl/index
There is a loop for processing long pull requests to Telegram

I didn't quite catch you when you said: 'connect to telegram.com'. Does TDLib and Telegram bot both connecting to telegram servers?

@KnorpelSenf
Copy link
Member

By default, grammY handles all updates in sequence. This is enough for 90 % of bots, and it leads to very simple and predictable behaviour. For example, in your case, you were able to find out that a handler takes way too much time. In a concurrent setup, this would have been much harder to detect, and your bot would probably just randomly OOM after a few weeks. Now you know, so you can fix it.

If you want to run your bot concurrently at a later point, you can use grammY runner.

TDLib is used internally by the Bot API server. You can read more in the docs. https://grammy.dev/guide/api

@Roma-Kyrnis
Copy link
Author

Thank you! I've got the point what I need to change ;)

I'll read the docs.

@Roma-Kyrnis
Copy link
Author

Have you thought about implementing the user's TDLib methods in Grammy?

@KnorpelSenf
Copy link
Member

That is not possible. grammY is not an MTProto client. It's a completely different API and you'll need to use a completely different library for that, such as https://github.com/MTKruto/MTKruto

@Roma-Kyrnis
Copy link
Author

Okay, thank you for the quick response and for the library creation)

May I join in implementing new features in the future, maybe in 1-2 months? I'm not sure if I will have time...
Just interesting in this topic: #110

@KnorpelSenf
Copy link
Member

Absolutely! Feel free to drop a message to https://t.me/grammyjs if you have questions or need advice. Looking forward to your contributions!

@awohsen
Copy link

awohsen commented Feb 3, 2024

Have you thought about implementing the user's TDLib methods in Grammy?

its possible via https://github.com/tdlight-team/tdlight-telegram-bot-api , they offer user MTProto methods over bot api - means you can use grammy to interact w/ user account. However you need to change api endpoints from /bot[token] to /user[token]

Read their docs for more information. I've been using it for few months and as long as its updated its all good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants