Bot Framework用户存储及Facebook Bot主动消息技术问询
一、正确存储用户实现主动推送的方式
首先得明确:Redis Store是会话存储,它的设计初衷是临时保存对话上下文,并非用来持久化用户数据——这就是你发现它无法永久保存用户ID的核心原因。
要实现稳定的主动消息推送,你需要改用持久化数据库(比如PostgreSQL、MySQL、SQL Server,轻量场景用SQLite也可),而且核心要存储的不是单一的用户ID,而是完整的ConversationReference对象。这个对象包含了触发主动对话所需的所有关键上下文:
- 用户的永久标识(
User.Id,对应Facebook的用户ID) - 会话ID(
Conversation.Id) - Bot自身的标识(
Bot.Id) - 平台通道信息(
ChannelId,比如facebook)
存储完整的ConversationReference的好处是,后续调用ContinueConversationAsync发起主动对话时,能直接复用对象,不用手动拼接各种参数,避免不同平台的会话规则差异导致的推送失败。同时,还要把这个对象和用户设置的提醒条件(比如触发时间、提醒类型)绑定存储,这样才能在满足条件时精准推送。
二、两种添加用户ID方法的选择
虽然你没贴具体代码,但常见的两种添加场景无非是以下两类,你可以结合业务逻辑判断:
场景1:用户主动触发设置时添加
比如用户发送“设置提醒”指令后,在处理该意图的对话逻辑里,将用户的ConversationReference存入数据库。
- 适用场景:只有明确需要接收提醒的用户才存储,避免无效数据堆积。
- 优势:精准度高,数据库仅保留有需求的用户,资源占用小。
场景2:用户首次互动时(MembersAdded事件)添加
在Bot的OnMembersAddedAsync事件处理中,当用户首次加入对话时自动存储ConversationReference。
- 适用场景:需要给所有和Bot互动过的用户推送消息(比如通用通知类内容)。
- 注意:要判断加入的是真实用户(不是Bot自身),避免存储无效的Bot ID。
核心判断标准:如果你的Bot只有用户主动设置提醒后才需要推送,选场景1;如果是所有互动过的用户都可能收到消息,选场景2。另外不管哪种方式,都要确保存储的是turnContext.Activity.From.Id(Facebook的永久用户ID),而非临时会话ID,还要加重复校验——比如存储前先查询数据库,避免同一个用户被多次插入。
三、主动发消息时移除拉黑用户的方法
当用户拉黑Bot后,平台会拒绝Bot的消息请求,你可以通过捕获发送异常来识别并清理这些无效用户:
捕获发送异常:调用
ContinueConversationAsync发送消息时,用try-catch包裹代码。Facebook平台通常会返回403 Forbidden状态码,错误信息里会包含类似"User has blocked the bot"或"Cannot send message to user"的关键词。识别拉黑状态:在catch块中解析异常响应,判断是否是用户拉黑导致的失败。示例代码(C#):
try { await adapter.ContinueConversationAsync(botAppId, conversationReference, async (turnContext) => { await turnContext.SendActivityAsync("你的提醒内容"); }, cancellationToken); } catch (ApiException ex) { // 匹配Facebook的拉黑错误特征 if (ex.StatusCode == HttpStatusCode.Forbidden && ex.Message.Contains("blocked")) { // 从数据库中删除该用户的记录 await RemoveUserFromDatabase(conversationReference.User.Id); } }
- 定期清理(可选):除了实时捕获异常,你也可以定期批量测试用户的可达性,但这种方式效率较低,实时捕获异常是更高效准确的方案。
内容的提问来源于stack exchange,提问作者PirateApp

