Typing indicator for bot framework in C# - botframework

I have a bot in Bot framework. Once user responds to the bot and bot is processing it, I want to show the typing indicator to the user in the meanwhile.
It is possible in Nodejs here - https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-send-typing-indicator
But how can I implement it in C#?

You have to send an activity of type Typing.
You can do it like that:
// Send "typing" information
Activity reply = activity.CreateReply();
reply.Type = ActivityTypes.Typing;
reply.Text = null;
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
await connector.Conversations.ReplyToActivityAsync(reply);

You can also create the message using context.MakeMessage if you need to instantly respond in MessageReceivedAsync of a dialog.
var typingMsg = context.MakeMessage();
typingMsg.Type = ActivityTypes.Typing;
typingMsg.Text = null;
await context.PostAsync(typingMsg);
The typing indicator is replaced by the next message sent (at least in the Bot Framework emulator). This doesn't seem to work in Slack.

Here's another approach to send a typing indicator with a bot framework in C#, JavaScript, or Python.

Try use this:
var reply = activity.CreateReply();
reply.Text = null;
reply.Type = ActivityTypes.Typing;
await context.PostAsync(reply);

For showing typing indicator while bot is processing just add in OnTurnAsync function before await base.OnTurnAsync(turnContext, cancellationToken); :
await turnContext.SendActivityAsync(new Activity { Type = ActivityTypes.Typing }, cancellationToken);

If V4, following lines of code can help. Keep this code at such a place so that it gets executed everytime during the dialogue flow.
IMessageActivity reply1 = innerDc.Context.Activity.CreateReply();
reply1.Type = ActivityTypes.Typing;
reply1.Text = null;
await innerDc.Context.SendActivityAsync( reply1, cancellationToken: cancellationToken);
await Task.Delay(1000);

Related

How do I return a Typing activity from my v4 Bot

I'm trying to add a Typing activity to a long-running action in my bot, but I keep getting a "BadGateway" error. Most of the samples I've found seem to be for bot framework v3, so the types or methods don't appear any more, and I've tried a few options for v4 (using C#), like the following:
await turnContext.SendActivityAsync(new Activity() { Type = ActivityTypes.Typing });
or
var typingActivity = new Activity()
{
Type = ActivityTypes.Typing
//RelatesTo = turnContext.Activity
};
typingActivity.ApplyConversationReference(typingActivity.GetConversationReference());
or
var act2 = MessageFactory.Text(null);
act2.Type = ActivityTypes.Typing;
await turnContext.SendActivityAsync(act2);
all of these result in a BadGateway error.
Can someone guide me on where I'm going wrong?
Your implementation is close, but needs a couple minor adjustments. Also, the text property is optional. If it's not needed, then you can simply remove it (same for the delay). This is what I use which adheres to the documentation (variable is used to match your code). You can reference the docs here.
var typingActivity = new Activity[] {
new Activity { Type = ActivityTypes.Typing },
new Activity { Type = "delay", Value= 3000 },
//MessageFactory.Text("Some message", "Some message"),
};
await turnContext.SendActivitiesAsync(typingActivity, cancellationToken);
Hope of help!
The answer of Steven Kanberg has the right code, but unfortunately this is a service issue at the moment, as confirmed in this issue on Github.
When the issue is resolved, it should be posted in the Github issue above.
Please try out this code to send a typing activity from your bot:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var typingActivity = MessageFactory.Text(string.Empty);
typingActivity.Type = ActivityTypes.Typing;
await turnContext.SendActivityAsync(typingActivity);
}

MS Teams - Don't show notification of specific message in the activity feed

Question
I have a simple Bot for MS Teams developed in C# with the Bot Builder SDK 3.15.0.0 targeting .NET framework 4.7.1.
When mentioned, it retrieves the Jira ticket Ids in the message and returns a single reply with a list of Cards, each one displaying a summary of a Jira Issue.
I'd like to know if it's possible to not populate the activity feed when sending the reply with the card attachments as it's not needed for my use case.
Example
This is how I usually build the reply to a user message
var reply = activity.CreateReply();
reply.AttachmentLayout = AttachmentLayoutTypes.List;
reply.Attachments = thumbnailCards;
await context.PostAsync(reply);
And this is what I tried after reading the docs at https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/activity-feed#rest-api-sample
var reply = activity.CreateReply();
reply.AttachmentLayout = AttachmentLayoutTypes.List;
reply.Attachments = thumbnailCards;
reply.ChannelData = JsonConvert.SerializeObject(new
{
notification = new
{
alert = false
}
});
await context.PostAsync(reply);
I was hoping that setting the ChannelData with notification.alert = false would just disable the notifications, but it actually doesn't display any message.
Have you tried using the Teams nuget package: https://www.nuget.org/packages/Microsoft.Bot.Connector.Teams
var reply = activity.CreateReply();
reply.ChannelData = JObject.FromObject(new TeamsChannelData()
{
Notification = new NotificationInfo(false)
});
Source for this package can be found here: https://github.com/OfficeDev/BotBuilder-MicrosoftTeams/
The alert you are getting in the activity feed is simply the "someone replied to your message" alert and is nothing special coming from the bot. This notification in the activity feed cannot be disabled as of now. Other team members won't receive this alert in activity feed unless they are following the same channel.
Sending notification using Rest API is designed to work for 1:1 chat.

Using ResumeAfter in proactive bot dialog

I have a couple of questions about the example that shows how to start a proactive dialog:
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(token);
var task = scope.Resolve<IDialogTask>();
var interruption = dialog.Void<T, IMessageActivity>();
task.Call(interruption, null);
await task.PollAsync(token);
await botData.FlushAsync(token);
}
What is the point of calling dialog.Void?
How can I use a ResumeAfter? If i add a ResumeAfter handler and await the result i get an error indicating it was expecting Call and got Poll
Is this code supposed to block until the dialog is complete? Because it does not
How can I push a proactive dialog and await its result?

How do I email from BotFramework from Intent

I am looking for an example of how to send an email from a botframework intent.
I have tried the code below but nothing gets sent. I am doing something wrong?
[LuisIntent("TestEmailIntent")]
public async Task FindFundFactSheetAsync(IDialogContext context, LuisResult result)
{
var emailMessage = context.MakeMessage();
emailMessage.Recipient.Id = "myEmail#hotmail.com";
emailMessage.Recipient.Name = "John Cleophas";
emailMessage.Text ="Test message"
var data = new EmailContentData();
var channelData = Newtonsoft.Json.JsonConvert.SerializeObject(data);
emailMessage.ChannelData = channelData;
await context.PostAsync(emailMessage);
context.Wait(MessageReceived);
}
Unless your bot is employing the email channel, you will need to send the email message using your own code, not via the BotFramework. Any posts will go back to the original channel (i.e. Skype, Facebook, etc.)

Sending message from bot to a Skype User using Botframework Version 3

Updated
I am developing a Skype bot with 1:1 conversation with Bot Framework.
In that I have a WebHook method which will call from an external service and sends message to my bot, then my bot will send that message to a skype user.
The following code is for v1 in message controller along with api/messages post method
public async Task<Message> Post([FromBody]Message message){}
[Route("~/api/messages/hook")]
[HttpPost]
public async Task<IHttpActionResult> WebHook([FromBody]WebHookMessage message)
{
if (message.Type == "EmotionUpdate")
{
const string fromBotAddress = "<Skype Bot ID here>";
const string toBotAddress = "<Destination Skype name here>";
var text = resolveEmoji(message.Data);
using (var client = new ConnectorClient())
{
var outMessage = new Message
{
To = new ChannelAccount("skype", address: toBotAddress , isBot: false),
From = new ChannelAccount("skype", address: $"8:{fromBotAddress}", isBot: true),
Text = text,
Language = "en",
};
await client.Messages.SendMessageAsync(outMessage);
}
}
return Ok();
}
I will call above WebHook from another service, so that my bot will send messages to the respective skype user.
Can anyone please help me how can I achieve the same in V3 bot framework?
I tried the following but not working
const string fromBotAddress = "Microsoft App ID of my bot";
const string toBotAddress = "skype username";
WebHookMessage processedData = JsonConvert.DeserializeObject<WebHookMessage>(message);
var text = resolveEmoji(processedData.Data);
using (var client = new ConnectorClient(new Uri("https://botname.azurewebsites.net/")
, "Bot Microsoft App Id", "Bot Microsoft App secret",null))
{
var outMessage = new Activity
{
ReplyToId = toBotAddress,
From = new ChannelAccount("skype", $"8:{fromBotAddress}"),
Text = text
};
await client.Conversations.SendToConversationAsync(outMessage);
}
But it is not working, finally what I want to achieve is I want my bot send a message to a user any time how we will send message to a person in skype.
The following code works, but there are some things that are not that obvious that I figured out (tested on Skype channel)
When a user interacts with the bot the user is allocated an id that can only be used from a specific bot..for example: I have multiple bots each using a skype channel. When I send a message from my skype user to bot A the id is different than for bot B. In the previous version of the bot framework I could just send a message to my real skype user id, but not anymore. In a way it simplifies the whole process because you only need the recipient's id and the framework takes care of the rest, so you don't have to specify a sender or bot Id (I guessed all that is linked behind the scenes)
[Route("OutboundMessages/Skype")]
[HttpPost]
public async Task<HttpResponseMessage> SendSkypeMessage(SkypePayload payload)
{
using (var client = new ConnectorClient(new Uri("https://skype.botframework.com")))
{
var conversation = await client.Conversations.CreateDirectConversationAsync(new ChannelAccount(), new ChannelAccount(payload.ToSkypeId));
IMessageActivity message = Activity.CreateMessageActivity();
message.From = new ChannelAccount();
message.Recipient = new ChannelAccount(payload.ToSkypeId);
message.Conversation = new ConversationAccount { Id= conversation.Id };
message.Text = payload.MessageBody;
await client.Conversations.SendToConversationAsync((Activity)message);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
I'm not sure I understand what you're trying to do. If you'd like to answer a message (activity), try something like this:
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var reply = activity.createReply(text, "en");
await connector.Conversations.ReplyToActivityAsync(reply);
Activity.createReply switches the From and Recipient fields from the incoming activity. You can also try setting these field manually.
UPDATE
You need to create a ConnectorClient to the Skype Connector Service, not to your bot! So try with the Uri http://skype.botframework.com it might work.
However, I don't think you can message a user on Skype without receiving a message from it in the first place (i.e. your bot needs to be added to the user's contacts). Once you have an incoming message from the user, you can use it the create replies, just as described above.
WebHookMessage processedData = JsonConvert.DeserializeObject<WebHookMessage>(message);
var text = resolveEmoji(processedData.Data);
var client = new ConnectorClient(new Uri(activity.serviceUrl));
var outMessage = activity.createReply(text);
await client.Conversations.SendToConversationAsync(outMessage);
activity is a message received from the given user earlier. In this case, activity.serviceUrl should be http://skype.botframework.com, but generally you should not rely on this.
You can try to create the activity (outMessage) manually; for that, I'd recommend inspecting the From and Recipient fields of a message coming from a Skype user and setting these fields accordingly. However, as mentioned before, your bot needs to be added to the user's contacts, so at this point it will have received a message from the user.

Resources