Overview of Messaging Extension
One of the Microsoft Teams App capabilities is to create Messaging extensions that allow us to customize and extend Teams client. The messaging extension can search or initiate actions and allows us to show/write our logic through buttons and forms in Teams client. We can initiate a Messaging extension from composing message area, the command box, or in the context of a message. It will allow us to send back the results of the user input or our processing of data back in form of a formatted card which will be embedded in the message compose area. Users can then choose to modify this composed message and choose to send it to the conversation.
Below are some screenshots that will help you understand from which all places the Messaging extensions can be invoked
1. From the Search box on top
2. From Message compose box
3. From the context of Message, click on the context menu and click on more Actions.
There are 3 types of Messaging extensions namely Action command, Search command, and link unfurling which we can create, you can read about it at this link
Today we are going to create our first Messaging extension of the action command, So let us get started.
Step 1 - Create Azure Bot
In this step, we are going to create Azure Bot because Messaging extension is dependent on the Bot framework to read the message/conversation context send results back in Messagebox.
From this step, we would have noted down App ID and App Secret to be used in the next steps.
Go to portal.azure.com
Click on Create resource
Search for 'Azure Bot' and click on create
Provide below inputs
Click on Review + Create
It will do validation and once validation passed, we will see Create button, click on it.
Once completed, we should see the below screen. Click on Go to resource
Next, we would enable the Bot Teams Channel.
Click on Save to enable Teams Channel.
The next step would be App ID and Secret which we need. Click on Configuration on the left blade.
We would have to note down this App ID from here and Click on Manage as displayed in the below screenshot.
It will take us to the below screen. Click on Certificate and secrets-> New client Secret and enter description and choose Expires. Click on Add button
It will create a new Secret which we will have to copy now itself because this would be only displayed once.
Keep this App ID and secret handy, we will use this in the next steps.
Step 2 - Create Teams App of type Messaging extension
We are going to use the Teams App Yoeman generator to scaffold our Teams App. You can refer to this link for setting up yo teams generator.
Open command prompt, create a new folder of your choice in your drive where you wish to create the solution. Run the below command.
yo teams
We would be asked with series of questions, please refer to the below screenshot for options you have to choose.
- As we are going to create an Action command, make sure you choose 'Action based messaging extension'
- Use Microsoft App ID as from the previous step.
Once the above process is completed, we should see the below output.
Use 'code .' to open the solution in Visual Studio Code.
Step 3 - Prepare Local Development environment to Test App
We would see the .env file on the root of the solution. If we would have added the correct App ID while running yo teams, we would see that APP id here
Paste the App Secret in the MICROSOFT_APP_PASSWORD variable.
Please note that once you publish this App to Azure, we have to make sure that the App Service configuration has these values set up with exact same variable names.
Step 4 - Test the App
Before we go into details of project structure and understand how all the files etc are related. Let us quickly run it
Go to solution root path in command prompt and run below command
gulp ngrok-serve
It would take some time to compile and run the solution
Once you see the above message the solution has run successfully.
Next, you would have to update the ngrok URL in the Azure AD app registration
yo teams would have generated a teams app package for us in the below folder path...
\Code\Teams\messaging-ext\package\messagingext.zip
Step 5 - Test the App in Teams
Go to teams and click on Apps on the left panel and then select upload a custom app(this button will only be visible if sideloading is enabled by Teams Administrator)
Click on Upload a custom app and select ‘Upload for me and my teams’,
Select the zip package at ‘D:\SP\samples\teamsapps\teams-graphtoolkit\package\teamsgraphtoolkit.zip’
Note - This zip file will be updated everything you run gulp ngrok-serve, so you have to make sure you upload the latest zip package whenever you have a new ngrok URL and also update the same in App Registration in the Azure portal.
Click on Add
Once installed successfully, you will see below the output of the Teams Tab
To try Extension, go to any chat conversation with any user or in the Teams channel.
As we have chosen yes to question while creating the solution "[extension] Do you need configuration or authorization when collecting the information? Yes"
It will allow us to get some configuration value from users to handle our logic, This would be a nice place to also authorize users with an external system.
Once you click on setup, it will open another pop-up window to collect configuration input. In this is the case it is just a Checkbox to On and Off.
Once you click ok, we will see the actual action command window, enter email address and click on Ok
Enter any email address and click ok then it will send show below output in the message compose area.
If you see it has the same email is used to generate format message...
So that's it, we have our Hello World Messaging for you...
Understanding the Project Structure and files
For real-world scenarios, we won't be using the default project structure and hence we should understand how this works.
First, let us how the Input form which asks for Email addresses comes from.
src\client\messagingExtMessageExtension\MessagingExtMessageExtensionAction.tsx
I won't go into details of how to build UI and use of state etc to read values from user inputs etc. But it would be typical of using React framework concepts which we will have to use. This particular component is a functional component and not a class component so it is using a hook etc.
The important line here to understand is highlighted above, when the user clicks on the Ok button, it is calling the below method which we have to remember when we have to pass the data to Task Submission event on the server-side supported targeted action's supported which has a method to handle this event.
microsoftTeams.tasks.submitTask({
email
})
In the File - src\server\messagingExtMessageExtension\MessagingExtMessageExtension.ts
We have the callBack method onSubmitAction which would be called when from client site microsoftTeams.tasks.submitTask method is called.
We will receive the data from the client site in the value object and the JSON object can be accessed by value.data class and then our object's attributes.
To access the email we would be using syntax 'value.data.email' as we are only passing email in JSON object from the client-side.
If you also notice, we are sending the MessagingExtensionResult type which is required to be sent, and in this, we can send cards,
By looking at the definition of MessagingExtensionResult, we can see that we have different possible values and combinations of what we can send back as result.
In the above case, the type is the result and the layout is a list but we are sending an array of attachments as cards. And the adaptive card is built by showing Email as Textblock and one random image :)
export interface MessagingExtensionResult {
/**
* @member {AttachmentLayout} [attachmentLayout] Hint for how to deal with
* multiple attachments. Possible values include: 'list', 'grid'
*/
attachmentLayout ? : AttachmentLayout;
/**
* @member {MessagingExtensionResultType} [type] The type of the result. Possible values include:
* 'result', 'auth', 'config', 'message', 'botMessagePreview'
*/
type ? : MessagingExtensionResultType;
/**
* @member {MessagingExtensionAttachment[]} [attachments] (Only when type is
* result) Attachments
*/
attachments ? : MessagingExtensionAttachment[];
/**
* @member {MessagingExtensionSuggestedAction} [suggestedActions]
*/
suggestedActions ? : MessagingExtensionSuggestedAction;
/**
* @member {string} [text] (Only when type is message) Text
*/
text ? : string;
/**
* @member {Activity} [activityPreview] (Only when type is botMessagePreview)
* Message activity to preview
*/
activityPreview ? : Activity;
}
So it is obvious now, that depending on our requirement we have to study above class and try the different combination on type of output we need
If we take a step back and have to understand how the MessagingExtMessageExtensionAction component was loaded when the user clicks the extension command. This is interesting and helps to also see how the configuration task module was invoked before the actual input form was loaded.
In the same file, src\server\messagingExtMessageExtension\MessagingExtMessageExtension.ts
We have a method onFetchTask that takes care of controlling what to load. This method would be called from the server-side and we can either send an adaptive card or embedded web view...so in this case, we are displaying our action/config HTML page depending on if the configuration window has loaded or not.
If the state property is not empty it means the config.html has been loaded and the user has added configuration values, so next time load the action.html file
Not it is again obvious that as this is server-side code it won't able to load the React components directly so it is actually loading pure HTML pages which are indirectly loaded our React components. This is not just true for Messaging extensions but if you are building any React-based application there would be always the HTML page that would be rendering react component using ReactDom.render method.
Let us see the code for action.html and config.html which is indirectly loading our React components.
These HTML files would be in the public folder, where we can add any public-facing HTML files.
I hope this article will help you understand the messaging extension concept and also provide you with step-by-step guidance on how to create a basic messaging extension from your side...!!
Happy coding...!!!!