2020-12-22 12:15:26 +00:00
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
|
|
|
using Myriad.Types;
|
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
namespace Myriad.Cache;
|
|
|
|
|
|
|
|
public class MemoryDiscordCache: IDiscordCache
|
2020-12-22 12:15:26 +00:00
|
|
|
{
|
2021-11-27 02:10:56 +00:00
|
|
|
private readonly ConcurrentDictionary<ulong, Channel> _channels = 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
|
|
|
{
|
2022-02-04 19:54:56 +00:00
|
|
|
if (!_guilds.ContainsKey(guild.Id))
|
|
|
|
{
|
|
|
|
_guilds[guild.Id] = new CachedGuild(guild);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var channels = _guilds[guild.Id].Channels;
|
|
|
|
_guilds[guild.Id] = new CachedGuild(guild)
|
|
|
|
{
|
|
|
|
Channels = channels,
|
|
|
|
};
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
return default;
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public async ValueTask SaveChannel(Channel channel)
|
|
|
|
{
|
|
|
|
_channels[channel.Id] = channel;
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
if (channel.Recipients != null)
|
|
|
|
foreach (var recipient in channel.Recipients)
|
|
|
|
await SaveUser(recipient);
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +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;
|
2021-11-22 00:42:35 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
return default;
|
|
|
|
}
|
2021-11-22 00:42:35 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public ValueTask SaveUser(User user)
|
|
|
|
{
|
|
|
|
_users[user.Id] = user;
|
|
|
|
return default;
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public ValueTask SaveSelfMember(ulong guildId, GuildMemberPartial member)
|
|
|
|
{
|
|
|
|
_guildMembers[guildId] = member;
|
|
|
|
return default;
|
|
|
|
}
|
2021-11-21 17:06:08 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public ValueTask SaveRole(ulong guildId, Role role)
|
|
|
|
{
|
|
|
|
_roles[role.Id] = role;
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
{
|
2021-11-27 02:10:56 +00:00
|
|
|
if (guild.Guild.Roles[i].Id != role.Id)
|
|
|
|
continue;
|
2021-08-27 15:03:47 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
guild.Guild.Roles[i] = role;
|
|
|
|
found = true;
|
2020-12-22 12:15:26 +00:00
|
|
|
}
|
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
}
|
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
return default;
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public ValueTask RemoveGuild(ulong guildId)
|
|
|
|
{
|
|
|
|
_guilds.TryRemove(guildId, out _);
|
|
|
|
return default;
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public ValueTask RemoveChannel(ulong channelId)
|
|
|
|
{
|
|
|
|
if (!_channels.TryRemove(channelId, out var channel))
|
2020-12-22 12:15:26 +00:00
|
|
|
return default;
|
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
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
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
return default;
|
|
|
|
}
|
2021-11-22 00:42:35 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public ValueTask RemoveUser(ulong userId)
|
|
|
|
{
|
|
|
|
_users.TryRemove(userId, out _);
|
|
|
|
return default;
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public Task<ulong> GetOwnUser() => Task.FromResult(_ownUserId!.Value);
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public ValueTask RemoveRole(ulong guildId, ulong roleId)
|
|
|
|
{
|
|
|
|
_roles.TryRemove(roleId, out _);
|
|
|
|
return default;
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
public Task<User?> TryGetUser(ulong userId)
|
|
|
|
{
|
|
|
|
_users.TryGetValue(userId, out var user);
|
|
|
|
return Task.FromResult(user);
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
|
2021-11-27 02:10:56 +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
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
return Task.FromResult(guild.Channels.Keys.Select(c => _channels[c]));
|
|
|
|
}
|
2020-12-22 12:15:26 +00:00
|
|
|
|
2021-11-27 02:10:56 +00:00
|
|
|
private record CachedGuild(Guild Guild)
|
|
|
|
{
|
2022-02-04 19:54:56 +00:00
|
|
|
public ConcurrentDictionary<ulong, bool> Channels { get; init; } = new();
|
2020-12-22 12:15:26 +00:00
|
|
|
}
|
|
|
|
}
|