I am trying to implement a 2-way chat using UCWA(Lync web app) , I am able to send the data to a client , but how do i receive it back from that client ? to implement a proper 2-way chat ?
I have attached the code to send some ping to a specific user , how do i receive data back from the user to implement the chat ?
private void StarConversation(string startConversationLink, string hostName, string accessToken, string tokenType)
{
var myObject = new JObject();
myObject.Add("operationId", Guid.NewGuid().ToString().Replace("-", ""));
myObject.Add("to", "sip:xyz#abcd.com");
myObject.Add("subject", "TestConv");
List<string> outgoing = new List<string>();
List<string> incoming = new List<string>();
List<string> timeout = new List<string>();
JObject href = new JObject();
string sending = "Hey there ! how are you doing ?";
href.Add("href", "data:text/plain;base64," + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sending)));
JObject message = new JObject();
message.Add("message", href);
myObject.Add("_links", message);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", tokenType + " " + accessToken);
client.PostAsync(new Uri(hostName + startConversationLink), new StringContent(JsonConvert.SerializeObject(myObject), Encoding.UTF8, "application/json")).Result;
if (response.IsSuccessStatusCode)
{
Uri temp = new Uri(hostName + m_eventsLink);
HttpResponseMessage response2 = Get(client, temp.ToString());
string nextLink = string.Empty;
string timeo ;
string ss = string.Empty;
int i =0;
if (response2.IsSuccessStatusCode)
{
string retData2 = response2.Content.ReadAsStringAsync().Result;
var o = JObject.Parse(retData2);
nextLink = o["_links"]["next"]["href"].Value<string>();
response = client.PostAsync(new Uri(hostName + startConversationLink), new StringContent(JsonConvert.SerializeObject(myObject), Encoding.UTF8, "application/json")).Result;
temp = new Uri(hostName + m_eventsLink);
response2 = Get(client, temp.ToString());
retData2 = response2.Content.ReadAsStringAsync().Result;
o = JObject.Parse(retData2);
}
}
}
What you are doing at this point is starting a Conversation with the user and providing an initial message to go with the messagingInvitation. What I do not know from this code slice is:
Are you listening to Events? (This is really important as this is how you will know that the 2nd party accepted/declined the messagingInvitation among other things like who is typing and messages). I would suggest looking into GettingStarted-Events and setting up a mechanism to request events.
Are you making frequent (~3-5min) POST request on reportMyActivity to ensure the application is not recycled. Check out Resources-reportMyActivity if not.
If the code is listening to events and reporting activity. The next step will be listening for the messagingInvitation and check conversation/messaging events. Conversation will transition from Connecting (state) (messageInvitation is being processed) to Connected (messagingInvitation was accepted) or Disconnected (messagingInvitation was declined, conversation ended, other errors). Assuming the other user accepts the next event to check is messaging state for Connected (this means the messaging modality is active in the conversation and useful methods like sendMessage, addParticipant, and setIsTyping will be available) or Disconnected (this means messaging modality is not active and addMessaging can be used to enable this modality.
If you can reach this point message events will appear when any participant sends a message to the conversation. The direction property can be used to determine when the current UCWA user has sent a message (outgoing) and for all other users there should be a participant property that can identify the user.
Related
we are currently developing some automation with the botframework.
At some point in the conversation, we sent some data through a service bus for processing and wait for a response and then want to continue with the conversation. We already implemented the part where we wait for an response entry in the service bus subscription and then we want to send an Activity from type Event to the bot.
We did the same steps with the proactive message as described in other posts.
We are able to recreate the botclient and conversation reference and all, but in the end when we send the activity, we always send it to the user and not to the bot. But this doesn't trigger the "EventActivityPrompt".
The only way where we achieved the desired outcome was when we made a post to api/messages, but this is too complicated for our taste, and we are looking for an easier way over the botClient (or similar technology)
Has anyone some good ideas? :)
ServiceBusReceiver Message Processing:
private static async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
// Process the message.
Console.WriteLine($"Received message: SequenceNumber:{message.SystemProperties.SequenceNumber} Body:{Encoding.UTF8.GetString(message.Body)}");
_logger?.LogInformation("Received message '{id}' with label '{label}' from queue.", message.MessageId, message.Label);
var data = JsonSerializer.Deserialize<BotCarLicensingOrderRpaRequest>(message.Body);
data.AdditionalData.TryGetValue("ServiceUrl", out var serviceUrl);
data.AdditionalData.TryGetValue("ChannelId", out var channelId);
data.AdditionalData.TryGetValue("BotId", out var botId);
data.AdditionalData.TryGetValue("UserId", out var userId);
data.AdditionalData.TryGetValue("ReplyToId", out var replyToId);
var conversationReference = _offTurnConversationService.CreateSyntheticConversationReference(
channelId?.ToString(),
data.ConversationId,
serviceUrl?.ToString());
conversationReference.User = new ChannelAccount()
{
Id = userId?.ToString(),
Role = "user"
};
conversationReference.Bot = new ChannelAccount
{
Id = botId?.ToString(),
Role = "bot"
};
var activity = (Activity)Activity.CreateEventActivity();
activity.Text = "success";
activity.ChannelId = channelId?.ToString();
activity.ServiceUrl = serviceUrl?.ToString();
activity.RelatesTo = conversationReference;
activity.Conversation = new ConversationAccount
{
Id = data.ConversationId
};
activity.ReplyToId = replyToId?.ToString();
activity.ApplyConversationReference(conversationReference, true);
// Complete the message so that it is not received again.
// This can be done only if the subscriptionClient is created in ReceiveMode.PeekLock mode (which is the default).
await _messageReceiver.CompleteAsync(message.SystemProperties.LockToken);
// This "works" but is complicated, as we have to set up a whole HTTP call
await _offTurnConversationService.SendActivityToBotAsync(activity);
// This just sends the Event to the user, no matter how I set up the conversation
// reference regarding From/Recipient
// And it doesn't help in continuing the conversation
await _offTurnConversationService.SendToConversationThroughPipelineAsync(
async (turnContext, cancellationToken) =>
{
await turnContext.SendActivityAsync(activity, cancellationToken: cancellationToken);
},
conversationReference);
// Note: Use the cancellationToken passed as necessary to determine if the subscriptionClient has already been closed.
// If subscriptionClient has already been closed, you can choose to not call CompleteAsync() or AbandonAsync() etc.
// to avoid unnecessary exceptions.
}
OffTurnConversationService:
public ConversationReference CreateSyntheticConversationReference(string channelId, string conversationId, string serviceUrl)
{
ArgumentGuard.NotNull(channelId, nameof(channelId));
ArgumentGuard.NotNull(conversationId, nameof(conversationId));
ArgumentGuard.NotNull(serviceUrl, nameof(serviceUrl));
if (string.IsNullOrEmpty(_botOptions.CurrentValue.BotId))
{
throw new InvalidOperationException("A valid bot id must be configured in your bot options in order to create a synthetic conversation reference.");
}
// WARNING: This implementation works for directline and webchat.
// Changes could be necessary for other channels.
var supportedChannels = new List<string>()
{
Channels.Directline,
Channels.Webchat
};
if (supportedChannels.Any(c => c.Equals(channelId, StringComparison.OrdinalIgnoreCase)))
{
_logger.LogWarning(
"The synthetic conversation reference created for channel {UsedChannel} might not work properly, " +
"because it's not supported and tested. Supported channels are {SupportedChannel}.",
channelId,
string.Join(",", supportedChannels));
}
var conversationReference = new ConversationReference()
{
Conversation = new ConversationAccount()
{
Id = conversationId
},
Bot = new ChannelAccount()
{
Id = _botOptions.CurrentValue.BotId,
Name = _botOptions.CurrentValue.BotId
},
ChannelId = channelId,
ServiceUrl = serviceUrl
};
return conversationReference;
}
public virtual async Task SendActivityToBotAsync(IActivity activity)
{
// Create the new request to POST to the client
var forwardRequest = new HttpRequestMessage()
{
RequestUri = new Uri(_botOptions.CurrentValue.ReplyServiceUrl),
Method = HttpMethod.Post,
};
// Change the host for the request to be the forwarding URL.
forwardRequest.Headers.Host = forwardRequest.RequestUri.Host;
// If the child bot is not running on local mode (no app-id/password),
// we're going send an authentication header.
OAuthResponse authToken = await GetTokenAsync(_botOptions.CurrentValue.MicrosoftAppId, _botOptions.CurrentValue.MicrosoftAppPassword);
forwardRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken.AccessToken);
// Altered activity to JSON content
var json = JsonConvert.SerializeObject(activity);
var content = new StringContent(json, Encoding.UTF8, "application/json");
forwardRequest.Content = content;
using var client = new HttpClient();
var response = await client.SendAsync(forwardRequest);
if (!response.IsSuccessStatusCode)
{
string message = $"Failed to send activity '{activity.Id}' to client bot. {response.ReasonPhrase}";
throw new Exception(message);
}
}
public virtual async Task SendToConversationThroughPipelineAsync(
BotCallbackHandler callback,
ConversationReference conversationReference)
{
ArgumentGuard.NotNull(callback, nameof(callback));
ArgumentGuard.NotNull(conversationReference, nameof(conversationReference));
// Avoiding 401 "Unauthorized" errors
TrustServiceUrl(conversationReference.ServiceUrl);
// Reuse adapter with its pipeline to send responses back to the user (like pro-active messages)
await ((BotAdapter)_botFrameworkHttpAdapter).ContinueConversationAsync(
_botOptions.CurrentValue.MicrosoftAppId,
conversationReference,
callback,
default);
}
I use CallResource.Create();
But it makes a call without ring back tone and disconnected and I need pure C# code to make outbound call form browser due to browser compatibility of twilio.js and I'm confusing regarding https URI what to specify there because create already make calls on "To" number.
public ActionResult MakeCall(string number)
{
string AccountSid = "********";
string AuthToken = "*********";
TwilioClient.Init(AccountSid, AuthToken);
var to = new PhoneNumber(number);
var from = new PhoneNumber("********");
ServicePointManager.SecurityProtocol =(SecurityProtocolType)3072;
if (number!= null) {
var call = CallResource.Create(to: to, from: from, m
achineDetection: "Enable",timeout:600, url: new Uri("https://handler.twilio.com/"));
ViewBag.Message = call.Sid;
}
return View("Index");
}
Hi I am new in push notification. I want to implement push notification on my website using service worker and fcm. I have follow this url 'https://developers.google.com/web/fundamentals/codelabs/push-notifications/' and create my client application its show notification popup when I allow notification its create entpoint , p256dh and auth key. I have also created a service using entpoint , p256dh , auth and private, public key and send notification . this notification is receipt by client.
c# code for send notification are given below.
var pushEndpoint = #"https://fcm.googleapis.com/fcm/send/fOetqkkxzlM:APA91bHASHHThdRC4dDCnZJxAy6ZngUHhxsT9mjF5xuBDpM5kzy-dq2jcbQtsiDcXZ9WcOTE4GWzUZkvGbt3xBH0y61mqbkRVkjvBfvgnUqAGf1hsIrUXpsrl9nfqmrlXx2UFWaTJerC";
var p256dh = #"BHmoaUoqJ6rUitaXj-YvXbuORO_x6lFigO-NV1ceDygb4GhywOw7WIKWOtWqMsVswl8pUCVJrPiNtyVQFirD1oI";
var auth = #"-aFkO3hZKP6u0BZwKRNDiw";
var subject = #"mailto:xyz#gmail.com";
var publicKey = #"BNizeduk-kgre_pz7aj0_t89t-5nvQttzyL17rQfsc7GlOFIfn0qVKyLF81ahCGdsMdhWz-5n_PQavZHywBt9Rs";
var privateKey = #"W21b4Jh9qSoGI7GpKHAq6ikMBjTStiBvQP_wWVvVRqE";
var subscription = new PushSubscription(pushEndpoint, p256dh, auth);
var vapidDetails = new VapidDetails(subject, publicKey, privateKey);
//var gcmAPIKey = #"[your key here]";
string payload = #"{""user"":{""name"":""mohan"",""teamname"":""b"",""email"":""c"",""players"":[""1"",""2""]}}";
var webPushClient = new WebPushClient();
try
{
webPushClient.SendNotification(subscription, payload, vapidDetails);
//webPushClient.SendNotification(subscription, "payload", gcmAPIKey);
}
catch (WebPushException exception)
{
Console.WriteLine("Http STATUS code" + exception.StatusCode);
}
my problem is for every client i have to pass I have to pass entpoint , p256dh , auth key.Can I automate it . like can i get this keys (entpoint , p256dh , auth ) for all the users who has subscribed users and send message at one time.
please help
I am using DirectLine API to send message to the bot, I need the service URL of the Published Bot to perform a post request for the load test as mentioned in the steps here https://blog.botframework.com/2017/06/19/Load-Testing-A-Bot/
This is the code, can anyone point where I am going wrong
private static async Task<Chat> TalkToTheBot(string Message)
{
Chat objChat = null;
// Connect to the DirectLine service
try
{
DirectLineClient client = new DirectLineClient(directLineSecret);
Conversation conversation = await client.Conversations.StartConversationAsync();
string watermark = null;
Activity reply = new Activity
{
From = new ChannelAccount("User1", "User Name"),
Text = "Hello",
Type = ActivityTypes.Message,
};
//await client.Conversations.PostActivityAsync(conversation.ConversationId, reply.CreateReply(text: Message, locale: "en-US"), CancellationToken.None);
await client.Conversations.PostActivityAsync(conversation.ConversationId,reply , CancellationToken.None);
// Get the response as a Chat object
objChat = await ReadBotMessagesAsync(client, conversation.ConversationId, watermark);
}
catch (Exception e)
{
throw;
}
// Return the response as a Chat object
return objChat;
}
private static async Task<Chat> ReadBotMessagesAsync(DirectLineClient client, string conversationId, string watermark)
{
// Create an Instance of the Chat object
Chat objChat = new Chat();
// We want to keep waiting until a message is received
bool messageReceived = false;
while (!messageReceived)
{
// Get any messages related to the conversation since the last watermark
ActivitySet messages = await client.Conversations.GetActivitiesAsync(conversationId, watermark, CancellationToken.None);
// Set the watermark to the message received
watermark = messages?.Watermark;
// Get all the messages
var messagesFromBotText = from message in messages.Activities
where message.From.Id == botId
select message;
// Loop through each message
foreach (var message in messagesFromBotText)
{
// We have Text
if (message.Text != null)
{
// Set the text response
// to the message text
objChat.ChatResponse
+= " "
+ message.Text.Replace("\n\n", "<br />");
}
}
// Mark messageReceived so we can break
// out of the loop
messageReceived = true;
}
// Set watermark on the Chat object that will be
// returned
objChat.watermark = watermark;
// Return a response as a Chat object
return objChat;
}
Per the article,
The serviceUrl property here is critical to note, and needs to be set
to the endpoint of your message sink/client.
and:
In order to test your bot, you’ll need to create a custom UI/message
sink to send and receive messages to your bot. This message sink will
effectively act like a channel and accept HTTP POST messages with
JSON-serialized bot framework activities.
Which basically means that you will have to build a "message client" and the url of that client is the one that you will have to provide in the serviceUrl of your request.
So to explain my problem, I have to give you the context.
I got a Bot built with microsoft bot framework deployed on slack. Now it can happen these "events" on my backend that the bot communicates with. When a event occurs, I want to notify my bot of it and then let it send a message to all of it's conversations that something has happend. So basicly:
Backend>Microserivce>Bot>users
To do this I have to store all conversations in my backend, which I do in a database there. When a event happends, the backend will post an activity to the bot with all the conversations(basicly their id's) and the event it should show them.
So in essence my backend need to post a message to my bot.
For doing this I found the microsoft directline api which acts as a middleman here, a more abstract way to talk with the bot. The problem is that I don't know how to do it. I followed microsofts own tutorial but it doesn't seem to work for me:
This is the endpoint that my backend uses to notify the bot. "content" contains conversations and events as a json formated string.
[HttpPost]
[Route("conversationsEvents")]
public HttpResponseMessage PostConversationsEvents([FromBody]string content)
{
NotifyBot.Notify(content);
return Request.CreateResponse(HttpStatusCode.NoContent );
}
NotifyBot.Notify(content) looks like this:
private static async Task StartBotConversation( string contents)
{
string directLineSecret = "secret";
string fromUser = "microserviceNotifyEndpoint";
Activity activity = new Activity
{
From = new ChannelAccount(fromUser),
Text = contents,
Type = ActivityTypes.Event
};
DirectLineClient client = new DirectLineClient(directLineSecret);
var conversation = await client.Conversations.StartConversationAsync();
await client.Conversations.PostActivityAsync(conversation.ConversationId, activity);
}
Basicly the execution get's stuck at var conversation = await client.Conversations.StartConversationAsync(); , it just waits forever.
I tried changing it to var conversation = await client.Conversations.StartConversationAsync().ConfigureAwait(continueOnCapturedContext: false);´the execution goes on but the activity doesn't seem to get posted.
I'm not sure why the call to .StartConversationAsync() would freeze in your case. Maybe you haven't enabled the Direct Line channel on dev.botframework.com/bots? Nonetheless, as pointed out by Sergey, the Direct Line is a Channel and not a means for communicating with your bot on other channels.
Check out the Connector Client: bot-builder-dotnet-connector
Here is a static example of using it to proactively send a message to a user from a bot: MicrosoftDX/botFramework-proactiveMessages - sample: ConversationStarter.cs
pertinent code from sample:
public static async Task Resume(string conversationId,string channelId)
{
var userAccount = new ChannelAccount(toId,toName);
var botAccount = new ChannelAccount(fromId, fromName);
var connector = new ConnectorClient(new Uri(serviceUrl));
IMessageActivity message = Activity.CreateMessageActivity();
if (!string.IsNullOrEmpty(conversationId) && !string.IsNullOrEmpty(channelId))
{
message.ChannelId = channelId;
}
else
{
conversationId = (await connector.Conversations.CreateDirectConversationAsync( botAccount, userAccount)).Id;
}
message.From = botAccount;
message.Recipient = userAccount;
message.Conversation = new ConversationAccount(id: conversationId);
message.Text = "Hello, this is a notification";
message.Locale = "en-Us";
await connector.Conversations.SendToConversationAsync((Activity)message);
}
The serviceUrl, the channelId, conversationId, toId, fromId, etc are cached from previous communication by the user to the bot (these are statically stored in this example, so only work for one user). This example shows how it is possible to proactively send a message to a user from a bot. The Direct Line api is not required.
You don't need to use DirectLine, it is designed for creating alternative bot UIs.
To implementing what your want, you may try the following:
First, you need to store users addresses to whom you want to send the messages. It my be done by storing the ResumptionCookie of a user last message in your backend database.
var state = new ResumptionCookie(message).GZipSerialize();
When your PostConversationsEvents is called, you may resume the conversation at the latest point with each users.
var resumptionCookie = ResumptionCookie.GZipDeserialize(state);
var message = resumptionCookie.GetMessage();
message.Text = content;
await Conversation.ResumeAsync(resumptionCookie, message);
It is not the only solution. As I said, in this case you just resumed the conversation with the user at the latest point. Another solution is to save the user address (user the same ResumptionCookie class) but start the conversation when you need to:
var resumptionCookie = ResumptionCookie.GZipDeserialize(state);
var message = cookie.GetMessage();
ConnectorClient client = new ConnectorClient(new Uri(message.ServiceUrl));
var conversation = await
client.Conversations.CreateDirectConversationAsync(message.Recipient, message.From);
message.Conversation.Id = conversation.Id;
var newMessage = message.CreateReply();
newMessage.Text = content;
await client.Conversations.SendToConversationAsync(newMessage);
See more details on BotFramework documentation.