Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
362 changes: 202 additions & 160 deletions samples/graph-pinned-messages/nodejs/ClientApp/package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,13 @@ class Dashboard extends Component {

// Exchange client token with server token and fetch the pinned message details.
exchangeClientTokenForServerToken = async (token) => {
var id = "ChatId"; //Replace {{ChatId ex - 19:[email protected]}}
if (!this.state.context || !this.state.context.chat || !this.state.context.chat.id) {
console.error("Chat context is not available");
this.setState({ isError: true });
return;
}

var id = this.state.context.chat.id;
axios.get(`/api/chat/getGraphAccessToken?ssoToken=${token}&chatId=${id}`).then((response) => {
var responseMessageData = response.data;
console.log("-----Response--->",responseMessageData);
Expand Down
3 changes: 3 additions & 0 deletions samples/graph-pinned-messages/python/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MicrosoftAppId=
MicrosoftAppPassword=
AAD_APP_TENANT_ID=
14 changes: 14 additions & 0 deletions samples/graph-pinned-messages/python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# TeamsFx files
env/.env.*.user
appManifest/build/

# python virtual environment
.venv/

# misc
.deployment/

# tmp files
__pycache__/

/build/
6 changes: 6 additions & 0 deletions samples/graph-pinned-messages/python/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"TeamsDevApp.ms-teams-vscode-extension",
"ms-python.python",
]
}
69 changes: 69 additions & 0 deletions samples/graph-pinned-messages/python/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch App (Edge)",
"type": "msedge",
"request": "launch",
"url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}",
"cascadeTerminateToConfigurations": [
"Python: Run App Locally"
],
"presentation": {
"group": "all",
"hidden": true
},
"internalConsoleOptions": "neverOpen"
},
{
"name": "Launch App (Chrome)",
"type": "chrome",
"request": "launch",
"url": "https://teams.microsoft.com/l/app/${{local:TEAMS_APP_ID}}?installAppPackage=true&webjoin=true&${account-hint}",
"cascadeTerminateToConfigurations": [
"Python: Run App Locally"
],
"presentation": {
"group": "all",
"hidden": true
},
"internalConsoleOptions": "neverOpen"
},
{
"name": "Python: Run App Locally",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/app.py",
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
}
],
"compounds": [
{
"name": "Debug (Edge)",
"configurations": [
"Launch App (Edge)",
"Python: Run App Locally"
],
"preLaunchTask": "Prepare Teams App Resources",
"presentation": {
"group": "all",
"order": 1
},
"stopAll": true
},
{
"name": "Debug (Chrome)",
"configurations": [
"Launch App (Chrome)",
"Python: Run App Locally"
],
"preLaunchTask": "Prepare Teams App Resources",
"presentation": {
"group": "all",
"order": 2
},
"stopAll": true
}
]
}
6 changes: 6 additions & 0 deletions samples/graph-pinned-messages/python/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"debug.onTaskErrors": "abort",
"python.analysis.extraPaths": [
"./Models"
]
}
78 changes: 78 additions & 0 deletions samples/graph-pinned-messages/python/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// This file is automatically generated by Microsoft 365 Agents Toolkit.
// The teamsfx tasks defined in this file require Microsoft 365 Agents Toolkit version >= 5.0.0.
// See https://aka.ms/teamsfx-tasks for details on how to customize each task.
{
"version": "2.0.0",
"tasks": [
{
"label": "Prepare Teams App Resources",
"dependsOn": [
"Validate prerequisites",
"Start local tunnel",
"Provision",
"Deploy"
],
"dependsOrder": "sequence"
},
{
// Check all required prerequisites.
// See https://aka.ms/teamsfx-tasks/check-prerequisites to know the details and how to customize the args.
"label": "Validate prerequisites",
"type": "teamsfx",
"command": "debug-check-prerequisites",
"args": {
"prerequisites": [
"m365Account", // Sign-in prompt for Microsoft 365 account, then validate if the account enables the sideloading permission.
"portOccupancy" // Validate available ports to ensure those debug ones are not occupied.
],
"portOccupancy": [
3978, // app service port
]
}
},
{
// Start the local tunnel service to forward public URL to local port and inspect traffic.
// See https://aka.ms/teamsfx-tasks/local-tunnel for the detailed args definitions.
"label": "Start local tunnel",
"type": "teamsfx",
"command": "debug-start-local-tunnel",
"args": {
"type": "dev-tunnel",
"ports": [
{
"portNumber": 3978,
"protocol": "http",
"access": "public",
"writeToEnvironmentFile": {
"endpoint": "BOT_ENDPOINT", // output tunnel endpoint as BOT_ENDPOINT
"domain": "BOT_DOMAIN" // output tunnel domain as BOT_DOMAIN
}
}
],
"env": "local"
},
"isBackground": true,
"problemMatcher": "$teamsfx-local-tunnel-watch"
},
{
// Create the debug resources.
// See https://aka.ms/teamsfx-tasks/provision to know the details and how to customize the args.
"label": "Provision",
"type": "teamsfx",
"command": "provision",
"args": {
"env": "local"
}
},
{
// Build project.
// See https://aka.ms/teamsfx-tasks/deploy to know the details and how to customize the args.
"label": "Deploy",
"type": "teamsfx",
"command": "deploy",
"args": {
"env": "local"
}
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
181 changes: 181 additions & 0 deletions samples/graph-pinned-messages/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
---
page_type: sample
description: This sample application demonstrates how to pin and display messages in Microsoft Teams chat using the Graph API.
products:
- office-teams
- office
- office-365
languages:
- python
extensions:
contentType: samples
createdDate: "29/07/2025 01:02:15 PM"
urlFragment: officedev-microsoft-teams-samples-graph-pinned-messages-python
---

# This is a sample application which demonstrates how to pin messages in chat using Graph api.

This sample tab application showcases the functionality of pinning messages in Microsoft Teams chat through the Graph API. Users can easily view and manage pinned messages, and the app includes features such as Teams Single Sign-On (SSO) with Flask backend support, ensuring a smooth and interactive experience.

## Included Features
* Teams SSO (tabs)
* Flask Backend
* Pinned Messages
* Graph API

## Interaction with app
![pinned-messages ](Images/GraphPinnedMessage.gif)

## Prerequisites

- Microsoft Teams is installed and you have an account (not a guest account)
- [Python SDK](https://www.python.org/downloads/) min version 3.8
- [dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) or [ngrok](https://ngrok.com/) latest version or equivalent tunnelling solution
- [M365 developer account](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/prepare-your-o365-tenant) or access to a Teams account with the appropriate permissions to install an app.
- [Microsoft 365 Agents Toolkit for VS Code](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension) or [TeamsFx CLI](https://learn.microsoft.com/microsoftteams/platform/toolkit/teamsfx-cli?pivots=version-one)

## Run the app (Using Microsoft 365 Agents Toolkit for Visual Studio Code)

The simplest way to run this sample in Teams is to use Microsoft 365 Agents Toolkit for Visual Studio Code.

1. Ensure you have downloaded and installed [Visual Studio Code](https://code.visualstudio.com/docs/setup/setup-overview)
2. Install the [Microsoft 365 Agents Toolkit extension](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension)
3. Select **File > Open Folder** in VS Code and choose this samples directory from the repo
4. Press **CTRL+Shift+P** to open the command box and enter **Python: Create Environment** to create and activate your desired virtual environment. Remember to select `requirements.txt` as dependencies to install when creating the virtual environment.
5. Using the extension, sign in with your Microsoft 365 account where you have permissions to upload custom apps
6. Select **Debug > Start Debugging** or **F5** to run the app in a Teams web client.
7. In the browser that launches, select the **Add** button to install the app to Teams.

## Setup

- Ensure that you've [enabled the Teams Channel](https://docs.microsoft.com/en-us/azure/bot-service/channel-connect-teams?view=azure-bot-service-4.0)
**NOTE:** When you create app registration, you will create an App ID and App password - make sure you keep these for later.

1. Register a new application in the [Microsoft Entra ID – App Registrations](https://go.microsoft.com/fwlink/?linkid=2083908) portal.
- Select **New Registration** and on the *register an application page*, set following values:
* Set **name** to your app name.
* Choose the **supported account types** (any account type will work)
* Leave **Redirect URI** empty.
* Choose **Register**.
- On the overview page, copy and save the **Application (client) ID, Directory (tenant) ID**. You’ll need those later when updating your Teams application manifest and in the appsettings.json.
- Under **Manage**, select **Expose an API**.
- Select the **Set** link to generate the Application ID URI in the form of `api://{AppID}`. Insert your fully qualified domain name (with a forward slash "/" appended to the end) between the double forward slashes and the GUID. The entire ID should have the form of: `api://fully-qualified-domain-name/{AppID}`
* ex: `api://%ngrokDomain%.ngrok-free.app/00000000-0000-0000-0000-000000000000`.
- Select the **Add a scope** button. In the panel that opens, enter `access_as_user` as the **Scope name**.
- Set **Who can consent?** to `Admins and users`
- Fill in the fields for configuring the admin and user consent prompts with values that are appropriate for the `access_as_user` scope:
* **Admin consent title:** Teams can access the user’s profile.
* **Admin consent description**: Allows Teams to call the app’s web APIs as the current user.
* **User consent title**: Teams can access the user profile and make requests on the user's behalf.
* **User consent description:** Enable Teams to call this app’s APIs with the same rights as the user.
- Ensure that **State** is set to **Enabled**
-. Select **Add scope**
* The domain part of the **Scope name** displayed just below the text field should automatically match the **Application ID** URI set in the previous step, with `/access_as_user` appended to the end:
* `api://[ngrokDomain].ngrok-free.app/00000000-0000-0000-0000-000000000000/access_as_user.
- In the **Authorized client applications** section, identify the applications that you want to authorize for your app’s web application. Each of the following IDs needs to be entered:
* `1fec8e78-bce4-4aaf-ab1b-5451cc387264` (Teams mobile/desktop application)
* `5e3ce6c0-2b1f-4285-8d4b-75ee78787346` (Teams web application)
- **Note** If you want to test or extend your Teams apps across Office and Outlook, kindly add below client application identifiers while doing Azure AD app registration in your tenant:
* `4765445b-32c6-49b0-83e6-1d93765276ca` (Office web)
* `0ec893e0-5785-4de6-99da-4ed124e5296c` (Office desktop)
* `bc59ab01-8403-45c6-8796-ac3ef710b3e3` (Outlook web)
* `d3590ed6-52b3-4102-aeff-aad2292ab01c` (Outlook desktop)
- Navigate to **API Permissions**, and make sure to add the follow permissions:
- Select Add a permission
-  Select Microsoft Graph -\> Delegated permissions.
- `Chat.Read`
- `Chat.ReadWrite`
- `ChatMessage.Send`
- Click on Add permissions. Please make sure to grant the admin consent for the required permissions.
- Navigate to **Authentication**
If an app hasn't been granted IT admin consent, users will have to provide consent the first time they use an app.
Set a redirect URI:
* Select **Add a platform**.
* Select **Single Page Application**.
* Enter the **redirect URI** for the app in the following format: `https://{Base_Url_Domain}/auth-end`. E.g. if you are using ngrok it would be `https://1234.ngrok-free.app` then your `{Base_Url_Domain}` will be`1234.ngrok-free.app` and if you are using dev tunnels then your domain will be `12345.devtunnels.ms`.

- Navigate to the **Certificates & secrets**. In the Client secrets section, click on "+ New client secret". Add a description (Name of the secret) for the secret and select “Never” for Expires. Click "Add". Once the client secret is created, copy its value, it need to be placed in the appsettings.json.

2. Setup NGROK
- Run ngrok - point to port 3978

```bash
ngrok http 3978 --host-header="localhost:3978"
```

Alternatively, you can also use the `dev tunnels`. Please follow [Create and host a dev tunnel](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started?tabs=windows) and host the tunnel with anonymous user access command as shown below:

```bash
devtunnel host -p 3978 --allow-anonymous
```

### 3. Setup for code

1. Clone the repository
```bash
git clone https://github.com/OfficeDev/Microsoft-Teams-Samples.git
```

2. In the folder where repository is cloned navigate to `samples/graph-pinned-messages/python`

3. Create and activate a virtual environment
```bash
python -m venv env
env\Scripts\activate # On Windows
```

4. Install dependencies
```bash
pip install -r requirements.txt
```

5. Update the `config.py` configuration with the ```APP_ID```, ```APP_PASSWORD``` and ```AAD_APP_TENANT_ID``` with values generated in step 1 while doing App Registration.

6. Run your app
```bash
python app.py
```

### 4. Setup Manifest for Teams
1) __*This step is specific to Teams.*__
- **Edit** the `manifest.json` contained in the `appManifest` folder to replace your Microsoft App Id (that was created when you registered your app earlier) *everywhere* you see the place holder string `<<YOUR-MICROSOFT-APP-ID>>` (depending on the scenario the Microsoft App Id may occur multiple times in the `manifest.json`)
- **Edit** the `manifest.json` for `configurationUrl` inside `configurableTabs` . Replace `{{BASE-URL-DOMAIN}}` with your app's base Url domain. E.g. if you are using ngrok it would be `https://1234.ngrok-free.app` then your base url domain will be `1234.ngrok-free.app`.
- **Edit** the `manifest.json` for `validDomains` with base Url domain. E.g. if you are using ngrok it would be `https://1234.ngrok-free.app` then your domain-name will be `1234.ngrok-free.app` and if you are using dev tunnels then your domain will be like: `12345.devtunnels.ms`.
- **Zip** up the contents of the `appManifest` folder to create a `manifest.zip` (Make sure that zip file does not contains any subfolder otherwise you will get error while uploading your .zip package)
- **Upload** the `manifest.zip` to Teams (In Teams Apps/Manage your apps click "Upload an app". Browse to and Open the .zip file. At the next dialog, click the Add button.)
- **Note** Add the app to groupChat scope.

## Running the sample

1) Install App and add to chat
![Add In Chat](Images/1.AddInChat.png)

2) Save the app configuration
![Save Configuration](Images/2.Save.png)

3) Select Message From List and click "Pin New Message"
![Tab Pin Message](Images/3.Tab_Pin_Message.png)

4) Successful Message
![Successful Message](Images/4.Successful_Message.png)

5) Pinned Message will appear in tab
![Tab Pinned Message](Images/5.Pinned_Message_In_Tab.png)

6) Pinned messages are also visible in the chat
![Pinned Message In Chat](Images/6.Pinned_Message_In_Chat.png)

7) You can also pin messages directly from the chat interface
![Pin Message From Chat](Images/7.Pin_Mesage_From_Chat.png)

8) Confirmation that message was pinned successfully
![Message Pinned](Images/8.Message_Pinned.png)

9) Pinned messages will also appear in the tab interface
![Pinned Message In Tab](Images/9.Pinned_Message_In_Tab.png)


## Further reading
- [Pinned message resource type](https://docs.microsoft.com/en-us/graph/api/chat-post-pinnedmessages?view=graph-rest-beta&tabs=csharp)

<img src="https://pnptelemetry.azurewebsites.net/microsoft-teams-samples/samples/graph-pinned-messages-python" />
Loading