Skip to content

Commit 0cf61b5

Browse files
author
Samarth Asthana
committed
Add support for short Teams meeting URLs in ParseJoinURL
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
1 parent 3a4ed77 commit 0cf61b5

11 files changed

Lines changed: 249 additions & 131 deletions

File tree

Samples/Common/Sample.Common.Beta/Meetings/JoinInfo.cs

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,52 @@ public static (ChatInfo, MeetingInfo) ParseJoinURL(string joinURL)
2828
{
2929
var decodedURL = WebUtility.UrlDecode(joinURL);
3030

31-
//// URL being needs to be in this format.
31+
//// Long URL format:
3232
//// https://teams.microsoft.com/l/meetup-join/19:cd9ce3da56624fe69c9d7cd026f9126d@thread.skype/1509579179399?context={"Tid":"72f988bf-86f1-41af-91ab-2d7cd011db47","Oid":"550fae72-d251-43ec-868c-373732c2704f","MessageId":"1536978844957"}
3333

3434
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
3535
var match = regex.Match(decodedURL);
36-
if (!match.Success)
36+
if (match.Success)
3737
{
38-
throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
38+
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
39+
{
40+
var ctxt = (Context)new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
41+
var chatInfo = new ChatInfo
42+
{
43+
ThreadId = match.Groups["thread"].Value,
44+
MessageId = match.Groups["message"].Value,
45+
ReplyChainMessageId = ctxt.MessageId,
46+
};
47+
48+
var meetingInfo = new OrganizerMeetingInfo
49+
{
50+
Organizer = new IdentitySet
51+
{
52+
User = new Identity { Id = ctxt.Oid },
53+
},
54+
};
55+
56+
// meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
57+
return (chatInfo, meetingInfo);
58+
}
3959
}
4060

41-
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
61+
//// Short URL format:
62+
//// https://teams.microsoft.com/meet/2414128301281?p=NzhDLMLT8b07DrkwkE
63+
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
64+
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
65+
if (shortUrlMatch.Success)
4266
{
43-
var ctxt = (Context)new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
44-
var chatInfo = new ChatInfo
67+
var meetingInfo = new JoinMeetingIdMeetingInfo
4568
{
46-
ThreadId = match.Groups["thread"].Value,
47-
MessageId = match.Groups["message"].Value,
48-
ReplyChainMessageId = ctxt.MessageId,
69+
JoinMeetingId = shortUrlMatch.Groups["meetingId"].Value,
70+
Passcode = shortUrlMatch.Groups["passcode"].Value,
4971
};
5072

51-
var meetingInfo = new OrganizerMeetingInfo
52-
{
53-
Organizer = new IdentitySet
54-
{
55-
User = new Identity { Id = ctxt.Oid },
56-
},
57-
};
58-
59-
// meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
60-
return (chatInfo, meetingInfo);
73+
return (new ChatInfo(), meetingInfo);
6174
}
75+
76+
throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
6277
}
6378

6479
/// <summary>

Samples/Common/Sample.Common.V1/Meetings/JoinInfo.cs

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,52 @@ public static (ChatInfo, MeetingInfo) ParseJoinURL(string joinURL)
2828
{
2929
var decodedURL = WebUtility.UrlDecode(joinURL);
3030

31-
//// URL being needs to be in this format.
31+
//// Long URL format:
3232
//// https://teams.microsoft.com/l/meetup-join/19:cd9ce3da56624fe69c9d7cd026f9126d@thread.skype/1509579179399?context={"Tid":"72f988bf-86f1-41af-91ab-2d7cd011db47","Oid":"550fae72-d251-43ec-868c-373732c2704f","MessageId":"1536978844957"}
3333

3434
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
3535
var match = regex.Match(decodedURL);
36-
if (!match.Success)
36+
if (match.Success)
3737
{
38-
throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
38+
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
39+
{
40+
var ctxt = (Context)new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
41+
var chatInfo = new ChatInfo
42+
{
43+
ThreadId = match.Groups["thread"].Value,
44+
MessageId = match.Groups["message"].Value,
45+
ReplyChainMessageId = ctxt.MessageId,
46+
};
47+
48+
var meetingInfo = new OrganizerMeetingInfo
49+
{
50+
Organizer = new IdentitySet
51+
{
52+
User = new Identity { Id = ctxt.Oid },
53+
},
54+
};
55+
56+
// meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
57+
return (chatInfo, meetingInfo);
58+
}
3959
}
4060

41-
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
61+
//// Short URL format:
62+
//// https://teams.microsoft.com/meet/2414128301281?p=NzhDLMLT8b07DrkwkE
63+
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
64+
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
65+
if (shortUrlMatch.Success)
4266
{
43-
var ctxt = (Context)new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
44-
var chatInfo = new ChatInfo
67+
var meetingInfo = new JoinMeetingIdMeetingInfo
4568
{
46-
ThreadId = match.Groups["thread"].Value,
47-
MessageId = match.Groups["message"].Value,
48-
ReplyChainMessageId = ctxt.MessageId,
69+
JoinMeetingId = shortUrlMatch.Groups["meetingId"].Value,
70+
Passcode = shortUrlMatch.Groups["passcode"].Value,
4971
};
5072

51-
var meetingInfo = new OrganizerMeetingInfo
52-
{
53-
Organizer = new IdentitySet
54-
{
55-
User = new Identity { Id = ctxt.Oid },
56-
},
57-
};
58-
59-
// meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
60-
return (chatInfo, meetingInfo);
73+
return (new ChatInfo(), meetingInfo);
6174
}
75+
76+
throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
6277
}
6378

6479
/// <summary>

Samples/Common/Sample.Common/Meetings/JoinInfo.cs

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,52 @@ public static (ChatInfo, MeetingInfo) ParseJoinURL(string joinURL)
2828
{
2929
var decodedURL = WebUtility.UrlDecode(joinURL);
3030

31-
//// URL being needs to be in this format.
31+
//// Long URL format:
3232
//// https://teams.microsoft.com/l/meetup-join/19:cd9ce3da56624fe69c9d7cd026f9126d@thread.skype/1509579179399?context={"Tid":"72f988bf-86f1-41af-91ab-2d7cd011db47","Oid":"550fae72-d251-43ec-868c-373732c2704f","MessageId":"1536978844957"}
3333

3434
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
3535
var match = regex.Match(decodedURL);
36-
if (!match.Success)
36+
if (match.Success)
3737
{
38-
throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
38+
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
39+
{
40+
var ctxt = (Context)new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
41+
var chatInfo = new ChatInfo
42+
{
43+
ThreadId = match.Groups["thread"].Value,
44+
MessageId = match.Groups["message"].Value,
45+
ReplyChainMessageId = ctxt.MessageId,
46+
};
47+
48+
var meetingInfo = new OrganizerMeetingInfo
49+
{
50+
Organizer = new IdentitySet
51+
{
52+
User = new Identity { Id = ctxt.Oid },
53+
},
54+
};
55+
meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
56+
57+
return (chatInfo, meetingInfo);
58+
}
3959
}
4060

41-
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
61+
//// Short URL format:
62+
//// https://teams.microsoft.com/meet/2414128301281?p=NzhDLMLT8b07DrkwkE
63+
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
64+
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
65+
if (shortUrlMatch.Success)
4266
{
43-
var ctxt = (Context)new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
44-
var chatInfo = new ChatInfo
67+
var meetingInfo = new JoinMeetingIdMeetingInfo
4568
{
46-
ThreadId = match.Groups["thread"].Value,
47-
MessageId = match.Groups["message"].Value,
48-
ReplyChainMessageId = ctxt.MessageId,
69+
JoinMeetingId = shortUrlMatch.Groups["meetingId"].Value,
70+
Passcode = shortUrlMatch.Groups["passcode"].Value,
4971
};
5072

51-
var meetingInfo = new OrganizerMeetingInfo
52-
{
53-
Organizer = new IdentitySet
54-
{
55-
User = new Identity { Id = ctxt.Oid },
56-
},
57-
};
58-
meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
59-
60-
return (chatInfo, meetingInfo);
73+
return (new ChatInfo(), meetingInfo);
6174
}
75+
76+
throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
6277
}
6378

6479
/// <summary>

Samples/PublicSamples/EchoBot/src/EchoBot/Bot/BotService.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ public async Task<ICall> JoinCallAsync(JoinCallBody joinCallBody)
194194

195195
var (chatInfo, meetingInfo) = JoinInfo.ParseJoinURL(joinCallBody.JoinUrl);
196196

197-
var tenantId = (meetingInfo as OrganizerMeetingInfo).Organizer.GetPrimaryIdentity().GetTenantId();
197+
var tenantId =
198+
joinCallBody.TenantId ??
199+
(meetingInfo as OrganizerMeetingInfo)?.Organizer.GetPrimaryIdentity()?.GetTenantId();
198200
var mediaSession = this.CreateLocalMediaSession();
199201

200202
var joinParams = new JoinMeetingParameters(chatInfo, meetingInfo, mediaSession)

Samples/PublicSamples/EchoBot/src/EchoBot/Models/JoinCallBody.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ public class JoinCallBody
2424
/// <value>The join URL.</value>
2525
public string JoinUrl { get; set; }
2626

27+
/// <summary>
28+
/// Gets or sets the tenant id.
29+
/// Required when using short meeting URLs (e.g. https://teams.microsoft.com/meet/...).
30+
/// The tenant id cannot be inferred from short URLs and must be provided separately.
31+
/// </summary>
32+
/// <value>The tenant id.</value>
33+
public string? TenantId { get; set; }
34+
2735
/// <summary>
2836
/// Gets or sets the display name.
2937
/// Teams client does not allow changing of ones own display name.

Samples/PublicSamples/EchoBot/src/EchoBot/Models/JoinInfo.cs

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,40 +43,55 @@ public static (ChatInfo, MeetingInfo) ParseJoinURL(string joinURL)
4343

4444
var decodedURL = WebUtility.UrlDecode(joinURL);
4545

46+
//// Long URL format:
4647
var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
4748
var match = regex.Match(decodedURL);
48-
if (!match.Success)
49+
if (match.Success)
4950
{
50-
throw new ArgumentException($"Join URL cannot be parsed: {joinURL}", nameof(joinURL));
51-
}
51+
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
52+
{
53+
var ctxt = (Meeting)new DataContractJsonSerializer(typeof(Meeting)).ReadObject(stream);
5254

53-
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
54-
{
55-
var ctxt = (Meeting)new DataContractJsonSerializer(typeof(Meeting)).ReadObject(stream);
55+
if (string.IsNullOrEmpty(ctxt.Tid))
56+
{
57+
throw new ArgumentException("Join URL is invalid: missing Tid", nameof(joinURL));
58+
}
5659

57-
if (string.IsNullOrEmpty(ctxt.Tid))
58-
{
59-
throw new ArgumentException("Join URL is invalid: missing Tid", nameof(joinURL));
60-
}
60+
var chatInfo = new ChatInfo
61+
{
62+
ThreadId = match.Groups["thread"].Value,
63+
MessageId = match.Groups["message"].Value,
64+
ReplyChainMessageId = ctxt.MessageId,
65+
};
6166

62-
var chatInfo = new ChatInfo
63-
{
64-
ThreadId = match.Groups["thread"].Value,
65-
MessageId = match.Groups["message"].Value,
66-
ReplyChainMessageId = ctxt.MessageId,
67-
};
67+
var meetingInfo = new OrganizerMeetingInfo
68+
{
69+
Organizer = new IdentitySet
70+
{
71+
User = new Identity { Id = ctxt.Oid },
72+
},
73+
};
74+
meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
75+
76+
return (chatInfo, meetingInfo);
77+
}
78+
}
6879

69-
var meetingInfo = new OrganizerMeetingInfo
80+
//// Short URL format: https://teams.microsoft.com/meet/<meetingId>?p=<passcode>
81+
var shortUrlRegex = new Regex("https://teams\\.microsoft\\.com/meet/(?<meetingId>[^?]+)\\?p=(?<passcode>.+)");
82+
var shortUrlMatch = shortUrlRegex.Match(decodedURL);
83+
if (shortUrlMatch.Success)
84+
{
85+
var meetingInfo = new JoinMeetingIdMeetingInfo
7086
{
71-
Organizer = new IdentitySet
72-
{
73-
User = new Identity { Id = ctxt.Oid },
74-
},
87+
JoinMeetingId = shortUrlMatch.Groups["meetingId"].Value,
88+
Passcode = shortUrlMatch.Groups["passcode"].Value,
7589
};
76-
meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);
7790

78-
return (chatInfo, meetingInfo);
91+
return (new ChatInfo(), meetingInfo);
7992
}
93+
94+
throw new ArgumentException($"Join URL cannot be parsed: {joinURL}", nameof(joinURL));
8095
}
8196
}
8297
}

Samples/PublicSamples/PsiBot/PsiBot/PsiBot.Model/Models/JoinCallBody.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ public class JoinCallBody
1616
/// <value>The join URL.</value>
1717
public string JoinURL { get; set; }
1818

19+
/// <summary>
20+
/// Gets or sets the tenant id.
21+
/// Required when using short meeting URLs (e.g. https://teams.microsoft.com/meet/...).
22+
/// The tenant id cannot be inferred from short URLs and must be provided separately.
23+
/// </summary>
24+
/// <value>The tenant id.</value>
25+
public string TenantId { get; set; }
26+
1927
/// <summary>
2028
/// Gets or sets the display name.
2129
/// Teams client does not allow changing of ones own display name.

0 commit comments

Comments
 (0)