1

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

目前我从 Facebook 收到此错误:

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

对我来说,这表明我已经成功设置了 messing_type 但没有设置标签。我已经尝试在活动中添加其他建议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 上交叉发布了这个

谢谢

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

这是我的代码,在所有普通情况下都能正常工作,但我无法使用消息标签在 Facebook 24 + 1 规则之外发送消息。其他一些信息是我已经在机器人框架门户上迁移了我的机器人,它在 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.
}
4

2 回答 2

3

这段代码对我有用。

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);
        }
    }
}
于 2018-03-07T09:43:17.643 回答
1

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

但是,如果这对其他人有帮助,那么这段代码确实对我有用(基本上修复是将标签和消息传递类型放在 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 或属性的回复,所以我创建了一个这样的辅助函数:

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

于 2018-12-07T17:50:04.643 回答