Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 170 additions & 83 deletions MessengerBot/Controllers/WebhookController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,93 +8,180 @@
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using MessengerBot.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Bot.Messenger;
using System.Web.Http.Controllers;
using Bot.Messenger.Models;

namespace MessengerBot.Controllers
{
public class WebhookController : ApiController
{
string pageToken = "page token";
string appSecret = "app secret";

public HttpResponseMessage Get()
{
var querystrings = Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
if (querystrings["hub.verify_token"] == "hello")
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(querystrings["hub.challenge"], Encoding.UTF8, "text/plain")
};
}
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
}

[HttpPost]
public async Task<HttpResponseMessage> Post()
{
var signature = Request.Headers.GetValues("X-Hub-Signature").FirstOrDefault().Replace("sha1=", "");
var body = await Request.Content.ReadAsStringAsync();
if (!VerifySignature(signature, body))
return new HttpResponseMessage(HttpStatusCode.BadRequest);

var value = JsonConvert.DeserializeObject<WebhookModel>(body);
if (value._object != "page")
return new HttpResponseMessage(HttpStatusCode.OK);

foreach (var item in value.entry[0].messaging)
{
if (item.message == null && item.postback == null)
continue;
else
await SendMessage(GetMessageTemplate(item.message.text, item.sender.id));
}

return new HttpResponseMessage(HttpStatusCode.OK);
}

private bool VerifySignature(string signature, string body)
{
var hashString = new StringBuilder();
using (var crypto = new HMACSHA1(Encoding.UTF8.GetBytes(appSecret)))
{
var hash = crypto.ComputeHash(Encoding.UTF8.GetBytes(body));
foreach (var item in hash)
hashString.Append(item.ToString("X2"));
}

return hashString.ToString().ToLower() == signature.ToLower();
}

/// <summary>
/// get text message template
/// </summary>
/// <param name="text">text</param>
/// <param name="sender">sender id</param>
/// <returns>json</returns>
private JObject GetMessageTemplate(string text, string sender)
{
return JObject.FromObject(new
{
recipient = new { id = sender },
message = new { text = text }
});
}

/// <summary>
/// send message
/// </summary>
/// <param name="json">json</param>
private async Task SendMessage(JObject json)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage res = await client.PostAsync($"https://graph.facebook.com/v2.6/me/messages?access_token={pageToken}", new StringContent(json.ToString(), Encoding.UTF8, "application/json"));
}
}
}
public class WebhookController : ApiController
{
string _pageToken = "page token";
string _appSecret = "app secret";
string _verifyToken = "hello";

string _quickReplyPayload_IsUserMsg = "WAS_USER_MESSAGE";
string _quickReplyPayload_IsNotUserMsg = "WAS_NOT_USER_MESSAGE";


private MessengerPlatform _Bot { get; set; }

protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);

/***Credentials are fetched from web.config ApplicationSettings when the CreateInstance
----method is called without a credentials parameter or if the parameterless constructor
----is used to initialize the MessengerPlatform class. This holds true for all types that inherit from
----Bot.Messenger.ApiBase

_Bot = MessengerPlatform.CreateInstance();
_Bot = new MessengerPlatform();
***/

_Bot = MessengerPlatform.CreateInstance(
MessengerPlatform.CreateCredentials(_appSecret, _pageToken, _verifyToken));
}

public HttpResponseMessage Get()
{
var querystrings = Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);

if (_Bot.Authenticator.VerifyToken(querystrings["hub.verify_token"]))
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(querystrings["hub.challenge"], Encoding.UTF8, "text/plain")
};
}

return new HttpResponseMessage(HttpStatusCode.Unauthorized);
}

[HttpPost]
public async Task<HttpResponseMessage> Post()
{
var body = await Request.Content.ReadAsStringAsync();

LogInfo("WebHook_Received", new Dictionary<string, string>
{
{ "Request Body", body }
});

if (!_Bot.Authenticator.VerifySignature(Request.Headers.GetValues("X-Hub-Signature").FirstOrDefault(), body))
return new HttpResponseMessage(HttpStatusCode.BadRequest);

WebhookModel webhookModel = _Bot.ProcessWebhookRequest(body);

if (webhookModel._Object != "page")
return new HttpResponseMessage(HttpStatusCode.OK);

foreach (var entry in webhookModel.Entries)
{
foreach (var evt in entry.Events)
{
if (evt.EventType == WebhookEventType.PostbackRecievedCallback
|| evt.EventType == WebhookEventType.MessageReceivedCallback)
{
await _Bot.SendApi.SendActionAsync(evt.Sender.ID, SenderAction.typing_on);

var userProfileRsp = await _Bot.UserProfileApi.GetUserProfileAsync(evt.Sender.ID);

if (evt.EventType == WebhookEventType.PostbackRecievedCallback)
{
await ProcessPostBack(evt.Sender.ID, userProfileRsp?.FirstName, evt.Postback);
}
if (evt.EventType == WebhookEventType.MessageReceivedCallback)
{
if (evt.Message.IsQuickReplyPostBack)
await ProcessPostBack(evt.Sender.ID, userProfileRsp?.FirstName, evt.Message.QuickReplyPostback);
else
{
await _Bot.SendApi.SendTextAsync(evt.Sender.ID, $"We got your message {userProfileRsp?.FirstName}, to prove it, we'll send it back to you :)");
await ResendMessageToUser(evt);
await ConfirmIfCorrect(evt);
}
}
}

await _Bot.SendApi.SendActionAsync(evt.Sender.ID, SenderAction.typing_off);

}
}

return new HttpResponseMessage(HttpStatusCode.OK);
}

private async Task ConfirmIfCorrect(WebhookEvent evt)
{
SendApiResponse sendQuickReplyResponse = await _Bot.SendApi.SendTextAsync(evt.Sender.ID, "Is that you message?", new List<QuickReply>
{
new QuickReply
{
ContentType = QuickReplyContentType.text,
Title = "Yes",
Payload = _quickReplyPayload_IsUserMsg
},
new QuickReply
{
ContentType = QuickReplyContentType.text,
Title = "No",
Payload = _quickReplyPayload_IsNotUserMsg
}
});

LogSendApiResponse(sendQuickReplyResponse);
}

private async Task ResendMessageToUser(WebhookEvent evt)
{
SendApiResponse response = new SendApiResponse();

if (evt.Message.Attachments == null)
{
string text = evt.Message?.Text;

if (string.IsNullOrWhiteSpace(text))
text = "Hello :)";

response = await _Bot.SendApi.SendTextAsync(evt.Sender.ID, $"Your Message => {text}");
}
else
{
foreach (var attachment in evt.Message.Attachments)
{
if (attachment.Type != AttachmentType.fallback && attachment.Type != AttachmentType.location)
{
response = await _Bot.SendApi.SendAttachmentAsync(evt.Sender.ID, attachment);
}
}
}

LogSendApiResponse(response);
}

private async Task ProcessPostBack(string userId, string username, Postback postback)
{
if (postback.Payload == _quickReplyPayload_IsNotUserMsg)
await _Bot.SendApi.SendTextAsync(userId, $"Sorry about that {username}, try sending something else.");
else if (postback.Payload == _quickReplyPayload_IsUserMsg)
await _Bot.SendApi.SendTextAsync(userId, $"Yay! We got it.");
}

private static void LogSendApiResponse(SendApiResponse response)
{
LogInfo("SendApi Web Request", new Dictionary<string, string>
{
{ "Response", response?.ToString() }
});
}

private static void LogInfo(string eventName, Dictionary<string, string> telemetryProperties)
{
//Log telemetry in DB or Application Insights
}
}
}

4 changes: 4 additions & 0 deletions MessengerBot/MessengerBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Bot.Messenger, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Bot.Messenger.1.0.0\lib\net45\Bot.Messenger.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
<Private>True</Private>
Expand Down
18 changes: 18 additions & 0 deletions MessengerBot/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@
http://go.microsoft.com/fwlink/?LinkId=301879
-->
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="Bot.Messenger.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<applicationSettings>
<Bot.Messenger.Settings>
<setting name="PageToken" serializeAs="String">
<value>page token</value>
</setting>
<setting name="AppSecret" serializeAs="String">
<value>app secret</value>
</setting>
<setting name="VerifyToken" serializeAs="String">
<value>hello</value>
</setting>
</Bot.Messenger.Settings>
</applicationSettings>
<appSettings></appSettings>
<system.web>
<compilation debug="true" targetFramework="4.6.1" />
Expand Down
1 change: 1 addition & 0 deletions MessengerBot/packages.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Bot.Messenger" version="1.0.0" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net461" />
Expand Down
Binary file modified README.md
Binary file not shown.