Bot Framework(C#)如何实现静态常驻建议操作菜单
问题根因
Bot Framework 的 Suggested Actions 从设计上就是单消息级交互控件,不支持会话级常驻:它只会挂载在所属的单条出站消息上,只要用户发送新消息,上一条消息带的 Suggested Actions 会被客户端自动移除,根本不存在“一次发送永久留存”的可能。
你之前碰到的两个问题全是这个机制导致的:
- 写在
OnTurnAsync里每次都重复弹:OnTurnAsync会响应所有类型的会话活动(普通消息、成员进群、事件推送、打字状态通知等等),没做过滤就会每轮交互都单独发一条带菜单的消息,必然重复 - 写在
OnMemberAddedAsync里首次显示后就消失:用户发第一条消息的时候,欢迎语上挂的菜单就被客户端自动清除了,这是控件默认行为,不是代码逻辑错误。
实现方案
方案1:使用通道原生持久菜单(推荐,真正实现静态常驻)
这是唯一能做到“加载后永久驻留、不随消息流消失”效果的方案,不需要在Bot的对话逻辑里重复处理菜单:
- Web Chat 通道:在前端初始化Web Chat时直接配置静态持久菜单,菜单会固定在输入区域旁,不会随消息滚动、不会因为用户发消息消失
- Microsoft Teams 通道:在Teams应用清单文件中配置全局命令菜单,菜单会固定在输入框下方,全会话周期生效
- Facebook Messenger、Line、WhatsApp等第三方通道:各通道均提供官方的持久化菜单配置入口,配置后全局生效,完全不需要Bot端发送菜单消息。
方案2:无原生菜单支持的通道,自动给所有出站消息附加菜单
如果所用通道不支持原生全局菜单,可以通过重写OnSendActivities方法,给所有发给用户的普通消息自动挂载菜单按钮,实现视觉上的常驻效果,同时避免重复发菜单的问题。
参考实现代码:
protected override async Task<ResourceResponse[]> OnSendActivitiesAsync(ITurnContext turnContext, List<Activity> activities, CancellationToken cancellationToken) { // 定义固定菜单项 var persistentMenu = new List<CardAction> { new CardAction(ActionTypes.ImBack, "查询订单", value: "查询订单"), new CardAction(ActionTypes.ImBack, "申请售后", value: "申请售后"), new CardAction(ActionTypes.ImBack, "联系人工", value: "联系人工") }; foreach (var activity in activities) { // 仅处理普通文本/卡片消息,跳过打字通知、trace日志、成员变更等非消息类活动 if (activity.Type != ActivityTypes.Message) continue; // 业务逻辑已经自定义了建议操作的话,不覆盖原有按钮,避免影响特殊交互流程 if (activity.SuggestedActions?.Actions != null && activity.SuggestedActions.Actions.Any()) continue; // 给当前消息挂载全局菜单 activity.SuggestedActions = new SuggestedActions { Actions = persistentMenu }; } return await base.OnSendActivitiesAsync(turnContext, activities, cancellationToken); }
这个实现的效果:
- 用户加入对话收到的第一条欢迎消息会自动带菜单,不需要单独在
OnMemberAddedAsync写发送逻辑 - 后续每一次Bot回复的消息都会自动附带菜单,上一条消息的菜单被客户端清除时,新回复的菜单刚好展示,用户视角下菜单不会消失
- 不会出现单独发送菜单消息导致的对话流冗余、重复展示问题
- 特殊业务场景需要自定义操作按钮时,全局菜单不会覆盖业务逻辑。
避坑说明
不要在OnTurnAsync里单独发送带菜单的活动,会导致菜单和正常回复割裂,对话流里出现大量重复的菜单消息,严重影响交互体验。
内容的提问来源于stack exchange,提问作者di007
相关产品推荐
相关产品推荐

