Add support for short Teams meeting URLs in ParseJoinURL#858
Add support for short Teams meeting URLs in ParseJoinURL#858samarthasthana wants to merge 4 commits intomasterfrom
Conversation
Teams now generates short meeting URLs in the format: https://teams.microsoft.com/meet/<meetingId>?p=<passcode> The existing ParseJoinURL only supports the legacy long format with context parameters. This change adds a fallback to parse short URLs using JoinMeetingIdMeetingInfo instead of OrganizerMeetingInfo. For short URLs, the tenant ID cannot be inferred from the URL and must be provided separately via the JoinCallBody.TenantId property (added to models that lacked it). Changes: - Updated ParseJoinURL in all 6 JoinInfo implementations to detect and parse both long and short URL formats - Added TenantId property to JoinCallBody in EchoBot, PsiBot, and RecordingBot models - Updated callers to safely handle both OrganizerMeetingInfo and JoinMeetingIdMeetingInfo return types using null-safe tenant extraction with JoinCallBody.TenantId fallback Fixes #829
There was a problem hiding this comment.
Pull request overview
Adds support across multiple samples/libs for parsing the newer short Teams meeting join URL format (https://teams.microsoft.com/meet/<meetingId>?p=<passcode>) by falling back to JoinMeetingIdMeetingInfo, and introduces a TenantId field in join request models where the tenant can’t be inferred from the URL.
Changes:
- Updated
ParseJoinURL/join-url parsing in 6JoinInfoimplementations to handle both long and short URL formats. - Added
TenantIdtoJoinCallBodymodels (EchoBot, PsiBot, RecordingBot) to support short URLs. - Updated join-call flows to extract tenant id from
JoinCallBody.TenantIdwith fallback toOrganizerMeetingInfo.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| Samples/V1.0Samples/AksSamples(Deprecated)/teams-recording-bot/src/RecordingBot.Services/Util/JoinInfo.cs | Adds short-URL parsing fallback via JoinMeetingIdMeetingInfo. |
| Samples/V1.0Samples/AksSamples(Deprecated)/teams-recording-bot/src/RecordingBot.Services/Bot/BotService.cs | Uses JoinCallBody.TenantId fallback when deriving tenant id. |
| Samples/V1.0Samples/AksSamples(Deprecated)/teams-recording-bot/src/RecordingBot.Model/Models/JoinCallBody.cs | Adds TenantId to the join request model. |
| Samples/PublicSamples/PsiBot/PsiBot/PsiBot.Service/Bot/BotService.cs | Adds short-URL parsing and tenant-id fallback for joining calls. |
| Samples/PublicSamples/PsiBot/PsiBot/PsiBot.Model/Models/JoinCallBody.cs | Adds TenantId to the join request model. |
| Samples/PublicSamples/EchoBot/src/EchoBot/Models/JoinInfo.cs | Adds short-URL parsing fallback via JoinMeetingIdMeetingInfo. |
| Samples/PublicSamples/EchoBot/src/EchoBot/Models/JoinCallBody.cs | Adds TenantId to the join request model. |
| Samples/PublicSamples/EchoBot/src/EchoBot/Bot/BotService.cs | Uses JoinCallBody.TenantId fallback when deriving tenant id. |
| Samples/Common/Sample.Common/Meetings/JoinInfo.cs | Adds short-URL parsing fallback in shared common library. |
| Samples/Common/Sample.Common.V1/Meetings/JoinInfo.cs | Adds short-URL parsing fallback in V1 common library. |
| Samples/Common/Sample.Common.Beta/Meetings/JoinInfo.cs | Adds short-URL parsing fallback in Beta common library. |
Comments suppressed due to low confidence (13)
Samples/PublicSamples/PsiBot/PsiBot/PsiBot.Service/Bot/BotService.cs:382
- The short URL regex captures the passcode with
.+, which will include any trailing query params if present. Prefer parsing the URL query (extractpexplicitly) or constrain the passcode match to stop at&/#.
//// Short URL format: https://teams.microsoft.com/meet/<meetingId>?p=<passcode>
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
if (shortUrlMatch.Success)
Samples/Common/Sample.Common/Meetings/JoinInfo.cs:35
- The long URL regex uses a greedy
{.*}capture for the context JSON. If any additional query parameters appear aftercontext=..., they’ll be included in the JSON string and deserialization will fail. Consider making the match non-greedy or parsing thecontextquery parameter viaUri/query parsing instead of regex.
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
var match = regex.Match(decodedURL);
Samples/Common/Sample.Common/Meetings/JoinInfo.cs:64
- The short URL regex captures the passcode with
.+, which will include any trailing query params (e.g.&foo=bar) if present. Prefer parsing thepquery parameter explicitly (Uri/query parsing) or constrain the match to stop at&/#.
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
Samples/Common/Sample.Common.V1/Meetings/JoinInfo.cs:35
- The long URL regex uses a greedy
{.*}capture for the context JSON; extra query parameters aftercontext=...would be captured and break JSON deserialization. Consider making the context capture non-greedy or parsing the query string to extractcontextsafely.
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
var match = regex.Match(decodedURL);
Samples/Common/Sample.Common.V1/Meetings/JoinInfo.cs:65
- The short URL regex captures the passcode with
.+, which will include any trailing query params if present. Prefer parsing thepquery parameter explicitly or constrain the match to stop at&/#.
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
if (shortUrlMatch.Success)
Samples/Common/Sample.Common.Beta/Meetings/JoinInfo.cs:65
- The short URL regex captures the passcode with
.+, which will include any trailing query params if present. Prefer parsing thepquery parameter explicitly or constrain the match to stop at&/#.
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
if (shortUrlMatch.Success)
Samples/V1.0Samples/AksSamples(Deprecated)/teams-recording-bot/src/RecordingBot.Services/Util/JoinInfo.cs:85
- The short URL regex captures the passcode with
.+, which will also include any additional query parameters (e.g.&foo=bar) if Teams adds them later. Prefer parsing viaUri+ query parsing, or at least restrict the capture to stop at&/#so Passcode is extracted reliably.
//// Short URL format: https://teams.microsoft.com/meet/<meetingId>?p=<passcode>
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
Samples/PublicSamples/EchoBot/src/EchoBot/Models/JoinInfo.cs:83
- The short URL regex captures the passcode with
.+, which will include any trailing query params if present. Prefer parsing the URL query (e.g. extractpexplicitly) or constrain the passcode match to stop at&/#so Passcode is correct.
//// Short URL format: https://teams.microsoft.com/meet/<meetingId>?p=<passcode>
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
if (shortUrlMatch.Success)
Samples/V1.0Samples/AksSamples(Deprecated)/teams-recording-bot/src/RecordingBot.Services/Util/JoinInfo.cs:51
- The long URL regex captures the
contextJSON with a greedy{.*}. If the URL ever contains additional query parameters aftercontext=..., the capture will include them and JSON deserialization will fail. Consider making the match non-greedy or extractingcontextviaUri/query parsing instead of regex.
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
var match = regex.Match(decodedURL);
Samples/Common/Sample.Common/Meetings/JoinInfo.cs:56
- Long URL parsing sets the organizer tenant id from
ctxt.Tidbut doesn’t validate it before calling SetTenantId. IfTidis missing/empty, this will either set an invalid value or throw from inside the SDK. Consider validating required fields (Tid/Oid) and throwing a clearer ArgumentException, as done in other JoinInfo implementations in this PR.
var meetingInfo = new OrganizerMeetingInfo
{
Organizer = new IdentitySet
{
User = new Identity { Id = ctxt.Oid },
},
};
meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
Samples/Common/Sample.Common.Beta/Meetings/JoinInfo.cs:35
- The long URL regex uses a greedy
{.*}capture for the context JSON; extra query parameters aftercontext=...would be captured and break JSON deserialization. Consider making the context capture non-greedy or extractingcontextfrom the query string instead of regex.
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
var match = regex.Match(decodedURL);
Samples/PublicSamples/EchoBot/src/EchoBot/Models/JoinInfo.cs:49
- The long URL regex captures the
contextJSON with a greedy{.*}. If additional query parameters appear aftercontext=..., the capture will include them and JSON deserialization will fail. Consider using non-greedy matching or parsing thecontextquery parameter explicitly viaUri/query parsing.
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
var match = regex.Match(decodedURL);
if (match.Success)
Samples/PublicSamples/PsiBot/PsiBot/PsiBot.Service/Bot/BotService.cs:348
- The long URL regex captures the
contextJSON with a greedy{.*}. If additional query parameters appear aftercontext=..., the capture will include them and JSON deserialization will fail. Consider extracting thecontextquery parameter viaUri/query parsing or using a non-greedy match.
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
var match = regex.Match(decodedURL);
if (match.Success)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var meetingInfo = new JoinMeetingIdMeetingInfo | ||
| { | ||
| Organizer = new IdentitySet | ||
| { | ||
| User = new Identity { Id = ctxt.Oid }, | ||
| }, | ||
| JoinMeetingId = shortUrlMatch.Groups["meetingId"].Value, | ||
| Passcode = shortUrlMatch.Groups["passcode"].Value, | ||
| }; | ||
| meetingInfo.Organizer.User.SetTenantId(ctxt.Tid); | ||
|
|
||
| return (chatInfo, meetingInfo); | ||
| return (new ChatInfo(), meetingInfo); | ||
| } |
There was a problem hiding this comment.
For short meeting URLs, this returns an empty ChatInfo. In EchoBot, JoinCallAsync later uses joinParams.ChatInfo.ThreadId as a dictionary key; if ThreadId is null/empty this will throw (ArgumentNullException) or cause collisions. Consider returning a non-null ThreadId surrogate (if supported) or adjusting the bot’s call tracking logic for JoinMeetingIdMeetingInfo joins.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
...mples/AksSamples(Deprecated)/teams-recording-bot/src/RecordingBot.Services/Bot/BotService.cs
Show resolved
Hide resolved
…ice.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@samarthasthana I've opened a new pull request, #859, to work on those changes. Once the pull request is ready, I'll request review from you. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…/src/RecordingBot.Services/Bot/BotService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Teams now generates short meeting URLs in the format: https://teams.microsoft.com/meet/?p=
The existing ParseJoinURL only supports the legacy long format with context parameters. This change adds a fallback to parse short URLs using JoinMeetingIdMeetingInfo instead of OrganizerMeetingInfo.
For short URLs, the tenant ID cannot be inferred from the URL and must be provided separately via the JoinCallBody.TenantId property (added to models that lacked it).
Changes:
Fixes #829