Microsoft Teams Adapter
Bot Framework adapter with Adaptive Cards for Microsoft Teams integration.
The Teams adapter (chat_sdk-teams) integrates with Microsoft Teams via the Bot Framework.
Installation
# Gemfile
gem "chat_sdk"
gem "chat_sdk-teams"Configuration
require "chat_sdk"
require "chat_sdk/teams"
teams = ChatSDK::Teams::Adapter.new(
app_id: ENV["TEAMS_APP_ID"], # or set TEAMS_APP_ID env var
app_password: ENV["TEAMS_APP_PASSWORD"], # or set TEAMS_APP_PASSWORD env var
tenant_id: ENV["TEAMS_TENANT_ID"] # optional, set TEAMS_TENANT_ID env var
)app_id and app_password are required. tenant_id is optional.
Environment Variables
| Variable | Description |
|---|---|
TEAMS_APP_ID | Bot's Application (client) ID from Azure AD |
TEAMS_APP_PASSWORD | Bot's client secret |
TEAMS_TENANT_ID | Azure AD tenant ID (optional) |
Azure Bot Setup
- Register a bot in the Azure Portal under Bot Services.
- Note the Application (client) ID and create a client secret.
- Configure the messaging endpoint to your webhook URL (e.g.,
https://your-app.com/webhooks/teams). - Create a Teams app manifest and install it in your Teams organization.
Webhook URL
https://your-domain.com/webhooks/teamsmap "/webhooks/teams" do
run bot.webhooks[:teams]
endCapabilities
| Capability | Supported | Notes |
|---|---|---|
edit_messages | Yes | |
delete_messages | Yes | |
ephemeral_messages | No | Raises NotSupportedError |
file_uploads | Yes | Via Base64-encoded attachments |
reactions | Inbound only | Parsing works, but adding/removing raises PlatformError |
modals | No | Raises NotSupportedError |
typing_indicator | Yes | Via Bot Framework typing activity |
streaming_edit | Yes | |
threads | Yes | Uses replyToId |
direct_messages | Yes | Creates 1:1 conversations |
message_history | Yes | Via Microsoft Graph API (requires additional credentials) |
Service URL Registration
Teams requires a serviceUrl to send messages. ChatSDK resolves a conversation URL in this order: the URL from a trusted incoming activity registered in the current adapter, the shared state key chat_sdk:teams:service_url:<conversation_id>, then the configured service_url: or DEFAULT_SERVICE_URL. For proactive messaging, you may register it manually:
teams_adapter = bot.adapter(:teams)
teams_adapter.register_service_url(
"conversation-id-123",
"https://smba.trafficmanager.net/..."
)ChatSDK::Chat injects its state adapter into adapters that support set_state(state). When processes share a state backend, a background job or another process can reply using the service URL persisted from an incoming activity. Redis, PostgreSQL, and MySQL state adapters can be shared across processes; ChatSDK::State::Memory is per-process. A process-local URL registered with register_service_url takes precedence over the value in shared state.
When opening a DM, the adapter reuses the user's last trusted activity service URL from state (with a 30-day TTL), then falls back to a process-local registered URL and the configured default. Set service_url: or TEAMS_SERVICE_URL to override the final default for a deployment whose Bot Framework service endpoint is not the standard Teams endpoint.
Cards Rendering
Cards are rendered as Adaptive Cards using the AdaptiveCardRenderer. They are sent as message attachments with content type application/vnd.microsoft.card.adaptive.
Direct Client Access
teams_adapter = bot.adapter(:teams)
client = teams_adapter.client # BotFrameworkClientMention Formatting
Teams mentions use the format <at>USER_ID</at>:
thread.mention_user("user-id-123") # => "<at>user-id-123</at>"Graph API Message History
The Bot Framework does not natively support fetching message history. To enable fetch_messages, provide Microsoft Graph API credentials:
teams = ChatSDK::Teams::Adapter.new(
app_id: ENV["TEAMS_APP_ID"],
app_password: ENV["TEAMS_APP_PASSWORD"],
graph_client_id: ENV["TEAMS_GRAPH_CLIENT_ID"],
graph_client_secret: ENV["TEAMS_GRAPH_CLIENT_SECRET"],
graph_tenant_id: ENV["TEAMS_GRAPH_TENANT_ID"]
)
messages, next_cursor = teams.fetch_messages(channel_id: "chat-id-123")The Graph API credentials are separate from the Bot Framework credentials. Register an Azure AD app with Chat.Read permissions and grant admin consent. Without these credentials, calling fetch_messages raises a ConfigurationError.