2

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.
}
Stu Price
  • 163
  • 12
  • I think you can have a look on your messages on Facebook's side to check if the flag is there or not, using Facebook Graph Explorer https://developers.facebook.com/tools/explorer/ – Nicolas R Mar 04 '18 at 21:22
  • Can you please provide the complete code that you are sending and constructive the activity which is causing the error? – D4RKCIDE Mar 06 '18 at 23:35
  • Hi @JasonSowers, please see my code added to the question and my responses to Ma3yTa below. Thanks. – Stu Price Mar 07 '18 at 14:39

2 Answers2

3

This code works for me.

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
  • 152
  • 3
  • 8
  • I get the same problem running your code. .NET Exception "Operation returned an invalid status code 'BadRequest" and on the bot framework portal I get this error: { "error": { "message": "(#100) Cannot send 'RESPONSE' messaging_type messages outside messaging window", "type": "OAuthException", "code": 100, "error_subcode": 2018208, "fbtrace_id": "GzdBC3tVbrn" } } – Stu Price Mar 07 '18 at 14:33
  • If I set the messaging_type using activity.ChannelData = JObject.FromObject(new { messaging_type = "MESSAGE_TAG" }); then I get a different error which just looks like the tag is missing. {"error":{"message":"(#100) Tag is required for MESSAGE_TAG messaging type.","type":"OAuthException","code":100,"error_subcode":2018199,"fbtrace_id":"EADNyI\/XPqw"}} – Stu Price Mar 07 '18 at 14:36
1

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 :-)

Stu Price
  • 163
  • 12