如何处理 facebook messenger 用户位置? C# 机器人框架

How to handle facebook messenger user location ? C# Botframework

我正在使用 C# 中的 Microsoft botframework 设计一个机器人,它将部署在 Messenger 上。

我要添加的功能之一是显示用户位置周围的景点。我在 facebooks 开发网站上发现了这个 curl 请求。

curl -X POST -H "Content-Type: application/json" -d '{
  "recipient":{
    "id":"USER_ID"
  },
  "message":{
    "text":"Please share your location:",
    "quick_replies":[
      {
        "content_type":"location",
      }
    ]
  }
}' "https://graph.facebook.com/v2.6/me/messages?access_token=PAGE_ACCESS_TOKEN"

除此之外,我还没有找到指定何时发送用户位置请求的方法以及存储位置以便我可以使用它进行附近搜索的方法。

如果有人能指出正确的方向,我将不胜感激。

这是我正在尝试复制的一个很好的例子。

这是我要复制的示例。

[LuisIntent("Stores")]
        public async Task Stores(IDialogContext context, LuisResult result)
        {
            var msg = "location";
            if (msg == "location")
            {
                Lresult = result;
                await context.Forward(new FacebookLocationDialog(), ResumeAfter, msg, CancellationToken.None);
            }
            else
            {
                await Stores(context, result);
            }
        }

        public async Task ResumeAfter(IDialogContext context, IAwaitable<Place> result)
        {
            var place = await result;

            if (place != default(Place))
            {
                var geo = (place.Geo as JObject)?.ToObject<GeoCoordinates>();
                if (geo != null)
                {
                    var reply = context.MakeMessage();
                    reply.Attachments.Add(new HeroCard
                    {
                        Title = "Open your location in bing maps!",
                        Buttons = new List<CardAction> {
                            new CardAction
                            {
                                Title = "Your location",
                                Type = ActionTypes.OpenUrl,
                                Value = $"https://www.bing.com/maps/?v=2&cp={geo.Latitude}~{geo.Longitude}&lvl=16&dir=0&sty=c&sp=point.{geo.Latitude}_{geo.Longitude}_You%20are%20here&ignoreoptin=1"
                            }
                        }

                    }.ToAttachment());

                    await context.PostAsync(reply);
                }
                else
                {
                    await context.PostAsync("No GeoCoordinates!");
                }
            }
            else
            {
                await context.PostAsync("No location extracted!");
            }

            context.Wait(Stores);
        }

    }
    [Serializable]
    public class FacebookLocationDialog : IDialog<Place>
    {
        public async Task StartAsync(IDialogContext context)
        {
            context.Wait(MessageReceivedAsync);
        }

        public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
        {
            var msg = await argument;
            if (msg.ChannelId == "facebook")
            {
                var reply = context.MakeMessage();
                reply.ChannelData = new FacebookMessage
                (
                    text: "Please share your location with me.",
                    quickReplies: new List<FacebookQuickReply>
                    {
                        // If content_type is location, title and payload are not used
                        // see https://developers.facebook.com/docs/messenger-platform/send-api-reference/quick-replies#fields
                        // for more information.
                        new FacebookQuickReply(
                            contentType: FacebookQuickReply.ContentTypes.Location,
                            title: default(string),
                            payload: default(string)
                        )
                    }
                );
                await context.PostAsync(reply);
                context.Wait(LocationReceivedAsync);
            }
            else
            {
                context.Done(default(Place));
            }
        }

        public virtual async Task LocationReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
        {
            var msg = await argument;
            var location = msg.Entities?.Where(t => t.Type == "Place").Select(t => t.GetAs<Place>()).FirstOrDefault();
            context.Done(location);
        }
    }

ResumeAfter 任务中的 "context.Wait(Stores);" 正在抛出 "The type arguments for method IDialogStack.Wait cannot be inferred from the usage"。

如何从 Facebook Messenger 获取位置信息

查看 GitHub 上的 BotBuilder-Location 项目:https://github.com/Microsoft/BotBuilder-Location

看来它可能是你的样本中使用的那个(根据提供的样本:https://github.com/Microsoft/BotBuilder-Location#address-selection-using-fb-messengers-location-picker-gui-dialog

您可能对 FacebookNativeLocationRetrieverDialog.cs:

特别感兴趣
private async Task StartAsync(IDialogContext context, string message)
{
    var reply = context.MakeMessage();
    reply.ChannelData = new FacebookMessage
    (
        text: message,
        quickReplies: new List<FacebookQuickReply>
        {
                new FacebookQuickReply(
                    contentType: FacebookQuickReply.ContentTypes.Location,
                    title: default(string),
                    payload: default(string)
                )
        }
    );

    await context.PostAsync(reply);
    context.Wait(this.MessageReceivedAsync);
}

顺便说一句,BotBuilder (https://github.com/Microsoft/BotBuilder/blob/master/CSharp/Samples/EchoBot/EchoLocationDialog.cs)

中提供的 EchoBot 样本也是如此

实施

[Serializable]
public class MyFacebookLocationDialog : IDialog<Place>
{
    public async Task StartAsync(IDialogContext context)
    {
        context.Wait(MessageReceivedAsync);
    }

    public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
    {
        var msg = await argument;

        // Here we prepare the message on Facebook that will ask for Location
        if (msg.ChannelId == "facebook")
        {
            var reply = context.MakeMessage();
            reply.ChannelData = new FacebookMessage
            (
                text: "Please share your location with me.",
                quickReplies: new List<FacebookQuickReply>
                {
                    // If content_type is location, title and payload are not used
                    // see https://developers.facebook.com/docs/messenger-platform/send-api-reference/quick-replies#fields
                    // for more information.
                    new FacebookQuickReply(
                        contentType: FacebookQuickReply.ContentTypes.Location,
                        title: default(string),
                        payload: default(string)
                    )
                }
            );
            await context.PostAsync(reply);

            // LocationReceivedAsync will be the place where we handle the result
            context.Wait(LocationReceivedAsync);
        }
        else
        {
            context.Done(default(Place));
        }
    }

    public async Task LocationReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
    {
        var msg = await argument;
        var location = msg.Entities?.Where(t => t.Type == "Place").Select(t => t.GetAs<Place>()).FirstOrDefault();

        // Printing message main content about location
        await context.PostAsync($"Location received: { Newtonsoft.Json.JsonConvert.SerializeObject(msg.Entities) }");

        // The result can be used then to do what you want, here in this sample it outputs a message with a link to Bing Maps centered on the position
        var geo = (location.Geo as JObject)?.ToObject<GeoCoordinates>();
        if (geo != null)
        {
            var reply = context.MakeMessage();
            reply.Attachments.Add(new HeroCard
            {
                Title = "Open your location in bing maps!",
                Buttons = new List<CardAction> {
                            new CardAction
                            {
                                Title = "Your location",
                                Type = ActionTypes.OpenUrl,
                                Value = $"https://www.bing.com/maps/?v=2&cp={geo.Latitude}~{geo.Longitude}&lvl=16&dir=0&sty=c&sp=point.{geo.Latitude}_{geo.Longitude}_You%20are%20here&ignoreoptin=1"
                            }
                        }

            }.ToAttachment());

            await context.PostAsync(reply);
            context.Done(location);
        }
        else
        {
            await context.PostAsync("No GeoCoordinates!");
            context.Done(default(Place));
        }
    }
}

演示图片:

可以从消息的实体列表访问作为消息发送的位置:

"type": "message",
"id": "mid.$cAAUW791mzPBhksN19999990ORr",
"timestamp": "2017-04-12T09:28:30.812Z",
"serviceUrl": "https://facebook.botframework.com",
"channelId": "facebook",
"from": {
    "id": "999999999999",
    "name": "StuartD"
},
"conversation": {
    "isGroup": false,
    "id": "999999999999-999999999999"
},
"recipient": {
    "id": "88888888888",
    "name": "Shhhh"
},
"attachments": [],
"entities": [{
    "type": "Place",
    "geo": {
        "elevation": 0.0,
        "latitude": 50.8249626159668,
        "longitude": -0.14287842810153961,
        "type": "GeoCoordinates"
    }
}

在控制器中:

var location = message.Entities?.FirstOrDefault(e => e.Type == "Place");
if (location != null) 
{
     var latitude = location.Properties["geo"]?["latitude"]?.ToString();
     var longitude = location.Properties["geo"]?["longitude"]?.ToString();
// etc