Integrate with Microsoft Teams
The Microsoft 365 integration API from Here™ includes functionality specifically for integrating your Here Core app with Microsoft Teams. This functionality includes:
-
Navigating users to existing Teams conversations (both group conversations and Teams Channel conversations)
-
Starting chats and calls
-
Enabling Here Core Interop to work "out of the box" with Teams
-
Sharing Instrument context to a Teams conversation
The rest of this article assumes that your app is connected to Microsoft 365 as described in Microsoft 365 integration.
Create a Teams connection
Before you are able to call Teams-specific functionality provided by the API, you must create a new TeamsConnection
object:
import { TeamsConnection } from '@openfin/microsoft365';
const useMsTeamsProtocol = true; // Optional, if true will use msteams: protocol to open links directly in the Teams app instead of via web browser (defaults to true)
const teamsConnection = new TeamsConnection(ms365Connection, useMsTeamsProtocol);
Teams deep linking
The API makes use of Teams deep linking to automate some features of the native Teams desktop application. Deep linking is provided with the msteams
protocol (the integration default) or alternatively via a standard https
link.
The msteams
protocol is preferred if you can assume that the native Teams app is installed on users' desktops, because it provides a seamless Teams user experience. Otherwise, a web page opens in the user's default browser, interrupting the user flow, which in addition to directing the Teams app to the correct function (if installed), also provides the user with options to use the Teams web app or download the native app.
Usage
Make a call or start a chat in Teams from Here Core
The API provides the option of making a call or starting a chat in Teams in either of the following ways:
-
Calling the API functions (
startChat
orstartCall
) directly -
Calling
registerIntentHandlers
and then raisingStartChat
orStartCall
intents via the Here Core Interop APIs or FDC3 APIs
Start a group chat by calling startChat
with a list of email addresses of users who should be included in the group chat:
import { GroupChatOptions } from '@openfin/microsoft365';
const startGroupChatOptions: GroupChatOptions = {
emailAddresses: ['user1@example.com', 'user2@example.com'],
topicName: 'New Topic', // Optional, name for the group chat
message: 'Let’s chat about something...', // Optional, text to prefill message field with
};
const resolvedUsersForChat = await teamsConnection.startChat(startGroupChatOptions);
For the best user experience, we recommend that all Microsoft Entra ID (formerly Azure Active Directory) users have email addresses defined. If an email address can't be resolved to an Entra ID user, no error is thrown.
Start a channel chat by calling startChat
with the IDs of the team and (optionally) the channel to start the chat with. These ID values would typically be known ahead of time, or alternatively can be discovered by executing a request to the Graph API:
import { ChannelChatOptions } from '@openfin/microsoft365';
const startChannelChatOptions: ChannelChatOptions = {
teamId: 'fbe2bf47-16c8-47cf-b4a5-4b9b187c508b',
channelId: '19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2', // Optional, if not provided will use team’s primary channel
};
const resolvedTeamChannelForChat = await teamsConnection.startChat(startChannelChatOptions);
);
Start a Teams call by calling startCall
with a list of email addresses of users who should be included in the call. By default, the caller’s camera is turned off when making the call, but can be turned on by default by setting the second parameter to true
:
const emailAddresses = ['user1@example.com', 'user2@example.com'];
// Make a video call
const withVideo = true;
const resolvedUsersForVideoCall = await teamsConnection.startCall(
emailAddresses,
withVideo // Optional, defaults to false
);
// Make an audio call
const resolvedUsersForAudioCall = await teamsConnection.startCall(emailAddresses);
All of the above functions return a Promise that resolves to one of the following:
-
For group chats or calls, to an array of
UserResult
objects in the same order as their matching email addresses were provided. If a user is not found for an email address,undefined
is returned instead of the object. -
For channel chats, to a
TeamChannelResult
object for the resolved Team and Channel.
If no email addresses can be resolved to UserResult
objects, or if no team or channel can be resolved to a TeamChannelResult
object, no error is thrown and the function resolves with an empty value.
Work with Interop/FDC3
Register intent handlers for StartChat
or StartCall
intents:
// Register StartChat and StartCall intent handlers for Teams
const teamsIntentHandlerRegistration = await teamsConnection.registerIntentHandlers();
// Trigger the handler by firing a StartChat or StartCall intent
await fin.me.interop.fireIntent({
name: 'StartChat', // or StartCall
context: {
type: 'fdc3.contactList',
contacts: [
{
type: 'fdc3.contact',
id: {
email: 'user1@example.com',
},
},
{
type: 'fdc3.contact',
id: {
email: 'user2@example.com',
},
},
],
},
});
// Unsubscribe intent handlers if intent handling is no longer required
await teamsIntentHandlerRegistration.unsubscribe();
Share instrument contexts to Teams conversations
The API provides the ability to share financial context data to a Teams chat, resulting in a formatted card being posted that allows users in the chat to send the message data to other Here Core apps via Interop.
The share
function is able to share either an instrument with price info:
or a chart image:
When the share
function is called, here's what happens:
-
Sharer’s desktop: Sharer posts a message to the recipient(s) providing the (hidden) context data in the message payload. The shared information is displayed with a button that fires an intent when clicked. If a chart image was shared, the button fires a ViewChart FDC3 intent, otherwise a ViewInstrument FDC3 intent is fired.
-
Recipient’s desktop: Receives message from sharer. When the button is clicked in the message card, a URL is opened in the user's default web browser that attempts to run (or if already running, focus on) the app that originally shared the message. At this point, the app fires the relevant intent via Interop.
📘 Note
Allow Here Core "start" links. For the button action from the Teams message to work, your client must allow internal access to
https://start.openfin.co
from the default web browser. When the user clicks the button, the app that calls theshare
function must already be running, or have access to the URL for the app's manifest so the app can be launched.
The process:
-
Specify the target to share to. To share to a group chat, provide an array of email addresses, or to share to a Team Channel, provide an object of type
TeamChannelTarget
:import { TeamChannelTarget } from '@openfin/microsoft365';
let target: string[] | TeamChannelTarget;
// Share to group chat
target = ['user1@example.com', 'user2@example.com'];
// Or, share to team channel
target = {
teamId: 'fbe2bf47-16c8-47cf-b4a5-4b9b187c508b',
channelId: '19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2', // Optional, if not provided will share to the team’s primary channel
} as TeamChannelTarget; -
Specify the financial instrument context data to share in the form of an FDC3 instrument context which must include the
id.ticker
value at a minimum and ideallyname
also (if required, you may also include additional properties and metadata to support your workflows):import * as fdc3 from '@finos/fdc3';
const context: fdc3.Instrument = {
type: 'fdc3.instrument',
name: 'Microsoft Corporation',
id: {
ticker: 'MSFT',
},
}; -
Create the options object to provide to the share function. To share a chart, specify an object of type
ShareChartOptions
. To share instrument price information, specify an object of typeShareInstrumentOptions
:import { ShareChartOptions, ShareInstrumentOptions } from '@openfin/microsoft365';
let shareOptions: ShareChartOptions | ShareInstrumentOptions;
// When sharing a chart
shareOptions = {
chartImage: new Blob([blobArrayBuffer], { type: 'image/png' }), // blobArrayBuffer would be a byte array containing the chart image data. Supported image types: gif, jpg, png
context,
target,
showMessage: true, // Optional, show message in Teams app after posting, defaults to true
timestamp: Date.now(), // Optional, date will not be displayed with chart if not provided
} as ShareChartOptions;
// Or, when sharing instrument price info
shareOptions = {
context,
priceCurrent: 128.95, // Instrument current price
priceHigh: 129.43, // Optional, instrument session high price
priceLow: 127.25, // Optional, instrument session low price
priceOpen: 127.42, // Optional, instrument session open price
showMessage: true, // Optional, show message in Teams app after posting, defaults to true
target,
timestamp: Date.now(), // Optional, defaults to now
} as ShareInstrumentOptions; -
Call the
share
function with the specified options:const resolvedShareTarget = await teamsConnection.share(shareOptions);
The share
function returns a Promise that resolves to either a TeamChannelResult
object or an array of UserResult
objects, depending on the target
specified. As with startChat
and startCall
, no error is thrown if the specified target cannot be resolved.