Microsoft Bot Framework 如何设置 Facebook 消息标签

Microsoft Bot Framework How to Set Facebook Message Tag

我的用例是能够通过使用 Facebook Dev Docs 中描述的消息标签在 Facebook 的 24 + 1 政策之外发送消息。它指出我需要设置 messaging_type 和一个有效的标签 。我已经设置了 messaging_type 但就是无法使标签起作用。

目前我从 Facebook 收到这个错误:

{
  "error": {
    "message": "(#100) Tag is required for MESSAGE_TAG messaging type.",
    "type": "OAuthException",
    "code": 100,
    "error_subcode": 2018199,
    "fbtrace_id": "GvmKwSrcqVb"
  }
}

对我来说,这表明我已经成功设置了 messaging_type 但不是标签。我已经尝试在 activity.Properties 中添加来自其他建议 GitHub #2924 的标签,如下所示,但它没有用,所以我也在 ChannelData 中尝试了它,但它也不起作用。

activity.Properties = new Newtonsoft.Json.Linq.JObject();
activity.Properties.Add("tag", "CONFIRMED_EVENT_REMINDER");

activity.ChannelData = JObject.FromObject(new
{
    messaging_type = "MESSAGE_TAG",
    tag = "CONFIRMED_EVENT_REMINDER"
});

任何帮助将不胜感激,因为这似乎非常接近工作但同时确实限制了我的机器人的能力。

我已经在 GitHub here 上交叉发布了这个。

谢谢

编辑 - 下面添加了代码示例

这是我的代码,它在所有普通情况下都能正常工作,但我无法使用消息标签在 Facebook 24 + 1 规则之外发送消息。其他一些信息是我已经在 bot 框架门户上迁移了我的 bot,它在 Facebook Messenger 上运行,我已经 pages_messaging 批准但没有申请 pages_messaging_subscriptions。

[RoutePrefix("api/outboundtest")]
public class SendBotMessageTestController : ApiController
{
    [HttpPost]
    [Route("SendSimpleMessage")]
    public async Task<HttpResponseMessage> SendSimpleMessage([FromBody] BotToCustomerMessageTestDTO dto)
    {
        try
        {
            var conversationRef = JsonConvert.DeserializeObject<ConversationReference>(dto.BotConversationJson);

            // We need to ensure the URL is trusted as we lose this from the in-memory cache of trusted URLs if/when the app pool recycles: https://github.com/Microsoft/BotBuilder/issues/1645
            MicrosoftAppCredentials.TrustServiceUrl(conversationRef.ServiceUrl);

            var activity = conversationRef.GetPostToBotMessage();

            var userAccount = new ChannelAccount(conversationRef.User.Id, conversationRef.User.Name);
            var botAccount = new ChannelAccount(conversationRef.Bot.Id, conversationRef.Bot.Name);

            activity.ChannelId = conversationRef.ChannelId;
            activity.From = botAccount;
            activity.Recipient = userAccount;
            activity.Conversation = new ConversationAccount(id: conversationRef.Conversation.Id);
            activity.Locale = "en-Gb";

            var connector = new ConnectorClient(new Uri(conversationRef.ServiceUrl), this.GetCredentials());

            if (activity.ChannelId == "facebook")
            {
                // Add TAG indicate we can send this message outside the allowed window as suggested here: https://github.com/Microsoft/BotBuilder/issues/2924
                activity.Properties = new Newtonsoft.Json.Linq.JObject();
                activity.Properties.Add("tag", "CONFIRMED_EVENT_REMINDER");

                // Set messaging_type as suggested here: https://github.com/Microsoft/BotBuilder/issues/4154 and https://developers.facebook.com/docs/messenger-platform/reference/send-api/
                activity.ChannelData = JObject.FromObject(new
                {
                    notification_type = "REGULAR",
                    messaging_type = "MESSAGE_TAG"
                });
            }

            // Send the message:
            activity.Text = dto.Message;
            await connector.Conversations.SendToConversationAsync((Activity)activity).ConfigureAwait(false);

            var resp = new HttpResponseMessage(HttpStatusCode.OK);
            resp.Content = new StringContent($"Message sent", System.Text.Encoding.UTF8, @"text/plain");
            return resp;

        }
        catch (Exception ex)
        {
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
        }
    }

    private MicrosoftAppCredentials GetCredentials()
    {
        return new MicrosoftAppCredentials("ABC", "XYZ");
    }
}

public class BotToCustomerMessageTestDTO
{
    public string BotConversationJson; // Stored from previous reply using activity.ToConversationReference()
    public string Message;      // This is the message to send.
}

这段代码适合我。

var starter = new ConversationStarter(conversationReference);
starter.Resume(msg)

public class ConversationStarter
{
    ConversationReference conversationReference;

    public ConversationStarter(ConversationReference cr)
        => conversationReference = cr;

    public async Task Resume(string text)
    {
        IMessageActivity message = Activity.CreateMessageActivity();
        message.Text = text;
        message.Locale = "en-Us";
        await Resume(message);
    }

    public async Task Resume(IMessageActivity message)
    {
        var connector = new ConnectorClient(new Uri(conversationReference.ServiceUrl));

        //unathorized workaround
        //https://github.com/Microsoft/BotBuilder/issues/2575
        //https://github.com/Microsoft/BotBuilder/issues/2155#issuecomment-276964664
        MicrosoftAppCredentials.TrustServiceUrl(conversationReference.ServiceUrl); 

        message.ChannelId = conversationReference.ChannelId ??
            (await connector.Conversations.CreateDirectConversationAsync(conversationReference.Bot, conversationReference.User)).Id;
        message.From = conversationReference.Bot;
        message.Recipient = conversationReference.User;
        message.Conversation = new ConversationAccount(id: conversationReference.Conversation.Id);
        var activity = (Activity)message;
        activity.Properties = new Newtonsoft.Json.Linq.JObject();
        activity.Properties.Add("tag", "APPLICATION_UPDATE");
        await connector.Conversations.SendToConversationAsync(activity);
    }

    public async Task ResumeAsDialog<T>(IDialog<T> dialog)
    {
        var message = conversationReference.GetPostToBotMessage();
        var client = new ConnectorClient(new Uri(message.ServiceUrl));

        using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
        {
            var botData = scope.Resolve<IBotData>();
            await botData.LoadAsync(CancellationToken.None);

            //This is our dialog stack
            var task = scope.Resolve<IDialogTask>();

            //interrupt the stack. This means that we're stopping whatever conversation that is currently happening with the user
            //Then adding this stack to run and once it's finished, we will be back to the original conversation
            task.Call(dialog.Void<T, IMessageActivity>(), null);

            await task.PollAsync(CancellationToken.None);
            //flush dialog stack
            await botData.FlushAsync(CancellationToken.None);
        }
    }
}

感谢@Ma3yTa 的帮助。我真的不知道为什么,但你的代码对我不起作用,可能是因为我没有 Facebook 的页面消息订阅权限。

但是,如果这对其他人有帮助,则此代码对我有用(基本上解决方法是将标签 & messaging_type 放在 ChannelData 和属性中)。

所以在 C# 中

        if (activity.ChannelId == "facebook")
        {
            // Add TAG indicate we can send this message outside the allowed window:
            activity.Properties.Add("tag", "BUSINESS_PRODUCTIVITY");
            activity.Properties.Add("messaging_type", "MESSAGE_TAG");

            activity.ChannelData = JObject.FromObject(new
            {
                messaging_type = "MESSAGE_TAG",
                notification_type = "REGULAR",
                tag = "BUSINESS_PRODUCTIVITY"
            });
        }

我还发现我到处都在使用不保留 ChannelData 或原始属性的回复 activity 所以我创建了一个这样的辅助函数:

    public static Activity CreateReply(Activity activity, string message)
    {
        var reply = activity.CreateReply();
        reply.ChannelData = activity.ChannelData;
        reply.Properties = activity.Properties;
        reply.Text = message;

        if (reply.ChannelId == "facebook" && reply.ChannelData == null)
        {
            // If we don't set this you the user doesn't see a notification on their phone:
            reply.ChannelData = JObject.FromObject(new { notification_type = "REGULAR" });
        }

        return reply;
    }

我现在可以使用此机制愉快地发送简单的文本消息或复杂的英雄卡片作为对话堆栈的一部分。我终于可以可靠地发送 Facebook 消息了:-)