Route threaded replies through Bot Framework reply endpoint - #586
Route threaded replies through Bot Framework reply endpoint#586Mehak Bindra (MehakBindra) wants to merge 4 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
App.reply() currently rejects non-numeric thread root IDs, which blocks proactive thread placement for valid non-numeric roots (e.g., IDs coming from inbound channelData.thread.id / activity IDs).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the Teams Python SDK’s outbound message routing so that threaded placement (L2 group-chat and channel threads) uses the Bot Framework reply endpoint (POST /v3/conversations/{conversationId}/activities/{rootMessageId}) while keeping L1 sends on the standard create-activity endpoint. It also preserves compatibility for proactive callers that still pass legacy ;messageid=-suffixed conversation IDs by translating them internally.
Changes:
- Add
thread_root_idrouting to the shared send path and route replies viaconversations.reply_to_activity(...)when present. - Introduce parsing for legacy
;messageid=conversation IDs and use inboundchannelData.thread.id(typed asThreadInfo) to determine reactive thread placement. - Update API client reply behavior to use the reply endpoint (path-based root ID) and adjust examples/tests accordingly.
File summaries
| File | Description |
|---|---|
| packages/apps/tests/test_app.py | Updates app-level tests to assert reply endpoint usage and legacy ID translation behavior. |
| packages/apps/tests/test_activity_context.py | Adds coverage for reactive thread placement matrix (personal vs groupChat vs channel). |
| packages/apps/src/microsoft_teams/apps/utils/thread.py | Adds legacy threaded conversation ID parsing and deprecates to_threaded_conversation_id. |
| packages/apps/src/microsoft_teams/apps/utils/init.py | Keeps exporting deprecated threading helper with pyright suppression. |
| packages/apps/src/microsoft_teams/apps/routing/activity_context.py | Computes thread root from inbound metadata / legacy suffix and threads sends via new routing. |
| packages/apps/src/microsoft_teams/apps/app.py | Translates legacy suffixed IDs for proactive sends and adds explicit thread-root send path. |
| packages/apps/src/microsoft_teams/apps/activity_send.py | Routes non-targeted sends via reply endpoint when thread_root_id is provided. |
| packages/apps/src/microsoft_teams/apps/init.py | Maintains deprecated threading helper export with pyright suppression. |
| packages/api/tests/unit/test_empty_inbound_objects.py | Adds test ensuring inbound channelData.thread.id is typed and read-only. |
| packages/api/tests/unit/test_conversation_client.py | Updates unit tests to validate reply endpoint URL and payload semantics (no replyToId). |
| packages/api/src/microsoft_teams/api/models/channel_data/thread_info.py | Introduces ThreadInfo model for inbound thread metadata. |
| packages/api/src/microsoft_teams/api/models/channel_data/channel_data.py | Adds `ChannelData.thread: ThreadInfo |
| packages/api/src/microsoft_teams/api/models/channel_data/init.py | Exports ThreadInfo. |
| packages/api/src/microsoft_teams/api/clients/conversation/activity.py | Changes activities.reply(...) to POST to /activities/{activity_id} without replyToId in payload. |
| examples/interacting-with-messages/src/threading_handlers.py | Updates sample to use app.reply(...) and inbound thread metadata rather than legacy threaded IDs. |
| examples/interacting-with-messages/README.md | Updates sample documentation to reflect deprecations and new thread-manual behavior. |
Review details
- Files reviewed: 16/16 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a61da459-b09d-412b-9dd1-1310f6dfd42d
| return True | ||
|
|
||
|
|
||
| def _thread_reference(ctx: ActivityContext[MessageActivity]) -> tuple[str, str]: |
There was a problem hiding this comment.
should we consider offering this as apart of the SDK? vs having them write a helper fn
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a61da459-b09d-412b-9dd1-1310f6dfd42d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a61da459-b09d-412b-9dd1-1310f6dfd42d
Summary
POST /v3/conversations/{conversationId}/activities/{rootMessageId}while retaining L1 sends;messageid=conversation IDs internallychannelData.thread.idand apply the reactive personal/group-chat/channel scope matrixMessageActivityInput.add_quote()Validation
uv run poe checkuv run pyrightuv build --all-packages