有没有办法在ConversationUpdate事件上启动表单流对话框?

时间:2017-06-04 11:23:14

标签: c# botframework slack

最终目标是:
botduilder sdk女士,Slack的聊天机器人。

当用户被添加到与bot的群聊(ActivityTypes.ConversationUpdate和activity.MembersAdded.Count!= 0)时,我想立即从使用表单流对话通过私人消息收集他的数据开始。但是从这一点上我找不到办法做到这一点,似乎你需要从用户那里得到一些消息。

这是否正确,唯一的解决方法是先要求用户输入一些文字(或按钮“让我们开始”)?
我也试过这个解决方案,从主动的例子中解析对话框:

else if (activity.Type == ActivityTypes.ConversationUpdate)
{            
    if (activity.MembersAdded.Count != 0)
    {
        using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
        {
            var botData = scope.Resolve<IBotData>();
            await botData.LoadAsync(CancellationToken.None);

            //This is our dialog stack
            var stack = scope.Resolve<IDialogStack>();

            //interrupt the stack. This means that we're stopping whatever conversation that is currently happening with the user
            //Then adding this stack to run and once it's finished, we will be back to the original conversation                            

            var questions = new WelcomePoll();
            var myform = new FormDialog<WelcomePoll>(questions, WelcomePoll.BuildForm, FormOptions.PromptInStart, null);

            stack.Call(myform, Resume); //GOT "STACK IS EMPTY" EXCEPTION                        

                        return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
        }
    }
}

但是在stack.Call行上得到System.InvalidOperationException:'Stack is empty'。也许我需要先创建堆栈,但我找不到正确的方法。 谢谢。

1 个答案:

答案 0 :(得分:2)

免责声明:我的答案比您的问题更通用。请参阅答案末尾的ConversationUpdate中的Dialog启动

发送&#34;真实&#34;可以在Microsoft Bot Framework中使用主动消息,但不能在每个通道中使用。

重点:当我说&#34;真实的主动消息&#34;时,要区别于documentation中的Microsoft正在调用的proactive message内容(来自机器人,但对于过去已经谈过的人):我在这里谈论的是向客户发送一条消息,该客户之前从未与机器人交谈过。

发送主动消息

发送主动消息的主要痛苦是了解您将在Conversation对象中设置的2个ChannelAccount(=用户,即您的机器人和您的最终用户)的 ID

那些ChannelAccount对象获得的ID取决于您当前使用的频道。例如,我之前在Bot Framework工作期间分析的内容:

  • 对于Skype,机器人ID是&#34; 28的串联:&#34;和它的Microsoft AppId,但你需要知道用户ID和它们之间的对话必须已经存在=&gt;你不能真正做一个主动的消息
  • 对于Facebook消息,机器人ID是FB页面ID,但用户ID是您无法轻易请求的特定消息ID =&gt;复杂,必须熟悉Facebook API
  • 对于Slack,机器人ID是其Slack的Bot_id的串联,&#39;:&#39;和Slack的Team_id。用户ID是其Slack的id的串联,&#39;:&#39;和Slack的Team_id =&gt;有趣
  • 对于Twilio SMS,机器人ID和用户ID是他们的电话号码=&gt;易
  • 对于电子邮件渠道,这是他们的电子邮件
  • ...

如果您想知道在获得这些ID后如何开始对话,请跳到最后一部分。

Slack ID focus

Slack的优点是可以通过仅知道一些信息并请求其API来获得开始对话所需的所有参数。例如,您只需知道即可开始对话:

  • Bot Slack的用户名
  • 用户电子邮件地址或Slack的用户名

然后你可以使用Slack的API来获得机器人真正需要的东西。

如何获取Slack数据?

1调用服务的方法,1使用它来获取正确的数据:

public class SlackService
{
    internal static async Task<SlackMembersRootObject> GetSlackUsersList(string slackApiToken, CancellationToken ct)
    {
        using (var client = new HttpClient())
        {
            using (var response = await client.GetAsync($"https://slack.com/api/users.list?token=" + $"{slackApiToken}", ct).ConfigureAwait(false))
            {
                var jsonString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                var rootObject = Newtonsoft.Json.JsonConvert.DeserializeObject<SlackMembersRootObject>(jsonString);
                return rootObject;
            }
        }
    }
}

public static async Task StartSlackDirectMessage(string msAppId, string msAppPassword, string slackApiToken, string message, string botSlackUsername, string botName, string destEmailAddress, string destName, string lang, CancellationToken ct)
{
    // Getting Slack user (and bot) list
    var slackUsersList = await SlackService.GetSlackUsersList(slackApiToken, CancellationToken.None);

    // Get Bot SlackId by searching its "username", you can also search by other criteria
    var slackBotUser = slackUsersList.Members.FirstOrDefault(x => x.Name == botSlackUsername);
    if (slackBotUser == null)
    {
        throw new Exception($"Slack API : no bot found for name '{botSlackUsername}'");
    }

    // Get User SlackId by email address
    var slackTargetedUser = slackUsersList.Members.FirstOrDefault(x => x.Profile.Email == destEmailAddress);
    if (slackTargetedUser != null)
    {
        // if found, starting the conversation by passing the right IDs
        await StartDirectMessage(msAppId, msAppPassword, "https://slack.botframework.com/", "slack", $"{slackBotUser.Profile.Bot_id}:{slackBotUser.Team_id}", botName, $"{slackTargetedUser.Id}:{slackTargetedUser.Team_id}", destName, message, lang, ct);
    }
    else
    {
        throw new Exception($"Slack API : no user found for email '{destEmailAddress}'");
    }
}

如何开始对话? 一旦您了解了有关机器人和用户的相关信息(再次依赖于频道),您就可以开始对话了。

  1. 创建对话
  2. 创建消息
  3. 发送消息给对话
  4. 示例方法:

    private static async Task StartDirectMessage(string msAppId, string msAppPassword, string connectorClientUrl, string channelId, string fromId, string fromName, string recipientId, string recipientName, string message, string locale, CancellationToken token)
    {
        // Init connector
        MicrosoftAppCredentials.TrustServiceUrl(connectorClientUrl, DateTime.Now.AddDays(7));
        var account = new MicrosoftAppCredentials(msAppId, msAppPassword);
        var connector = new ConnectorClient(new Uri(connectorClientUrl), account);
    
        // Init conversation members
        ChannelAccount channelFrom = new ChannelAccount(fromId, fromName);
        ChannelAccount channelTo = new ChannelAccount(recipientId, recipientName);
    
        // Create Conversation
        var conversation = await connector.Conversations.CreateDirectConversationAsync(channelFrom, channelTo);
    
        // Create message Activity and send it to Conversation
        IMessageActivity newMessage = Activity.CreateMessageActivity();
        newMessage.Type = ActivityTypes.Message;
        newMessage.From = channelFrom;
        newMessage.Recipient = channelTo;
    
        newMessage.Locale = (locale ?? "en-US");
        newMessage.ChannelId = channelId;
        newMessage.Conversation = new ConversationAccount(id: conversation.Id);
        newMessage.Text = message;
        await connector.Conversations.SendToConversationAsync((Activity)newMessage);
    }
    

    回到你的背景:   - 您在群组对话(ActivityTypes.ConversationUpdate and activity.MembersAdded.Count != 0)上获得了ConversationUpdate:在此ConversationUpdate中,您可以获取userId和您的botId。然后你开始一个新的对话(使用上面的代码),然后创建一个你不发送的虚假消息并从中获取IDialogTask:你将能够启动你的对话

    示例:

    if (activity.MembersAdded.Count != 0)
    {
        // We have to create a new conversation between Bot and AddedUser
    
        #region Conversation creation
        // Connector init
        MicrosoftAppCredentials.TrustServiceUrl(activity.ServiceUrl, DateTime.Now.AddDays(7));
        var account = new MicrosoftAppCredentials("yourMsAppId", "yourMsAppPassword");
        var connector = new ConnectorClient(new Uri(activity.ServiceUrl), account);
    
        // From the conversationUpdate message, Recipient = bot and From = User
        ChannelAccount botChannelAccount = new ChannelAccount(activity.Recipient.Id, activity.Recipient.Name);
        ChannelAccount userChannelAccount = new ChannelAccount(activity.From.Id, activity.From.Name);
    
        // Create Conversation
        var conversation = await connector.Conversations.CreateDirectConversationAsync(botChannelAccount, userChannelAccount);
        #endregion
    
        // Then we prepare a fake message from bot to user, mandatory to get the working IDialogTask. This message MUST be from User to Bot, if you want the following Dialog to be from Bot to User
        IMessageActivity fakeMessage = Activity.CreateMessageActivity();
        fakeMessage.From = userChannelAccount;
        fakeMessage.Recipient = botChannelAccount;
        fakeMessage.ChannelId = activity.ChannelId;
        fakeMessage.Conversation = new ConversationAccount(id: conversation.Id);
    
        fakeMessage.ServiceUrl = activity.ServiceUrl;
        fakeMessage.Id = Guid.NewGuid().ToString();
    
        // Then use this fake message to launch the new dialog
        using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, fakeMessage))
        {
            var botData = scope.Resolve<IBotData>();
            await botData.LoadAsync(CancellationToken.None);
    
            //This is our dialog stack
            var task = scope.Resolve<IDialogTask>();
    
            //interrupt the stack. This means that we're stopping whatever conversation that is currently happening with the user
            //Then adding this stack to run and once it's finished, we will be back to the original conversation
            var dialog = new DemoDialog();
            task.Call(dialog.Void<object, IMessageActivity>(), null);
    
            await task.PollAsync(CancellationToken.None);
    
            //flush dialog stack
            await botData.FlushAsync(CancellationToken.None);
    
        }
    }