diff --git a/VPLink/Services/AvatarService.cs b/VPLink/Services/AvatarService.cs index 7850aeb..8f90ef2 100644 --- a/VPLink/Services/AvatarService.cs +++ b/VPLink/Services/AvatarService.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System.Reactive.Linq; using System.Reactive.Subjects; using Microsoft.Extensions.Hosting; @@ -17,6 +18,7 @@ internal sealed class AvatarService : BackgroundService, IAvatarService private readonly VirtualParadiseClient _virtualParadiseClient; private readonly Subject _avatarJoined = new(); private readonly Subject _avatarLeft = new(); + private readonly ConcurrentDictionary _cachedAvatars = new(); /// /// Initializes a new instance of the class. @@ -55,7 +57,8 @@ internal sealed class AvatarService : BackgroundService, IAvatarService if (!configuration.AnnounceAvatarEvents || avatar.IsBot && !configuration.AnnounceBots) return; - _avatarJoined.OnNext(avatar); + if (AddCachedAvatar(avatar)) + _avatarJoined.OnNext(avatar); } private void OnVPAvatarLeft(VirtualParadiseAvatar avatar) @@ -66,6 +69,24 @@ internal sealed class AvatarService : BackgroundService, IAvatarService if (!configuration.AnnounceAvatarEvents || avatar.IsBot && !configuration.AnnounceBots) return; - _avatarLeft.OnNext(avatar); + if (RemoveCachedAvatar(avatar)) + _avatarLeft.OnNext(avatar); + } + + private bool AddCachedAvatar(VirtualParadiseAvatar avatar) + { + if (avatar is null) throw new ArgumentNullException(nameof(avatar)); + + bool result = !_cachedAvatars.Values.Any(a => a.User.Id == avatar.User.Id && a.Name == avatar.Name); + _cachedAvatars[avatar.Session] = avatar; + return result; + } + + private bool RemoveCachedAvatar(VirtualParadiseAvatar avatar) + { + if (avatar is null) throw new ArgumentNullException(nameof(avatar)); + + _cachedAvatars.TryRemove(avatar.Session, out _); + return !_cachedAvatars.Values.Any(a => a.User.Id == avatar.User.Id && a.Name == avatar.Name); } }