feat(bot): add Redis cache
This commit is contained in:
parent
fadf007abc
commit
c2094e3b7a
346
Myriad/Cache/RedisDiscordCache.cs
Normal file
346
Myriad/Cache/RedisDiscordCache.cs
Normal file
@ -0,0 +1,346 @@
|
||||
using Google.Protobuf;
|
||||
|
||||
using StackExchange.Redis;
|
||||
using StackExchange.Redis.KeyspaceIsolation;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using Myriad.Types;
|
||||
|
||||
namespace Myriad.Cache;
|
||||
|
||||
#pragma warning disable 4014
|
||||
public class RedisDiscordCache : IDiscordCache
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
public RedisDiscordCache(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private ConnectionMultiplexer _redis { get; set; }
|
||||
private ulong _ownUserId { get; set; }
|
||||
|
||||
public async Task InitAsync(string addr, ulong ownUserId)
|
||||
{
|
||||
_redis = await ConnectionMultiplexer.ConnectAsync(addr);
|
||||
_ownUserId = ownUserId;
|
||||
}
|
||||
|
||||
private IDatabase db => _redis.GetDatabase().WithKeyPrefix("discord:");
|
||||
|
||||
public async ValueTask SaveGuild(Guild guild)
|
||||
{
|
||||
_logger.Verbose("Saving guild {GuildId} to redis", guild.Id);
|
||||
|
||||
var g = new CachedGuild();
|
||||
g.Id = guild.Id;
|
||||
g.Name = guild.Name;
|
||||
g.OwnerId = guild.OwnerId;
|
||||
g.PremiumTier = (int) guild.PremiumTier;
|
||||
|
||||
var tr = db.CreateTransaction();
|
||||
|
||||
tr.HashSetAsync("guilds", guild.Id.HashWrapper(g));
|
||||
|
||||
foreach (var role in guild.Roles)
|
||||
{
|
||||
// Don't call SaveRole because that updates guild state
|
||||
// and we just got a brand new one :)
|
||||
// actually with redis it doesn't update guild state, but we're still doing it here because transaction
|
||||
tr.HashSetAsync("roles", role.Id.HashWrapper(new CachedRole()
|
||||
{
|
||||
Id = role.Id,
|
||||
Name = role.Name,
|
||||
Position = role.Position,
|
||||
Permissions = (ulong) role.Permissions,
|
||||
Mentionable = role.Mentionable,
|
||||
}));
|
||||
|
||||
tr.HashSetAsync($"guild_roles:{guild.Id}", role.Id, true, When.NotExists);
|
||||
}
|
||||
|
||||
await tr.ExecuteAsync();
|
||||
}
|
||||
|
||||
public async ValueTask SaveChannel(Channel channel)
|
||||
{
|
||||
_logger.Verbose("Saving channel {ChannelId} to redis", channel.Id);
|
||||
|
||||
await db.HashSetAsync("channels", channel.Id.HashWrapper(channel.ToProtobuf()));
|
||||
|
||||
if (channel.GuildId != null)
|
||||
await db.HashSetAsync($"guild_channels:{channel.GuildId.Value}", channel.Id, true, When.NotExists);
|
||||
|
||||
// todo: use a transaction for this?
|
||||
if (channel.Recipients != null)
|
||||
foreach (var recipient in channel.Recipients)
|
||||
await SaveUser(recipient);
|
||||
}
|
||||
|
||||
public ValueTask SaveOwnUser(ulong userId)
|
||||
{
|
||||
// we get the own user ID in InitAsync, so no need to save it here
|
||||
return default;
|
||||
}
|
||||
|
||||
public async ValueTask SaveUser(User user)
|
||||
{
|
||||
_logger.Verbose("Saving user {UserId} to redis", user.Id);
|
||||
|
||||
var u = new CachedUser()
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
Discriminator = user.Discriminator,
|
||||
Bot = user.Bot,
|
||||
};
|
||||
|
||||
if (user.Avatar != null)
|
||||
u.Avatar = user.Avatar;
|
||||
|
||||
await db.HashSetAsync("users", user.Id.HashWrapper(u));
|
||||
}
|
||||
|
||||
public async ValueTask SaveSelfMember(ulong guildId, GuildMemberPartial member)
|
||||
{
|
||||
_logger.Verbose("Saving self member for guild {GuildId} to redis", guildId);
|
||||
|
||||
var gm = new CachedGuildMember();
|
||||
foreach (var role in member.Roles)
|
||||
gm.Roles.Add(role);
|
||||
|
||||
await db.HashSetAsync("members", guildId.HashWrapper(gm));
|
||||
}
|
||||
|
||||
public async ValueTask SaveRole(ulong guildId, Myriad.Types.Role role)
|
||||
{
|
||||
_logger.Verbose("Saving role {RoleId} in {GuildId} to redis", role.Id, guildId);
|
||||
|
||||
await db.HashSetAsync("roles", role.Id.HashWrapper(new CachedRole()
|
||||
{
|
||||
Id = role.Id,
|
||||
Mentionable = role.Mentionable,
|
||||
Name = role.Name,
|
||||
Permissions = (ulong) role.Permissions,
|
||||
Position = role.Position,
|
||||
}));
|
||||
|
||||
await db.HashSetAsync($"guild_roles:{guildId}", role.Id, true, When.NotExists);
|
||||
}
|
||||
|
||||
public async 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
|
||||
|
||||
if (await TryGetChannel(channelId) == null)
|
||||
await db.HashSetAsync("channels", channelId.HashWrapper(new CachedChannel()
|
||||
{
|
||||
Id = channelId,
|
||||
Type = (int) Channel.ChannelType.Dm,
|
||||
}));
|
||||
}
|
||||
|
||||
public async ValueTask RemoveGuild(ulong guildId)
|
||||
=> await db.HashDeleteAsync("guilds", guildId);
|
||||
|
||||
public async ValueTask RemoveChannel(ulong channelId)
|
||||
{
|
||||
var oldChannel = await TryGetChannel(channelId);
|
||||
|
||||
if (oldChannel == null)
|
||||
return;
|
||||
|
||||
await db.HashDeleteAsync("channels", channelId);
|
||||
|
||||
if (oldChannel.GuildId != null)
|
||||
await db.HashDeleteAsync($"guild_channels:{oldChannel.GuildId.Value}", oldChannel.Id);
|
||||
}
|
||||
|
||||
public async ValueTask RemoveUser(ulong userId)
|
||||
=> await db.HashDeleteAsync("users", userId);
|
||||
|
||||
// todo: try getting this from redis if we don't have it yet
|
||||
public Task<ulong> GetOwnUser() => Task.FromResult(_ownUserId);
|
||||
|
||||
public async ValueTask RemoveRole(ulong guildId, ulong roleId)
|
||||
{
|
||||
await db.HashDeleteAsync("roles", roleId);
|
||||
await db.HashDeleteAsync($"guild_roles:{guildId}", roleId);
|
||||
}
|
||||
|
||||
public async Task<Guild?> TryGetGuild(ulong guildId)
|
||||
{
|
||||
var redisGuild = await db.HashGetAsync("guilds", guildId);
|
||||
if (redisGuild.IsNullOrEmpty)
|
||||
return null;
|
||||
|
||||
var guild = ((byte[])redisGuild).Unmarshal<CachedGuild>();
|
||||
|
||||
var redisRoles = await db.HashGetAllAsync($"guild_roles:{guildId}");
|
||||
|
||||
// todo: put this in a transaction or something
|
||||
var roles = await Task.WhenAll(redisRoles.Select(r => TryGetRole((ulong)r.Name)));
|
||||
|
||||
#pragma warning disable 8619
|
||||
return guild.FromProtobuf() with { Roles = roles } ;
|
||||
#pragma warning restore 8619
|
||||
}
|
||||
|
||||
public async Task<Channel?> TryGetChannel(ulong channelId)
|
||||
{
|
||||
var redisChannel = await db.HashGetAsync("channels", channelId);
|
||||
if (redisChannel.IsNullOrEmpty)
|
||||
return null;
|
||||
|
||||
return ((byte[])redisChannel).Unmarshal<CachedChannel>().FromProtobuf();
|
||||
}
|
||||
|
||||
public async Task<User?> TryGetUser(ulong userId)
|
||||
{
|
||||
var redisUser = await db.HashGetAsync("users", userId);
|
||||
if (redisUser.IsNullOrEmpty)
|
||||
return null;
|
||||
|
||||
return ((byte[])redisUser).Unmarshal<CachedUser>().FromProtobuf();
|
||||
}
|
||||
|
||||
public async Task<GuildMemberPartial?> TryGetSelfMember(ulong guildId)
|
||||
{
|
||||
var redisMember = await db.HashGetAsync("members", guildId);
|
||||
if (redisMember.IsNullOrEmpty)
|
||||
return null;
|
||||
|
||||
return new GuildMemberPartial()
|
||||
{
|
||||
Roles = ((byte[])redisMember).Unmarshal<CachedGuildMember>().Roles.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<Myriad.Types.Role?> TryGetRole(ulong roleId)
|
||||
{
|
||||
var redisRole = await db.HashGetAsync("roles", roleId);
|
||||
if (redisRole.IsNullOrEmpty)
|
||||
return null;
|
||||
|
||||
var role = ((byte[])redisRole).Unmarshal<CachedRole>();
|
||||
|
||||
return new Myriad.Types.Role()
|
||||
{
|
||||
Id = role.Id,
|
||||
Name = role.Name,
|
||||
Position = role.Position,
|
||||
Permissions = (PermissionSet) role.Permissions,
|
||||
Mentionable = role.Mentionable,
|
||||
};
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<Guild> GetAllGuilds()
|
||||
{
|
||||
// return _guilds.Values
|
||||
// .Select(g => g.Guild)
|
||||
// .ToAsyncEnumerable();
|
||||
return new Guild[] {}.ToAsyncEnumerable();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId)
|
||||
{
|
||||
var redisChannels = await db.HashGetAllAsync($"guild_channels:{guildId}");
|
||||
if (redisChannels.Length == 0)
|
||||
throw new ArgumentException("Guild not found", nameof(guildId));
|
||||
|
||||
#pragma warning disable 8619
|
||||
return await Task.WhenAll(redisChannels.Select(c => TryGetChannel((ulong) c.Name)));
|
||||
#pragma warning restore 8619
|
||||
}
|
||||
}
|
||||
|
||||
internal static class CacheProtoExt
|
||||
{
|
||||
public static Guild FromProtobuf(this CachedGuild guild)
|
||||
=> new Guild()
|
||||
{
|
||||
Id = guild.Id,
|
||||
Name = guild.Name,
|
||||
OwnerId = guild.OwnerId,
|
||||
PremiumTier = (PremiumTier) guild.PremiumTier,
|
||||
};
|
||||
|
||||
public static CachedChannel ToProtobuf(this Channel channel)
|
||||
{
|
||||
var c = new CachedChannel();
|
||||
c.Id = channel.Id;
|
||||
c.Type = (int) channel.Type;
|
||||
if (channel.Position != null)
|
||||
c.Position = channel.Position.Value;
|
||||
c.Name = channel.Name;
|
||||
if (channel.PermissionOverwrites != null)
|
||||
foreach (var overwrite in channel.PermissionOverwrites)
|
||||
c.PermissionOverwrites.Add(new Overwrite() {
|
||||
Id = overwrite.Id,
|
||||
Type = (int) overwrite.Type,
|
||||
Allow = (ulong) overwrite.Allow,
|
||||
Deny = (ulong) overwrite.Deny,
|
||||
});
|
||||
if (channel.GuildId != null)
|
||||
c.GuildId = channel.GuildId.Value;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public static Channel FromProtobuf(this CachedChannel channel)
|
||||
=> new Channel()
|
||||
{
|
||||
Id = channel.Id,
|
||||
Type = (Channel.ChannelType) channel.Type,
|
||||
Position = channel.Position,
|
||||
Name = channel.Name,
|
||||
PermissionOverwrites = channel.PermissionOverwrites
|
||||
.Select(x => new Channel.Overwrite()
|
||||
{
|
||||
Id = x.Id,
|
||||
Type = (Channel.OverwriteType) x.Type,
|
||||
Allow = (PermissionSet) x.Allow,
|
||||
Deny = (PermissionSet) x.Deny,
|
||||
}).ToArray(),
|
||||
GuildId = channel.HasGuildId ? channel.GuildId : null,
|
||||
ParentId = channel.HasParentId ? channel.ParentId : null,
|
||||
};
|
||||
|
||||
public static User FromProtobuf(this CachedUser user)
|
||||
=> new User()
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
Discriminator = user.Discriminator,
|
||||
Avatar = user.HasAvatar ? user.Avatar : null,
|
||||
Bot = user.Bot,
|
||||
};
|
||||
}
|
||||
|
||||
internal static class RedisExt
|
||||
{
|
||||
// convenience method
|
||||
public static HashEntry[] HashWrapper<T>(this ulong key, T value) where T : IMessage
|
||||
=> new[] { new HashEntry(key, value.ToByteArray()) };
|
||||
}
|
||||
|
||||
public static class ProtobufExt
|
||||
{
|
||||
private static Dictionary<string, MessageParser> _parser = new();
|
||||
|
||||
public static byte[] Marshal(this IMessage message) => message.ToByteArray();
|
||||
|
||||
public static T Unmarshal<T>(this byte[] message) where T : IMessage<T>, new()
|
||||
{
|
||||
var type = typeof(T).ToString();
|
||||
if (_parser.ContainsKey(type))
|
||||
return (T)_parser[type].ParseFrom(message);
|
||||
else
|
||||
{
|
||||
_parser.Add(type, new MessageParser<T>(() => new T()));
|
||||
return Unmarshal<T>(message);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.13.0"/>
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.32.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.37.0" PrivateAssets="All"/>
|
||||
<PackageReference Include="Polly" Version="7.2.1"/>
|
||||
<PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.1.1"/>
|
||||
<PackageReference Include="Serilog" Version="2.10.0"/>
|
||||
@ -28,4 +31,7 @@
|
||||
<PackageReference Include="System.Linq.Async" Version="5.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="../proto/discord_cache.proto" GrpcServices="Client" Link="Protos/discord_cache.proto"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -2,6 +2,32 @@
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net6.0": {
|
||||
"Google.Protobuf": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.13.0, )",
|
||||
"resolved": "3.13.0",
|
||||
"contentHash": "/6VgKCh0P59x/rYsBkCvkUanF0TeUYzwV9hzLIWgt23QRBaKHoxaaMkidEWhKibLR88c3PVCXyyrx9Xlb+Ne6w==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.2",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.2"
|
||||
}
|
||||
},
|
||||
"Grpc.Net.ClientFactory": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.32.0, )",
|
||||
"resolved": "2.32.0",
|
||||
"contentHash": "ixqSWxPK49P+5z6M2dDBHca0k+sXFe2KHHTJK3P+YXp6QOTHv5CHxNdaW8GrFF34Eh1FJ56Q2ADe383+FEAp6Q==",
|
||||
"dependencies": {
|
||||
"Grpc.Net.Client": "2.32.0",
|
||||
"Microsoft.Extensions.Http": "3.0.3"
|
||||
}
|
||||
},
|
||||
"Grpc.Tools": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.37.0, )",
|
||||
"resolved": "2.37.0",
|
||||
"contentHash": "cud/urkbw3QoQ8+kNeCy2YI0sHrh7td/1cZkVbH6hDLIXX7zzmJbV/KjYSiqiYtflQf+S5mJPLzDQWScN/QdDg=="
|
||||
},
|
||||
"Polly": {
|
||||
"type": "Direct",
|
||||
"requested": "[7.2.1, )",
|
||||
@ -36,6 +62,109 @@
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "cPtIuuH8TIjVHSi2ewwReWGW1PfChPE0LxPIDlfwVcLuTM9GANFTXiMB7k3aC4sk3f0cQU25LNKzx+jZMxijqw=="
|
||||
},
|
||||
"Grpc.Core.Api": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.32.0",
|
||||
"contentHash": "t9H6P/oYA4ZQI4fWq4eEwq2GmMNqmOSRfz5+YIat7pQuFmz1hRC2Vq/fL9ZVV1mjd5kHqBlhupMdlsBOsaxeEw==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.3"
|
||||
}
|
||||
},
|
||||
"Grpc.Net.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.32.0",
|
||||
"contentHash": "T4lKl51ahaSprLcgoZvgn8zYwh834DpaPnrDs6jBRdipL2NHIAC0rPeE7UyzDp/lzv4Xll2tw1u65Fg9ckvErg==",
|
||||
"dependencies": {
|
||||
"Grpc.Net.Common": "2.32.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "3.0.3",
|
||||
"System.Diagnostics.DiagnosticSource": "4.5.1"
|
||||
}
|
||||
},
|
||||
"Grpc.Net.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.32.0",
|
||||
"contentHash": "vDsgy6fs+DlsylppjK9FBGTMMUe8vfAmaURV7ZTurM27itr8qBwymgqmwnVB2hcP1q35NqKx2NvPGe5S2IEnDw==",
|
||||
"dependencies": {
|
||||
"Grpc.Core.Api": "2.32.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "0F/MLd7yOSjhswQSFO6tkTREHxBffE/AS9gnvtx1jVFaKNdJPEj+2KOdREmYZ4Orpvf4nwXGwbRpX5SLlwIPEw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "3.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "ND+ka7njp3HgVJCVn0YgpuxbFWBOCkcQaK+UBGJNseDhjz6I/qpXmCqLK+nXSzxU7cdscFWnUJS6wjEEEkMvSQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "3.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "hRmuReZgWqWqko4RXaGd/DP9L7380+HafHgbR5CMc7AZYmoLpUmeV8O8sgZqJONCbzg1q0Sz8U8Gy99eETpGPA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "3.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "Vu59TuHl3zoRI8vwK6gQL2EbWI2Qf/uBHFkSJXb4pgNvW7g8yK6Gn3v1bXDIKbMKEneTApriHfCVde0O314K+g==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "Wb1ejBzHhCvp7VMr+M7vlHoXb68mJ89IHj4L+TzL8yA+X7Iz2UTAEkl8aIbhRloroYJw5zvlIPtKF5uA4wFlxw=="
|
||||
},
|
||||
"Microsoft.Extensions.Http": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "dcyB8szIcSynjVZRuFgqkZpPgTc5zeRSj1HMXSmNqWbHYKiPYJl8ZQgBHz6wmZNSUUNGpCs5uxUg8DZHHDC1Ew==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.3",
|
||||
"Microsoft.Extensions.Logging": "3.0.3",
|
||||
"Microsoft.Extensions.Options": "3.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "uAZppu6kWIgS+VtVjqmhh+k3bMztwWQR5HYxI++Cn5Kz5m099g0KJ+krUrckaZP9NqIplQu63tPR5YpNWnjLuw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Binder": "3.0.3",
|
||||
"Microsoft.Extensions.DependencyInjection": "3.0.3",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "3.0.3",
|
||||
"Microsoft.Extensions.Options": "3.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "m2Jyi/MEn043WMI1I6J1ALuCThktZ93rd7eqzYeLmMcA0bdZC+TBVl0LuEbEWM01dWeeBjOoagjNwQTzOi2r6A=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "cvP/w0kyT9TmrDPMY2ZHJBWx+gRH0jHKaJPkzN47UBpLLC4KbqVU5AoCMK47+ZChlINhqJX2WTflbLe5KufD/A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.3",
|
||||
"Microsoft.Extensions.Primitives": "3.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.3",
|
||||
"contentHash": "eJuAFVIH9zUZ7j7tCSbJD+tS0dueENIerwoGxFL8RYqCmbEqQ7wVOG+mt2mZAbEpnMPsGl1Fc/HIhWpB9ftwhg=="
|
||||
},
|
||||
"Microsoft.NETCore.Platforms": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
@ -75,6 +204,11 @@
|
||||
"System.Security.Permissions": "5.0.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.DiagnosticSource": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "zCno/m44ymWhgLFh7tELDG9587q0l/EynPM0m4KgLaWQbz/TEKvNRX2YT5ip2qXW/uayifQ2ZqbnErsKJ4lYrQ=="
|
||||
},
|
||||
"System.Diagnostics.PerformanceCounter": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
@ -99,6 +233,16 @@
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "irMYm3vhVgRsYvHTU5b2gsT2CwT/SMM6LZFzuJjpIvT5Z4CshxNsaoBC1X/LltwuR3Opp8d6jOS/60WwOb7Q2Q=="
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.2",
|
||||
"contentHash": "wprSFgext8cwqymChhrBLu62LMg/1u92bU+VOwyfBimSPVFXtsNqEWC92Pf9ofzJFlk4IHmJA75EDJn1b2goAQ=="
|
||||
},
|
||||
"System.Security.AccessControl": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
|
@ -20,6 +20,7 @@ public class BotConfig
|
||||
|
||||
public string? GatewayQueueUrl { get; set; }
|
||||
public bool UseRedisRatelimiter { get; set; } = false;
|
||||
public bool UseRedisCache { get; set; } = false;
|
||||
|
||||
public string? RedisGatewayUrl { get; set; }
|
||||
|
||||
|
@ -2,6 +2,7 @@ using Autofac;
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
using Myriad.Cache;
|
||||
using Myriad.Gateway;
|
||||
using Myriad.Types;
|
||||
using Myriad.Rest;
|
||||
@ -51,6 +52,10 @@ public class Init
|
||||
if (config.UseRedisRatelimiter)
|
||||
await redis.InitAsync(coreConfig);
|
||||
|
||||
var cache = services.Resolve<IDiscordCache>();
|
||||
if (cache is RedisDiscordCache)
|
||||
await (cache as RedisDiscordCache).InitAsync(coreConfig.RedisAddr, config.ClientId!.Value);
|
||||
|
||||
if (config.Cluster == null)
|
||||
{
|
||||
// "Connect to the database" (ie. set off database migrations and ensure state)
|
||||
|
@ -44,7 +44,13 @@ public class BotModule: Module
|
||||
}).AsSelf().SingleInstance();
|
||||
builder.RegisterType<Cluster>().AsSelf().SingleInstance();
|
||||
builder.RegisterType<RedisGatewayService>().AsSelf().SingleInstance();
|
||||
builder.Register(c => { return new MemoryDiscordCache(); }).AsSelf().As<IDiscordCache>().SingleInstance();
|
||||
builder.Register<IDiscordCache>(c => {
|
||||
var botConfig = c.Resolve<BotConfig>();
|
||||
|
||||
if (botConfig.UseRedisCache)
|
||||
return new RedisDiscordCache(c.Resolve<ILogger>());
|
||||
return new MemoryDiscordCache();
|
||||
}).AsSelf().SingleInstance();
|
||||
builder.RegisterType<PrivateChannelService>().AsSelf().SingleInstance();
|
||||
|
||||
builder.Register(c =>
|
||||
|
@ -1520,6 +1520,8 @@
|
||||
"myriad": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Google.Protobuf": "3.13.0",
|
||||
"Grpc.Net.ClientFactory": "2.32.0",
|
||||
"Polly": "7.2.1",
|
||||
"Polly.Contrib.WaitAndRetry": "1.1.1",
|
||||
"Serilog": "2.10.0",
|
||||
|
@ -2063,6 +2063,8 @@
|
||||
"myriad": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Google.Protobuf": "3.13.0",
|
||||
"Grpc.Net.ClientFactory": "2.32.0",
|
||||
"Polly": "7.2.1",
|
||||
"Polly.Contrib.WaitAndRetry": "1.1.1",
|
||||
"Serilog": "2.10.0",
|
||||
|
47
proto/discord_cache.proto
Normal file
47
proto/discord_cache.proto
Normal file
@ -0,0 +1,47 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package myriad.cache;
|
||||
|
||||
message Overwrite {
|
||||
uint64 id = 1;
|
||||
int32 type = 2;
|
||||
uint64 allow = 3;
|
||||
uint64 deny = 4;
|
||||
}
|
||||
|
||||
message CachedChannel {
|
||||
uint64 id = 1;
|
||||
int32 type = 2;
|
||||
int32 position = 3;
|
||||
string name = 4;
|
||||
repeated Overwrite permission_overwrites = 5;
|
||||
optional uint64 guild_id = 6;
|
||||
optional uint64 parent_id = 7;
|
||||
}
|
||||
|
||||
message CachedGuildMember {
|
||||
repeated uint64 roles = 1;
|
||||
}
|
||||
|
||||
message CachedGuild {
|
||||
uint64 id = 1;
|
||||
string name = 2;
|
||||
uint64 owner_id = 3;
|
||||
int32 premium_tier = 4;
|
||||
}
|
||||
|
||||
message CachedRole {
|
||||
uint64 id = 1;
|
||||
string name = 2;
|
||||
int32 position = 3;
|
||||
uint64 permissions = 4;
|
||||
bool mentionable = 5;
|
||||
}
|
||||
|
||||
message CachedUser {
|
||||
uint64 id = 1;
|
||||
string username = 2;
|
||||
string discriminator = 3;
|
||||
optional string avatar = 4;
|
||||
bool bot = 5;
|
||||
}
|
Loading…
Reference in New Issue
Block a user