feat: upgrade to .NET 6, refactor everything
This commit is contained in:
parent
d28e99ba43
commit
1918c56937
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Builders
|
namespace Myriad.Builders;
|
||||||
{
|
|
||||||
public class EmbedBuilder
|
public class EmbedBuilder
|
||||||
{
|
{
|
||||||
private Embed _embed = new();
|
|
||||||
private readonly List<Embed.Field> _fields = new();
|
private readonly List<Embed.Field> _fields = new();
|
||||||
|
private Embed _embed = new();
|
||||||
|
|
||||||
public EmbedBuilder Title(string? title)
|
public EmbedBuilder Title(string? title)
|
||||||
{
|
{
|
||||||
@ -35,47 +33,32 @@ namespace Myriad.Builders
|
|||||||
|
|
||||||
public EmbedBuilder Footer(Embed.EmbedFooter? footer)
|
public EmbedBuilder Footer(Embed.EmbedFooter? footer)
|
||||||
{
|
{
|
||||||
_embed = _embed with
|
_embed = _embed with { Footer = footer };
|
||||||
{
|
|
||||||
Footer = footer
|
|
||||||
};
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbedBuilder Image(Embed.EmbedImage? image)
|
public EmbedBuilder Image(Embed.EmbedImage? image)
|
||||||
{
|
{
|
||||||
_embed = _embed with
|
_embed = _embed with { Image = image };
|
||||||
{
|
|
||||||
Image = image
|
|
||||||
};
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public EmbedBuilder Thumbnail(Embed.EmbedThumbnail? thumbnail)
|
public EmbedBuilder Thumbnail(Embed.EmbedThumbnail? thumbnail)
|
||||||
{
|
{
|
||||||
_embed = _embed with
|
_embed = _embed with { Thumbnail = thumbnail };
|
||||||
{
|
|
||||||
Thumbnail = thumbnail
|
|
||||||
};
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbedBuilder Author(Embed.EmbedAuthor? author)
|
public EmbedBuilder Author(Embed.EmbedAuthor? author)
|
||||||
{
|
{
|
||||||
_embed = _embed with
|
_embed = _embed with { Author = author };
|
||||||
{
|
|
||||||
Author = author
|
|
||||||
};
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbedBuilder Timestamp(string? timestamp)
|
public EmbedBuilder Timestamp(string? timestamp)
|
||||||
{
|
{
|
||||||
_embed = _embed with
|
_embed = _embed with { Timestamp = timestamp };
|
||||||
{
|
|
||||||
Timestamp = timestamp
|
|
||||||
};
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,4 +71,3 @@ namespace Myriad.Builders
|
|||||||
public Embed Build() =>
|
public Embed Build() =>
|
||||||
_embed with { Fields = _fields.ToArray() };
|
_embed with { Fields = _fields.ToArray() };
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,12 +1,9 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Extensions;
|
using Myriad.Extensions;
|
||||||
using Myriad.Gateway;
|
using Myriad.Gateway;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Cache
|
namespace Myriad.Cache;
|
||||||
{
|
|
||||||
public static class DiscordCacheExtensions
|
public static class DiscordCacheExtensions
|
||||||
{
|
{
|
||||||
public static ValueTask HandleGatewayEvent(this IDiscordCache cache, IGatewayEvent evt)
|
public static ValueTask HandleGatewayEvent(this IDiscordCache cache, IGatewayEvent evt)
|
||||||
@ -94,12 +91,10 @@ namespace Myriad.Cache
|
|||||||
await cache.SaveUser(mention);
|
await cache.SaveUser(mention);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ValueTask TrySaveDmChannelStub(this IDiscordCache cache, ulong? guildId, ulong channelId)
|
private static ValueTask TrySaveDmChannelStub(this IDiscordCache cache, ulong? guildId, ulong channelId) =>
|
||||||
{
|
|
||||||
// DM messages don't get Channel Create events first, so we need to save
|
// DM messages don't get Channel Create events first, so we need to save
|
||||||
// some kind of stub channel object until we get the real one
|
// some kind of stub channel object until we get the real one
|
||||||
return guildId != null ? default : cache.SaveDmChannelStub(channelId);
|
guildId != null ? default : cache.SaveDmChannelStub(channelId);
|
||||||
}
|
|
||||||
|
|
||||||
private static async ValueTask SaveThreadListSync(this IDiscordCache cache, ThreadListSyncEvent evt)
|
private static async ValueTask SaveThreadListSync(this IDiscordCache cache, ThreadListSyncEvent evt)
|
||||||
{
|
{
|
||||||
@ -120,6 +115,4 @@ namespace Myriad.Cache
|
|||||||
|
|
||||||
return PermissionSet.Dm;
|
return PermissionSet.Dm;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,10 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Cache
|
namespace Myriad.Cache;
|
||||||
{
|
|
||||||
public interface IDiscordCache
|
public interface IDiscordCache
|
||||||
{
|
{
|
||||||
public ValueTask SaveOwnUser(ulong userId);
|
public ValueTask SaveOwnUser(ulong userId);
|
||||||
@ -31,4 +28,3 @@ namespace Myriad.Cache
|
|||||||
public IAsyncEnumerable<Guild> GetAllGuilds();
|
public IAsyncEnumerable<Guild> GetAllGuilds();
|
||||||
public Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId);
|
public Task<IEnumerable<Channel>> GetGuildChannels(ulong guildId);
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,21 +1,17 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Cache
|
namespace Myriad.Cache;
|
||||||
{
|
|
||||||
public class MemoryDiscordCache: IDiscordCache
|
public class MemoryDiscordCache: IDiscordCache
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<ulong, Channel> _channels = new();
|
private readonly ConcurrentDictionary<ulong, Channel> _channels = new();
|
||||||
private readonly ConcurrentDictionary<ulong, ulong> _dmChannels = 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, CachedGuild> _guilds = new();
|
||||||
private readonly ConcurrentDictionary<ulong, Role> _roles = new();
|
private readonly ConcurrentDictionary<ulong, Role> _roles = new();
|
||||||
private readonly ConcurrentDictionary<ulong, User> _users = new();
|
private readonly ConcurrentDictionary<ulong, User> _users = new();
|
||||||
private readonly ConcurrentDictionary<ulong, GuildMemberPartial> _guildMembers = new();
|
|
||||||
private ulong? _ownUserId { get; set; }
|
private ulong? _ownUserId { get; set; }
|
||||||
|
|
||||||
public ValueTask SaveGuild(Guild guild)
|
public ValueTask SaveGuild(Guild guild)
|
||||||
@ -38,14 +34,12 @@ namespace Myriad.Cache
|
|||||||
guild.Channels.TryAdd(channel.Id, true);
|
guild.Channels.TryAdd(channel.Id, true);
|
||||||
|
|
||||||
if (channel.Recipients != null)
|
if (channel.Recipients != null)
|
||||||
{
|
|
||||||
foreach (var recipient in channel.Recipients)
|
foreach (var recipient in channel.Recipients)
|
||||||
{
|
{
|
||||||
_dmChannels[recipient.Id] = channel.Id;
|
_dmChannels[recipient.Id] = channel.Id;
|
||||||
await SaveUser(recipient);
|
await SaveUser(recipient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask SaveOwnUser(ulong userId)
|
public ValueTask SaveOwnUser(ulong userId)
|
||||||
{
|
{
|
||||||
@ -86,16 +80,11 @@ namespace Myriad.Cache
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
|
||||||
_guilds[guildId] = guild with
|
_guilds[guildId] = guild with
|
||||||
{
|
{
|
||||||
Guild = guild.Guild with
|
Guild = guild.Guild with { Roles = guild.Guild.Roles.Concat(new[] { role }).ToArray() }
|
||||||
{
|
|
||||||
Roles = guild.Guild.Roles.Concat(new[] { role }).ToArray()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
@ -104,11 +93,7 @@ namespace Myriad.Cache
|
|||||||
{
|
{
|
||||||
// Use existing channel object if present, otherwise add a stub
|
// 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
|
// We may get a message create before channel create and we want to have it saved
|
||||||
_channels.GetOrAdd(channelId, id => new Channel
|
_channels.GetOrAdd(channelId, id => new Channel { Id = id, Type = Channel.ChannelType.Dm });
|
||||||
{
|
|
||||||
Id = id,
|
|
||||||
Type = Channel.ChannelType.Dm
|
|
||||||
});
|
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,4 +188,3 @@ namespace Myriad.Cache
|
|||||||
public readonly ConcurrentDictionary<ulong, bool> Channels = new();
|
public readonly ConcurrentDictionary<ulong, bool> Channels = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,12 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Cache;
|
using Myriad.Cache;
|
||||||
using Myriad.Rest;
|
using Myriad.Rest;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions;
|
||||||
{
|
|
||||||
public static class CacheExtensions
|
public static class CacheExtensions
|
||||||
{
|
{
|
||||||
public static async Task<Guild> GetGuild(this IDiscordCache cache, ulong guildId)
|
public static async Task<Guild> GetGuild(this IDiscordCache cache, ulong guildId)
|
||||||
@ -37,7 +34,8 @@ namespace Myriad.Extensions
|
|||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async ValueTask<User?> GetOrFetchUser(this IDiscordCache cache, DiscordApiClient rest, ulong userId)
|
public static async ValueTask<User?> GetOrFetchUser(this IDiscordCache cache, DiscordApiClient rest,
|
||||||
|
ulong userId)
|
||||||
{
|
{
|
||||||
if (await cache.TryGetUser(userId) is User cacheUser)
|
if (await cache.TryGetUser(userId) is User cacheUser)
|
||||||
return cacheUser;
|
return cacheUser;
|
||||||
@ -48,7 +46,8 @@ namespace Myriad.Extensions
|
|||||||
return restUser;
|
return restUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async ValueTask<Channel?> GetOrFetchChannel(this IDiscordCache cache, DiscordApiClient rest, ulong channelId)
|
public static async ValueTask<Channel?> GetOrFetchChannel(this IDiscordCache cache, DiscordApiClient rest,
|
||||||
|
ulong channelId)
|
||||||
{
|
{
|
||||||
if (await cache.TryGetChannel(channelId) is { } cacheChannel)
|
if (await cache.TryGetChannel(channelId) is { } cacheChannel)
|
||||||
return cacheChannel;
|
return cacheChannel;
|
||||||
@ -59,7 +58,8 @@ namespace Myriad.Extensions
|
|||||||
return restChannel;
|
return restChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Channel> GetOrCreateDmChannel(this IDiscordCache cache, DiscordApiClient rest, ulong recipientId)
|
public static async Task<Channel> GetOrCreateDmChannel(this IDiscordCache cache, DiscordApiClient rest,
|
||||||
|
ulong recipientId)
|
||||||
{
|
{
|
||||||
if (await cache.TryGetDmChannel(recipientId) is { } cacheChannel)
|
if (await cache.TryGetDmChannel(recipientId) is { } cacheChannel)
|
||||||
return cacheChannel;
|
return cacheChannel;
|
||||||
@ -79,4 +79,3 @@ namespace Myriad.Extensions
|
|||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions;
|
||||||
{
|
|
||||||
public static class ChannelExtensions
|
public static class ChannelExtensions
|
||||||
{
|
{
|
||||||
public static string Mention(this Channel channel) => $"<#{channel.Id}>";
|
public static string Mention(this Channel channel) => $"<#{channel.Id}>";
|
||||||
@ -13,4 +13,3 @@ namespace Myriad.Extensions
|
|||||||
or Channel.ChannelType.GuildPrivateThread
|
or Channel.ChannelType.GuildPrivateThread
|
||||||
or Channel.ChannelType.GuildNewsThread;
|
or Channel.ChannelType.GuildNewsThread;
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions;
|
||||||
{
|
|
||||||
public static class GuildExtensions
|
public static class GuildExtensions
|
||||||
{
|
{
|
||||||
public static int FileSizeLimit(this Guild guild)
|
public static int FileSizeLimit(this Guild guild)
|
||||||
@ -19,4 +19,3 @@ namespace Myriad.Extensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
using Myriad.Gateway;
|
using Myriad.Gateway;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions;
|
||||||
{
|
|
||||||
public static class MessageExtensions
|
public static class MessageExtensions
|
||||||
{
|
{
|
||||||
public static string JumpLink(this Message msg) =>
|
public static string JumpLink(this Message msg) =>
|
||||||
@ -11,4 +11,3 @@ namespace Myriad.Extensions
|
|||||||
public static string JumpLink(this MessageReactionAddEvent msg) =>
|
public static string JumpLink(this MessageReactionAddEvent msg) =>
|
||||||
$"https://discord.com/channels/{msg.GuildId}/{msg.ChannelId}/{msg.MessageId}";
|
$"https://discord.com/channels/{msg.GuildId}/{msg.ChannelId}/{msg.MessageId}";
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,23 +1,45 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Cache;
|
using Myriad.Cache;
|
||||||
using Myriad.Gateway;
|
using Myriad.Gateway;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions;
|
||||||
{
|
|
||||||
public static class PermissionExtensions
|
public static class PermissionExtensions
|
||||||
{
|
{
|
||||||
public static Task<PermissionSet> PermissionsFor(this IDiscordCache cache, MessageCreateEvent message) =>
|
private const PermissionSet NeedsViewChannel =
|
||||||
PermissionsFor(cache, message.ChannelId, message.Author.Id, message.Member, isWebhook: message.WebhookId != null);
|
PermissionSet.SendMessages |
|
||||||
|
PermissionSet.SendTtsMessages |
|
||||||
|
PermissionSet.ManageMessages |
|
||||||
|
PermissionSet.EmbedLinks |
|
||||||
|
PermissionSet.AttachFiles |
|
||||||
|
PermissionSet.ReadMessageHistory |
|
||||||
|
PermissionSet.MentionEveryone |
|
||||||
|
PermissionSet.UseExternalEmojis |
|
||||||
|
PermissionSet.AddReactions |
|
||||||
|
PermissionSet.Connect |
|
||||||
|
PermissionSet.Speak |
|
||||||
|
PermissionSet.MuteMembers |
|
||||||
|
PermissionSet.DeafenMembers |
|
||||||
|
PermissionSet.MoveMembers |
|
||||||
|
PermissionSet.UseVad |
|
||||||
|
PermissionSet.Stream |
|
||||||
|
PermissionSet.PrioritySpeaker;
|
||||||
|
|
||||||
public static Task<PermissionSet> PermissionsFor(this IDiscordCache cache, ulong channelId, GuildMember member) =>
|
private const PermissionSet NeedsSendMessages =
|
||||||
|
PermissionSet.MentionEveryone |
|
||||||
|
PermissionSet.SendTtsMessages |
|
||||||
|
PermissionSet.AttachFiles |
|
||||||
|
PermissionSet.EmbedLinks;
|
||||||
|
|
||||||
|
public static Task<PermissionSet> PermissionsFor(this IDiscordCache cache, MessageCreateEvent message) =>
|
||||||
|
PermissionsFor(cache, message.ChannelId, message.Author.Id, message.Member, message.WebhookId != null);
|
||||||
|
|
||||||
|
public static Task<PermissionSet>
|
||||||
|
PermissionsFor(this IDiscordCache cache, ulong channelId, GuildMember member) =>
|
||||||
PermissionsFor(cache, channelId, member.User.Id, member);
|
PermissionsFor(cache, channelId, member.User.Id, member);
|
||||||
|
|
||||||
public static async Task<PermissionSet> PermissionsFor(this IDiscordCache cache, ulong channelId, ulong userId, GuildMemberPartial? member, bool isWebhook = false)
|
public static async Task<PermissionSet> PermissionsFor(this IDiscordCache cache, ulong channelId, ulong userId,
|
||||||
|
GuildMemberPartial? member, bool isWebhook = false)
|
||||||
{
|
{
|
||||||
if (!(await cache.TryGetChannel(channelId) is Channel channel))
|
if (!(await cache.TryGetChannel(channelId) is Channel channel))
|
||||||
// todo: handle channel not found better
|
// todo: handle channel not found better
|
||||||
@ -59,7 +81,8 @@ namespace Myriad.Extensions
|
|||||||
public static PermissionSet PermissionsFor(Guild guild, Channel channel, MessageCreateEvent msg) =>
|
public static PermissionSet PermissionsFor(Guild guild, Channel channel, MessageCreateEvent msg) =>
|
||||||
PermissionsFor(guild, channel, msg.Author.Id, msg.Member);
|
PermissionsFor(guild, channel, msg.Author.Id, msg.Member);
|
||||||
|
|
||||||
public static PermissionSet PermissionsFor(Guild guild, Channel channel, ulong userId, GuildMemberPartial? member)
|
public static PermissionSet PermissionsFor(Guild guild, Channel channel, ulong userId,
|
||||||
|
GuildMemberPartial? member)
|
||||||
{
|
{
|
||||||
if (channel.Type == Channel.ChannelType.Dm)
|
if (channel.Type == Channel.ChannelType.Dm)
|
||||||
return PermissionSet.Dm;
|
return PermissionSet.Dm;
|
||||||
@ -90,10 +113,8 @@ namespace Myriad.Extensions
|
|||||||
|
|
||||||
var perms = PermissionSet.None;
|
var perms = PermissionSet.None;
|
||||||
foreach (var role in guild.Roles)
|
foreach (var role in guild.Roles)
|
||||||
{
|
|
||||||
if (role.Id == guild.Id || roleIds.Contains(role.Id))
|
if (role.Id == guild.Id || roleIds.Contains(role.Id))
|
||||||
perms |= role.Permissions;
|
perms |= role.Permissions;
|
||||||
}
|
|
||||||
|
|
||||||
if (perms.HasFlag(PermissionSet.Administrator))
|
if (perms.HasFlag(PermissionSet.Administrator))
|
||||||
return PermissionSet.All;
|
return PermissionSet.All;
|
||||||
@ -115,7 +136,6 @@ namespace Myriad.Extensions
|
|||||||
var userAllow = PermissionSet.None;
|
var userAllow = PermissionSet.None;
|
||||||
|
|
||||||
foreach (var overwrite in channel.PermissionOverwrites)
|
foreach (var overwrite in channel.PermissionOverwrites)
|
||||||
{
|
|
||||||
switch (overwrite.Type)
|
switch (overwrite.Type)
|
||||||
{
|
{
|
||||||
case Channel.OverwriteType.Role when overwrite.Id == channel.GuildId:
|
case Channel.OverwriteType.Role when overwrite.Id == channel.GuildId:
|
||||||
@ -131,7 +151,6 @@ namespace Myriad.Extensions
|
|||||||
userAllow |= overwrite.Allow;
|
userAllow |= overwrite.Allow;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
perms &= ~everyoneDeny;
|
perms &= ~everyoneDeny;
|
||||||
perms |= everyoneAllow;
|
perms |= everyoneAllow;
|
||||||
@ -142,35 +161,7 @@ namespace Myriad.Extensions
|
|||||||
return perms;
|
return perms;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const PermissionSet NeedsViewChannel =
|
public static string ToPermissionString(this PermissionSet perms) =>
|
||||||
PermissionSet.SendMessages |
|
|
||||||
PermissionSet.SendTtsMessages |
|
|
||||||
PermissionSet.ManageMessages |
|
|
||||||
PermissionSet.EmbedLinks |
|
|
||||||
PermissionSet.AttachFiles |
|
|
||||||
PermissionSet.ReadMessageHistory |
|
|
||||||
PermissionSet.MentionEveryone |
|
|
||||||
PermissionSet.UseExternalEmojis |
|
|
||||||
PermissionSet.AddReactions |
|
|
||||||
PermissionSet.Connect |
|
|
||||||
PermissionSet.Speak |
|
|
||||||
PermissionSet.MuteMembers |
|
|
||||||
PermissionSet.DeafenMembers |
|
|
||||||
PermissionSet.MoveMembers |
|
|
||||||
PermissionSet.UseVad |
|
|
||||||
PermissionSet.Stream |
|
|
||||||
PermissionSet.PrioritySpeaker;
|
|
||||||
|
|
||||||
private const PermissionSet NeedsSendMessages =
|
|
||||||
PermissionSet.MentionEveryone |
|
|
||||||
PermissionSet.SendTtsMessages |
|
|
||||||
PermissionSet.AttachFiles |
|
|
||||||
PermissionSet.EmbedLinks;
|
|
||||||
|
|
||||||
public static string ToPermissionString(this PermissionSet perms)
|
|
||||||
{
|
|
||||||
// TODO: clean string
|
// TODO: clean string
|
||||||
return perms.ToString();
|
perms.ToString();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,9 +1,7 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions;
|
||||||
{
|
|
||||||
public static class SnowflakeExtensions
|
public static class SnowflakeExtensions
|
||||||
{
|
{
|
||||||
public static readonly DateTimeOffset DiscordEpoch = new(2015, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
public static readonly DateTimeOffset DiscordEpoch = new(2015, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||||
@ -17,4 +15,3 @@ namespace Myriad.Extensions
|
|||||||
public static DateTimeOffset Timestamp(this Webhook webhook) => SnowflakeToTimestamp(webhook.Id);
|
public static DateTimeOffset Timestamp(this Webhook webhook) => SnowflakeToTimestamp(webhook.Id);
|
||||||
public static DateTimeOffset Timestamp(this User user) => SnowflakeToTimestamp(user.Id);
|
public static DateTimeOffset Timestamp(this User user) => SnowflakeToTimestamp(user.Id);
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions;
|
||||||
{
|
|
||||||
public static class UserExtensions
|
public static class UserExtensions
|
||||||
{
|
{
|
||||||
public static string Mention(this User user) => $"<@{user.Id}>";
|
public static string Mention(this User user) => $"<@{user.Id}>";
|
||||||
@ -9,4 +9,3 @@ namespace Myriad.Extensions
|
|||||||
public static string AvatarUrl(this User user, string? format = "png", int? size = 128) =>
|
public static string AvatarUrl(this User user, string? format = "png", int? size = 128) =>
|
||||||
$"https://cdn.discordapp.com/avatars/{user.Id}/{user.Avatar}.{format}?size={size}";
|
$"https://cdn.discordapp.com/avatars/{user.Id}/{user.Avatar}.{format}?size={size}";
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,16 +1,12 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Gateway.Limit;
|
using Myriad.Gateway.Limit;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public class Cluster
|
public class Cluster
|
||||||
{
|
{
|
||||||
private readonly GatewaySettings _gatewaySettings;
|
private readonly GatewaySettings _gatewaySettings;
|
||||||
@ -25,9 +21,9 @@ namespace Myriad.Gateway
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Func<Shard, IGatewayEvent, Task>? EventReceived { get; set; }
|
public Func<Shard, IGatewayEvent, Task>? EventReceived { get; set; }
|
||||||
public event Action<Shard>? ShardCreated;
|
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, Shard> Shards => _shards;
|
public IReadOnlyDictionary<int, Shard> Shards => _shards;
|
||||||
|
public event Action<Shard>? ShardCreated;
|
||||||
|
|
||||||
public async Task Start(GatewayInfo.Bot info)
|
public async Task Start(GatewayInfo.Bot info)
|
||||||
{
|
{
|
||||||
@ -46,6 +42,7 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
await StartShards();
|
await StartShards();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartShards()
|
private async Task StartShards()
|
||||||
{
|
{
|
||||||
_logger.Information("Connecting shards...");
|
_logger.Information("Connecting shards...");
|
||||||
@ -79,12 +76,9 @@ namespace Myriad.Gateway
|
|||||||
private IGatewayRatelimiter GetRateLimiter(int recommendedConcurrency)
|
private IGatewayRatelimiter GetRateLimiter(int recommendedConcurrency)
|
||||||
{
|
{
|
||||||
if (_gatewaySettings.GatewayQueueUrl != null)
|
if (_gatewaySettings.GatewayQueueUrl != null)
|
||||||
{
|
|
||||||
return new TwilightGatewayRatelimiter(_logger, _gatewaySettings.GatewayQueueUrl);
|
return new TwilightGatewayRatelimiter(_logger, _gatewaySettings.GatewayQueueUrl);
|
||||||
}
|
|
||||||
|
|
||||||
var concurrency = GetActualShardConcurrency(recommendedConcurrency);
|
var concurrency = GetActualShardConcurrency(recommendedConcurrency);
|
||||||
return new LocalGatewayRatelimiter(_logger, concurrency);
|
return new LocalGatewayRatelimiter(_logger, concurrency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ChannelCreateEvent: Channel, IGatewayEvent;
|
public record ChannelCreateEvent: Channel, IGatewayEvent;
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ChannelDeleteEvent: Channel, IGatewayEvent;
|
public record ChannelDeleteEvent: Channel, IGatewayEvent;
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ChannelUpdateEvent: Channel, IGatewayEvent;
|
public record ChannelUpdateEvent: Channel, IGatewayEvent;
|
||||||
}
|
|
@ -1,11 +1,10 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GuildCreateEvent: Guild, IGatewayEvent
|
public record GuildCreateEvent: Guild, IGatewayEvent
|
||||||
{
|
{
|
||||||
public Channel[] Channels { get; init; }
|
public Channel[] Channels { get; init; }
|
||||||
public GuildMember[] Members { get; init; }
|
public GuildMember[] Members { get; init; }
|
||||||
public Channel[] Threads { get; init; }
|
public Channel[] Threads { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GuildDeleteEvent(ulong Id, bool Unavailable): IGatewayEvent;
|
public record GuildDeleteEvent(ulong Id, bool Unavailable): IGatewayEvent;
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GuildMemberAddEvent: GuildMember, IGatewayEvent
|
public record GuildMemberAddEvent: GuildMember, IGatewayEvent
|
||||||
{
|
{
|
||||||
public ulong GuildId { get; init; }
|
public ulong GuildId { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,10 +1,9 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public class GuildMemberRemoveEvent: IGatewayEvent
|
public class GuildMemberRemoveEvent: IGatewayEvent
|
||||||
{
|
{
|
||||||
public ulong GuildId { get; init; }
|
public ulong GuildId { get; init; }
|
||||||
public User User { get; init; }
|
public User User { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GuildMemberUpdateEvent: GuildMember, IGatewayEvent
|
public record GuildMemberUpdateEvent: GuildMember, IGatewayEvent
|
||||||
{
|
{
|
||||||
public ulong GuildId { get; init; }
|
public ulong GuildId { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GuildRoleCreateEvent(ulong GuildId, Role Role): IGatewayEvent;
|
public record GuildRoleCreateEvent(ulong GuildId, Role Role): IGatewayEvent;
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GuildRoleDeleteEvent(ulong GuildId, ulong RoleId): IGatewayEvent;
|
public record GuildRoleDeleteEvent(ulong GuildId, ulong RoleId): IGatewayEvent;
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GuildRoleUpdateEvent(ulong GuildId, Role Role): IGatewayEvent;
|
public record GuildRoleUpdateEvent(ulong GuildId, Role Role): IGatewayEvent;
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GuildUpdateEvent: Guild, IGatewayEvent;
|
public record GuildUpdateEvent: Guild, IGatewayEvent;
|
||||||
}
|
|
@ -1,8 +1,5 @@
|
|||||||
using System;
|
namespace Myriad.Gateway;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
|
||||||
{
|
|
||||||
public interface IGatewayEvent
|
public interface IGatewayEvent
|
||||||
{
|
{
|
||||||
public static readonly Dictionary<string, Type> EventTypes = new()
|
public static readonly Dictionary<string, Type> EventTypes = new()
|
||||||
@ -36,4 +33,3 @@ namespace Myriad.Gateway
|
|||||||
{ "INTERACTION_CREATE", typeof(InteractionCreateEvent) }
|
{ "INTERACTION_CREATE", typeof(InteractionCreateEvent) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record InteractionCreateEvent: Interaction, IGatewayEvent;
|
public record InteractionCreateEvent: Interaction, IGatewayEvent;
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record MessageCreateEvent: Message, IGatewayEvent
|
public record MessageCreateEvent: Message, IGatewayEvent
|
||||||
{
|
{
|
||||||
public GuildMemberPartial? Member { get; init; }
|
public GuildMemberPartial? Member { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record MessageDeleteBulkEvent(ulong[] Ids, ulong ChannelId, ulong? GuildId): IGatewayEvent;
|
public record MessageDeleteBulkEvent(ulong[] Ids, ulong ChannelId, ulong? GuildId): IGatewayEvent;
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record MessageDeleteEvent(ulong Id, ulong ChannelId, ulong? GuildId): IGatewayEvent;
|
public record MessageDeleteEvent(ulong Id, ulong ChannelId, ulong? GuildId): IGatewayEvent;
|
||||||
}
|
|
@ -1,8 +1,7 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record MessageReactionAddEvent(ulong UserId, ulong ChannelId, ulong MessageId, ulong? GuildId,
|
public record MessageReactionAddEvent(ulong UserId, ulong ChannelId, ulong MessageId, ulong? GuildId,
|
||||||
GuildMember? Member,
|
GuildMember? Member,
|
||||||
Emoji Emoji): IGatewayEvent;
|
Emoji Emoji): IGatewayEvent;
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record MessageReactionRemoveAllEvent(ulong ChannelId, ulong MessageId, ulong? GuildId): IGatewayEvent;
|
public record MessageReactionRemoveAllEvent(ulong ChannelId, ulong MessageId, ulong? GuildId): IGatewayEvent;
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record MessageReactionRemoveEmojiEvent
|
public record MessageReactionRemoveEmojiEvent
|
||||||
(ulong ChannelId, ulong MessageId, ulong? GuildId, Emoji Emoji): IGatewayEvent;
|
(ulong ChannelId, ulong MessageId, ulong? GuildId, Emoji Emoji): IGatewayEvent;
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record MessageReactionRemoveEvent
|
public record MessageReactionRemoveEvent
|
||||||
(ulong UserId, ulong ChannelId, ulong MessageId, ulong? GuildId, Emoji Emoji): IGatewayEvent;
|
(ulong UserId, ulong ChannelId, ulong MessageId, ulong? GuildId, Emoji Emoji): IGatewayEvent;
|
||||||
}
|
|
@ -1,15 +1,15 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record MessageUpdateEvent(ulong Id, ulong ChannelId): IGatewayEvent
|
public record MessageUpdateEvent(ulong Id, ulong ChannelId): IGatewayEvent
|
||||||
{
|
{
|
||||||
public Optional<string?> Content { get; init; }
|
public Optional<string?> Content { get; init; }
|
||||||
public Optional<User> Author { get; init; }
|
public Optional<User> Author { get; init; }
|
||||||
public Optional<GuildMemberPartial> Member { get; init; }
|
public Optional<GuildMemberPartial> Member { get; init; }
|
||||||
public Optional<Message.Attachment[]> Attachments { get; init; }
|
public Optional<Message.Attachment[]> Attachments { get; init; }
|
||||||
|
|
||||||
public Optional<ulong?> GuildId { get; init; }
|
public Optional<ulong?> GuildId { get; init; }
|
||||||
// TODO: lots of partials
|
// TODO: lots of partials
|
||||||
}
|
}
|
||||||
}
|
|
@ -2,8 +2,8 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ReadyEvent: IGatewayEvent
|
public record ReadyEvent: IGatewayEvent
|
||||||
{
|
{
|
||||||
[JsonPropertyName("v")] public int Version { get; init; }
|
[JsonPropertyName("v")] public int Version { get; init; }
|
||||||
@ -12,4 +12,3 @@ namespace Myriad.Gateway
|
|||||||
public ShardInfo? Shard { get; init; }
|
public ShardInfo? Shard { get; init; }
|
||||||
public ApplicationPartial Application { get; init; }
|
public ApplicationPartial Application { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ResumedEvent: IGatewayEvent;
|
public record ResumedEvent: IGatewayEvent;
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ThreadCreateEvent: Channel, IGatewayEvent;
|
public record ThreadCreateEvent: Channel, IGatewayEvent;
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ThreadDeleteEvent: IGatewayEvent
|
public record ThreadDeleteEvent: IGatewayEvent
|
||||||
{
|
{
|
||||||
public ulong Id { get; init; }
|
public ulong Id { get; init; }
|
||||||
@ -9,4 +9,3 @@ namespace Myriad.Gateway
|
|||||||
public ulong? ParentId { get; init; }
|
public ulong? ParentId { get; init; }
|
||||||
public Channel.ChannelType Type { get; init; }
|
public Channel.ChannelType Type { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,11 +1,10 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ThreadListSyncEvent: IGatewayEvent
|
public record ThreadListSyncEvent: IGatewayEvent
|
||||||
{
|
{
|
||||||
public ulong GuildId { get; init; }
|
public ulong GuildId { get; init; }
|
||||||
public ulong[]? ChannelIds { get; init; }
|
public ulong[]? ChannelIds { get; init; }
|
||||||
public Channel[] Threads { get; init; }
|
public Channel[] Threads { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ThreadUpdateEvent: Channel, IGatewayEvent;
|
public record ThreadUpdateEvent: Channel, IGatewayEvent;
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
using System;
|
namespace Myriad.Gateway;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
|
||||||
{
|
|
||||||
// TODO: unused?
|
// TODO: unused?
|
||||||
public class GatewayCloseException: Exception
|
public class GatewayCloseException: Exception
|
||||||
{
|
{
|
||||||
@ -32,4 +30,3 @@ namespace Myriad.Gateway
|
|||||||
public const int InvalidIntent = 4013;
|
public const int InvalidIntent = 4013;
|
||||||
public const int DisallowedIntent = 4014;
|
public const int DisallowedIntent = 4014;
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
using System;
|
namespace Myriad.Gateway;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
|
||||||
{
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum GatewayIntent
|
public enum GatewayIntent
|
||||||
{
|
{
|
||||||
@ -21,4 +19,3 @@ namespace Myriad.Gateway
|
|||||||
DirectMessageReactions = 1 << 13,
|
DirectMessageReactions = 1 << 13,
|
||||||
DirectMessageTyping = 1 << 14
|
DirectMessageTyping = 1 << 14
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GatewayPacket
|
public record GatewayPacket
|
||||||
{
|
{
|
||||||
[JsonPropertyName("op")] public GatewayOpcode Opcode { get; init; }
|
[JsonPropertyName("op")] public GatewayOpcode Opcode { get; init; }
|
||||||
@ -30,4 +30,3 @@ namespace Myriad.Gateway
|
|||||||
Hello = 10,
|
Hello = 10,
|
||||||
HeartbeatAck = 11
|
HeartbeatAck = 11
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GatewaySettings
|
public record GatewaySettings
|
||||||
{
|
{
|
||||||
public string Token { get; init; }
|
public string Token { get; init; }
|
||||||
@ -7,4 +7,3 @@ namespace Myriad.Gateway
|
|||||||
public int? MaxShardConcurrency { get; init; }
|
public int? MaxShardConcurrency { get; init; }
|
||||||
public string? GatewayQueueUrl { get; init; }
|
public string? GatewayQueueUrl { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
namespace Myriad.Gateway.Limit;
|
||||||
|
|
||||||
namespace Myriad.Gateway.Limit
|
|
||||||
{
|
|
||||||
public interface IGatewayRatelimiter
|
public interface IGatewayRatelimiter
|
||||||
{
|
{
|
||||||
public Task Identify(int shard);
|
public Task Identify(int shard);
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,21 +1,19 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Gateway.Limit
|
namespace Myriad.Gateway.Limit;
|
||||||
{
|
|
||||||
public class LocalGatewayRatelimiter: IGatewayRatelimiter
|
public class LocalGatewayRatelimiter: IGatewayRatelimiter
|
||||||
{
|
{
|
||||||
// docs specify 5 seconds, but we're actually throttling connections, not identify, so we need a bit of leeway
|
// docs specify 5 seconds, but we're actually throttling connections, not identify, so we need a bit of leeway
|
||||||
private static readonly TimeSpan BucketLength = TimeSpan.FromSeconds(6);
|
private static readonly TimeSpan BucketLength = TimeSpan.FromSeconds(6);
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<int, ConcurrentQueue<TaskCompletionSource>> _buckets = new();
|
private readonly ConcurrentDictionary<int, ConcurrentQueue<TaskCompletionSource>> _buckets = new();
|
||||||
|
private readonly ILogger _logger;
|
||||||
private readonly int _maxConcurrency;
|
private readonly int _maxConcurrency;
|
||||||
|
|
||||||
private Task? _refillTask;
|
private Task? _refillTask;
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
public LocalGatewayRatelimiter(ILogger logger, int maxConcurrency)
|
public LocalGatewayRatelimiter(ILogger logger, int maxConcurrency)
|
||||||
{
|
{
|
||||||
@ -70,4 +68,3 @@ namespace Myriad.Gateway.Limit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,19 +1,13 @@
|
|||||||
using System;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Gateway.Limit
|
namespace Myriad.Gateway.Limit;
|
||||||
{
|
|
||||||
public class TwilightGatewayRatelimiter: IGatewayRatelimiter
|
public class TwilightGatewayRatelimiter: IGatewayRatelimiter
|
||||||
{
|
{
|
||||||
private readonly string _url;
|
private readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(60) };
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly HttpClient _httpClient = new()
|
private readonly string _url;
|
||||||
{
|
|
||||||
Timeout = TimeSpan.FromSeconds(60)
|
|
||||||
};
|
|
||||||
|
|
||||||
public TwilightGatewayRatelimiter(ILogger logger, string url)
|
public TwilightGatewayRatelimiter(ILogger logger, string url)
|
||||||
{
|
{
|
||||||
@ -24,7 +18,6 @@ namespace Myriad.Gateway.Limit
|
|||||||
public async Task Identify(int shard)
|
public async Task Identify(int shard)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.Information("Shard {ShardId}: Requesting identify at gateway queue {GatewayQueueUrl}",
|
_logger.Information("Shard {ShardId}: Requesting identify at gateway queue {GatewayQueueUrl}",
|
||||||
@ -32,10 +25,6 @@ namespace Myriad.Gateway.Limit
|
|||||||
await _httpClient.GetAsync(_url);
|
await _httpClient.GetAsync(_url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (TimeoutException)
|
catch (TimeoutException) { }
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GatewayHello(int HeartbeatInterval);
|
public record GatewayHello(int HeartbeatInterval);
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GatewayIdentify
|
public record GatewayIdentify
|
||||||
{
|
{
|
||||||
public string Token { get; init; }
|
public string Token { get; init; }
|
||||||
@ -25,4 +25,3 @@ namespace Myriad.Gateway
|
|||||||
[JsonPropertyName("$device")] public string Device { get; init; }
|
[JsonPropertyName("$device")] public string Device { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GatewayResume(string Token, string SessionId, int Seq);
|
public record GatewayResume(string Token, string SessionId, int Seq);
|
||||||
}
|
|
@ -3,8 +3,8 @@ using System.Text.Json.Serialization;
|
|||||||
using Myriad.Serialization;
|
using Myriad.Serialization;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record GatewayStatusUpdate
|
public record GatewayStatusUpdate
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonSnakeCaseStringEnumConverter))]
|
[JsonConverter(typeof(JsonSnakeCaseStringEnumConverter))]
|
||||||
@ -22,4 +22,3 @@ namespace Myriad.Gateway
|
|||||||
public UserStatus Status { get; init; }
|
public UserStatus Status { get; init; }
|
||||||
public bool Afk { get; init; }
|
public bool Afk { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
using System;
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Gateway.Limit;
|
using Myriad.Gateway.Limit;
|
||||||
using Myriad.Gateway.State;
|
using Myriad.Gateway.State;
|
||||||
@ -11,8 +9,8 @@ using Myriad.Types;
|
|||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Context;
|
using Serilog.Context;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public class Shard
|
public class Shard
|
||||||
{
|
{
|
||||||
private const string LibraryName = "Myriad (for PluralKit)";
|
private const string LibraryName = "Myriad (for PluralKit)";
|
||||||
@ -121,13 +119,11 @@ namespace Myriad.Gateway
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateStatus(GatewayStatusUpdate payload)
|
public async Task UpdateStatus(GatewayStatusUpdate payload)
|
||||||
{
|
=> await _conn.Send(new GatewayPacket
|
||||||
await _conn.Send(new GatewayPacket
|
|
||||||
{
|
{
|
||||||
Opcode = GatewayOpcode.PresenceUpdate,
|
Opcode = GatewayOpcode.PresenceUpdate,
|
||||||
Payload = payload
|
Payload = payload
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ConnectInner()
|
private async Task ConnectInner()
|
||||||
{
|
{
|
||||||
@ -149,14 +145,11 @@ namespace Myriad.Gateway
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DisconnectInner(WebSocketCloseStatus closeStatus)
|
private Task DisconnectInner(WebSocketCloseStatus closeStatus)
|
||||||
{
|
=> _conn.Disconnect(closeStatus, null);
|
||||||
await _conn.Disconnect(closeStatus, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendIdentify()
|
private async Task SendIdentify()
|
||||||
{
|
=> await _conn.Send(new GatewayPacket
|
||||||
await _conn.Send(new GatewayPacket
|
|
||||||
{
|
{
|
||||||
Opcode = GatewayOpcode.Identify,
|
Opcode = GatewayOpcode.Identify,
|
||||||
Payload = new GatewayIdentify
|
Payload = new GatewayIdentify
|
||||||
@ -174,21 +167,16 @@ namespace Myriad.Gateway
|
|||||||
LargeThreshold = 50
|
LargeThreshold = 50
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendResume((string SessionId, int? LastSeq) arg)
|
private async Task SendResume((string SessionId, int? LastSeq) arg)
|
||||||
{
|
=> await _conn.Send(new GatewayPacket
|
||||||
await _conn.Send(new GatewayPacket
|
|
||||||
{
|
{
|
||||||
Opcode = GatewayOpcode.Resume,
|
Opcode = GatewayOpcode.Resume,
|
||||||
Payload = new GatewayResume(_settings.Token, arg.SessionId, arg.LastSeq ?? 0)
|
Payload = new GatewayResume(_settings.Token, arg.SessionId, arg.LastSeq ?? 0)
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendHeartbeat(int? lastSeq)
|
private async Task SendHeartbeat(int? lastSeq)
|
||||||
{
|
=> await _conn.Send(new GatewayPacket { Opcode = GatewayOpcode.Heartbeat, Payload = lastSeq });
|
||||||
await _conn.Send(new GatewayPacket { Opcode = GatewayOpcode.Heartbeat, Payload = lastSeq });
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Reconnect(WebSocketCloseStatus closeStatus, TimeSpan delay)
|
private async Task Reconnect(WebSocketCloseStatus closeStatus, TimeSpan delay)
|
||||||
{
|
{
|
||||||
@ -221,4 +209,3 @@ namespace Myriad.Gateway
|
|||||||
SocketClosed?.Invoke(closeStatus, description);
|
SocketClosed?.Invoke(closeStatus, description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,27 +1,30 @@
|
|||||||
using System;
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public class ShardConnection: IAsyncDisposable
|
public class ShardConnection: IAsyncDisposable
|
||||||
{
|
{
|
||||||
private ClientWebSocket? _client;
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ShardPacketSerializer _serializer;
|
private readonly ShardPacketSerializer _serializer;
|
||||||
|
private ClientWebSocket? _client;
|
||||||
|
|
||||||
|
public ShardConnection(JsonSerializerOptions jsonSerializerOptions, ILogger logger)
|
||||||
|
{
|
||||||
|
_logger = logger.ForContext<ShardConnection>();
|
||||||
|
_serializer = new ShardPacketSerializer(jsonSerializerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
public WebSocketState State => _client?.State ?? WebSocketState.Closed;
|
public WebSocketState State => _client?.State ?? WebSocketState.Closed;
|
||||||
public WebSocketCloseStatus? CloseStatus => _client?.CloseStatus;
|
public WebSocketCloseStatus? CloseStatus => _client?.CloseStatus;
|
||||||
public string? CloseStatusDescription => _client?.CloseStatusDescription;
|
public string? CloseStatusDescription => _client?.CloseStatusDescription;
|
||||||
|
|
||||||
public ShardConnection(JsonSerializerOptions jsonSerializerOptions, ILogger logger)
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
_logger = logger.ForContext<ShardConnection>();
|
await CloseInner(WebSocketCloseStatus.NormalClosure, null);
|
||||||
_serializer = new(jsonSerializerOptions);
|
_client?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Connect(string url, CancellationToken ct)
|
public async Task Connect(string url, CancellationToken ct)
|
||||||
@ -53,12 +56,6 @@ namespace Myriad.Gateway
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
|
||||||
{
|
|
||||||
await CloseInner(WebSocketCloseStatus.NormalClosure, null);
|
|
||||||
_client?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<GatewayPacket?> Read()
|
public async Task<GatewayPacket?> Read()
|
||||||
{
|
{
|
||||||
// from `ManagedWebSocket.s_validReceiveStates`
|
// from `ManagedWebSocket.s_validReceiveStates`
|
||||||
@ -80,10 +77,7 @@ namespace Myriad.Gateway
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Uri GetConnectionUri(string baseUri) => new UriBuilder(baseUri)
|
private Uri GetConnectionUri(string baseUri) => new UriBuilder(baseUri) { Query = "v=9&encoding=json" }.Uri;
|
||||||
{
|
|
||||||
Query = "v=9&encoding=json"
|
|
||||||
}.Uri;
|
|
||||||
|
|
||||||
private async Task CloseInner(WebSocketCloseStatus closeStatus, string? description)
|
private async Task CloseInner(WebSocketCloseStatus closeStatus, string? description)
|
||||||
{
|
{
|
||||||
@ -119,4 +113,3 @@ namespace Myriad.Gateway
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public record ShardInfo(int ShardId, int NumShards);
|
public record ShardInfo(int ShardId, int NumShards);
|
||||||
}
|
|
@ -1,12 +1,9 @@
|
|||||||
using System;
|
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.IO;
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public class ShardPacketSerializer
|
public class ShardPacketSerializer
|
||||||
{
|
{
|
||||||
private const int BufferSize = 64 * 1024;
|
private const int BufferSize = 64 * 1024;
|
||||||
@ -40,7 +37,8 @@ namespace Myriad.Gateway
|
|||||||
await socket.SendAsync(bytes.AsMemory(), WebSocketMessageType.Text, true, default);
|
await socket.SendAsync(bytes.AsMemory(), WebSocketMessageType.Text, true, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(WebSocketMessageType type, GatewayPacket packet)> DeserializeMultipleBuffer(ClientWebSocket socket, IMemoryOwner<byte> buf, ValueWebSocketReceiveResult res)
|
private async Task<(WebSocketMessageType type, GatewayPacket packet)> DeserializeMultipleBuffer(
|
||||||
|
ClientWebSocket socket, IMemoryOwner<byte> buf, ValueWebSocketReceiveResult res)
|
||||||
{
|
{
|
||||||
await using var stream = new MemoryStream(BufferSize * 4);
|
await using var stream = new MemoryStream(BufferSize * 4);
|
||||||
stream.Write(buf.Memory.Span.Slice(0, res.Count));
|
stream.Write(buf.Memory.Span.Slice(0, res.Count));
|
||||||
@ -61,10 +59,10 @@ namespace Myriad.Gateway
|
|||||||
return DeserializeObject(res, span);
|
return DeserializeObject(res, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
private (WebSocketMessageType type, GatewayPacket packet) DeserializeObject(ValueWebSocketReceiveResult res, Span<byte> span)
|
private (WebSocketMessageType type, GatewayPacket packet) DeserializeObject(
|
||||||
|
ValueWebSocketReceiveResult res, Span<byte> span)
|
||||||
{
|
{
|
||||||
var packet = JsonSerializer.Deserialize<GatewayPacket>(span, _jsonSerializerOptions)!;
|
var packet = JsonSerializer.Deserialize<GatewayPacket>(span, _jsonSerializerOptions)!;
|
||||||
return (res.MessageType, packet);
|
return (res.MessageType, packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,5 @@
|
|||||||
using System;
|
namespace Myriad.Gateway.State;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Myriad.Gateway.State
|
|
||||||
{
|
|
||||||
public class HeartbeatWorker: IAsyncDisposable
|
public class HeartbeatWorker: IAsyncDisposable
|
||||||
{
|
{
|
||||||
private Task? _worker;
|
private Task? _worker;
|
||||||
@ -11,6 +7,11 @@ namespace Myriad.Gateway.State
|
|||||||
|
|
||||||
public TimeSpan? CurrentHeartbeatInterval { get; private set; }
|
public TimeSpan? CurrentHeartbeatInterval { get; private set; }
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await Stop();
|
||||||
|
}
|
||||||
|
|
||||||
public async ValueTask Start(TimeSpan heartbeatInterval, Func<Task> callback)
|
public async ValueTask Start(TimeSpan heartbeatInterval, Func<Task> callback)
|
||||||
{
|
{
|
||||||
if (_worker != null)
|
if (_worker != null)
|
||||||
@ -54,10 +55,4 @@ namespace Myriad.Gateway.State
|
|||||||
private static TimeSpan GetInitialHeartbeatDelay(TimeSpan heartbeatInterval) =>
|
private static TimeSpan GetInitialHeartbeatDelay(TimeSpan heartbeatInterval) =>
|
||||||
// Docs specify `heartbeat_interval * random.random()` but we'll add a lil buffer :)
|
// Docs specify `heartbeat_interval * random.random()` but we'll add a lil buffer :)
|
||||||
heartbeatInterval * (new Random().NextDouble() * 0.9 + 0.05);
|
heartbeatInterval * (new Random().NextDouble() * 0.9 + 0.05);
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
|
||||||
{
|
|
||||||
await Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Gateway.State
|
namespace Myriad.Gateway.State;
|
||||||
{
|
|
||||||
public enum ShardState
|
public enum ShardState
|
||||||
{
|
{
|
||||||
Disconnected,
|
Disconnected,
|
||||||
@ -8,4 +8,3 @@ namespace Myriad.Gateway.State
|
|||||||
Connected,
|
Connected,
|
||||||
Reconnecting
|
Reconnecting
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,32 +1,37 @@
|
|||||||
using System;
|
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Gateway.State;
|
using Myriad.Gateway.State;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway;
|
||||||
{
|
|
||||||
public class ShardStateManager
|
public class ShardStateManager
|
||||||
{
|
{
|
||||||
private readonly HeartbeatWorker _heartbeatWorker = new();
|
private readonly HeartbeatWorker _heartbeatWorker = new();
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
private readonly ShardInfo _info;
|
private readonly ShardInfo _info;
|
||||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||||
private ShardState _state = ShardState.Disconnected;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private DateTimeOffset? _lastHeartbeatSent;
|
|
||||||
private TimeSpan? _latency;
|
|
||||||
private bool _hasReceivedHeartbeatAck;
|
private bool _hasReceivedHeartbeatAck;
|
||||||
|
|
||||||
private string? _sessionId;
|
private DateTimeOffset? _lastHeartbeatSent;
|
||||||
private int? _lastSeq;
|
private int? _lastSeq;
|
||||||
|
private TimeSpan? _latency;
|
||||||
|
|
||||||
|
private string? _sessionId;
|
||||||
|
|
||||||
|
public ShardStateManager(ShardInfo info, JsonSerializerOptions jsonSerializerOptions, ILogger logger)
|
||||||
|
{
|
||||||
|
_info = info;
|
||||||
|
_jsonSerializerOptions = jsonSerializerOptions;
|
||||||
|
_logger = logger.ForContext<ShardStateManager>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShardState State { get; private set; } = ShardState.Disconnected;
|
||||||
|
|
||||||
public ShardState State => _state;
|
|
||||||
public TimeSpan? Latency => _latency;
|
public TimeSpan? Latency => _latency;
|
||||||
public User? User { get; private set; }
|
public User? User { get; private set; }
|
||||||
public ApplicationPartial? Application { get; private set; }
|
public ApplicationPartial? Application { get; private set; }
|
||||||
@ -40,16 +45,9 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
public event Action<TimeSpan> OnHeartbeatReceived;
|
public event Action<TimeSpan> OnHeartbeatReceived;
|
||||||
|
|
||||||
public ShardStateManager(ShardInfo info, JsonSerializerOptions jsonSerializerOptions, ILogger logger)
|
|
||||||
{
|
|
||||||
_info = info;
|
|
||||||
_jsonSerializerOptions = jsonSerializerOptions;
|
|
||||||
_logger = logger.ForContext<ShardStateManager>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task HandleConnectionOpened()
|
public Task HandleConnectionOpened()
|
||||||
{
|
{
|
||||||
_state = ShardState.Handshaking;
|
State = ShardState.Handshaking;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +101,7 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
await HandleEvent(evt);
|
await HandleEvent(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,7 +117,7 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
private async Task IdentifyOrResume()
|
private async Task IdentifyOrResume()
|
||||||
{
|
{
|
||||||
_state = ShardState.Identifying;
|
State = ShardState.Identifying;
|
||||||
|
|
||||||
if (_sessionId != null)
|
if (_sessionId != null)
|
||||||
{
|
{
|
||||||
@ -173,7 +172,7 @@ namespace Myriad.Gateway
|
|||||||
_logger.Information("Shard {ShardId}: Received Ready", _info.ShardId);
|
_logger.Information("Shard {ShardId}: Received Ready", _info.ShardId);
|
||||||
|
|
||||||
_sessionId = ready.SessionId;
|
_sessionId = ready.SessionId;
|
||||||
_state = ShardState.Connected;
|
State = ShardState.Connected;
|
||||||
User = ready.User;
|
User = ready.User;
|
||||||
Application = ready.Application;
|
Application = ready.Application;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -183,7 +182,7 @@ namespace Myriad.Gateway
|
|||||||
{
|
{
|
||||||
_logger.Information("Shard {ShardId}: Received Resume", _info.ShardId);
|
_logger.Information("Shard {ShardId}: Received Resume", _info.ShardId);
|
||||||
|
|
||||||
_state = ShardState.Connected;
|
State = ShardState.Connected;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +211,7 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
private async Task DoReconnect(WebSocketCloseStatus closeStatus, TimeSpan delay)
|
private async Task DoReconnect(WebSocketCloseStatus closeStatus, TimeSpan delay)
|
||||||
{
|
{
|
||||||
_state = ShardState.Reconnecting;
|
State = ShardState.Reconnecting;
|
||||||
await Reconnect(closeStatus, delay);
|
await Reconnect(closeStatus, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,15 +231,16 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.Verbose("Shard {ShardId}: Deserializing {EventType} to {ClrType}", _info.ShardId, eventType, clrType);
|
_logger.Verbose("Shard {ShardId}: Deserializing {EventType} to {ClrType}", _info.ShardId, eventType,
|
||||||
|
clrType);
|
||||||
return JsonSerializer.Deserialize(payload.GetRawText(), clrType, _jsonSerializerOptions)
|
return JsonSerializer.Deserialize(payload.GetRawText(), clrType, _jsonSerializerOptions)
|
||||||
as IGatewayEvent;
|
as IGatewayEvent;
|
||||||
}
|
}
|
||||||
catch (JsonException e)
|
catch (JsonException e)
|
||||||
{
|
{
|
||||||
_logger.Error(e, "Shard {ShardId}: Error deserializing event {EventType} to {ClrType}", _info.ShardId, eventType, clrType);
|
_logger.Error(e, "Shard {ShardId}: Error deserializing event {EventType} to {ClrType}", _info.ShardId,
|
||||||
|
eventType, clrType);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,8 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Rest.Exceptions;
|
using Myriad.Rest.Exceptions;
|
||||||
using Myriad.Rest.Ratelimit;
|
using Myriad.Rest.Ratelimit;
|
||||||
@ -19,16 +15,17 @@ using Polly;
|
|||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Context;
|
using Serilog.Context;
|
||||||
|
|
||||||
namespace Myriad.Rest
|
namespace Myriad.Rest;
|
||||||
{
|
|
||||||
public class BaseRestClient: IAsyncDisposable
|
public class BaseRestClient: IAsyncDisposable
|
||||||
{
|
{
|
||||||
|
private readonly string _baseUrl;
|
||||||
private readonly Version _httpVersion = new(2, 0);
|
private readonly Version _httpVersion = new(2, 0);
|
||||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly Ratelimiter _ratelimiter;
|
private readonly Ratelimiter _ratelimiter;
|
||||||
private readonly AsyncPolicy<HttpResponseMessage> _retryPolicy;
|
private readonly AsyncPolicy<HttpResponseMessage> _retryPolicy;
|
||||||
private readonly string _baseUrl;
|
public EventHandler<(string, int, long)> OnResponseEvent;
|
||||||
|
|
||||||
public BaseRestClient(string userAgent, string token, ILogger logger, string baseUrl)
|
public BaseRestClient(string userAgent, string token, ILogger logger, string baseUrl)
|
||||||
{
|
{
|
||||||
@ -61,7 +58,6 @@ namespace Myriad.Rest
|
|||||||
}
|
}
|
||||||
|
|
||||||
public HttpClient Client { get; }
|
public HttpClient Client { get; }
|
||||||
public EventHandler<(string, int, long)> OnResponseEvent;
|
|
||||||
|
|
||||||
public ValueTask DisposeAsync()
|
public ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
@ -94,7 +90,8 @@ namespace Myriad.Rest
|
|||||||
return await ReadResponse<T>(response);
|
return await ReadResponse<T>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T?> PostMultipart<T>(string path, (string endpointName, ulong major) ratelimitParams, object? payload, MultipartFile[]? files)
|
public async Task<T?> PostMultipart<T>(string path, (string endpointName, ulong major) ratelimitParams,
|
||||||
|
object? payload, MultipartFile[]? files)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
using var response = await Send(() =>
|
using var response = await Send(() =>
|
||||||
@ -151,13 +148,11 @@ namespace Myriad.Rest
|
|||||||
mfd.Add(new ByteArrayContent(bodyJson), "payload_json");
|
mfd.Add(new ByteArrayContent(bodyJson), "payload_json");
|
||||||
|
|
||||||
if (files != null)
|
if (files != null)
|
||||||
{
|
|
||||||
for (var i = 0; i < files.Length; i++)
|
for (var i = 0; i < files.Length; i++)
|
||||||
{
|
{
|
||||||
var (filename, stream, _) = files[i];
|
var (filename, stream, _) = files[i];
|
||||||
mfd.Add(new StreamContent(stream), $"files[{i}]", filename);
|
mfd.Add(new StreamContent(stream), $"files[{i}]", filename);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
request.Content = mfd;
|
request.Content = mfd;
|
||||||
}
|
}
|
||||||
@ -195,7 +190,8 @@ namespace Myriad.Rest
|
|||||||
}
|
}
|
||||||
catch (Exception exc)
|
catch (Exception exc)
|
||||||
{
|
{
|
||||||
_logger.Error(exc, "HTTP error: {RequestMethod} {RequestUrl}", request.Method, request.RequestUri);
|
_logger.Error(exc, "HTTP error: {RequestMethod} {RequestUrl}", request.Method,
|
||||||
|
request.RequestUri);
|
||||||
|
|
||||||
// kill the running thread
|
// kill the running thread
|
||||||
// in PluralKit.Bot, this error is ignored in "IsOurProblem" (PluralKit.Bot/Utils/MiscUtils.cs)
|
// in PluralKit.Bot, this error is ignored in "IsOurProblem" (PluralKit.Bot/Utils/MiscUtils.cs)
|
||||||
@ -235,12 +231,14 @@ namespace Myriad.Rest
|
|||||||
var body = await response.Content.ReadAsStringAsync();
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
var apiError = TryParseApiError(body);
|
var apiError = TryParseApiError(body);
|
||||||
if (apiError != null)
|
if (apiError != null)
|
||||||
_logger.Warning("Discord API error: {DiscordErrorCode} {DiscordErrorMessage}", apiError.Code, apiError.Message);
|
_logger.Warning("Discord API error: {DiscordErrorCode} {DiscordErrorMessage}", apiError.Code,
|
||||||
|
apiError.Message);
|
||||||
|
|
||||||
throw CreateDiscordException(response, body, apiError);
|
throw CreateDiscordException(response, body, apiError);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DiscordRequestException CreateDiscordException(HttpResponseMessage response, string body, DiscordApiError? apiError)
|
private DiscordRequestException CreateDiscordException(HttpResponseMessage response, string body,
|
||||||
|
DiscordApiError? apiError)
|
||||||
{
|
{
|
||||||
return response.StatusCode switch
|
return response.StatusCode switch
|
||||||
{
|
{
|
||||||
@ -320,4 +318,3 @@ namespace Myriad.Rest
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,6 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Myriad.Rest.Types;
|
using Myriad.Rest.Types;
|
||||||
using Myriad.Rest.Types.Requests;
|
using Myriad.Rest.Types.Requests;
|
||||||
@ -8,22 +6,22 @@ using Myriad.Types;
|
|||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Rest
|
namespace Myriad.Rest;
|
||||||
{
|
|
||||||
public class DiscordApiClient
|
public class DiscordApiClient
|
||||||
{
|
{
|
||||||
public const string UserAgent = "DiscordBot (https://github.com/xSke/PluralKit/tree/main/Myriad/, v1)";
|
public const string UserAgent = "DiscordBot (https://github.com/xSke/PluralKit/tree/main/Myriad/, v1)";
|
||||||
private const string DefaultApiBaseUrl = "https://discord.com/api/v9";
|
private const string DefaultApiBaseUrl = "https://discord.com/api/v9";
|
||||||
private readonly BaseRestClient _client;
|
private readonly BaseRestClient _client;
|
||||||
|
|
||||||
|
public EventHandler<(string, int, long)> OnResponseEvent;
|
||||||
|
|
||||||
public DiscordApiClient(string token, ILogger logger, string? baseUrl = null)
|
public DiscordApiClient(string token, ILogger logger, string? baseUrl = null)
|
||||||
{
|
{
|
||||||
_client = new BaseRestClient(UserAgent, token, logger, baseUrl ?? DefaultApiBaseUrl);
|
_client = new BaseRestClient(UserAgent, token, logger, baseUrl ?? DefaultApiBaseUrl);
|
||||||
_client.OnResponseEvent += (_, ev) => OnResponseEvent?.Invoke(null, ev);
|
_client.OnResponseEvent += (_, ev) => OnResponseEvent?.Invoke(null, ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventHandler<(string, int, long)> OnResponseEvent;
|
|
||||||
|
|
||||||
public Task<GatewayInfo> GetGateway() =>
|
public Task<GatewayInfo> GetGateway() =>
|
||||||
_client.Get<GatewayInfo>("/gateway", ("GetGateway", default))!;
|
_client.Get<GatewayInfo>("/gateway", ("GetGateway", default))!;
|
||||||
|
|
||||||
@ -50,15 +48,19 @@ namespace Myriad.Rest
|
|||||||
("GetGuildMember", guildId));
|
("GetGuildMember", guildId));
|
||||||
|
|
||||||
public Task<Message> CreateMessage(ulong channelId, MessageRequest request, MultipartFile[]? files = null) =>
|
public Task<Message> CreateMessage(ulong channelId, MessageRequest request, MultipartFile[]? files = null) =>
|
||||||
_client.PostMultipart<Message>($"/channels/{channelId}/messages", ("CreateMessage", channelId), request, files)!;
|
_client.PostMultipart<Message>($"/channels/{channelId}/messages", ("CreateMessage", channelId), request,
|
||||||
|
files)!;
|
||||||
|
|
||||||
public Task<Message> EditMessage(ulong channelId, ulong messageId, MessageEditRequest request) =>
|
public Task<Message> EditMessage(ulong channelId, ulong messageId, MessageEditRequest request) =>
|
||||||
_client.Patch<Message>($"/channels/{channelId}/messages/{messageId}", ("EditMessage", channelId), request)!;
|
_client.Patch<Message>($"/channels/{channelId}/messages/{messageId}", ("EditMessage", channelId), request)!;
|
||||||
|
|
||||||
public Task DeleteMessage(ulong channelId, ulong messageId) =>
|
public Task DeleteMessage(ulong channelId, ulong messageId) =>
|
||||||
_client.Delete($"/channels/{channelId}/messages/{messageId}", ("DeleteMessage", channelId));
|
_client.Delete($"/channels/{channelId}/messages/{messageId}", ("DeleteMessage", channelId));
|
||||||
|
|
||||||
public Task DeleteMessage(Message message) =>
|
public Task DeleteMessage(Message message) =>
|
||||||
_client.Delete($"/channels/{message.ChannelId}/messages/{message.Id}", ("DeleteMessage", message.ChannelId));
|
_client.Delete($"/channels/{message.ChannelId}/messages/{message.Id}",
|
||||||
|
("DeleteMessage", message.ChannelId));
|
||||||
|
|
||||||
public Task CreateReaction(ulong channelId, ulong messageId, Emoji emoji) =>
|
public Task CreateReaction(ulong channelId, ulong messageId, Emoji emoji) =>
|
||||||
_client.Put<object>($"/channels/{channelId}/messages/{messageId}/reactions/{EncodeEmoji(emoji)}/@me",
|
_client.Put<object>($"/channels/{channelId}/messages/{messageId}/reactions/{EncodeEmoji(emoji)}/@me",
|
||||||
("CreateReaction", channelId), null);
|
("CreateReaction", channelId), null);
|
||||||
@ -141,10 +143,9 @@ namespace Myriad.Rest
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task<Channel> CreateDm(ulong recipientId) =>
|
public Task<Channel> CreateDm(ulong recipientId) =>
|
||||||
_client.Post<Channel>($"/users/@me/channels", ("CreateDM", default), new CreateDmRequest(recipientId))!;
|
_client.Post<Channel>("/users/@me/channels", ("CreateDM", default), new CreateDmRequest(recipientId))!;
|
||||||
|
|
||||||
private static string EncodeEmoji(Emoji emoji) =>
|
private static string EncodeEmoji(Emoji emoji) =>
|
||||||
WebUtility.UrlEncode(emoji.Id != null ? $"{emoji.Name}:{emoji.Id}" : emoji.Name) ??
|
WebUtility.UrlEncode(emoji.Id != null ? $"{emoji.Name}:{emoji.Id}" : emoji.Name) ??
|
||||||
throw new ArgumentException("Could not encode emoji");
|
throw new ArgumentException("Could not encode emoji");
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Myriad.Rest
|
namespace Myriad.Rest;
|
||||||
{
|
|
||||||
public record DiscordApiError(string Message, int Code)
|
public record DiscordApiError(string Message, int Code)
|
||||||
{
|
{
|
||||||
public JsonElement? Errors { get; init; }
|
public JsonElement? Errors { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,7 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace Myriad.Rest.Exceptions
|
namespace Myriad.Rest.Exceptions;
|
||||||
{
|
|
||||||
public class DiscordRequestException: Exception
|
public class DiscordRequestException: Exception
|
||||||
{
|
{
|
||||||
public DiscordRequestException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError)
|
public DiscordRequestException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError)
|
||||||
@ -36,7 +34,8 @@ namespace Myriad.Rest.Exceptions
|
|||||||
|
|
||||||
public class UnauthorizedException: DiscordRequestException
|
public class UnauthorizedException: DiscordRequestException
|
||||||
{
|
{
|
||||||
public UnauthorizedException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError) : base(
|
public UnauthorizedException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError) :
|
||||||
|
base(
|
||||||
response, responseBody, apiError)
|
response, responseBody, apiError)
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
@ -74,4 +73,3 @@ namespace Myriad.Rest.Exceptions
|
|||||||
public UnknownDiscordRequestException(HttpResponseMessage response, string responseBody,
|
public UnknownDiscordRequestException(HttpResponseMessage response, string responseBody,
|
||||||
DiscordApiError? apiError) : base(response, responseBody, apiError) { }
|
DiscordApiError? apiError) : base(response, responseBody, apiError) { }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,7 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
using Myriad.Rest.Ratelimit;
|
using Myriad.Rest.Ratelimit;
|
||||||
|
|
||||||
namespace Myriad.Rest.Exceptions
|
namespace Myriad.Rest.Exceptions;
|
||||||
{
|
|
||||||
public class RatelimitException: Exception
|
public class RatelimitException: Exception
|
||||||
{
|
{
|
||||||
public RatelimitException(string? message) : base(message) { }
|
public RatelimitException(string? message) : base(message) { }
|
||||||
@ -26,4 +24,3 @@ namespace Myriad.Rest.Exceptions
|
|||||||
{
|
{
|
||||||
public GloballyRatelimitedException() : base("Global rate limit hit") { }
|
public GloballyRatelimitedException() : base("Global rate limit hit") { }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,10 +1,7 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Rest.Ratelimit
|
namespace Myriad.Rest.Ratelimit;
|
||||||
{
|
|
||||||
public class Bucket
|
public class Bucket
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan Epsilon = TimeSpan.FromMilliseconds(10);
|
private static readonly TimeSpan Epsilon = TimeSpan.FromMilliseconds(10);
|
||||||
@ -14,10 +11,10 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
||||||
|
private bool _hasReceivedHeaders;
|
||||||
|
|
||||||
private DateTimeOffset? _nextReset;
|
private DateTimeOffset? _nextReset;
|
||||||
private bool _resetTimeValid;
|
private bool _resetTimeValid;
|
||||||
private bool _hasReceivedHeaders;
|
|
||||||
|
|
||||||
public Bucket(ILogger logger, string key, ulong major, int limit)
|
public Bucket(ILogger logger, string key, ulong major, int limit)
|
||||||
{
|
{
|
||||||
@ -82,8 +79,10 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
var headerNextReset = DateTimeOffset.UtcNow + headers.ResetAfter.Value; // todo: server time
|
var headerNextReset = DateTimeOffset.UtcNow + headers.ResetAfter.Value; // todo: server time
|
||||||
if (_nextReset == null || headerNextReset > _nextReset)
|
if (_nextReset == null || headerNextReset > _nextReset)
|
||||||
{
|
{
|
||||||
_logger.Verbose("{BucketKey}/{BucketMajor}: Received reset time {NextReset} from server (after: {NextResetAfter}, remaining: {Remaining}, local remaining: {LocalRemaining})",
|
_logger.Verbose(
|
||||||
Key, Major, headerNextReset, headers.ResetAfter.Value, headers.Remaining, Remaining);
|
"{BucketKey}/{BucketMajor}: Received reset time {NextReset} from server (after: {NextResetAfter}, remaining: {Remaining}, local remaining: {LocalRemaining})",
|
||||||
|
Key, Major, headerNextReset, headers.ResetAfter.Value, headers.Remaining, Remaining
|
||||||
|
);
|
||||||
|
|
||||||
_nextReset = headerNextReset;
|
_nextReset = headerNextReset;
|
||||||
_resetTimeValid = true;
|
_resetTimeValid = true;
|
||||||
@ -98,7 +97,8 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
var oldRemaining = Remaining;
|
var oldRemaining = Remaining;
|
||||||
Remaining = Math.Min(headers.Remaining.Value, Remaining);
|
Remaining = Math.Min(headers.Remaining.Value, Remaining);
|
||||||
|
|
||||||
_logger.Debug("{BucketKey}/{BucketMajor}: Received first remaining of {HeaderRemaining}, previous local remaining is {LocalRemaining}, new local remaining is {Remaining}",
|
_logger.Debug(
|
||||||
|
"{BucketKey}/{BucketMajor}: Received first remaining of {HeaderRemaining}, previous local remaining is {LocalRemaining}, new local remaining is {Remaining}",
|
||||||
Key, Major, headers.Remaining.Value, oldRemaining, Remaining);
|
Key, Major, headers.Remaining.Value, oldRemaining, Remaining);
|
||||||
_hasReceivedHeaders = true;
|
_hasReceivedHeaders = true;
|
||||||
}
|
}
|
||||||
@ -170,4 +170,3 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
return delay;
|
return delay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,12 +1,9 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Rest.Ratelimit
|
namespace Myriad.Rest.Ratelimit;
|
||||||
{
|
|
||||||
public class BucketManager: IDisposable
|
public class BucketManager: IDisposable
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan StaleBucketTimeout = TimeSpan.FromMinutes(5);
|
private static readonly TimeSpan StaleBucketTimeout = TimeSpan.FromMinutes(5);
|
||||||
@ -44,7 +41,8 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
if (!_knownKeyLimits.TryGetValue(key, out var knownLimit))
|
if (!_knownKeyLimits.TryGetValue(key, out var knownLimit))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
_logger.Debug("Creating new bucket {BucketKey}/{BucketMajor} with limit {KnownLimit}", key, major, knownLimit);
|
_logger.Debug("Creating new bucket {BucketKey}/{BucketMajor} with limit {KnownLimit}", key, major,
|
||||||
|
knownLimit);
|
||||||
return _buckets.GetOrAdd((key, major),
|
return _buckets.GetOrAdd((key, major),
|
||||||
k => new Bucket(_logger, k.Item1, k.Item2, knownLimit));
|
k => new Bucket(_logger, k.Item1, k.Item2, knownLimit));
|
||||||
}
|
}
|
||||||
@ -79,4 +77,3 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,12 +1,7 @@
|
|||||||
using System;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Polly;
|
using Polly;
|
||||||
|
|
||||||
namespace Myriad.Rest.Ratelimit
|
namespace Myriad.Rest.Ratelimit;
|
||||||
{
|
|
||||||
public class DiscordRateLimitPolicy: AsyncPolicy<HttpResponseMessage>
|
public class DiscordRateLimitPolicy: AsyncPolicy<HttpResponseMessage>
|
||||||
{
|
{
|
||||||
public const string EndpointContextKey = "Endpoint";
|
public const string EndpointContextKey = "Endpoint";
|
||||||
@ -43,4 +38,3 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,10 +1,7 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace Myriad.Rest.Ratelimit
|
namespace Myriad.Rest.Ratelimit;
|
||||||
{
|
|
||||||
public record RatelimitHeaders
|
public record RatelimitHeaders
|
||||||
{
|
{
|
||||||
private const string LimitHeader = "X-RateLimit-Limit";
|
private const string LimitHeader = "X-RateLimit-Limit";
|
||||||
@ -26,8 +23,6 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
public bool HasRatelimitInfo =>
|
public bool HasRatelimitInfo =>
|
||||||
Limit != null && Remaining != null && Reset != null && ResetAfter != null && Bucket != null;
|
Limit != null && Remaining != null && Reset != null && ResetAfter != null && Bucket != null;
|
||||||
|
|
||||||
public RatelimitHeaders() { }
|
|
||||||
|
|
||||||
public static RatelimitHeaders Parse(HttpResponseMessage response)
|
public static RatelimitHeaders Parse(HttpResponseMessage response)
|
||||||
{
|
{
|
||||||
var headers = new RatelimitHeaders
|
var headers = new RatelimitHeaders
|
||||||
@ -82,4 +77,3 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,11 +1,9 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
using Myriad.Rest.Exceptions;
|
using Myriad.Rest.Exceptions;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Myriad.Rest.Ratelimit
|
namespace Myriad.Rest.Ratelimit;
|
||||||
{
|
|
||||||
public class Ratelimiter: IDisposable
|
public class Ratelimiter: IDisposable
|
||||||
{
|
{
|
||||||
private readonly BucketManager _buckets;
|
private readonly BucketManager _buckets;
|
||||||
@ -83,4 +81,3 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
private bool IsGloballyRateLimited(DateTimeOffset now) =>
|
private bool IsGloballyRateLimited(DateTimeOffset now) =>
|
||||||
_globalRateLimitExpiry > now;
|
_globalRateLimitExpiry > now;
|
||||||
}
|
}
|
||||||
}
|
|
@ -2,8 +2,8 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
using Myriad.Serialization;
|
using Myriad.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types
|
namespace Myriad.Rest.Types;
|
||||||
{
|
|
||||||
public record AllowedMentions
|
public record AllowedMentions
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(JsonSnakeCaseStringEnumConverter))]
|
[JsonConverter(typeof(JsonSnakeCaseStringEnumConverter))]
|
||||||
@ -19,4 +19,3 @@ namespace Myriad.Rest.Types
|
|||||||
public ulong[]? Roles { get; set; }
|
public ulong[]? Roles { get; set; }
|
||||||
public bool RepliedUser { get; set; }
|
public bool RepliedUser { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,6 +1,3 @@
|
|||||||
using System.IO;
|
namespace Myriad.Rest.Types;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types
|
|
||||||
{
|
|
||||||
public record MultipartFile(string Filename, Stream Data, string? Description);
|
public record MultipartFile(string Filename, Stream Data, string? Description);
|
||||||
}
|
|
@ -1,13 +1,10 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types
|
namespace Myriad.Rest.Types;
|
||||||
{
|
|
||||||
public record ApplicationCommandRequest
|
public record ApplicationCommandRequest
|
||||||
{
|
{
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
public string Description { get; init; }
|
public string Description { get; init; }
|
||||||
public List<ApplicationCommandOption>? Options { get; init; }
|
public List<ApplicationCommandOption>? Options { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests;
|
||||||
{
|
|
||||||
public record CreateDmRequest(ulong RecipientId);
|
public record CreateDmRequest(ulong RecipientId);
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests;
|
||||||
{
|
|
||||||
public record CreateWebhookRequest(string Name);
|
public record CreateWebhookRequest(string Name);
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests;
|
||||||
{
|
|
||||||
public record ExecuteWebhookRequest
|
public record ExecuteWebhookRequest
|
||||||
{
|
{
|
||||||
public string? Content { get; init; }
|
public string? Content { get; init; }
|
||||||
@ -11,4 +11,3 @@ namespace Myriad.Rest.Types.Requests
|
|||||||
public Message.Attachment[] Attachments { get; set; }
|
public Message.Attachment[] Attachments { get; set; }
|
||||||
public AllowedMentions? AllowedMentions { get; init; }
|
public AllowedMentions? AllowedMentions { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -3,8 +3,8 @@ using System.Text.Json.Serialization;
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests;
|
||||||
{
|
|
||||||
public record MessageEditRequest
|
public record MessageEditRequest
|
||||||
{
|
{
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
@ -22,4 +22,3 @@ namespace Myriad.Rest.Types.Requests
|
|||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<MessageComponent[]?> Components { get; init; }
|
public Optional<MessageComponent[]?> Components { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests;
|
||||||
{
|
|
||||||
public record MessageRequest
|
public record MessageRequest
|
||||||
{
|
{
|
||||||
public string? Content { get; set; }
|
public string? Content { get; set; }
|
||||||
@ -11,4 +11,3 @@ namespace Myriad.Rest.Types.Requests
|
|||||||
public Embed? Embed { get; set; }
|
public Embed? Embed { get; set; }
|
||||||
public MessageComponent[]? Components { get; set; }
|
public MessageComponent[]? Components { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
namespace Myriad.Rest.Types
|
namespace Myriad.Rest.Types;
|
||||||
{
|
|
||||||
public record ModifyGuildMemberRequest
|
public record ModifyGuildMemberRequest
|
||||||
{
|
{
|
||||||
public string? Nick { get; init; }
|
public string? Nick { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -2,8 +2,8 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests;
|
||||||
{
|
|
||||||
public record WebhookMessageEditRequest
|
public record WebhookMessageEditRequest
|
||||||
{
|
{
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
@ -12,4 +12,3 @@ namespace Myriad.Rest.Types.Requests
|
|||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<AllowedMentions> AllowedMentions { get; init; }
|
public Optional<AllowedMentions> AllowedMentions { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Serialization
|
namespace Myriad.Serialization;
|
||||||
{
|
|
||||||
public static class JsonSerializerOptionsExtensions
|
public static class JsonSerializerOptionsExtensions
|
||||||
{
|
{
|
||||||
public static JsonSerializerOptions ConfigureForMyriad(this JsonSerializerOptions opts)
|
public static JsonSerializerOptions ConfigureForMyriad(this JsonSerializerOptions opts)
|
||||||
@ -18,4 +18,3 @@ namespace Myriad.Serialization
|
|||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
using System;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Myriad.Serialization
|
namespace Myriad.Serialization;
|
||||||
{
|
|
||||||
// From https://github.com/J0rgeSerran0/JsonNamingPolicy/blob/master/JsonSnakeCaseNamingPolicy.cs, no NuGet :/
|
// From https://github.com/J0rgeSerran0/JsonNamingPolicy/blob/master/JsonSnakeCaseNamingPolicy.cs, no NuGet :/
|
||||||
public class JsonSnakeCaseNamingPolicy: JsonNamingPolicy
|
public class JsonSnakeCaseNamingPolicy: JsonNamingPolicy
|
||||||
{
|
{
|
||||||
@ -85,4 +84,3 @@ namespace Myriad.Serialization
|
|||||||
return stringBuilder.ToString().ToLower();
|
return stringBuilder.ToString().ToLower();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
using System;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Serialization
|
namespace Myriad.Serialization;
|
||||||
{
|
|
||||||
public class JsonSnakeCaseStringEnumConverter: JsonConverterFactory
|
public class JsonSnakeCaseStringEnumConverter: JsonConverterFactory
|
||||||
{
|
{
|
||||||
private readonly JsonStringEnumConverter _inner = new(new JsonSnakeCaseNamingPolicy());
|
private readonly JsonStringEnumConverter _inner = new(new JsonSnakeCaseNamingPolicy());
|
||||||
@ -14,4 +13,3 @@ namespace Myriad.Serialization
|
|||||||
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
|
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
|
||||||
_inner.CreateConverter(typeToConvert, options);
|
_inner.CreateConverter(typeToConvert, options);
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
using System;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Serialization
|
namespace Myriad.Serialization;
|
||||||
{
|
|
||||||
public class JsonStringConverter: JsonConverter<object>
|
public class JsonStringConverter: JsonConverter<object>
|
||||||
{
|
{
|
||||||
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
@ -19,4 +18,3 @@ namespace Myriad.Serialization
|
|||||||
writer.WriteStringValue(inner);
|
writer.WriteStringValue(inner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,28 +1,13 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
|
|
||||||
namespace Myriad.Serialization
|
namespace Myriad.Serialization;
|
||||||
{
|
|
||||||
public class OptionalConverterFactory: JsonConverterFactory
|
public class OptionalConverterFactory: JsonConverterFactory
|
||||||
{
|
{
|
||||||
public class Inner<T>: JsonConverter<Optional<T>>
|
|
||||||
{
|
|
||||||
public override Optional<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
var inner = JsonSerializer.Deserialize<T>(ref reader, options);
|
|
||||||
return new(inner!);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
JsonSerializer.Serialize(writer, value.HasValue ? value.GetValue() : default, typeof(T), options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
var innerType = typeToConvert.GetGenericArguments()[0];
|
var innerType = typeToConvert.GetGenericArguments()[0];
|
||||||
@ -44,5 +29,19 @@ namespace Myriad.Serialization
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Inner<T>: JsonConverter<Optional<T>>
|
||||||
|
{
|
||||||
|
public override Optional<T> Read(ref Utf8JsonReader reader, Type typeToConvert,
|
||||||
|
JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var inner = JsonSerializer.Deserialize<T>(ref reader, options);
|
||||||
|
return new Optional<T>(inner!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
JsonSerializer.Serialize(writer, value.HasValue ? value.GetValue() : default, typeof(T), options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,10 @@
|
|||||||
using System;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Serialization
|
namespace Myriad.Serialization;
|
||||||
{
|
|
||||||
public class PermissionSetJsonConverter: JsonConverter<PermissionSet>
|
public class PermissionSetJsonConverter: JsonConverter<PermissionSet>
|
||||||
{
|
{
|
||||||
public override PermissionSet Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override PermissionSet Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
@ -21,4 +20,3 @@ namespace Myriad.Serialization
|
|||||||
writer.WriteStringValue(((ulong)value).ToString());
|
writer.WriteStringValue(((ulong)value).ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,11 +1,10 @@
|
|||||||
using System;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Gateway;
|
using Myriad.Gateway;
|
||||||
|
|
||||||
namespace Myriad.Serialization
|
namespace Myriad.Serialization;
|
||||||
{
|
|
||||||
public class ShardInfoJsonConverter: JsonConverter<ShardInfo>
|
public class ShardInfoJsonConverter: JsonConverter<ShardInfo>
|
||||||
{
|
{
|
||||||
public override ShardInfo? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override ShardInfo? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
@ -25,4 +24,3 @@ namespace Myriad.Serialization
|
|||||||
writer.WriteEndArray();
|
writer.WriteEndArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record Application: ApplicationPartial
|
public record Application: ApplicationPartial
|
||||||
{
|
{
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
@ -22,4 +22,3 @@ namespace Myriad.Types
|
|||||||
public ulong Id { get; init; }
|
public ulong Id { get; init; }
|
||||||
public int Flags { get; init; }
|
public int Flags { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record ApplicationCommand
|
public record ApplicationCommand
|
||||||
{
|
{
|
||||||
public ulong Id { get; init; }
|
public ulong Id { get; init; }
|
||||||
@ -8,4 +8,3 @@ namespace Myriad.Types
|
|||||||
public string Description { get; init; }
|
public string Description { get; init; }
|
||||||
public ApplicationCommandOption[]? Options { get; init; }
|
public ApplicationCommandOption[]? Options { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record ApplicationCommandInteractionData
|
public record ApplicationCommandInteractionData
|
||||||
{
|
{
|
||||||
public ulong? Id { get; init; }
|
public ulong? Id { get; init; }
|
||||||
@ -8,4 +8,3 @@ namespace Myriad.Types
|
|||||||
public string? CustomId { get; init; }
|
public string? CustomId { get; init; }
|
||||||
public ComponentType? ComponentType { get; init; }
|
public ComponentType? ComponentType { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record ApplicationCommandInteractionDataOption
|
public record ApplicationCommandInteractionDataOption
|
||||||
{
|
{
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
public object? Value { get; init; }
|
public object? Value { get; init; }
|
||||||
public ApplicationCommandInteractionDataOption[]? Options { get; init; }
|
public ApplicationCommandInteractionDataOption[]? Options { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record ApplicationCommandOption(ApplicationCommandOption.OptionType Type, string Name, string Description)
|
public record ApplicationCommandOption(ApplicationCommandOption.OptionType Type, string Name, string Description)
|
||||||
{
|
{
|
||||||
public enum OptionType
|
public enum OptionType
|
||||||
@ -21,4 +21,3 @@ namespace Myriad.Types
|
|||||||
|
|
||||||
public record Choice(string Name, object Value);
|
public record Choice(string Name, object Value);
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record Interaction
|
public record Interaction
|
||||||
{
|
{
|
||||||
public enum InteractionType
|
public enum InteractionType
|
||||||
@ -19,4 +19,3 @@ namespace Myriad.Types
|
|||||||
public string Token { get; init; }
|
public string Token { get; init; }
|
||||||
public Message? Message { get; init; }
|
public Message? Message { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -3,8 +3,8 @@ using System.Text.Json.Serialization;
|
|||||||
using Myriad.Rest.Types;
|
using Myriad.Rest.Types;
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
|
|
||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record InteractionApplicationCommandCallbackData
|
public record InteractionApplicationCommandCallbackData
|
||||||
{
|
{
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
@ -25,4 +25,3 @@ namespace Myriad.Types
|
|||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<MessageComponent[]?> Components { get; init; }
|
public Optional<MessageComponent[]?> Components { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record InteractionResponse
|
public record InteractionResponse
|
||||||
{
|
{
|
||||||
public enum ResponseType
|
public enum ResponseType
|
||||||
@ -14,4 +14,3 @@ namespace Myriad.Types
|
|||||||
public ResponseType Type { get; init; }
|
public ResponseType Type { get; init; }
|
||||||
public InteractionApplicationCommandCallbackData? Data { get; init; }
|
public InteractionApplicationCommandCallbackData? Data { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record Channel
|
public record Channel
|
||||||
{
|
{
|
||||||
public enum ChannelType
|
public enum ChannelType
|
||||||
@ -41,5 +41,5 @@ namespace Myriad.Types
|
|||||||
Role = 0,
|
Role = 0,
|
||||||
Member = 1
|
Member = 1
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public enum ButtonStyle
|
public enum ButtonStyle
|
||||||
{
|
{
|
||||||
Primary = 1,
|
Primary = 1,
|
||||||
@ -8,4 +8,3 @@ namespace Myriad.Types
|
|||||||
Danger = 4,
|
Danger = 4,
|
||||||
Link = 5
|
Link = 5
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,8 +1,7 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public enum ComponentType
|
public enum ComponentType
|
||||||
{
|
{
|
||||||
ActionRow = 1,
|
ActionRow = 1,
|
||||||
Button = 2
|
Button = 2
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record MessageComponent
|
public record MessageComponent
|
||||||
{
|
{
|
||||||
public ComponentType Type { get; init; }
|
public ComponentType Type { get; init; }
|
||||||
@ -11,4 +11,3 @@ namespace Myriad.Types
|
|||||||
public bool? Disabled { get; init; }
|
public bool? Disabled { get; init; }
|
||||||
public MessageComponent[]? Components { get; init; }
|
public MessageComponent[]? Components { get; init; }
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types;
|
||||||
{
|
|
||||||
public record Embed
|
public record Embed
|
||||||
{
|
{
|
||||||
public string? Title { get; init; }
|
public string? Title { get; init; }
|
||||||
@ -59,4 +59,3 @@ namespace Myriad.Types
|
|||||||
bool Inline = false
|
bool Inline = false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user