mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-04 04:56:49 +00:00
feat: gateway service
This commit is contained in:
parent
1118d8bdf8
commit
e4ed354536
50 changed files with 1737 additions and 545 deletions
|
|
@ -100,15 +100,18 @@ public static class DiscordCacheExtensions
|
|||
await cache.SaveChannel(thread);
|
||||
}
|
||||
|
||||
public static async Task<PermissionSet> BotPermissionsIn(this IDiscordCache cache, ulong channelId)
|
||||
public static async Task<PermissionSet> BotPermissionsIn(this IDiscordCache cache, ulong guildId, ulong channelId)
|
||||
{
|
||||
var channel = await cache.GetRootChannel(channelId);
|
||||
if (cache is HttpDiscordCache)
|
||||
return await ((HttpDiscordCache)cache).BotPermissions(guildId, channelId);
|
||||
|
||||
var channel = await cache.GetRootChannel(guildId, channelId);
|
||||
|
||||
if (channel.GuildId != null)
|
||||
{
|
||||
var userId = cache.GetOwnUser();
|
||||
var member = await cache.TryGetSelfMember(channel.GuildId.Value);
|
||||
return await cache.PermissionsFor2(channelId, userId, member);
|
||||
return await cache.PermissionsFor2(guildId, channelId, userId, member);
|
||||
}
|
||||
|
||||
return PermissionSet.Dm;
|
||||
|
|
|
|||
75
Myriad/Cache/HTTPDiscordCache.cs
Normal file
75
Myriad/Cache/HTTPDiscordCache.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
using Serilog;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
|
||||
using Myriad.Serialization;
|
||||
using Myriad.Types;
|
||||
|
||||
namespace Myriad.Cache;
|
||||
|
||||
public class HttpDiscordCache: IDiscordCache
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly HttpClient _client;
|
||||
private readonly string _cacheEndpoint;
|
||||
private readonly ulong _ownUserId;
|
||||
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
|
||||
public HttpDiscordCache(ILogger logger, HttpClient client, string cacheEndpoint, ulong ownUserId)
|
||||
{
|
||||
_logger = logger;
|
||||
_client = client;
|
||||
_cacheEndpoint = cacheEndpoint;
|
||||
_ownUserId = ownUserId;
|
||||
_jsonSerializerOptions = new JsonSerializerOptions().ConfigureForMyriad();
|
||||
}
|
||||
|
||||
public ValueTask SaveGuild(Guild guild) => default;
|
||||
public ValueTask SaveChannel(Channel channel) => default;
|
||||
public ValueTask SaveUser(User user) => default;
|
||||
public ValueTask SaveSelfMember(ulong guildId, GuildMemberPartial member) => default;
|
||||
public ValueTask SaveRole(ulong guildId, Myriad.Types.Role role) => default;
|
||||
public ValueTask SaveDmChannelStub(ulong channelId) => default;
|
||||
public ValueTask RemoveGuild(ulong guildId) => default;
|
||||
public ValueTask RemoveChannel(ulong channelId) => default;
|
||||
public ValueTask RemoveUser(ulong userId) => default;
|
||||
public ValueTask RemoveRole(ulong guildId, ulong roleId) => default;
|
||||
|
||||
public ulong GetOwnUser() => _ownUserId;
|
||||
|
||||
// todo: cluster
|
||||
private async Task<T?> QueryCache<T>(string endpoint)
|
||||
{
|
||||
var response = await _client.GetAsync($"{_cacheEndpoint}{endpoint}");
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
return default;
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.Found)
|
||||
throw new Exception($"failed to query http cache: {response.StatusCode}");
|
||||
|
||||
var plaintext = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<T>(plaintext, _jsonSerializerOptions);
|
||||
}
|
||||
|
||||
public Task<Guild?> TryGetGuild(ulong guildId)
|
||||
=> QueryCache<Guild?>($"/guilds/{guildId}");
|
||||
|
||||
public Task<Channel?> TryGetChannel(ulong guildId, ulong channelId)
|
||||
=> QueryCache<Channel?>($"/guilds/{guildId}/channels/{channelId}");
|
||||
|
||||
// this should be a GetUserCached method on nirn-proxy (it's always called as GetOrFetchUser)
|
||||
// so just return nothing
|
||||
public Task<User?> TryGetUser(ulong userId)
|
||||
=> Task.FromResult<User?>(null);
|
||||
|
||||
public Task<GuildMemberPartial?> TryGetSelfMember(ulong guildId)
|
||||
=> QueryCache<GuildMemberPartial?>($"/guilds/{guildId}/members/@me");
|
||||
|
||||
public Task<PermissionSet> BotPermissions(ulong guildId, ulong channelId)
|
||||
=> QueryCache<PermissionSet>($"/guilds/{guildId}/channels/{channelId}/permissions/@me");
|
||||
|
||||
public Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId)
|
||||
=> QueryCache<IEnumerable<Channel>>($"/guilds/{guildId}/channels");
|
||||
}
|
||||
|
|
@ -18,11 +18,9 @@ public interface IDiscordCache
|
|||
|
||||
internal ulong GetOwnUser();
|
||||
public Task<Guild?> TryGetGuild(ulong guildId);
|
||||
public Task<Channel?> TryGetChannel(ulong channelId);
|
||||
public Task<Channel?> TryGetChannel(ulong guildId, ulong channelId);
|
||||
public Task<User?> TryGetUser(ulong userId);
|
||||
public Task<GuildMemberPartial?> TryGetSelfMember(ulong guildId);
|
||||
public Task<Role?> TryGetRole(ulong roleId);
|
||||
|
||||
public IAsyncEnumerable<Guild> GetAllGuilds();
|
||||
public Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId);
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ public class MemoryDiscordCache: IDiscordCache
|
|||
return Task.FromResult(cg?.Guild);
|
||||
}
|
||||
|
||||
public Task<Channel?> TryGetChannel(ulong channelId)
|
||||
public Task<Channel?> TryGetChannel(ulong _, ulong channelId)
|
||||
{
|
||||
_channels.TryGetValue(channelId, out var channel);
|
||||
return Task.FromResult(channel);
|
||||
|
|
@ -155,19 +155,6 @@ public class MemoryDiscordCache: IDiscordCache
|
|||
return Task.FromResult(guildMember);
|
||||
}
|
||||
|
||||
public Task<Role?> TryGetRole(ulong roleId)
|
||||
{
|
||||
_roles.TryGetValue(roleId, out var role);
|
||||
return Task.FromResult(role);
|
||||
}
|
||||
|
||||
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))
|
||||
|
|
|
|||
|
|
@ -1,340 +0,0 @@
|
|||
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;
|
||||
private readonly ulong _ownUserId;
|
||||
public RedisDiscordCache(ILogger logger, ulong ownUserId)
|
||||
{
|
||||
_logger = logger;
|
||||
_ownUserId = ownUserId;
|
||||
}
|
||||
|
||||
private ConnectionMultiplexer _redis { get; set; }
|
||||
|
||||
public async Task InitAsync(string addr)
|
||||
{
|
||||
_redis = await ConnectionMultiplexer.ConnectAsync(addr);
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
public ulong GetOwnUser() => _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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,27 +13,13 @@ public static class CacheExtensions
|
|||
return guild;
|
||||
}
|
||||
|
||||
public static async Task<Channel> GetChannel(this IDiscordCache cache, ulong channelId)
|
||||
public static async Task<Channel> GetChannel(this IDiscordCache cache, ulong guildId, ulong channelId)
|
||||
{
|
||||
if (!(await cache.TryGetChannel(channelId) is Channel channel))
|
||||
if (!(await cache.TryGetChannel(guildId, channelId) is Channel channel))
|
||||
throw new KeyNotFoundException($"Channel {channelId} not found in cache");
|
||||
return channel;
|
||||
}
|
||||
|
||||
public static async Task<User> GetUser(this IDiscordCache cache, ulong userId)
|
||||
{
|
||||
if (!(await cache.TryGetUser(userId) is User user))
|
||||
throw new KeyNotFoundException($"User {userId} not found in cache");
|
||||
return user;
|
||||
}
|
||||
|
||||
public static async Task<Role> GetRole(this IDiscordCache cache, ulong roleId)
|
||||
{
|
||||
if (!(await cache.TryGetRole(roleId) is Role role))
|
||||
throw new KeyNotFoundException($"Role {roleId} not found in cache");
|
||||
return role;
|
||||
}
|
||||
|
||||
public static async ValueTask<User?> GetOrFetchUser(this IDiscordCache cache, DiscordApiClient rest,
|
||||
ulong userId)
|
||||
{
|
||||
|
|
@ -47,9 +33,9 @@ public static class CacheExtensions
|
|||
}
|
||||
|
||||
public static async ValueTask<Channel?> GetOrFetchChannel(this IDiscordCache cache, DiscordApiClient rest,
|
||||
ulong channelId)
|
||||
ulong guildId, ulong channelId)
|
||||
{
|
||||
if (await cache.TryGetChannel(channelId) is { } cacheChannel)
|
||||
if (await cache.TryGetChannel(guildId, channelId) is { } cacheChannel)
|
||||
return cacheChannel;
|
||||
|
||||
var restChannel = await rest.GetChannel(channelId);
|
||||
|
|
@ -58,13 +44,13 @@ public static class CacheExtensions
|
|||
return restChannel;
|
||||
}
|
||||
|
||||
public static async Task<Channel> GetRootChannel(this IDiscordCache cache, ulong channelOrThread)
|
||||
public static async Task<Channel> GetRootChannel(this IDiscordCache cache, ulong guildId, ulong channelOrThread)
|
||||
{
|
||||
var channel = await cache.GetChannel(channelOrThread);
|
||||
var channel = await cache.GetChannel(guildId, channelOrThread);
|
||||
if (!channel.IsThread())
|
||||
return channel;
|
||||
|
||||
var parent = await cache.GetChannel(channel.ParentId!.Value);
|
||||
var parent = await cache.GetChannel(guildId, channel.ParentId!.Value);
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
|
|
@ -32,23 +32,23 @@ public static class PermissionExtensions
|
|||
PermissionSet.EmbedLinks;
|
||||
|
||||
public static Task<PermissionSet> PermissionsForMCE(this IDiscordCache cache, MessageCreateEvent message) =>
|
||||
PermissionsFor2(cache, message.ChannelId, message.Author.Id, message.Member, message.WebhookId != null);
|
||||
PermissionsFor2(cache, message.GuildId ?? 0, message.ChannelId, message.Author.Id, message.Member, message.WebhookId != null);
|
||||
|
||||
public static Task<PermissionSet>
|
||||
PermissionsForMemberInChannel(this IDiscordCache cache, ulong channelId, GuildMember member) =>
|
||||
PermissionsFor2(cache, channelId, member.User.Id, member);
|
||||
PermissionsForMemberInChannel(this IDiscordCache cache, ulong guildId, ulong channelId, GuildMember member) =>
|
||||
PermissionsFor2(cache, guildId, channelId, member.User.Id, member);
|
||||
|
||||
public static async Task<PermissionSet> PermissionsFor2(this IDiscordCache cache, ulong channelId, ulong userId,
|
||||
public static async Task<PermissionSet> PermissionsFor2(this IDiscordCache cache, ulong guildId, ulong channelId, ulong userId,
|
||||
GuildMemberPartial? member, bool isThread = false)
|
||||
{
|
||||
if (!(await cache.TryGetChannel(channelId) is Channel channel))
|
||||
if (!(await cache.TryGetChannel(guildId, channelId) is Channel channel))
|
||||
// todo: handle channel not found better
|
||||
return PermissionSet.Dm;
|
||||
|
||||
if (channel.GuildId == null)
|
||||
return PermissionSet.Dm;
|
||||
|
||||
var rootChannel = await cache.GetRootChannel(channelId);
|
||||
var rootChannel = await cache.GetRootChannel(guildId, channelId);
|
||||
|
||||
var guild = await cache.GetGuild(channel.GuildId.Value);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue