2024-10-14 14:56:40 +02:00
|
|
|
// 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/>.
|
|
|
|
|
|
2024-08-19 16:12:28 +02:00
|
|
|
using System.Collections.Concurrent;
|
2024-10-09 22:31:58 +02:00
|
|
|
using Remora.Discord.API.Abstractions.Gateway.Events;
|
2024-08-19 16:12:28 +02:00
|
|
|
using Remora.Discord.API.Abstractions.Objects;
|
2024-10-09 22:31:58 +02:00
|
|
|
using Remora.Discord.API.Abstractions.Rest;
|
|
|
|
|
using Remora.Discord.API.Objects;
|
2024-08-19 16:12:28 +02:00
|
|
|
using Remora.Rest.Core;
|
|
|
|
|
|
|
|
|
|
namespace Catalogger.Backend.Cache.InMemoryCache;
|
|
|
|
|
|
2024-10-09 22:31:58 +02:00
|
|
|
public class InMemoryMemberCache(IDiscordRestGuildAPI guildApi, ILogger logger) : IMemberCache
|
2024-08-19 16:12:28 +02:00
|
|
|
{
|
2024-10-09 22:31:58 +02:00
|
|
|
private readonly ILogger _logger = logger.ForContext<InMemoryMemberCache>();
|
2024-10-14 14:56:40 +02:00
|
|
|
|
2024-10-09 22:31:58 +02:00
|
|
|
private readonly ConcurrentDictionary<
|
|
|
|
|
(Snowflake GuildId, Snowflake UserId),
|
|
|
|
|
IGuildMember
|
|
|
|
|
> _members = new();
|
2024-10-14 14:56:40 +02:00
|
|
|
|
2024-08-19 16:12:28 +02:00
|
|
|
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())
|
2024-10-09 17:35:11 +02:00
|
|
|
throw new CataloggerError(
|
|
|
|
|
"Member with undefined User passed to InMemoryMemberCache.SetAsync"
|
|
|
|
|
);
|
2024-08-19 16:12:28 +02:00
|
|
|
_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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-02 17:00:33 +02:00
|
|
|
public Task RemoveAsync(Snowflake guildId, Snowflake userId)
|
|
|
|
|
{
|
|
|
|
|
_members.Remove((guildId, userId), out _);
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 17:35:11 +02:00
|
|
|
public Task<bool> IsGuildCachedAsync(Snowflake guildId) =>
|
|
|
|
|
Task.FromResult(_guilds.ContainsKey(guildId));
|
2024-08-19 16:12:28 +02:00
|
|
|
|
|
|
|
|
public Task MarkAsCachedAsync(Snowflake guildId)
|
|
|
|
|
{
|
|
|
|
|
_guilds[guildId] = 1;
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task MarkAsUncachedAsync(Snowflake guildId)
|
|
|
|
|
{
|
|
|
|
|
_guilds.Remove(guildId, out _);
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
2024-10-09 22:31:58 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2024-10-20 15:20:22 +02:00
|
|
|
|
|
|
|
|
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;
|
2024-10-09 17:35:11 +02:00
|
|
|
}
|