如何实现Telegram Bot发送消息后等待用户回复的模式
问题场景
- 开发Telegram Bot时需要实现:机器人发送提示消息后,暂停常规命令响应逻辑,等待并捕获用户下一条回复的交互模式
- 当前项目依赖NuGet包:
- Telegram.Bot 16.0.1
- Newtonsoft.Json
- 现有业务代码(存在逻辑缺陷:发送提示后未等待用户输入,直接执行了置顶、发送通知逻辑):
case "Ask question❓": { await client.SendTextMessageAsync(chatId: msg.Chat.Id, $"Write your question, to pin it on the chat-forum «{msg.Chat.Title}»!\n❗Please note that your question must follow the rules of this chat-forum.", replyMarkup: new ReplyKeyboardRemove()); await client.PinChatMessageAsync(chatId: msg.Chat.Id, messageId: msg.MessageId); await client.SendTextMessageAsync(chatId: msg.Chat.Id, $"🔥New question from {msg.From.Username}!", replyToMessageId: msg.MessageId, replyMarkup: GetButtons()); break; }
实现方案
Telegram Bot本身是Webhook/长轮询的无状态模型,不存在原生的阻塞式「等待回复」接口,核心实现逻辑是自行维护用户会话状态,根据状态分流处理收到的消息。
步骤1:定义会话状态存储
单实例部署场景直接用线程安全的内存字典存储用户状态即可,无需额外依赖:
// 存储结构:Key为用户ID,Value为用户当前会话状态 private static readonly ConcurrentDictionary<long, UserSession> _sessionStore = new(); // 定义机器人状态枚举 public enum BotStatus { Idle, // 空闲状态,走常规命令匹配逻辑 WaitForQuestionSubmit // 等待用户提交问题 } // 会话实体,存储状态和上下文数据 public class UserSession { public BotStatus Status { get; set; } public string ForumName { get; set; } public int TriggerMessageId { get; set; } }
步骤2:修改触发命令的处理逻辑
用户点击「Ask question❓」按钮时,仅发送提示消息、标记用户状态,不要执行需要等用户输入后才触发的置顶、发通知逻辑:
case "Ask question❓": { // 标记用户当前为等待提交问题状态,存入上下文 _sessionStore[msg.From.Id] = new UserSession { Status = BotStatus.WaitForQuestionSubmit, ForumName = msg.Chat.Title, TriggerMessageId = msg.MessageId }; // 发送输入提示 await client.SendTextMessageAsync( chatId: msg.Chat.Id, text: $"Write your question, to pin it on the chat-forum «{msg.Chat.Title}»!\n❗Please note that your question must follow the rules of this chat-forum.", replyMarkup: new ReplyKeyboardRemove() ); break; }
步骤3:在消息处理入口增加状态分流
每次收到用户新消息时,优先检查用户是否处于等待输入状态,如果是则直接走对应场景的处理逻辑,处理完重置状态,不再走常规命令匹配:
// 消息处理入口优先校验会话状态 if (_sessionStore.TryGetValue(msg.From.Id, out var session) && session.Status == BotStatus.WaitForQuestionSubmit) { // 当前消息就是用户提交的问题内容 string questionContent = msg.Text; // 执行后续业务逻辑 await client.PinChatMessageAsync(chatId: msg.Chat.Id, messageId: session.TriggerMessageId); await client.SendTextMessageAsync( chatId: msg.Chat.Id, text: $"🔥New question from {msg.From.Username}:\n{questionContent}", replyToMessageId: session.TriggerMessageId, replyMarkup: GetButtons() ); // 处理完成,重置用户状态为空闲 session.Status = BotStatus.Idle; // 终止后续流程,不要进入常规命令匹配 return; } // 原有常规命令匹配逻辑(比如上面的case "Ask question❓"分支)放在状态判断之后
扩展说明
- 如果需要增加超时自动取消功能,给
UserSession加个LastOperateTime时间戳字段,加个定时任务定期清理超过设定时长(比如10分钟)的等待状态,同时给用户发提示说明等待超时即可 - 多交互场景可以直接扩展
BotStatus枚举,比如增加WaitForEmailInput、WaitForFeedbackSubmit等值,对应不同的处理分支即可 - 如果是多实例部署,把内存字典替换成Redis等共享存储即可,逻辑完全一致
内容的提问来源于stack exchange,提问作者LanceMinecraft
相关产品推荐
相关产品推荐

