Microsoft Botframework向Bot(而非用户)发送主动消息

时间:2020-10-12 16:38:26

标签: c# botframework

我们目前正在使用botframework开发自动化。

在对话的某个时刻,我们通过服务总线发送了一些数据进行处理,并等待响应,然后希望继续对话。我们已经实现了在服务总线订阅中等待响应条目的部分,然后我们希望将Event类型的Activity发送到bot。

我们按照其他帖子中所述对主动消息执行了相同的步骤。 我们能够重新创建botclient和对话引用以及所有内容,但是最后,当我们发送活动时,我们总是将其发送给用户而不是机器人。但这不会触发“ EventActivityPrompt”。

达到期望结果的唯一方法是在api / messages上发布帖子,但这对于我们的口味来说太复杂了,我们正在寻找一种通过botClient(或类似技术)的更简单的方法

有人有好主意吗? :)

ServiceBusReceiver消息处理:

private static async Task ProcessMessagesAsync(Message message, CancellationToken token)
    {
        // Process the message.
        Console.WriteLine($"Received message: SequenceNumber:{message.SystemProperties.SequenceNumber} Body:{Encoding.UTF8.GetString(message.Body)}");

        _logger?.LogInformation("Received message '{id}' with label '{label}' from queue.", message.MessageId, message.Label);


        var data = JsonSerializer.Deserialize<BotCarLicensingOrderRpaRequest>(message.Body);

        data.AdditionalData.TryGetValue("ServiceUrl", out var serviceUrl);
        data.AdditionalData.TryGetValue("ChannelId", out var channelId);
        data.AdditionalData.TryGetValue("BotId", out var botId);
        data.AdditionalData.TryGetValue("UserId", out var userId);
        data.AdditionalData.TryGetValue("ReplyToId", out var replyToId);
        var conversationReference = _offTurnConversationService.CreateSyntheticConversationReference(
            channelId?.ToString(),
            data.ConversationId,
            serviceUrl?.ToString());
        conversationReference.User = new ChannelAccount()
        {
            Id = userId?.ToString(),
            Role = "user"
        };
        conversationReference.Bot = new ChannelAccount
        {
            Id = botId?.ToString(),
            Role = "bot"
        };
        var activity = (Activity)Activity.CreateEventActivity();
        activity.Text = "success";
        activity.ChannelId = channelId?.ToString();
        activity.ServiceUrl = serviceUrl?.ToString();
        activity.RelatesTo = conversationReference;
        activity.Conversation = new ConversationAccount
        {
            Id = data.ConversationId
        };

        activity.ReplyToId = replyToId?.ToString();

        activity.ApplyConversationReference(conversationReference, true);

        // Complete the message so that it is not received again.
        // This can be done only if the subscriptionClient is created in ReceiveMode.PeekLock mode (which is the default).
        await _messageReceiver.CompleteAsync(message.SystemProperties.LockToken);
        // This "works" but is complicated, as we have to set up a whole HTTP call
        await _offTurnConversationService.SendActivityToBotAsync(activity);
        
        // This just sends the Event to the user, no matter how I set up the conversation 
        // reference regarding From/Recipient
        // And it doesn't help in continuing the conversation
        await _offTurnConversationService.SendToConversationThroughPipelineAsync(
            async (turnContext, cancellationToken) =>
            {
                await turnContext.SendActivityAsync(activity, cancellationToken: cancellationToken);
            },
            conversationReference);
        // Note: Use the cancellationToken passed as necessary to determine if the subscriptionClient has already been closed.
        // If subscriptionClient has already been closed, you can choose to not call CompleteAsync() or AbandonAsync() etc.
        // to avoid unnecessary exceptions.
    }

OffTurnConversationService:

public ConversationReference CreateSyntheticConversationReference(string channelId, string conversationId, string serviceUrl)
    {
        ArgumentGuard.NotNull(channelId, nameof(channelId));
        ArgumentGuard.NotNull(conversationId, nameof(conversationId));
        ArgumentGuard.NotNull(serviceUrl, nameof(serviceUrl));

        if (string.IsNullOrEmpty(_botOptions.CurrentValue.BotId))
        {
            throw new InvalidOperationException("A valid bot id must be configured in your bot options in order to create a synthetic conversation reference.");
        }

        // WARNING: This implementation works for directline and webchat.
        //          Changes could be necessary for other channels.
        var supportedChannels = new List<string>()
        {
            Channels.Directline,
            Channels.Webchat
        };
        if (supportedChannels.Any(c => c.Equals(channelId, StringComparison.OrdinalIgnoreCase)))
        {
            _logger.LogWarning(
                "The synthetic conversation reference created for channel {UsedChannel} might not work properly, " +
                "because it's not supported and tested. Supported channels are {SupportedChannel}.",
                channelId,
                string.Join(",", supportedChannels));
        }

        var conversationReference = new ConversationReference()
        {
            Conversation = new ConversationAccount()
            {
                Id = conversationId
            },
            Bot = new ChannelAccount()
            {
                Id = _botOptions.CurrentValue.BotId,
                Name = _botOptions.CurrentValue.BotId
            },
            ChannelId = channelId,
            ServiceUrl = serviceUrl
        };

        return conversationReference;
    }

public virtual async Task SendActivityToBotAsync(IActivity activity)
    {
        // Create the new request to POST to the client
        var forwardRequest = new HttpRequestMessage()
        {
            RequestUri = new Uri(_botOptions.CurrentValue.ReplyServiceUrl),
            Method = HttpMethod.Post,
        };

        // Change the host for the request to be the forwarding URL.
        forwardRequest.Headers.Host = forwardRequest.RequestUri.Host;

        // If the child bot is not running on local mode (no app-id/password), 
        // we're going send an authentication header.
        OAuthResponse authToken = await GetTokenAsync(_botOptions.CurrentValue.MicrosoftAppId, _botOptions.CurrentValue.MicrosoftAppPassword);
        forwardRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken.AccessToken);

        // Altered activity to JSON content
        var json = JsonConvert.SerializeObject(activity);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        forwardRequest.Content = content;

        using var client = new HttpClient();
        var response = await client.SendAsync(forwardRequest);
        if (!response.IsSuccessStatusCode)
        {
            string message = $"Failed to send activity '{activity.Id}' to client bot. {response.ReasonPhrase}";
            throw new Exception(message);
        }
    }
public virtual async Task SendToConversationThroughPipelineAsync(
        BotCallbackHandler callback,
        ConversationReference conversationReference)
    {
        ArgumentGuard.NotNull(callback, nameof(callback));
        ArgumentGuard.NotNull(conversationReference, nameof(conversationReference));

        // Avoiding 401 "Unauthorized" errors
        TrustServiceUrl(conversationReference.ServiceUrl);

        // Reuse adapter with its pipeline to send responses back to the user (like pro-active messages)
        await ((BotAdapter)_botFrameworkHttpAdapter).ContinueConversationAsync(
            _botOptions.CurrentValue.MicrosoftAppId,
            conversationReference,
            callback,
            default);
    }

0 个答案:

没有答案