run dotnet format
This commit is contained in:
parent
05989242f9
commit
ac2671452d
@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
@ -11,66 +11,71 @@ namespace Myriad.Builders
|
|||||||
|
|
||||||
public EmbedBuilder Title(string? title)
|
public EmbedBuilder Title(string? title)
|
||||||
{
|
{
|
||||||
_embed = _embed with {Title = title};
|
_embed = _embed with { Title = title };
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbedBuilder Description(string? description)
|
public EmbedBuilder Description(string? description)
|
||||||
{
|
{
|
||||||
_embed = _embed with { Description = description};
|
_embed = _embed with { Description = description };
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbedBuilder Url(string? url)
|
public EmbedBuilder Url(string? url)
|
||||||
{
|
{
|
||||||
_embed = _embed with {Url = url};
|
_embed = _embed with { Url = url };
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbedBuilder Color(uint? color)
|
public EmbedBuilder Color(uint? color)
|
||||||
{
|
{
|
||||||
_embed = _embed with {Color = color};
|
_embed = _embed with { Color = color };
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Myriad.Gateway;
|
using Myriad.Gateway;
|
||||||
|
|
||||||
@ -69,9 +69,9 @@ namespace Myriad.Cache
|
|||||||
private static async ValueTask SaveMessageCreate(this IDiscordCache cache, MessageCreateEvent evt)
|
private static async ValueTask SaveMessageCreate(this IDiscordCache cache, MessageCreateEvent evt)
|
||||||
{
|
{
|
||||||
await cache.TrySaveDmChannelStub(evt.GuildId, evt.ChannelId);
|
await cache.TrySaveDmChannelStub(evt.GuildId, evt.ChannelId);
|
||||||
|
|
||||||
await cache.SaveUser(evt.Author);
|
await cache.SaveUser(evt.Author);
|
||||||
foreach (var mention in evt.Mentions)
|
foreach (var mention in evt.Mentions)
|
||||||
await cache.SaveUser(mention);
|
await cache.SaveUser(mention);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ namespace Myriad.Cache
|
|||||||
|
|
||||||
private static async ValueTask SaveThreadListSync(this IDiscordCache cache, ThreadListSyncEvent evt)
|
private static async ValueTask SaveThreadListSync(this IDiscordCache cache, ThreadListSyncEvent evt)
|
||||||
{
|
{
|
||||||
foreach (var thread in evt.Threads)
|
foreach (var thread in evt.Threads)
|
||||||
await cache.SaveChannel(thread);
|
await cache.SaveChannel(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -15,7 +15,7 @@ namespace Myriad.Cache
|
|||||||
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();
|
||||||
|
|
||||||
public ValueTask SaveGuild(Guild guild)
|
public ValueTask SaveGuild(Guild guild)
|
||||||
{
|
{
|
||||||
SaveGuildRaw(guild);
|
SaveGuildRaw(guild);
|
||||||
@ -61,18 +61,20 @@ namespace Myriad.Cache
|
|||||||
var found = false;
|
var found = false;
|
||||||
for (var i = 0; i < guild.Guild.Roles.Length; i++)
|
for (var i = 0; i < guild.Guild.Roles.Length; i++)
|
||||||
{
|
{
|
||||||
if (guild.Guild.Roles[i].Id != role.Id)
|
if (guild.Guild.Roles[i].Id != role.Id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
guild.Guild.Roles[i] = role;
|
guild.Guild.Roles[i] = role;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
_guilds[guildId] = guild with {
|
_guilds[guildId] = guild with
|
||||||
Guild = guild.Guild with {
|
{
|
||||||
Roles = guild.Guild.Roles.Concat(new[] { role}).ToArray()
|
Guild = guild.Guild with
|
||||||
|
{
|
||||||
|
Roles = guild.Guild.Roles.Concat(new[] { role }).ToArray()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -87,7 +89,7 @@ namespace Myriad.Cache
|
|||||||
// 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,
|
Id = id,
|
||||||
Type = Channel.ChannelType.Dm
|
Type = Channel.ChannelType.Dm
|
||||||
});
|
});
|
||||||
return default;
|
return default;
|
||||||
@ -134,7 +136,7 @@ namespace Myriad.Cache
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetChannel(ulong channelId, out Channel channel) =>
|
public bool TryGetChannel(ulong channelId, out Channel channel) =>
|
||||||
_channels.TryGetValue(channelId, out channel!);
|
_channels.TryGetValue(channelId, out channel!);
|
||||||
|
|
||||||
public bool TryGetDmChannel(ulong userId, out Channel channel)
|
public bool TryGetDmChannel(ulong userId, out Channel channel)
|
||||||
@ -143,7 +145,7 @@ namespace Myriad.Cache
|
|||||||
if (!_dmChannels.TryGetValue(userId, out var channelId))
|
if (!_dmChannels.TryGetValue(userId, out var channelId))
|
||||||
return false;
|
return false;
|
||||||
return TryGetChannel(channelId, out channel);
|
return TryGetChannel(channelId, out channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetUser(ulong userId, out User user) =>
|
public bool TryGetUser(ulong userId, out User user) =>
|
||||||
_users.TryGetValue(userId, out user!);
|
_users.TryGetValue(userId, out user!);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Myriad.Cache;
|
using Myriad.Cache;
|
||||||
@ -15,7 +15,7 @@ namespace Myriad.Extensions
|
|||||||
throw new KeyNotFoundException($"Guild {guildId} not found in cache");
|
throw new KeyNotFoundException($"Guild {guildId} not found in cache");
|
||||||
return guild;
|
return guild;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Channel GetChannel(this IDiscordCache cache, ulong channelId)
|
public static Channel GetChannel(this IDiscordCache cache, ulong channelId)
|
||||||
{
|
{
|
||||||
if (!cache.TryGetChannel(channelId, out var channel))
|
if (!cache.TryGetChannel(channelId, out var channel))
|
||||||
@ -36,14 +36,14 @@ namespace Myriad.Extensions
|
|||||||
throw new KeyNotFoundException($"User {userId} not found in cache");
|
throw new KeyNotFoundException($"User {userId} not found in cache");
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Role GetRole(this IDiscordCache cache, ulong roleId)
|
public static Role GetRole(this IDiscordCache cache, ulong roleId)
|
||||||
{
|
{
|
||||||
if (!cache.TryGetRole(roleId, out var role))
|
if (!cache.TryGetRole(roleId, out var role))
|
||||||
throw new KeyNotFoundException($"User {roleId} not found in cache");
|
throw new KeyNotFoundException($"User {roleId} not found in cache");
|
||||||
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 (cache.TryGetUser(userId, out var cacheUser))
|
if (cache.TryGetUser(userId, out var cacheUser))
|
||||||
@ -54,7 +54,7 @@ namespace Myriad.Extensions
|
|||||||
await cache.SaveUser(restUser);
|
await cache.SaveUser(restUser);
|
||||||
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 (cache.TryGetChannel(channelId, out var cacheChannel))
|
if (cache.TryGetChannel(channelId, out var cacheChannel))
|
||||||
@ -79,9 +79,9 @@ namespace Myriad.Extensions
|
|||||||
public static Channel GetRootChannel(this IDiscordCache cache, ulong channelOrThread)
|
public static Channel GetRootChannel(this IDiscordCache cache, ulong channelOrThread)
|
||||||
{
|
{
|
||||||
var channel = cache.GetChannel(channelOrThread);
|
var channel = cache.GetChannel(channelOrThread);
|
||||||
if (!channel.IsThread())
|
if (!channel.IsThread())
|
||||||
return channel;
|
return channel;
|
||||||
|
|
||||||
var parent = cache.GetChannel(channel.ParentId!.Value);
|
var parent = cache.GetChannel(channel.ParentId!.Value);
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions
|
||||||
{
|
{
|
||||||
public static class GuildExtensions
|
public static class GuildExtensions
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Gateway;
|
using Myriad.Gateway;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions
|
||||||
@ -7,7 +7,7 @@ namespace Myriad.Extensions
|
|||||||
{
|
{
|
||||||
public static string JumpLink(this Message msg) =>
|
public static string JumpLink(this Message msg) =>
|
||||||
$"https://discord.com/channels/{msg.GuildId}/{msg.ChannelId}/{msg.Id}";
|
$"https://discord.com/channels/{msg.GuildId}/{msg.ChannelId}/{msg.Id}";
|
||||||
|
|
||||||
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,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@ -21,12 +21,12 @@ namespace Myriad.Extensions
|
|||||||
if (!cache.TryGetChannel(channelId, out var channel))
|
if (!cache.TryGetChannel(channelId, out var channel))
|
||||||
// todo: handle channel not found better
|
// todo: handle channel not found better
|
||||||
return PermissionSet.Dm;
|
return PermissionSet.Dm;
|
||||||
|
|
||||||
if (channel.GuildId == null)
|
if (channel.GuildId == null)
|
||||||
return PermissionSet.Dm;
|
return PermissionSet.Dm;
|
||||||
|
|
||||||
var rootChannel = cache.GetRootChannel(channelId);
|
var rootChannel = cache.GetRootChannel(channelId);
|
||||||
|
|
||||||
var guild = cache.GetGuild(channel.GuildId.Value);
|
var guild = cache.GetGuild(channel.GuildId.Value);
|
||||||
|
|
||||||
if (isWebhook)
|
if (isWebhook)
|
||||||
@ -34,10 +34,10 @@ namespace Myriad.Extensions
|
|||||||
|
|
||||||
return PermissionsFor(guild, rootChannel, userId, member);
|
return PermissionsFor(guild, rootChannel, userId, member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PermissionSet EveryonePermissions(this Guild guild) =>
|
public static PermissionSet EveryonePermissions(this Guild guild) =>
|
||||||
guild.Roles.FirstOrDefault(r => r.Id == guild.Id)?.Permissions ?? PermissionSet.Dm;
|
guild.Roles.FirstOrDefault(r => r.Id == guild.Id)?.Permissions ?? PermissionSet.Dm;
|
||||||
|
|
||||||
public static PermissionSet EveryonePermissions(this IDiscordCache cache, Channel channel)
|
public static PermissionSet EveryonePermissions(this IDiscordCache cache, Channel channel)
|
||||||
{
|
{
|
||||||
if (channel.Type == Channel.ChannelType.Dm)
|
if (channel.Type == Channel.ChannelType.Dm)
|
||||||
@ -62,7 +62,7 @@ namespace Myriad.Extensions
|
|||||||
{
|
{
|
||||||
if (channel.Type == Channel.ChannelType.Dm)
|
if (channel.Type == Channel.ChannelType.Dm)
|
||||||
return PermissionSet.Dm;
|
return PermissionSet.Dm;
|
||||||
|
|
||||||
if (member == null)
|
if (member == null)
|
||||||
// this happens with system (Discord platform-owned) users - they're not actually in the guild, so there is no member object.
|
// this happens with system (Discord platform-owned) users - they're not actually in the guild, so there is no member object.
|
||||||
return EveryonePermissions(guild);
|
return EveryonePermissions(guild);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ 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);
|
||||||
|
|
||||||
public static DateTimeOffset SnowflakeToTimestamp(ulong snowflake) =>
|
public static DateTimeOffset SnowflakeToTimestamp(ulong snowflake) =>
|
||||||
DiscordEpoch + TimeSpan.FromMilliseconds(snowflake >> 22);
|
DiscordEpoch + TimeSpan.FromMilliseconds(snowflake >> 22);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Extensions
|
namespace Myriad.Extensions
|
||||||
{
|
{
|
||||||
@ -6,7 +6,7 @@ namespace Myriad.Extensions
|
|||||||
{
|
{
|
||||||
public static string Mention(this User user) => $"<@{user.Id}>";
|
public static string Mention(this User user) => $"<@{user.Id}>";
|
||||||
|
|
||||||
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,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -30,7 +30,7 @@ namespace Myriad.Gateway
|
|||||||
public IReadOnlyDictionary<int, Shard> Shards => _shards;
|
public IReadOnlyDictionary<int, Shard> Shards => _shards;
|
||||||
public User? User => _shards.Values.Select(s => s.User).FirstOrDefault(s => s != null);
|
public User? User => _shards.Values.Select(s => s.User).FirstOrDefault(s => s != null);
|
||||||
public ApplicationPartial? Application => _shards.Values.Select(s => s.Application).FirstOrDefault(s => s != null);
|
public ApplicationPartial? Application => _shards.Values.Select(s => s.Application).FirstOrDefault(s => s != null);
|
||||||
|
|
||||||
public async Task Start(GatewayInfo.Bot info)
|
public async Task Start(GatewayInfo.Bot info)
|
||||||
{
|
{
|
||||||
await Start(info.Url, 0, info.Shards - 1, info.Shards, info.SessionStartLimit.MaxConcurrency);
|
await Start(info.Url, 0, info.Shards - 1, info.Shards, info.SessionStartLimit.MaxConcurrency);
|
||||||
@ -39,7 +39,7 @@ namespace Myriad.Gateway
|
|||||||
public async Task Start(string url, int shardMin, int shardMax, int shardTotal, int recommendedConcurrency)
|
public async Task Start(string url, int shardMin, int shardMax, int shardTotal, int recommendedConcurrency)
|
||||||
{
|
{
|
||||||
_ratelimiter = GetRateLimiter(recommendedConcurrency);
|
_ratelimiter = GetRateLimiter(recommendedConcurrency);
|
||||||
|
|
||||||
var shardCount = shardMax - shardMin + 1;
|
var shardCount = shardMax - shardMin + 1;
|
||||||
_logger.Information("Starting {ShardCount} of {ShardTotal} shards (#{ShardMin}-#{ShardMax}) at {Url}",
|
_logger.Information("Starting {ShardCount} of {ShardTotal} shards (#{ShardMin}-#{ShardMax}) at {Url}",
|
||||||
shardCount, shardTotal, shardMin, shardMax, url);
|
shardCount, shardTotal, shardMin, shardMax, url);
|
||||||
@ -51,7 +51,7 @@ namespace Myriad.Gateway
|
|||||||
private async Task StartShards()
|
private async Task StartShards()
|
||||||
{
|
{
|
||||||
_logger.Information("Connecting shards...");
|
_logger.Information("Connecting shards...");
|
||||||
foreach (var shard in _shards.Values)
|
foreach (var shard in _shards.Values)
|
||||||
await shard.Start();
|
await shard.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ namespace Myriad.Gateway
|
|||||||
var shard = new Shard(_gatewaySettings, shardInfo, _ratelimiter!, url, _logger);
|
var shard = new Shard(_gatewaySettings, shardInfo, _ratelimiter!, url, _logger);
|
||||||
shard.OnEventReceived += evt => OnShardEventReceived(shard, evt);
|
shard.OnEventReceived += evt => OnShardEventReceived(shard, evt);
|
||||||
_shards[shardInfo.ShardId] = shard;
|
_shards[shardInfo.ShardId] = shard;
|
||||||
|
|
||||||
ShardCreated?.Invoke(shard);
|
ShardCreated?.Invoke(shard);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +69,12 @@ namespace Myriad.Gateway
|
|||||||
if (EventReceived != null)
|
if (EventReceived != null)
|
||||||
await EventReceived(shard, evt);
|
await EventReceived(shard, evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetActualShardConcurrency(int recommendedConcurrency)
|
private int GetActualShardConcurrency(int recommendedConcurrency)
|
||||||
{
|
{
|
||||||
if (_gatewaySettings.MaxShardConcurrency == null)
|
if (_gatewaySettings.MaxShardConcurrency == null)
|
||||||
return recommendedConcurrency;
|
return recommendedConcurrency;
|
||||||
|
|
||||||
return Math.Min(_gatewaySettings.MaxShardConcurrency.Value, recommendedConcurrency);
|
return Math.Min(_gatewaySettings.MaxShardConcurrency.Value, recommendedConcurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ namespace Myriad.Gateway
|
|||||||
{
|
{
|
||||||
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,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
public record GuildDeleteEvent(ulong Id, bool Unavailable): IGatewayEvent;
|
public record GuildDeleteEvent(ulong Id, bool Unavailable): IGatewayEvent;
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
public record GuildRoleDeleteEvent(ulong GuildId, ulong RoleId): IGatewayEvent;
|
public record GuildRoleDeleteEvent(ulong GuildId, ulong RoleId): IGatewayEvent;
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
@ -7,33 +7,33 @@ namespace Myriad.Gateway
|
|||||||
{
|
{
|
||||||
public static readonly Dictionary<string, Type> EventTypes = new()
|
public static readonly Dictionary<string, Type> EventTypes = new()
|
||||||
{
|
{
|
||||||
{"READY", typeof(ReadyEvent)},
|
{ "READY", typeof(ReadyEvent) },
|
||||||
{"RESUMED", typeof(ResumedEvent)},
|
{ "RESUMED", typeof(ResumedEvent) },
|
||||||
{"GUILD_CREATE", typeof(GuildCreateEvent)},
|
{ "GUILD_CREATE", typeof(GuildCreateEvent) },
|
||||||
{"GUILD_UPDATE", typeof(GuildUpdateEvent)},
|
{ "GUILD_UPDATE", typeof(GuildUpdateEvent) },
|
||||||
{"GUILD_DELETE", typeof(GuildDeleteEvent)},
|
{ "GUILD_DELETE", typeof(GuildDeleteEvent) },
|
||||||
{"GUILD_MEMBER_ADD", typeof(GuildMemberAddEvent)},
|
{ "GUILD_MEMBER_ADD", typeof(GuildMemberAddEvent) },
|
||||||
{"GUILD_MEMBER_REMOVE", typeof(GuildMemberRemoveEvent)},
|
{ "GUILD_MEMBER_REMOVE", typeof(GuildMemberRemoveEvent) },
|
||||||
{"GUILD_MEMBER_UPDATE", typeof(GuildMemberUpdateEvent)},
|
{ "GUILD_MEMBER_UPDATE", typeof(GuildMemberUpdateEvent) },
|
||||||
{"GUILD_ROLE_CREATE", typeof(GuildRoleCreateEvent)},
|
{ "GUILD_ROLE_CREATE", typeof(GuildRoleCreateEvent) },
|
||||||
{"GUILD_ROLE_UPDATE", typeof(GuildRoleUpdateEvent)},
|
{ "GUILD_ROLE_UPDATE", typeof(GuildRoleUpdateEvent) },
|
||||||
{"GUILD_ROLE_DELETE", typeof(GuildRoleDeleteEvent)},
|
{ "GUILD_ROLE_DELETE", typeof(GuildRoleDeleteEvent) },
|
||||||
{"CHANNEL_CREATE", typeof(ChannelCreateEvent)},
|
{ "CHANNEL_CREATE", typeof(ChannelCreateEvent) },
|
||||||
{"CHANNEL_UPDATE", typeof(ChannelUpdateEvent)},
|
{ "CHANNEL_UPDATE", typeof(ChannelUpdateEvent) },
|
||||||
{"CHANNEL_DELETE", typeof(ChannelDeleteEvent)},
|
{ "CHANNEL_DELETE", typeof(ChannelDeleteEvent) },
|
||||||
{"THREAD_CREATE", typeof(ThreadCreateEvent)},
|
{ "THREAD_CREATE", typeof(ThreadCreateEvent) },
|
||||||
{"THREAD_UPDATE", typeof(ThreadUpdateEvent)},
|
{ "THREAD_UPDATE", typeof(ThreadUpdateEvent) },
|
||||||
{"THREAD_DELETE", typeof(ThreadDeleteEvent)},
|
{ "THREAD_DELETE", typeof(ThreadDeleteEvent) },
|
||||||
{"THREAD_LIST_SYNC", typeof(ThreadListSyncEvent)},
|
{ "THREAD_LIST_SYNC", typeof(ThreadListSyncEvent) },
|
||||||
{"MESSAGE_CREATE", typeof(MessageCreateEvent)},
|
{ "MESSAGE_CREATE", typeof(MessageCreateEvent) },
|
||||||
{"MESSAGE_UPDATE", typeof(MessageUpdateEvent)},
|
{ "MESSAGE_UPDATE", typeof(MessageUpdateEvent) },
|
||||||
{"MESSAGE_DELETE", typeof(MessageDeleteEvent)},
|
{ "MESSAGE_DELETE", typeof(MessageDeleteEvent) },
|
||||||
{"MESSAGE_DELETE_BULK", typeof(MessageDeleteBulkEvent)},
|
{ "MESSAGE_DELETE_BULK", typeof(MessageDeleteBulkEvent) },
|
||||||
{"MESSAGE_REACTION_ADD", typeof(MessageReactionAddEvent)},
|
{ "MESSAGE_REACTION_ADD", typeof(MessageReactionAddEvent) },
|
||||||
{"MESSAGE_REACTION_REMOVE", typeof(MessageReactionRemoveEvent)},
|
{ "MESSAGE_REACTION_REMOVE", typeof(MessageReactionRemoveEvent) },
|
||||||
{"MESSAGE_REACTION_REMOVE_ALL", typeof(MessageReactionRemoveAllEvent)},
|
{ "MESSAGE_REACTION_REMOVE_ALL", typeof(MessageReactionRemoveAllEvent) },
|
||||||
{"MESSAGE_REACTION_REMOVE_EMOJI", typeof(MessageReactionRemoveEmojiEvent)},
|
{ "MESSAGE_REACTION_REMOVE_EMOJI", typeof(MessageReactionRemoveEmojiEvent) },
|
||||||
{"INTERACTION_CREATE", typeof(InteractionCreateEvent)}
|
{ "INTERACTION_CREATE", typeof(InteractionCreateEvent) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
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,4 @@
|
|||||||
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,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
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,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
public record ResumedEvent: IGatewayEvent;
|
public record ResumedEvent: IGatewayEvent;
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
// TODO: unused?
|
// TODO: unused?
|
||||||
public class GatewayCloseException: Exception
|
public class GatewayCloseException: Exception
|
||||||
{
|
{
|
||||||
public GatewayCloseException(int closeCode, string closeReason): base($"{closeCode}: {closeReason}")
|
public GatewayCloseException(int closeCode, string closeReason) : base($"{closeCode}: {closeReason}")
|
||||||
{
|
{
|
||||||
CloseCode = closeCode;
|
CloseCode = closeCode;
|
||||||
CloseReason = closeReason;
|
CloseReason = closeReason;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
@ -7,10 +7,12 @@ namespace Myriad.Gateway
|
|||||||
[JsonPropertyName("op")] public GatewayOpcode Opcode { get; init; }
|
[JsonPropertyName("op")] public GatewayOpcode Opcode { get; init; }
|
||||||
[JsonPropertyName("d")] public object? Payload { get; init; }
|
[JsonPropertyName("d")] public object? Payload { get; init; }
|
||||||
|
|
||||||
[JsonPropertyName("s")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
[JsonPropertyName("s")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public int? Sequence { get; init; }
|
public int? Sequence { get; init; }
|
||||||
|
|
||||||
[JsonPropertyName("t")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
[JsonPropertyName("t")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public string? EventType { get; init; }
|
public string? EventType { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
public record GatewaySettings
|
public record GatewaySettings
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Myriad.Gateway.Limit
|
namespace Myriad.Gateway.Limit
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ namespace Myriad.Gateway.Limit
|
|||||||
{
|
{
|
||||||
Timeout = TimeSpan.FromSeconds(60)
|
Timeout = TimeSpan.FromSeconds(60)
|
||||||
};
|
};
|
||||||
|
|
||||||
public TwilightGatewayRatelimiter(ILogger logger, string url)
|
public TwilightGatewayRatelimiter(ILogger logger, string url)
|
||||||
{
|
{
|
||||||
_url = url;
|
_url = url;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
public record GatewayHello(int HeartbeatInterval);
|
public record GatewayHello(int HeartbeatInterval);
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
public record GatewayResume(string Token, string SessionId, int Seq);
|
public record GatewayResume(string Token, string SessionId, int Seq);
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Serialization;
|
using Myriad.Serialization;
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -68,18 +68,18 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
_conn = new ShardConnection(_jsonSerializerOptions, _logger);
|
_conn = new ShardConnection(_jsonSerializerOptions, _logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ShardLoop()
|
private async Task ShardLoop()
|
||||||
{
|
{
|
||||||
// may be superfluous but this adds shard id to ambient context which is nice
|
// may be superfluous but this adds shard id to ambient context which is nice
|
||||||
using var _ = LogContext.PushProperty("ShardId", _info.ShardId);
|
using var _ = LogContext.PushProperty("ShardId", _info.ShardId);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ConnectInner();
|
await ConnectInner();
|
||||||
|
|
||||||
await HandleConnectionOpened();
|
await HandleConnectionOpened();
|
||||||
|
|
||||||
while (_conn.State == WebSocketState.Open)
|
while (_conn.State == WebSocketState.Open)
|
||||||
@ -90,7 +90,7 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
await _stateManager.HandlePacketReceived(packet);
|
await _stateManager.HandlePacketReceived(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
await HandleConnectionClosed(_conn.CloseStatus, _conn.CloseStatusDescription);
|
await HandleConnectionClosed(_conn.CloseStatus, _conn.CloseStatusDescription);
|
||||||
|
|
||||||
_logger.Information("Shard {ShardId}: Reconnecting after delay {ReconnectDelay}",
|
_logger.Information("Shard {ShardId}: Reconnecting after delay {ReconnectDelay}",
|
||||||
@ -103,20 +103,20 @@ namespace Myriad.Gateway
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.Error(e, "Shard {ShardId}: Error in main shard loop, reconnecting in 5 seconds...", _info.ShardId);
|
_logger.Error(e, "Shard {ShardId}: Error in main shard loop, reconnecting in 5 seconds...", _info.ShardId);
|
||||||
|
|
||||||
// todo: exponential backoff here? this should never happen, ideally...
|
// todo: exponential backoff here? this should never happen, ideally...
|
||||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Start()
|
public async Task Start()
|
||||||
{
|
{
|
||||||
if (_worker == null)
|
if (_worker == null)
|
||||||
_worker = ShardLoop();
|
_worker = ShardLoop();
|
||||||
|
|
||||||
// we can probably TCS this instead of spin loop but w/e
|
// we can probably TCS this instead of spin loop but w/e
|
||||||
while (State != ShardState.Identifying)
|
while (State != ShardState.Identifying)
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ namespace Myriad.Gateway
|
|||||||
Payload = payload
|
Payload = payload
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ConnectInner()
|
private async Task ConnectInner()
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
@ -148,12 +148,12 @@ namespace Myriad.Gateway
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DisconnectInner(WebSocketCloseStatus closeStatus)
|
private async Task DisconnectInner(WebSocketCloseStatus closeStatus)
|
||||||
{
|
{
|
||||||
await _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
|
||||||
@ -165,8 +165,8 @@ namespace Myriad.Gateway
|
|||||||
Intents = _settings.Intents,
|
Intents = _settings.Intents,
|
||||||
Properties = new GatewayIdentify.ConnectionProperties
|
Properties = new GatewayIdentify.ConnectionProperties
|
||||||
{
|
{
|
||||||
Browser = LibraryName,
|
Browser = LibraryName,
|
||||||
Device = LibraryName,
|
Device = LibraryName,
|
||||||
Os = Environment.OSVersion.ToString()
|
Os = Environment.OSVersion.ToString()
|
||||||
},
|
},
|
||||||
Shard = _info,
|
Shard = _info,
|
||||||
@ -175,7 +175,7 @@ namespace Myriad.Gateway
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -184,12 +184,12 @@ namespace Myriad.Gateway
|
|||||||
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)
|
||||||
{
|
{
|
||||||
_reconnectDelay = delay;
|
_reconnectDelay = delay;
|
||||||
@ -202,7 +202,7 @@ namespace Myriad.Gateway
|
|||||||
Ready?.Invoke();
|
Ready?.Invoke();
|
||||||
if (arg is ResumedEvent)
|
if (arg is ResumedEvent)
|
||||||
Resumed?.Invoke();
|
Resumed?.Invoke();
|
||||||
|
|
||||||
await (OnEventReceived?.Invoke(arg) ?? Task.CompletedTask);
|
await (OnEventReceived?.Invoke(arg) ?? Task.CompletedTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
private async Task HandleConnectionClosed(WebSocketCloseStatus? closeStatus, string? description)
|
private async Task HandleConnectionClosed(WebSocketCloseStatus? closeStatus, string? description)
|
||||||
{
|
{
|
||||||
_logger.Information("Shard {ShardId}: Connection closed ({CloseStatus}/{Description})",
|
_logger.Information("Shard {ShardId}: Connection closed ({CloseStatus}/{Description})",
|
||||||
_info.ShardId, closeStatus, description ?? "<null>");
|
_info.ShardId, closeStatus, description ?? "<null>");
|
||||||
await _stateManager.HandleConnectionClosed();
|
await _stateManager.HandleConnectionClosed();
|
||||||
SocketClosed?.Invoke(closeStatus, description);
|
SocketClosed?.Invoke(closeStatus, description);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -13,11 +13,11 @@ namespace Myriad.Gateway
|
|||||||
private ClientWebSocket? _client;
|
private ClientWebSocket? _client;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ShardPacketSerializer _serializer;
|
private readonly ShardPacketSerializer _serializer;
|
||||||
|
|
||||||
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 ShardConnection(JsonSerializerOptions jsonSerializerOptions, ILogger logger)
|
||||||
{
|
{
|
||||||
_logger = logger.ForContext<ShardConnection>();
|
_logger = logger.ForContext<ShardConnection>();
|
||||||
@ -28,7 +28,7 @@ namespace Myriad.Gateway
|
|||||||
{
|
{
|
||||||
_client?.Dispose();
|
_client?.Dispose();
|
||||||
_client = new ClientWebSocket();
|
_client = new ClientWebSocket();
|
||||||
|
|
||||||
await _client.ConnectAsync(GetConnectionUri(url), ct);
|
await _client.ConnectAsync(GetConnectionUri(url), ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ namespace Myriad.Gateway
|
|||||||
public async Task Send(GatewayPacket packet)
|
public async Task Send(GatewayPacket packet)
|
||||||
{
|
{
|
||||||
// from `ManagedWebSocket.s_validSendStates`
|
// from `ManagedWebSocket.s_validSendStates`
|
||||||
if (_client is not {State: WebSocketState.Open or WebSocketState.CloseReceived})
|
if (_client is not { State: WebSocketState.Open or WebSocketState.CloseReceived })
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -62,7 +62,7 @@ namespace Myriad.Gateway
|
|||||||
public async Task<GatewayPacket?> Read()
|
public async Task<GatewayPacket?> Read()
|
||||||
{
|
{
|
||||||
// from `ManagedWebSocket.s_validReceiveStates`
|
// from `ManagedWebSocket.s_validReceiveStates`
|
||||||
if (_client is not {State: WebSocketState.Open or WebSocketState.CloseSent})
|
if (_client is not { State: WebSocketState.Open or WebSocketState.CloseSent })
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -79,7 +79,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"
|
Query = "v=9&encoding=json"
|
||||||
@ -89,10 +89,10 @@ namespace Myriad.Gateway
|
|||||||
{
|
{
|
||||||
if (_client == null)
|
if (_client == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var client = _client;
|
var client = _client;
|
||||||
_client = null;
|
_client = null;
|
||||||
|
|
||||||
// from `ManagedWebSocket.s_validCloseStates`
|
// from `ManagedWebSocket.s_validCloseStates`
|
||||||
if (client.State is WebSocketState.Open or WebSocketState.CloseReceived or WebSocketState.CloseSent)
|
if (client.State is WebSocketState.Open or WebSocketState.CloseReceived or WebSocketState.CloseSent)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Gateway
|
namespace Myriad.Gateway
|
||||||
{
|
{
|
||||||
public record ShardInfo(int ShardId, int NumShards);
|
public record ShardInfo(int ShardId, int NumShards);
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
@ -10,7 +10,7 @@ namespace Myriad.Gateway
|
|||||||
public class ShardPacketSerializer
|
public class ShardPacketSerializer
|
||||||
{
|
{
|
||||||
private const int BufferSize = 64 * 1024;
|
private const int BufferSize = 64 * 1024;
|
||||||
|
|
||||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||||
|
|
||||||
public ShardPacketSerializer(JsonSerializerOptions jsonSerializerOptions)
|
public ShardPacketSerializer(JsonSerializerOptions jsonSerializerOptions)
|
||||||
@ -21,15 +21,15 @@ namespace Myriad.Gateway
|
|||||||
public async ValueTask<(WebSocketMessageType type, GatewayPacket? packet)> ReadPacket(ClientWebSocket socket)
|
public async ValueTask<(WebSocketMessageType type, GatewayPacket? packet)> ReadPacket(ClientWebSocket socket)
|
||||||
{
|
{
|
||||||
using var buf = MemoryPool<byte>.Shared.Rent(BufferSize);
|
using var buf = MemoryPool<byte>.Shared.Rent(BufferSize);
|
||||||
|
|
||||||
var res = await socket.ReceiveAsync(buf.Memory, default);
|
var res = await socket.ReceiveAsync(buf.Memory, default);
|
||||||
if (res.MessageType == WebSocketMessageType.Close)
|
if (res.MessageType == WebSocketMessageType.Close)
|
||||||
return (res.MessageType, null);
|
return (res.MessageType, null);
|
||||||
|
|
||||||
if (res.EndOfMessage)
|
if (res.EndOfMessage)
|
||||||
// Entire packet fits within one buffer, deserialize directly
|
// Entire packet fits within one buffer, deserialize directly
|
||||||
return DeserializeSingleBuffer(buf, res);
|
return DeserializeSingleBuffer(buf, res);
|
||||||
|
|
||||||
// Otherwise copy to stream buffer and deserialize from there
|
// Otherwise copy to stream buffer and deserialize from there
|
||||||
return await DeserializeMultipleBuffer(socket, buf, res);
|
return await DeserializeMultipleBuffer(socket, buf, res);
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ namespace Myriad.Gateway
|
|||||||
stream.Write(buf.Memory.Span.Slice(0, res.Count));
|
stream.Write(buf.Memory.Span.Slice(0, res.Count));
|
||||||
}
|
}
|
||||||
|
|
||||||
return DeserializeObject(res, stream.GetBuffer().AsSpan(0, (int) stream.Length));
|
return DeserializeObject(res, stream.GetBuffer().AsSpan(0, (int)stream.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
private (WebSocketMessageType type, GatewayPacket packet) DeserializeSingleBuffer(
|
private (WebSocketMessageType type, GatewayPacket packet) DeserializeSingleBuffer(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ namespace Myriad.Gateway.State
|
|||||||
{
|
{
|
||||||
private Task? _worker;
|
private Task? _worker;
|
||||||
private CancellationTokenSource? _workerCts;
|
private CancellationTokenSource? _workerCts;
|
||||||
|
|
||||||
public TimeSpan? CurrentHeartbeatInterval { get; private set; }
|
public TimeSpan? CurrentHeartbeatInterval { get; private set; }
|
||||||
|
|
||||||
public async ValueTask Start(TimeSpan heartbeatInterval, Func<Task> callback)
|
public async ValueTask Start(TimeSpan heartbeatInterval, Func<Task> callback)
|
||||||
@ -23,9 +23,9 @@ namespace Myriad.Gateway.State
|
|||||||
|
|
||||||
public async ValueTask Stop()
|
public async ValueTask Stop()
|
||||||
{
|
{
|
||||||
if (_worker == null)
|
if (_worker == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_workerCts?.Cancel();
|
_workerCts?.Cancel();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -38,12 +38,12 @@ namespace Myriad.Gateway.State
|
|||||||
_worker = null;
|
_worker = null;
|
||||||
CurrentHeartbeatInterval = null;
|
CurrentHeartbeatInterval = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Worker(TimeSpan heartbeatInterval, Func<Task> callback, CancellationToken ct)
|
private async Task Worker(TimeSpan heartbeatInterval, Func<Task> callback, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var initialDelay = GetInitialHeartbeatDelay(heartbeatInterval);
|
var initialDelay = GetInitialHeartbeatDelay(heartbeatInterval);
|
||||||
await Task.Delay(initialDelay, ct);
|
await Task.Delay(initialDelay, ct);
|
||||||
|
|
||||||
while (!ct.IsCancellationRequested)
|
while (!ct.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await callback();
|
await callback();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Gateway.State
|
namespace Myriad.Gateway.State
|
||||||
{
|
{
|
||||||
public enum ShardState
|
public enum ShardState
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -18,7 +18,7 @@ namespace Myriad.Gateway
|
|||||||
private readonly ShardInfo _info;
|
private readonly ShardInfo _info;
|
||||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||||
private ShardState _state = ShardState.Disconnected;
|
private ShardState _state = ShardState.Disconnected;
|
||||||
|
|
||||||
private DateTimeOffset? _lastHeartbeatSent;
|
private DateTimeOffset? _lastHeartbeatSent;
|
||||||
private TimeSpan? _latency;
|
private TimeSpan? _latency;
|
||||||
private bool _hasReceivedHeartbeatAck;
|
private bool _hasReceivedHeartbeatAck;
|
||||||
@ -30,7 +30,7 @@ namespace Myriad.Gateway
|
|||||||
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; }
|
||||||
|
|
||||||
public Func<Task> SendIdentify { get; init; }
|
public Func<Task> SendIdentify { get; init; }
|
||||||
public Func<(string SessionId, int? LastSeq), Task> SendResume { get; init; }
|
public Func<(string SessionId, int? LastSeq), Task> SendResume { get; init; }
|
||||||
public Func<int?, Task> SendHeartbeat { get; init; }
|
public Func<int?, Task> SendHeartbeat { get; init; }
|
||||||
@ -52,7 +52,7 @@ namespace Myriad.Gateway
|
|||||||
_state = ShardState.Handshaking;
|
_state = ShardState.Handshaking;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleConnectionClosed()
|
public async Task HandleConnectionClosed()
|
||||||
{
|
{
|
||||||
_latency = null;
|
_latency = null;
|
||||||
@ -71,36 +71,36 @@ namespace Myriad.Gateway
|
|||||||
case GatewayOpcode.Heartbeat:
|
case GatewayOpcode.Heartbeat:
|
||||||
await HandleHeartbeatRequest();
|
await HandleHeartbeatRequest();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GatewayOpcode.HeartbeatAck:
|
case GatewayOpcode.HeartbeatAck:
|
||||||
await HandleHeartbeatAck();
|
await HandleHeartbeatAck();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GatewayOpcode.Reconnect:
|
case GatewayOpcode.Reconnect:
|
||||||
{
|
{
|
||||||
await HandleReconnect();
|
await HandleReconnect();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GatewayOpcode.InvalidSession:
|
case GatewayOpcode.InvalidSession:
|
||||||
{
|
{
|
||||||
var canResume = DeserializePayload<bool>(packet);
|
var canResume = DeserializePayload<bool>(packet);
|
||||||
await HandleInvalidSession(canResume);
|
await HandleInvalidSession(canResume);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GatewayOpcode.Dispatch:
|
case GatewayOpcode.Dispatch:
|
||||||
_lastSeq = packet.Sequence;
|
_lastSeq = packet.Sequence;
|
||||||
|
|
||||||
var evt = DeserializeEvent(packet.EventType!, (JsonElement) packet.Payload!);
|
var evt = DeserializeEvent(packet.EventType!, (JsonElement)packet.Payload!);
|
||||||
if (evt != null)
|
if (evt != null)
|
||||||
{
|
{
|
||||||
if (evt is ReadyEvent ready)
|
if (evt is ReadyEvent ready)
|
||||||
await HandleReady(ready);
|
await HandleReady(ready);
|
||||||
|
|
||||||
if (evt is ResumedEvent)
|
if (evt is ResumedEvent)
|
||||||
await HandleResumed();
|
await HandleResumed();
|
||||||
|
|
||||||
await HandleEvent(evt);
|
await HandleEvent(evt);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -110,7 +110,7 @@ namespace Myriad.Gateway
|
|||||||
private async Task HandleHello(GatewayHello hello)
|
private async Task HandleHello(GatewayHello hello)
|
||||||
{
|
{
|
||||||
var interval = TimeSpan.FromMilliseconds(hello.HeartbeatInterval);
|
var interval = TimeSpan.FromMilliseconds(hello.HeartbeatInterval);
|
||||||
|
|
||||||
_hasReceivedHeartbeatAck = true;
|
_hasReceivedHeartbeatAck = true;
|
||||||
await _heartbeatWorker.Start(interval, HandleHeartbeatTimer);
|
await _heartbeatWorker.Start(interval, HandleHeartbeatTimer);
|
||||||
await IdentifyOrResume();
|
await IdentifyOrResume();
|
||||||
@ -152,14 +152,14 @@ namespace Myriad.Gateway
|
|||||||
_sessionId = null;
|
_sessionId = null;
|
||||||
_lastSeq = null;
|
_lastSeq = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Information("Shard {ShardId}: Received Invalid Session (can resume? {CanResume})",
|
_logger.Information("Shard {ShardId}: Received Invalid Session (can resume? {CanResume})",
|
||||||
_info.ShardId, canResume);
|
_info.ShardId, canResume);
|
||||||
|
|
||||||
var delay = TimeSpan.FromMilliseconds(new Random().Next(1000, 5000));
|
var delay = TimeSpan.FromMilliseconds(new Random().Next(1000, 5000));
|
||||||
await DoReconnect(WebSocketCloseStatus.NormalClosure, delay);
|
await DoReconnect(WebSocketCloseStatus.NormalClosure, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleReconnect()
|
private async Task HandleReconnect()
|
||||||
{
|
{
|
||||||
_logger.Information("Shard {ShardId}: Received Reconnect", _info.ShardId);
|
_logger.Information("Shard {ShardId}: Received Reconnect", _info.ShardId);
|
||||||
@ -167,7 +167,7 @@ namespace Myriad.Gateway
|
|||||||
// we use 1005 (no error specified) instead
|
// we use 1005 (no error specified) instead
|
||||||
await DoReconnect(WebSocketCloseStatus.Empty, TimeSpan.FromSeconds(1));
|
await DoReconnect(WebSocketCloseStatus.Empty, TimeSpan.FromSeconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task HandleReady(ReadyEvent ready)
|
private Task HandleReady(ReadyEvent ready)
|
||||||
{
|
{
|
||||||
_logger.Information("Shard {ShardId}: Received Ready", _info.ShardId);
|
_logger.Information("Shard {ShardId}: Received Ready", _info.ShardId);
|
||||||
@ -218,10 +218,10 @@ namespace Myriad.Gateway
|
|||||||
|
|
||||||
private T DeserializePayload<T>(GatewayPacket packet)
|
private T DeserializePayload<T>(GatewayPacket packet)
|
||||||
{
|
{
|
||||||
var packetPayload = (JsonElement) packet.Payload!;
|
var packetPayload = (JsonElement)packet.Payload!;
|
||||||
return JsonSerializer.Deserialize<T>(packetPayload.GetRawText(), _jsonSerializerOptions)!;
|
return JsonSerializer.Deserialize<T>(packetPayload.GetRawText(), _jsonSerializerOptions)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IGatewayEvent? DeserializeEvent(string eventType, JsonElement payload)
|
private IGatewayEvent? DeserializeEvent(string eventType, JsonElement payload)
|
||||||
{
|
{
|
||||||
if (!IGatewayEvent.EventTypes.TryGetValue(eventType, out var clrType))
|
if (!IGatewayEvent.EventTypes.TryGetValue(eventType, out var clrType))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@ -52,7 +52,7 @@ namespace Myriad.Rest
|
|||||||
var waitPolicy = Policy
|
var waitPolicy = Policy
|
||||||
.Handle<RatelimitBucketExhaustedException>()
|
.Handle<RatelimitBucketExhaustedException>()
|
||||||
.WaitAndRetryAsync(3,
|
.WaitAndRetryAsync(3,
|
||||||
(_, e, _) => ((RatelimitBucketExhaustedException) e).RetryAfter,
|
(_, e, _) => ((RatelimitBucketExhaustedException)e).RetryAfter,
|
||||||
(_, _, _, _) => Task.CompletedTask)
|
(_, _, _, _) => Task.CompletedTask)
|
||||||
.AsAsyncPolicy<HttpResponseMessage>();
|
.AsAsyncPolicy<HttpResponseMessage>();
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ namespace Myriad.Rest
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T?> Get<T>(string path, (string endpointName, ulong major) ratelimitParams) where T: class
|
public async Task<T?> Get<T>(string path, (string endpointName, ulong major) ratelimitParams) where T : class
|
||||||
{
|
{
|
||||||
using var response = await Send(() => new HttpRequestMessage(HttpMethod.Get, ApiBaseUrl + path),
|
using var response = await Send(() => new HttpRequestMessage(HttpMethod.Get, ApiBaseUrl + path),
|
||||||
ratelimitParams, true);
|
ratelimitParams, true);
|
||||||
@ -81,7 +81,7 @@ namespace Myriad.Rest
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T?> Post<T>(string path, (string endpointName, ulong major) ratelimitParams, object? body)
|
public async Task<T?> Post<T>(string path, (string endpointName, ulong major) ratelimitParams, object? body)
|
||||||
where T: class
|
where T : class
|
||||||
{
|
{
|
||||||
using var response = await Send(() =>
|
using var response = await Send(() =>
|
||||||
{
|
{
|
||||||
@ -91,9 +91,9 @@ namespace Myriad.Rest
|
|||||||
}, ratelimitParams);
|
}, ratelimitParams);
|
||||||
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(() =>
|
||||||
{
|
{
|
||||||
@ -105,7 +105,7 @@ namespace Myriad.Rest
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T?> Patch<T>(string path, (string endpointName, ulong major) ratelimitParams, object? body)
|
public async Task<T?> Patch<T>(string path, (string endpointName, ulong major) ratelimitParams, object? body)
|
||||||
where T: class
|
where T : class
|
||||||
{
|
{
|
||||||
using var response = await Send(() =>
|
using var response = await Send(() =>
|
||||||
{
|
{
|
||||||
@ -117,7 +117,7 @@ namespace Myriad.Rest
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T?> Put<T>(string path, (string endpointName, ulong major) ratelimitParams, object? body)
|
public async Task<T?> Put<T>(string path, (string endpointName, ulong major) ratelimitParams, object? body)
|
||||||
where T: class
|
where T : class
|
||||||
{
|
{
|
||||||
using var response = await Send(() =>
|
using var response = await Send(() =>
|
||||||
{
|
{
|
||||||
@ -160,7 +160,7 @@ namespace Myriad.Rest
|
|||||||
request.Content = mfd;
|
request.Content = mfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<T?> ReadResponse<T>(HttpResponseMessage response) where T: class
|
private async Task<T?> ReadResponse<T>(HttpResponseMessage response) where T : class
|
||||||
{
|
{
|
||||||
if (response.StatusCode == HttpStatusCode.NoContent)
|
if (response.StatusCode == HttpStatusCode.NoContent)
|
||||||
return null;
|
return null;
|
||||||
@ -174,7 +174,7 @@ namespace Myriad.Rest
|
|||||||
return await _retryPolicy.ExecuteAsync(async _ =>
|
return await _retryPolicy.ExecuteAsync(async _ =>
|
||||||
{
|
{
|
||||||
using var __ = LogContext.PushProperty("EndpointName", ratelimitParams.endpointName);
|
using var __ = LogContext.PushProperty("EndpointName", ratelimitParams.endpointName);
|
||||||
|
|
||||||
var request = createRequest();
|
var request = createRequest();
|
||||||
_logger.Debug("Request: {RequestMethod} {RequestPath}",
|
_logger.Debug("Request: {RequestMethod} {RequestPath}",
|
||||||
request.Method, request.RequestUri);
|
request.Method, request.RequestUri);
|
||||||
@ -189,7 +189,7 @@ namespace Myriad.Rest
|
|||||||
|
|
||||||
_logger.Debug(
|
_logger.Debug(
|
||||||
"Response: {RequestMethod} {RequestPath} -> {StatusCode} {ReasonPhrase} (in {ResponseDurationMs} ms)",
|
"Response: {RequestMethod} {RequestPath} -> {StatusCode} {ReasonPhrase} (in {ResponseDurationMs} ms)",
|
||||||
request.Method, request.RequestUri, (int) response.StatusCode, response.ReasonPhrase, stopwatch.ElapsedMilliseconds);
|
request.Method, request.RequestUri, (int)response.StatusCode, response.ReasonPhrase, stopwatch.ElapsedMilliseconds);
|
||||||
|
|
||||||
await HandleApiError(response, ignoreNotFound);
|
await HandleApiError(response, ignoreNotFound);
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ namespace Myriad.Rest
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ namespace Myriad.Rest
|
|||||||
|
|
||||||
public Task<Guild?> GetGuild(ulong id) =>
|
public Task<Guild?> GetGuild(ulong id) =>
|
||||||
_client.Get<Guild>($"/guilds/{id}", ("GetGuild", id));
|
_client.Get<Guild>($"/guilds/{id}", ("GetGuild", id));
|
||||||
|
|
||||||
public Task<Channel[]> GetGuildChannels(ulong id) =>
|
public Task<Channel[]> GetGuildChannels(ulong id) =>
|
||||||
_client.Get<Channel[]>($"/guilds/{id}/channels", ("GetGuildChannels", id))!;
|
_client.Get<Channel[]>($"/guilds/{id}/channels", ("GetGuildChannels", id))!;
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ namespace Myriad.Rest
|
|||||||
|
|
||||||
public Task<Webhook> CreateWebhook(ulong channelId, CreateWebhookRequest request) =>
|
public Task<Webhook> CreateWebhook(ulong channelId, CreateWebhookRequest request) =>
|
||||||
_client.Post<Webhook>($"/channels/{channelId}/webhooks", ("CreateWebhook", channelId), request)!;
|
_client.Post<Webhook>($"/channels/{channelId}/webhooks", ("CreateWebhook", channelId), request)!;
|
||||||
|
|
||||||
public Task<Webhook> GetWebhook(ulong webhookId) =>
|
public Task<Webhook> GetWebhook(ulong webhookId) =>
|
||||||
_client.Get<Webhook>($"/webhooks/{webhookId}/webhooks", ("GetWebhook", webhookId))!;
|
_client.Get<Webhook>($"/webhooks/{webhookId}/webhooks", ("GetWebhook", webhookId))!;
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ namespace Myriad.Rest
|
|||||||
var url = $"/webhooks/{webhookId}/{webhookToken}?wait=true";
|
var url = $"/webhooks/{webhookId}/{webhookToken}?wait=true";
|
||||||
if (threadId != null)
|
if (threadId != null)
|
||||||
url += $"&thread_id={threadId}";
|
url += $"&thread_id={threadId}";
|
||||||
|
|
||||||
return _client.PostMultipart<Message>(url,
|
return _client.PostMultipart<Message>(url,
|
||||||
("ExecuteWebhook", webhookId), request, files)!;
|
("ExecuteWebhook", webhookId), request, files)!;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Myriad.Rest
|
namespace Myriad.Rest
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
@ -29,43 +29,49 @@ namespace Myriad.Rest.Exceptions
|
|||||||
|
|
||||||
public class NotFoundException: DiscordRequestException
|
public class NotFoundException: DiscordRequestException
|
||||||
{
|
{
|
||||||
public NotFoundException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError): base(
|
public NotFoundException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError) : base(
|
||||||
response, responseBody, apiError) { }
|
response, responseBody, apiError)
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ForbiddenException: DiscordRequestException
|
public class ForbiddenException: DiscordRequestException
|
||||||
{
|
{
|
||||||
public ForbiddenException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError): base(
|
public ForbiddenException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError) : base(
|
||||||
response, responseBody, apiError) { }
|
response, responseBody, apiError)
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConflictException: DiscordRequestException
|
public class ConflictException: DiscordRequestException
|
||||||
{
|
{
|
||||||
public ConflictException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError): base(
|
public ConflictException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError) : base(
|
||||||
response, responseBody, apiError) { }
|
response, responseBody, apiError)
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BadRequestException: DiscordRequestException
|
public class BadRequestException: DiscordRequestException
|
||||||
{
|
{
|
||||||
public BadRequestException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError): base(
|
public BadRequestException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError) : base(
|
||||||
response, responseBody, apiError) { }
|
response, responseBody, apiError)
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TooManyRequestsException: DiscordRequestException
|
public class TooManyRequestsException: DiscordRequestException
|
||||||
{
|
{
|
||||||
public TooManyRequestsException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError):
|
public TooManyRequestsException(HttpResponseMessage response, string responseBody, DiscordApiError? apiError) :
|
||||||
base(response, responseBody, apiError) { }
|
base(response, responseBody, apiError)
|
||||||
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UnknownDiscordRequestException: DiscordRequestException
|
public class UnknownDiscordRequestException: DiscordRequestException
|
||||||
{
|
{
|
||||||
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,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
using Myriad.Rest.Ratelimit;
|
using Myriad.Rest.Ratelimit;
|
||||||
|
|
||||||
@ -6,12 +6,12 @@ namespace Myriad.Rest.Exceptions
|
|||||||
{
|
{
|
||||||
public class RatelimitException: Exception
|
public class RatelimitException: Exception
|
||||||
{
|
{
|
||||||
public RatelimitException(string? message): base(message) { }
|
public RatelimitException(string? message) : base(message) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RatelimitBucketExhaustedException: RatelimitException
|
public class RatelimitBucketExhaustedException: RatelimitException
|
||||||
{
|
{
|
||||||
public RatelimitBucketExhaustedException(Bucket bucket, TimeSpan retryAfter): base(
|
public RatelimitBucketExhaustedException(Bucket bucket, TimeSpan retryAfter) : base(
|
||||||
"Rate limit bucket exhausted, request blocked")
|
"Rate limit bucket exhausted, request blocked")
|
||||||
{
|
{
|
||||||
Bucket = bucket;
|
Bucket = bucket;
|
||||||
@ -24,6 +24,6 @@ namespace Myriad.Rest.Exceptions
|
|||||||
|
|
||||||
public class GloballyRatelimitedException: RatelimitException
|
public class GloballyRatelimitedException: RatelimitException
|
||||||
{
|
{
|
||||||
public GloballyRatelimitedException(): base("Global rate limit hit") { }
|
public GloballyRatelimitedException() : base("Global rate limit hit") { }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -54,7 +54,7 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
"{BucketKey}/{BucketMajor}: Bucket has [{BucketRemaining}/{BucketLimit} left], allowing through",
|
"{BucketKey}/{BucketMajor}: Bucket has [{BucketRemaining}/{BucketLimit} left], allowing through",
|
||||||
Key, Major, Remaining, Limit);
|
Key, Major, Remaining, Limit);
|
||||||
Remaining--;
|
Remaining--;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_semaphore.Wait();
|
_semaphore.Wait();
|
||||||
|
|
||||||
_logger.Verbose("{BucketKey}/{BucketMajor}: Received rate limit headers: {@RateLimitHeaders}",
|
_logger.Verbose("{BucketKey}/{BucketMajor}: Received rate limit headers: {@RateLimitHeaders}",
|
||||||
Key, Major, headers);
|
Key, Major, headers);
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (headers.Limit != null)
|
if (headers.Limit != null)
|
||||||
Limit = headers.Limit.Value;
|
Limit = headers.Limit.Value;
|
||||||
|
|
||||||
if (headers.Remaining != null && !_hasReceivedHeaders)
|
if (headers.Remaining != null && !_hasReceivedHeaders)
|
||||||
@ -98,7 +98,7 @@ 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;
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_semaphore.Wait();
|
_semaphore.Wait();
|
||||||
|
|
||||||
// If we don't have any reset data, "snap" it to now
|
// If we don't have any reset data, "snap" it to now
|
||||||
// This happens before first request and at this point the reset is invalid anyway, so it's fine
|
// This happens before first request and at this point the reset is invalid anyway, so it's fine
|
||||||
// but it ensures the stale timeout doesn't trigger early by using `default` value
|
// but it ensures the stale timeout doesn't trigger early by using `default` value
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -72,7 +72,7 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
{
|
{
|
||||||
if (now - bucket.LastUsed <= StaleBucketTimeout)
|
if (now - bucket.LastUsed <= StaleBucketTimeout)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_logger.Debug("Pruning unused bucket {BucketKey}/{BucketMajor} (last used at {BucketLastUsed})",
|
_logger.Debug("Pruning unused bucket {BucketKey}/{BucketMajor} (last used at {BucketLastUsed})",
|
||||||
bucket.Key, bucket.Major, bucket.LastUsed);
|
bucket.Key, bucket.Major, bucket.LastUsed);
|
||||||
_buckets.TryRemove(key, out _);
|
_buckets.TryRemove(key, out _);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@ -13,7 +13,7 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
private const string ResetAfterHeader = "X-RateLimit-Reset-After";
|
private const string ResetAfterHeader = "X-RateLimit-Reset-After";
|
||||||
private const string BucketHeader = "X-RateLimit-Bucket";
|
private const string BucketHeader = "X-RateLimit-Bucket";
|
||||||
private const string GlobalHeader = "X-RateLimit-Global";
|
private const string GlobalHeader = "X-RateLimit-Global";
|
||||||
|
|
||||||
public bool Global { get; private set; }
|
public bool Global { get; private set; }
|
||||||
public int? Limit { get; private set; }
|
public int? Limit { get; private set; }
|
||||||
public int? Remaining { get; private set; }
|
public int? Remaining { get; private set; }
|
||||||
@ -25,7 +25,7 @@ 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 RatelimitHeaders() { }
|
||||||
|
|
||||||
public static RatelimitHeaders Parse(HttpResponseMessage response)
|
public static RatelimitHeaders Parse(HttpResponseMessage response)
|
||||||
@ -41,12 +41,12 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
|
|
||||||
var resetTimestamp = TryGetDouble(response, ResetHeader);
|
var resetTimestamp = TryGetDouble(response, ResetHeader);
|
||||||
if (resetTimestamp != null)
|
if (resetTimestamp != null)
|
||||||
headers.Reset = DateTimeOffset.FromUnixTimeMilliseconds((long) (resetTimestamp.Value * 1000));
|
headers.Reset = DateTimeOffset.FromUnixTimeMilliseconds((long)(resetTimestamp.Value * 1000));
|
||||||
|
|
||||||
var resetAfterSeconds = TryGetDouble(response, ResetAfterHeader);
|
var resetAfterSeconds = TryGetDouble(response, ResetAfterHeader);
|
||||||
if (resetAfterSeconds != null)
|
if (resetAfterSeconds != null)
|
||||||
headers.ResetAfter = TimeSpan.FromSeconds(resetAfterSeconds.Value);
|
headers.ResetAfter = TimeSpan.FromSeconds(resetAfterSeconds.Value);
|
||||||
|
|
||||||
var global = TryGetHeader(response, GlobalHeader);
|
var global = TryGetHeader(response, GlobalHeader);
|
||||||
if (global != null && bool.TryParse(global, out var globalBool))
|
if (global != null && bool.TryParse(global, out var globalBool))
|
||||||
headers.Global = globalBool;
|
headers.Global = globalBool;
|
||||||
@ -58,27 +58,27 @@ namespace Myriad.Rest.Ratelimit
|
|||||||
{
|
{
|
||||||
if (!response.Headers.TryGetValues(headerName, out var values))
|
if (!response.Headers.TryGetValues(headerName, out var values))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return values.FirstOrDefault();
|
return values.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int? TryGetInt(HttpResponseMessage response, string headerName)
|
private static int? TryGetInt(HttpResponseMessage response, string headerName)
|
||||||
{
|
{
|
||||||
var valueString = TryGetHeader(response, headerName);
|
var valueString = TryGetHeader(response, headerName);
|
||||||
|
|
||||||
if (!int.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
|
if (!int.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double? TryGetDouble(HttpResponseMessage response, string headerName)
|
private static double? TryGetDouble(HttpResponseMessage response, string headerName)
|
||||||
{
|
{
|
||||||
var valueString = TryGetHeader(response, headerName);
|
var valueString = TryGetHeader(response, headerName);
|
||||||
|
|
||||||
if (!double.TryParse(valueString, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
|
if (!double.TryParse(valueString, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
using Myriad.Rest.Exceptions;
|
using Myriad.Rest.Exceptions;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Serialization;
|
using Myriad.Serialization;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types
|
namespace Myriad.Rest.Types
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests
|
||||||
{
|
{
|
||||||
public record CreateDmRequest(ulong RecipientId);
|
public record CreateDmRequest(ulong RecipientId);
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests
|
||||||
{
|
{
|
||||||
public record CreateWebhookRequest(string Name);
|
public record CreateWebhookRequest(string Name);
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
@ -8,14 +8,14 @@ namespace Myriad.Rest.Types.Requests
|
|||||||
public record MessageEditRequest
|
public record MessageEditRequest
|
||||||
{
|
{
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<string?> Content { get; init; }
|
public Optional<string?> Content { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<Embed?> Embed { get; init; }
|
public Optional<Embed?> Embed { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<Message.MessageFlags> Flags { get; init; }
|
public Optional<Message.MessageFlags> Flags { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<AllowedMentions> AllowedMentions { get; init; }
|
public Optional<AllowedMentions> AllowedMentions { get; init; }
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Myriad.Types;
|
using Myriad.Types;
|
||||||
|
|
||||||
namespace Myriad.Rest.Types.Requests
|
namespace Myriad.Rest.Types.Requests
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Rest.Types
|
namespace Myriad.Rest.Types
|
||||||
{
|
{
|
||||||
public record ModifyGuildMemberRequest
|
public record ModifyGuildMemberRequest
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
|
|
||||||
@ -7,8 +7,8 @@ namespace Myriad.Rest.Types.Requests
|
|||||||
public record WebhookMessageEditRequest
|
public record WebhookMessageEditRequest
|
||||||
{
|
{
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<string?> Content { get; init; }
|
public Optional<string?> Content { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<AllowedMentions> AllowedMentions { get; init; }
|
public Optional<AllowedMentions> AllowedMentions { get; init; }
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Myriad.Serialization
|
namespace Myriad.Serialization
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
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;
|
||||||
@ -22,11 +22,11 @@ namespace Myriad.Serialization
|
|||||||
JsonSerializer.Serialize(writer, value.HasValue ? value.GetValue() : default, typeof(T), 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];
|
||||||
return (JsonConverter?) Activator.CreateInstance(
|
return (JsonConverter?)Activator.CreateInstance(
|
||||||
typeof(Inner<>).MakeGenericType(innerType),
|
typeof(Inner<>).MakeGenericType(innerType),
|
||||||
BindingFlags.Instance | BindingFlags.Public,
|
BindingFlags.Instance | BindingFlags.Public,
|
||||||
null,
|
null,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
@ -13,12 +13,12 @@ namespace Myriad.Serialization
|
|||||||
var str = reader.GetString();
|
var str = reader.GetString();
|
||||||
if (str == null) return default;
|
if (str == null) return default;
|
||||||
|
|
||||||
return (PermissionSet) ulong.Parse(str);
|
return (PermissionSet)ulong.Parse(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, PermissionSet value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, PermissionSet value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
writer.WriteStringValue(((ulong) value).ToString());
|
writer.WriteStringValue(((ulong)value).ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record Activity: ActivityPartial
|
public record Activity: ActivityPartial
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public record ActivityPartial
|
public record ActivityPartial
|
||||||
{
|
{
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
public ActivityType Type { get; init; }
|
public ActivityType Type { get; init; }
|
||||||
public string? Url { get; init; }
|
public string? Url { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ActivityType
|
public enum ActivityType
|
||||||
{
|
{
|
||||||
Game = 0,
|
Game = 0,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record Application: ApplicationPartial
|
public record Application: ApplicationPartial
|
||||||
{
|
{
|
||||||
@ -16,7 +16,7 @@
|
|||||||
public string? Slug { get; init; }
|
public string? Slug { get; init; }
|
||||||
public string? CoverImage { get; init; }
|
public string? CoverImage { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public record ApplicationPartial
|
public record ApplicationPartial
|
||||||
{
|
{
|
||||||
public ulong Id { get; init; }
|
public ulong Id { get; init; }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record ApplicationCommand
|
public record ApplicationCommand
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record ApplicationCommandInteractionData
|
public record ApplicationCommandInteractionData
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record ApplicationCommandInteractionDataOption
|
public record ApplicationCommandInteractionDataOption
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record Interaction
|
public record Interaction
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
using Myriad.Rest.Types;
|
using Myriad.Rest.Types;
|
||||||
using Myriad.Utils;
|
using Myriad.Utils;
|
||||||
@ -9,19 +9,19 @@ namespace Myriad.Types
|
|||||||
{
|
{
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<bool?> Tts { get; init; }
|
public Optional<bool?> Tts { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<string?> Content { get; init; }
|
public Optional<string?> Content { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<Embed[]?> Embeds { get; init; }
|
public Optional<Embed[]?> Embeds { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<AllowedMentions?> AllowedMentions { get; init; }
|
public Optional<AllowedMentions?> AllowedMentions { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<Message.MessageFlags> Flags { get; init; }
|
public Optional<Message.MessageFlags> Flags { get; init; }
|
||||||
|
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
public Optional<MessageComponent[]?> Components { get; init; }
|
public Optional<MessageComponent[]?> Components { get; init; }
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record InteractionResponse
|
public record InteractionResponse
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record Channel
|
public record Channel
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record Embed
|
public record Embed
|
||||||
{
|
{
|
||||||
@ -16,44 +16,44 @@
|
|||||||
public EmbedAuthor? Author { get; init; }
|
public EmbedAuthor? Author { get; init; }
|
||||||
public Field[]? Fields { get; init; }
|
public Field[]? Fields { get; init; }
|
||||||
|
|
||||||
public record EmbedFooter (
|
public record EmbedFooter(
|
||||||
string Text,
|
string Text,
|
||||||
string? IconUrl = null,
|
string? IconUrl = null,
|
||||||
string? ProxyIconUrl = null
|
string? ProxyIconUrl = null
|
||||||
);
|
);
|
||||||
|
|
||||||
public record EmbedImage (
|
public record EmbedImage(
|
||||||
string? Url,
|
string? Url,
|
||||||
uint? Width = null,
|
uint? Width = null,
|
||||||
uint? Height = null
|
uint? Height = null
|
||||||
);
|
);
|
||||||
|
|
||||||
public record EmbedThumbnail (
|
public record EmbedThumbnail(
|
||||||
string? Url,
|
string? Url,
|
||||||
string? ProxyUrl = null,
|
string? ProxyUrl = null,
|
||||||
uint? Width = null,
|
uint? Width = null,
|
||||||
uint? Height = null
|
uint? Height = null
|
||||||
);
|
);
|
||||||
|
|
||||||
public record EmbedVideo (
|
public record EmbedVideo(
|
||||||
string? Url,
|
string? Url,
|
||||||
uint? Width = null,
|
uint? Width = null,
|
||||||
uint? Height = null
|
uint? Height = null
|
||||||
);
|
);
|
||||||
|
|
||||||
public record EmbedProvider (
|
public record EmbedProvider(
|
||||||
string? Name,
|
string? Name,
|
||||||
string? Url
|
string? Url
|
||||||
);
|
);
|
||||||
|
|
||||||
public record EmbedAuthor (
|
public record EmbedAuthor(
|
||||||
string? Name = null,
|
string? Name = null,
|
||||||
string? Url = null,
|
string? Url = null,
|
||||||
string? IconUrl = null,
|
string? IconUrl = null,
|
||||||
string? ProxyIconUrl = null
|
string? ProxyIconUrl = null
|
||||||
);
|
);
|
||||||
|
|
||||||
public record Field (
|
public record Field(
|
||||||
string Name,
|
string Name,
|
||||||
string Value,
|
string Value,
|
||||||
bool Inline = false
|
bool Inline = false
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record Emoji
|
public record Emoji
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record GatewayInfo
|
public record GatewayInfo
|
||||||
{
|
{
|
||||||
public string Url { get; init; }
|
public string Url { get; init; }
|
||||||
|
|
||||||
public record Bot: GatewayInfo
|
public record Bot: GatewayInfo
|
||||||
{
|
{
|
||||||
public int Shards { get; init; }
|
public int Shards { get; init; }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record SessionStartLimit
|
public record SessionStartLimit
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Myriad.Types
|
namespace Myriad.Types
|
||||||
{
|
{
|
||||||
public record Guild
|
public record Guild
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user