Close MS Teams StageView programmatically - microsoft-teams

I'm displaying a third party web page (client page) in a StageView, opened from an AdaptiveCard tabInfoAction:
{
type: "invoke",
title: "open in stageView",
value: {
type: "tab/tabInfoAction",
tabInfo: {
contentUrl: "example.com",
websiteUrl: "example.com",
name: "View",
entityId: "entityId",
},
},
}
I'd like to close the stage view based on a user interaction via #microsoft/teams-js. I can do that if displayed in a task module by calling teamsjs.dialog.submit() in the client code. Is there a similar way to do it in this context?

Related

How to handle user leaving conversation

We have welcome examples using OnMembersAddedAsync method but no examples showing how to handle user leaving conversation. I tried to override OnMembersRemovedAsync but it does not seem to be invoked (at least when I use bot framework emulator).
I need to do some cleanup at the event of user leaving/left conversation.
An example or any tips would be appreciated.
Update: I'm using C# and Bot framework v4
This is going to be channel specific as it is dependent on the channel providing a feature that sends an update when the user leaves a conversation. Any other channels, you will need to research.
For Facebook, I was unable to find a scope that covers such an action. These are the available scopes which you can reference more closely here:
messages
message_deliveries
message_echoes
message_reads
messaging_account_linking
messaging_checkout_updates (beta)
messaging_game_plays
messaging_handovers
messaging_optins
messaging_payments(beta)
messaging_policy_enforcement
messaging_postbacks
messaging_pre_checkouts (beta)
messaging_referrals
standby
Web Chat, as a feature, also does not include this. However, given this is a web page, you can utilize the onbeforeunload() window function to dispatch an event. The event listener will make use of Web Chat's store to dispatch either a message or event to the bot. For the sake of clarity, I'm sending different types of data via SEND_MESSAGE and SEND_EVENT.
const store = window.WebChat.createStore( {}, ( { dispatch } ) => next => async action => {
return next( action );
};
window.addEventListener( 'sendEventActivity', ( { data } ) => {
store.dispatch({
type: 'WEB_CHAT/SEND_MESSAGE',
payload: {
text: data
}
} )
,
store.dispatch( {
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'user_event',
value: {
name: 'end_conversation',
value: 'user ended conversation'
},
text: 'The user has left the conversation.'
}
} )
} );
window.onbeforeunload = function() {
const eventSendActivity = new Event( 'sendEventActivity' );
eventSendActivity.data = 'User left conversation';
window.dispatchEvent( eventSendActivity );
}
{ type: 'message',
id: '4uPdpZhlTFfBMziBE7EmEI-f|0000004',
timestamp: 2020-01-10T18:21:26.767Z,
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: { id: 'dl_123', name: 'johndoe', role: 'user' },
conversation: { id: '4uPdpZhlTFfBMziBE7EmEI-f' },
recipient: { id: 'botberg#QaeuoeEamLg', name: 'Dungeon Runner' },
textFormat: 'plain',
locale: 'en-US',
text: 'User left conversation',
channelData:
{ clientActivityID: '15786804807910gegwkp2kai',
clientTimestamp: '2020-01-10T18:21:20.792Z' }
}
{ type: 'event',
id: '4uPdpZhlTFfBMziBE7EmEI-f|0000005',
timestamp: 2020-01-10T18:21:26.780Z,
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: { id: 'dl_123', name: 'johndoe', role: 'user' },
conversation: { id: '4uPdpZhlTFfBMziBE7EmEI-f' },
recipient: { id: 'botberg#QaeuoeEamLg', name: 'Dungeon Runner' },
locale: 'en-US',
channelData:
{ clientActivityID: '1578680480821h7kgfm9cyz',
clientTimestamp: '2020-01-10T18:21:20.821Z' },
value:
{ name: 'end_conversation', value: 'user ended conversation' },
name: 'user_event'
}
Hope of help!
Update (Aug. 6th, 2021):
As Chrome, and other browsers, disallow blocking / delaying the closing of a window during onbeforeunload(), sending an event using an event handler or listener is usually unreliable, at best. At worst, it just doesn't work.
However, there is another method that does appear work by using 'window.navigator.sendBeacon()' (providing it's supported).
sendBeacon is a low-level, simplified version of fetch that “asynchronously sends a small amount of data over HTTP to a web server”. It only sends as a POST, only takes the URL or URL + data as properties, and doesn’t wait for a response. As the docs state:
The data is sent reliably
It's sent asynchronously
It doesn't impact the loading of the next page
In testing, I have coupled it with a proactive messaging endpoint in my bot and it appears to work perfectly. (My code sample below is in JS, pulled from a project - for reference, here is the proactive messaging C# sample, available in other languages, as well).
When I navigate to another page or close the browser, sendBeacon posts the message to the endpoint creating the proactive message, which, in turn, sends an activity to the bot. When I return to the Web Chat page, the message is now visible in the chat window. Keep in mind, I also have persistence set up in Web Chat allowing me to return to a conversation previously started.
In the below image, I demo loading Web Chat, navigating to my browser’s home page, and then return – the proactive message is now visible in the chat.
(Web Chat) hosting page:
window.onbeforeunload = () => {
let body = { user: { userName: 'McUser', userId: 'abc123' } };
const headers = { type: 'application/json', 'Access-Control-Allow-Origin': '*' };
const blob = new Blob( [ JSON.stringify( body ) ], headers )
navigator.sendBeacon( 'http://localhost:3978/api/notify', blob )
}
Bot's proactive messaging endpoint:
server.post('/api/notify', async (req, res) => {
const userName = req.body.user.userName;
const userId = req.body.user.userId;
for (const conversationReference of Object.values(conversationReferences)) {
await adapter.continueConversation(conversationReference, async (turnContext) => {
await turnContext.sendActivity(`${ userName } (userId: ${ userId }) exited chat.`);
});
}
});

How do i handle oauth in Nuxt with a separate API server?

Currently I have two separate apps
Frontend (Nuxt)
Backend (Golang)
On the backend I'm using a third party library called Goth and I'm using Facebook as the provider. Everything works fine on the backend but I'm confused on how to do on the frontend which is on the Nuxt side
So technically on the backend there will be two urls
/auth/facebook
/auth/facebook/callback
Once everything is correct then I will get the object from facebook on the backend side which is the API written in Golang
data: {
RawData: {
email: "john#gmail.com",
first_name: "John",
id: "123123",
last_name: "Grave",
name: "John Grave",
picture: {
data: {
height: 50,
is_silhouette: true,
url: "https://platform-lookaside.fbsbx.com/platform/profilepic/?asid=13123123&height=50&width=50&ext=1553079619&hash=AeTX5RW5K_avWLbI",
width: 50
}
}
},
Provider: "facebook",
Email: "john#gmail.com",
Name: "John Grave",
FirstName: "John",
LastName: "Grave",
NickName: "John Grave",
Description: "",
UserID: "123123",
AvatarURL: "https://platform-lookaside.fbsbx.com/platform/profilepic/?asid=2312802522337124&height=50&width=50&ext=1553079619&hash=AeTX5RW5K_avWLbI",
Location: "",
AccessToken: "EAAIuR3NSCPwBAEcp2jskHuUCzdWLB97Aq99nCV5HuieVVz8xGfJ6exAZDZD",
AccessTokenSecret: "",
RefreshToken: "",
ExpiresAt: "2019-04-19T15:52:59.895655+08:00"
},
status: 200
}
Assume that everything is working fine on the backend.
The only thing that I could think of is calling it on the method (Nuxt side)
export default {
methods: {
facebookLogin() {
window.location.href = `http://localhost:8080/auth/facebook`
}
}
}
This will just redirect to the
http://localhost:8080/auth/facebook/callback?code=AQAaq9GYcGAnQ9wUCDAd5BFRHxMRjqGFR0J6zjGtYpD-
What are the correct steps should I do to communicate with the backend OAuth API?
Thanks!
Hi #sinusGob you can use this the auth plugin of nuxt
See the docs.
https://auth.nuxtjs.org/reference/providers/facebook
Thanks.

Get URL Referer [BotFramework-WebChat]

I'm using a directline inside my website and I was wondering if there is anyway to get the URL of the website inside my bot code. Previously, in v3, I was initializing the chat with:
BotChat.App({
directLine: { secret: "{directline_secret}" },
user: { id: 'You', referrer: window.location.href},
bot: { id: '{bot_id}' },
resize: 'detect'
}, document.getElementById("bot"));
and I was able to get the referrer with this line of code activity.From.Properties["referrer"].ToString(), but in v4 I can't find a way to get the referrer inside the bot.
Can someone help me?
Thanks in advance.
In v4 the value is part of the turnContext.activity (in Node) or turnContext.Activity (in C#) object. Passing the url value as you have done in your question (i.e., as part of the user object) you would access it like so (Node example):
async onTurn(turnContext) {
if (
turnContext.activity.type === "event" && turnContext.activity.name === "eventName"
) {
this.userProfile.location = turnContext.activity.from.referrer;
await console.log(this.userProfile.location);
}
I included a name as well as specified a type in my BotChat.App post to match this event to in the turnContext.activity:
function testMethod(someValue) {
botConnection
.postActivity({
from: { id: 'me', referrer: window.location.href },
name: 'eventName',
type: 'event',
value: someValue
})
.subscribe(function (id) {
console.log('"eventName" sent');
});
};
In this example, the method is tied to a button being pressed on the page.
Hope of help!

BotBuilder backchannel event data into session

TLDR: How can I initialise my conversations with data sent from the backchannel and use that information throughout the conversation with a user?
Using Microsoft botbuilder, a "backchannel" mechanism is provided with which I can send data to and from the bot.
Similar to this answer I am using the backchannel method to send a conversationStarted event to the bot in order to start a conversation as you might expect.
Upon receiving the event, I start a dialog which fires off my proactive messages to the user.
bot.on('event', event => {
if (event.name === 'conversationStarted') {
bot.beginDialog(event.address, determineWhichDialogToStart(event))
}
})
I can then use middleware or event listeners to intercept this event and see its contents. Sure enough I can see the event
{ type: 'event',
name: 'conversationStarted',
text: '',
myMetaProperty: 'foobar',
attachments: [],
entities: [],
address:
{ id: '8f5d3952-df3b-4340-89b4-97360f8d4965',
channelId: 'emulator',
user: { id: '33abc508-86d7-49c1-aa68-00a3491fda12' },
conversation: { id: '4e8a943d-6a45-41f2-aa11-6671cc1ca4f3' },
bot: { id: 'bot' },
serviceUrl: 'http://localhost:3010/' },
source: 'emulator',
agent: 'botbuilder',
user: { id: '33abc508-86d7-49c1-aa68-00a3491fda12' } }
But the events I can listen to to see this event, don't have access to the session because the message hasn't yet been dispatched to one it seems.
If I use the botbuilder middleware, or the routing event, I then see that it's been turned into a message rather than an event, and has lost my meta data passed to the event. (see myMetaProperty)
{ type: 'message',
agent: 'botbuilder',
source: 'emulator',
sourceEvent: {},
address:
{ id: '8f5d3952-df3b-4340-89b4-97360f8d4965',
channelId: 'emulator',
user: { id: '33abc508-86d7-49c1-aa68-00a3491fda12' },
conversation: { id: '4e8a943d-6a45-41f2-aa11-6671cc1ca4f3' },
bot: { id: 'bot' },
serviceUrl: 'http://localhost:3010/' },
text: '',
user: { id: '33abc508-86d7-49c1-aa68-00a3491fda12' } }
I tried getting access to the session in my receive event/middleware per the comment here on Github but whilst I get access to a session, it's not the session.
bot.on('receive', event => {
bot.loadSessionWithoutDispatching(event.address, (error, session) => {
session.conversationData.myMetaProperty = event.myMetaProperty
})
})
This actually ends up starting a new session - having looked into the loadSession/loadSessionWithoutDispatching, they both lead to startSession and therefore me adding data to the session is lost when i try to use it in a dialog
bot.dialog('example', [
(session, args, next) => {
console.log(session.conversationData.myMetaProperty) // nope
console.log(session.cantUseThisEither) // nope
session.send('foo')
next()
}
])
Just to reiterate the question now that there's been a bit of background, how can I initialise my conversations with data sent from the backchannel and use that information throughout the conversation with a user?
I ended up starting a generic dialog which sets up my session and then moves onto the root dialog.
bot.on('event', event => {
if (event.name === 'conversationStarted') {
bot.beginDialog(event.address, 'initialise-conversation', {
myMetaProperty: event.myMetaProperty
});
}
});
bot.dialog('initialise-conversation', (session, args, next) => {
session.conversationData.myMetaProperty = args.myMetaProperty
session.beginDialog('/');
});
Then whenever I need myMetaProperty throughout my conversation I can get it from session.conversationData
--
In response to the comment below, here's some additional information which may help someone.
The shape of the event you receive on the bot must contain an address obviously. For example here's the one I send to my bot.
{ myMetaProperty: 'foo',
name: 'conversationStarted',
type: 'event',
address:
{ id: '4q5aaBw77aNGGvrpkeji8A',
channelId: 'custom-directline',
user: { id: 'c214imy6PCJZY4G1x2AXSD' },
conversation: { id: 'bGvMBjEmFTg3DMxsbvJGjH' },
bot: { id: 'bot' },
serviceUrl: 'https://localhost:3010/' },
source: 'custom-directline',
agent: 'botbuilder',
user: { id: 'c214imy6PCJZY4G1x2AXSD' } }
Start by ensuring that you're sending the address. The key bits of information for routing messages back to an individual are the user and conversation objects.
For example, if you're sending information using the backchannel of the botframework webchat you'd do it as follows.
var user = { id: uuidv4() };
var botConnection = new BotChat.DirectLine({...});
botConnection.postActivity({
type: 'event',
from: user,
name: 'conversationStarted',
myMetaProperty: 'foo',
}).subscribe();
BotChat.App({
user: user,
botConnection: botConnection,
}, document.getElementById("bot"));
This will be picked up by the event listener described above.
Another thing to note is that the emulator (and possibly other clients/channels send a conversationUpdate event which I just passed onto my conversationStarted event when I was using it. Here's the listener to do that for reference in case it's useful.
bot.on('conversationUpdate', message => {
message.membersAdded &&
message.membersAdded
.filter(identity => identity.id === message.address.bot.id)
.forEach(identity => bot.emit('conversationStarted', message));
});
You can pass data via beginDialog(address: IAddress, dialogId: string, dialogArgs?: any, done?: (err: Error) => void): void;, you can pass the data you want to send as the third parameter in this function.
You can refer to the source code at https://github.com/Microsoft/BotBuilder/blob/master/Node/core/src/bots/UniversalBot.ts#L243 for more details.

dijit/form/Select onSelect event

Are there other events that can be registered with dojo/form/Select, except onChange?
I'd need to execute a callback function every time user selects an option, even though he selects the same option as it was selected last time. The options I have tried: onSelect, onClick did not work.
var spatialSelectionStore = new Memory({
data: [
{ label: "Rectangle", id: "RECT" },
{ label: "Polygon", id: "POLY" },
{ label: "Circle", id: "CIRC" },
{ label: "Freehand", id: "FREE" }
]
});
var os = new ObjectStore({ objectStore: spatialSelectionStore });
spatialQuerySelect = new Select({
id: "selectionType",
style: { width: "100px" },
store: os,
onChange: activateDrawTool
}, "cp_selectByShapeId");
spatialQuerySelect.startup();
I found a way to do this, and while it may not be the best way to do it, it seems to work.
I set up an aspect to fire a function after the Select._setValueAttr function executes, which is fired by the widget every time you click on either the menu drop-down or a drop-down item. Because of this, I added a check to make sure the function callback only fires when you click on a menu item (i.e. after the menu has closed). I also had to delete the onChange callback you added to Select manually, as this interfered with the aspect.
HTML
<div id="foo"></div>
JavaScript
require(["dojo/aspect", "dojo/store/Memory", "dijit/form/Select", "dojo/data/ObjectStore", "dojo/dom-construct", "dojo/dom", "dojo/aspect"], function(aspect, Memory, Select, ObjectStore, domConstruct, dom, aspect) {
var spatialSelectionStore = new Memory({
data: [
{ label: "Rectangle", id: "RECT" },
{ label: "Polygon", id: "POLY" },
{ label: "Circle", id: "CIRC" },
{ label: "Freehand", id: "FREE" }
]
});
var os = new ObjectStore({ objectStore: spatialSelectionStore });
spatialQuerySelect = new Select({
id: "selectionType",
style: { width: "100px" },
store: os
}, "cp_selectByShapeId");
spatialQuerySelect.startup();
aspect.after(spatialQuerySelect, "_setValueAttr", function() {
if(spatialQuerySelect.dropDown.isShowingNow === false) {
alert(spatialQuerySelect.get('value'));
}
});
domConstruct.place(spatialQuerySelect.domNode, dom.byId("foo"), "first");
});
Fiddle
Aspects can be very powerful, but if you use too many and rely on them too heavily, you can end up with a horrible mess of spaghetti code, so I recommend you use them sparingly, and only when necessary.
In case you're not familiar with what they do, you can tell an aspect to fire before, after, or around another method, and the aspect will "listen" to that method being fired and behave appropriately with your function callback. Further documentation.
spatialQuerySelect.dropDown.on("execute",function() {
alert(spatialQuerySelect.get('value'));
});
this would also work for all option.
onExecute: function(){
// summary:
// Attach point for notification about when a menu item has been executed.
// This is an internal mechanism used for Menus to signal to their parent to
// close them, because they are about to execute the onClick handler. In
// general developers should not attach to or override this method.
// tags:
// protected
},

Resources