1

I am using botframework v4, but coming over from v3, I have not found any documentation that is similar to the code I use below but for v4, regarding sending proactive messages from Azure Function App

Below is the code I previously used but am having trouble adapting:

var builder = require('botbuilder');

// setup bot credentials
var connector = new builder.ChatConnector({
  appId: process.env.MICROSOFT_APP_ID,
  appPassword: process.env.MICROSOFT_APP_PASSWORD
});

module.exports = function (context, req) {
    if (req.body) {
        var savedAddress = req.body.channelAddress;
        var inMemoryStorage = new builder.MemoryBotStorage();
        var bot = new builder.UniversalBot(connector).set('storage', inMemoryStorage); 
        sendProactiveMessage(savedAddress, bot)
    }
};

function sendProactiveMessage(address, bot) {
    var msg = new builder.Message().address(address);
    msg.textLocale('en-US');
    var img = {
        attachments: [{
            contentType: "image/jpg",
            contentUrl: latestUrl,
        }]
    };
    msg.addAttachment(img.attachments[0]);
    msg.text('hello');
    bot.send(msg);
}

This works fine with v3 but not v4.

If possible I would also like to find a way to log a user out:

await botAdapter.signOutUser(innerDc.context, this.connectionName);

This is how I do it in the bot itself, but doing so from Azure Functions again is proving difficult.

Any help would be appreciated.

JDT
  • 965
  • 2
  • 8
  • 20

1 Answers1

2

Great that you are making the move from v3 to v4! Have you had a look at Send proactive notifications to users? This example is pretty straight forward and can be used within an Azure function.

First you retrieve the Conversation Reference in your bot by calling TurnContext.getConversationReference(context.activity);. This is the reference you could use in your proactive function to open the conversation. In your case you provide that via the request body to a proactive function, so I will do the same in my example.

My proactive endpoint example is written in Typescript, however it works the same way in plain Javascript. Create a HTTP trigger in Azure Functions and use the following code. I have added comments inline for clarity.

const { BotFrameworkAdapter } = require('botbuilder');

// Create adapter.
// If you need to share this adapter in multiple functions, you could
// instantiate it somewhere else and import it in every function.
const adapter = new BotFrameworkAdapter({
    appId: process.env.MicrosoftAppId,
    appPassword: process.env.MicrosoftAppPassword
});

module.exports = async function (context, req) {

    // Validate if request has a body
    if (!req.body) {
        context.res = {
            status: 400,
            body: "Please pass a conversation reference in the request body"
        };
        return;
    }

    // Retrieve conversation reference from POST body
    const conversationReference = req.body;

    // Open the conversation and retrieve a TurnContext
    await adapter.continueConversation(conversationReference, async turnContext => {

         // Send a text activity
         // Depending on the channel, you might need to use  https://aka.ms/BotTrustServiceUrl

         await turnContext.sendActivity('Proactive hello');

    });

    context.res = {
        body: 'Message sent!'
    };

};

In the end you could make a request to this Azure Function, where you pass the Conversation Reference as body of the type application/json.

Extending this example with features like signOutUser is simple. You can call all functions within the continueConversation function, just as in a normal bot. You can even receive the adapter object there if you wish.

await adapter.continueConversation(conversationReference, async turnContext => {
    // Sign user out
    turnContext.adapter.signOutUser(turnContext, 'connection-name');
});
Mick
  • 2,946
  • 14
  • 19
  • Hey thanks for the response, is this going to work in an Azure Function App when it specifies Node? Is it possible to translate it to node please? – JDT Mar 12 '20 at 21:24
  • @JDT, I have changed my examples to plain Javascript which you could use in Azure Functions. Just make sure you have the `botbuilder` dependency installed. – Mick Mar 12 '20 at 23:46
  • Thank you for the translation! I am currently getting `at new RestError (D:\home\site\wwwroot\node_modules\@azure\ms-rest-js\dist\msRest.node.js:1397:28) ` with this code - it fails at the sendActivity stage. – JDT Mar 13 '20 at 13:15
  • Which version of `botbuilder` have you installed? Could you possibly share your full package.json. – Mick Mar 13 '20 at 13:23
  • 1
    The solution above requires `const BotConnector = require("botframework-connector"); const credentials = new BotConnector.MicrosoftAppCredentials( process.env.MicrosoftAppId, process.env.MicrosoftAppPassword ); BotConnector.MicrosoftAppCredentials.trustServiceUrl( conversationReference.serviceUrl );` then it works fine. Thanks! – JDT Mar 13 '20 at 13:49
  • I'm having an issue with the signout feature however. The function seems to work - no errors are thrown. But it doesn't seem to have an effect on the bot itself. Are there additional functions/actions required to log the user out similar to `logoutDialog.js` from the sample? (https://github.com/microsoft/BotBuilder-Samples/blob/master/samples/javascript_nodejs/18.bot-authentication/dialogs/logoutDialog.js) – JDT Mar 13 '20 at 14:17
  • @JDT, what do you want to accomplish? Do you just want to sign out / remove the users tokens from the Bot Service Auth service, or do you also want to clear the dialog stack? – Mick Mar 14 '20 at 13:03
  • Ok, so it seems the issue is with the fact I was putting the connectionName in as a string "this_connection_string" vs process.env.connectionName - it works now. Thanks again. – JDT Mar 14 '20 at 13:44
  • Similar to the original question, I assume attachments/images/videos can be sent like this too? As a separate note, is it possible to also `beginDialog` to choose certain dialogs/waterfalls using the proactive message method? – JDT Mar 14 '20 at 14:06
  • @JDT, you can do everything you would like within the proactive message method. You have access to the `TurnContext` within the method, the same context as in your normal bot. – Mick Mar 14 '20 at 17:12