public override bool Equals(object?obj) { if (obj is TeamsParticipant participant) { var result = Mri?.Equals(participant.Mri, StringComparison.InvariantCultureIgnoreCase) ?? false; if (!result) { // check IDs but only if they are GUIDs result = (Id?.Equals(participant.Id, StringComparison.InvariantCultureIgnoreCase) ?? false) && IdIsGuid && participant.IdIsGuid; } return(result); } return(base.Equals(obj)); }
public TeamsParticipant(string?mri) { IsValid = false; if (mri != null) { // remove contact url part (e.g. "https://emea.ng.msg.teams.microsoft.com/v1/users/ME/contacts/") Mri = Regex.Replace(mri.Trim(), ContactUrlPattern, ""); } else { Mri = null; } // sample for skypeId contained: 8:orgid:00000000-0000-beef-0000-000000000000;skypeid=orgid:00000000-0000-beef-0000-000000000000 if ((Mri?.ToLowerInvariant().Contains(";skype") ?? false) && ((!Mri?.ToLowerInvariant().EndsWith("skype")) ?? false)) { //Debugger.Break(); <-- enable to discover potentially unknown ID formats } Prefix = ""; IdIsGuid = false; Kind = ParticipantKind.Unknown; Id = Mri; if (!string.IsNullOrEmpty(Id)) { var mriMatches = Regex.Matches(Id, MriPatternClosed); // use closed pattern, otherwise chat IDs would match partially if (mriMatches.Count > 0) { // type is 8, 28 etc.; can be missing var participantType = mriMatches[0].Groups["type"].ToString(); // subtype is "orgid", "teamsvisitor" etc.; can be empty // var participantSubType = mriMatches[0].Groups["subtype"].ToString(); Id = mriMatches[0].Groups["id"].ToString(); Prefix = mriMatches[0].Groups["prefix"].ToString(); // without prefix we only accept GUIds, no arbitrary strings; WITH prefix the id can be an arbitrary string IdIsGuid = Regex.Match(Id, GuidPattern).Success; if (!string.IsNullOrWhiteSpace(participantType) || IdIsGuid) { IsValid = true; } switch (participantType) { case "4": // phone case "8": Kind = ParticipantKind.User; break; case "19": Kind = ParticipantKind.TeamsChat; break; case "28": Kind = ParticipantKind.AppOrBot; break; case "48": Kind = ParticipantKind.Notification; break; default: Kind = ParticipantKind.Unknown; break; } } if (Kind == ParticipantKind.Unknown) { // still unknown? let's see // something like "19:[email protected]" if (Mri?.StartsWith("19:") ?? false) { Kind = ParticipantKind.TeamsChat; IsValid = true; } else if ((mri?.StartsWith("48:") ?? false) || (mri?.Contains("contacts/28:") ?? false)) { // something like "https://notifications.skype.net/v1/users/ME/conversations/48:notifications" - not sure if this is possible as sender, but it exists Kind = ParticipantKind.Notification; } } } }