facebooktagsbotframeworkmessageproactive

Microsoft Bot Framework How to Set Facebook Message Tag


My use case is to be able to send a message outside Facebook's 24 + 1 policy by using message tags as described on Facebook Dev Docs. It states I need to set the messaging_type and a valid tag. I have set the messaging_type but just can't get the tag to work.

At present I get this error from Facebook:

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

To me this indicates I have successfully set the messaging_type but not the tag. I have tried adding the tag from other suggestions GitHub #2924 in the activity.Properties as below but it didn't work so I've also tried it in the ChannelData which also doesn't work.

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"
});

Any help would be greatly appreciated as this seems so close to working but at the same time is really limiting my bot's capabilities.

I've cross posted this on GitHub here.

Thanks

Edit - Code Example Added Below

This is my code, it works fine in all ordinary cases but I can't send a message outside of the Facebook 24 + 1 rule by using message tags. Some other info is I've migrated my bot on the bot framework portal and it's live on Facebook Messenger where I have pages_messaging approved but haven't applied for 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.
}

Solution

  • Thanks to @Ma3yTa for helping with this. I don't really know why but your code didn't work for me, maybe because I don't have the page messaging subscription permission from Facebook.

    However, if this helps others, this code does work for me (essentially the fix was to put the tag & messaging_type in both the ChannelData and the Properties).

    So in 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"
                });
            }
    

    I have also found that I use replies all over the place which don't preserve the ChannelData or the Properties from the original activity so I created a helper function like this:

        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;
        }
    

    I can now happily send simple text messages or complex Hero Cards as part of dialog stack using this mechanism. Finally, I can reliably send out Facebook messages :-)