You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于go-telegram-bot-api实现Telegram多层菜单导航

Telegram多层菜单导航实现方案(基于go-telegram-bot-api/v5)

问题描述

基于github.com/go-telegram-bot-api/telegram-bot-api/v5实现Telegram多层菜单导航时,遇到两个核心问题:

  • 无法在Page变量中添加Callback,避免循环引用问题
  • 需要保存用户当前所在页面及点击的按钮状态
    目前未找到合适的GitHub示例及有效解决方案。

现有代码

package main

import (
"log"

tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)

type Page struct {
Name     string
Buttons  [][]tgbotapi.KeyboardButton
Parent   *Page
Children []*Page
Callback func(update tgbotapi.Update, page *Page, bot *tgbotapi.BotAPI)
}

func sendPage(ChatID int64, page *Page, bot *tgbotapi.BotAPI) {
buttons := tgbotapi.NewReplyKeyboard(page.Buttons...)
message := tgbotapi.NewMessage(ChatID, page.Name)
message.ReplyMarkup = buttons
_, err := bot.Send(message)
if err != nil {
    log.Println("Error sending message: ", err)
}
}

var (
mainMenuPage = &Page{
    Name: "Главное меню",
    Buttons: [][]tgbotapi.KeyboardButton{
        {
            tgbotapi.NewKeyboardButton("Профиль"),
            tgbotapi.NewKeyboardButton("Кошелек"),
        },
        {
            tgbotapi.NewKeyboardButton("Пустая кнопка"),
            tgbotapi.NewKeyboardButton("Фидбек"),
        },
    },
    Parent:   nil,
    Children: []*Page{},
    Callback: nil,
    }
)

var (
    profileMenuPage = &Page{
        Name: "Профиль",
        Buttons: [][]tgbotapi.KeyboardButton{
            {
            tgbotapi.NewKeyboardButton("Мой профиль"),
            tgbotapi.NewKeyboardButton("Статистика"),
        },
        {
            tgbotapi.NewKeyboardButton("Добавить кошелек"),
            tgbotapi.NewKeyboardButton("Назад"),
        },
    },
    Parent:   nil,
    Children: []*Page{},
    Callback: nil,
}
)

func main() {
// 初始化机器人并创建更新接收通道
bot, err := tgbotapi.NewBotAPI("token")
if err != nil {
    log.Panic(err)
}
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)

u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates := bot.GetUpdatesChan(u)

sentMainMenu := false

// 更新处理器
for update := range updates {
    if update.Message != nil {
        // 回复用户消息
        msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text)
        bot.Send(msg)

        // 首次发送主菜单
        if !sentMainMenu {
            sendPage(update.Message.Chat.ID, mainMenuPage, bot)
            sentMainMenu = true
                }
            }
        }
    }

解决方案

1. 解决Callback循环引用问题

不在Page结构体中直接绑定Callback,改用按钮文本-处理函数映射的方式,在更新处理器中动态匹配执行逻辑,彻底避免循环引用。

示例:

// 按钮文本对应的处理函数映射
var buttonHandlers = map[string]func(update tgbotapi.Update, bot *tgbotapi.BotAPI){
    "Профиль": func(update tgbotapi.Update, bot *tgbotapi.BotAPI) {
        sendPage(update.Message.Chat.ID, profileMenuPage, bot)
    },
    "Назад": func(update tgbotapi.Update, bot *tgbotapi.BotAPI) {
        chatID := update.Message.Chat.ID
        currentPage := userPages[chatID]
        // 返回父页面,无父页面则回到主菜单
        if currentPage.Parent != nil {
            sendPage(chatID, currentPage.Parent, bot)
        } else {
            sendPage(chatID, mainMenuPage, bot)
        }
    },
    "Мой профиль": func(update tgbotapi.Update, bot *tgbotapi.BotAPI) {
        msg := tgbotapi.NewMessage(update.Message.Chat.ID, "这里是你的个人信息展示区域")
        bot.Send(msg)
    },
}

2. 保存用户页面状态

使用map[int64]*Page存储每个用户(以ChatID为标识)当前所在页面,每次发送页面时更新该映射。

示例:

// 存储用户当前页面,生产环境建议改用数据库或缓存
var userPages = make(map[int64]*Page)

// 改造sendPage函数,添加页面状态更新
func sendPage(ChatID int64, page *Page, bot *tgbotapi.BotAPI) {
    buttons := tgbotapi.NewReplyKeyboard(page.Buttons...)
    message := tgbotapi.NewMessage(ChatID, page.Name)
    message.ReplyMarkup = buttons
    _, err := bot.Send(message)
    if err != nil {
        log.Println("Error sending message: ", err)
        return
    }
    // 更新用户当前页面
    userPages[ChatID] = page
}

3. 完整更新处理器改造

func main() {
    bot, err := tgbotapi.NewBotAPI("token")
    if err != nil {
        log.Panic(err)
    }
    bot.Debug = true
    log.Printf("Authorized on account %s", bot.Self.UserName)

    u := tgbotapi.NewUpdate(0)
    u.Timeout = 60
    updates := bot.GetUpdatesChan(u)

    for update := range updates {
        if update.Message != nil {
            chatID := update.Message.Chat.ID
            text := update.Message.Text

            // 用户首次交互时发送主菜单
            if _, exists := userPages[chatID]; !exists {
                sendPage(chatID, mainMenuPage, bot)
                continue
            }

            // 匹配按钮处理逻辑
            if handler, exists := buttonHandlers[text]; exists {
                handler(update, bot)
            } else {
                // 处理未知输入
                msg := tgbotapi.NewMessage(chatID, "未知命令,请使用菜单按钮操作")
                bot.Send(msg)
            }
        }
    }
}

4. 页面层级关联优化

通过init函数完成页面父子关系绑定,让层级结构更清晰:

func init() {
    mainMenuPage.Children = append(mainMenuPage.Children, profileMenuPage)
    profileMenuPage.Parent = mainMenuPage
}

以上改造既解决了循环引用问题,又能稳定保存用户页面状态,实现完整的多层菜单导航逻辑。


内容的提问来源于stack exchange,提问作者hey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 16:40:10