Get conversation ID in directline bot framework inline - botframework

I am using directline for webchat.
I want to include a refresh button on top of the chat and for that I need the conversation ID. How can I get the ID? Is it possible by using inline webchat ?
This the refresh button that I am trying to implement

I was facing the same problem, as I wanted to pass the conversation ID to my custom controller for initial authentication and correspondingly, push a custom auth data to the conversation stack of the bot framework relating to that specific conversation ID.
My hunting got me to this issue post on Github:
In the 3rd post by inmarktech he mentions the below code:
var params = BotChat.queryParams(location.search);
var my_token = params['my_token'];
var botConnection = new BotChat.DirectLine({
secret: 'DIRECTLINE_SECRET'
});
BotChat.App({
botConnection: botConnection
,user: { id: 'USER_ID', name: 'User' } // user.id auto updates after first user message
}, document.getElementById("bot"));
botConnection.connectionStatus$.subscribe(function (status) {
if (status == 2) { // wait for connection is 'OnLine' to send data to bot
var convID = botConnection.conversationId;
botConnection.postActivity({
from: { id: convID } // because first time user ID == conversation ID
,type: 'event'
,name: 'registerUserData' // event name as we need
,value: my_token // data attached to event
}).subscribe(function (activityId) {
// This subscription is a MUST
// If I remove this handler the postActivity not reaches the bot
});
}
});
as you can see he is subscribing to botConnection.connectionStatus$ and as and when the status property is equal to 2(Online) you can then fetch the Conversation ID from the botConnection object.
Hope that helps :)

Related

Field Service: correct way to cancel a BookableResourceBooking via SDK/API?

The BookableResourceBooking entity is documented here:
https://learn.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/entities/bookableresourcebooking
I'd like to able to cancel a booking but I can't seem to find any SDK or API docs that explain how to do so. Would changing the bookingstatus value to "canceled" be sufficient to cancel a booking? Where would I input the reason code?
You can do this using SDK as well as API.
When you see Bookable Resource Booking in CRM, you can see Deactivate button. Clicking on it will deactivte the Bookable Resource Booking.
Now there is one more clean way to manage data, you can set Booking status to cancelled and then deactivate record in this way you can capture complete data as why Bookable Resource Booking record is cancelled/deactivated.
var entity = {};
entity["bookingstatus#odata.bind"] = "/bookingstatuses(bbda588b-013a-eb11-a813-000d3a25bbe9)"; /* cancelled booking status ID*/
entity.statecode = 1; /*Inactive*/
entity.statuscode = 2; /*Inactive*/
Xrm.WebApi.online.updateRecord("bookableresourcebooking", "bbda588b-013a-eb11-a813-000d3a25bbe9", entity).then(
function success(result) {
var updatedEntityId = result.id;
},
function(error) {
Xrm.Utility.alertDialog(error.message);
}
);

Microsoft BotFramework-WebChat is getting two welcome messages

I am using code based on https://github.com/Microsoft/BotFramework-WebChat/blob/master/samples/15.d.backchannel-send-welcome-event/index.html
When I load the web page I get two of the welcome messages. Looking at the console output of my bot I can see two conversation updates happening.
This doesn't happen with the Bot framework emulator, which only shows one welcome message.
The only place where my code differs from the sample is in rendering:
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
styleOptions,
userID: guid(),
}, document.getElementById('webchat'));
Why is this hapening? Why is the web channel sending two "join" events for the user?
My code handling conversation updates looks like this:
} else if (turnContext.activity.type === ActivityTypes.ConversationUpdate) {
if (DEBUG) { console.log("ConversationUpdate"); }
// Do we have any new members added to the conversation?
if (turnContext.activity.membersAdded.length !== 0) {
// Iterate over all new members added to the conversation
for (var idx in turnContext.activity.membersAdded) {
console.log(turnContext.activity.membersAdded);
// Greet anyone that was not the target (recipient) of this message
// the 'bot' is the recipient for events from the channel,
// turnContext.activity.membersAdded == turnContext.activity.recipient.Id indicates the
// bot was added to the conversation.
if (turnContext.activity.membersAdded[idx].id != turnContext.activity.recipient.id) {
if (DEBUG) {console.log("Starting MASTER_DIALOG");}
const user = await this.userProfile.get(turnContext, {});
user.id = this.guid();
await this.userProfile.set(turnContext, user);
await this.userState.saveChanges(turnContext);
return await dialogContext.beginDialog(MASTER_DIALOG)
}
}
}
}
Using the ConversationUpdate event for sending a welcome message is not recommended. Read more about how to properly send a greeting message.
There will be two ConversationUpdate events per connection. One for when bot joins the conversation and one for when a (human) user joins the conversation. In your current code you are iterating over all new members, where you have to filter out the bot itself.
A better option would be to make use of a custom event sent using the backchannel. In the example you mention, you already have this functionality. It will sent a new event webchat/join to your bot, which even includes the browser language by default.

Is it possible to detect when a user opens the chat window on Facebook?

I'm trying to create a chatbot where in order to avoid the user opening the chat window and not knowing the available options, I want to give some basic instructions when the user opens the chat window.
Is there any trigger available when the user opens a chat window? Maybe then I can check, and if there's not an ongoing conversation I could provide basic instructions.
I did some googling and found nothing about this. Is it possible to do something like this, and if not, is there a way to mitigate this problem, and provide the user with information regarding the chatbot capabilities and supported instructions?
Facebook does not allow bots to initiate a conversation, unlike Skype or other platforms.
There are still some tricks you can do :
Go on the Settings of your Facebook page, then Messaging and check "Show a Messenger Greeting" as below, and write your greeting sentence.
The result will look like this :
You can also set a "Get Started" button to trigger an event.
Here's the doc :
"https://developers.facebook.com/docs/messenger-platform/thread-settings/get-started-button"
You can monitor for two event types: ConversationUpdate and ContactRelationUpdate.
The first one (ConversationUpdate) is called when a user is added or removed from the conversation. So, there's a place where you can introduce available options. It will be each type the new conversation has started. So, it may become annoying, you may add a check - do not show it if the user has been using the bot for some time.
The second (ContactRelationUpdate) is called when a user adds or removes the bot to/from the contacts. In general, it is only called once per user action.
Here's the extract from the Bot-Frameworks examples:
For Node.Js
bot.on('conversationUpdate', function (message) {
// Check for group conversations
if (message.address.conversation.isGroup) {
// Send a hello message when bot is added
if (message.membersAdded) {
message.membersAdded.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
var reply = new builder.Message()
.address(message.address)
.text("Hello everyone!");
bot.send(reply);
}
});
}
// Send a goodbye message when bot is removed
if (message.membersRemoved) {
message.membersRemoved.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
var reply = new builder.Message()
.address(message.address)
.text("Goodbye");
bot.send(reply);
}
});
}
}
});
bot.on('contactRelationUpdate', function (message) {
if (message.action === 'add') {
var name = message.user ? message.user.name : null;
var reply = new builder.Message()
.address(message.address)
.text("Hello %s... Thanks for adding me. Say 'hello' to see some great demos.", name || 'there');
bot.send(reply);
} else {
// delete their data
}
});
For C#
private void HandleMessage(Activity message)
{
if (message.Type == ActivityTypes.ConversationUpdate)
{
if (activity.MembersAdded.Any(m => m.Id == activity.Recipient.Id))
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var response = activity.CreateReply();
response.Text = "Hi! I am Bot. Here's what you can do...";
await connector.Conversations.ReplyToActivityAsync(response);
}
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
if (Activity.AsContactRelationUpdateActivity().Action == ContactRelationUpdateActionTypes.Add)
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var response = activity.CreateReply();
response.Text = "Hi! I am Bot. Thanks for adding me. Here's what you can do...";
}
}
return null;
}
I think the acid answer is not.
But you can intercept the IConversationUpdateActivity type message to know if the user has added the bot to a conversation. In the C# project template you can find a code block that ask for this message type but do nothing.

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.

GoogleCalendarAPI accept/decline event

I am working on GoogleCalendar API and using node.js as a platform to build my application.
I am able to create events using authentication procedure and creating the calendar event using the access token generated while authenticating.
My question is that suppose if we have any attendee in the event and I want to accept/decline the event using the calendar API from the attendee's side, how can we do that?
I have tried fetching the calendar event of the attendee and matching it with the iCalUID of the event which was originally created and then modifying the event using update event on the attendee's calendar.
Event creator or owner cannot modify the response of attendees. Only attendees can modify their status.
To update the status on the side of the user, You may use the Event.update API and provide value for 'attendees.responseStatus'. Attendee's response status has 4 (four) possible value (described below).
'needsAction' - has not responded to the invitation.
'declined' - has declined the invitation.
'tentative' - has tentatively accepted the invitation
'accepted' - has accepted the invitation.
In addition to this, You can use the word "primary" as value for the calendar id to represent the currently logged in user
CalendarId: Calendar identifier. To retrieve calendar IDs call the calendarList.list method. If you want to access the primary calendar of the currently logged in user, use the "primary" keyword. (string).
For the id, you need to use the "id" returned by the Events.list API not the "iCalUID". Those two are different as described here.
Other fields that you need to provide are the email (of the attendee), startdate and enddate.
For more information, you may view the official documentation, link below:
https://developers.google.com/google-apps/calendar/v3/reference/events
Here is an example in java, using PATCH. Create an event object with the just the information you want to change, in this case the attendee and the response status. This code is running as the attendee.
final Event event = new Event()
.setAttendees(Arrays.asList(new EventAttendee().setEmail(email)
.setResponseStatus("declined")));
try
getCalendarService(googleAccountCredential).events()
.patch(CALENDAR_PRIMARY, calendarEventId, event)
.setSendNotifications(true)
.setOauthToken(googleAccountCredential.getToken()).execute();
return true;
} catch (final Exception ex) {
...
return false;
}
}
Like Android Enthusiast discussed, only the attendee can update his or her calendar from the attendee's side. You should also check the documentation as he suggested. The answer below is a working example for node.js and python
To update the event, you need to have the eventId and the user email. Get the event from the calendar(with the eventID),
loop through all the attendees, change responseStatus for
that particular attendee and then update the google calendar
For node js using the google api
const { google } = require('googleapis');
const calendar = google.calendar({ version: 'v3', auth: 'YOUR-API-KEY-HERE' });
#get the event to be updated
let theEvent = calendar.events.get({ calendarId: 'primary', eventId: eventId })
#loop through the whole attendee
for (let i = 0, i < theEvent['atendees'].length; i++){
if (theEvent['atendees'][i]['email'] === userEmail){
theEvent['atendees'][i]['responseStatus'] = 'accepted'
}
}
#update the google event
calendar.events.update({ calendarId: 'primary', eventId: theEventId, body: theEvent}, function(err, event) {
  if (err) {
    console.log('there was an error');
    return;
  }
  console.log('Event updated');
});
For python using googleapiclient
from googleapiclient.discovery import build
calendar = build('calendar', 'v3', credentials=credential)
event = calendar.events().get(calendarId='primary', eventId='eventId').execute()
For attendee in event['atendees']:
if atendee['email'] == user_email:
attendee['responseStatus'] = 'accepted'
break
#update the google event
udated_event = calendar.events().update(calendarId='primary', eventId=eventId, body=event).execute()
Lets suppose that you already have the event payload with the attendees key, then you need to get the ID for the created event:
created_event = gcal_service.events().insert(
calendarId='primary', body=event_payload
).execute()
then copy the attendees in a new object
accepted_attendees = {}
accepted_attendees['attendees'] = event_payload['attendees'].copy()
and now what you need to do is submit a patch to the attendees calendar based on the event_id, like this:
for attendee in event_payload['attendees']:
attendee_pos = accepted_attendees['attendees'].index(attendee)
accepted_attendees['attendees'].pop(attendee_pos)
accepted_attendees['attendees'].append({
'email': attendee['email'],
'self': True,
'responseStatus': 'accepted',
'additionalGuests': 0,
})
gcal_service.events().patch(
calendarId='primary',
eventId=created_event['id'],
body=accepted_attendees
)
And that's all, all the other attendees, now have accepted the event, hope it helps.
To respond, you need to get the event with the same event id from the attendee's calendar and then perform a patch or an update operation changing the response status of this attendee from needsAction to accepted / declined.
A bit of documentation on how events are copied between attendees and organizers:
https://developers.google.com/google-apps/calendar/concepts/sharing
Here is an example in python for Google Calendar Api v3. You can either use update or patch. Both of them are working.
all_attendees = event['attendees']
event['attendees'] = [{
'email': 'you#example.com',
'self': True,
'responseStatus': 'accepted',
'additionalGuests': 0,
}]
updated_event = service.events().patch(calendarId=calendar_id, eventId=event_id, body=event).execute()
Have fun

Resources