-
Notifications
You must be signed in to change notification settings - Fork 344
feat(messaging): add Microsoft Teams channel adapter #607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
slvnlrt
wants to merge
4
commits into
spacedriveapp:main
Choose a base branch
from
slvnlrt:pr/teams-adapter
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
e61e094
feat(messaging): add Microsoft Teams channel adapter
slvnlrt 9a94d95
fix(teams): address review — single-instance routing, hot-reload warn…
slvnlrt 104df40
style(teams): satisfy clippy (collapsible-if, too_many_arguments)
slvnlrt 79bc8e0
fix(teams): key serviceUrl lookup on runtime-prefixed conversation id…
slvnlrt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| { | ||
| "title": "Messaging", | ||
| "pages": ["messaging", "discord-setup", "slack-setup", "telegram-setup", "twitch-setup", "email-setup"] | ||
| "pages": ["messaging", "discord-setup", "slack-setup", "teams-setup", "telegram-setup", "twitch-setup", "email-setup"] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| --- | ||
| title: Teams Setup | ||
| description: Connect Spacebot to Microsoft Teams. | ||
| --- | ||
|
|
||
| # Teams Setup | ||
|
|
||
| Connect Spacebot to Microsoft Teams as a bot. Teams talks to your bot over the Azure Bot Service, so this takes a bit more setup than the other channels: an Entra (Azure AD) app registration, an Azure Bot resource, a Teams app package, and a public HTTPS endpoint Microsoft can reach. | ||
|
|
||
| You need three values from Azure — an **App ID**, a **Client Secret**, and a **Tenant ID** — plus a public URL that forwards to Spacebot. | ||
|
|
||
| <Callout type="warn"> | ||
| Teams requires a **public HTTPS endpoint**. The bot does not connect outbound like Slack; the Azure Bot Service delivers messages by POSTing to a URL you register. A localhost bind is not reachable. Put Spacebot behind a reverse proxy (Caddy, nginx) or a tunnel (cloudflared) that terminates TLS and forwards to the bot's port. | ||
| </Callout> | ||
|
|
||
| ## Step 1: Register an Entra app | ||
|
|
||
| In the [Azure portal](https://portal.azure.com), go to **Microsoft Entra ID** → **App registrations** → **New registration**. | ||
|
|
||
| Name it (e.g. "Spacebot"), pick the supported account types for your org, and register. | ||
|
|
||
| From the app's **Overview**, copy the **Application (client) ID** and the **Directory (tenant) ID**. | ||
|
|
||
| Then go to **Certificates & secrets** → **New client secret**, create one, and copy its **Value** immediately (it is shown only once). This is your **Client Secret**. | ||
|
|
||
| ## Step 2: Create the Azure Bot | ||
|
|
||
| Create an **Azure Bot** resource (the free **F0** SKU is enough). Point it at the app you just registered (use the existing App ID rather than creating a new identity). | ||
|
|
||
| In the bot's **Configuration**, set the **Messaging endpoint** to your public URL with the `/api/messages` path: | ||
|
|
||
| ``` | ||
| https://your-public-host/api/messages | ||
| ``` | ||
|
|
||
| Then open **Channels** and add the **Microsoft Teams** channel. | ||
|
|
||
| ## Step 3: Build and install the Teams app | ||
|
|
||
| Teams needs an app package (a zip with a `manifest.json` and two icons) to surface the bot to users. | ||
|
|
||
| <Callout type="info"> | ||
| The manifest `id` (the Teams app id) must be its **own** GUID, distinct from the bot's App ID. `bots[].botId` is the App ID from Step 1. Scopes are lowercase (`personal`, `team`, `groupchat`), and `validDomains` plus an `accentColor` are required. | ||
| </Callout> | ||
|
|
||
| Upload the package in Teams (**Apps** → **Manage your apps** → **Upload an app**). Most tenants require admin approval: approve it in the **Teams admin center** under **Manage apps**, where you can also restrict who may install it. | ||
|
|
||
| ## Step 4: Configure Spacebot | ||
|
|
||
| Add a `[messaging.teams]` block to your config. Keep the client secret out of the file by referencing an environment variable. | ||
|
|
||
| ```toml | ||
| [messaging.teams] | ||
| enabled = true | ||
|
|
||
| # App ID and Tenant ID from Step 1 (not secret). | ||
| app_id = "00000000-0000-0000-0000-000000000000" | ||
| tenant_id = "00000000-0000-0000-0000-000000000000" | ||
|
|
||
| # Reference the secret from the environment; never hardcode it. | ||
| client_secret = "env:TEAMS_CLIENT_SECRET" | ||
|
|
||
| # Inbound listener. The reverse proxy / tunnel forwards here. | ||
| port = 3979 | ||
| bind = "0.0.0.0" | ||
|
|
||
| # Teams user IDs allowed to DM the bot. "*" allows everyone. | ||
| dm_allowed_users = ["*"] | ||
|
|
||
| [[bindings]] | ||
| agent_id = "main" | ||
| channel = "teams" | ||
| ``` | ||
|
|
||
| Start Spacebot with the secret in the environment: | ||
|
|
||
| ```bash | ||
| TEAMS_CLIENT_SECRET="your-secret-value" spacebot start | ||
| ``` | ||
|
|
||
| ## DM permissions | ||
|
|
||
| Channel and group messages are open: the bot replies wherever it is @mentioned. Direct messages are **fail-closed** — an empty `dm_allowed_users` blocks every DM. | ||
|
|
||
| - To allow specific people, list their Teams user IDs (the `29:...` MRI strings). | ||
| - To allow everyone (org-wide deployments), set `dm_allowed_users = ["*"]`. | ||
|
|
||
| <Callout type="info"> | ||
| You usually do not know a user's `29:...` MRI up front. The simplest path is to **@mention the bot in a channel** (channel messages are not gated), or set the `"*"` wildcard. To allowlist one person, send a DM once (it is dropped), read the dropped `sender_id` from the logs, and add that value. | ||
| </Callout> | ||
|
|
||
| ## What works | ||
|
|
||
| | Capability | Notes | | ||
| |------------|-------| | ||
| | Text replies | Plain text in DMs, channels, and group chats | | ||
| | @mentions | The bot responds when @mentioned in a channel or group | | ||
| | Adaptive Cards | Structured cards rendered from rich replies | | ||
| | Typing indicator | Shown while the bot is working | | ||
| | Inbound files and images | Delivered to the agent (personal scope) | | ||
| | Buttons | Adaptive Card buttons; a click comes back to the agent | | ||
|
|
||
| ## Limitations | ||
|
|
||
| - **One Teams bot per instance.** A single default `[messaging.teams]` adapter is supported. Named multi-bot instances are not yet available. | ||
| - **Files are personal-scope.** Inbound file attachments work in 1:1 chats; channel files require Microsoft Graph and are out of scope. | ||
| - **Rotate the client secret** before relying on it in production, and keep it in the environment rather than the config file. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Document the actual Teams DM allowlist identifier.
The implementation allows DMs by matching AAD object IDs (or UPN strings), but this guide tells users to paste
29:...MRI values intodm_allowed_users. That will make the setup look correct while DMs still fail closed.Suggested fix
Also applies to: 83-90
🤖 Prompt for AI Agents