Send proactive message for Skype using Direct Line API - c#

I am using Direct Line API v4 to send a message to my Web Chat proactively using an existing conversation (by passing the existing conversation ID, saved when the conversation started).
Code:
var client = new DirectLineClient("secreat");
var activity = new Activity();
activity.From = new ChannelAccount("userid");
activity.Type = "resume";
activity.ChannelId = "directline";
activity.Text = "Hi";
activity.Conversation = new ConversationAccount(id: "existingconverstaionid");
var result = client.Conversations.PostActivity("existingconverstaionid", activity);
This code runs fine and I am able to continue with my existing conversation with my Web Chat channel. I would like this same functionality to work for my Skype channel, so I have replaced userid and existingconversationid I received from the Skype conversation, but this does not work...

You can't use the DirectLineClient to broadcast a message to the Skype channel. It is actually quite easy, since you can just use the BotFramework SDK to send a proactive message, as long as you saved the conversation info.
Read more about sending proactive messages using BotFramework v4 or have a look at the example.

Related

Cannot send email from SendGrid Account api key

i have created secret key from azure and use in code it's working perfectly fine but when i use secret key create from my gmail account i do not receive any email.
I am using the same code for both send grid api keys one from azure sendgrid account another from my direct sendgrid account but from azure sendgrid account emails are delivering without any error and in sendgrid activities tab also there is recent record but form my direct sendgrid account emails are not delivering and there is not recent record in activity tab.
static async Task Execute()
{
dynamic client = new SendGridClient("SG.Ge6udT3CQtKCuGIxhGwqkg.***************-B--zeJXJKnlomAzB2XzQ");
var msg = new SendGridMessage();
//noreply#teamx.ae
msg.SetFrom(new EmailAddress("360loyalofficial#gmail.com", "Teamx | Services"));
var recipients = new List<EmailAddress>
{
new EmailAddress("nauman.nasir138#gmail.com")
};
msg.AddTos(recipients);
msg.SetSubject("Test");
msg.AddContent(MimeType.Text, "test");
msg.AddContent(MimeType.Html, "test");
var response = await client.SendEmailAsync(msg);
}
Your messages won't be delivered because you don't own or control the gmail.com domain.
More details can be found here:
https://sendgrid.com/blog/dont-send-email-from-domains-you-dont-control/
but essentially it's a security feature to stop scammers and your message won't be delivered.

How do I send a notification to a user in Teams via the Bot Framework?

I have a Bot created with v4 of the Microsoft Bot Framework. I can successfully use this bot in the "Test in Web Chat" portion in the Azure Portal. I can also successfully use this bot in an app that I've created in Microsoft Teams. I now want to send a notification from the "Test in Web Chat" piece to a specific user in Teams. For example, in the "Test in Web Chat" piece, I'd like to enter
Hello someuser#mytenant.com
When this is sent via the "Test in Web Chat" piece, I'd like to show "Hello" in Microsoft Teams to only someuser#mytenant.com. I have successfully tokenized the string from the "Test in Web Chat". Thus, I know what I want to send, and who I want to send it to. However, I do not know how to actually send it.
Currently, I have the following in my bot:
public class EchoBot : ActivityHandler
{
private ConcurrentDictionary<string, ConversationReference> _conversationReferences;
public EchoBot(ConcurrentDictionary<string, ConversationReference> conversationReferences)
{
_conversationReferences = conversationReferencs;
}
private void AddConversationReference(Activity activity)
{
var reference = activity.GetConversationReference();
_conversationReferences.AddOrUpdate(reference.User.Id, reference, (key, newValue) => reference);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> context, CancellationToken cancellationToken)
{
AddConversationReference(context.Activity as Activity);
var parameters = GetParameters(); // Parses context.Activity.Text;
// Send a message to the target (i.e. someuser#mytenant.com)
var connection = new Microsoft.Bot.Connector.ConnectorClient(new Uri(context.Activity.ServiceUrl));
var tenant = context.Activity.GetChannelData<TeamsChannelData>().Tenant;
// how do I send the message to parameters.Target?
// Confirm message was sent to the sender
var confirmation = $"Message was sent to {parameters.Target}.";
await context.SendActivityAsync(MessageFactory.Text(confirmation));
}
}
I've reviewed how to send proactive notifications to users. However, I've been unsuccessful in a) getting the user specified in parameters.Target and b) sending a notification to that user. What am I missing?
First, you'll need to map user#email.com to their Teams userId (maybe with a static dictionary), which is in the format of:
29:1I9Is_Sx0O-Iy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzXXXXXXXXMij8BVMdBcL9L_RwWNJyAHFQb0TXXXXXX
You can get the Teams UserId by either:
Querying the roster, or
Having the user message the bot, and setting a breakpoint on an incoming message, looking at the Activity.ChannelData for the Teams userId, or
Dynamically build a static dictionary of all incoming messages that stores the user's email mapped to their Teams userId (I believe both are found in Activity.ChannelData).
Note: #1 and #2 both require a user to message the bot, first, which sort of defeats the purpose of proactive messages
After you have the appropriate Teams IDs, you just send a proactive message to a Teams user. The end of this link also mentions trustServiceUrl, which you may find handy if you run into permissions/auth issues when trying to send a proactive message.

Change sender on reply using EWS Managed API

I'm currently trying to configure Mail2Bug to create Bugs in Azure DevOps when new emails arrive in a shared mailbox. Everything was going well until the part where it needs to reply to the incoming message.
The code which handles this function can be found in EWSIncomingMessage.cs:
public void Reply(string replyHtml, bool replyAll)
{
//_message is of type EmailMessage
var reply = _message.CreateReply(replyAll);
reply.BodyPrefix = new MessageBody(BodyType.HTML, replyHtml);
reply.Send();
}
Instead of replying using the shared mailbox's email, it uses the one from the authenticated user. I'm assuming this has to do with how CreateReply populates the reply MailMessage in combination with EWS.
Are there any ways around this (possibly by creating a new MailMessage and simulate a reply)?
You could refer to the below code:
var message = (EmailMessage) Item.Bind(service, new ItemId(uniqueId), PropertySet.FirstClassProperties);
var reply = message.CreateReply(false);
reply.BodyPrefix = "Response text goes here";
var replyMessage = reply.Save(WellKnownFolderName.Drafts);
replyMessage.Attachments.AddFileAttachment("d:\\inbox\\test.pdf");
replyMessage.Update(ConflictResolutionMode.AlwaysOverwrite);
replyMessage.SendAndSaveCopy();
For more information, please refer to these links:
Replying with attachments to an email message with EWS
How to reply to an email using the EWS Managed API?
Respond to email messages by using EWS in Exchange

Resume Bot Framework dialog when triggered by external service

The Scenario
I have a bot built using the Bot Framework with a series of dialogs. One of these dialogs gives the user the option of inputting some complex data via a web page by presenting a button to them. Clicking the button they are then taken to the site, fill out the data, save and are then directed back to the bot.
I want my bot to pause the dialog until it receives an event from my web page telling me the user has saved the data and then continue asking the user questions.
Before
I had a version implemented whereby I would store a ConversationReference before the user clicked the button and then when the external event happened I would send the cards and next messages I wanted to show (not in a dialog) from a webhook, that was fine but it got quite complicated/messy - I'd rather keep the whole app in one continuous dialog.
Idea 1: Use DirectLine API
I did some research and many people were suggesting using the DirectLine API. So I implemented this:
public async Task SendEventAsync(InternalEventMessage message, ConversationReference reference) {
var client = new DirectLineClient(!String.IsNullOrEmpty(_settings.DirectLineSecret) ? _settings.DirectLineSecret : null);
if (_settings.SiteUrl.Contains("localhost")) {
client.BaseUri = new Uri(_settings.DirectLineServiceUrl);
}
var eventMessage = Activity.CreateEventActivity();
//Wrong way round?!?
eventMessage.From = reference.Bot;
eventMessage.Type = ActivityTypes.Event;
eventMessage.Value = message;
var conversation = await client.Conversations.PostActivityAsync(reference.Conversation.Id, eventMessage as Activity);
}
This uses the DirectLine client to send an event message to the serviceUrl using a stored ConversationReference, basically imitating a user (bot and user seem to be the wrong way round in the SDK). Checking for localhost was so that the DirectLine library pointed at the emulator server rather than https://directline.botframework.com.
In my dialog I call:
//method above shows input button and links to web page
context.Wait(WaitForAddressInput);
}
private async Task WaitForAddressInput(IDialogContext context, IAwaitable<IActivity> result) {
var message = await result;
switch (message.Type) {
case ActivityTypes.Message:
//TODO: Add response
break;
case ActivityTypes.Event:
var eventMessage = message as IEventActivity;
if (((JObject)eventMessage.Value).ToObject<InternalEventMessage>().Type == EventType.AddressInputComplete) {
_addressResult = (await _tableService.ReadOrderById(Order.OrderId)).Address;
await context.PostAsync($"Great}");
context.Done(_addressResult);
}
break;
}
}
This waits for any message from the user after the button has been shown and if our event matches then we proceed with the dialog.
This works locally using the emulator but, frustratingly, doesn't live. It fails to recognise channels created via webchat or Messenger. That is explained here: Microsoft Bot Framework DirectLine Can't Access Conversations
For security reasons, you can't use DirectLine to spy on messages from
another conversation.
So I can't access a channel that I haven't created using DirectLine.
Idea 2: BotConnector
So I thought I'd try the BotConnector using similar code:
public async Task SendEventAsync(InternalEventMessage message, Microsoft.Bot.Connector.DirectLine.ConversationReference reference) {
var botAccount = new ChannelAccount(reference.User.Id, reference.User.Name);
var userAccount = new ChannelAccount(reference.Bot.Id, reference.Bot.Name);
MicrosoftAppCredentials.TrustServiceUrl(reference.ServiceUrl);
var connector = new ConnectorClient(new Uri(reference.ServiceUrl), new MicrosoftAppCredentials("xxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxx"));
connector.Credentials.InitializeServiceClient();
var eventMessage = Activity.CreateMessageActivity();
eventMessage.Recipient = botAccount;
eventMessage.From = userAccount;
eventMessage.Type = ActivityTypes.Event;
eventMessage.Conversation = new ConversationAccount(id: reference.Conversation.Id);
eventMessage.ServiceUrl = reference.ServiceUrl;
eventMessage.Timestamp = DateTimeOffset.UtcNow;
eventMessage.LocalTimestamp = DateTime.Now;
eventMessage.ChannelId = reference.ChannelId;
var result = await connector.Conversations.SendToConversationAsync(eventMessage as Microsoft.Bot.Connector.Activity);
}
This doesn't crash and I can see the event appear in the emulator request console but nothing happens, it seems to be ignored!
Idea 3: Try to imitate the bot service calling my bot
I haven't tried this yet because I think it might be the most time consuming but I was reading here about the service authentication and wondered if it would be possible to imitate the hosted bot service sending a message and send my event that way with the required data?
This seems like a fairly common scenario so I'm surprised I haven't come across a way to do this yet. If anyone has any other ideas on how I can send an event message to my bot from an external service then I'd love to hear it.
Update:
See my answer below Eric's to see what I did.
Idea 1:
DirectLine is a channel, not a library to use in order to connect to channels. (For instance: you would not use Facebook Messenger to connect to Skype) DirectLineClient is useful for creating a client application that connects to the DirectLine channel through the Direct Line connector service.
Idea 2:
This method should work. In fact, the BotAuth library uses this method for the MagicNumber login flow within the CallbackController: https://github.com/MicrosoftDX/botauth/blob/9a0a9f1b665f4aa95b6d60d09346dda90d8b314e/CSharp/BotAuth/Controllers/CallbackController.cs
For your scenario, you should be able to construct a CardAction of type ActionTypes.OpenUrl that contains a value with the ConversationReference encoded in the url. Clicking the button will call an mvc controller that displays a page (saving the ConversationReference in a cookie or something) and when the user finishes adding the address on the page, use the ConversationReference to send an event to the bot (similar to how BotAuth resumes the conversation in the CallbackController).
Idea 3:
This would bypass the connector services, and is not a supported scenario. The link you shared explains the details of how authentication works in the Bot Framework, not how to bypass the connector services.
Eric's answer led me to solve the issue using the BotAuth example but, for completeness, here is what I did using Idea 2.
I created a CallbackController on my Bot Framework endpoint and then used the following code to send an event back to the awaiting dialog:
MicrosoftAppCredentials.TrustServiceUrl(reference.ServiceUrl);
var message = reference.GetPostToBotMessage();
message.Value = new InternalEventMessage(type);
message.Type = ActivityTypes.Event;
await Conversation.ResumeAsync(reference, message);
The dialog awaits with this code and continues:
context.Wait(WaitForAddressInput);
}
private async Task WaitForAddressInput(IDialogContext context,
IAwaitable<IActivity> result)
{
var message = await result;
switch (message.Type)
{
case ActivityTypes.Message:
//TODO: Add response
break;
case ActivityTypes.Event:
//Process event and continue!
break;
}
}
This is the most complicated issue I've had with the Bot Framework and I found the docs a little lacking. Hope this helps someone!

Reply to bot from web API

In short, I'm trying to resume a conversation with a bot after a successful web API login. But I'm having trouble sending a reply to the bot. I have the ChannedId as well as the UserId. I'm not sure whether this is enough. I'm also not sure how to construct a new ConversationReference, which from my understanding is required to perform the reply (Conversation.ResumeAsync)
I have looked at the following example: MicrosoftDX/AuthBot, but the example uses ResumptionCookie which is deprecated. Furthermore, the example utilizes Azure AD authentication. I use cookie authentication in my web API
I require a response from my web API to continue the login form flow on the bot.
var conversationReference = ???;
// Create the message that is send to conversation to resume the login flow
var message = conversationReference.GetPostToUserMessage();
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
{
var client = scope.Resolve<IConnectorClient>();
var sc = scope.Resolve<IStateClient>();
await Conversation.ResumeAsync(conversationReference, message);
}

Categories