-
Notifications
You must be signed in to change notification settings - Fork 178
/
Copy pathFacebookHelper.cs
250 lines (221 loc) · 9.25 KB
/
FacebookHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Bot.Builder.Community.Adapters.Facebook.FacebookEvents;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
namespace Bot.Builder.Community.Adapters.Facebook
{
/// <summary>
/// Helper class for converting between Bot Framework objects and Facebook API objects.
/// </summary>
public static class FacebookHelper
{
/// <summary>
/// Converts a Bot Framework activity to a Facebook messenger outbound message ready for the API.
/// </summary>
/// <param name="activity">The activity to be converted to Facebook message.</param>
/// <returns>The resulting message.</returns>
/// <exception cref="ArgumentNullException"><paramref name="activity"/> is null.</exception>
public static FacebookMessage ActivityToFacebook(Activity activity)
{
if (activity == null)
{
throw new ArgumentNullException(nameof(activity));
}
var facebookMessage = new FacebookMessage(activity.Conversation.Id, new Message(), "RESPONSE");
facebookMessage.Message.Text = activity.Text;
if (activity.ChannelData != null)
{
facebookMessage = activity.GetChannelData<FacebookMessage>();
if (facebookMessage.SenderAction != null)
{
facebookMessage.Message = null;
}
else
{
// make sure the quick reply has a type
if (facebookMessage.Message.QuickReplies.Any())
{
foreach (var reply in facebookMessage.Message.QuickReplies)
{
if (string.IsNullOrWhiteSpace(reply.ContentType))
{
reply.ContentType = "text";
}
}
}
}
}
if (activity.Attachments != null && activity.Attachments.Count > 0 && facebookMessage.Message != null)
{
var payload = JsonConvert.DeserializeObject<AttachmentPayload>(JsonConvert.SerializeObject(
activity.Attachments[0].Content,
Formatting.None,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
}));
var facebookAttachment = new FacebookAttachment
{
Type = activity.Attachments[0].ContentType,
Payload = payload,
};
facebookMessage.Message.Attachment = facebookAttachment;
}
return facebookMessage;
}
/// <summary>
/// Converts a single Facebook messenger message to a Bot Framework activity.
/// </summary>
/// <param name="message">The message to be processed.</param>
/// <returns>An Activity with the result.</returns>
/// <exception cref="ArgumentNullException"><paramref name="message"/> is null.</exception>
/// <remarks>A webhook call may deliver more than one message at a time.</remarks>
public static Activity ProcessSingleMessage(FacebookMessage message)
{
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
if (message.Sender == null && message.OptIn?.UserRef != null)
{
message.Sender = new FacebookBotUser { Id = message.OptIn?.UserRef };
}
var activity = new Activity()
{
ChannelId = "facebook",
Timestamp = default,
Conversation = new ConversationAccount()
{
Id = message.Sender?.Id,
},
From = new ChannelAccount()
{
Id = message.Sender?.Id,
Name = message.Sender?.Id,
},
Recipient = new ChannelAccount()
{
Id = message.Recipient.Id,
Name = message.Recipient.Id,
},
ChannelData = message,
Type = ActivityTypes.Event,
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.Text = message.Message.Text;
activity.Type = activity.GetChannelData<FacebookMessage>().Message.IsEcho ? ActivityTypes.Event : ActivityTypes.Message;
if (message.Message.Attachments != null && message.Message.Attachments.Count > 0)
{
activity.Attachments = HandleMessageAttachments(message.Message);
}
}
else if (message.PostBack != null)
{
activity.Type = ActivityTypes.Message;
activity.Text = message.PostBack.Payload;
}
else if (message.Referral != null)
{
activity.Type = ActivityTypes.Event;
activity.Value = message.Referral.Ref;
}
return activity;
}
/// <summary>
/// Extracts attachments from a Facebook message.
/// </summary>
/// <param name="message">The message to get attachments from.</param>
/// <returns>A List of the attachments contained within the message.</returns>
public static List<Attachment> HandleMessageAttachments(Message message)
{
var attachmentsList = new List<Attachment>();
foreach (var facebookAttachment in message.Attachments)
{
var attachment = new Attachment
{
Content = facebookAttachment.Payload,
ContentType = facebookAttachment.Type,
};
attachmentsList.Add(attachment);
}
return attachmentsList;
}
/// <summary>
/// Writes an HTTP response payload.
/// </summary>
/// <param name="response">The HTTP response to write to.</param>
/// <param name="code">The status code to apply.</param>
/// <param name="text">The text to be written.</param>
/// <param name="encoding">The encoding for the text.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="response"/>, <paramref name="text"/>,
/// or <paramref name="encoding"/> is null.</exception>
public static async Task WriteAsync(HttpResponse response, HttpStatusCode code, string text, Encoding encoding, CancellationToken cancellationToken)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
response.ContentType = "text/plain";
response.StatusCode = (int)code;
var data = encoding.GetBytes(text);
await response.Body.WriteAsync(data, 0, data.Length, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Generates an activity that displays a typing indicator.
/// </summary>
/// <param name="recipientId">The ID of the recipient of the message.</param>
/// <returns>An activity with sender_action equal to "typing_on".</returns>
public static Activity GenerateTypingActivity(string recipientId)
{
var activity = new Activity()
{
ChannelId = "facebook",
Conversation = new ConversationAccount()
{
Id = recipientId,
},
ChannelData = new FacebookMessage(recipientId, null, string.Empty),
Type = ActivityTypes.Message,
Text = null,
};
// we need only the sender action (and the recipient id) to be present in the message
var message = activity.GetChannelData<FacebookMessage>();
message.SenderAction = "typing_on";
message.MessagingType = null;
message.Sender = null;
return activity;
}
}
}