How to detected dead conversation references when using proactive conversations - botframework

We are using ContinueConversationAsync to send a Proactive Message to existing conversations which is working, what we want it a way to detect if that conversation is still active for instance if the conversation channel is webchat that session may no longer exists or a teams channel and the user has now left the organisation. Otherwise our ConversationReference table will just grow indefinably. At the moment SetProactiveMessage still just continues with no error even if there is no longer a user on the other end.
var conversationReference = botProactiveMessageConversation.ConversationReferenceJson.FromJson<ConversationReference>();
conversationReference.ActivityId = null;
MicrosoftAppCredentials.TrustServiceUrl(conversationReference.ServiceUrl);
await defaultAdapter.ContinueConversationAsync(botProactiveMessageConversation.BotAppId, conversationReference, async (ITurnContext turnContext, CancellationToken cancellationToken) =>
{
turnContext.SetProactiveMessage(botProactiveMessageConversation.ProactiveMessageData);
await dialogBot.OnTurnAsync(turnContext, cancellationToken);
}, default);

Unfortunately, there is no concept of a dead conversation in Direct Line. Subsequently, there is no method you can rely upon that is built in. A conversation's Conversation.Id is stored by the service for 14 days (subject to change, so don't rely on this as a rule) at which point it is purged. When storing your conversation reference, you can append a lastAccessed date and, when your time threshold is reached, it is purged from your store.
As for determining if a member is still part of a team or in an org, you will need to rely on a separate service call. Your best bet would be to use Microsoft's Graph API to check statuses.
Hope of help!

Related

Parse server unsubscribe all users from a channel

I have a Parse server where user can do a one-time subscription to a few events.
Some Installations will be subscribed to a few channels like: ["Event1", "Event2"].
Some installations will subscribed to one channel like ["Event1"]
When a specific event happens, users subscribed to that event should be notified once only. They have to subscribe again to get notification when that Event1 happens.
How should I bulk update all installations, so that, let's say when "Event1" happened, I can remove "Event1" from thousands of installations?
I am worried that if I do something like these:
var query = new Parse.Query(Parse.Installation);
query.equalTo('channels', channel);
query.limit(1000);
const installations = await query.find({useMasterKey:true});
...for loop to update these installations individually
I will easily hit a the find limit, and that means if I have 20k installations with Event1 I can't really clean them up easily in one shot.
Is there any other simple method to unsubscribe all users from one channel?

Unable to initiate a new conversation with botframework and Microsoft teams

I am trying to follow the steps here: https://github.com/getazureready/teamsdev/blob/main/Lab%204%20-%20Conversational%20Bots.md (Exercise 3). I am running this as instructed:
await adapter.continueConversation(conversationReference, async turnContext => {
await turnContext.sendActivity(message);
});
However, this is not starting a new conversation in the channel, it replies to the same conversation initiated by the user. How do we start a new conversation in the channel?
conversationReference has an id property inside there, which has the actual main chat id, and can also have a reference to a specific message on the end, e.g.:
[long string] = conversation itself, vs
[long string];messageid=[short string]
if you use option 2, it will reply to an existing thread, but without that it will start a new thread.
So, in your context, modify the conversationReference's ID and remove the ;messageid=[short string] part

onTurn error for Teams bot on certain Teams system messages

I have a bot (based on the old core-bot-sample) that is deployed to Microsoft Teams. Sometimes, when there is a notification event in Teams such as New Channel Added or Channel Deleted (or possible User Added/Removed), I am getting the following onTurn Error: "error":"Cannot read property 'length' of undefined". By looking at the code, it seems the Welcome Message code is to blame. The only length property is on context.activity.membersAdded, so that must be causing the issue. But I don't understand exactly what is happening. Based on the statement below, the event must be triggering a ConversationUpdate activity, but without the membersAdded property. Can anyone shed some light on what this activity is that Teams is triggering, and what I should add to this welcome message statement to prevent the error message from occuring? To clarify, the error message is coming in the Posts channel of the Team/Channel where the event such as channel removal message is coming.
Code section where I think the error is occurring:
} else if (context.activity.type === ActivityTypes.ConversationUpdate) {
// Handle ConversationUpdate activity type, which is used to indicates new members add to
// the conversation.
// see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
// Do we have any new members added to the conversation?
if (context.activity.membersAdded.length !== 0) {
// Iterate over all new members added to the conversation
for (var idx in context.activity.membersAdded) {
// Greet anyone that was not the target (recipient) of this message
// the 'bot' is the recipient for events from the channel,
// context.activity.membersAdded == context.activity.recipient.Id indicates the
// bot was added to the conversation.
if (context.activity.membersAdded[idx].id === context.activity.recipient.id) {
// Welcome user.
await context.sendActivity('Hi! I\'m the IT Innovation Bot. I can answer questions about the innovation team and capture your innovation ideas. Let me know how I can help!')
}
}
}
}
It looks like that would fail for any activity where the type was ConversationUpdate, but the JSON payload doesn't contain the membersAdded object. The list of those events can be found here:
https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/subscribe-to-conversation-events?tabs=json
You could test this by firing one of the non-membersAdded events (for example, add a new channel to the team, or remove a member). You could probably fix this by doing a null check on the membersAdded object.

Responding to Conversations async: Graph or Bot?

I have a Teams Message extension that returns a Task response which is a medium sized embedded web view iFrame
This is working successfully; including added a custom Tab within the channel and other nice magic calls to Microsoft Graph.
What I am confused about is how to do (and this is probably my not understanding the naming of things)
insert "something" Back into the Message/Post stream which is a link to newly created Tab ... like the what you get when you have a "configureTabs" style Tab created -- there is a friendly Message (Post) in the chat pointing to this new Tab.
do I do this with Microsoft Graph or back through the Bot?
the code that does the communication may be a different service elsewhere that is acting async ... so it needs to communicate with something somewhere with context. Confused if this is the Bot with some params or Microsoft Graph with params.
how to insert an image (rather than a link to the tab) into the Message/Post stream -- but showing the image not a link off to some random URL (ie: )
could not find any samples that do this; again, will be async as per above; but the format of the message will be a Card or something custom?
So just to be clear, a Task Response is NOT the same as a Tab, albeit that they might end up hosted in the same backend web application (and also albeit that your TAB can actual bring up your Task Response popup/iframe using the Teams javascript library).
Aside from that, in order to post something back to the channel, like when the Tab is created, there are two ways to do so:
First is to use Graph Api's Create ChatMessage option (this link is just for a channel though - not sure if your tab/task apply to group chats and/or 1-1 chats as well).
2nd Option is to have a Bot be part of your application as well. Then, when you're ready to send something to the channel, you'd effectively be sending something called a "pro-active messaging". You need to have certain reference data to do this, which you would get when the bot is installed into the channel ("conversation reference", "ServiceUrl", and so on). I describe this more in my answer at Programmatically sending a message to a bot in Microsoft Teams
With regards sending the image, either of the above would work here too, in terms of how to send the image. As to the sending of an image, you'd need to make use of one of the kinds of "Cards" (basically "richer" messages than just raw text). You can learn more about this at Introducing cards and about the types of cards for Teams at Card reference. There are a few that can be used to send an image, depending on perhaps what else you want the card to do. For instance, an Adaptive Card can send an image, some text, and an action button of some sort.
Hope that helps
To close the loop for future readers.
I used the following Microsoft Graph API docs, and the posting above, and this is working: Create chatMessage in a channel and Creating a Custom Microsoft Graph call from the SDK
The custom graph call (as it is not implemented in the .NET SDK at the time of this response) looks something like:
var convoReq = $"https://graph.microsoft.com/beta/teams/{groupId}/channels/{channelId}/messages";
var body = this.TeamsMessageFactory(newCreatedTabUrl, anotherstring).ToJson();
var postMessage = new HttpRequestMessage(HttpMethod.Post, convoReq)
{
Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json")
};
await _graphClient.CurrentGraphClient.AuthenticationProvider.AuthenticateRequestAsync(postMessage);
var response = await _graphClient.CurrentGraphClient.HttpProvider.SendAsync(postMessage);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
return true;
}
The groupId and channelId are found elsewhere; and the TeamsMessageFactory is just some boilerplate that serialized the C# object graph for the POST request, as detailed in Create chatMessage in a channel

Pubnub chat application with storage

I'm looking to develop a chat application with Pubnub where I want to make sure all the chat messages that are send is been stored in the database and also want to send messages in chat.
I found out that I can use the Parse with pubnub to provide storage options, But I'm not sure how to setup those two in a way where the messages and images send in the chat are been stored in the database.
Anyone have done this before with pubnub and parse? Are there any other easy options available to use with pubnub instead of using parse?
Sutha,
What you are seeking is not a trivial solution unless you are talking about a limited number of end users. So I wouldn't say there are no "easy" solutions, but there are solutions.
The reason is your server would need to listen (subscribe) to every chat channel that is active and store the messages being sent into your database. Imagine your app scaling to 1 million users (doesn't even need to get that big, but that number should help you realize how this can get tricky to scale where several server instances are listening to channels in a non-overlapping manner or with overlap but using a server queue implementation and de-duping messages).
That said, yes, there are PubNub customers that have implemented such a solution - Parse not being the key to making this happen, by the way.
You have three basic options for implementing this:
Implement a solution that will allow many instances of your server to subscribe to all of the channels as they become active and store the messages as they come in. There are a lot of details to making this happen so if you are not up to this then this is not likely where you want to go.
There is a way to monitor all channels that become active or inactive with PubNub Presence webhooks (enable Presence on your keys). You would use this to keep a list of all channels that your server would use to pull history (enable Storage & Playback on your keys) from in an on-demand (not completely realtime) fashion.
For every channel that goes active or inactive, your server will receive these events via the REST call (and endpoint that you implement on your server - your Parse server in this case):
channel active: record "start chat" timetoken in your Parse db
channel inactive: record "end chat" timetoken in your Parse db
the inactive event is the kickoff for a process that uses start/end timetokens that you recorded for that channel to get history from for channel from PubNub: pubnub.history({channel: channelName, start:startTT, end:endTT})
you will need to iterate on this history call until you receive < 100 messages (100 is the max number of messages you can retrieve at a time)
as you retrieve these messages you will save them to your Parse db
New Presence Webhooks have been added:
We now have webhooks for all presence events: join, leave, timeout, state-change.
Finally, you could just save each message to Parse db on success of every pubnub.publish call. I am not a Parse expert and barely know all of its capabilities but I believe they have some sort or store local then sync to cloud db option (like StackMob when that was a product), but even if not, you will save msg to Parse cloud db directly.
The code would look something like this (not complete, likely errors, figure it out or ask PubNub support for details) in your JavaScript client (on the browser).
var pubnub = PUBNUB({
publish_key : your_pub_key,
subscribe_key : your_sub_key
});
var msg = ... // get the message form your UI text box or whatever
pubnub.publish({
// this is some variable you set up when you enter a chat room
channel: chat_channel,
message: msg
callback: function(event){
// DISCLAIMER: code pulled from [Parse example][4]
// but there are some object creation details
// left out here and msg object is not
// fully fleshed out in this sample code
var ChatMessage = Parse.Object.extend("ChatMessage");
var chatMsg = new ChatMessage();
chatMsg.set("message", msg);
chatMsg.set("user", uuid);
chatMsg.set("channel", chat_channel);
chatMsg.set("timetoken", event[2]);
// this ChatMessage object can be
// whatever you want it to be
chatMsg.save();
}
error: function (error) {
// Handle error here, like retry until success, for example
console.log(JSON.stringify(error));
}
});
You might even just store the entire set of publishes (on both ends of the conversation) based on time interval, number of publishes or size of total data but be careful because either user could exit the chat and the browser without notice and you will fail to save. So the per publish save is probably best practice if a bit noisy.
I hope you find one of these techniques as a means to get started in the right direction. There are details left out so I expect you will have follow up questions.
Just some other links that might be helpful:
http://blog.parse.com/learn/building-a-killer-webrtc-video-chat-app-using-pubnub-parse/
http://www.pubnub.com/blog/realtime-collaboration-sync-parse-api-pubnub/
https://www.pubnub.com/knowledge-base/discussion/293/how-do-i-publish-a-message-from-parse
And we have a PubNub Parse SDK, too. :)

Resources