最终目标是:
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'。也许我需要先创建堆栈,但我找不到正确的方法。 谢谢。
答案 0 :(得分:2)
免责声明:我的答案比您的问题更通用。请参阅答案末尾的ConversationUpdate中的Dialog启动
发送&#34;真实&#34;可以在Microsoft Bot Framework中使用主动消息,但不能在每个通道中使用。
重点:当我说&#34;真实的主动消息&#34;时,要区别于documentation中的Microsoft正在调用的proactive message
内容(来自机器人,但对于过去已经谈过的人):我在这里谈论的是向客户发送一条消息,该客户之前从未与机器人交谈过。
发送主动消息的主要痛苦是了解您将在Conversation对象中设置的2个ChannelAccount(=用户,即您的机器人和您的最终用户)的 ID 。
那些ChannelAccount对象获得的ID取决于您当前使用的频道。例如,我之前在Bot Framework工作期间分析的内容:
如果您想知道在获得这些ID后如何开始对话,请跳到最后一部分。
Slack的优点是可以通过仅知道一些信息并请求其API来获得开始对话所需的所有参数。例如,您只需知道即可开始对话:
然后你可以使用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}'");
}
}
如何开始对话? 一旦您了解了有关机器人和用户的相关信息(再次依赖于频道),您就可以开始对话了。
示例方法:
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);
}
}