-
Notifications
You must be signed in to change notification settings - Fork 254
Description
Here is the drafted GitHub feature request issue, grounded entirely in the actual codebase architecture:
Feature Request: Microsoft Teams Messaging Integration
Title: feat(messaging): add Microsoft Teams adapter
Labels: enhancement, messaging, new-adapter
Summary
Spacebot currently supports five native messaging platforms: Discord, Slack, Telegram, Twitch, and Webchat, plus Email and Webhook. 1
This issue requests adding Microsoft Teams as a first-class messaging adapter, following the exact same pattern used by the existing adapters. Microsoft Teams is one of the most widely used enterprise collaboration platforms in the world, and supporting it would unlock Spacebot for corporate/enterprise multi-agent deployments.
Motivation
Spacebot is explicitly built for teams and multi-user environments. The README states:
"For teams — connect it to Slack. Each channel gets a dedicated conversation with shared memory."
Microsoft Teams users deserve equivalent first-class support. Enterprise teams running on Microsoft 365 currently have no path to deploying Spacebot natively in their environment, and the existing Webhook adapter is too low-level for a full integration (no typing indicators, no rich cards, no per-channel permission filtering, etc.).
Implementation Plan
The integration follows the established adapter pattern precisely. Here is what needs to be built:
1. New Adapter File: src/messaging/teams.rs
A new TeamsAdapter struct must implement the Messaging trait defined in src/messaging/traits.rs: 2
The trait requires:
name(&self) -> &str— return the runtime key (e.g."teams"or"teams:ops")start(&self) -> Result<InboundStream>— connect to Teams via the Bot Framework SDK and begin receiving messagesrespond(&self, message, response) -> Result<()>— send outbound responses using Adaptive Cards, plain text, or file uploadssend_status(&self, message, status) -> Result<()>— show typing indicators via the Bot Framework'ssendActivitywithtypingtypebroadcast(&self, target, response) -> Result<()>— proactive messaging to a Teams channelfetch_history(&self, message, limit) -> Result<Vec<HistoryMessage>>— backfill recent messages using the Microsoft Graph APIhealth_check(&self) -> Result<()>— verify the Bot Framework credentials are validshutdown(&self) -> Result<()>— gracefully disconnect
Because the Messaging trait has a blanket implementation into MessagingDyn, any type implementing Messaging is automatically usable via Arc<dyn MessagingDyn> in the MessagingManager: 3
2. Declare the Module in src/messaging.rs
pub mod teams;This mirrors how Slack and Discord are declared today: 1
3. New Config Types in src/config/types.rs
Following the pattern of SlackConfig / SlackInstanceConfig: 4
A TeamsConfig and TeamsInstanceConfig need to be added, holding:
enabled: boolapp_id: String— Azure AD App (Client) IDapp_password: String— Bot Framework client secrettenant_id: Option<String>— for single-tenant botsdm_allowed_users: Vec<String>— AAD Object IDs of users allowed to DMinstances: Vec<TeamsInstanceConfig>— for multi-tenant named instances
SystemSecrets must be implemented (matching the Slack pattern) to register TEAMS_APP_ID and TEAMS_APP_PASSWORD as system-category secrets that are never injected into worker subprocesses.
4. Add teams Field to MessagingConfig
MessagingConfig in src/config/types.rs needs a new optional field:
pub teams: Option<TeamsConfig>,Today it holds: 5
5. TOML Schema: src/config/toml_schema.rs
A TomlTeamsConfig struct needs to be added and wired into TomlMessagingConfig: 6
6. Permissions: src/config/permissions.rs
A TeamsPermissions struct (following the SlackPermissions / TelegramPermissions pattern) should be added: 7
Teams-specific filters:
tenant_filter: Option<Vec<String>>— restrict to specific Azure tenantsteam_filter: Option<Vec<String>>— restrict to specific Teams (group IDs)channel_filter: HashMap<String, Vec<String>>— team_id → allowed channel IDsdm_allowed_users: Vec<String>— AAD user object IDs
The Arc<ArcSwap<TeamsPermissions>> pattern used by Slack/Discord enables hot-reloadable permissions without restarting the adapter connection.
7. Binding Extension
The Binding struct currently has guild_id (Discord), workspace_id (Slack), and chat_id (Telegram): 5
A new field team_id: Option<String> should be added to Binding (and its TOML/API counterparts) to filter bindings by Microsoft Teams group/team ID.
8. MessagingManager Registration
The adapter is registered using the existing register() / register_and_start() API in MessagingManager, which supports hot-reload and retry with exponential backoff at no extra implementation cost: 8
Proposed config.toml Interface
[messaging.teams]
app_id = "env:TEAMS_APP_ID"
app_password = "env:TEAMS_APP_PASSWORD"
tenant_id = "your-azure-tenant-id" # optional; omit for multi-tenant
# Optional: named instances for multi-tenant or multi-bot setups
[[messaging.teams.instances]]
name = "eng-bot"
app_id = "env:TEAMS_ENG_APP_ID"
app_password = "env:TEAMS_ENG_APP_PASSWORD"
[[bindings]]
agent_id = "my-agent"
channel = "teams"
team_id = "your-teams-group-id" # optional; omit for all teams
channel_ids = ["channel-id-1"] # optional; omit for all channelsTechnology / SDK
The recommended approach is to use Microsoft's Bot Framework for Rust via REST (the SDK is not Rust-native), or the Microsoft Graph REST API using reqwest + tokio. The adapter would:
-
Inbound: Expose an HTTPS webhook endpoint that Teams posts activity payloads to (similar to the Slack Socket Mode listener in
src/messaging/slack.rs). 9 -
Outbound: Call the Bot Framework REST API (
https://smba.trafficmanager.net/...) using the OAuth 2.0client_credentialsflow to obtain a bearer token, then POSTActivityobjects — plain text, Adaptive Cards, or file attachments. -
Typing indicator: Send an
Activityof type"typing"before the response, matching thesend_statuscontract: 10 -
History backfill: Call Microsoft Graph
GET /teams/{team-id}/channels/{channel-id}/messagesto implementfetch_history.
Suggested Cargo dependencies:
# In Cargo.toml
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
# (reqwest is likely already present; no new HTTP dep needed)No heavy SDK needed — the Bot Framework REST protocol is well-documented and straightforward with reqwest + serde_json.
Outbound Response Mapping
The existing OutboundResponse variants map naturally to Teams:
OutboundResponse |
Teams equivalent |
|---|---|
Text(s) |
Plain text Activity |
File { filename, data, ... } |
Teams file attachment via Graph upload |
Reaction(emoji) |
Message like/reaction (Graph API) |
RichMessage { blocks, ... } |
Adaptive Card payload |
StreamChunk / StreamEnd |
Edit the message via PUT activity |
Reference Links
- Azure Bot Service — Bot Framework REST API
- Microsoft Graph — Teams Channel Messages API
- Adaptive Cards specification
- Bot Framework Activity schema
- Register a bot with Azure
- Microsoft Teams — Send proactive messages
- Existing Slack adapter (reference implementation)
- Existing Discord adapter (reference implementation)
- Messaging trait definition
Acceptance Criteria
-
TeamsAdapterimplements theMessagingtrait (all 7 methods) -
TeamsConfig/TeamsInstanceConfigadded tosrc/config/types.rswithSystemSecretsimpl -
teamsfield added toMessagingConfig -
TomlTeamsConfigadded tosrc/config/toml_schema.rs -
TeamsPermissionswithfrom_config/from_instance_configadded tosrc/config/permissions.rs -
team_idfield added toBinding(config, TOML schema, API, TypeScript client) - Hot-reload support: permissions swap via
Arc<ArcSwap<TeamsPermissions>>without reconnect - Named instance support (e.g.
messaging.teams.instances[].name = "ops") - Inbound: plain text messages,
@mentionevents, and file attachments parsed intoInboundMessage - Outbound: text (chunked), Adaptive Cards, file uploads, typing indicators, and reactions
-
fetch_historyimplemented via Microsoft Graph API - Credentials (
app_id,app_password) registered inSystemSecretsand never injected into worker subprocesses (matching the security model of all existing adapters) -
TEAMS_APP_ID/TEAMS_APP_PASSWORDenv vars supported - Integration docs added to
docs/content/docs/(messaging)/ -
just preflightandjust gate-prpass
Notes
The MessagingManager's built-in retry-with-exponential-backoff means the Teams adapter gets automatic resilience to transient Bot Framework outages for free. 11
The existing security model (environment sanitisation, system-secret isolation, output scrubbing) applies automatically to the Teams adapter with no extra work, as long as TeamsConfig implements SystemSecrets correctly — matching how SlackConfig does it today. 12
Notes
-
The entire feature is additive — no changes to any existing adapter or the core dispatch loop are required. The
MessagingManager'sregister()andregister_and_start()APIs already handle everything needed. 13 -
The
Messaging+MessagingDynblanket-impl pattern means no changes to the manager's routing, respond, or broadcast code paths are needed — a Teams adapter that passes the trait bounds is wired in automatically. 14 -
The Teams Bot Framework uses an inbound webhook model (Teams POSTs to your server), unlike Slack Socket Mode (your server connects outward). This means the Teams adapter will need to register an HTTPS endpoint — similar in structure to the existing
WebhookAdapter. 15 -
The
OutboundResponse::RichMessagevariant (which Discord uses for embeds and Slack uses for Block Kit) maps directly to Adaptive Cards on Teams — no newOutboundResponsevariants should be needed.