public FacebookWebHook()
        {
            Get["/__fbmp/webhook"] = this.HandleRequest(this.WebhookGet);

            Post["/__fbmp/webhook"] = this.HandleRequest(this.WebhookPost);

            Delete["/__fbmp/optin/{type}"] = this.HandleRequest(this.HandleOptOut);
            Post["/__fbmp/optout/{type}"]  = this.HandleRequest(this.HandleOptOut);

            Get["/__fbmp/optin/{type}"] = this.HandleRequest((arg) =>
            {
                return(FacebookWebHook.IsOptInActive(this.SiteDatabase, this.Context, (string)arg.type));
            });

            Get["/__fbmp/linkaccounts"] = this.HandleRequest((arg) =>
            {
                var session = this.SiteDatabase.Query <FacebookChatSession>()
                              .Where(s => s.NcbUserId == 0)
                              .AsEnumerable();

                foreach (var existingSession in session)
                {
                    var customerPSID = existingSession.PageScopedId;

                    // no prior session - see if user have logged in with us before
                    IEnumerable <dynamic> result = FacebookWebHook.FacebookApiGet(this.CurrentSite,
                                                                                  "/" + customerPSID + "/ids_for_apps",
                                                                                  true);

                    var idList = result.FirstOrDefault();

                    if (idList != null) // user have logged in with us before
                    {
                        var id = (string)idList.id;

                        var existingUser = this.SiteDatabase.Query <NcbUser>().Where(u => u.FacebookAppScopedId == id).FirstOrDefault();
                        if (existingUser != null)
                        {
                            existingSession.NcbUserId = existingUser.Id;
                            existingSession.IsRecheckSubscriptionRequired = true;
                            this.SiteDatabase.UpsertRecord(existingSession);

                            if (existingUser.FacebookPageScopedId == null)
                            {
                                existingUser.FacebookPageScopedId = customerPSID;
                                this.SiteDatabase.UpsertRecord(existingUser);
                            }
                        }
                    }
                }

                return("OK");
            });
        }
        public FacebookWebHook()
        {
            Get["/__fbmp/webhook"] = this.HandleRequest(this.WebhookGet);

            Post["/__fbmp/webhook"] = this.HandleRequest(this.WebhookPost);

            Delete["/__fbmp/optin/{type}"] = this.HandleRequest(this.HandleOptOut);
            Post["/__fbmp/optout/{type}"]  = this.HandleRequest(this.HandleOptOut);

            Get["/__fbmp/optin/{type}"] = this.HandleRequest((arg) =>
            {
                return(FacebookWebHook.IsOptInActive(this.SiteDatabase, this.Context, (string)arg.type));
            });
        }
        private void HandleChangesWebhook(dynamic fullbody, dynamic entry)
        {
            var change = entry.changes[0];

            if (change.field == "live_videos" &&
                change.value.status == "live")
            {
                string id = change.value.id;
                IEnumerable <dynamic> getIdApiResult = FacebookWebHook.FacebookApiGet(this.CurrentSite, string.Format("/{0}?fields=video,title,description,permalink_url", id));

                var result = getIdApiResult.FirstOrDefault();
                this.HandleNewLiveVideo(fullbody, id, result);
                return;
            }

            if (change.field == "feed" &&
                change.value.reaction_type != null)
            {
                this.HandleReaction(fullbody,
                                    (string)change.value.post_id,
                                    (string)change.value.sender_id,
                                    (string)change.value.sender_name,
                                    (string)change.value.reaction_type,
                                    change.value);
                return;
            }

            if (change.field == "feed" &&
                change.value.comment_id != null)
            {
                this.HandleComment(fullbody,
                                   (string)change.value.post_id,
                                   (string)change.value.sender_id,
                                   (string)change.value.sender_name,
                                   (string)change.value.message,
                                   change.value);
                return;
            }

            if (change.field == "leadgen")
            {
                this.HandleLeadGenSetup(fullbody, change.value);
                return;
            }
        }
        /// <summary>
        /// Handle lead generation ad
        /// </summary>
        /// <param name="fullbody"></param>
        /// <param name="values"></param>
        private void HandleLeadGenSetup(dynamic fullbody, dynamic values)
        {
            var leadId       = (string)values.leadgen_id;
            var created_time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
                               .AddMilliseconds((long)values.created_time)
                               .ToLocalTime();

            IEnumerable <dynamic> result = FacebookWebHook.FacebookApiGet(this.CurrentSite, "/" + leadId);

            var lead = result.FirstOrDefault();

            if (lead == null || lead.error != null)
            {
                MailSenderModule.SendEmail("*****@*****.**", "LeadGen Error - Cannot get Lead", "Lead Id: " + leadId + " Message: " + lead.error.message);
                return;
            }

            JObject fieldValues = new JObject();

            foreach (var field in lead.field_data as JArray)
            {
                if (field["values"].Count() == 1)
                {
                    fieldValues.Add(field["name"].Value <string>(), field["values"][0].Value <string>());
                }
                else
                {
                    fieldValues.Add(field["name"].Value <string>(), field["values"]);
                }
            }

            this.HandleLeadGen(
                (string)values.ad_id,
                (string)values.form_id,
                (string)values.leadgen_id,
                created_time,
                (string)values.page_id,
                (string)values.adgroup_id,
                (object)lead,
                fieldValues);
        }
        /// <summary>
        /// Call Facebook Graph API with given set of parameters
        /// </summary>
        /// <param name="url"></param>
        /// <param name="sendSecretProof"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static dynamic FacebookApi(Method method, dynamic siteSettings, string url, object payload, bool sendSecretProof = false, bool throwOnError = false, params string[] queryStringPair)
        {
            RestClient  client = new RestClient("https://graph.facebook.com/");
            RestRequest req    = new RestRequest(url, method);

            // add parameters
            if (queryStringPair != null)
            {
                if (queryStringPair.Length % 2 != 0)
                {
                    throw new ArgumentOutOfRangeException("parameterPairs are not pairs");
                }

                for (int i = 0; i < queryStringPair.Length; i += 2)
                {
                    var key   = queryStringPair[i];
                    var value = queryStringPair[i + 1];

                    if (key == "access_token")
                    {
                        throw new ArgumentOutOfRangeException("access_token will be added automatically");
                    }

                    if (key == "appsecret_proof")
                    {
                        throw new ArgumentOutOfRangeException("appsecret_proof will be added automatically");
                    }

                    req.Parameters.Add(new Parameter()
                    {
                        Name  = key,
                        Value = value,
                        Type  = ParameterType.QueryString
                    });
                }
            }

            if (sendSecretProof)
            {
                string hash = MemoryCache.Default["FacebookMessengerModule.appsecret_proof"] as string;
                if (hash == null)
                {
                    hash = FacebookWebHook.HashHmac((string)siteSettings.FacebookMessenger.PageAccessToken,
                                                    (string)siteSettings.Application.FacebookAppSecret);

                    MemoryCache.Default["FacebookMessengerModule.appsecret_proof"] = hash;
                }
                req.AddQueryParameter("appsecret_proof", hash);
            }

            req.AddQueryParameter("access_token", (string)siteSettings.FacebookMessenger.PageAccessToken);

            if (payload is string)
            {
                req.AddParameter("payload", payload);
            }
            else
            {
                req.RequestFormat = DataFormat.Json;
                req.AddJsonBody(payload);
            }

            var response = client.Execute(req);

            if (response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                MailSenderModule.SendEmail("*****@*****.**", "Facebook API Post Error", response.ToString());

                if (throwOnError)
                {
                    throw new Exception("Server Returned: " + response.StatusCode);
                }
            }

            return(JObject.Parse(response.Content));
        }
        /// <summary>
        /// Call Facebook Graph API with given set of parameters
        /// </summary>
        /// <param name="url"></param>
        /// <param name="sendSecretProof"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static IEnumerable <dynamic> FacebookApiGet(dynamic siteSettings, string url, bool sendSecretProof = false, bool throwOnError = false, params string[] queryStringPair)
        {
            var client = new RestClient("https://graph.facebook.com/");
            var req    = new RestRequest(url, Method.GET);

            // add parameters
            if (queryStringPair != null)
            {
                if (queryStringPair.Length % 2 != 0)
                {
                    throw new ArgumentOutOfRangeException("parameterPairs are not pairs");
                }

                for (int i = 0; i < queryStringPair.Length; i += 2)
                {
                    var key   = queryStringPair[i];
                    var value = queryStringPair[i + 1];

                    if (key == "access_token")
                    {
                        throw new ArgumentOutOfRangeException("access_token will be added automatically");
                    }

                    if (key == "appsecret_proof")
                    {
                        throw new ArgumentOutOfRangeException("appsecret_proof will be added automatically");
                    }

                    req.Parameters.Add(new Parameter()
                    {
                        Name  = key,
                        Value = value,
                        Type  = ParameterType.QueryString
                    });
                }
            }

            if (sendSecretProof)
            {
                string hash = MemoryCache.Default["FacebookMessengerModule.appsecret_proof"] as string;
                if (hash == null)
                {
                    hash = FacebookWebHook.HashHmac((string)siteSettings.FacebookMessenger.PageAccessToken,
                                                    (string)siteSettings.Application.FacebookAppSecret);

                    MemoryCache.Default["FacebookMessengerModule.appsecret_proof"] = hash;
                }
                req.AddQueryParameter("appsecret_proof", hash);
            }

            req.AddQueryParameter("access_token", (string)siteSettings.FacebookMessenger.PageAccessToken);

LoadMore:

            var response = client.Execute(req);

            if (throwOnError && response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                throw new Exception("Server Returned: " + response.StatusCode);
            }

            dynamic result = JObject.Parse(response.Content);

            if (result.data != null)
            {
                foreach (var item in result.data)
                {
                    yield return(item);
                }
            }
            else
            {
                // this is a single item response
                yield return(result);
            }

            if (result.paging != null && result.paging.next != null)
            {
                req.AddQueryParameter("after", (string)result.paging.cursors.after);
                goto LoadMore;
            }
        }
        private dynamic HandleMessagingWebhook(dynamic entry, dynamic messaging)
        {
            if (messaging.delivery != null) // this is just delivery message
            {
                return("EVENT_RECEIVED");
            }

            if (messaging.read != null) // this is just delivery message
            {
                return("EVENT_RECEIVED");
            }

            // Get the sender PSID
            string customerPSID = messaging.sender.id;

            if (customerPSID == (string)entry.id)      // this is message sent by bot
            {
                customerPSID = messaging.recipient.id; // so the cusotmer psid is recipient
            }

            lock (BaseModule.GetLockObject(customerPSID))
            {
                // try to get the current chat session
                var existingSession = MemoryCache.Default["FBMP:" + customerPSID] as FacebookChatSession;

                Action linkAccount = () =>
                {
                    // no prior session - see if user have logged in with us before
                    IEnumerable <dynamic> result = FacebookWebHook.FacebookApiGet(this.CurrentSite,
                                                                                  "/" + customerPSID + "/ids_for_apps",
                                                                                  true);

                    var idList = result.FirstOrDefault();

                    if (idList != null) // user have logged in with us before
                    {
                        var id = (string)idList.id;

                        var existingUser = this.SiteDatabase.Query <NcbUser>().Where(u => u.FacebookAppScopedId == id).FirstOrDefault();
                        if (existingUser != null)
                        {
                            existingSession.NcbUserId = existingUser.Id;

                            if (existingUser.FacebookPageScopedId == null)
                            {
                                existingUser.FacebookPageScopedId = customerPSID;
                                this.SiteDatabase.UpsertRecord(existingUser);
                            }
                        }
                        else
                        {
                            // cannot find in database - something must slipped
                            // should create user here

                            MailSenderModule.SendEmail("*****@*****.**",
                                                       "FacebookWebHook Handler Error",

                                                       "<b>User :</b>" + customerPSID + "<br/>" +
                                                       "User have logged in with us before, with App Scoped Id but did not have record in database");
                        }
                    }
                };


                if (existingSession == null)
                {
                    existingSession = this.SiteDatabase.Query <FacebookChatSession>().Where(u => u.PageScopedId == customerPSID).FirstOrDefault();

                    if (existingSession == null)
                    {
                        existingSession = new FacebookChatSession();

                        existingSession.PageScopedId = customerPSID;
                        linkAccount();

                        MemoryCache.Default["FBMP:" + customerPSID] = existingSession;
                    }
                }
                // this user have chat with us already
                // but may have registered on the website after they chat with us
                if (existingSession.NcbUserId == 0)
                {
                    linkAccount();

                    MemoryCache.Default["FBMP:" + customerPSID] = existingSession;
                }

                // Update profile if profile is outdated
                if (DateTime.Now.Subtract(existingSession.LastProfileUpdate).TotalDays > 7)
                {
                    if (existingSession.NcbUserId != 0)
                    {
                        var user = this.SiteDatabase.GetById <NcbUser>(existingSession.NcbUserId);

                        existingSession.UserProfile = user.Profile;
                    }
                    else
                    {
                        IEnumerable <dynamic> result = FacebookWebHook.FacebookApiGet(this.CurrentSite,
                                                                                      "/" + customerPSID,
                                                                                      false);

                        existingSession.UserProfile = result.FirstOrDefault();
                    }

                    existingSession.LastProfileUpdate = DateTime.Now;
                    this.SiteDatabase.UpsertRecord(existingSession);

                    MemoryCache.Default["FBMP:" + customerPSID] = existingSession;
                }

                existingSession.HandleWebhook(this.SiteDatabase, this.CurrentSite, messaging);
            }

            return("EVENT_RECEIVED");
        }