14

I have a bot built on BotFramework 3.5 and hosted on Azure as a WebApp. I didn't face any problems with implementation of scenarios where the bot needs to respond to user's input. However there is a need to teach him to start conversations by some schedule. To reach the goal I created a WebJob which is a simple console app basically. Here is a code used to initiate a message from bot to user:

            var botAccount = new ChannelAccount(id: from);
            var userAccount = new ChannelAccount(id: to);
            var conversation = new ConversationAccount(false, conversationId);

            var connector = new ConnectorClient(serviceUrl);

            IMessageActivity message = Activity.CreateMessageActivity();
            message.From = botAccount;
            message.Recipient = userAccount;
            message.Conversation = conversation;
            message.Text = text;
            message.Locale = locale;
            await connector.Conversations.SendToConversationAsync((Activity)message);

from, to, serviceUrl, conversationId - are taken from the previous conversation, so I'd expect they are valid. However on SendToConversationAsync exception is thrown:

System.UnauthorizedAccessException: Authorization for Microsoft App ID 3a26a4d4-f75a-4feb-b3e0-37a7fa24e5fc failed with status code Unauthorized and reason phrase 'Unauthorized' ---> System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized)

The app.config file contains the same values as the original bot API, including AppId and AppSecret. I saw a few questions raised on the same topic, but didn't manage to find an answer.

Am I missing something? Is it a valid approach to send messages on behalf of bot from the console app?

Artem
  • 2,084
  • 2
  • 21
  • 29
  • Does your webapp use authentication, authorization ? – Thomas Mar 12 '17 at 20:14
  • it uses 3 auth parameters (AppId, AppPassword and BotId) defined in the web.config. The same values are injected into console app – Artem Mar 13 '17 at 15:52
  • Can you post the code of the webapp where authentication is configured ? How do you authenticate to the webapp ? are you sending a token ? – Thomas Mar 13 '17 at 20:29

2 Answers2

24

According to your description, I followed this tutorial for getting started with the Connector and followed this tutorial for sending and Receiving Activities.

Based on your code, I created my console application and I could reproduce the same issue, then I found a git issue about the similar issue. After some trials, I could make it work as expected on my side, you could refer to it:

MicrosoftAppCredentials.TrustServiceUrl("{ServiceUrl}", DateTime.Now.AddDays(7));
var account=new MicrosoftAppCredentials("MicrosoftAppIdKey", "MicrosoftAppPasswordKey");
var connector = new ConnectorClient(new Uri("{ServiceUrl}"),account);

OR

Implement your DelegatingHandler

public class MyDelegatingHandler : DelegatingHandler
{
    private string _token;
    public MyDelegatingHandler(string token)
    {
        _token = token;
    }

    protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token);
        return base.SendAsync(request, cancellationToken);
    }
}

Then, you need to build your ConnectorClient as follows:

var account=new MicrosoftAppCredentials("{MicrosoftAppIdKey}", "{MicrosoftAppPasswordKey}");
var jwtToken=await account.GetTokenAsync();
var connector = new ConnectorClient(new Uri("{serviceUrl}"),handlers:new MyDelegatingHandler(jwtToken));

Here is my console application code snippet, you could refer to it:

try
{
    var userAccount = new ChannelAccount() { Id = "default-user", Name = "user" };
    var botAccount = new ChannelAccount() { Id = "934493jn5f6f348f", Name = "console-Bot" };
    string url = "{serviceUrl}";

    MicrosoftAppCredentials.TrustServiceUrl(url, DateTime.Now.AddDays(7));
    var account = new MicrosoftAppCredentials("{MicrosoftAppIdKey}", "{MicrosoftAppPasswordKey}");
    var connector = new ConnectorClient(new Uri(url), account);

    IMessageActivity message = Activity.CreateMessageActivity();
    message.From = botAccount;
    message.Recipient = userAccount;
    message.Conversation = new ConversationAccount() { Id = "{conversationId}" };
    message.Text = "Message sent from console application!!!";
    message.Locale = "en-us";
    var response = await connector.Conversations.SendToConversationAsync((Activity)message);
    Console.WriteLine($"response:{response.Id}");
}
catch (Exception e)
{
    Console.WriteLine($"exception:{e.Message}\r\n{e.StackTrace}");
}

Result

enter image description here

Bruce Chen
  • 18,207
  • 2
  • 21
  • 35
  • Your post helped me overcome my authentication problem when sending a proactive message, so thank you for that. However I found it was possible to comment out the two lines of JWT access token code you posted above, restart my test server application and still successfully authenticate while sending a proactive message. – camelCase Mar 22 '17 at 09:22
  • 3
    CANNOT UPVOTE THIS ENOUGH! – chris31389 Mar 22 '17 at 14:21
  • Super!! ..this works fantastic .. but wondering will the same thing work if another bot instead of console app with all the user and channel details tries to message the user?.(with MicrosoftAppIdKey and secret of its own)?. – Mohan Kumar DB Jul 07 '17 at 14:19
  • Excellent answer! – The Memebot Aug 23 '17 at 18:28
  • Will the call to `MicrosoftAppCredentials.TrustServiceUrl` start a new 7 day expiration or carry on with the existing one? Thanks – Richard Bailey Nov 23 '17 at 08:13
0

Try creating a new conversation like this:

var conversationId = await connector.Conversations.CreateDirectConversationAsync(botAccount, userAccount);
martinni39
  • 323
  • 3
  • 10
  • sure I did. It doesn't work. Apparently error is not related with the way conversation is created – Artem Mar 13 '17 at 15:48