6e 中文文档教程
Telegraf Inline Menu
此菜单库旨在为您的 Telegram 机器人轻松创建内联菜单。
Installation
$ npm install telegraf-inline-menu
或使用 yarn
:
$ yarn add telegraf-inline-menu
考虑将 TypeScript 与此库一起使用,因为它有助于更快地发现一些常见错误。
TypeScript 和 Telegraf 机器人的一个很好的起点可能是这个 repo:EdJoPaTo/telegram-typescript-bot-template
Examples
Basic Example
const {Telegraf} = require('telegraf')
const {MenuTemplate, MenuMiddleware} = require('telegraf-inline-menu')
// or
import {Telegraf} from 'telegraf'
import {MenuTemplate, MenuMiddleware} from 'telegraf-inline-menu'
const menuTemplate = new MenuTemplate<MyContext>(ctx => `Hey ${ctx.from.first_name}!`)
menuTemplate.interact('I am excited!', 'a', {
do: async ctx => ctx.reply('As am I!')
})
const bot = new Telegraf(process.env.BOT_TOKEN)
const menuMiddleware = new MenuMiddleware('/', menuTemplate)
bot.command('start', ctx => menuMiddleware.replyToContext(ctx))
bot.use(menuMiddleware)
bot.launch()
More interesting one
看这里的代码:TypeScript / JavaScript(考虑使用 TypeScript)
Migrate from version 4 to version 5
如果您的项目仍在使用此库的版本 4,请参阅 v4 文档 并考虑重构到版本 5。
要迁移的内容列表:
TelegrafInlineMenu
was splitted into multiple classes. When you usednew TelegrafInlineMenu(text)
you will usenew MenuTemplate(body)
now.- Applying the menu to the bot via
bot.use
changed. This can now be done with theMenuMiddleware
. Check the Basic Example button
andsimpleButton
are combined and renamed intointeract
. See How can I run a simple method when pressing a button?selectSubmenu
was renamed tochooseIntoSubmenu
select
was splitted intochoose
andselect
. See Whats the difference between choose and select?question
is moved into a seperate library. see Didnt this menu had a question function?- The menu does not automatically add back and main menu buttons anymore. Use
menuTemplate.manualRow(createBackMainMenuButtons())
for that at each menu which should include these buttons. setCommand
andreplyMenuMiddleware
were replaced by multiple different functions. See Can I send the menu manually?
Migrate from version 5 to version 6
版本 6 从 telegraf 3.38 切换到 4.0。 请参阅 telegraf 迁移指南了解这组更改。
telegraf-inline-menu 相对不受此影响。 除了 telegraf 更改之外,唯一需要更改的是 ctx.match
的更改。 只需将 match
添加到您的 MyContext
类型:
export interface MyContext extends TelegrafContext {
readonly match: RegExpExecArray | undefined;
…
}
telegraf 知道匹配何时可用。 默认上下文不再匹配。 telegraf-inline-menu 也应该在未来的版本中知道这一点。
How does it work
电报内联键盘有按钮。 这些按钮有文本和回调数据。
当按下按钮时,回调数据将发送到机器人。 你从 Telegraf 的 bot.action
知道了这一点。
该库既创建按钮又监听回调数据事件。 当按下按钮并且其回调数据正在发生时,将执行与按钮相关的功能。
为了处理带有子菜单的树状菜单结构,按钮本身使用树状结构来区分按钮。 把它想象成 PC 上的文件结构。
主菜单使用 /
作为回调数据。 主菜单中的按钮将使用 /my-button
作为回调数据。 当您创建子菜单时,它将再次以 /
结束:/my-submenu/
。
通过这种方式,库知道在发生操作时要做什么: 如果回调数据以 /
结尾,它将显示相应的菜单。 如果它不以 /
结尾,则它是要执行的交互。
您可以使用 Telegraf 中间件来查看在您点击按钮时使用了哪些回调数据:
bot.use((ctx, next) => {
if (ctx.callbackQuery) {
console.log('callback data just happened', ctx.callbackQuery.data)
}
return next()
})
bot.use(menuMiddleware)
您还可以查看菜单中间件使用的所有正则表达式,以通过 console.log( menuMiddleware.tree())
。 不要被输出吓到,并尝试在源代码中找到可以找到该结构的位置。 当您点击一个按钮时,特定的回调数据将与其中一个正则表达式匹配。 还尝试创建一个新按钮并在树中找到它。
如果你想手动发送你的子菜单 /my-submenu/
你必须提供当你按下菜单中的按钮时使用的相同路径。
Improve the docs
如果您对图书馆的工作方式有任何疑问,请前往 issues 并提前提问。 您还可以加入 Telegraf 社区聊天,以讨论您的想法。
当您认为此解释有待改进时,请随时打开 Pull Request! 关于这是如何工作的,我已经陷入了困境。 您是获取有关此库的知识的专家。 让我们一起改进!
FAQ
Can I use HTML / MarkdownV2 in the message body?
也许这也很有用:npm package telegram-format
const menuTemplate = new MenuTemplate<MyContext>(ctx => {
const text = '_Hey_ *there*!'
return {text, parse_mode: 'Markdown'}
})
Can the menu body be some media?
菜单主体可以是包含 media
和 type
媒体。 media
和 type
与 Telegrams InputMedia 相同. 媒体刚刚传递给 telegraf,因此请查看有关如何处理文件 的文档。
示例 包含一个媒体子菜单,其中包含所有当前支持的媒体类型。
const menuTemplate = new MenuTemplate<MyContext>((ctx, path) => {
// Do something
return {
type: 'photo',
media: {
source: `./${ctx.from.id}.jpg`
},
text: 'Some *caption*',
parse_mode: 'Markdown'
}
})
MenuTemplate
的参数可以传递一个主体或一个返回主体的函数。 主体可以是字符串或具有如上所示选项的对象。 当作为函数使用时,参数是调用时菜单的上下文和路径。
How can I run a simple method when pressing a button?
menuTemplate.interact('Text', 'unique', {
do: async ctx => {
await ctx.answerCbQuery('yaay')
return false
}
})
Why do I have to return a boolean or string for the do/set function?
您可以控制之后是否要更新菜单。 当用户按下一个按钮时,菜单文本中的某些内容会发生变化,您希望用户看到更新后的内容。 您可以返回一个相对路径以在之后转到或一个简单的布尔值(yes = true
,no = false
)。
使用路径会变得非常方便。 例如,当您想返回父菜单时,您可以使用路径 ..
。 或者使用 ../sibling
到同级菜单。
如果你只想在不做逻辑的情况下导航,你应该更喜欢 .navigate(...)
。
menuTemplate.interact('Text', 'unique', {
do: async ctx => {
await ctx.answerCbQuery('go to parent menu after doing some logic')
return '..'
}
})
How to use a dynamic text of a button?
翻译 (telegraf-i18n) 您的机器人时通常需要这样做。
还可以查看其他按钮,例如 toggle,因为它们会为您格式化按钮文本。
menuTemplate.interact(ctx => ctx.i18n.t('button'), 'unique', {
do: async ctx => {
await ctx.answerCbQuery(ctx.i18n.t('reponse'))
return '.'
}
})
How can I show an url button?
menuTemplate.url('Text', 'https://edjopato.de')
How can I display two buttons in the same row?
在第二个按钮中使用 joinLastRow
menuTemplate.interact('Text', 'unique', {
do: async ctx => ctx.answerCbQuery('yaay')
})
menuTemplate.interact('Text', 'unique', {
joinLastRow: true,
do: async ctx => ctx.answerCbQuery('yaay')
})
How can I toggle a value easily?
menuTemplate.toggle('Text', 'unique', {
isSet: ctx => ctx.session.isFunny,
set: (ctx, newState) => {
ctx.session.isFunny = newState
}
})
How can I select one of many values?
menuTemplate.select('unique', ['human', 'bird'], {
isSet: (ctx, key) => ctx.session.choice === key,
set: (ctx, key) => {
ctx.session.choice = key
}
})
How can I toggle many values?
menuTemplate.select('unique', ['has arms', 'has legs', 'has eyes', 'has wings'], {
showFalseEmoji: true,
isSet: (ctx, key) => Boolean(ctx.session.bodyparts[key]),
set: (ctx, key, newState) => {
ctx.session.bodyparts[key] = newState
}
})
How can I interact with many values based on the pressed button?
menuTemplate.choose('unique', ['walk', 'swim'], {
do: async (ctx, key) => {
await ctx.answerCbQuery(`Lets ${key}`)
// You can also go back to the parent menu afterwards for some 'quick' interactions in submenus
return '..'
}
})
Whats the difference between choose and select?
如果您想根据选择执行某些操作,请使用 menuTemplate.choose(...)
。 如果你想改变某些东西的状态,从许多选项中选择一个,例如,使用 menuTemplate.select(...)
。
menuTemplate.select(…)
在按下按钮时自动更新菜单并显示当前选择的内容。 menuTemplate.choose(…)
运行您要运行的方法。
How can I use dynamic text for many values with choose or select?
一种方法是通过 Record
作为选择的输入:
const choices: Record<string, string> = {
a: 'Alphabet',
b: 'Beta',
c: 'Canada'
}
menuTemplate.choose('unique', choices, …)
您还可以使用 buttonText
函数 .choose(…)< /code> 或
formatState
用于 .select(…)
(和 .toggle(…)
)
menuTemplate.choose('unique', ['a', 'b'], {
do: …,
buttonText: (context, text) => {
return text.toUpperCase()
}
})
I have too much content for one message. Can I use a pagination?
menuTemplate.pagination
基本上是一个美化的 choose
。 您可以提供您拥有的页面数量以及当前页面是什么,它会告诉您用户要查看的页面。 将您的内容分成页面仍然是您的工作。 这使您可以进行各种变体。
menuTemplate.pagination('unique', {
getTotalPages: () => 42,
getCurrentPage: context => context.session.page,
setPage: (context, page) => {
context.session.page = page
}
})
My choose/select has too many choices. Can I use a pagination?
如果您不使用分页,您可能会注意到并没有显示您的所有选择。 默认只显示第一页。 您可以通过 maxRows
和 columns
选择行数和列数。 分页的工作方式与 menuTemplate.pagination
类似,但您无需提供总页数,因为这是根据您的选择计算得出的。
menuTemplate.choose('eat', ['cheese', 'bread', 'salad', 'tree', …], {
columns: 1,
maxRows: 2,
getCurrentPage: context => context.session.page,
setPage: (context, page) => {
context.session.page = page
}
})
How can I use a submenu?
const submenu = new MenuTemplate<MyContext>('I am a submenu')
submenuTemplate.interact('Text', 'unique', {
do: async ctx => ctx.answerCbQuery('You hit a button in a submenu')
})
submenuTemplate.manualRow(createBackMainMenuButtons())
menuTemplate.submenu('Text', 'unique', submenuTemplate)
How can I use a submenu with many choices?
const submenuTemplate = new MenuTemplate<MyContext>(ctx => `You chose city ${ctx.match[1]}`)
submenuTemplate.interact('Text', 'unique', {
do: async ctx => {
console.log('Take a look at ctx.match. It contains the chosen city', ctx.match)
return ctx.answerCbQuery('You hit a button in a submenu')
}
})
submenuTemplate.manualRow(createBackMainMenuButtons())
menuTemplate.chooseIntoSubmenu('unique', ['Gotham', 'Mos Eisley', 'Springfield'], submenuTemplate)
Can I close the menu?
您可以像使用 telegraf 一样删除消息:context.deleteMessage()
。 请记住:您不能删除超过 48 小时的消息。
deleteMenuFromContext
试图帮助您解决这个问题: 它试图删除菜单。 如果这不起作用,则键盘将从消息中删除,这样用户就不会不小心按下某些东西。
menuTemplate.interact('Delete the menu', 'unique', {
do: async context => {
await deleteMenuFromContext(context)
}
})
Can I send the menu manually?
如果你想发送根菜单使用 ctx => menuMiddleware.replyToContext(ctx)
const menuMiddleware = new MenuMiddleware('/', menuTemplate)
bot.command('start', ctx => menuMiddleware.replyToContext(ctx))
您还可以为要打开的特定子菜单指定 replyToContext
函数的路径。 请参阅它是如何工作的以了解您必须提供哪条路径作为最后一个参数。
const menuMiddleware = new MenuMiddleware('/', menuTemplate)
bot.command('start', ctx => menuMiddleware.replyToContext(ctx, path))
您还可以使用 replyMenuToContext
等 sendMenu 函数手动发送菜单。
import {MenuTemplate, replyMenuToContext} from 'telegraf-inline-menu'
const settingsMenu = new MenuTemplate('Settings')
bot.command('settings', async ctx => replyMenuToContext(settingsMenu, ctx, '/settings/'))
Can I send the menu from external events?
从外部事件发送时,您仍然必须为消息提供上下文,否则菜单的某些部分可能无法按预期工作!
请参阅它是如何工作的以了解您必须提供哪条路径作为 generateSendMenuToChatFunction
的最后一个参数。
const sendMenuFunction = generateSendMenuToChatFunction(bot.telegram, menu, '/settings/')
async function externalEventOccured() {
await sendMenuFunction(userId, context)
}
Didnt this menu had a question function?
是的。 它被移动到版本 5 的单独库中,因为它使源代码过于复杂。
当你想使用它时,检查 telegraf-stateless-question。
import {getMenuOfPath} from 'telegraf-inline-menu'
const myQuestion = new TelegrafStatelessQuestion<MyContext>('unique', async (context, additionalState) => {
const answer = context.message.text
console.log('user responded with', answer)
await replyMenuToContext(menuTemplate, context, additionalState)
})
bot.use(myQuestion.middleware())
menuTemplate.interact('Question', 'unique', {
do: async (context, path) => {
const text = 'Tell me the answer to the world and everything.'
const additionalState = getMenuOfPath(path)
await myQuestion.replyWithMarkdown(context, text, additionalState)
}
})
Documentation
这些方法本身应该有解释文档。 此外,文档中应该有多个 @example 条目,以查看使用该方法的不同方式。
如果您认为 jsdoc / readme 可以改进,请继续并创建一个 Pull Request。 让我们一起改进!
Telegraf Inline Menu
This menu library is made to easily create an inline menu for your Telegram bot.
Installation
$ npm install telegraf-inline-menu
or using yarn
:
$ yarn add telegraf-inline-menu
Consider using TypeScript with this library as it helps with finding some common mistakes faster.
A good starting point for TypeScript and Telegraf bots might be this repo: EdJoPaTo/telegram-typescript-bot-template
Examples
Basic Example
const {Telegraf} = require('telegraf')
const {MenuTemplate, MenuMiddleware} = require('telegraf-inline-menu')
// or
import {Telegraf} from 'telegraf'
import {MenuTemplate, MenuMiddleware} from 'telegraf-inline-menu'
const menuTemplate = new MenuTemplate<MyContext>(ctx => `Hey ${ctx.from.first_name}!`)
menuTemplate.interact('I am excited!', 'a', {
do: async ctx => ctx.reply('As am I!')
})
const bot = new Telegraf(process.env.BOT_TOKEN)
const menuMiddleware = new MenuMiddleware('/', menuTemplate)
bot.command('start', ctx => menuMiddleware.replyToContext(ctx))
bot.use(menuMiddleware)
bot.launch()
More interesting one
Look at the code here: TypeScript / JavaScript (consider using TypeScript)
Migrate from version 4 to version 5
If your project still uses version 4 of this library see v4 documentation and consider refactoring to version 5.
List of things to migrate:
TelegrafInlineMenu
was splitted into multiple classes. When you usednew TelegrafInlineMenu(text)
you will usenew MenuTemplate(body)
now.- Applying the menu to the bot via
bot.use
changed. This can now be done with theMenuMiddleware
. Check the Basic Example button
andsimpleButton
are combined and renamed intointeract
. See How can I run a simple method when pressing a button?selectSubmenu
was renamed tochooseIntoSubmenu
select
was splitted intochoose
andselect
. See Whats the difference between choose and select?question
is moved into a seperate library. see Didnt this menu had a question function?- The menu does not automatically add back and main menu buttons anymore. Use
menuTemplate.manualRow(createBackMainMenuButtons())
for that at each menu which should include these buttons. setCommand
andreplyMenuMiddleware
were replaced by multiple different functions. See Can I send the menu manually?
Migrate from version 5 to version 6
Version 6 switched from telegraf 3.38 to 4.0. See the telegraf migration guide for this set of changes.
telegraf-inline-menu is relativly unaffected by this. The only change required besides the telegraf changes is the change of ctx.match
. Simply add match
to your MyContext
type:
export interface MyContext extends TelegrafContext {
readonly match: RegExpExecArray | undefined;
…
}
telegraf knows when match is available or not. The default Context does not have match anymore. telegraf-inline-menu should also know this in a future release.
How does it work
Telegrams inline keyboards have buttons. These buttons have a text and callback data.
When a button is hit the callback data is sent to the bot. You know this from Telegraf from bot.action
.
This library both creates the buttons and listens for callback data events. When a button is pressed and its callback data is occuring the function relevant to the button is executed.
In order to handle tree like menu structures with submenus the buttons itself use a tree like structure to differentiate between the buttons. Imagine it as the file structure on a PC.
The main menu uses /
as callback data. A button in the main menu will use /my-button
as callback data. When you create a submenu it will end with a /
again: /my-submenu/
.
This way the library knows what do to when an action occurs: If the callback data ends with a /
it will show the corresponding menu. If it does not end with a /
it is an interaction to be executed.
You can use a Telegraf middleware in order to see which callback data is used when you hit a button:
bot.use((ctx, next) => {
if (ctx.callbackQuery) {
console.log('callback data just happened', ctx.callbackQuery.data)
}
return next()
})
bot.use(menuMiddleware)
You can also take a look on all the regular expressions the menu middleware is using to notice a button click with console.log(menuMiddleware.tree())
. Dont be scared by the output and try to find where you can find the structure in the sourcecode. When you hit a button the specific callback data will be matched by one of the regular expressions. Also try to create a new button and find it within the tree.
If you want to manually send your submenu /my-submenu/
you have to supply the same path that is used when you press the button in the menu.
Improve the docs
If you have any questions on how the library works head out to the issues and ask ahead. You can also join the Telegraf community chat in order to talk about the questions on your mind.
When you think there is something to improve on this explanation, feel free to open a Pull Request! I am already stuck in my bubble on how this is working. You are the expert on getting the knowledge about this library. Lets improve things together!
FAQ
Can I use HTML / MarkdownV2 in the message body?
Maybe this is also useful: npm package telegram-format
const menuTemplate = new MenuTemplate<MyContext>(ctx => {
const text = '_Hey_ *there*!'
return {text, parse_mode: 'Markdown'}
})
Can the menu body be some media?
The menu body can be an object containing media
and type
for media. The media
and type
is the same as Telegrams InputMedia. The media is just passed to telegraf so check its documentation on how to work with files.
The example features a media submenu with all currently supported media types.
const menuTemplate = new MenuTemplate<MyContext>((ctx, path) => {
// Do something
return {
type: 'photo',
media: {
source: `./${ctx.from.id}.jpg`
},
text: 'Some *caption*',
parse_mode: 'Markdown'
}
})
The argument of the MenuTemplate
can be passed a body or a function returning a body. A body can be a string or an object with options like seen above. When using as a function the arguments are the context and the path of the menu when called.
How can I run a simple method when pressing a button?
menuTemplate.interact('Text', 'unique', {
do: async ctx => {
await ctx.answerCbQuery('yaay')
return false
}
})
Why do I have to return a boolean or string for the do/set function?
You can control if you want to update the menu afterwards or not. When the user presses a button which changes something in the menu text you want the user to see the updated content. You can return a relative path to go to afterwards or a simple boolean (yes = true
, no = false
).
Using paths can become super handy. For example when you want to return to the parent menu you can use the path ..
. Or to a sibling menu with ../sibling
.
If you just want to navigate without doing logic you should prefer .navigate(…)
.
menuTemplate.interact('Text', 'unique', {
do: async ctx => {
await ctx.answerCbQuery('go to parent menu after doing some logic')
return '..'
}
})
How to use a dynamic text of a button?
This is often required when translating (telegraf-i18n) your bot.
Also check out other buttons like toggle as they do some formatting of the button text for you.
menuTemplate.interact(ctx => ctx.i18n.t('button'), 'unique', {
do: async ctx => {
await ctx.answerCbQuery(ctx.i18n.t('reponse'))
return '.'
}
})
How can I show an url button?
menuTemplate.url('Text', 'https://edjopato.de')
How can I display two buttons in the same row?
Use joinLastRow
in the second button
menuTemplate.interact('Text', 'unique', {
do: async ctx => ctx.answerCbQuery('yaay')
})
menuTemplate.interact('Text', 'unique', {
joinLastRow: true,
do: async ctx => ctx.answerCbQuery('yaay')
})
How can I toggle a value easily?
menuTemplate.toggle('Text', 'unique', {
isSet: ctx => ctx.session.isFunny,
set: (ctx, newState) => {
ctx.session.isFunny = newState
}
})
How can I select one of many values?
menuTemplate.select('unique', ['human', 'bird'], {
isSet: (ctx, key) => ctx.session.choice === key,
set: (ctx, key) => {
ctx.session.choice = key
}
})
How can I toggle many values?
menuTemplate.select('unique', ['has arms', 'has legs', 'has eyes', 'has wings'], {
showFalseEmoji: true,
isSet: (ctx, key) => Boolean(ctx.session.bodyparts[key]),
set: (ctx, key, newState) => {
ctx.session.bodyparts[key] = newState
}
})
How can I interact with many values based on the pressed button?
menuTemplate.choose('unique', ['walk', 'swim'], {
do: async (ctx, key) => {
await ctx.answerCbQuery(`Lets ${key}`)
// You can also go back to the parent menu afterwards for some 'quick' interactions in submenus
return '..'
}
})
Whats the difference between choose and select?
If you want to do something based on the choice use menuTemplate.choose(…)
. If you want to change the state of something, select one out of many options for example, use menuTemplate.select(…)
.
menuTemplate.select(…)
automatically updates the menu on pressing the button and shows what it currently selected. menuTemplate.choose(…)
runs the method you want to run.
How can I use dynamic text for many values with choose or select?
One way of doing so is via Record<string, string>
as input for the choices:
const choices: Record<string, string> = {
a: 'Alphabet',
b: 'Beta',
c: 'Canada'
}
menuTemplate.choose('unique', choices, …)
You can also use the buttonText
function for .choose(…)
or formatState
for .select(…)
(and .toggle(…)
)
menuTemplate.choose('unique', ['a', 'b'], {
do: …,
buttonText: (context, text) => {
return text.toUpperCase()
}
})
I have too much content for one message. Can I use a pagination?
menuTemplate.pagination
is basically a glorified choose
. You can supply the amount of pages you have and whats your current page is and it tells you which page the user whats to see. Splitting your content into pages is still your job to do. This allows you for all kinds of variations on your side.
menuTemplate.pagination('unique', {
getTotalPages: () => 42,
getCurrentPage: context => context.session.page,
setPage: (context, page) => {
context.session.page = page
}
})
My choose/select has too many choices. Can I use a pagination?
If you dont use a pagination you might have noticed that not all of your choices are displayed. Per default only the first page is shown. You can select the amount of rows and columns via maxRows
and columns
. The pagination works similar to menuTemplate.pagination
but you do not need to supply the amount of total pages as this is calculated from your choices.
menuTemplate.choose('eat', ['cheese', 'bread', 'salad', 'tree', …], {
columns: 1,
maxRows: 2,
getCurrentPage: context => context.session.page,
setPage: (context, page) => {
context.session.page = page
}
})
How can I use a submenu?
const submenu = new MenuTemplate<MyContext>('I am a submenu')
submenuTemplate.interact('Text', 'unique', {
do: async ctx => ctx.answerCbQuery('You hit a button in a submenu')
})
submenuTemplate.manualRow(createBackMainMenuButtons())
menuTemplate.submenu('Text', 'unique', submenuTemplate)
How can I use a submenu with many choices?
const submenuTemplate = new MenuTemplate<MyContext>(ctx => `You chose city ${ctx.match[1]}`)
submenuTemplate.interact('Text', 'unique', {
do: async ctx => {
console.log('Take a look at ctx.match. It contains the chosen city', ctx.match)
return ctx.answerCbQuery('You hit a button in a submenu')
}
})
submenuTemplate.manualRow(createBackMainMenuButtons())
menuTemplate.chooseIntoSubmenu('unique', ['Gotham', 'Mos Eisley', 'Springfield'], submenuTemplate)
Can I close the menu?
You can delete the message like you would do with telegraf: context.deleteMessage()
. Keep in mind: You can not delete messages which are older than 48 hours.
deleteMenuFromContext
tries to help you with that: It tries to delete the menu. If that does not work the keyboard is removed from the message so the user will not accidentally press something.
menuTemplate.interact('Delete the menu', 'unique', {
do: async context => {
await deleteMenuFromContext(context)
}
})
Can I send the menu manually?
If you want to send the root menu use ctx => menuMiddleware.replyToContext(ctx)
const menuMiddleware = new MenuMiddleware('/', menuTemplate)
bot.command('start', ctx => menuMiddleware.replyToContext(ctx))
You can also specify a path to the replyToContext
function for the specific submenu you want to open. See How does it work to understand which path you have to supply as the last argument.
const menuMiddleware = new MenuMiddleware('/', menuTemplate)
bot.command('start', ctx => menuMiddleware.replyToContext(ctx, path))
You can also use sendMenu functions like replyMenuToContext
to send a menu manually.
import {MenuTemplate, replyMenuToContext} from 'telegraf-inline-menu'
const settingsMenu = new MenuTemplate('Settings')
bot.command('settings', async ctx => replyMenuToContext(settingsMenu, ctx, '/settings/'))
Can I send the menu from external events?
When sending from external events you still have to supply the context to the message or some parts of your menu might not work as expected!
See How does it work to understand which path you have to supply as the last argument of generateSendMenuToChatFunction
.
const sendMenuFunction = generateSendMenuToChatFunction(bot.telegram, menu, '/settings/')
async function externalEventOccured() {
await sendMenuFunction(userId, context)
}
Didnt this menu had a question function?
Yes. It was moved into a seperate library with version 5 as it made the source code overly complicated.
When you want to use it check telegraf-stateless-question.
import {getMenuOfPath} from 'telegraf-inline-menu'
const myQuestion = new TelegrafStatelessQuestion<MyContext>('unique', async (context, additionalState) => {
const answer = context.message.text
console.log('user responded with', answer)
await replyMenuToContext(menuTemplate, context, additionalState)
})
bot.use(myQuestion.middleware())
menuTemplate.interact('Question', 'unique', {
do: async (context, path) => {
const text = 'Tell me the answer to the world and everything.'
const additionalState = getMenuOfPath(path)
await myQuestion.replyWithMarkdown(context, text, additionalState)
}
})
Documentation
The methods should have explaining documentation by itself. Also there should be multiple @example entries in the docs to see different ways of using the method.
If you think the jsdoc / readme can be improved just go ahead and create a Pull Request. Lets improve things together!