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

使用Golang Telego开发Telegram Bot:如何实现FSM?

在Telego中实现有限状态机(FSM)

Telego框架并未像aiogram那样内置FSM功能,需要手动实现状态跟踪逻辑。核心思路是通过维护用户与状态的映射关系,根据用户当前状态来分发和处理消息。

实现步骤与示例代码

1. 定义状态类型

首先自定义状态枚举,明确业务中需要的状态:

type UserState int

const (
    StateIdle UserState = iota       // 空闲状态,无等待操作
    StateWaitingForName              // 等待用户输入姓名
    StateWaitingForAge               // 等待用户输入年龄
)

2. 并发安全的状态存储

由于Telego的消息处理是多goroutine并发执行的,需要用互斥锁保证状态存储的线程安全:

import "sync"

var (
    userStates = make(map[int64]UserState) // key: 用户ID,value: 当前状态
    stateMutex sync.RWMutex                // 读写锁保护map
)

3. 状态操作辅助函数

封装状态的读取和更新逻辑,简化后续代码:

// 设置用户状态
func setUserState(userID int64, state UserState) {
    stateMutex.Lock()
    defer stateMutex.Unlock()
    userStates[userID] = state
}

// 获取用户当前状态,默认返回空闲状态
func getUserState(userID int64) UserState {
    stateMutex.RLock()
    defer stateMutex.RUnlock()
    if state, exists := userStates[userID]; exists {
        return state
    }
    return StateIdle
}

4. 结合Handler实现状态驱动的消息处理

修改基础代码,加入状态判断逻辑,实现一个收集用户信息的流程:

package main

import (
    "fmt"
    "os"
    "sync"

    "github.com/mymmrac/telego"
    th "github.com/mymmrac/telego/telegohandler"
    tu "github.com/mymmrac/telego/telegoutil"
)

type UserState int

const (
    StateIdle UserState = iota
    StateWaitingForName
    StateWaitingForAge
)

var (
    userStates = make(map[int64]UserState)
    stateMutex sync.RWMutex
)

func setUserState(userID int64, state UserState) {
    stateMutex.Lock()
    defer stateMutex.Unlock()
    userStates[userID] = state
}

func getUserState(userID int64) UserState {
    stateMutex.RLock()
    defer stateMutex.RUnlock()
    if state, exists := userStates[userID]; exists {
        return state
    }
    return StateIdle
}

func main() {
    botToken := os.Getenv("TOKEN")

    bot, err := telego.NewBot(botToken, telego.WithDefaultDebugLogger())
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    updates, _ := bot.UpdatesViaLongPolling(nil)
    bh, _ := th.NewBotHandler(bot, updates)

    defer bh.Stop()
    defer bot.StopLongPolling()

    // 处理/start命令
    bh.Handle(func(bot *telego.Bot, update telego.Update) {
        userID := update.Message.From.ID
        setUserState(userID, StateIdle)
        _, _ = bot.SendMessage(tu.Message(
            tu.ID(update.Message.Chat.ID),
            fmt.Sprintf("Hello %s! 发送/profile开始完善个人信息", update.Message.From.FirstName),
        ))
    }, th.CommandEqual("start"))

    // 处理/profile命令,进入等待姓名状态
    bh.Handle(func(bot *telego.Bot, update telego.Update) {
        userID := update.Message.From.ID
        setUserState(userID, StateWaitingForName)
        _, _ = bot.SendMessage(tu.ID(update.Message.Chat.ID), "请输入你的姓名:")
    }, th.CommandEqual("profile"))

    // 处理普通消息,根据用户状态分支处理
    bh.Handle(func(bot *telego.Bot, update telego.Update) {
        if update.Message == nil || update.Message.Text == "" {
            return
        }

        userID := update.Message.From.ID
        chatID := update.Message.Chat.ID
        currentState := getUserState(userID)

        switch currentState {
        case StateWaitingForName:
            // 这里可以保存姓名到数据库或其他存储
            fmt.Printf("用户%d的姓名:%s\n", userID, update.Message.Text)
            setUserState(userID, StateWaitingForAge)
            _, _ = bot.SendMessage(tu.ID(chatID), "请输入你的年龄:")
        case StateWaitingForAge:
            // 这里可以保存年龄到数据库或其他存储
            fmt.Printf("用户%d的年龄:%s\n", userID, update.Message.Text)
            setUserState(userID, StateIdle)
            _, _ = bot.SendMessage(tu.ID(chatID), "个人信息已收集完成!")
        case StateIdle:
            _, _ = bot.SendMessage(tu.ID(chatID), "未知指令,发送/start开始,或发送/profile完善信息")
        }
    }, th.AnyMessage())

    bh.Start()
}

扩展说明

  • 状态持久化:如果需要bot重启后不丢失用户状态,可以将userStates替换为数据库(如Redis、SQLite),用用户ID作为键存储状态。
  • 复杂状态流转:对于更复杂的业务流程,可以将状态逻辑封装为单独的结构体或函数,避免handler代码过于臃肿。
  • 超时处理:可以为状态添加超时机制,比如用户长时间未响应时自动重置为空闲状态,需要结合定时器实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 10:51:38