|  | 
|  | 1 | +When you think about the exchange of messages between your bot and the user, you're probably thinking about the scenario where the user sends a message to your bot and your bot then replies to the user with a message of its own. We call this _reactive messaging_ and it's by far the most common flow that you should optimize your bot's code for. | 
|  | 2 | + | 
|  | 3 | +It is possible, however, for your bot to initiate a conversation with the user by sending them a message first. We call this _proactive messaging_ and while the code you'll write to send a proactive message is very similar to what you'd write in the reactive case, there are a few differences that are worth exploring. | 
|  | 4 | + | 
|  | 5 | +The first thing to note is that before you can send a proactive message to a user, the user will have to send at least one reactive style message to your bot. There are two reasons for this. | 
|  | 6 | + | 
|  | 7 | +1. You need to get the user's `ConversationReference` and save it somewhere for future use. You can think of the conversation reference as the user's address, as it contains information about the channel they came in on, their user ID, the conversation ID, and even the server that should receive any future messages. This object is simple JSON and should be saved whole without tampering. | 
|  | 8 | + | 
|  | 9 | +1. Most channels by policy won't let a bot initiate conversations with users they've never spoken to before. Depending on the channel the user might need to explicitly add the bot to a conversation or at a minimum send an initial message to the bot. | 
|  | 10 | + | 
|  | 11 | +> ![NOTE] | 
|  | 12 | +> This bot currently runs properly only when deployed to Azure. However, you can test the bot without publishing it. | 
|  | 13 | +
 | 
|  | 14 | +## Set up your development environment | 
|  | 15 | + | 
|  | 16 | +The features used in this article aren't available in the Bot Builder SDK v4.0.0-alpha2018031301 NuGet packages. | 
|  | 17 | + | 
|  | 18 | +1. Clone the repo and build the packages locally (see [Building the SDK](Building-the-SDK)). | 
|  | 19 | + | 
|  | 20 | +1. Create an empty ASP.NET Core Web Application project. | 
|  | 21 | + | 
|  | 22 | +1. Add references to the following packages: | 
|  | 23 | +    ```text | 
|  | 24 | +    Microsoft.Bot.Builder | 
|  | 25 | +    Microsoft.Bot.Builder.Core | 
|  | 26 | +    Microsoft.Bot.Builder.Core.Extensions | 
|  | 27 | +    Microsoft.Bot.Builder.Integration.AspNet.Core | 
|  | 28 | +    Microsoft.Bot.Connector | 
|  | 29 | +    Microsoft.Bot.Schema | 
|  | 30 | +    ``` | 
|  | 31 | +
 | 
|  | 32 | +1. Add a `default.html` file under the `wwwroot` folder. | 
|  | 33 | +
 | 
|  | 34 | +1. In the `Program.cs` file, change the namespace to `Microsoft.Bot.Samples`. | 
|  | 35 | +
 | 
|  | 36 | +## Define your bot | 
|  | 37 | +
 | 
|  | 38 | +The following bot definition uses the conversation reference to send a proactive message to the user. | 
|  | 39 | +- When the user enters `subscribe`, the bot immediately confirms the request, and then sends a proactive message after a 2 second delay. | 
|  | 40 | +- When the users types anything else, the bot prompts the user to type `subscribe`. | 
|  | 41 | +
 | 
|  | 42 | +Create a `ProactiveBot.cs` class file and update the file to this: | 
|  | 43 | +
 | 
|  | 44 | +```csharp | 
|  | 45 | +using Microsoft.Bot.Builder; | 
|  | 46 | +using Microsoft.Bot.Builder.Adapters; | 
|  | 47 | +using Microsoft.Bot.Schema; | 
|  | 48 | +using System.Threading.Tasks; | 
|  | 49 | +
 | 
|  | 50 | +namespace Microsoft.Bot.Samples | 
|  | 51 | +{ | 
|  | 52 | +    public class ProactiveBot : IBot | 
|  | 53 | +    { | 
|  | 54 | +        public async Task OnReceiveActivity(ITurnContext context) | 
|  | 55 | +        { | 
|  | 56 | +            if (context.Request.Type is ActivityTypes.Message) | 
|  | 57 | +            { | 
|  | 58 | +                var messageText = context.Request?.AsMessageActivity()?.Text; | 
|  | 59 | +                if (messageText?.Trim().ToLowerInvariant() is "subscrie") | 
|  | 60 | +                { | 
|  | 61 | +                    // Confirm the request. | 
|  | 62 | +                    await context.SendActivity("Thank You! We will message you shortly."); | 
|  | 63 | +
 | 
|  | 64 | +                    // Send the proactive message. | 
|  | 65 | +                    var reference = TurnContext.GetConversationReference(context.Request); | 
|  | 66 | +                    SubscribeUser((BotFrameworkAdapter)context.Adapter, | 
|  | 67 | +                        ((BotFrameworkTurnContext)context).BotAppId, reference); | 
|  | 68 | +                } | 
|  | 69 | +                else | 
|  | 70 | +                { | 
|  | 71 | +                    // Prompt the user to type "subscribe". | 
|  | 72 | +                    await context.SendActivity("Type `subscribe` to receive a proactive message."); | 
|  | 73 | +                } | 
|  | 74 | +            } | 
|  | 75 | +        } | 
|  | 76 | +
 | 
|  | 77 | +        private async void SubscribeUser(BotFrameworkAdapter adapter, string appId, ConversationReference reference) | 
|  | 78 | +        { | 
|  | 79 | +            await adapter.ContinueConversation(appId, reference, async context => | 
|  | 80 | +            { | 
|  | 81 | +                await Task.Delay(2000); | 
|  | 82 | +                await context.SendActivity("You've been notified!"); | 
|  | 83 | +            }); | 
|  | 84 | +        } | 
|  | 85 | +    } | 
|  | 86 | +} | 
|  | 87 | +``` | 
|  | 88 | + | 
|  | 89 | +The bot does not store the conversation reference for later, but you can save this data in your actual code. The bot calls the `SubscribeUser` method and notifies the user that they're now subscribed, and you're then free to do pretty much anything you would normally do in your bot's `OnReceiveActivity` handler. | 
|  | 90 | + | 
|  | 91 | +The `SubscribeUser` method passes the conversation reference, along with the bot's application ID, to the adapter's `ContinueConversation` method. The adapter creates a `context` object and passes it to your callback method, which uses the context to send the message. | 
|  | 92 | + | 
|  | 93 | +> ![NOTE] | 
|  | 94 | +> Within the callback, no activity was received so `context.Request` is null. | 
|  | 95 | +
 | 
|  | 96 | +## Update your Startup.cs file | 
|  | 97 | + | 
|  | 98 | +We still need to add the bot and use the Bot Framework in the `Startus.cs` file. Replace its contents: | 
|  | 99 | + | 
|  | 100 | +```csharp | 
|  | 101 | +using Microsoft.AspNetCore.Builder; | 
|  | 102 | +using Microsoft.AspNetCore.Hosting; | 
|  | 103 | +using Microsoft.Bot.Builder.BotFramework; | 
|  | 104 | +using Microsoft.Bot.Builder.Core.Extensions; | 
|  | 105 | +using Microsoft.Bot.Builder.Integration.AspNet.Core; | 
|  | 106 | +using Microsoft.Extensions.Configuration; | 
|  | 107 | +using Microsoft.Extensions.DependencyInjection; | 
|  | 108 | +using System; | 
|  | 109 | +using System.Diagnostics; | 
|  | 110 | +using System.Threading.Tasks; | 
|  | 111 | + | 
|  | 112 | +namespace Microsoft.Bot.Samples | 
|  | 113 | +{ | 
|  | 114 | +    public class Startup | 
|  | 115 | +    { | 
|  | 116 | +        public IConfiguration Configuration { get; } | 
|  | 117 | + | 
|  | 118 | +        public Startup(IHostingEnvironment env) | 
|  | 119 | +        { | 
|  | 120 | +            var builder = new ConfigurationBuilder() | 
|  | 121 | +                .SetBasePath(env.ContentRootPath) | 
|  | 122 | +                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) | 
|  | 123 | +                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) | 
|  | 124 | +                .AddEnvironmentVariables(); | 
|  | 125 | +            Configuration = builder.Build(); | 
|  | 126 | +        } | 
|  | 127 | + | 
|  | 128 | +        // This method gets called by the runtime. Use this method to add services to the container. | 
|  | 129 | +        public void ConfigureServices(IServiceCollection services) | 
|  | 130 | +        { | 
|  | 131 | +            services.AddSingleton(_ => Configuration); | 
|  | 132 | +            services.AddBot<ProactiveBot>(options => | 
|  | 133 | +            { | 
|  | 134 | +                options.CredentialProvider = new ConfigurationCredentialProvider(Configuration); | 
|  | 135 | +                options.EnableProactiveMessages = true; | 
|  | 136 | +            }); | 
|  | 137 | +        } | 
|  | 138 | + | 
|  | 139 | +        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | 
|  | 140 | +        public void Configure(IApplicationBuilder app, IHostingEnvironment env) | 
|  | 141 | +        { | 
|  | 142 | +            if (env.IsDevelopment()) | 
|  | 143 | +            { | 
|  | 144 | +                app.UseDeveloperExceptionPage(); | 
|  | 145 | +            } | 
|  | 146 | + | 
|  | 147 | +            app.UseDefaultFiles(); | 
|  | 148 | +            app.UseStaticFiles(); | 
|  | 149 | +            app.UseBotFramework(); | 
|  | 150 | +        } | 
|  | 151 | +    } | 
|  | 152 | +} | 
|  | 153 | +``` | 
|  | 154 | + | 
|  | 155 | +## Test your bot | 
|  | 156 | + | 
|  | 157 | +To test your bot, deploy it to Azure as a registration only bot, and test it in Web Chat. | 
0 commit comments