How to use ConversationBot from #microosft/teamsfx and EventHandler from botbuiler in the microsoft team app - botframework

I am developing an app using teamsfx toolkit and I have two important use case.
Need to send notifications to particular users via API
I also need to send a welcome message to newly installed users.
I am using ConversationBot from #microosft/temasfx library to set adapter config and send notifications to users using API/notification URL like
const bot = new ConversationBot({
// The bot id and password to create BotFrameworkAdapter.
adapterConfig: {
appId: config.botId,
appPassword: config.botPassword,
}}
// and when the server.post api use this to send notification
for (const target of await Mybot.notification.installations()) {
I also need to extend an EventHandler class to override
this.onMembersAdded(async (context, next) => {()
method to send a welcome message. But I am unable to like these two classes in the same app.
I have
const bot = new ConversationBot
Like I also have
class WelcomeBot extends ActivityHandler
{
this.onMemebersAdded(){//sending welcome message}
So the question is how to link both in the same bot app so that I can send a welcome Message using EventHandler and send Notifications via ConversationBot?

If your app is created via Teams Toolkit, probably you have following code (normally in index.js|ts):
server.post("/api/messages", async (req, res) => {
await bot.requestHandler(req, res);
});
Here bot is a ConversationBot. To integrate your WelcomeBot with ConversationBot, please change the code await bot.requestHandler(req, res); to await bot.adapter.processActivity(req, res, (context) => welcomeBot.run(context));
The full code may look like:
/// init somewhere
const bot = new ConversationBot...
const welcomeBot = new WelcomeBot...
/// The "/api/messages" handler
server.post("/api/messages", async (req, res) => {
await bot.adapter.processActivity(req, res, (context) => welcomeBot.run(context));
});
The details behind are - All bot operations depend on Adapter. The ConversationBot provides a helper method requestHandler to simplify the code. But if you'd like to add your own bot logic to the Adapter, you need to explicitly get Adapter via ConversationBot.adapter, then add your own logic.

Related

Skill bot sending proactive messages to a user

I have developed a bot which sends Proactive messages to user
I also cretaed bots which trigger skills.
I was trying to write something where a skills bot/Dialog would be able to send proactive message received via webhooks to the user and continue with the existing Skill dialog.
I am not able to quite understand that. If anyone could help me there.
I used the sample from here to create a simple Skill bot which saves the ConversationReference of the current Activity and calls a service in OnMessageActivityReceived()
// Save ConversationReference
var conversationReference = activity.GetConversationReference();
_conversationReferences.AddOrUpdate(conversationReference.User.Id, conversationReference, (key, newValue) => conversationReference);
// Calling external service
HttpResponseMessage responsemMsg = await client.PostAsync(RequestURI, stringContent);
if (responsemMsg.IsSuccessStatusCode)
{
var apiResponse = await responsemMsg.Content.ReadAsStringAsync();
//Post the API response to bot again
await turnContext.SendActivityAsync(MessageFactory.Text($"Message Sent {turnContext.Activity.Text}"), cancellationToken);
}
The called service eventually emits an event which calls an action in the NotifyController in my Skills Bot. It tries to grab the ConverstaionReference and send the activity using the TurnContext.
//I am using the Skill Bot Id for _appId parameter
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, async (context, token) =>
{
await context.SendActivityAsync(proMsg.eventName);
await context.SendActivityAsync(proMsg.context.ToString());
}, new System.Threading.CancellationToken());
This SendActivity fails and OnTurnError from the Skill Bot handles the exception.
I am not sure where excatly I am going wrong. I am new to the Bot framework learning.
My issue got resolved by using the ContinueConversation() overload with ClaimsIdentity and setting the right claims for audience, appid etc. It was basically authentication issue.
public virtual Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, ConversationReference reference, string audience, BotCallbackHandler callback, CancellationToken cancellationToken);
This is how I created the ClaimsIdentity:
System.Collections.Generic.List<Claim> lclaim = new System.Collections.Generic.List<Claim>
{
new Claim(AuthenticationConstants.VersionClaim, "2.0"),
new Claim(AuthenticationConstants.AudienceClaim, <SkillBotId>),
new Claim(AuthenticationConstants.AppIdClaim, <SkillBotId>),
new Claim(AuthenticationConstants.AuthorizedParty, <SkillBotId>),
};
ClaimsIdentity ci = new ClaimsIdentity(lclaim);
async Task BotCallBack(ITurnContext turnContext, CancellationToken token)
{
<code to send activity back to parent bot>
}
await ((BotAdapter)this.botFrameworkHttpAdapter).ContinueConversationAsync(
ci,
conversationData.ConversationReference,
<ParentBotId>,
callback: BotCallBack,
default).ConfigureAwait(false);

cannot store context object in database msbotframework nodejs

I have created a bot and installed it to my microsoft teams. and I got conversation update event along with the contextObject.
/ Listen for incoming requests.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
console.log(context);
await bot.run(context);
});
});
I want to store this context object for future reference. I tried storing it in postgress database of column type json. When I retrieve the context object from database and perform some actions like
context.sendActivity(MessageFactory.text('All messages have been sent.'));
it is throwing activity not found error
[onTurnError] unhandled error: Error: Missing activity on context
I want to store the context Object somewhere. or Is there any way that I can get the context object from "activity".
Have a look at how to send proactive notifications to users.
In short; there are helper function to achieve your goal. First you retrieve the conversation reference.
const conversationReference = TurnContext.getConversationReference(context.activity);
Followed by the following snippet to continue a conversation, based on the saved actvity.
await adapter.continueConversation(conversationReference, async turnContext => {
// If you encounter permission-related errors when sending this message, see
// https://aka.ms/BotTrustServiceUrl
await turnContext.sendActivity('proactive hello');
});

Can we customize the bot endpoint given from Bot Framework SDK v4 (.net core)

I am very new to Bot Framework.
Is there any possibility to change the endpoint something like https://[domain].azurewebsites.net/api/messages/{customer_id}.
I have a reusable code that I want to use it with several bots registered in Azure Bot Channel. With the help of {customer_id} I can detect which appId and appPassword can be used to successfully authenticate the bot.
services.AddBot<DialogBot>(options => {
options.CredentialProvider = new SimpleCredentialProvider(appId, appPassword);
// Catches any errors that occur during a conversation turn and logs them to currently
// configured ILogger.
ILogger logger = _loggerFactory.CreateLogger<DialogBot>();
options.OnTurnError = async (context, exception) =>
{
logger.LogError($"Exception caught : {exception}");
await context.SendActivityAsync("Sorry, it looks like something went wrong.");
await conversationState.DeleteAsync(context);
};
});
Any suggestions or help would be highly appreciated.
Thanks in advance.

Calling my .NET Core Teams Bot from Angular

I have created a Teams bot in .NET Core from following the sample found here: https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/57.teams-conversation-bot
This is working and is running locally with ngrok. I have a controller with a route of api/messages:
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter Adapter;
private readonly IBot Bot;
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
Adapter = adapter;
Bot = bot;
}
[HttpPost]
public async Task PostAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await Adapter.ProcessAsync(Request, Response, Bot);
}
}
I now want to call a POST to api/messages from my Angular client using TypeScript to send a proactive message to a specific Teams user.
I did figure out how to set the ConversationParameters in TeamsConversationBot.cs to a specific Teams user by doing the following:
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new[] { new ChannelAccount("[insert unique Teams user guid here]") },
TenantId = turnContext.Activity.Conversation.TenantId,
};
but what I'm struggling with is how to build a JSON request that sends the Teams user guid (and maybe a couple other details) to my api/messages route from TypeScript.
How do I go about doing this? What parameters/body do I need to send? I haven't been able to find samples online that show how to do this.
Update below for added clarification
I am building a web chat app using Angular for our customers. What I'm trying to do is send a proactive message to our internal employees, who are using Microsoft Teams, when a customer performs some action via the chat app (initiates a conversation, sends a message, etc.).
I've built a Teams bot using .NET Core using this sample: https://kutt.it/ZCftjJ. Modifiying that sample, I can hardcode my Teams user ID and the proactive message is showing up successfully in Teams:
var proactiveMessage = MessageFactory.Text($"This is a proactive message.");
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new[] { new ChannelAccount("insert Teams ID here") },
TenantId = turnContext.Activity.Conversation.TenantId,
};
await ((BotFrameworkAdapter)turnContext.Adapter).CreateConversationAsync(teamsChannelId, serviceUrl, credentials, conversationParameters,
async (t1, c1) =>
{
conversationReference = t1.Activity.GetConversationReference();
await ((BotFrameworkAdapter)turnContext.Adapter).ContinueConversationAsync(_appId, conversationReference,
async (t2, c2) =>
{
await t2.SendActivityAsync(proactiveMessage, c2);
},
cancellationToken);
},
cancellationToken);
What I'm struggling with is:
How to configure my Angular app to notify my bot of a new proactive message I want to send.
How to configure the bot to accept some custom parameters (Teams user ID, message).
It sounds like you've got some progress with pro-active messaging already. Is it working 100%? If not, I've covered the topic a few times here on stack overflow - here's an example that might help: Programmatically sending a message to a bot in Microsoft Teams
However, with regards -trigging- the pro-active message, the truth is you can do it from anywhere/in any way. For instance, I have Azure Functions that run on their own schedules, and pro-active send messages as if they're from the bot, even though the code isn't running inside the bot at all. You haven't fully described where the Angular app fits into the picture (like who's using it for what), but as an example in your scenario, you could create another endpoint inside your bot controller, and do the work inside there directly (e.g. add something like below:)
[HttpPost]
public async Task ProActiveMessage([FromQuery]string conversationId)
{
//retrieve conversation details by id from storage (e.g. database)
//send pro-active message
//respond with something back to the Angular client
}
hope that helps,
Hilton's answer is still good, but the part about proactively messaging them without prior interaction requires too long of a response. So, responding to your latest comments:
Yes, the bot needs to be installed for whatever team the user resides in that you want to proactively message. It won't have permissions to do so, otherwise.
You don't need to override OnMembersAddedAsync; just query the roster (see below).
You don't need a conversation ID to do this. I'd make your API, instead, accept their Teams ID. You can get this by querying the Teams Roster, which you'll need to do in advance and store in a hash table or something...maybe a database if your team size is sufficiently large.
As far as required information, you need enough to build the ConversationParameters:
var conversationParameters = new ConversationParameters
{
IsGroup = false,
Bot = turnContext.Activity.Recipient,
Members = new ChannelAccount[] { teamMember },
TenantId = turnContext.Activity.Conversation.TenantId,
};
...which you then use to CreateConversationAsync:
await ((BotFrameworkAdapter)turnContext.Adapter).CreateConversationAsync(
teamsChannelId,
serviceUrl,
credentials,
conversationParameters,
async (t1, c1) =>
{
conversationReference = t1.Activity.GetConversationReference();
await ((BotFrameworkAdapter)turnContext.Adapter).ContinueConversationAsync(
_appId,
conversationReference,
async (t2, c2) =>
{
await t2.SendActivityAsync(proactiveMessage, c2);
},
cancellationToken);
},
cancellationToken);
Yes, you can modify that sample. It returns a Bad Request because only a particular schema is allowed on /api/messages. You'll need to add your own endpoint. Here's an example of NotifyController, which one of our other samples uses. You can see that it accepts GET requests. You'd just need to modify that our build your own that accepts POST requests.
All of this being said, all of this seems like it may be a bigger task than you're ready for. Nothing wrong with that; that's how we learn. Instead of jumping straight into this, I'd start with:
Get the Proactive Sample working and dig through the code until you really understand how the API part works.
Get the Teams Sample working, then try to make it message individual users.
Then build your bot that messages users without prior interaction.
If you run into trouble feel free to browse my answers. I've answered similar questions to this, a lot. Be aware, however, that we've switched from the Teams Middleware that I mention in some of my answers to something more integrated into the SDK. Our Teams Samples (samples 50-60) show how to do just about everything.

How to implement a welcome activity when the bot first starts - NLP is from Google Dialogflow

How to implement a welcome activity when the bot first starts - NLP is from Google Dialogflow.
I have designed the chatbot -intent, entities and NLP from Google Dialogflow and I have integrated successfully with the botframework webchat in a html file on referring this url.
The bot design and also the bot response is good to go. My most expected is am not getting the Bot response first here.
The welcome intent from Google Dialogflow has to get trigger from the following code as per the link given above.
But I am unable to get the bot trigger first here.
How to trigger the event of Google Dialogflow from the code.
I am expecting same like this
Note: Also referred this url
When a user joins WebChat, a conversation update activity will be sent to the bot. Once the activity is received, you can check if a member was added and send the welcome message accordingly.
If you are using the Activity Handler that was released in v4.3, you can simply add an onMembersAdded handler and send the welcome message from there.
class Bot extends ActivityHandler{
constructor() {
super();
this.onMembersAdded(async (context, next) => {
const { membersAdded } = context.activity;
for (let member of membersAdded) {
if (member.id !== context.activity.recipient.id) {
await context.sendActivity("Welcome Message!");
}
}
await next();
});
...
}
}
If you are not using the activity handler, in the bot's onTurn method, you can check if the incoming activity handler is a conversation update and if a member has been added.
async onTurn(turnContext) {
if (turnContext.activity.type === ActivityTypes.ConversationUpdate) {
if (turnContext.activity.membersAdded && turnContext.activity.membersAdded.length > 0) {
for (let member of turnContext.activity.membersAdded) {
if (member.id !== turnContext.activity.recipient.id) {
await turnContext.sendActivity("Welcome Message!");
}
}
}
} ...
}
For more details on sending welcome messages, please take a look at this sample.
Hope this helps!

Resources