private HashSet <TeamsUserWithSource> CollectUsersFromChatAndMessages(Chat chat, List <Message> messages)
        {
            var users         = new HashSet <TeamsUserWithSource>();
            var creatorUserId = TeamsParticipant.FromFirstValid(chat.creator);

            if (creatorUserId.IsValid)
            {
                users.Add(new TeamsUserWithSource(creatorUserId, TeamsUserSource.ChatCreator));
            }
            foreach (var m in messages)
            {
                // "https://emea.ng.msg.teams.microsoft.com/v1/users/ME/contacts/8:orgid:00000000-0000-beef-0000-000000000000"
                // note: for system messages like user leave and user join this is the contact url for the chat id
                var userId = (TeamsParticipant)m.from;
                if (userId.IsValid && !(userId.Kind == ParticipantKind.TeamsChat))
                {
                    users.Add(new TeamsUserWithSource(userId, TeamsUserSource.SenderOfMessageInChat));
                }

                if (m.properties?.mentions != null)
                {
                    foreach (var mention in m.properties.mentions)
                    {
                        users.Add(new TeamsUserWithSource((TeamsParticipant)mention.mri, TeamsUserSource.MentionedInChat));
                    }
                }
            }

            chat.members?.ForEach(member => users.Add(new TeamsUserWithSource((TeamsParticipant)member.mri, TeamsUserSource.OfficialChatMember)));
            return(users);
        }
        public TeamsUserTokenContext GetOrCreateUserTokenContext(TeamsParticipant userId)
        {
#pragma warning disable CS8603                         // Possible null reference return.
            return(GetUserTokenContext(userId, true)); // cannot be null

#pragma warning restore CS8603                         // Possible null reference return.
        }
 public ProcessedTeamsUser(TeamsDataContext dataContext, TeamsParticipant userId, TeamsUser?originalUser, TeamsUserState state)
 {
     OriginalUser = originalUser;
     UserId       = userId;
     DataContext  = dataContext;
     State        = state;
 }
 public TeamsTokenInfo(TeamsParticipant userId, TeamsTokenType tokenType, string tokenString, string authHeader, DateTime validFromUtc, DateTime validToUtc)
 {
     UserId       = userId;
     TokenType    = tokenType;
     TokenString  = tokenString;
     AuthHeader   = authHeader;
     ValidFromUtc = validFromUtc;
     ValidToUtc   = validToUtc;
 }
        public TeamsUserTokenContext?GetUserTokenContext(TeamsParticipant userId, bool createIfNotExisting)
        {
            if (!userTokenContexts.TryGetValue(userId, out var context) && createIfNotExisting)
            {
                context = new TeamsUserTokenContext(userId);
                userTokenContexts.Add(userId, context);
            }

            return(context);
        }
        public TeamsTokenInfo?GetTokenForIdentity(TeamsParticipant userId, string url)
        {
            var context = GetOrCreateUserTokenContext(userId);

            if (ApiTokenType.TryGetValue(url, out var tokenType))
            {
                return(context[tokenType]);
            }
            else
            {
                throw new TeasmCompanionException("Handle the case where we need user-dependent hosts");
            }
        }
示例#7
0
        public async Task <IEnumerable <ProcessedTenant>?> GetTenantsAsync(TeamsParticipant userId)
        {
            var userContext = tokenRetriever.GetOrCreateUserTokenContext(userId);
            var tokenType   = TeamsTokenType.MyTenantsAuthHeader;
            var tokenInfo   = userContext[tokenType];

            if (tokenInfo == null)
            {
                logger.Debug("Cannot get tenants for user {Mri}, no token of type {TokenType} present", userId.ToString().Truncate(Constants.UserIdLogLength), tokenType);
                return(null);
            }

            return(await GetTenantsAsync(tokenInfo));
        }
 public ProcessedUser(TeamsParticipant userId, MyProperties props)
 {
     Properties = props;
     UserId     = userId;
 }
 public TeamsUserWithSource(TeamsParticipant user, TeamsUserSource userSource)
 {
     User       = user;
     UserSource = userSource;
 }
 public ProcessedTeamsUser(TeamsDataContext ctx, TeamsParticipant userId)
 {
     UserId      = userId;
     DataContext = ctx;
 }
 public TeamsUserTokenContext(TeamsParticipant userId)
 {
     UserId = userId;
 }
 public static string? Truncate(this TeamsParticipant userId, int length, bool dotDotDot = false)
 {
     return ((string?)userId)?.Truncate(length, dotDotDot);
 }
示例#13
0
        protected async Task GenerateTextContentExtractUsersAndUpdateSubject()
        {
            if (ctx == null || !ctx.HasValue)
            {
                return;
            }
            var nonNullContext = ctx.Value;

            try {
                var textContent = new StringBuilder();
                if (Messagetype == MessageType.RichText_Html)
                {
                    HtmlContent = Internal_Content;
                }
                else
                if (Messagetype == MessageType.ThreadActivity_AddMember ||
                    (Messagetype == MessageType.ThreadActivity_MemberJoined && Internal_Content.StartsWith("<addmember>"))        // there seem to be (old?) addmember messages that come with the wrong message type of ThreadActivity_MemberJoined... handle this here
                    )
                {
                    XmlDocument doc = new XmlDocument();
                    // need to force single XML values into array type since we know it also can be an array
                    doc.LoadXml($"<root xmlns:json='http://james.newtonking.com/projects/json'>{Internal_Content.Replace("<target", "<target json:Array='true'").Replace("<detailedtargetinfo", "<detailedtargetinfo json:Array='true'") ?? ""}</root>");
                    var json = JsonConvert.SerializeXmlNode(doc);
                    var data = JsonUtils.DeserializeObject <ThreadActivityAddMemberWrapper>(logger, json);

                    await Task.WhenAll(data.root.addmember.detailedtargetinfo?.Select(targetInfo => teamsUserRegistry.RegisterDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)targetInfo.id, targetInfo.friendlyName, OriginalArrivalTime)) ?? new List <Task>());

                    if (!string.IsNullOrWhiteSpace(data.root.addmember.initiator))
                    {
                        await teamsUserRegistry.RegisterDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)data.root.addmember.initiator, data.root.addmember.detailedinitiatorinfo?.friendlyName, OriginalArrivalTime);
                    }

                    // TODO: process alternate user display name
                    var memberNames = await Task.WhenAll(data.root.addmember.target.Select(t => teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)t)));

                    MessageSubject = $"✈️ {await teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)data.root.addmember.initiator)} added: " + string.Join(", ", memberNames);
                    textContent.Append(MessageSubject);
                }
                else
                if (Messagetype == MessageType.ThreadActivity_DeleteMember)
                {
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml($"<root xmlns:json='http://james.newtonking.com/projects/json'>{Internal_Content.Replace("<target", "<target json:Array='true'").Replace("<detailedtargetinfo", "<detailedtargetinfo json:Array='true'")}</root>");
                    var json = JsonConvert.SerializeXmlNode(doc);
                    var data = JsonUtils.DeserializeObject <ThreadActivityDeleteMemberWrapper>(logger, json);

                    await Task.WhenAll(data.root.deletemember.detailedtargetinfo?.Select(targetInfo => teamsUserRegistry.RegisterDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)targetInfo.id, targetInfo.friendlyName, OriginalArrivalTime)) ?? new List <Task>());

                    if (!string.IsNullOrWhiteSpace(data.root.deletemember.initiator))
                    {
                        await teamsUserRegistry.RegisterDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)data.root.deletemember.initiator, data.root.deletemember.detailedinitiatorinfo?.friendlyName, OriginalArrivalTime);
                    }

                    var memberNames = await Task.WhenAll(data.root.deletemember.target.Select(t => teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)t)));

                    MessageSubject = $"✈️ {await teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, new TeamsParticipant(data.root.deletemember?.initiator))} removed: " + string.Join(", ", memberNames);
                    textContent.Append(MessageSubject);
                }
                else
                if (Messagetype == MessageType.Event_Call)
                {
                    XmlDocument doc = new XmlDocument();
                    // prepare XML to JSON conversion; force "part" being a list which cannot be infered from XML if there is only one element
                    doc.LoadXml($"<root xmlns:json='http://james.newtonking.com/projects/json'>{Internal_Content.Replace("<part ", "<part json:Array='true' ")}</root>");
                    var json = JsonConvert.SerializeXmlNode(doc);
                    var data = JsonUtils.DeserializeObject <EventCallWrapper>(logger, json);

                    await Task.WhenAll(data.root.partlist?.part?.Select(member => teamsUserRegistry.RegisterDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)member.identity, member.displayName, OriginalArrivalTime)) ?? new List <Task>());

                    // sometimes p.name contains the user id and p.identity is empty
                    var memberNames = await Task.WhenAll(data.root.partlist?.part?.Select(p => teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, TeamsParticipant.FromFirstValid(p.identity, p.name))) ?? new List <Task <string> >());

                    var callEnded = Internal_Content.Contains("<ended/>");
                    if (callEnded)
                    {
                        MessageSubject = "☎️ Call ended for: " + string.Join(", ", memberNames);
                    }
                    else
                    {
                        var from        = (TeamsParticipant)Internal_FromContactUrl;
                        var displayName = await teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, from);

                        MessageSubject = $"☎️ Call started by {displayName}";
                    }
                    textContent.Append(MessageSubject);
                }
                else
                if (Messagetype == MessageType.ThreadActivity_MemberJoined)
                {
                    var data = JsonUtils.DeserializeObject <ThreadEventMemberJoined>(logger, Internal_Content);

                    await Task.WhenAll(data.members.Select(member => teamsUserRegistry.RegisterDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)member.id, member.friendlyname, OriginalArrivalTime)));

                    var memberNames = (await Task.WhenAll(data.members.Select(member => teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)member.id))));
                    MessageSubject = "✈️ Member(s) joined: " + string.Join(", ", memberNames); // note: friendlyname is sometimes empty; second note: für ehemalige Mitarbeiter kann ein ID-Lookup fehlschlagen, aber der friendlyName dennoch gesetzt sein
                    textContent.Append(MessageSubject);
                }
                else
                if (Messagetype == MessageType.ThreadActivity_MemberLeft)
                {
                    var data = JsonUtils.DeserializeObject <ThreadEventMemberLeft>(logger, Internal_Content);

                    await Task.WhenAll(data.members.Select(member => teamsUserRegistry.RegisterDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)member.id, member.friendlyname, OriginalArrivalTime)));

                    var memberNames = await Task.WhenAll(data.members.Select(member => teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, (TeamsParticipant)member.id)));

                    MessageSubject = "✈️ Member(s) left: " + string.Join(", ", memberNames);
                    textContent.Append(MessageSubject);
                }
                else
                if (Messagetype == MessageType.RichText_Media_CallRecording)
                {
                    XmlDocument doc = new XmlDocument();
                    // prepare XML to JSON conversion; force "part" being a list which cannot be infered from XML if there is only one element
                    doc.LoadXml($"<root xmlns:json='http://james.newtonking.com/projects/json'>{Internal_Content.Replace("<Identifiers>", "<Identifiers json:Array='true'>").Replace("<RecordingContent ", "<RecordingContent json:Array='true' ").Replace("<RequestedExports ", "<RequestedExports json:Array='true' ")}</root>");
                    var json = JsonConvert.SerializeXmlNode(doc);
                    var data = JsonUtils.DeserializeObject <RichTextMedia_CallRecordingWrapper>(logger, json);

                    MessageSubject = $"✍️ Recording started by {await teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, new TeamsParticipant(data.root?.URIObject?.RecordingInitiatorId?.value))}";
                    textContent.Append(MessageSubject);
                }
                else if (Messagetype == MessageType.Text)
                {
                    HtmlContent = Internal_Content;
                }
                else if (Messagetype == MessageType.ThreadActivity_TopicUpdate)
                {
                    XmlDocument doc = new XmlDocument();
                    // <topicupdate><eventtime>0000000000000</eventtime><initiator>8:orgid:00000000-0000-beef-0000-000000000000</initiator><value>New topic</value></topicupdate>
                    doc.LoadXml($"<root xmlns:json='http://james.newtonking.com/projects/json'>{Internal_Content}</root>");
                    var json = JsonConvert.SerializeXmlNode(doc);
                    var data = JsonUtils.DeserializeObject <ThreadActivityTopicUpdateWrapper>(logger, json);

                    var user = await teamsUserRegistry.GetUserByIdAsync(nonNullContext, (TeamsParticipant)data.root.topicupdate.initiator, false);

                    if (user != null && user.HasDisplayName)
                    {
                        if (From.Count == 1 && (From[0].UserId.Kind == ParticipantKind.TeamsChat || From[0].UserId.Kind == ParticipantKind.Unknown))
                        {
                            From.Clear();
                            From.Add(user);
                        }
                    }

                    MessageSubject = $"®️ Topic set to '{data.root.topicupdate.value}' by {await teamsUserRegistry.GetDisplayNameForUserIdAsync(nonNullContext, new TeamsParticipant(data.root.topicupdate.initiator))}";
                    textContent.Append(MessageSubject);
                }
                else
                {
                    textContent.Append("Unknown message type, don't know how to render: " + Messagetype);
                }

                if (string.IsNullOrWhiteSpace(HtmlContent))
                {
                    HtmlContent = MessageSubject;
                }
                TextContent = textContent.ToString();
            } catch (Exception e)
            {
                // exceptions here will cancel the whole chat from being parsed; log the message content to analyze it later
                logger.Error(e, "[{TenantName}] Exception while processing message content in method {MethodName}; Original message content: {MessageContent}", nonNullContext.Tenant.TenantName, nameof(GenerateTextContentExtractUsersAndUpdateSubject), SerializeOriginalMessageAsJson());
                // don't just skip a failed message, but cancel chat retrieval and fix the underlying issue, then try again
                throw;
            }
        }
示例#14
0
        public void TestParticipantCreation()
        {
            TeamsParticipant participant;

            participant = new TeamsParticipant("28:00000000-0000-beef-0000-000000000000");
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("00000000-0000-beef-0000-000000000000", participant.Id);
            Assert.AreEqual(ParticipantKind.AppOrBot, participant.Kind);
            participant = JsonConvert.DeserializeObject <TeamsParticipant>(JsonConvert.SerializeObject(participant));
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("00000000-0000-beef-0000-000000000000", participant.Id);
            Assert.AreEqual(ParticipantKind.AppOrBot, participant.Kind);

            participant = new TeamsParticipant("8:orgid:00000000-0000-beef-0000-000000000000");
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("00000000-0000-beef-0000-000000000000", participant.Id);
            Assert.AreEqual(ParticipantKind.User, participant.Kind);
            participant = JsonConvert.DeserializeObject <TeamsParticipant>(JsonConvert.SerializeObject(participant));
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("00000000-0000-beef-0000-000000000000", participant.Id);
            Assert.AreEqual(ParticipantKind.User, participant.Kind);

            participant = new TeamsParticipant("00000000-0000-beef-0000-000000000000");
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("00000000-0000-beef-0000-000000000000", participant.Id);
            Assert.AreEqual(ParticipantKind.Unknown, participant.Kind);
            participant = JsonConvert.DeserializeObject <TeamsParticipant>(JsonConvert.SerializeObject(participant));
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("00000000-0000-beef-0000-000000000000", participant.Id);
            Assert.AreEqual(ParticipantKind.Unknown, participant.Kind);

            participant = new TeamsParticipant("https://emea.ng.msg.teams.microsoft.com/v1/users/ME/contacts/28:0af95b67-5890-4306-9c1c-a8591cead09e");
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("0af95b67-5890-4306-9c1c-a8591cead09e", participant.Id);
            Assert.AreEqual(ParticipantKind.AppOrBot, participant.Kind);
            participant = JsonConvert.DeserializeObject <TeamsParticipant>(JsonConvert.SerializeObject(participant));
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("0af95b67-5890-4306-9c1c-a8591cead09e", participant.Id);
            Assert.AreEqual(ParticipantKind.AppOrBot, participant.Kind);

            participant = (TeamsParticipant)"19:00000000-0000-beef-0000-000000000000_00000000-0000-beef-0000-000000000000@unq.gbl.spaces";
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("19:00000000-0000-beef-0000-000000000000_00000000-0000-beef-0000-000000000000@unq.gbl.spaces", participant.Id);
            Assert.AreEqual(ParticipantKind.TeamsChat, participant.Kind);
            participant = JsonConvert.DeserializeObject <TeamsParticipant>(JsonConvert.SerializeObject(participant));
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("19:00000000-0000-beef-0000-000000000000_00000000-0000-beef-0000-000000000000@unq.gbl.spaces", participant.Id);
            Assert.AreEqual(ParticipantKind.TeamsChat, participant.Kind);

            participant = new TeamsParticipant("https://notifications.skype.net/v1/users/ME/contacts/28:integration:t0ri00alov");
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("t0ri00alov", participant.Id);
            Assert.AreEqual(ParticipantKind.AppOrBot, participant.Kind);
            participant = JsonConvert.DeserializeObject <TeamsParticipant>(JsonConvert.SerializeObject(participant));
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("t0ri00alov", participant.Id);
            Assert.AreEqual(ParticipantKind.AppOrBot, participant.Kind);

            participant = new TeamsParticipant("8:teamsvisitor:a111a1a111aa111a1aa1aa1a11aaaaa1");
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("a111a1a111aa111a1aa1aa1a11aaaaa1", participant.Id);
            Assert.AreEqual(ParticipantKind.User, participant.Kind);
            participant = JsonConvert.DeserializeObject <TeamsParticipant>(JsonConvert.SerializeObject(participant));
            Assert.IsTrue(participant.IsValid);
            Assert.AreEqual("a111a1a111aa111a1aa1aa1a11aaaaa1", participant.Id);
            Assert.AreEqual(ParticipantKind.User, participant.Kind);

            participant = (TeamsParticipant)null;
            Assert.IsFalse(participant.IsValid);
            Assert.IsNull(participant.Id);
            Assert.AreEqual(ParticipantKind.Unknown, participant.Kind);
            participant = JsonConvert.DeserializeObject <TeamsParticipant>(JsonConvert.SerializeObject(participant));
            Assert.IsFalse(participant.IsValid);
            Assert.IsNull(participant.Id);
            Assert.AreEqual(ParticipantKind.Unknown, participant.Kind);
        }