PluralKit/Myriad/Cache/MemoryDiscordCache.cs

190 lines
5.5 KiB
C#
Raw Normal View History

2020-12-22 12:15:26 +00:00
using System.Collections.Concurrent;
using Myriad.Types;
namespace Myriad.Cache;
public class MemoryDiscordCache: IDiscordCache
2020-12-22 12:15:26 +00:00
{
private readonly ConcurrentDictionary<ulong, Channel> _channels = new();
private readonly ConcurrentDictionary<ulong, ulong> _dmChannels = new();
private readonly ConcurrentDictionary<ulong, GuildMemberPartial> _guildMembers = new();
private readonly ConcurrentDictionary<ulong, CachedGuild> _guilds = new();
private readonly ConcurrentDictionary<ulong, Role> _roles = new();
private readonly ConcurrentDictionary<ulong, User> _users = new();
private ulong? _ownUserId { get; set; }
public ValueTask SaveGuild(Guild guild)
2020-12-22 12:15:26 +00:00
{
SaveGuildRaw(guild);
2020-12-22 12:15:26 +00:00
foreach (var role in guild.Roles)
// Don't call SaveRole because that updates guild state
// and we just got a brand new one :)
_roles[role.Id] = role;
2020-12-22 12:15:26 +00:00
return default;
}
2020-12-22 12:15:26 +00:00
public async ValueTask SaveChannel(Channel channel)
{
_channels[channel.Id] = channel;
2020-12-22 12:15:26 +00:00
if (channel.GuildId != null && _guilds.TryGetValue(channel.GuildId.Value, out var guild))
guild.Channels.TryAdd(channel.Id, true);
2020-12-22 12:15:26 +00:00
if (channel.Recipients != null)
foreach (var recipient in channel.Recipients)
2020-12-25 12:19:35 +00:00
{
_dmChannels[recipient.Id] = channel.Id;
await SaveUser(recipient);
2020-12-25 12:19:35 +00:00
}
}
2020-12-22 12:15:26 +00:00
public ValueTask SaveOwnUser(ulong userId)
{
// this (hopefully) never changes at runtime, so we skip out on re-assigning it
if (_ownUserId == null)
_ownUserId = userId;
return default;
}
public ValueTask SaveUser(User user)
{
_users[user.Id] = user;
return default;
}
2020-12-22 12:15:26 +00:00
public ValueTask SaveSelfMember(ulong guildId, GuildMemberPartial member)
{
_guildMembers[guildId] = member;
return default;
}
public ValueTask SaveRole(ulong guildId, Role role)
{
_roles[role.Id] = role;
2020-12-22 12:15:26 +00:00
if (_guilds.TryGetValue(guildId, out var guild))
{
// TODO: this code is stinky
var found = false;
for (var i = 0; i < guild.Guild.Roles.Length; i++)
2020-12-22 12:15:26 +00:00
{
if (guild.Guild.Roles[i].Id != role.Id)
continue;
2021-08-27 15:03:47 +00:00
guild.Guild.Roles[i] = role;
found = true;
2020-12-22 12:15:26 +00:00
}
if (!found)
_guilds[guildId] = guild with
{
Guild = guild.Guild with { Roles = guild.Guild.Roles.Concat(new[] { role }).ToArray() }
};
2021-03-18 10:28:11 +00:00
}
return default;
}
2020-12-22 12:15:26 +00:00
public ValueTask SaveDmChannelStub(ulong channelId)
{
// Use existing channel object if present, otherwise add a stub
// We may get a message create before channel create and we want to have it saved
_channels.GetOrAdd(channelId, id => new Channel { Id = id, Type = Channel.ChannelType.Dm });
return default;
}
2020-12-22 12:15:26 +00:00
public ValueTask RemoveGuild(ulong guildId)
{
_guilds.TryRemove(guildId, out _);
return default;
}
2020-12-22 12:15:26 +00:00
public ValueTask RemoveChannel(ulong channelId)
{
if (!_channels.TryRemove(channelId, out var channel))
2020-12-22 12:15:26 +00:00
return default;
if (channel.GuildId != null && _guilds.TryGetValue(channel.GuildId.Value, out var guild))
guild.Channels.TryRemove(channel.Id, out _);
2020-12-22 12:15:26 +00:00
return default;
}
public ValueTask RemoveUser(ulong userId)
{
_users.TryRemove(userId, out _);
return default;
}
2020-12-22 12:15:26 +00:00
public Task<ulong> GetOwnUser() => Task.FromResult(_ownUserId!.Value);
2020-12-22 12:15:26 +00:00
public ValueTask RemoveRole(ulong guildId, ulong roleId)
{
_roles.TryRemove(roleId, out _);
return default;
}
2020-12-22 12:15:26 +00:00
public Task<Guild?> TryGetGuild(ulong guildId)
{
_guilds.TryGetValue(guildId, out var cg);
return Task.FromResult(cg?.Guild);
}
2020-12-25 12:19:35 +00:00
public Task<Channel?> TryGetChannel(ulong channelId)
{
_channels.TryGetValue(channelId, out var channel);
return Task.FromResult(channel);
}
2020-12-22 12:15:26 +00:00
public Task<Channel?> TryGetDmChannel(ulong userId)
{
if (!_dmChannels.TryGetValue(userId, out var channelId))
return Task.FromResult((Channel?)null);
return TryGetChannel(channelId);
}
public Task<User?> TryGetUser(ulong userId)
{
_users.TryGetValue(userId, out var user);
return Task.FromResult(user);
}
2020-12-22 12:15:26 +00:00
public Task<GuildMemberPartial?> TryGetSelfMember(ulong guildId)
{
_guildMembers.TryGetValue(guildId, out var guildMember);
return Task.FromResult(guildMember);
}
2020-12-22 12:15:26 +00:00
public Task<Role?> TryGetRole(ulong roleId)
{
_roles.TryGetValue(roleId, out var role);
return Task.FromResult(role);
}
2020-12-22 12:15:26 +00:00
public IAsyncEnumerable<Guild> GetAllGuilds()
{
return _guilds.Values
.Select(g => g.Guild)
.ToAsyncEnumerable();
}
public Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId)
{
if (!_guilds.TryGetValue(guildId, out var guild))
throw new ArgumentException("Guild not found", nameof(guildId));
2020-12-22 12:15:26 +00:00
return Task.FromResult(guild.Channels.Keys.Select(c => _channels[c]));
}
2020-12-22 12:15:26 +00:00
private CachedGuild SaveGuildRaw(Guild guild) =>
_guilds.GetOrAdd(guild.Id, (_, g) => new CachedGuild(g), guild);
private record CachedGuild(Guild Guild)
{
public readonly ConcurrentDictionary<ulong, bool> Channels = new();
2020-12-22 12:15:26 +00:00
}
}