Catalogger.NET/Catalogger.Backend/Cache/InMemoryCache/InMemoryMemberCache.cs

157 lines
5.7 KiB
C#

// Copyright (C) 2021-present sam (starshines.gay)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System.Collections.Concurrent;
using Remora.Discord.API.Abstractions.Gateway.Events;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Discord.API.Abstractions.Rest;
using Remora.Discord.API.Objects;
using Remora.Rest.Core;
namespace Catalogger.Backend.Cache.InMemoryCache;
public class InMemoryMemberCache(IDiscordRestGuildAPI guildApi, ILogger logger) : IMemberCache
{
private readonly ILogger _logger = logger.ForContext<InMemoryMemberCache>();
private readonly ConcurrentDictionary<
(Snowflake GuildId, Snowflake UserId),
IGuildMember
> _members = new();
private readonly ConcurrentDictionary<Snowflake, byte> _guilds = new();
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
public Task<IGuildMember?> TryGetAsync(Snowflake guildId, Snowflake userId) =>
_members.TryGetValue((guildId, userId), out var member)
? Task.FromResult(member)
: Task.FromResult<IGuildMember?>(null);
#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type.
public Task SetAsync(Snowflake guildId, IGuildMember member)
{
if (!member.User.IsDefined())
throw new CataloggerError(
"Member with undefined User passed to InMemoryMemberCache.SetAsync"
);
_members[(guildId, member.User.Value.ID)] = member;
return Task.CompletedTask;
}
public async Task SetManyAsync(Snowflake guildId, IReadOnlyList<IGuildMember> members)
{
foreach (var member in members)
await SetAsync(guildId, member);
}
public Task RemoveAsync(Snowflake guildId, Snowflake userId)
{
_members.Remove((guildId, userId), out _);
return Task.CompletedTask;
}
public Task<bool> IsGuildCachedAsync(Snowflake guildId) =>
Task.FromResult(_guilds.ContainsKey(guildId));
public Task MarkAsCachedAsync(Snowflake guildId)
{
_guilds[guildId] = 1;
return Task.CompletedTask;
}
public Task MarkAsUncachedAsync(Snowflake guildId)
{
_guilds.Remove(guildId, out _);
return Task.CompletedTask;
}
public async Task UpdateAsync(IGuildMemberUpdate newMember)
{
if (!_members.TryGetValue((newMember.GuildID, newMember.User.ID), out var oldMember))
{
_logger.Warning(
"Received member update event for {MemberId} in {GuildId}, but member wasn't found in cache. Fetching them and storing.",
newMember.User.ID,
newMember.GuildID
);
var memberResult = await guildApi.GetGuildMemberAsync(
newMember.GuildID,
newMember.User.ID
);
if (!memberResult.IsSuccess)
{
_logger.Error(
"Could not get uncached member {MemberId} in {GuildId} via REST: {Error}",
newMember.User.ID,
newMember.GuildID,
memberResult.Error
);
return;
}
_members[(newMember.GuildID, newMember.User.ID)] = memberResult.Entity;
return;
}
// Unwrapping the interface because fuck it, we ball
var member = (GuildMember)oldMember with
{
User = new Optional<IUser>(newMember.User),
Nickname = newMember.Nickname.HasValue ? newMember.Nickname.Value : oldMember.Nickname,
Avatar = newMember.Avatar.HasValue ? newMember.Avatar : oldMember.Avatar,
Roles = newMember.Roles.ToArray(),
JoinedAt = newMember.JoinedAt ?? oldMember.JoinedAt,
PremiumSince = newMember.PremiumSince.HasValue
? newMember.PremiumSince.Value
: oldMember.PremiumSince,
IsPending = newMember.IsPending.HasValue
? newMember.IsPending.Value
: oldMember.IsPending,
CommunicationDisabledUntil = newMember.CommunicationDisabledUntil.HasValue
? newMember.CommunicationDisabledUntil.Value
: oldMember.CommunicationDisabledUntil,
};
_members[(newMember.GuildID, newMember.User.ID)] = member;
}
public Task SetMemberNamesAsync(Snowflake guildId, IEnumerable<IGuildMember> members) =>
Task.CompletedTask;
public Task UpdateMemberNameAsync(
Snowflake guildId,
Snowflake userId,
string prevName,
string newName
) => Task.CompletedTask;
public Task<IEnumerable<(string Name, string Id)>> GetMemberNamesAsync(
Snowflake guildId,
string prefix
) => Task.FromResult<IEnumerable<(string, string)>>([]);
public Task TryRemoveMemberNameAsync(Snowflake guildId, string username) => Task.CompletedTask;
public Task RemoveAllMembersAsync(Snowflake guildId)
{
foreach (var kv in _members.Where(kv => kv.Key.GuildId == guildId))
_members.Remove(kv.Key, out _);
_guilds.Remove(guildId, out _);
return Task.CompletedTask;
}
}