grammyjs / grammY

The Telegram Bot Framework.
https://grammy.dev
MIT License
2.04k stars 106 forks source link

GrammyError: Call to 'setMyCommands' failed! (404: Not Found) #605

Closed devsheva closed 1 month ago

devsheva commented 1 month ago

I'm developing a simple bot and using Jest to run some unit tests. I looked at the example on official docs, but it didn't use setMyCommands api, so it wasn't really my case.

I'm getting GrammyError: Call to 'setMyCommands' failed! (404: Not Found)when running tests.

stacktrace

    GrammyError: Call to 'setMyCommands' failed! (404: Not Found)

       9 | )
      10 |
    > 11 | await bot.api.setMyCommands([
         | ^
      12 |   { command: 'start', description: 'Start the bot' },
      13 |   { command: 'setup', description: 'Setup your preferences' },
      14 |   { command: 'help', description: 'Display help text' },

bot.ts

import { Bot } from 'grammy'
import { BOT_TOKEN } from './config'

const bot = new Bot(BOT_TOKEN!)

bot.command('help', (ctx) =>
  ctx.reply('Bot is under construction. Please wait for the next update.')
)

await bot.api.setMyCommands([
  { command: 'start', description: 'Start the bot' },
  { command: 'setup', description: 'Setup your preferences' },
  { command: 'help', description: 'Display help text' },
])

export default bot

bot.spec.ts

import { jest } from '@jest/globals'
import bot from '@/bot'

const TIMEOUT = 5000
let outgoingRequest = []

beforeAll(async () => {
  // Incorrectly return undefined instead of the respective object types.
  bot.api.config.use((prev, method, payload) => {
    outgoingRequest.push({ method, payload })
    return { ok: true, result: {} } as any
  })

  await bot.init()
}, TIMEOUT)

beforeEach(() => {
  outgoingRequest = []
})
KnorpelSenf commented 1 month ago

https://grammy.dev/resources/faq#_404-not-found

devsheva commented 1 month ago

So basically is just cause I'm using "test token" but it still hits Telegram? So how should I test it?

KnorpelSenf commented 1 month ago

Your code is executed in the wrong order if you expect that the transformer is installed before the API call is performed. Naturally, the module is loaded before the tests are run, and your API call already happens on module load but the transformer is installed on test execution.

If you do this, you need to use a valid bot token as described at the link above.

Alternatively, you can reorder your code to install the transformer first.

devsheva commented 1 month ago

Alright, I think I'll opt to reorder the code since I want to do tests without hitting external API. But my last question regards this is, I can wrap the setMyCommands api call in a function, but when should I call this one then?

KnorpelSenf commented 1 month ago

In your beforeAll hook maybe?

devsheva commented 1 month ago

That would make sense, but I mean in my code in general. Does setMyCommands need to be called in a specific order or that's indifferent? Of course before starting the bot i guess

KnorpelSenf commented 1 month ago

It is irrelevant when you call it as long as the transformer is installed before that :)

devsheva commented 1 month ago

No ok I totally agree with you. Maybe I didn't explain what I meant well, but basically I'm just asking about the order in the running general code, not the tests concern one

KnorpelSenf commented 1 month ago

If we're not talking about tests, then my personal opinion is to never call setMyCommands for global commands from bot code. Instead, call it in your deployment script, or maybe just configure it with @BotFather directly.

I think that it only really makes sense to use the method when you apply scopes. For example, when the bot joins a chat, it could set the commands for that specific chat.

devsheva commented 1 month ago

Gotcha, I didn't think about it in that way. Really appreciate the answer and the fast replies. I'll close this as completed