Facebook adapter/add handover feature
* Add Facebook handover feature
This commit is contained in:
Родитель
c20ac4ce0e
Коммит
c5d2be4754
|
@ -165,6 +165,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapt
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Facebook", "libraries\Adapters\Microsoft.Bot.Builder.Adapters.Facebook\Microsoft.Bot.Builder.Adapters.Facebook.csproj", "{C8E31CD2-89D4-4659-9557-43EC9C99D984}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot\Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot.csproj", "{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug - NuGet Packages|Any CPU = Debug - NuGet Packages|Any CPU
|
||||
|
@ -796,6 +798,16 @@ Global
|
|||
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -869,6 +881,7 @@ Global
|
|||
{9F7B2BDF-973B-4639-B890-357EB967B2ED} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
|
||||
{E372D54C-73AA-41DB-A471-81348AC37F36} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
|
||||
{C8E31CD2-89D4-4659-9557-43EC9C99D984} = {6230B915-B238-4E57-AAC4-06B4498F540F}
|
||||
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7173C9F3-A7F9-496E-9078-9156E35D6E16}
|
||||
|
|
|
@ -21,6 +21,11 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
{
|
||||
private const string HubModeSubscribe = "subscribe";
|
||||
|
||||
/// <summary>
|
||||
/// The constant ID representing the page inbox.
|
||||
/// </summary>
|
||||
private const string PageInboxId = "263902037430900";
|
||||
|
||||
private readonly FacebookClientWrapper _facebookClient;
|
||||
|
||||
/// <summary>
|
||||
|
@ -61,9 +66,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
|
||||
foreach (var activity in activities)
|
||||
{
|
||||
if (activity.Type != ActivityTypes.Message)
|
||||
if (activity.Type != ActivityTypes.Message && activity.Type != ActivityTypes.Event)
|
||||
{
|
||||
throw new Exception("Only Activities of type Message are supported for sending.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var message = FacebookHelper.ActivityToFacebook(activity);
|
||||
|
@ -76,6 +81,23 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
|
||||
var res = await _facebookClient.SendMessageAsync("/me/messages", message, null, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (activity.Type == ActivityTypes.Event)
|
||||
{
|
||||
if (activity.Name.Equals("pass_thread_control", StringComparison.InvariantCulture))
|
||||
{
|
||||
var recipient = (string)activity.Value == "inbox" ? PageInboxId : (string)activity.Value;
|
||||
await _facebookClient.PassThreadControlAsync(recipient, activity.Conversation.Id, "Pass thread control").ConfigureAwait(false);
|
||||
}
|
||||
else if (activity.Name.Equals("take_thread_control", StringComparison.InvariantCulture))
|
||||
{
|
||||
await _facebookClient.TakeThreadControlAsync(activity.Conversation.Id, "Take thread control from a secondary receiver").ConfigureAwait(false);
|
||||
}
|
||||
else if (activity.Name.Equals("request_thread_control", StringComparison.InvariantCulture))
|
||||
{
|
||||
await _facebookClient.RequestThreadControlAsync(activity.Conversation.Id, "Request thread control to the primary receiver").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
var response = new ResourceResponse()
|
||||
{
|
||||
Id = res,
|
||||
|
@ -153,31 +175,33 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
return;
|
||||
}
|
||||
|
||||
string stringifyBody;
|
||||
string stringifiedBody;
|
||||
|
||||
using (var sr = new StreamReader(request.Body))
|
||||
{
|
||||
stringifyBody = sr.ReadToEnd();
|
||||
stringifiedBody = sr.ReadToEnd();
|
||||
}
|
||||
|
||||
if (!_facebookClient.VerifySignature(request, stringifyBody))
|
||||
if (!_facebookClient.VerifySignature(request, stringifiedBody))
|
||||
{
|
||||
await FacebookHelper.WriteAsync(response, HttpStatusCode.Unauthorized, string.Empty, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
|
||||
throw new Exception("WARNING: Webhook received message with invalid signature. Potential malicious behavior!");
|
||||
}
|
||||
|
||||
FacebookResponseEvent facebookEvent = null;
|
||||
FacebookResponseEvent facebookResponseEvent = null;
|
||||
|
||||
facebookEvent = JsonConvert.DeserializeObject<FacebookResponseEvent>(stringifyBody);
|
||||
facebookResponseEvent = JsonConvert.DeserializeObject<FacebookResponseEvent>(stringifiedBody);
|
||||
|
||||
foreach (var entry in facebookEvent.Entry)
|
||||
foreach (var entry in facebookResponseEvent.Entry)
|
||||
{
|
||||
var payload = new List<FacebookMessage>();
|
||||
|
||||
payload = entry.Changes ?? entry.Messaging;
|
||||
payload = entry.Changes ?? entry.Messaging ?? entry.Standby;
|
||||
|
||||
foreach (var message in payload)
|
||||
{
|
||||
message.IsStandby = entry.Standby != null;
|
||||
|
||||
var activity = FacebookHelper.ProcessSingleMessage(message);
|
||||
|
||||
using (var context = new TurnContext(this, activity))
|
||||
|
@ -185,24 +209,6 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
await RunPipelineAsync(context, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle standby messages (this bot is not the active receiver)
|
||||
if (entry.Standby != null)
|
||||
{
|
||||
payload = entry.Standby;
|
||||
|
||||
foreach (var message in payload)
|
||||
{
|
||||
// Indicate that this message was received in standby mode rather than normal mode.
|
||||
message.Standby = true;
|
||||
var activity = FacebookHelper.ProcessSingleMessage(message);
|
||||
|
||||
using (var context = new TurnContext(this, activity))
|
||||
{
|
||||
await RunPipelineAsync(context, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,5 +159,99 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
|
||||
await FacebookHelper.WriteAsync(response, statusCode, challenge, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts webhook control events to Facebook.
|
||||
/// </summary>
|
||||
/// <param name="postType">The REST post type (GET, PUT, POST, etc).</param>
|
||||
/// <param name="content">The string content to be posted to Facebook.</param>
|
||||
/// <returns>A bool indicating the success of the operation.</returns>
|
||||
public virtual async Task<bool> PostToFacebookApiAsync(string postType, string content)
|
||||
{
|
||||
if (postType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(postType));
|
||||
}
|
||||
|
||||
if (content == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(content));
|
||||
}
|
||||
|
||||
var graphApiBaseUrl = $"https://{_options.FacebookApiHost}/{_options.FacebookApiVersion + postType}?access_token={_options.FacebookAccessToken}";
|
||||
var requestPath = string.Format(System.Globalization.CultureInfo.InvariantCulture, graphApiBaseUrl, postType, _options.FacebookAccessToken);
|
||||
var stringContent = new StringContent(content, Encoding.UTF8, "application/json");
|
||||
|
||||
using (var requestMessage = new HttpRequestMessage())
|
||||
{
|
||||
requestMessage.Method = new HttpMethod("POST");
|
||||
requestMessage.RequestUri = new Uri(requestPath);
|
||||
requestMessage.Content = stringContent;
|
||||
requestMessage.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
|
||||
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
var res = await client.SendAsync(requestMessage, CancellationToken.None).ConfigureAwait(false);
|
||||
return res.IsSuccessStatusCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the request_thread_control webhook event to Facebook.
|
||||
/// </summary>
|
||||
/// <param name="userId">The sender user Id.</param>
|
||||
/// <param name="message">An optional message for the metadata paremter.</param>
|
||||
/// <returns>A bool value indicating the success of the operation.</returns>
|
||||
public virtual async Task<bool> RequestThreadControlAsync(string userId, string message)
|
||||
{
|
||||
if (userId == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(userId));
|
||||
}
|
||||
|
||||
var content = new { recipient = new { id = userId }, metadata = message };
|
||||
return await PostToFacebookApiAsync("/me/request_thread_control", JsonConvert.SerializeObject(content)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the take_thread_control webhook event to Facebook.
|
||||
/// </summary>
|
||||
/// <param name="userId">The sender user Id.</param>
|
||||
/// <param name="message">An optional message for the metadata paremter.</param>
|
||||
/// <returns>A bool value indicating the success of the operation.</returns>
|
||||
public virtual async Task<bool> TakeThreadControlAsync(string userId, string message)
|
||||
{
|
||||
if (userId == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(userId));
|
||||
}
|
||||
|
||||
var content = new { recipient = new { id = userId }, metadata = message };
|
||||
return await PostToFacebookApiAsync("/me/take_thread_control", JsonConvert.SerializeObject(content)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the pass_thread_control webhook event to Facebook.
|
||||
/// </summary>
|
||||
/// <param name="targetAppId">The Id of the target app to pass control to.</param>
|
||||
/// <param name="userId">The sender user Id.</param>
|
||||
/// <param name="message">An optional message for the metadata paremter.</param>
|
||||
/// <returns>A bool value indicating the success of the operation.</returns>
|
||||
public virtual async Task<bool> PassThreadControlAsync(string targetAppId, string userId, string message)
|
||||
{
|
||||
if (targetAppId == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(targetAppId));
|
||||
}
|
||||
|
||||
if (userId == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(userId));
|
||||
}
|
||||
|
||||
var content = new { recipient = new { id = userId }, target_app_id = targetAppId, metadata = message };
|
||||
return await PostToFacebookApiAsync("/me/pass_thread_control", JsonConvert.SerializeObject(content)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
|
||||
{
|
||||
public class MessagePayload
|
||||
public class AttachmentPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the url of the attachment.
|
|
@ -19,6 +19,6 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
|
|||
/// </summary>
|
||||
/// <value>Payload of the attachment.</value>
|
||||
[JsonProperty(PropertyName = "payload")]
|
||||
public MessagePayload Payload { get; set; }
|
||||
public AttachmentPayload Payload { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,21 +20,21 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
|
|||
public long Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the messaging list.
|
||||
/// Gets the messaging list.
|
||||
/// </summary>
|
||||
/// <value>List containing one messaging object. Note that even though this is an enumerable, it will only contain one object.</value>
|
||||
public List<FacebookMessage> Messaging { get; set; }
|
||||
public List<FacebookMessage> Messaging { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the changes list.
|
||||
/// Gets the changes list.
|
||||
/// </summary>
|
||||
/// <value>List containing the list of changes.</value>
|
||||
public List<FacebookMessage> Changes { get; set; }
|
||||
public List<FacebookMessage> Changes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the standby messages list.
|
||||
/// Gets the standby messages list.
|
||||
/// </summary>
|
||||
/// <value>List containing the messages sent while in standby mode.</value>
|
||||
public List<FacebookMessage> Standby { get; set; }
|
||||
public List<FacebookMessage> Standby { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
|
|||
[JsonProperty(PropertyName = "object")]
|
||||
public string ResponseObject { get; set; }
|
||||
|
||||
public List<FacebookEntry> Entry { get; set; }
|
||||
public List<FacebookEntry> Entry { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
|
||||
{
|
||||
public class FacebookPassThreadControl : FacebookThreadControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or Sets the app id of the new owner.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 263902037430900 for the page inbox.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// The app id of the new owner.
|
||||
/// </value>
|
||||
[JsonProperty("new_owner_app_id")]
|
||||
public string NewOwnerAppId { get; set; }
|
||||
|
||||
[JsonProperty("previous_owner_app_id")]
|
||||
public string PreviousOwnerAppId { get; set; }
|
||||
|
||||
[JsonProperty("requested_owner_app_id")]
|
||||
public string RequestedOwnerAppId { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
|
||||
{
|
||||
/// <summary>
|
||||
/// A Facebook thread control message, including appid of requested thread owner and an optional message to send with the request
|
||||
/// <see cref="Metadata"/>.
|
||||
/// </summary>
|
||||
public class FacebookRequestThreadControl : FacebookThreadControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or Sets the app id of the requested owner.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 263902037430900 for the page inbox.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// the app id of the requested owner.
|
||||
/// </value>
|
||||
[JsonProperty("requested_owner_app_id")]
|
||||
public string RequestedOwnerAppId { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
|
||||
{
|
||||
public class FacebookTakeThreadControl : FacebookThreadControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or Sets the app id of the previous owner.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 263902037430900 for the page inbox.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// The app id of the previous owner.
|
||||
/// </value>
|
||||
[JsonProperty("previous_owner_app_id")]
|
||||
public string PreviousOwnerAppId { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
|
||||
{
|
||||
public class FacebookThreadControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or Sets the message sent from the requester.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example: "All yours!".
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// Message sent from the requester.
|
||||
/// </value>
|
||||
[JsonProperty("metadata")]
|
||||
public string Metadata { get; set; }
|
||||
}
|
||||
}
|
|
@ -32,7 +32,6 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
|
||||
facebookMessage.Message.Text = activity.Text;
|
||||
|
||||
// map these fields to their appropriate place
|
||||
if (activity.ChannelData != null)
|
||||
{
|
||||
facebookMessage = activity.GetChannelData<FacebookMessage>();
|
||||
|
@ -52,7 +51,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
|
||||
if (activity.Attachments != null && activity.Attachments.Count > 0)
|
||||
{
|
||||
var payload = JsonConvert.DeserializeObject<MessagePayload>(JsonConvert.SerializeObject(
|
||||
var payload = JsonConvert.DeserializeObject<AttachmentPayload>(JsonConvert.SerializeObject(
|
||||
activity.Attachments[0].Content,
|
||||
Formatting.None,
|
||||
new JsonSerializerSettings
|
||||
|
@ -60,13 +59,13 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
NullValueHandling = NullValueHandling.Ignore,
|
||||
}));
|
||||
|
||||
var attach = new FacebookAttachment
|
||||
var facebookAttachment = new FacebookAttachment
|
||||
{
|
||||
Type = activity.Attachments[0].ContentType,
|
||||
Payload = payload,
|
||||
};
|
||||
|
||||
facebookMessage.Message.Attachment = attach;
|
||||
facebookMessage.Message.Attachment = facebookAttachment;
|
||||
}
|
||||
|
||||
return facebookMessage;
|
||||
|
@ -112,18 +111,24 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
Text = null,
|
||||
};
|
||||
|
||||
if (message.PassThreadControl != null)
|
||||
{
|
||||
activity.Value = message.PassThreadControl;
|
||||
}
|
||||
else if (message.RequestThreadControl != null)
|
||||
{
|
||||
activity.Value = message.RequestThreadControl;
|
||||
}
|
||||
else if (message.TakeThreadControl != null)
|
||||
{
|
||||
activity.Value = message.TakeThreadControl;
|
||||
}
|
||||
|
||||
if (message.Message != null)
|
||||
{
|
||||
activity.Type = ActivityTypes.Message;
|
||||
activity.Text = message.Message.Text;
|
||||
activity.Type = activity.GetChannelData<FacebookMessage>().Message.IsEcho ? ActivityTypes.Event : ActivityTypes.Message;
|
||||
|
||||
if (activity.GetChannelData<FacebookMessage>().Message.IsEcho)
|
||||
{
|
||||
activity.Type = ActivityTypes.Event;
|
||||
}
|
||||
|
||||
// copy all fields (like attachments, sticker, quick_reply, nlp, etc.)
|
||||
activity.ChannelData = message.Message;
|
||||
if (message.Message.Attachments != null && message.Message.Attachments.Count > 0)
|
||||
{
|
||||
activity.Attachments = HandleMessageAttachments(message.Message);
|
||||
|
@ -138,6 +143,11 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
return activity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts attachments from the facebook message.
|
||||
/// </summary>
|
||||
/// <param name="message">The <see cref="Message"/>used for input.</param>
|
||||
/// <returns>A List of <see cref="Attachment"/>.</returns>
|
||||
public static List<Attachment> HandleMessageAttachments(Message message)
|
||||
{
|
||||
var attachmentsList = new List<Attachment>();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents;
|
||||
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook
|
||||
|
@ -79,16 +80,53 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
[JsonProperty(PropertyName = "sender_action")]
|
||||
public string SenderAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp.
|
||||
/// </summary>
|
||||
/// <value>Timestamp.</value>
|
||||
[JsonProperty(PropertyName = "timestamp")]
|
||||
public long TimeStamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the message was received while in Standby mode.
|
||||
/// </summary>
|
||||
/// <value>Value indicating whether the message was received while in Standby mode.</value>
|
||||
[JsonProperty(PropertyName = "standby")]
|
||||
public bool Standby { get; set; }
|
||||
public bool IsStandby { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the postback property.
|
||||
/// </summary>
|
||||
/// <value>The postback payload. Postbacks occur when a postback button, Get Started button, or persistent menu item is tapped.</value>
|
||||
[JsonProperty(PropertyName = "postback")]
|
||||
public FacebookPostBack PostBack { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the optin property.
|
||||
/// </summary>
|
||||
/// <value>The optin field. See https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_optins. </value>
|
||||
[JsonProperty(PropertyName = "optin")]
|
||||
public FacebookRecipient OptIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the contents of the pass_thread_control property.
|
||||
/// </summary>
|
||||
/// <value>A <see cref="FacebookPassThreadControl"> holding the contents of the pass_thread_control property.</value>.
|
||||
[JsonProperty(PropertyName = "pass_thread_control")]
|
||||
public FacebookPassThreadControl PassThreadControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the contents of the take_thread_control property.
|
||||
/// </summary>
|
||||
/// <value>A <see cref="FacebookTakeThreadControl"> holding the contents of the pass_thread_control property.</value>.
|
||||
[JsonProperty(PropertyName = "take_thread_control")]
|
||||
public FacebookTakeThreadControl TakeThreadControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the contents of the request_thread_control property.
|
||||
/// </summary>
|
||||
/// <value>A <see cref="FacebookRequestThreadControl"> holding the contents of the pass_thread_control property.</value>.
|
||||
[JsonProperty(PropertyName = "request_thread_control")]
|
||||
public FacebookRequestThreadControl RequestThreadControl { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,14 +26,14 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
/// <summary>
|
||||
/// Gets or sets a list of attachments.
|
||||
/// </summary>
|
||||
/// <value>Attachments.</value>
|
||||
/// <value>Attachments that could come with a Facebook message.</value>
|
||||
[JsonProperty(PropertyName = "attachments")]
|
||||
public List<FacebookAttachment> Attachments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attachment.
|
||||
/// </summary>
|
||||
/// <value>Attachment.</value>
|
||||
/// <value>Single attachment that will be sent back to Facebook.</value>
|
||||
[JsonProperty(PropertyName = "attachment")]
|
||||
public FacebookAttachment Attachment { get; set; }
|
||||
|
||||
|
@ -52,9 +52,20 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
|
|||
[JsonProperty(PropertyName = "quick_replies")]
|
||||
public List<FacebookQuickReply> QuickReplies { get; set; } = new List<FacebookQuickReply>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the message was sent from the page itself.
|
||||
/// </summary>
|
||||
/// <value>A value indicating whether the message was sent from the page itself.</value>
|
||||
[JsonProperty(PropertyName = "is_echo")]
|
||||
public bool IsEcho { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Mid.
|
||||
/// </summary>
|
||||
/// <value>Message ID.</value>
|
||||
[JsonProperty(PropertyName = "mid")]
|
||||
public string Mid { get; set; }
|
||||
|
||||
public bool ShouldSerializeQuickReplies()
|
||||
{
|
||||
return QuickReplies.Count > 0;
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover;
|
||||
using Microsoft.Bot.Schema;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot.Bots
|
||||
{
|
||||
public class EchoBot : ActivityHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// This option passes thread control from the secondary receiver to a primary receiver.
|
||||
/// </summary>
|
||||
private const string OptionPassPrimaryBot = "Pass to primary";
|
||||
|
||||
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var activity = MessageFactory.Text("Hello and Welcome!");
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
|
||||
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
if (turnContext.Activity.GetChannelData<FacebookMessage>().IsStandby)
|
||||
{
|
||||
if ((turnContext.Activity as Activity)?.Text == "OtherBot")
|
||||
{
|
||||
var activity = new Activity();
|
||||
activity.Type = ActivityTypes.Event;
|
||||
|
||||
//Action
|
||||
(activity as IEventActivity).Name = "request_thread_control";
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (turnContext.Activity.Attachments != null)
|
||||
{
|
||||
foreach (var attachment in turnContext.Activity.Attachments)
|
||||
{
|
||||
var activity = MessageFactory.Text($" I got {turnContext.Activity.Attachments.Count} attachments");
|
||||
|
||||
var image = new Attachment(
|
||||
attachment.ContentType,
|
||||
content: attachment.Content);
|
||||
|
||||
activity.Attachments.Add(image);
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IActivity activity;
|
||||
|
||||
switch (turnContext.Activity.Text)
|
||||
{
|
||||
case OptionPassPrimaryBot:
|
||||
activity = MessageFactory.Text("Redirecting to the primary bot...");
|
||||
activity.Type = ActivityTypes.Event;
|
||||
(activity as IEventActivity).Name = "pass_thread_control";
|
||||
(activity as IEventActivity).Value = "<PRIMARY RECEIVER APP ID>";
|
||||
break;
|
||||
|
||||
case "Redirected to the bot":
|
||||
activity = MessageFactory.Text("Hello Human, I'm the secondary bot to help you!");
|
||||
break;
|
||||
case "Little":
|
||||
activity = MessageFactory.Text($"You have spoken the forbidden word!");
|
||||
break;
|
||||
default:
|
||||
activity = MessageFactory.Text($"Echo Secondary: {turnContext.Activity.Text}");
|
||||
break;
|
||||
}
|
||||
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
if (turnContext.Activity.Value != null)
|
||||
{
|
||||
var metadata = (turnContext.Activity.Value as FacebookThreadControl).Metadata;
|
||||
|
||||
if (metadata.Equals("Pass thread control"))
|
||||
{
|
||||
var activity = MessageFactory.Text("Hello Human, I'm the secondary bot to help you!");
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Attachment CreateTemplateAttachment(string filePath)
|
||||
{
|
||||
var templateAttachmentJson = File.ReadAllText(filePath);
|
||||
var templateAttachment = new Attachment()
|
||||
{
|
||||
ContentType = "template",
|
||||
Content = JsonConvert.DeserializeObject(templateAttachmentJson),
|
||||
};
|
||||
return templateAttachment;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Bot.Builder.Integration.AspNet.Core;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot.Controllers
|
||||
{
|
||||
// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot
|
||||
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be
|
||||
// achieved by specifying a more specific type for the bot constructor argument.
|
||||
[Route("api/messages")]
|
||||
[ApiController]
|
||||
public class BotController : ControllerBase
|
||||
{
|
||||
private readonly IBotFrameworkHttpAdapter _adapter;
|
||||
private readonly IBot _bot;
|
||||
|
||||
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
|
||||
{
|
||||
_adapter = adapter;
|
||||
_bot = bot;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task PostAsync()
|
||||
{
|
||||
// Delegate the processing of the HTTP POST to the adapter.
|
||||
// The adapter will invoke the bot.
|
||||
await _adapter.ProcessAsync(Request, Response, _bot);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task GetAsync()
|
||||
{
|
||||
// Delegate the processing of the HTTP POST to the adapter.
|
||||
// The adapter will invoke the bot.
|
||||
await _adapter.ProcessAsync(Request, Response, _bot);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the location of the Resource Group."
|
||||
}
|
||||
},
|
||||
"groupName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the name of the Resource Group."
|
||||
}
|
||||
},
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The name of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan. Defaults to \"westus\"."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"appServicePlanName": "[parameters('newAppServicePlanName')]",
|
||||
"resourcesLocation": "[parameters('newAppServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('groupName')]",
|
||||
"type": "Microsoft.Resources/resourceGroups",
|
||||
"apiVersion": "2018-05-01",
|
||||
"location": "[parameters('groupLocation')]",
|
||||
"properties": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2018-05-01",
|
||||
"name": "storageDeployment",
|
||||
"resourceGroup": "[parameters('groupName')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new App Service Plan",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"properties": {
|
||||
"name": "[variables('appServicePlanName')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using the new App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"serverFarmId": "[variables('appServicePlanName')]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "WEBSITE_NODE_DEFAULT_VERSION",
|
||||
"value": "10.14.1"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2017-12-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "bot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"developerAppInsightsApplicationId": null,
|
||||
"developerAppInsightKey": null,
|
||||
"publishingCredentials": null,
|
||||
"storageResourceId": null
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"defaultValue": "F0",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The name of the new App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"existingAppServicePlan": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "Name of the existing App Service Plan used to create the Web App for the bot."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
|
||||
"useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
|
||||
"servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
|
||||
"resourcesLocation": "[parameters('appServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"condition": "[not(variables('useExistingAppServicePlan'))]",
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"properties": {
|
||||
"name": "[variables('servicePlanName')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using an App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms/', variables('servicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"serverFarmId": "[variables('servicePlanName')]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "WEBSITE_NODE_DEFAULT_VERSION",
|
||||
"value": "10.14.1"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2017-12-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "bot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"developerAppInsightsApplicationId": null,
|
||||
"developerAppInsightKey": null,
|
||||
"publishingCredentials": null,
|
||||
"storageResourceId": null
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\libraries\Adapters\Microsoft.Bot.Builder.Adapters.Facebook\Microsoft.Bot.Builder.Adapters.Facebook.csproj" />
|
||||
<ProjectReference Include="..\..\..\libraries\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\libraries\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Resources\adaptive_card.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29102.190
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot", "Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot.csproj", "{29FEF930-5333-41D4-8035-D052F056BD65}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{29FEF930-5333-41D4-8035-D052F056BD65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{29FEF930-5333-41D4-8035-D052F056BD65}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{29FEF930-5333-41D4-8035-D052F056BD65}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{29FEF930-5333-41D4-8035-D052F056BD65}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {EC808DD8-B0F5-48FC-AE87-05163C7BA6C4}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0
|
||||
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateWebHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseStartup<Startup>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
# EchoBot using Facebook Adapter
|
||||
|
||||
Bot Framework v4 echo bot using facebook Adapter sample.
|
||||
|
||||
This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple echo bot that connects with Facebook to respond to messages.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [.NET Core SDK](https://dotnet.microsoft.com/download) version 2.1
|
||||
|
||||
```bash
|
||||
# determine dotnet version
|
||||
dotnet --version
|
||||
```
|
||||
|
||||
## To try this sample
|
||||
|
||||
1 - Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Microsoft/botbuilder-dotnet.git
|
||||
```
|
||||
|
||||
2 - In a terminal, navigate to `test/Adapters/Microsoft.Bot.Builder.Adapters.Facebook.TestBot`
|
||||
|
||||
3 - Connect the bot with Facebook by following the instructions below.
|
||||
|
||||
4 - Run the bot from a terminal or from Visual Studio, choose option A or B.
|
||||
|
||||
A) From a terminal
|
||||
|
||||
```bash
|
||||
# run the bot
|
||||
dotnet run
|
||||
```
|
||||
|
||||
B) Or from Visual Studio
|
||||
|
||||
- Launch Visual Studio
|
||||
- File -> Open -> Project/Solution
|
||||
- Navigate to `test/Adapters/Microsoft.Bot.Builder.Adapters.Facebook.TestBot` folder
|
||||
- Select `Microsoft.Bot.Builder.Adapters.Facebook.TestBot.csproj` file
|
||||
- Press <kbd>F5</kbd> to run the project
|
||||
|
||||
|
||||
### Connect the bot with Facebook
|
||||
|
||||
1 - Create a Facebook Account for Developers (https://developers.facebook.com/).
|
||||
|
||||
2 - Create a new App. Give a name to the app and click Create App ID button.
|
||||
|
||||
3 - In the Dashboard go to Add a Product and select Messenger by clicking on Set Up button
|
||||
|
||||
A) In the Access Tokens section, select a Facebook Page or create a new one. This is the page where the bot will be tested later.
|
||||
|
||||
B) After selecting the page, the permissions should be edited, click on the button "Add or Remove Pages", select the page just created as option and continue.
|
||||
|
||||
C) A Page Access Token is generated. Copy it, it will be needed to connect the adapter.
|
||||
|
||||
4 - Get the app credentials. Go to Settings, Basic and copy the App Secret.
|
||||
|
||||
5 - Set the tokens in appsettings.json file:
|
||||
|
||||
- VerifyToken (create one. It will be used to validate received messages)
|
||||
- AppSecret (the one obtained in step 4)
|
||||
- AccessToken (the one obtained in step 3.c)
|
||||
|
||||
6 - Using a tunneling tool like [Ngrok](https://ngrok.com/download), expose the bot's endpoint.
|
||||
|
||||
7 - Go back to the Facebook for Developers page and click on Messenger, Settings.
|
||||
In the Webhooks section, click on Subscribe To Events button.
|
||||
|
||||
A) Complete the Callback URL with the ngrok https URL adding '/api/messages'.
|
||||
Fill in the Verify Token with the one setted on your bot.
|
||||
Subscribe to the following events: messages, messaging_postbacks, messaging_optins, messaging_deliveries
|
||||
|
||||
B) Click Verify and Save button.
|
||||
|
||||
8 - Subscribe the webhook to the Page.
|
||||
|
||||
9 - Go to the Page and click Add a Button.
|
||||
|
||||
A) Select a Send Message button.
|
||||
|
||||
B) Select Messenger.
|
||||
|
||||
C) And click Finish button.
|
||||
|
||||
10 - Finally, click on the button created and test your bot!
|
||||
|
||||
## Deploy the bot to Azure
|
||||
|
||||
To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Bot Framework Documentation](https://docs.botframework.com)
|
||||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
|
||||
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
|
||||
- [.NET Core CLI tools](https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x)
|
||||
- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
|
||||
- [Azure Portal](https://portal.azure.com)
|
||||
- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/)
|
||||
- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"template_type": "button",
|
||||
"text": "What do you want to do next?",
|
||||
"buttons": [
|
||||
{
|
||||
"type": "web_url",
|
||||
"url": "https://www.messenger.com",
|
||||
"title": "Visit Messenger"
|
||||
},
|
||||
{
|
||||
"type": "postback",
|
||||
"title": "Say Hello",
|
||||
"payload": "Hello button"
|
||||
},
|
||||
{
|
||||
"type": "postback",
|
||||
"title": "Say Goodbye",
|
||||
"payload": "Goodbye button"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"template_type": "generic",
|
||||
"elements": [
|
||||
{
|
||||
"title": "Welcome to BotFramework!",
|
||||
"image_url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
|
||||
"subtitle": "Know more about Bots.",
|
||||
"default_action": {
|
||||
"type": "web_url",
|
||||
"url": "https://dev.botframework.com/",
|
||||
"webview_height_ratio": "compact"
|
||||
},
|
||||
"buttons": [
|
||||
{
|
||||
"type": "web_url",
|
||||
"url": "https://dev.botframework.com/",
|
||||
"title": "View Website"
|
||||
},
|
||||
{
|
||||
"type": "postback",
|
||||
"title": "Start Chatting",
|
||||
"payload": "Chatting"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"template_type": "button",
|
||||
"text": "You can contact with a live agent.",
|
||||
"buttons": [
|
||||
{
|
||||
"type": "postback",
|
||||
"title": "Contact",
|
||||
"payload": "Handover"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"template_type": "media",
|
||||
"elements": [
|
||||
{
|
||||
"media_type": "video",
|
||||
"url": "https://www.facebook.com/MicrosoftLatam/videos/724316691373804/"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
//
|
||||
// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot.Bots;
|
||||
using Microsoft.Bot.Builder.Integration.AspNet.Core;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||
|
||||
// Create the Bot Framework Facebook Adapter.
|
||||
services.AddSingleton<IBotFrameworkHttpAdapter, FacebookAdapter>();
|
||||
|
||||
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
|
||||
services.AddTransient<IBot, EchoBot>();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
|
||||
// app.UseHttpsRedirection();
|
||||
app.UseMvc();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"FacebookVerifyToken": "",
|
||||
"FacebookAppSecret": "",
|
||||
"FacebookAccessToken": ""
|
||||
}
|
|
@ -0,0 +1,420 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Microsoft.Bot.Builder.Facebook.Sample</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
font-family: Segoe UI;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
background-image: url("data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 4638.9 651.6' style='enable-background:new 0 0 4638.9 651.6;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%2355A0E0;%7D .st1%7Bfill:none;%7D .st2%7Bfill:%230058A8;%7D .st3%7Bfill:%23328BD8;%7D .st4%7Bfill:%23B6DCF1;%7D .st5%7Bopacity:0.2;fill:url(%23SVGID_1_);enable-background:new ;%7D%0A%3C/style%3E%3Crect y='1.1' class='st0' width='4640' height='646.3'/%3E%3Cpath class='st1' d='M3987.8,323.6L4310.3,1.1h-65.6l-460.1,460.1c-17.5,17.5-46.1,17.5-63.6,0L3260.9,1.1H0v646.3h3660.3 L3889,418.7c17.5-17.5,46.1-17.5,63.6,0l228.7,228.7h66.6l-260.2-260.2C3970.3,369.8,3970.3,341.1,3987.8,323.6z'/%3E%3Cpath class='st2' d='M3784.6,461.2L4244.7,1.1h-983.9l460.1,460.1C3738.4,478.7,3767.1,478.7,3784.6,461.2z'/%3E%3Cpath class='st3' d='M4640,1.1h-329.8l-322.5,322.5c-17.5,17.5-17.5,46.1,0,63.6l260.2,260.2H4640L4640,1.1L4640,1.1z'/%3E%3Cpath class='st4' d='M3889,418.8l-228.7,228.7h521.1l-228.7-228.7C3935.2,401.3,3906.5,401.3,3889,418.8z'/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='3713.7576' y1='438.1175' x2='3911.4084' y2='14.2535' gradientTransform='matrix(1 0 0 -1 0 641.3969)'%3E%3Cstop offset='0' style='stop-color:%23FFFFFF;stop-opacity:0.5'/%3E%3Cstop offset='1' style='stop-color:%23FFFFFF'/%3E%3C/linearGradient%3E%3Cpath class='st5' d='M3952.7,124.5c-17.5-17.5-46.1-17.5-63.6,0l-523,523h1109.6L3952.7,124.5z'/%3E%3C/svg%3E%0A");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
background-position: right;
|
||||
background-color: #55A0E0;
|
||||
width: 100%;
|
||||
font-size: 44px;
|
||||
height: 120px;
|
||||
color: white;
|
||||
padding: 30px 0 40px 0px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20150.2%20125%22%20style%3D%22enable-background%3Anew%200%200%20150.2%20125%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23FFFFFF%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.5%22%20class%3D%22st0%22%20width%3D%22149.7%22%20height%3D%22125%22/%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M59%2C102.9L21.8%2C66c-3.5-3.5-3.5-9.1%2C0-12.5l37-36.5l2.9%2C3l-37%2C36.4c-1.8%2C1.8-1.8%2C4.7%2C0%2C6.6l37.2%2C37L59%2C102.9z%22%0A%09%09/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M92.5%2C102.9l-3-3l37.2-37c0.9-0.9%2C1.4-2%2C1.4-3.3c0-1.2-0.5-2.4-1.4-3.3L89.5%2C20l2.9-3l37.2%2C36.4%0A%09%09c1.7%2C1.7%2C2.6%2C3.9%2C2.6%2C6.3s-0.9%2C4.6-2.6%2C6.3L92.5%2C102.9z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M90.1%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C98.1%2C64.7%2C94.4%2C68.4%2C90.1%2C68.4z%0A%09%09%20M90.1%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S91.9%2C56.5%2C90.1%2C56.5z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M61.4%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C69.5%2C64.7%2C65.8%2C68.4%2C61.4%2C68.4z%0A%09%09%20M61.4%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S63.3%2C56.5%2C61.4%2C56.5z%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
|
||||
background-repeat: no-repeat;
|
||||
float: left;
|
||||
height: 140px;
|
||||
width: 140px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
padding-left: 1%;
|
||||
color: #FFFFFF;
|
||||
font-family: "Segoe UI";
|
||||
font-size: 72px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.35px;
|
||||
line-height: 96px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-inner-container {
|
||||
min-width: 480px;
|
||||
max-width: 1366px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-inner-container::after {
|
||||
content: "";
|
||||
clear: both;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.main-content-area {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
color: #000000;
|
||||
font-family: "Segoe UI";
|
||||
font-size: 46px;
|
||||
font-weight: 300;
|
||||
line-height: 62px;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
color: #808080;
|
||||
font-size: 24px;
|
||||
font-family: "Segoe UI";
|
||||
font-size: 24px;
|
||||
font-weight: 200;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.main-text-p1 {
|
||||
padding-top: 48px;
|
||||
padding-bottom: 28px;
|
||||
}
|
||||
|
||||
.endpoint {
|
||||
height: 32px;
|
||||
width: 571px;
|
||||
color: #808080;
|
||||
font-family: "Segoe UI";
|
||||
font-size: 24px;
|
||||
font-weight: 200;
|
||||
line-height: 32px;
|
||||
padding-top: 28px;
|
||||
}
|
||||
|
||||
.how-to-build-section {
|
||||
padding-top: 20px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.how-to-build-section>h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.35px;
|
||||
line-height: 22px;
|
||||
margin: 0 0 24px 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.step-container {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-container dl {
|
||||
border-left: 1px solid #A0A0A0;
|
||||
display: block;
|
||||
padding: 0 24px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.step-container dl>dt::before {
|
||||
background-color: white;
|
||||
border: 1px solid #A0A0A0;
|
||||
border-radius: 100%;
|
||||
content: '';
|
||||
left: 47px;
|
||||
height: 11px;
|
||||
position: absolute;
|
||||
width: 11px;
|
||||
}
|
||||
|
||||
.step-container dl>.test-bullet::before {
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
.step-container dl>dt {
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.step-container dl>dd {
|
||||
font-size: inherit;
|
||||
line-height: 20px;
|
||||
margin-left: 0;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
.step-container:last-child dl {
|
||||
border-left: 1px solid transparent;
|
||||
}
|
||||
|
||||
.ctaLink {
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
color: #006AB1;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
padding: 0;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.ctaLink:focus {
|
||||
outline: 1px solid #00bcf2;
|
||||
}
|
||||
|
||||
.ctaLink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
display: flex;
|
||||
height: 38px;
|
||||
margin-right: 15px;
|
||||
width: 38px;
|
||||
}
|
||||
|
||||
.step-icon>div {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.ms-logo-container {
|
||||
min-width: 580px;
|
||||
max-width: 980px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
transition: bottom 400ms;
|
||||
}
|
||||
|
||||
.ms-logo {
|
||||
float: right;
|
||||
background-image: url("data:image/svg+xml;utf8,%0A%3Csvg%20version%3D%221.1%22%20id%3D%22MS-symbol%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20400%20120%22%20style%3D%22enable-background%3Anew%200%200%20400%20120%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23737474%3B%7D%0A%09.st2%7Bfill%3A%23D63F26%3B%7D%0A%09.st3%7Bfill%3A%23167D3E%3B%7D%0A%09.st4%7Bfill%3A%232E76BC%3B%7D%0A%09.st5%7Bfill%3A%23FDB813%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.6%22%20class%3D%22st0%22%20width%3D%22398.7%22%20height%3D%22119%22/%3E%0A%3Cpath%20class%3D%22st1%22%20d%3D%22M171.3%2C38.4v43.2h-7.5V47.7h-0.1l-13.4%2C33.9h-5l-13.7-33.9h-0.1v33.9h-6.9V38.4h10.8l12.4%2C32h0.2l13.1-32H171.3%0A%09z%20M177.6%2C41.7c0-1.2%2C0.4-2.2%2C1.3-3c0.9-0.8%2C1.9-1.2%2C3.1-1.2c1.3%2C0%2C2.4%2C0.4%2C3.2%2C1.3c0.8%2C0.8%2C1.3%2C1.8%2C1.3%2C3c0%2C1.2-0.4%2C2.2-1.3%2C3%0A%09c-0.9%2C0.8-1.9%2C1.2-3.2%2C1.2s-2.3-0.4-3.1-1.2C178%2C43.8%2C177.6%2C42.8%2C177.6%2C41.7z%20M185.7%2C50.6v31h-7.3v-31H185.7z%20M207.8%2C76.3%0A%09c1.1%2C0%2C2.3-0.3%2C3.6-0.8c1.3-0.5%2C2.5-1.2%2C3.6-2v6.8c-1.2%2C0.7-2.5%2C1.2-4%2C1.5c-1.5%2C0.3-3.1%2C0.5-4.9%2C0.5c-4.6%2C0-8.3-1.4-11.1-4.3%0A%09c-2.9-2.9-4.3-6.6-4.3-11c0-5%2C1.5-9.1%2C4.4-12.3c2.9-3.2%2C7-4.8%2C12.4-4.8c1.4%2C0%2C2.7%2C0.2%2C4.1%2C0.5c1.4%2C0.4%2C2.5%2C0.8%2C3.3%2C1.2v7%0A%09c-1.1-0.8-2.3-1.5-3.4-1.9c-1.2-0.5-2.4-0.7-3.6-0.7c-2.9%2C0-5.2%2C0.9-7%2C2.8c-1.8%2C1.9-2.7%2C4.4-2.7%2C7.6c0%2C3.1%2C0.8%2C5.6%2C2.5%2C7.3%0A%09C202.6%2C75.4%2C204.9%2C76.3%2C207.8%2C76.3z%20M235.7%2C50.1c0.6%2C0%2C1.1%2C0%2C1.6%2C0.1s0.9%2C0.2%2C1.2%2C0.3v7.4c-0.4-0.3-0.9-0.5-1.7-0.8%0A%09c-0.7-0.3-1.6-0.4-2.7-0.4c-1.8%2C0-3.3%2C0.8-4.5%2C2.3c-1.2%2C1.5-1.9%2C3.8-1.9%2C7v15.6h-7.3v-31h7.3v4.9h0.1c0.7-1.7%2C1.7-3%2C3-4%0A%09C232.2%2C50.6%2C233.8%2C50.1%2C235.7%2C50.1z%20M238.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3%0A%09c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5c-4.8%2C0-8.6-1.4-11.4-4.2C240.3%2C75.3%2C238.9%2C71.4%2C238.9%2C66.6z%0A%09%20M246.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5%0A%09c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7C247.2%2C60.5%2C246.5%2C63%2C246.5%2C66.3z%20M281.5%2C58.8c0%2C1%2C0.3%2C1.9%2C1%2C2.5%0A%09c0.7%2C0.6%2C2.1%2C1.3%2C4.4%2C2.2c2.9%2C1.2%2C5%2C2.5%2C6.1%2C3.9c1.2%2C1.5%2C1.8%2C3.2%2C1.8%2C5.3c0%2C2.9-1.1%2C5.3-3.4%2C7c-2.2%2C1.8-5.3%2C2.7-9.1%2C2.7%0A%09c-1.3%2C0-2.7-0.2-4.3-0.5c-1.6-0.3-2.9-0.7-4-1.2v-7.2c1.3%2C0.9%2C2.8%2C1.7%2C4.3%2C2.2c1.5%2C0.5%2C2.9%2C0.8%2C4.2%2C0.8c1.6%2C0%2C2.9-0.2%2C3.6-0.7%0A%09c0.8-0.5%2C1.2-1.2%2C1.2-2.3c0-1-0.4-1.9-1.2-2.5c-0.8-0.7-2.4-1.5-4.6-2.4c-2.7-1.1-4.6-2.4-5.7-3.8c-1.1-1.4-1.7-3.2-1.7-5.4%0A%09c0-2.8%2C1.1-5.1%2C3.3-6.9c2.2-1.8%2C5.1-2.7%2C8.6-2.7c1.1%2C0%2C2.3%2C0.1%2C3.6%2C0.4c1.3%2C0.2%2C2.5%2C0.6%2C3.4%2C0.9v6.9c-1-0.6-2.1-1.2-3.4-1.7%0A%09c-1.3-0.5-2.6-0.7-3.8-0.7c-1.4%2C0-2.5%2C0.3-3.2%2C0.8C281.9%2C57.1%2C281.5%2C57.8%2C281.5%2C58.8z%20M297.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2%0A%09c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5%0A%09c-4.8%2C0-8.6-1.4-11.4-4.2C299.4%2C75.3%2C297.9%2C71.4%2C297.9%2C66.6z%20M305.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6%0A%09c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7%0A%09C306.3%2C60.5%2C305.5%2C63%2C305.5%2C66.3z%20M353.9%2C56.6h-10.9v25h-7.4v-25h-5.2v-6h5.2v-4.3c0-3.3%2C1.1-5.9%2C3.2-8c2.1-2.1%2C4.8-3.1%2C8.1-3.1%0A%09c0.9%2C0%2C1.7%2C0%2C2.4%2C0.1c0.7%2C0.1%2C1.3%2C0.2%2C1.8%2C0.4V42c-0.2-0.1-0.7-0.3-1.3-0.5c-0.6-0.2-1.3-0.3-2.1-0.3c-1.5%2C0-2.7%2C0.5-3.5%2C1.4%0A%09s-1.2%2C2.4-1.2%2C4.2v3.7h10.9v-7l7.3-2.2v9.2h7.4v6h-7.4v14.5c0%2C1.9%2C0.3%2C3.3%2C1%2C4c0.7%2C0.8%2C1.8%2C1.2%2C3.3%2C1.2c0.4%2C0%2C0.9-0.1%2C1.5-0.3%0A%09c0.6-0.2%2C1.1-0.4%2C1.6-0.7v6c-0.5%2C0.3-1.2%2C0.5-2.3%2C0.7c-1.1%2C0.2-2.1%2C0.3-3.2%2C0.3c-3.1%2C0-5.4-0.8-6.9-2.5c-1.5-1.6-2.3-4.1-2.3-7.4%0A%09V56.6z%22/%3E%0A%3Cg%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2224%22%20class%3D%22st2%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2224%22%20class%3D%22st3%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2261.8%22%20class%3D%22st4%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2261.8%22%20class%3D%22st5%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
.ms-logo-container>div {
|
||||
min-height: 60px;
|
||||
width: 150px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 90px 0px 0 20px;
|
||||
min-width: 480px;
|
||||
max-width: 1366px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.column {
|
||||
float: left;
|
||||
width: 45%;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.row:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.download-the-emulator {
|
||||
height: 20px;
|
||||
color: #0063B1;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
padding-bottom: 70px;
|
||||
}
|
||||
|
||||
.how-to-iframe {
|
||||
max-width: 700px !important;
|
||||
min-width: 650px !important;
|
||||
height: 700px !important;
|
||||
}
|
||||
|
||||
.remove-frame-height {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1300px) {
|
||||
.ms-logo {
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
font-size: 40x;
|
||||
}
|
||||
|
||||
.column {
|
||||
float: none;
|
||||
padding-top: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ms-logo-container {
|
||||
padding-top: 30px;
|
||||
min-width: 480px;
|
||||
max-width: 650px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 20px 0px 0 20px;
|
||||
min-width: 480px;
|
||||
max-width: 650px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1370px) {
|
||||
header {
|
||||
background-color: #55A0E0;
|
||||
background-size: auto 200px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1230px) {
|
||||
header {
|
||||
background-color: #55A0E0;
|
||||
background-size: auto 200px;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
font-size: 44px;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
header {
|
||||
background-color: #55A0E0;
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 632px) {
|
||||
.header-text {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 10px 0px 0 10px;
|
||||
max-width: 490px !important;
|
||||
min-width: 410px !important;
|
||||
}
|
||||
|
||||
.endpoint {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.step-container dl>dd {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.column {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
height: 110px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.how-to-iframe {
|
||||
max-width: 480px !important;
|
||||
min-width: 400px !important;
|
||||
height: 650px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.remove-frame-height {
|
||||
max-height: 10px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
loadFrame();
|
||||
});
|
||||
var loadFrame = function () {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute("id", "iframe");
|
||||
var offLineHTMLContent = "";
|
||||
var frameElement = document.getElementById("how-to-iframe");
|
||||
if (window.navigator.onLine) {
|
||||
iframe.src = 'https://docs.botframework.com/static/abs/pages/f5.htm';
|
||||
iframe.setAttribute("scrolling", "no");
|
||||
iframe.setAttribute("frameborder", "0");
|
||||
iframe.setAttribute("width", "100%");
|
||||
iframe.setAttribute("height", "100%");
|
||||
var frameDiv = document.getElementById("how-to-iframe");
|
||||
frameDiv.appendChild(iframe);
|
||||
} else {
|
||||
frameElement.classList.add("remove-frame-height");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="header-inner-container">
|
||||
<div class="header-icon" style="display: inline-block"></div>
|
||||
<div class="header-text" style="display: inline-block">Microsoft.Bot.Builder.Facebook.Sample Bot</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="row">
|
||||
<div class="column" class="main-content-area">
|
||||
<div class="content-title">Your bot is ready!</div>
|
||||
<div class="main-text main-text-p1">You can test your bot in the Bot Framework Emulator<br />
|
||||
by connecting to http://localhost:3978/api/messages.</div>
|
||||
<div class="main-text download-the-emulator"><a class="ctaLink"
|
||||
href="https://aka.ms/bot-framework-F5-download-emulator" target="_blank">Download the Emulator</a>
|
||||
</div>
|
||||
<div class="main-text">Visit <a class="ctaLink" href="https://aka.ms/bot-framework-F5-abs-home"
|
||||
target="_blank">Azure
|
||||
Bot Service</a> to register your bot and add it to<br />
|
||||
various channels. The bot's endpoint URL typically looks
|
||||
like this:</div>
|
||||
<div class="endpoint">https://<i>your_bots_hostname</i>/api/messages</div>
|
||||
</div>
|
||||
<div class="column how-to-iframe" id="how-to-iframe"></div>
|
||||
</div>
|
||||
<div class="ms-logo-container">
|
||||
<div class="ms-logo"></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover;
|
||||
using Microsoft.Bot.Schema;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
@ -14,6 +15,8 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.TestBot.Bots
|
|||
{
|
||||
public class EchoBot : ActivityHandler
|
||||
{
|
||||
private const string PageInboxId = "263902037430900";
|
||||
|
||||
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var activity = MessageFactory.Text("Hello and Welcome!");
|
||||
|
@ -22,7 +25,19 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.TestBot.Bots
|
|||
|
||||
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
if (turnContext.Activity.Attachments != null)
|
||||
if (turnContext.Activity.GetChannelData<FacebookMessage>().IsStandby)
|
||||
{
|
||||
if ((turnContext.Activity as Activity)?.Text == "Little")
|
||||
{
|
||||
var activity = MessageFactory.Text("Primary Bot taking back control...");
|
||||
activity.Type = ActivityTypes.Event;
|
||||
|
||||
//Action
|
||||
(activity as IEventActivity).Name = "take_thread_control";
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (turnContext.Activity.Attachments != null)
|
||||
{
|
||||
foreach (var attachment in turnContext.Activity.Attachments)
|
||||
{
|
||||
|
@ -60,6 +75,38 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.TestBot.Bots
|
|||
case "Chatting":
|
||||
activity = MessageFactory.Text("Hello! How can I help you?");
|
||||
break;
|
||||
case "handover template":
|
||||
activity = MessageFactory.Attachment(CreateTemplateAttachment(Directory.GetCurrentDirectory() + @"/Resources/HandoverTemplatePayload.json"));
|
||||
break;
|
||||
case "Handover":
|
||||
activity = MessageFactory.Text("Redirecting...");
|
||||
activity.Type = ActivityTypes.Event;
|
||||
(activity as IEventActivity).Name = "pass_thread_control";
|
||||
(activity as IEventActivity).Value = "inbox";
|
||||
break;
|
||||
case "bot template":
|
||||
activity = MessageFactory.Attachment(CreateTemplateAttachment(Directory.GetCurrentDirectory() + @"/Resources/HandoverBotsTemplatePayload.json"));
|
||||
break;
|
||||
case "SecondaryBot":
|
||||
activity = MessageFactory.Text("Redirecting to the secondary bot...");
|
||||
activity.Type = ActivityTypes.Event;
|
||||
|
||||
//Action
|
||||
(activity as IEventActivity).Name = "pass_thread_control";
|
||||
|
||||
//AppId
|
||||
(activity as IEventActivity).Value = "<SECOND RECEIVER APP ID>";
|
||||
break;
|
||||
case "TakeControl":
|
||||
activity = MessageFactory.Text("Primary Bot Taking control...");
|
||||
activity.Type = ActivityTypes.Event;
|
||||
|
||||
//Action
|
||||
(activity as IEventActivity).Name = "take_thread_control";
|
||||
break;
|
||||
case "OtherBot":
|
||||
activity = MessageFactory.Text($"Secondary bot is requesting me the thread control. Passing thread control!");
|
||||
break;
|
||||
default:
|
||||
activity = MessageFactory.Text($"Echo: {turnContext.Activity.Text}");
|
||||
break;
|
||||
|
@ -73,11 +120,34 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.TestBot.Bots
|
|||
{
|
||||
if (turnContext.Activity.Value != null)
|
||||
{
|
||||
var inputs = (Dictionary<string, string>)turnContext.Activity.Value;
|
||||
var name = inputs["Name"];
|
||||
var metadata = (turnContext.Activity.Value as FacebookThreadControl).Metadata;
|
||||
|
||||
var activity = MessageFactory.Text($"How are you doing {name}?");
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
if (metadata == null)
|
||||
{
|
||||
var requester = (turnContext.Activity.Value as FacebookRequestThreadControl).RequestedOwnerAppId;
|
||||
|
||||
if (requester == PageInboxId)
|
||||
{
|
||||
var activity = MessageFactory.Text($"The Inbox is requesting me the thread control. Passing thread control!");
|
||||
activity.Type = ActivityTypes.Event;
|
||||
(activity as IEventActivity).Name = "pass_thread_control";
|
||||
(activity as IEventActivity).Value = "inbox";
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (metadata.Equals("Request thread control to the primary receiver"))
|
||||
{
|
||||
var activity = new Activity();
|
||||
activity.Type = ActivityTypes.Event;
|
||||
(activity as IEventActivity).Name = "pass_thread_control";
|
||||
(activity as IEventActivity).Value = "<APP ID>";
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
else if (metadata.Equals("Pass thread control") || metadata.Equals("Pass thread control from Page Inbox"))
|
||||
{
|
||||
var activity = MessageFactory.Text("Hello Again Human, I'm the bot");
|
||||
await turnContext.SendActivityAsync(activity, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"template_type": "button",
|
||||
"text": "You can contact with a secondary bot.",
|
||||
"buttons": [
|
||||
{
|
||||
"type": "postback",
|
||||
"title": "Contact",
|
||||
"payload": "SecondaryBot"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"template_type": "button",
|
||||
"text": "You can contact with a live agent.",
|
||||
"buttons": [
|
||||
{
|
||||
"type": "postback",
|
||||
"title": "Contact",
|
||||
"payload": "Handover"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -226,22 +226,99 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async void SendActivitiesAsyncShouldFailWithActivityTypeNotMessage()
|
||||
public async void SendActivitiesAsyncShouldPostToFacebookOnPassThreadControl()
|
||||
{
|
||||
var facebookAdapter = new FacebookAdapter(new Mock<FacebookClientWrapper>(_testOptions).Object);
|
||||
const string testResponse = "Test Response";
|
||||
var facebookClientWrapper = new Mock<FacebookClientWrapper>(_testOptions);
|
||||
var facebookAdapter = new FacebookAdapter(facebookClientWrapper.Object);
|
||||
var activity = new Activity
|
||||
{
|
||||
Type = ActivityTypes.Event,
|
||||
Text = "Test text",
|
||||
Name = "pass_thread_control",
|
||||
Conversation = new ConversationAccount()
|
||||
{
|
||||
Id = "Test id",
|
||||
},
|
||||
ChannelData = new FacebookMessage("recipientId", new Message(), "messagingtype"),
|
||||
};
|
||||
Activity[] activities = { activity };
|
||||
ResourceResponse[] responses = null;
|
||||
|
||||
facebookClientWrapper.Setup(api => api.SendMessageAsync(It.IsAny<string>(), It.IsAny<FacebookMessage>(), It.IsAny<HttpMethod>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(testResponse));
|
||||
facebookClientWrapper.Setup(api => api.PassThreadControlAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(true));
|
||||
|
||||
using (var turnContext = new TurnContext(facebookAdapter, activity))
|
||||
{
|
||||
await Assert.ThrowsAsync<Exception>(async () =>
|
||||
{
|
||||
await facebookAdapter.SendActivitiesAsync(turnContext, activities, default);
|
||||
});
|
||||
responses = await facebookAdapter.SendActivitiesAsync(turnContext, activities, default);
|
||||
}
|
||||
|
||||
Assert.Equal(testResponse, responses[0].Id);
|
||||
facebookClientWrapper.Verify(api => api.PassThreadControlAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SendActivitiesAsyncShouldPostToFacebookOnTakeThreadControl()
|
||||
{
|
||||
const string testResponse = "Test Response";
|
||||
var facebookClientWrapper = new Mock<FacebookClientWrapper>(_testOptions);
|
||||
var facebookAdapter = new FacebookAdapter(facebookClientWrapper.Object);
|
||||
var activity = new Activity
|
||||
{
|
||||
Type = ActivityTypes.Event,
|
||||
Text = "Test text",
|
||||
Name = "take_thread_control",
|
||||
Conversation = new ConversationAccount()
|
||||
{
|
||||
Id = "Test id",
|
||||
},
|
||||
ChannelData = new FacebookMessage("recipientId", new Message(), "messagingtype"),
|
||||
};
|
||||
Activity[] activities = { activity };
|
||||
ResourceResponse[] responses = null;
|
||||
|
||||
facebookClientWrapper.Setup(api => api.SendMessageAsync(It.IsAny<string>(), It.IsAny<FacebookMessage>(), It.IsAny<HttpMethod>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(testResponse));
|
||||
facebookClientWrapper.Setup(api => api.TakeThreadControlAsync(It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(true));
|
||||
|
||||
using (var turnContext = new TurnContext(facebookAdapter, activity))
|
||||
{
|
||||
responses = await facebookAdapter.SendActivitiesAsync(turnContext, activities, default);
|
||||
}
|
||||
|
||||
Assert.Equal(testResponse, responses[0].Id);
|
||||
facebookClientWrapper.Verify(api => api.TakeThreadControlAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SendActivitiesAsyncShouldPostToFacebookOnRequestThreadControl()
|
||||
{
|
||||
const string testResponse = "Test Response";
|
||||
var facebookClientWrapper = new Mock<FacebookClientWrapper>(_testOptions);
|
||||
var facebookAdapter = new FacebookAdapter(facebookClientWrapper.Object);
|
||||
var activity = new Activity
|
||||
{
|
||||
Type = ActivityTypes.Event,
|
||||
Text = "Test text",
|
||||
Name = "request_thread_control",
|
||||
Conversation = new ConversationAccount()
|
||||
{
|
||||
Id = "Test id",
|
||||
},
|
||||
ChannelData = new FacebookMessage("recipientId", new Message(), "messagingtype"),
|
||||
};
|
||||
Activity[] activities = { activity };
|
||||
ResourceResponse[] responses = null;
|
||||
|
||||
facebookClientWrapper.Setup(api => api.SendMessageAsync(It.IsAny<string>(), It.IsAny<FacebookMessage>(), It.IsAny<HttpMethod>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(testResponse));
|
||||
facebookClientWrapper.Setup(api => api.RequestThreadControlAsync(It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(true));
|
||||
|
||||
using (var turnContext = new TurnContext(facebookAdapter, activity))
|
||||
{
|
||||
responses = await facebookAdapter.SendActivitiesAsync(turnContext, activities, default);
|
||||
}
|
||||
|
||||
Assert.Equal(testResponse, responses[0].Id);
|
||||
facebookClientWrapper.Verify(api => api.RequestThreadControlAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents;
|
||||
using Microsoft.Bot.Schema;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
|
@ -102,6 +103,57 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.Tests
|
|||
Assert.Equal(facebookMessage.PostBack.Payload, activity.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessSingleMessageShouldReturnActivityWithPassThreadControlRequest()
|
||||
{
|
||||
var facebookMessageJson = File.ReadAllText(Directory.GetCurrentDirectory() + @"/Files/PayloadWithPassThreadControl.json");
|
||||
var facebookResponse = JsonConvert.DeserializeObject<FacebookResponseEvent>(facebookMessageJson);
|
||||
|
||||
var payload = new List<FacebookMessage>();
|
||||
|
||||
payload = facebookResponse.Entry[0].Messaging;
|
||||
|
||||
var activity = FacebookHelper.ProcessSingleMessage(payload[0]);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Recipient.Id, activity.Recipient.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Sender.Id, activity.Conversation.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Sender.Id, activity.Conversation.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].PassThreadControl, activity.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessSingleMessageShouldReturnActivityWithRequestThreadControlRequest()
|
||||
{
|
||||
var facebookMessageJson = File.ReadAllText(Directory.GetCurrentDirectory() + @"/Files/PayloadWithRequestThreadControl.json");
|
||||
var facebookResponse = JsonConvert.DeserializeObject<FacebookResponseEvent>(facebookMessageJson);
|
||||
|
||||
var payload = new List<FacebookMessage>();
|
||||
|
||||
payload = facebookResponse.Entry[0].Messaging;
|
||||
|
||||
var activity = FacebookHelper.ProcessSingleMessage(payload[0]);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Recipient.Id, activity.Recipient.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Sender.Id, activity.Conversation.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Sender.Id, activity.Conversation.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].RequestThreadControl, activity.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessSingleMessageShouldReturnActivityWithTakeThreadControlRequest()
|
||||
{
|
||||
var facebookMessageJson = File.ReadAllText(Directory.GetCurrentDirectory() + @"/Files/PayloadWithTakeThreadControl.json");
|
||||
var facebookResponse = JsonConvert.DeserializeObject<FacebookResponseEvent>(facebookMessageJson);
|
||||
|
||||
var payload = new List<FacebookMessage>();
|
||||
|
||||
payload = facebookResponse.Entry[0].Messaging;
|
||||
|
||||
var activity = FacebookHelper.ProcessSingleMessage(payload[0]);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Recipient.Id, activity.Recipient.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Sender.Id, activity.Conversation.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].Sender.Id, activity.Conversation.Id);
|
||||
Assert.Equal(facebookResponse.Entry[0].Messaging[0].TakeThreadControl, activity.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsyncShouldFailWithNullResponse()
|
||||
{
|
||||
|
|
|
@ -160,5 +160,53 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.Tests
|
|||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => { await facebookClientWrapper.VerifyWebhookAsync(httpRequest.Object, null, default); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void PassThreadControlAsyncShouldThrowExceptionWithNullTargetAppId()
|
||||
{
|
||||
var facebookClientWrapper = new FacebookClientWrapper(_testOptions);
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => { await facebookClientWrapper.PassThreadControlAsync(null, "fakeUserId", "Test Pass Thread Control"); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void PassThreadControlAsyncShouldThrowExceptionWithNullUserId()
|
||||
{
|
||||
var facebookClientWrapper = new FacebookClientWrapper(_testOptions);
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => { await facebookClientWrapper.PassThreadControlAsync("fakeAppId", null, "Test Pass Thread Control"); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RequestThreadControlAsyncShouldThrowExceptionWithNullUserId()
|
||||
{
|
||||
var facebookClientWrapper = new FacebookClientWrapper(_testOptions);
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => { await facebookClientWrapper.RequestThreadControlAsync(null, "Test Pass Thread Control"); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void TakeThreadControlAsyncShouldThrowExceptionWithNullUserId()
|
||||
{
|
||||
var facebookClientWrapper = new FacebookClientWrapper(_testOptions);
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => { await facebookClientWrapper.TakeThreadControlAsync(null, "Test Pass Thread Control"); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void PostToFacebookApiAsyncShouldThrowExceptionWithNullPostType()
|
||||
{
|
||||
var facebookClientWrapper = new FacebookClientWrapper(_testOptions);
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => { await facebookClientWrapper.PostToFacebookApiAsync(null, "fakeContent"); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void PostToFacebookApiAsyncShouldThrowExceptionWithNullContent()
|
||||
{
|
||||
var facebookClientWrapper = new FacebookClientWrapper(_testOptions);
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => { await facebookClientWrapper.PostToFacebookApiAsync("fakePostType", null); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"object": "page",
|
||||
"entry": [
|
||||
{
|
||||
"id": "fakeId",
|
||||
"time": 1572630578783,
|
||||
"messaging": [
|
||||
{
|
||||
"recipient": {
|
||||
"id": "fakeId"
|
||||
},
|
||||
"timestamp": 1572630578783,
|
||||
"sender": {
|
||||
"id": "fakeId"
|
||||
},
|
||||
"pass_thread_control": {
|
||||
"new_owner_app_id": 1234567890,
|
||||
"metadata": "Pass thread control"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"object": "page",
|
||||
"entry": [
|
||||
{
|
||||
"id": "fakeId",
|
||||
"time": 1572630401588,
|
||||
"messaging": [
|
||||
{
|
||||
"recipient": {
|
||||
"id": "fakeId"
|
||||
},
|
||||
"timestamp": 1572630401588,
|
||||
"sender": {
|
||||
"id": "fakeId"
|
||||
},
|
||||
"request_thread_control": {
|
||||
"requested_owner_app_id": 1234567890,
|
||||
"metadata": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"object": "page",
|
||||
"entry": [
|
||||
{
|
||||
"id": "fakeId",
|
||||
"time": 1572630386105,
|
||||
"messaging": [
|
||||
{
|
||||
"recipient": {
|
||||
"id": "fakeId"
|
||||
},
|
||||
"timestamp": 1572630386105,
|
||||
"sender": {
|
||||
"id": "fakeId"
|
||||
},
|
||||
"take_thread_control": {
|
||||
"previous_owner_app_id": 1234567890,
|
||||
"metadata": "Take thread control from a secondary receiver"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -33,6 +33,15 @@
|
|||
<None Update="Files\Payload.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\PayloadWithRequestThreadControl.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\PayloadWithTakeThreadControl.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\PayloadWithPassThreadControl.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Files\RequestResponse.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
|
Загрузка…
Ссылка в новой задаче