Optional struct needs work still, + writing the converter for it is going to be a headachepull/1435/head
@@ -0,0 +1,106 @@ | |||||
https://gist.github.com/SinisterRectus/9518f3e7d0d1ccb4335b2a0d389c30b0 | |||||
Sorted By Route | |||||
-------------------------------------------------------------------------------------------------------------------- | |||||
Get Entitlements GET /applications/{application.id}/entitlements | |||||
Get Entitlement GET /applications/{application.id}/entitlements/{entitlement.id} | |||||
Delete Test Entitlement DELETE /applications/{application.id}/entitlements/{entitlement.id}/ | |||||
Consume SKU POST /applications/{application.id}/entitlements/{entitlement.id}/consume | |||||
Get SKUs GET /applications/{application.id}/skus | |||||
Delete/Close Channel DELETE /channels/{channel.id} | |||||
Get Channel GET /channels/{channel.id} | |||||
Modify Channel PUT/PATCH /channels/{channel.id} | |||||
Get Channel Invites GET /channels/{channel.id}/invites | |||||
Create Channel Invite POST /channels/{channel.id}/invites | |||||
Get Channel Messages GET /channels/{channel.id}/messages | |||||
Create Message POST /channels/{channel.id}/messages | |||||
Bulk Delete Messages POST /channels/{channel.id}/messages/bulk-delete | |||||
Bulk Delete Messages (deprecated) POST /channels/{channel.id}/messages/bulk_delete | |||||
Delete Message DELETE /channels/{channel.id}/messages/{message.id} | |||||
Get Channel Message GET /channels/{channel.id}/messages/{message.id} | |||||
Edit Message PATCH /channels/{channel.id}/messages/{message.id} | |||||
Delete All Reactions DELETE /channels/{channel.id}/messages/{message.id}/reactions | |||||
Get Reactions GET /channels/{channel.id}/messages/{message.id}/reactions/{emoji} | |||||
Delete Own Reaction DELETE /channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me | |||||
Create Reaction PUT /channels/{channel.id}/messages/{message.id}/reactions/{emoji}/@me | |||||
Delete User Reaction DELETE /channels/{channel.id}/messages/{message.id}/reactions/{emoji}/{user.id} | |||||
Delete Channel Permission DELETE /channels/{channel.id}/permissions/{overwrite.id} | |||||
Edit Channel Permissions PUT /channels/{channel.id}/permissions/{overwrite.id} | |||||
Get Pinned Messages GET /channels/{channel.id}/pins | |||||
Delete Pinned Channel Message DELETE /channels/{channel.id}/pins/{message.id} | |||||
Add Pinned Channel Message PUT /channels/{channel.id}/pins/{message.id} | |||||
Group DM Remove Recipient DELETE /channels/{channel.id}/recipients/{user.id} | |||||
Group DM Add Recipient PUT /channels/{channel.id}/recipients/{user.id} | |||||
Trigger Typing Indicator POST /channels/{channel.id}/typing | |||||
Get Channel Webhooks GET /channels/{channel.id}/webhooks | |||||
Create Webhook POST /channels/{channel.id}/webhooks | |||||
Get Gateway GET /gateway | |||||
Get Gateway Bot GET /gateway/bot | |||||
Create Guild POST /guilds | |||||
Delete Guild DELETE /guilds/{guild.id} | |||||
Get Guild GET /guilds/{guild.id} | |||||
Modify Guild PATCH /guilds/{guild.id} | |||||
Get Guild Audit Log GET /guilds/{guild.id}/audit-logs | |||||
Get Guild Bans GET /guilds/{guild.id}/bans | |||||
Remove Guild Ban DELETE /guilds/{guild.id}/bans/{user.id} | |||||
Get Guild Ban GET /guilds/{guild.id}/bans/{user.id} | |||||
Create Guild Ban PUT /guilds/{guild.id}/bans/{user.id} | |||||
Get Guild Channels GET /guilds/{guild.id}/channels | |||||
Modify Guild Channel Positions PATCH /guilds/{guild.id}/channels | |||||
Create Guild Channel POST /guilds/{guild.id}/channels | |||||
Get Guild Embed GET /guilds/{guild.id}/embed | |||||
Modify Guild Embed PATCH /guilds/{guild.id}/embed | |||||
List Guild Emojis GET /guilds/{guild.id}/emojis | |||||
Create Guild Emoji POST /guilds/{guild.id}/emojis | |||||
Delete Guild Emoji DELETE /guilds/{guild.id}/emojis/{emoji.id} | |||||
Get Guild Emoji GET /guilds/{guild.id}/emojis/{emoji.id} | |||||
Modify Guild Emoji PATCH /guilds/{guild.id}/emojis/{emoji.id} | |||||
Get Guild Integrations GET /guilds/{guild.id}/integrations | |||||
Create Guild Integration POST /guilds/{guild.id}/integrations | |||||
Delete Guild Integration DELETE /guilds/{guild.id}/integrations/{integration.id} | |||||
Modify Guild Integration PATCH /guilds/{guild.id}/integrations/{integration.id} | |||||
Sync Guild Integration POST /guilds/{guild.id}/integrations/{integration.id}/sync | |||||
Get Guild Invites GET /guilds/{guild.id}/invites | |||||
List Guild Members GET /guilds/{guild.id}/members | |||||
Modify Current User Nick PATCH /guilds/{guild.id}/members/@me/nick | |||||
Remove Guild Member DELETE /guilds/{guild.id}/members/{user.id} | |||||
Get Guild Member GET /guilds/{guild.id}/members/{user.id} | |||||
Modify Guild Member PATCH /guilds/{guild.id}/members/{user.id} | |||||
Add Guild Member PUT /guilds/{guild.id}/members/{user.id} | |||||
Remove Guild Member Role DELETE /guilds/{guild.id}/members/{user.id}/roles/{role.id} | |||||
Add Guild Member Role PUT /guilds/{guild.id}/members/{user.id}/roles/{role.id} | |||||
Get Guild Prune Count GET /guilds/{guild.id}/prune | |||||
Begin Guild Prune POST /guilds/{guild.id}/prune | |||||
Get Guild Voice Regions GET /guilds/{guild.id}/regions | |||||
Get Guild Roles GET /guilds/{guild.id}/roles | |||||
Modify Guild Role Positions PATCH /guilds/{guild.id}/roles | |||||
Create Guild Role POST /guilds/{guild.id}/roles | |||||
Delete Guild Role DELETE /guilds/{guild.id}/roles/{role.id} | |||||
Modify Guild Role PATCH /guilds/{guild.id}/roles/{role.id} | |||||
Get Guild Vanity URL GET /guilds/{guild.id}/vanity-url | |||||
Get Guild Webhooks GET /guilds/{guild.id}/webhooks | |||||
Get Guild Widget Image GET /guilds/{guild.id}/widget.png | |||||
Delete Invite DELETE /invites/{invite.code} | |||||
Get Invite GET /invites/{invite.code} | |||||
Get Current Application Information GET /oauth2/applications/@me | |||||
Delete Purchase Discount DELETE /store/skus/{sku.id}/discounts/{user.id}/ | |||||
Create Purchase Discount PUT /store/skus/{sku.id}/discounts/{user.id}/ | |||||
Get Current User GET /users/@me | |||||
Modify Current User PATCH /users/@me | |||||
Get User DMs GET /users/@me/channels | |||||
Create DM POST /users/@me/channels | |||||
Create Group DM POST /users/@me/channels | |||||
Get User Connections GET /users/@me/connections | |||||
Get Current User Guilds GET /users/@me/guilds | |||||
Leave Guild DELETE /users/@me/guilds/{guild.id} | |||||
Get User GET /users/{user.id} | |||||
List Voice Regions GET /voice/regions | |||||
Delete Webhook DELETE /webhooks/{webhook.id} | |||||
Get Webhook GET /webhooks/{webhook.id} | |||||
Modify Webhook PATCH /webhooks/{webhook.id} | |||||
Delete Webhook with Token DELETE /webhooks/{webhook.id}/{webhook.token} | |||||
Get Webhook with Token GET /webhooks/{webhook.id}/{webhook.token} | |||||
Modify Webhook with Token PATCH /webhooks/{webhook.id}/{webhook.token} | |||||
Execute Webhook POST /webhooks/{webhook.id}/{webhook.token} | |||||
Execute GitHub-Compatible Webhook POST /webhooks/{webhook.id}/{webhook.token}/github | |||||
Execute Slack-Compatible Webhook POST /webhooks/{webhook.id}/{webhook.token}/slack |
@@ -0,0 +1,24 @@ | |||||
namespace Discord | |||||
{ | |||||
/// <summary> | |||||
/// A Snowflake represents a unique, 64-bit identifier. | |||||
/// </summary> | |||||
public struct Snowflake | |||||
{ | |||||
private readonly ulong _value; | |||||
private Snowflake(ulong value) | |||||
{ | |||||
_value = value; | |||||
} | |||||
public static implicit operator ulong(Snowflake snowflake) | |||||
{ | |||||
return snowflake._value; | |||||
} | |||||
public static implicit operator Snowflake(ulong value) | |||||
{ | |||||
return new Snowflake(value); | |||||
} | |||||
} | |||||
} |
@@ -1,7 +1,7 @@ | |||||
using System.Text.Json; | using System.Text.Json; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Refit; | using Refit; | ||||
using Discord.Rest.Models; | |||||
using Discord.Models; | |||||
using System.Net.Http.Headers; | using System.Net.Http.Headers; | ||||
using System; | using System; | ||||
using System.Net.Http; | using System.Net.Http; | ||||
@@ -32,11 +32,11 @@ namespace Discord.Rest | |||||
}; | }; | ||||
_api = RestService.For<IDiscordRestApi>(_http, refitSettings); | _api = RestService.For<IDiscordRestApi>(_http, refitSettings); | ||||
} | } | ||||
public Task<GatewayInfo> GetGatewayInfoAsync() | public Task<GatewayInfo> GetGatewayInfoAsync() | ||||
{ | |||||
return _api.GetGatewayInfoAsync(); | |||||
} | |||||
=> _api.GetGatewayInfoAsync(); | |||||
public Task<GatewayInfo> GetBotGatewayInfoAsync() | |||||
=> _api.GetBotGatewayInfoAsync(); | |||||
public void Dispose() | public void Dispose() | ||||
{ | { | ||||
@@ -1,13 +1,34 @@ | |||||
using System; | using System; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Refit; | using Refit; | ||||
using Discord.Rest.Models; | |||||
using Discord.Models; | |||||
namespace Discord.Rest | namespace Discord.Rest | ||||
{ | { | ||||
public interface IDiscordRestApi | public interface IDiscordRestApi | ||||
{ | { | ||||
// --- /applications | |||||
// --- /channels | |||||
// --- /gateway | |||||
[Get("/gateway/bot")] | [Get("/gateway/bot")] | ||||
Task<GatewayInfo> GetGatewayInfoAsync(); | Task<GatewayInfo> GetGatewayInfoAsync(); | ||||
[Get("/gateway/bot")] | |||||
Task<GatewayInfo> GetBotGatewayInfoAsync(); | |||||
// --- /guilds | |||||
// --- /invites | |||||
// --- /oauth2 | |||||
// --- /store | |||||
// --- /users | |||||
// --- /voice | |||||
// --- /webhooks | |||||
} | } | ||||
} | } |
@@ -0,0 +1,59 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Text.Json.Serialization; | |||||
namespace Discord.Models | |||||
{ | |||||
public class Channel | |||||
{ | |||||
public const int MinChannelNameLength = 2; | |||||
public const int MaxChannelNameLength = 100; | |||||
public const int MinChannelTopicLength = 0; | |||||
public const int MaxChannelTopicLength = 1024; | |||||
public const int MinUserLimit = 0; | |||||
public const int MaxUserLimit = 100; | |||||
public const int MinBitrate = 8000; | |||||
public const int MaxBitrate = 384000; | |||||
public const int MinRateLimitPerUser = 0; | |||||
public const int MaxRateLimitPerUser = 21600; | |||||
[JsonPropertyName("id")] | |||||
public Snowflake Id { get; set; } | |||||
[JsonPropertyName("type")] | |||||
public ChannelType Type { get; set; } | |||||
[JsonPropertyName("guild_id")] | |||||
public Optional<Snowflake> GuildId { get; set; } | |||||
[JsonPropertyName("position")] | |||||
public Optional<short> Position { get; set; } | |||||
[JsonPropertyName("permission_overwrites")] | |||||
public Optional<Overwrite[]> Overwrites { get; set; } | |||||
[JsonPropertyName("name")] | |||||
public Optional<string> Name { get; set; } | |||||
[JsonPropertyName("topic")] | |||||
public Optional<string?> Topic { get; set; } | |||||
[JsonPropertyName("nsfw")] | |||||
public Optional<bool> Nsfw { get; set; } | |||||
[JsonPropertyName("user_limit")] | |||||
public Optional<short> Bitrate { get; set; } | |||||
[JsonPropertyName("rate_limit_per_user")] | |||||
public Optional<int> RateLimitPerUser { get; set; } | |||||
[JsonPropertyName("recipients")] | |||||
public Optional<User[]> Recipients { get; set; } | |||||
[JsonPropertyName("icon")] | |||||
public Optional<string?> IconId { get; set; } | |||||
[JsonPropertyName("owner_id")] | |||||
public Optional<Snowflake> OwnerId { get; set; } | |||||
[JsonPropertyName("application_id")] | |||||
public Optional<Snowflake> ApplicationId { get; set; } | |||||
[JsonPropertyName("parent_id")] | |||||
public Optional<Snowflake> ParentId { get; set; } | |||||
[JsonPropertyName("last_pin_timestamp")] | |||||
public Optional<DateTimeOffset> LastPinTimestamp { get; set; } | |||||
// omitted: last_message_id | |||||
} | |||||
} |
@@ -0,0 +1,13 @@ | |||||
namespace Discord.Models | |||||
{ | |||||
public enum ChannelType : byte | |||||
{ | |||||
Text = 0, | |||||
Direct = 1, | |||||
Voice = 2, | |||||
Group = 3, | |||||
Category = 4, | |||||
News = 5, | |||||
Store = 6 | |||||
} | |||||
} |
@@ -1,16 +1,16 @@ | |||||
#pragma warning disable CS8618 // Uninitialized NRT expected in models | #pragma warning disable CS8618 // Uninitialized NRT expected in models | ||||
using System.Text.Json.Serialization; | using System.Text.Json.Serialization; | ||||
namespace Discord.Rest.Models | |||||
namespace Discord.Models | |||||
{ | { | ||||
public class GatewayInfo | public class GatewayInfo | ||||
{ | { | ||||
[JsonPropertyName("url")] | [JsonPropertyName("url")] | ||||
public string Url { get; set; } | public string Url { get; set; } | ||||
[JsonPropertyName("shards")] | [JsonPropertyName("shards")] | ||||
public int Shards { get; set; } | |||||
public int? Shards { get; set; } | |||||
[JsonPropertyName("session_start_limit")] | [JsonPropertyName("session_start_limit")] | ||||
public GatewaySessionStartInfo SessionStartInfo { get; set; } | |||||
public GatewaySessionStartInfo? SessionStartInfo { get; set; } | |||||
} | } | ||||
public class GatewaySessionStartInfo | public class GatewaySessionStartInfo | ||||
@@ -0,0 +1,36 @@ | |||||
using System; | |||||
namespace Discord.Models | |||||
{ | |||||
[Flags] | |||||
public enum ChannelPermissions : ulong | |||||
{ | |||||
// General | |||||
CreateInstantInvite = 0x0000_0001, | |||||
ManageChannel = 0x0000_0010, | |||||
AddReactions = 0x0000_0040, | |||||
ViewChannel = 0x0000_0400, | |||||
ManagePermissions = 0x1000_0000, | |||||
ManageWebhooks = 0x2000_0000, | |||||
// Messages | |||||
SendMessages = 0x0000_0800, | |||||
SendTtsMessages = 0x0000_0100, | |||||
ManageMessages = 0x0000_02000, | |||||
EmbedLinks = 0x0000_4000, | |||||
AttachFiles = 0x0000_8000, | |||||
ReadMessageHistory = 0x0001_0000, | |||||
MentionEveryone = 0x0002_0000, | |||||
UseExternalEmoji = 0x0004_0000, | |||||
// Voice | |||||
Connect = 0x0010_0000, | |||||
Speak = 0x0020_0000, | |||||
MuteMembers = 0x0040_0000, | |||||
DeafenMembers = 0x0080_0000, | |||||
MoveMembers = 0x0100_0000, | |||||
UseVoiceActivity = 0x0200_0000, | |||||
PrioritySpeaker = 0x0000_0100, | |||||
Stream = 0x0000_0200, | |||||
} | |||||
} |
@@ -0,0 +1,45 @@ | |||||
using System; | |||||
namespace Discord.Models | |||||
{ | |||||
// todo: doc these when other models exist | |||||
[Flags] | |||||
public enum GuildPermissions : ulong | |||||
{ | |||||
// General | |||||
CreateInstantInvite = 0x0000_0001, | |||||
KickMembers = 0x0000_0002, | |||||
BanMembers = 0x0000_0004, | |||||
Administrator = 0x0000_0008, | |||||
ManageChannels = 0x0000_0010, | |||||
ManageGuild = 0x0000_0020, | |||||
AddReactions = 0x0000_0040, | |||||
ViewAuditLog = 0x0000_0080, | |||||
ViewChannel = 0x0000_0400, | |||||
ChangeNickname = 0x0400_0000, | |||||
ManageNicknames = 0x0800_0000, | |||||
ManageRoles = 0x1000_0000, | |||||
ManageWebhooks = 0x2000_0000, | |||||
ManageEmoji = 0x4000_0000, | |||||
// Messages | |||||
SendMessages = 0x0000_0800, | |||||
SendTtsMessages = 0x0000_0100, | |||||
ManageMessages = 0x0000_02000, | |||||
EmbedLinks = 0x0000_4000, | |||||
AttachFiles = 0x0000_8000, | |||||
ReadMessageHistory = 0x0001_0000, | |||||
MentionEveryone = 0x0002_0000, | |||||
UseExternalEmoji = 0x0004_0000, | |||||
// Voice | |||||
Connect = 0x0010_0000, | |||||
Speak = 0x0020_0000, | |||||
MuteMembers = 0x0040_0000, | |||||
DeafenMembers = 0x0080_0000, | |||||
MoveMembers = 0x0100_0000, | |||||
UseVoiceActivity = 0x0200_0000, | |||||
PrioritySpeaker = 0x0000_0100, | |||||
Stream = 0x0000_0200, | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Text.Json.Serialization; | |||||
namespace Discord.Models | |||||
{ | |||||
public class Overwrite | |||||
{ | |||||
[JsonPropertyName("id")] | |||||
public Snowflake Id { get; set; } | |||||
[JsonPropertyName("type")] | |||||
[JsonConverter(typeof(JsonStringEnumConverter))] | |||||
public PermissionTarget TargetType { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,8 @@ | |||||
namespace Discord.Models | |||||
{ | |||||
public enum PermissionTarget | |||||
{ | |||||
Member, | |||||
Role | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
using System; | |||||
namespace Discord.Models | |||||
{ | |||||
[Flags] | |||||
public enum AccountFlags : short | |||||
{ | |||||
None = 0, | |||||
Employee = 1<<0, | |||||
Partner = 1<<1, | |||||
HypesquadEvents = 1<<2, | |||||
BugHunter = 1<<3, | |||||
HypesquadBravery = 1<<6, | |||||
HypesquadBrilliance = 1<<7, | |||||
HypesquadBalance = 1<<8, | |||||
EarlySupporter = 1<<9, | |||||
TeamUser = 1<<10, | |||||
System = 1<<12, | |||||
} | |||||
} |
@@ -0,0 +1,8 @@ | |||||
namespace Discord.Models | |||||
{ | |||||
public enum PremiumType : byte | |||||
{ | |||||
Classic = 1, | |||||
Nitro = 2 | |||||
} | |||||
} |
@@ -0,0 +1,33 @@ | |||||
#pragma warning disable CS8618 // Uninitialized NRT expected in models <username> | |||||
using System.Text.Json.Serialization; | |||||
namespace Discord.Models | |||||
{ | |||||
public class User | |||||
{ | |||||
[JsonPropertyName("id")] | |||||
public Snowflake Id { get; set; } | |||||
[JsonPropertyName("username")] | |||||
public string Username { get; set; } | |||||
[JsonPropertyName("discriminator")] | |||||
public ushort Discriminator { get; set; } | |||||
[JsonPropertyName("avatar")] | |||||
public string? AvatarId { get; set; } | |||||
[JsonPropertyName("bot")] | |||||
public Optional<bool> Bot { get; set; } | |||||
[JsonPropertyName("system")] | |||||
public Optional<bool> System { get; set; } | |||||
[JsonPropertyName("mfa_enabled")] | |||||
public Optional<bool> MfaEnabled { get; set; } | |||||
[JsonPropertyName("locale")] | |||||
public Optional<string> Locale { get; set; } | |||||
[JsonPropertyName("verified")] | |||||
public Optional<bool> Verified { get; set; } | |||||
[JsonPropertyName("email")] | |||||
public Optional<string> Email { get; set; } | |||||
[JsonPropertyName("flags")] | |||||
public Optional<AccountFlags> Flags { get; set; } | |||||
[JsonPropertyName("premium_type")] | |||||
public Optional<PremiumType> PremiumType { get; set; } | |||||
} | |||||
} |
@@ -8,7 +8,7 @@ using Refit; | |||||
// https://blog.martincostello.com/refit-and-system-text-json/ | // https://blog.martincostello.com/refit-and-system-text-json/ | ||||
namespace Discord.Rest | |||||
namespace Discord | |||||
{ | { | ||||
public class JsonContentSerializer : IContentSerializer | public class JsonContentSerializer : IContentSerializer | ||||
{ | { |
@@ -0,0 +1,22 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Text.Json; | |||||
using System.Text.Json.Serialization; | |||||
namespace Discord.Serialization | |||||
{ | |||||
// 😅 | |||||
public class OptionalConverter<T> : JsonConverter<Optional<T>> | |||||
{ | |||||
public override Optional<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | |||||
{ | |||||
throw new NotImplementedException(); | |||||
} | |||||
public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options) | |||||
{ | |||||
throw new NotImplementedException(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,12 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
// todo: impl | |||||
namespace Discord | |||||
{ | |||||
public struct Optional<T> | |||||
{ | |||||
public bool IsSpecified { get; private set; } | |||||
public T Value { get; set; } | |||||
} | |||||
} |