如何在Skype Bot的CallingController中获取MessageController的数据?
在Microsoft Bot Framework中关联MessageController与CallingController的会话数据
咱先理清楚核心逻辑:Message和Calling是Skype机器人的两个独立请求通道,但它们共享同一个用户身份和会话上下文。要实现通话时等待用户聊天输入并处理,关键是通过Bot Framework的状态管理机制来跨控制器存储和读取会话状态——比如标记通话是否在等待输入、存储通话会话ID,以及用户输入的值。
下面是具体的实现步骤:
1. 配置状态存储与自定义状态属性
首先得在项目里配置状态服务,同时定义一个自定义类来存储通话相关的会话数据,这样两个控制器都能访问到这些数据。
步骤1.1:定义会话数据类
创建一个简单的类来保存通话状态:
public class CallSessionData { // 标记是否正在等待用户输入 public bool WaitingForInput { get; set; } // 当前活跃的通话会话ID,用来关联具体通话 public string ActiveCallSessionId { get; set; } // 用户输入的字母数字值 public string UserInputValue { get; set; } }
步骤1.2:在Startup.cs中注册状态服务
在ConfigureServices方法里添加状态存储和状态属性的注册(开发环境用MemoryStorage,生产建议用Azure Blob/Cosmos DB):
public void ConfigureServices(IServiceCollection services) { // 注册状态存储(开发环境) services.AddSingleton<IStorage, MemoryStorage>(); // 注册用户状态(Skype的用户ID在Message和Calling通道是一致的) services.AddSingleton<UserState>(); // 注册我们自定义的CallSessionData状态属性 services.AddSingleton<StatePropertyAccessor<CallSessionData>>(sp => { var userState = sp.GetService<UserState>(); return userState.CreateProperty<CallSessionData>("CallSessionData"); }); }
2. 在CallingController中标记“等待输入”状态
当通话流程走到需要用户输入的环节时,更新会话状态,标记为等待输入,并记录当前通话的会话ID。
示例代码(CallingController中的处理方法):
[Route("api/calling")] [ApiController] public class CallingController : ControllerBase { private readonly StatePropertyAccessor<CallSessionData> _callSessionDataAccessor; private readonly UserState _userState; public CallingController(StatePropertyAccessor<CallSessionData> callSessionDataAccessor, UserState userState) { _callSessionDataAccessor = callSessionDataAccessor; _userState = userState; } [HttpPost] public async Task<IActionResult> Post([FromBody] Activity activity) { // 处理通话逻辑,比如当需要用户输入时: if (activity.Type == ActivityTypes.Call && /* 判断是否走到需要输入的步骤 */) { var callSessionId = activity.CallId; var turnContext = new TurnContext(new BotAdapter(), activity); // 获取当前用户的会话数据 var callSessionData = await _callSessionDataAccessor.GetAsync(turnContext, () => new CallSessionData()); // 标记为等待输入,记录通话ID callSessionData.WaitingForInput = true; callSessionData.ActiveCallSessionId = callSessionId; // 保存状态 await _userState.SaveChangesAsync(turnContext); // 这里可以向用户发送通话中的提示,比如“请在聊天框输入你的字母数字码” // 然后进入等待状态,比如用轮询检查输入是否到达 await WaitForUserInput(turnContext, callSessionId); } return Ok(); } // 自定义等待用户输入的方法 private async Task WaitForUserInput(TurnContext turnContext, string callSessionId) { while (true) { await Task.Delay(1000); // 每秒检查一次状态 var callSessionData = await _callSessionDataAccessor.GetAsync(turnContext, () => new CallSessionData()); // 如果已经收到输入,并且属于当前通话 if (!callSessionData.WaitingForInput && !string.IsNullOrEmpty(callSessionData.UserInputValue) && callSessionData.ActiveCallSessionId == callSessionId) { // 执行预设的处理逻辑,比如验证输入、返回通话响应 await ProcessUserInput(callSessionData.UserInputValue, turnContext); // 重置状态 callSessionData.UserInputValue = null; callSessionData.ActiveCallSessionId = null; await _userState.SaveChangesAsync(turnContext); break; } // 可选:添加超时逻辑,比如等待5分钟后结束等待 // if (超时条件) break; } } // 处理用户输入的逻辑 private async Task ProcessUserInput(string inputValue, TurnContext turnContext) { // 这里写你的预设逻辑,比如验证格式、返回通话结果 await turnContext.SendActivityAsync($"收到你的输入:{inputValue},正在处理..."); // 通话相关的响应处理,比如播放语音提示等 } }
3. 在MessageController中接收输入并更新状态
当用户在聊天框发送消息时,检查当前会话是否处于“等待输入”状态,如果是,就保存输入值并标记状态为已完成。
示例代码(MessageController中的Post方法):
[Route("api/messages")] [ApiController] public class MessageController : ControllerBase { private readonly StatePropertyAccessor<CallSessionData> _callSessionDataAccessor; private readonly UserState _userState; public MessageController(StatePropertyAccessor<CallSessionData> callSessionDataAccessor, UserState userState) { _callSessionDataAccessor = callSessionDataAccessor; _userState = userState; } [HttpPost] public async Task<IActionResult> Post([FromBody] Activity activity) { if (activity.Type == ActivityTypes.Message) { var turnContext = new TurnContext(new BotAdapter(), activity); var callSessionData = await _callSessionDataAccessor.GetAsync(turnContext, () => new CallSessionData()); // 如果当前处于通话等待输入的状态 if (callSessionData.WaitingForInput) { // 获取用户输入的字母数字值 var userInput = activity.Text.Trim(); // 可以先验证输入格式,比如是否符合字母数字要求 if (Regex.IsMatch(userInput, @"^[a-zA-Z0-9]+$")) { callSessionData.UserInputValue = userInput; callSessionData.WaitingForInput = false; await _userState.SaveChangesAsync(turnContext); await turnContext.SendActivityAsync($"已收到你的输入:{userInput},通话将继续处理"); } else { await turnContext.SendActivityAsync("请输入有效的字母数字值"); } } else { // 处理非通话场景的聊天消息 await turnContext.SendActivityAsync("欢迎使用机器人,请先发起通话操作"); } } return Ok(); } }
4. 关键注意事项
- 生产环境状态存储:MemoryStorage只适合开发测试,生产环境一定要用Azure Blob Storage或者Azure Cosmos DB,避免重启机器人后丢失状态数据。
- 用户ID一致性:Skype的
activity.From.Id在Message和Calling通道中是相同的,所以用UserState来存储数据是可靠的。 - 超时处理:一定要给等待输入的逻辑添加超时机制,避免通话无限期等待用户输入。
- 并发处理:如果同一个用户可能发起多个通话,要注意用ActiveCallSessionId来区分不同的通话会话,避免数据混乱。
内容的提问来源于stack exchange,提问作者Mainak Chatterjee
相关产品推荐
相关产品推荐

