/// <summary>
        /// Joins the call asynchronously.
        /// </summary>
        /// <param name="joinCallBody">The join call body.</param>
        /// <returns>The <see cref="ICall"/> that was requested to join.</returns>
        public async Task <ICall> JoinCallAsync(JoinCallController.JoinCallBody joinCallBody)
        {
            // A tracking id for logging purposes. Helps identify this call in logs.
            var correlationId = Guid.NewGuid();

            MeetingInfo meetingInfo;
            ChatInfo    chatInfo;

            if (!string.IsNullOrWhiteSpace(joinCallBody.MeetingId))
            {
                // Meeting id is a cloud-video-interop numeric meeting id.
                var onlineMeeting = await this.OnlineMeetings
                                    .GetOnlineMeetingAsync(joinCallBody.TenantId, joinCallBody.MeetingId, correlationId)
                                    .ConfigureAwait(false);

                meetingInfo = new OrganizerMeetingInfo {
                    Organizer = onlineMeeting.Participants.Organizer.Identity,
                };
                chatInfo = onlineMeeting.ChatInfo;
                //// meetingInfo.AllowConversationWithoutHost = joinCallBody.AllowConversationWithoutHost;
            }
            else
            {
                (chatInfo, meetingInfo) = JoinInfo.ParseJoinURL(joinCallBody.JoinURL);
            }

            var tenantId =
                joinCallBody.TenantId ??
                (meetingInfo as OrganizerMeetingInfo)?.Organizer.GetPrimaryIdentity()?.GetTenantId();
            ILocalMediaSession mediaSession = this.CreateLocalMediaSession();

            var joinParams = new JoinMeetingParameters(chatInfo, meetingInfo, mediaSession)
            {
                RemoveFromDefaultAudioRoutingGroup = joinCallBody.RemoveFromDefaultRoutingGroup,
                TenantId      = tenantId,
                CorrelationId = correlationId,
            };

            if (!string.IsNullOrWhiteSpace(joinCallBody.DisplayName))
            {
                // Teams client does not allow changing of ones own display name.
                // If display name is specified, we join as anonymous (guest) user
                // with the specified display name.  This will put bot into lobby
                // unless lobby bypass is disabled.
                joinParams.GuestIdentity = new Identity
                {
                    Id          = Guid.NewGuid().ToString(),
                    DisplayName = joinCallBody.DisplayName,
                };
            }

            var statefulCall = await this.Client.Calls().AddAsync(joinParams).ConfigureAwait(false);

            this.logger.Info($"Call creation complete: {statefulCall.Id}");
            return(statefulCall);
        }
示例#2
0
        /// <summary>
        /// Joins the call asynchronously.
        /// </summary>
        /// <param name="joinCallBody">The join call body.</param>
        /// <param name="incidentId">Incident Id.</param>
        /// <returns>The <see cref="ICall"/> that was requested to join.</returns>
        public async Task <ICall> JoinCallAsync(JoinCallRequestData joinCallBody, string incidentId = "")
        {
            // A tracking id for logging purposes. Helps identify this call in logs.
            var correlationId = string.IsNullOrEmpty(joinCallBody.CorrelationId) ? Guid.NewGuid() : new Guid(joinCallBody.CorrelationId);

            Microsoft.Graph.MeetingInfo meetingInfo;
            ChatInfo chatInfo;

            if (!string.IsNullOrWhiteSpace(joinCallBody.MeetingId))
            {
                // Meeting id is a cloud-video-interop numeric meeting id.
                var onlineMeeting = await this.OnlineMeetings
                                    .GetOnlineMeetingAsync(joinCallBody.TenantId, joinCallBody.MeetingId, correlationId)
                                    .ConfigureAwait(false);

                meetingInfo = new OrganizerMeetingInfo {
                    Organizer = onlineMeeting.Participants.Organizer.Identity,
                };
                meetingInfo.AllowConversationWithoutHost = joinCallBody.AllowConversationWithoutHost;
                chatInfo = onlineMeeting.ChatInfo;
            }
            else
            {
                (chatInfo, meetingInfo) = JoinInfo.ParseJoinURL(joinCallBody.JoinURL);
                meetingInfo.AllowConversationWithoutHost = joinCallBody.AllowConversationWithoutHost;
            }

            var tenantId =
                joinCallBody.TenantId ??
                (meetingInfo as OrganizerMeetingInfo)?.Organizer.GetPrimaryIdentity()?.GetTenantId();
            var mediaToPrefetch = new List <MediaInfo>();

            foreach (var m in this.MediaMap)
            {
                mediaToPrefetch.Add(m.Value.MediaInfo);
            }

            var joinParams = new JoinMeetingParameters(chatInfo, meetingInfo, new[] { Modality.Audio }, mediaToPrefetch)
            {
                RemoveFromDefaultAudioRoutingGroup = joinCallBody.RemoveFromDefaultRoutingGroup,
                TenantId      = tenantId,
                CorrelationId = correlationId,
            };

            var statefulCall = await this.Client.Calls().AddAsync(joinParams).ConfigureAwait(false);

            this.AddCallToHandlers(statefulCall, new IncidentCallContext(IncidentCallType.BotMeeting, incidentId));

            this.graphLogger.Info($"Join Call complete: {statefulCall.Id}");

            return(statefulCall);
        }
示例#3
0
        /// <summary>
        /// Parse Join URL into its components.
        /// </summary>
        /// <param name="joinURL">Join URL from Team's meeting body.</param>
        /// <returns>Parsed data.</returns>
        /// <exception cref="ArgumentException">Join URL cannot be null or empty: {joinURL} - joinURL</exception>
        /// <exception cref="ArgumentException">Join URL cannot be parsed: {joinURL} - joinURL</exception>
        /// <exception cref="ArgumentException">Join URL is invalid: missing Tid - joinURL</exception>
        public static (ChatInfo, MeetingInfo) ParseJoinURL(string joinURL)
        {
            if (string.IsNullOrEmpty(joinURL))
            {
                throw new ArgumentException($"Join URL cannot be null or empty: {joinURL}", nameof(joinURL));
            }

            var decodedURL = WebUtility.UrlDecode(joinURL);

            //// URL being needs to be in this format.
            //// https://teams.microsoft.com/l/meetup-join/19:[email protected]/1509579179399?context={"Tid":"72f988bf-86f1-41af-91ab-2d7cd011db47","Oid":"550fae72-d251-43ec-868c-373732c2704f","MessageId":"1536978844957"}

            var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
            var match = regex.Match(decodedURL);

            if (!match.Success)
            {
                throw new ArgumentException($"Join URL cannot be parsed: {joinURL}", nameof(joinURL));
            }

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
            {
                var ctxt = (Meeting) new DataContractJsonSerializer(typeof(Meeting)).ReadObject(stream);

                if (string.IsNullOrEmpty(ctxt.Tid))
                {
                    throw new ArgumentException("Join URL is invalid: missing Tid", nameof(joinURL));
                }

                var chatInfo = new ChatInfo
                {
                    ThreadId            = match.Groups["thread"].Value,
                    MessageId           = match.Groups["message"].Value,
                    ReplyChainMessageId = ctxt.MessageId,
                };

                var meetingInfo = new OrganizerMeetingInfo
                {
                    Organizer = new IdentitySet
                    {
                        User = new Identity {
                            Id = ctxt.Oid
                        },
                    },
                };
                meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);

                return(chatInfo, meetingInfo);
            }
        }
示例#4
0
        /// <summary>
        /// Joins the call asynchronously.
        /// </summary>
        /// <param name="joinCallBody">The join call body.</param>
        /// <param name="incidentId">Incident Id.</param>
        /// <returns>The <see cref="ICall"/> that was requested to join.</returns>
        public async Task <ICall> JoinCallAsync(JoinCallRequestData joinCallBody, string incidentId = "")
        {
            // A tracking id for logging purposes. Helps identify this call in logs.
            var scenarioId = string.IsNullOrEmpty(joinCallBody.ScenarioId) ? Guid.NewGuid() : new Guid(joinCallBody.ScenarioId);

            MeetingInfo meetingInfo;
            ChatInfo    chatInfo;

            if (!string.IsNullOrWhiteSpace(joinCallBody.VideoTeleconferenceId))
            {
                // Meeting id is a cloud-video-interop numeric meeting id.
                var onlineMeeting = await OnlineMeetings
                                    .GetOnlineMeetingAsync(joinCallBody.TenantId, joinCallBody.VideoTeleconferenceId, scenarioId)
                                    .ConfigureAwait(false);

                meetingInfo = new OrganizerMeetingInfo {
                    Organizer = onlineMeeting.Participants.Organizer.Identity,
                };
                chatInfo = onlineMeeting.ChatInfo;
            }
            else
            {
                (chatInfo, meetingInfo) = JoinInfo.ParseJoinURL(joinCallBody.JoinUrl);
            }

            var tenantId =
                joinCallBody.TenantId ??
                (meetingInfo as OrganizerMeetingInfo)?.Organizer.GetPrimaryIdentity()?.GetTenantId();
            var mediaToPrefetch = new List <MediaInfo>();

            foreach (var m in MediaMap)
            {
                mediaToPrefetch.Add(m.Value.MediaInfo);
            }

            var joinParams = new JoinMeetingParameters(chatInfo, meetingInfo, new[] { Modality.Audio }, mediaToPrefetch)
            {
                TenantId = tenantId,
            };

            var statefulCall = await Client.Calls().AddAsync(joinParams, scenarioId).ConfigureAwait(false);

            AddCallToHandlers(statefulCall, new IncidentCallContext(IncidentCallType.BotMeeting, incidentId));

            graphLogger.Info($"Join Call complete: {statefulCall.Id}");

            return(statefulCall);
        }
        /// <summary>
        /// Parse Join URL into its components.
        /// </summary>
        /// <param name="joinURL">Join URL from Team's meeting body.</param>
        /// <returns>Parsed data.</returns>
        public static (ChatInfo, MeetingInfo) ParseJoinURL(string joinURL)
        {
            var decodedURL = WebUtility.UrlDecode(joinURL);

            //// URL being needs to be in this format.
            //// https://teams.microsoft.com/l/meetup-join/19:[email protected]/1509579179399?context={"Tid":"72f988bf-86f1-41af-91ab-2d7cd011db47","Oid":"550fae72-d251-43ec-868c-373732c2704f","MessageId":"1536978844957"}
            //// https://teams.microsoft.com/l/meetup-join/19:meeting_MDQzYmJlMDctMWJiZS00OGExLTlmYjUtZTczNzVhZGM1OTQx@thread.v2/0?context={"Tid":"c80f38d3-c04c-49bf-a48b-9d99278d4ac6","Oid":"782f076f-f6f9-4bff-9673-ea1997283e9c"}

            var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
            var match = regex.Match(decodedURL);

            if (!match.Success)
            {
                throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
            }

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
            {
                var ctxt     = (Context) new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
                var chatInfo = new ChatInfo
                {
                    ThreadId            = match.Groups["thread"].Value,
                    MessageId           = match.Groups["message"].Value,
                    ReplyChainMessageId = ctxt.MessageId,
                };

                var meetingInfo = new OrganizerMeetingInfo
                {
                    Organizer = new IdentitySet
                    {
                        User = new Identity {
                            Id = ctxt.Oid
                        },
                    },
                };
                meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);

                return(chatInfo, meetingInfo);
            }
        }
示例#6
0
        /// <summary>
        /// Parse Join URL into its components.
        /// </summary>
        /// <param name="joinURL">Join URL from Team's meeting body.</param>
        /// <returns>Parsed data.</returns>
        public static (ChatInfo, MeetingInfo) ParseJoinURL(string joinURL)
        {
            var decodedURL = WebUtility.UrlDecode(joinURL);

            //// URL being needs to be in this format.
            //// https://teams.microsoft.com/l/meetup-join/19:[email protected]/1509579179399?context={"Tid":"72f988bf-86f1-41af-91ab-2d7cd011db47","Oid":"550fae72-d251-43ec-868c-373732c2704f","MessageId":"1536978844957"}
            //// https://teams.microsoft.com/l/meetup-join/19%3a35a466cd6e2d4efda6df1ab5456ec170%40thread.skype/1595884031503?context=%7b%22Tid%22%3a%2209c8b911-b715-46dc-a14e-a547989bad27%22%2c%22Oid%22%3a%2287bfe48d-53e2-42c9-af53-50c03ae06611%22%7d
            var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
            var match = regex.Match(decodedURL);

            if (!match.Success)
            {
                throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
            }

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
            {
                var ctxt     = (Context) new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
                var chatInfo = new ChatInfo
                {
                    ThreadId            = match.Groups["thread"].Value,
                    MessageId           = match.Groups["message"].Value,
                    ReplyChainMessageId = ctxt.MessageId,
                };

                var meetingInfo = new OrganizerMeetingInfo
                {
                    Organizer = new IdentitySet
                    {
                        User = new Identity {
                            Id = ctxt.Oid
                        },
                    },
                };
                meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);

                return(chatInfo, meetingInfo);
            }
        }
示例#7
0
        public static (ChatInfo, MeetingInfo) ParseJoinURL(string joinURL)
        {
            var decodedURL = WebUtility.UrlDecode(joinURL);

            var regex = new Regex("https://teams\\.microsoft\\.com.*/(?<thread>[^/]+)/(?<message>[^/]+)\\?context=(?<context>{.*})");
            var match = regex.Match(decodedURL);

            if (!match.Success)
            {
                throw new ArgumentException($"Join URL cannot be parsed: {joinURL}.", nameof(joinURL));
            }

            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(match.Groups["context"].Value)))
            {
                var ctxt     = (Context) new DataContractJsonSerializer(typeof(Context)).ReadObject(stream);
                var chatInfo = new ChatInfo
                {
                    ThreadId            = match.Groups["thread"].Value,
                    MessageId           = match.Groups["message"].Value,
                    ReplyChainMessageId = ctxt.MessageId,
                };

                var meetingInfo = new OrganizerMeetingInfo
                {
                    Organizer = new IdentitySet
                    {
                        User = new Identity {
                            Id = ctxt.Oid
                        },
                    },
                };
                meetingInfo.Organizer.User.SetTenantId(ctxt.Tid);

                return(chatInfo, meetingInfo);
            }
        }