| @@ -2,6 +2,7 @@ | |||
| namespace Discord.Commands | |||
| { | |||
| //TODO: Check support for escaping | |||
| public static class CommandParser | |||
| { | |||
| private enum CommandParserPart | |||
| @@ -35,6 +35,17 @@ | |||
| <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | |||
| <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'FullDebug|AnyCPU'"> | |||
| <DebugSymbols>true</DebugSymbols> | |||
| <OutputPath>bin\FullDebug\</OutputPath> | |||
| <DefineConstants>TRACE;DEBUG;NET45,TEST_RESPONSES</DefineConstants> | |||
| <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
| <WarningLevel>2</WarningLevel> | |||
| <DebugType>full</DebugType> | |||
| <PlatformTarget>AnyCPU</PlatformTarget> | |||
| <ErrorReport>prompt</ErrorReport> | |||
| <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | |||
| <HintPath>..\..\..\DiscordBot\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> | |||
| @@ -57,6 +68,33 @@ | |||
| <None Include="packages.config" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <Compile Include="..\Discord.Net\API\Common.cs"> | |||
| <Link>API\Common.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\API\Endpoints.cs"> | |||
| <Link>API\Endpoints.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\API\HttpException.cs"> | |||
| <Link>API\HttpException.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\API\Requests.cs"> | |||
| <Link>API\Requests.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\API\Responses.cs"> | |||
| <Link>API\Responses.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\API\RestClient.BuiltIn.cs"> | |||
| <Link>API\RestClient.BuiltIn.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\API\RestClient.cs"> | |||
| <Link>API\RestClient.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\API\RestClient.Events.cs"> | |||
| <Link>API\RestClient.Events.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\API\RestClient.SharpRest.cs"> | |||
| <Link>API\RestClient.SharpRest.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Audio\Opus.cs"> | |||
| <Link>Audio\Opus.cs</Link> | |||
| </Compile> | |||
| @@ -84,6 +122,9 @@ | |||
| <Compile Include="..\Discord.Net\Collections\Users.cs"> | |||
| <Link>Collections\Users.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\DiscordAPIClient.cs"> | |||
| <Link>DiscordAPIClient.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\DiscordClient.API.cs"> | |||
| <Link>DiscordClient.API.cs</Link> | |||
| </Compile> | |||
| @@ -150,74 +191,44 @@ | |||
| <Compile Include="..\Discord.Net\Models\User.cs"> | |||
| <Link>Models\User.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\Common.cs"> | |||
| <Link>Net\API\Common.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\DiscordAPIClient.cs"> | |||
| <Link>Net\API\DiscordAPIClient.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\Endpoints.cs"> | |||
| <Link>Net\API\Endpoints.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\HttpException.cs"> | |||
| <Link>Net\API\HttpException.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\Requests.cs"> | |||
| <Link>Net\API\Requests.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\Responses.cs"> | |||
| <Link>Net\API\Responses.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\RestClient.BuiltIn.cs"> | |||
| <Link>Net\API\RestClient.BuiltIn.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\RestClient.cs"> | |||
| <Link>Net\API\RestClient.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\RestClient.Events.cs"> | |||
| <Link>Net\API\RestClient.Events.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\API\RestClient.SharpRest.cs"> | |||
| <Link>Net\API\RestClient.SharpRest.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\Commands.cs"> | |||
| <Link>Net\WebSockets\Commands.cs</Link> | |||
| <Compile Include="..\Discord.Net\TimeoutException.cs"> | |||
| <Link>TimeoutException.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\DataWebSocket.cs"> | |||
| <Link>Net\WebSockets\DataWebSocket.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\Data\Commands.cs"> | |||
| <Link>WebSockets\Data\Commands.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\DataWebSockets.Events.cs"> | |||
| <Link>Net\WebSockets\DataWebSockets.Events.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\Data\DataWebSocket.cs"> | |||
| <Link>WebSockets\Data\DataWebSocket.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\Events.cs"> | |||
| <Link>Net\WebSockets\Events.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\Data\DataWebSockets.Events.cs"> | |||
| <Link>WebSockets\Data\DataWebSockets.Events.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\VoiceCommands.cs"> | |||
| <Link>Net\WebSockets\VoiceCommands.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\Data\Events.cs"> | |||
| <Link>WebSockets\Data\Events.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\VoiceEvents.cs"> | |||
| <Link>Net\WebSockets\VoiceEvents.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\Voice\Commands.cs"> | |||
| <Link>WebSockets\Voice\Commands.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\VoiceWebSocket.cs"> | |||
| <Link>Net\WebSockets\VoiceWebSocket.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\Voice\Events.cs"> | |||
| <Link>WebSockets\Voice\Events.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\VoiceWebSocket.Events.cs"> | |||
| <Link>Net\WebSockets\VoiceWebSocket.Events.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\Voice\VoiceWebSocket.cs"> | |||
| <Link>WebSockets\Voice\VoiceWebSocket.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\WebSocket.BuiltIn.cs"> | |||
| <Link>Net\WebSockets\WebSocket.BuiltIn.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\Voice\VoiceWebSocket.Events.cs"> | |||
| <Link>WebSockets\Voice\VoiceWebSocket.Events.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\WebSocket.cs"> | |||
| <Link>Net\WebSockets\WebSocket.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\WebSocket.BuiltIn.cs"> | |||
| <Link>WebSockets\WebSocket.BuiltIn.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\WebSocket.Events.cs"> | |||
| <Link>Net\WebSockets\WebSocket.Events.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\WebSocket.cs"> | |||
| <Link>WebSockets\WebSocket.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\Net\WebSockets\WebSocketMessage.cs"> | |||
| <Link>Net\WebSockets\WebSocketMessage.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\WebSocket.Events.cs"> | |||
| <Link>WebSockets\WebSocket.Events.cs</Link> | |||
| </Compile> | |||
| <Compile Include="..\Discord.Net\TimeoutException.cs"> | |||
| <Link>TimeoutException.cs</Link> | |||
| <Compile Include="..\Discord.Net\WebSockets\WebSocketMessage.cs"> | |||
| <Link>WebSockets\WebSocketMessage.cs</Link> | |||
| </Compile> | |||
| <Compile Include="Properties\AssemblyInfo.cs" /> | |||
| </ItemGroup> | |||
| @@ -0,0 +1,295 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| namespace Discord.API | |||
| { | |||
| //User | |||
| public class UserReference | |||
| { | |||
| [JsonProperty("username")] | |||
| public string Username; | |||
| [JsonProperty("id")] | |||
| public string Id; | |||
| [JsonProperty("discriminator")] | |||
| public string Discriminator; | |||
| [JsonProperty("avatar")] | |||
| public string Avatar; | |||
| } | |||
| public class SelfUserInfo : UserReference | |||
| { | |||
| [JsonProperty("email")] | |||
| public string Email; | |||
| [JsonProperty("verified")] | |||
| public bool IsVerified; | |||
| } | |||
| //Members | |||
| public class MemberReference | |||
| { | |||
| [JsonProperty("user_id")] | |||
| public string UserId; | |||
| [JsonProperty("user")] | |||
| public UserReference User; | |||
| [JsonProperty("guild_id")] | |||
| public string GuildId; | |||
| } | |||
| public class MemberInfo : MemberReference | |||
| { | |||
| [JsonProperty("joined_at")] | |||
| public DateTime? JoinedAt; | |||
| [JsonProperty("roles")] | |||
| public string[] Roles; | |||
| } | |||
| public class ExtendedMemberInfo : MemberInfo | |||
| { | |||
| [JsonProperty("mute")] | |||
| public bool IsMuted; | |||
| [JsonProperty("deaf")] | |||
| public bool IsDeafened; | |||
| } | |||
| public class PresenceMemberInfo : MemberReference | |||
| { | |||
| [JsonProperty("game_id")] | |||
| public string GameId; | |||
| [JsonProperty("status")] | |||
| public string Status; | |||
| } | |||
| public class VoiceMemberInfo : MemberReference | |||
| { | |||
| [JsonProperty("channel_id")] | |||
| public string ChannelId; | |||
| [JsonProperty("suppress")] | |||
| public bool? IsSuppressed; | |||
| [JsonProperty("session_id")] | |||
| public string SessionId; | |||
| [JsonProperty("self_mute")] | |||
| public bool? IsSelfMuted; | |||
| [JsonProperty("self_deaf")] | |||
| public bool? IsSelfDeafened; | |||
| [JsonProperty("mute")] | |||
| public bool IsMuted; | |||
| [JsonProperty("deaf")] | |||
| public bool IsDeafened; | |||
| [JsonProperty("token")] | |||
| public string Token; | |||
| } | |||
| //Channels | |||
| public class ChannelReference | |||
| { | |||
| [JsonProperty("id")] | |||
| public string Id; | |||
| [JsonProperty("guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty("name")] | |||
| public string Name; | |||
| [JsonProperty("type")] | |||
| public string Type; | |||
| } | |||
| public class ChannelInfo : ChannelReference | |||
| { | |||
| public sealed class PermissionOverwrite | |||
| { | |||
| [JsonProperty("type")] | |||
| public string Type; | |||
| [JsonProperty("id")] | |||
| public string Id; | |||
| [JsonProperty("deny")] | |||
| public uint Deny; | |||
| [JsonProperty("allow")] | |||
| public uint Allow; | |||
| } | |||
| [JsonProperty("last_message_id")] | |||
| public string LastMessageId; | |||
| [JsonProperty("is_private")] | |||
| public bool IsPrivate; | |||
| [JsonProperty("position")] | |||
| public int Position; | |||
| [JsonProperty("permission_overwrites")] | |||
| public PermissionOverwrite[] PermissionOverwrites; | |||
| [JsonProperty("recipient")] | |||
| public UserReference Recipient; | |||
| } | |||
| //Guilds (Servers) | |||
| public class GuildReference | |||
| { | |||
| [JsonProperty("id")] | |||
| public string Id; | |||
| [JsonProperty("name")] | |||
| public string Name; | |||
| } | |||
| public class GuildInfo : GuildReference | |||
| { | |||
| [JsonProperty("afk_channel_id")] | |||
| public string AFKChannelId; | |||
| [JsonProperty("afk_timeout")] | |||
| public int AFKTimeout; | |||
| [JsonProperty("embed_channel_id")] | |||
| public string EmbedChannelId; | |||
| [JsonProperty("embed_enabled")] | |||
| public bool EmbedEnabled; | |||
| [JsonProperty("icon")] | |||
| public string Icon; | |||
| [JsonProperty("joined_at")] | |||
| public DateTime? JoinedAt; | |||
| [JsonProperty("owner_id")] | |||
| public string OwnerId; | |||
| [JsonProperty("region")] | |||
| public string Region; | |||
| [JsonProperty("roles")] | |||
| public RoleInfo[] Roles; | |||
| } | |||
| public class ExtendedGuildInfo : GuildInfo | |||
| { | |||
| [JsonProperty("channels")] | |||
| public ChannelInfo[] Channels; | |||
| [JsonProperty("members")] | |||
| public ExtendedMemberInfo[] Members; | |||
| [JsonProperty("presences")] | |||
| public PresenceMemberInfo[] Presences; | |||
| [JsonProperty("voice_states")] | |||
| public VoiceMemberInfo[] VoiceStates; | |||
| } | |||
| //Messages | |||
| public class MessageReference | |||
| { | |||
| [JsonProperty("id")] | |||
| public string Id; | |||
| [JsonProperty("channel_id")] | |||
| public string ChannelId; | |||
| [JsonProperty("message_id")] | |||
| public string MessageId { get { return Id; } set { Id = value; } } | |||
| } | |||
| public class Message : MessageReference | |||
| { | |||
| public sealed class Attachment | |||
| { | |||
| [JsonProperty("id")] | |||
| public string Id; | |||
| [JsonProperty("url")] | |||
| public string Url; | |||
| [JsonProperty("proxy_url")] | |||
| public string ProxyUrl; | |||
| [JsonProperty("size")] | |||
| public int Size; | |||
| [JsonProperty("filename")] | |||
| public string Filename; | |||
| [JsonProperty("width")] | |||
| public int Width; | |||
| [JsonProperty("height")] | |||
| public int Height; | |||
| } | |||
| public sealed class Embed | |||
| { | |||
| public sealed class Reference | |||
| { | |||
| [JsonProperty("url")] | |||
| public string Url; | |||
| [JsonProperty("name")] | |||
| public string Name; | |||
| } | |||
| public sealed class ThumbnailInfo | |||
| { | |||
| [JsonProperty("url")] | |||
| public string Url; | |||
| [JsonProperty("proxy_url")] | |||
| public string ProxyUrl; | |||
| [JsonProperty("width")] | |||
| public int Width; | |||
| [JsonProperty("height")] | |||
| public int Height; | |||
| } | |||
| [JsonProperty("url")] | |||
| public string Url; | |||
| [JsonProperty("type")] | |||
| public string Type; | |||
| [JsonProperty("title")] | |||
| public string Title; | |||
| [JsonProperty("description")] | |||
| public string Description; | |||
| [JsonProperty("author")] | |||
| public Reference Author; | |||
| [JsonProperty("provider")] | |||
| public Reference Provider; | |||
| [JsonProperty("thumbnail")] | |||
| public ThumbnailInfo Thumbnail; | |||
| } | |||
| [JsonProperty("tts")] | |||
| public bool IsTextToSpeech; | |||
| [JsonProperty("mention_everyone")] | |||
| public bool IsMentioningEveryone; | |||
| [JsonProperty("timestamp")] | |||
| public DateTime Timestamp; | |||
| [JsonProperty("edited_timestamp")] | |||
| public DateTime? EditedTimestamp; | |||
| [JsonProperty("mentions")] | |||
| public UserReference[] Mentions; | |||
| [JsonProperty("embeds")] | |||
| public Embed[] Embeds; //TODO: Parse this | |||
| [JsonProperty("attachments")] | |||
| public Attachment[] Attachments; | |||
| [JsonProperty("content")] | |||
| public string Content; | |||
| [JsonProperty("author")] | |||
| public UserReference Author; | |||
| [JsonProperty("nonce")] | |||
| public string Nonce; | |||
| } | |||
| //Roles | |||
| public class RoleReference | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty("role_id")] | |||
| public string RoleId; | |||
| } | |||
| public class RoleInfo | |||
| { | |||
| [JsonProperty("permissions")] | |||
| public int Permissions; | |||
| [JsonProperty("name")] | |||
| public string Name; | |||
| [JsonProperty("id")] | |||
| public string Id; | |||
| } | |||
| //Invites | |||
| public class Invite | |||
| { | |||
| [JsonProperty("inviter")] | |||
| public UserReference Inviter; | |||
| [JsonProperty("guild")] | |||
| public GuildReference Guild; | |||
| [JsonProperty("channel")] | |||
| public ChannelReference Channel; | |||
| [JsonProperty("code")] | |||
| public string Code; | |||
| [JsonProperty("xkcdpass")] | |||
| public string XkcdPass; | |||
| } | |||
| public class ExtendedInvite : Invite | |||
| { | |||
| [JsonProperty("max_age")] | |||
| public int MaxAge; | |||
| [JsonProperty("max_uses")] | |||
| public int MaxUses; | |||
| [JsonProperty("revoked")] | |||
| public bool IsRevoked; | |||
| [JsonProperty("temporary")] | |||
| public bool IsTemporary; | |||
| [JsonProperty("uses")] | |||
| public int Uses; | |||
| [JsonProperty("created_at")] | |||
| public DateTime CreatedAt; | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| namespace Discord.Net.API | |||
| namespace Discord.API | |||
| { | |||
| internal static class Endpoints | |||
| { | |||
| @@ -45,5 +45,6 @@ | |||
| public const string StatusActiveMaintenance = "scheduled-maintenances/active.json"; | |||
| public const string StatusUnresolvedMaintenance = "scheduled-maintenances/unresolved.json"; | |||
| public const string StatusUpcomingMaintenance = "scheduled-maintenances/upcoming.json"; | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| using System; | |||
| using System.Net; | |||
| namespace Discord.Net.API | |||
| namespace Discord.API | |||
| { | |||
| public class HttpException : Exception | |||
| { | |||
| @@ -0,0 +1,142 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| namespace Discord.API | |||
| { | |||
| //Auth | |||
| internal sealed class RegisterRequest | |||
| { | |||
| [JsonProperty("fingerprint")] | |||
| public string Fingerprint; | |||
| [JsonProperty("username")] | |||
| public string Username; | |||
| } | |||
| internal sealed class LoginRequest | |||
| { | |||
| [JsonProperty("email")] | |||
| public string Email; | |||
| [JsonProperty("password")] | |||
| public string Password; | |||
| } | |||
| //Channels | |||
| internal sealed class CreateChannelRequest | |||
| { | |||
| [JsonProperty("name")] | |||
| public string Name; | |||
| [JsonProperty("type")] | |||
| public string Type; | |||
| } | |||
| internal sealed class CreatePMChannelRequest | |||
| { | |||
| [JsonProperty("recipient_id")] | |||
| public string RecipientId; | |||
| } | |||
| internal sealed class EditChannelRequest | |||
| { | |||
| [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Name; | |||
| [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Topic; | |||
| } | |||
| //Invites | |||
| internal sealed class CreateInviteRequest | |||
| { | |||
| [JsonProperty("max_age")] | |||
| public int MaxAge; | |||
| [JsonProperty("max_uses")] | |||
| public int MaxUses; | |||
| [JsonProperty("temporary")] | |||
| public bool IsTemporary; | |||
| [JsonProperty("xkcdpass")] | |||
| public bool WithXkcdPass; | |||
| } | |||
| //Members | |||
| internal sealed class EditMemberRequest | |||
| { | |||
| [JsonProperty(PropertyName = "mute", NullValueHandling = NullValueHandling.Ignore)] | |||
| public bool? Mute; | |||
| [JsonProperty(PropertyName = "deaf", NullValueHandling = NullValueHandling.Ignore)] | |||
| public bool? Deaf; | |||
| [JsonProperty(PropertyName = "roles", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string[] Roles; | |||
| } | |||
| //Messages | |||
| internal sealed class SendMessageRequest | |||
| { | |||
| [JsonProperty("content")] | |||
| public string Content; | |||
| [JsonProperty("mentions")] | |||
| public string[] Mentions; | |||
| [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Nonce; | |||
| [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)] | |||
| public bool IsTTS; | |||
| } | |||
| internal sealed class EditMessageRequest | |||
| { | |||
| [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Content; | |||
| [JsonProperty("mentions", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string[] Mentions; | |||
| } | |||
| //Permissions | |||
| internal sealed class SetChannelPermissionsRequest //Both creates and modifies | |||
| { | |||
| [JsonProperty("id")] | |||
| public string Id; | |||
| [JsonProperty("type")] | |||
| public string Type; | |||
| [JsonProperty("allow")] | |||
| public uint Allow; | |||
| [JsonProperty("deny")] | |||
| public uint Deny; | |||
| } | |||
| //Profile | |||
| internal sealed class EditProfileRequest | |||
| { | |||
| [JsonProperty(PropertyName = "password")] | |||
| public string CurrentPassword; | |||
| [JsonProperty(PropertyName = "email", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Email; | |||
| [JsonProperty(PropertyName = "new_password", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Password; | |||
| [JsonProperty(PropertyName = "username", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Username; | |||
| [JsonProperty(PropertyName = "avatar", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Avatar; | |||
| } | |||
| //Roles | |||
| internal sealed class EditRoleRequest | |||
| { | |||
| [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Name; | |||
| [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)] | |||
| public uint? Permissions; | |||
| } | |||
| //Servers | |||
| internal sealed class CreateServerRequest | |||
| { | |||
| [JsonProperty("name")] | |||
| public string Name; | |||
| [JsonProperty("region")] | |||
| public string Region; | |||
| } | |||
| internal sealed class EditServerRequest | |||
| { | |||
| [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Name; | |||
| [JsonProperty("region", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Region; | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| using System; | |||
| using System.Net.Http; | |||
| namespace Discord.Net.API | |||
| namespace Discord.API | |||
| { | |||
| internal partial class RestClient | |||
| { | |||
| @@ -6,7 +6,7 @@ using System.Net.Http; | |||
| using System.Threading; | |||
| using System.Threading.Tasks; | |||
| namespace Discord.Net.API | |||
| namespace Discord.API | |||
| { | |||
| internal class RestSharpRestEngine : IRestEngine | |||
| { | |||
| @@ -6,7 +6,7 @@ using System.Reflection; | |||
| using System.Threading; | |||
| using System.Threading.Tasks; | |||
| namespace Discord.Net.API | |||
| namespace Discord.API | |||
| { | |||
| internal interface IRestEngine | |||
| { | |||
| @@ -1,6 +1,5 @@ | |||
| using Discord.Helpers; | |||
| using Discord.Net; | |||
| using Discord.Net.API; | |||
| using Discord.API; | |||
| using Discord.Helpers; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| @@ -17,34 +16,54 @@ namespace Discord | |||
| public partial class DiscordClient | |||
| { | |||
| //Servers | |||
| /// <summary> Creates a new server with the provided name and region (see Regions). </summary> | |||
| public async Task<Server> CreateServer(string name, string region) | |||
| public const int MaxMessageSize = 2000; | |||
| //Bans | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(Member member) | |||
| => Ban(member?.ServerId, member?.UserId); | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(Server server, User user) | |||
| => Ban(server?.Id, user?.Id); | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(Server server, string userId) | |||
| => Ban(server?.Id, userId); | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(string server, User user) | |||
| => Ban(server, user?.Id); | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(string serverId, string userId) | |||
| { | |||
| CheckReady(); | |||
| if (name == null) throw new ArgumentNullException(nameof(name)); | |||
| if (region == null) throw new ArgumentNullException(nameof(region)); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| var response = await _api.CreateServer(name, region).ConfigureAwait(false); | |||
| var server = _servers.GetOrAdd(response.Id); | |||
| server.Update(response); | |||
| return server; | |||
| return _api.Ban(serverId, userId); | |||
| } | |||
| /// <summary> Leaves the provided server, destroying it if you are the owner. </summary> | |||
| public Task<Server> LeaveServer(Server server) | |||
| => LeaveServer(server?.Id); | |||
| /// <summary> Leaves the provided server, destroying it if you are the owner. </summary> | |||
| public async Task<Server> LeaveServer(string serverId) | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public Task Unban(Member member) | |||
| => Unban(member?.ServerId, member?.UserId); | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public Task Unban(Server server, User user) | |||
| => Unban(server?.Id, user?.Id); | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public Task Unban(Server server, string userId) | |||
| => Unban(server?.Id, userId); | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public Task Unban(string server, User user) | |||
| => Unban(server, user?.Id); | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public async Task Unban(string serverId, string userId) | |||
| { | |||
| CheckReady(); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| try { await _api.LeaveServer(serverId).ConfigureAwait(false); } | |||
| try { await _api.Unban(serverId, userId).ConfigureAwait(false); } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| return _servers.TryRemove(serverId); | |||
| } | |||
| //Channels | |||
| /// <summary> Creates a new channel with the provided name and type (see ChannelTypes). </summary> | |||
| public Task<Channel> CreateChannel(Server server, string name, string type) | |||
| @@ -62,6 +81,7 @@ namespace Discord | |||
| channel.Update(response); | |||
| return channel; | |||
| } | |||
| /// <summary> Returns the private channel with the provided user, creating one if it does not currently exist. </summary> | |||
| public Task<Channel> CreatePMChannel(string userId) => CreatePMChannel(_users[userId], userId); | |||
| /// <summary> Returns the private channel with the provided user, creating one if it does not currently exist. </summary> | |||
| @@ -70,7 +90,7 @@ namespace Discord | |||
| public Task<Channel> CreatePMChannel(Member member) => CreatePMChannel(member.User, member.UserId); | |||
| private async Task<Channel> CreatePMChannel(User user, string userId) | |||
| { | |||
| CheckReady(); | |||
| CheckReady(); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| Channel channel = null; | |||
| @@ -85,6 +105,19 @@ namespace Discord | |||
| return channel; | |||
| } | |||
| /// <summary> Edits the provided channel, changing only non-null attributes. </summary> | |||
| public Task EditChannel(Channel channel) | |||
| => EditChannel(channel?.Id); | |||
| /// <summary> Edits the provided channel, changing only non-null attributes. </summary> | |||
| public Task EditChannel(string channelId, string name = null, string topic = null) | |||
| { | |||
| CheckReady(); | |||
| if (channelId == null) throw new ArgumentNullException(nameof(channelId)); | |||
| if (topic == null) throw new ArgumentNullException(nameof(topic)); | |||
| return _api.EditChannel(channelId, name: name, topic: topic); | |||
| } | |||
| /// <summary> Destroys the provided channel. </summary> | |||
| public Task<Channel> DestroyChannel(Channel channel) | |||
| => DestroyChannel(channel?.Id); | |||
| @@ -99,52 +132,6 @@ namespace Discord | |||
| return _channels.TryRemove(channelId); | |||
| } | |||
| //Bans | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(Member member) | |||
| => Ban(member?.ServerId, member?.UserId); | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(Server server, User user) | |||
| => Ban(server?.Id, user?.Id); | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(Server server, string userId) | |||
| => Ban(server?.Id, userId); | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(string server, User user) | |||
| => Ban(server, user?.Id); | |||
| /// <summary> Bans a user from the provided server. </summary> | |||
| public Task Ban(string serverId, string userId) | |||
| { | |||
| CheckReady(); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| return _api.Ban(serverId, userId); | |||
| } | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public Task Unban(Member member) | |||
| => Unban(member?.ServerId, member?.UserId); | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public Task Unban(Server server, User user) | |||
| => Unban(server?.Id, user?.Id); | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public Task Unban(Server server, string userId) | |||
| => Unban(server?.Id, userId); | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public Task Unban(string server, User user) | |||
| => Unban(server, user?.Id); | |||
| /// <summary> Unbans a user from the provided server. </summary> | |||
| public async Task Unban(string serverId, string userId) | |||
| { | |||
| CheckReady(); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| try { await _api.Unban(serverId, userId).ConfigureAwait(false); } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| } | |||
| //Invites | |||
| /// <summary> Creates a new invite to the default channel of the provided server. </summary> | |||
| /// <param name="maxAge"> Time (in seconds) until the invite expires. Set to 0 to never expire. </param> | |||
| @@ -178,14 +165,29 @@ namespace Discord | |||
| return invite; | |||
| } | |||
| /// <summary> Deletes the provided invite. </summary> | |||
| public async Task DestroyInvite(string inviteId) | |||
| { | |||
| CheckReady(); | |||
| if (inviteId == null) throw new ArgumentNullException(nameof(inviteId)); | |||
| try | |||
| { | |||
| //Check if this is a human-readable link and get its ID | |||
| var response = await _api.GetInvite(inviteId).ConfigureAwait(false); | |||
| await _api.DeleteInvite(response.Code).ConfigureAwait(false); | |||
| } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| } | |||
| /// <summary> Gets more info about the provided invite code. </summary> | |||
| /// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks> | |||
| public async Task<Invite> GetInvite(string id) | |||
| public async Task<Invite> GetInvite(string inviteIdOrXkcd) | |||
| { | |||
| CheckReady(); | |||
| if (id == null) throw new ArgumentNullException(nameof(id)); | |||
| if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd)); | |||
| var response = await _api.GetInvite(id).ConfigureAwait(false); | |||
| var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false); | |||
| var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id); | |||
| invite.Update(response); | |||
| return invite; | |||
| @@ -200,39 +202,42 @@ namespace Discord | |||
| return _api.AcceptInvite(invite.Id); | |||
| } | |||
| /// <summary> Accepts the provided invite. </summary> | |||
| public async Task AcceptInvite(string code) | |||
| public async Task AcceptInvite(string inviteId) | |||
| { | |||
| CheckReady(); | |||
| if (code == null) throw new ArgumentNullException(nameof(code)); | |||
| if (inviteId == null) throw new ArgumentNullException(nameof(inviteId)); | |||
| //Remove trailing slash and any non-code url parts | |||
| if (code.Length > 0 && code[code.Length - 1] == '/') | |||
| code = code.Substring(0, code.Length - 1); | |||
| int index = code.LastIndexOf('/'); | |||
| if (inviteId.Length > 0 && inviteId[inviteId.Length - 1] == '/') | |||
| inviteId = inviteId.Substring(0, inviteId.Length - 1); | |||
| int index = inviteId.LastIndexOf('/'); | |||
| if (index >= 0) | |||
| code = code.Substring(index + 1); | |||
| inviteId = inviteId.Substring(index + 1); | |||
| //Check if this is a human-readable link and get its ID | |||
| var invite = await GetInvite(code).ConfigureAwait(false); | |||
| var invite = await GetInvite(inviteId).ConfigureAwait(false); | |||
| await _api.AcceptInvite(invite.Id).ConfigureAwait(false); | |||
| } | |||
| /// <summary> Deletes the provided invite. </summary> | |||
| public async Task DeleteInvite(string code) | |||
| //Members | |||
| public Task EditMember(Member member, bool? mute = null, bool? deaf = null, string[] roles = null) | |||
| => EditMember(member?.ServerId, member?.UserId, mute, deaf, roles); | |||
| public Task EditMember(Server server, User user, bool? mute = null, bool? deaf = null, string[] roles = null) | |||
| => EditMember(server?.Id, user?.Id, mute, deaf, roles); | |||
| public Task EditMember(Server server, string userId, bool? mute = null, bool? deaf = null, string[] roles = null) | |||
| => EditMember(server?.Id, userId, mute, deaf, roles); | |||
| public Task EditMember(string serverId, User user, bool? mute = null, bool? deaf = null, string[] roles = null) | |||
| => EditMember(serverId, user?.Id, mute, deaf, roles); | |||
| public Task EditMember(string serverId, string userId, bool? mute = null, bool? deaf = null, string[] roles = null) | |||
| { | |||
| CheckReady(); | |||
| if (code == null) throw new ArgumentNullException(nameof(code)); | |||
| if (serverId == null) throw new NullReferenceException(nameof(serverId)); | |||
| if (userId == null) throw new NullReferenceException(nameof(userId)); | |||
| try | |||
| { | |||
| //Check if this is a human-readable link and get its ID | |||
| var response = await _api.GetInvite(code).ConfigureAwait(false); | |||
| await _api.DeleteInvite(response.Code).ConfigureAwait(false); | |||
| } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| return _api.EditMember(serverId, userId, mute, deaf, roles); | |||
| } | |||
| //Chat | |||
| //Messages | |||
| /// <summary> Sends a message to the provided channel. </summary> | |||
| public Task<Message[]> SendMessage(Channel channel, string text) | |||
| => SendMessage(channel?.Id, text, new string[0]); | |||
| @@ -252,18 +257,18 @@ namespace Discord | |||
| if (text == null) throw new ArgumentNullException(nameof(text)); | |||
| if (mentions == null) throw new ArgumentNullException(nameof(mentions)); | |||
| int blockCount = (int)Math.Ceiling(text.Length / (double)DiscordAPIClient.MaxMessageSize); | |||
| int blockCount = (int)Math.Ceiling(text.Length / (double)MaxMessageSize); | |||
| Message[] result = new Message[blockCount]; | |||
| for (int i = 0; i < blockCount; i++) | |||
| { | |||
| int index = i * DiscordAPIClient.MaxMessageSize; | |||
| int index = i * MaxMessageSize; | |||
| string blockText = text.Substring(index, Math.Min(2000, text.Length - index)); | |||
| var nonce = GenerateNonce(); | |||
| if (_config.UseMessageQueue) | |||
| { | |||
| var msg = _messages.GetOrAdd("nonce_" + nonce, channel.Id, _currentUserId); | |||
| var currentMember = _members[msg.UserId, channel.ServerId]; | |||
| msg.Update(new Net.API.Message | |||
| msg.Update(new API.Message | |||
| { | |||
| Content = blockText, | |||
| Timestamp = DateTime.UtcNow, | |||
| @@ -302,35 +307,37 @@ namespace Discord | |||
| return await SendMessage(channel, text, new string[0]).ConfigureAwait(false); | |||
| } | |||
| /// <summary> Edits a message the provided message. </summary> | |||
| public Task EditMessage(Message message, string text) | |||
| => EditMessage(message?.ChannelId, message?.Id, text, new string[0]); | |||
| /// <summary> Edits a message the provided message. </summary> | |||
| public Task EditMessage(Channel channel, string messageId, string text) | |||
| => EditMessage(channel?.Id, messageId, text, new string[0]); | |||
| /// <summary> Edits a message the provided message. </summary> | |||
| public Task EditMessage(string channelId, string messageId, string text) | |||
| => EditMessage(channelId, messageId, text, new string[0]); | |||
| /// <summary> Edits a message the provided message, mentioning certain users. </summary> | |||
| /// <summary> Sends a file to the provided channel. </summary> | |||
| public Task SendFile(Channel channel, string filePath) | |||
| => SendFile(channel?.Id, filePath); | |||
| /// <summary> Sends a file to the provided channel. </summary> | |||
| public Task SendFile(string channelId, string filePath) | |||
| { | |||
| CheckReady(); | |||
| if (channelId == null) throw new ArgumentNullException(nameof(channelId)); | |||
| if (filePath == null) throw new ArgumentNullException(nameof(filePath)); | |||
| return _api.SendFile(channelId, filePath); | |||
| } | |||
| /// <summary> Edits the provided message, changing only non-null attributes. </summary> | |||
| /// <remarks> While not required, it is recommended to include a mention reference in the text (see Mention.User). </remarks> | |||
| public Task EditMessage(Message message, string text, string[] mentions) | |||
| public Task EditMessage(Message message, string text = null, string[] mentions = null) | |||
| => EditMessage(message?.ChannelId, message?.Id, text, mentions); | |||
| /// <summary> Edits a message the provided message, mentioning certain users. </summary> | |||
| /// <summary> Edits the provided message, changing only non-null attributes. </summary> | |||
| /// <remarks> While not required, it is recommended to include a mention reference in the text (see Mention.User). </remarks> | |||
| public Task EditMessage(Channel channel, string messageId, string text, string[] mentions) | |||
| public Task EditMessage(Channel channel, string messageId, string text = null, string[] mentions = null) | |||
| => EditMessage(channel?.Id, messageId, text, mentions); | |||
| /// <summary> Edits a message the provided message, mentioning certain users. </summary> | |||
| /// <summary> Edits the provided message, changing only non-null attributes. </summary> | |||
| /// <remarks> While not required, it is recommended to include a mention reference in the text (see Mention.User). </remarks> | |||
| public async Task EditMessage(string channelId, string messageId, string text, string[] mentions) | |||
| public async Task EditMessage(string channelId, string messageId, string text = null, string[] mentions = null) | |||
| { | |||
| CheckReady(); | |||
| if (channelId == null) throw new ArgumentNullException(nameof(channelId)); | |||
| if (messageId == null) throw new ArgumentNullException(nameof(messageId)); | |||
| if (text == null) throw new ArgumentNullException(nameof(text)); | |||
| if (mentions == null) throw new ArgumentNullException(nameof(mentions)); | |||
| if (text.Length > DiscordAPIClient.MaxMessageSize) | |||
| text = text.Substring(0, DiscordAPIClient.MaxMessageSize); | |||
| if (text != null && text.Length > MaxMessageSize) | |||
| text = text.Substring(0, MaxMessageSize); | |||
| var model = await _api.EditMessage(messageId, channelId, text, mentions).ConfigureAwait(false); | |||
| var msg = _messages[messageId]; | |||
| @@ -383,19 +390,6 @@ namespace Discord | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| } | |||
| } | |||
| /// <summary> Sends a file to the provided channel. </summary> | |||
| public Task SendFile(Channel channel, string filePath) | |||
| => SendFile(channel?.Id, filePath); | |||
| /// <summary> Sends a file to the provided channel. </summary> | |||
| public Task SendFile(string channelId, string filePath) | |||
| { | |||
| CheckReady(); | |||
| if (channelId == null) throw new ArgumentNullException(nameof(channelId)); | |||
| if (filePath == null) throw new ArgumentNullException(nameof(filePath)); | |||
| return _api.SendFile(channelId, filePath); | |||
| } | |||
| /// <summary> Downloads last count messages from the server, starting at beforeMessageId if it's provided. </summary> | |||
| public Task<Message[]> DownloadMessages(Channel channel, int count, string beforeMessageId = null, bool cache = true) | |||
| @@ -449,113 +443,6 @@ namespace Discord | |||
| return null; | |||
| } | |||
| //Roles | |||
| /// <summary>Note: due to current API limitations, the created role cannot be returned. </summary> | |||
| public Task CreateRole(Server server) | |||
| => CreateRole(server?.Id); | |||
| /// <summary>Note: due to current API limitations, the created role cannot be returned. </summary> | |||
| public Task CreateRole(string serverId) | |||
| { | |||
| CheckReady(); | |||
| if (serverId == null) throw new NullReferenceException(nameof(serverId)); | |||
| return _api.CreateRole(serverId); | |||
| } | |||
| public Task RenameRole(Role role, string newName) | |||
| => RenameRole(role?.ServerId, role?.Id, newName); | |||
| public Task RenameRole(string serverId, string roleId, string newName) | |||
| { | |||
| CheckReady(); | |||
| if (roleId == null) throw new NullReferenceException(nameof(roleId)); | |||
| if (newName == null) throw new NullReferenceException(nameof(newName)); | |||
| return _api.RenameRole(serverId, roleId, newName); | |||
| } | |||
| public Task DeleteRole(Role role) | |||
| => DeleteRole(role?.ServerId, role?.Id); | |||
| public Task DeleteRole(string serverId, string roleId) | |||
| { | |||
| CheckReady(); | |||
| if (roleId == null) throw new NullReferenceException(nameof(roleId)); | |||
| return _api.DeleteRole(serverId, roleId); | |||
| } | |||
| public Task AddRoleMember(Role role, string serverId, string userId) | |||
| => AddRoleMember(role?.Id, GetMember(serverId, userId)); | |||
| public Task AddRoleMember(Role role, string serverId, User user) | |||
| => AddRoleMember(role?.Id, GetMember(serverId, user)); | |||
| public Task AddRoleMember(Role role, Server server, string userId) | |||
| => AddRoleMember(role?.Id, GetMember(server, userId)); | |||
| public Task AddRoleMember(Role role, Server server, User user) | |||
| => AddRoleMember(role?.Id, GetMember(server, user)); | |||
| public Task AddRoleMember(Role role, Member member) | |||
| => AddRoleMember(role?.Id, member); | |||
| public Task AddRoleMember(string roleId, string serverId, string userId) | |||
| => AddRoleMember(roleId, GetMember(serverId, userId)); | |||
| public Task AddRoleMember(string roleId, string serverId, User user) | |||
| => AddRoleMember(roleId, GetMember(serverId, user)); | |||
| public Task AddRoleMember(string roleId, Server server, string userId) | |||
| => AddRoleMember(roleId, GetMember(server, userId)); | |||
| public Task AddRoleMember(string roleId, Server server, User user) | |||
| => AddRoleMember(roleId, GetMember(server, user)); | |||
| public Task AddRoleMember(string roleId, Member member) | |||
| { | |||
| CheckReady(); | |||
| if (roleId == null) throw new NullReferenceException(nameof(roleId)); | |||
| if (member == null) throw new NullReferenceException(nameof(member)); | |||
| if (!member.RoleIds.Contains(roleId)) | |||
| { | |||
| var oldRoles = member.RoleIds; | |||
| string[] newRoles = new string[oldRoles.Length + 1]; | |||
| for (int i = 0; i < oldRoles.Length; i++) | |||
| newRoles[i] = oldRoles[i]; | |||
| return _api.SetMemberRoles(member.ServerId, member.UserId, newRoles); | |||
| } | |||
| return TaskHelper.CompletedTask; | |||
| } | |||
| public Task RemoveRoleMember(Role role, string serverId, string userId) | |||
| => RemoveRoleMember(role?.Id, GetMember(serverId, userId)); | |||
| public Task RemoveRoleMember(Role role, string serverId, User user) | |||
| => RemoveRoleMember(role?.Id, GetMember(serverId, user)); | |||
| public Task RemoveRoleMember(Role role, Server server, string userId) | |||
| => RemoveRoleMember(role?.Id, GetMember(server, userId)); | |||
| public Task RemoveRoleMember(Role role, Server server, User user) | |||
| => RemoveRoleMember(role?.Id, GetMember(server, user)); | |||
| public Task RemoveRoleMember(Role role, Member member) | |||
| => RemoveRoleMember(role?.Id, member); | |||
| public Task RemoveRoleMember(string roleId, string serverId, string userId) | |||
| => RemoveRoleMember(roleId, GetMember(serverId, userId)); | |||
| public Task RemoveRoleMember(string roleId, string serverId, User user) | |||
| => RemoveRoleMember(roleId, GetMember(serverId, user)); | |||
| public Task RemoveRoleMember(string roleId, Server server, string userId) | |||
| => RemoveRoleMember(roleId, GetMember(server, userId)); | |||
| public Task RemoveRoleMember(string roleId, Server server, User user) | |||
| => RemoveRoleMember(roleId, GetMember(server, user)); | |||
| public Task RemoveRoleMember(string roleId, Member member) | |||
| { | |||
| CheckReady(); | |||
| if (roleId == null) throw new NullReferenceException(nameof(roleId)); | |||
| if (member == null) throw new NullReferenceException(nameof(member)); | |||
| if (member.RoleIds.Contains(roleId)) | |||
| { | |||
| var oldRoles = member.RoleIds; | |||
| string[] newRoles = new string[oldRoles.Length - 1]; | |||
| for (int i = 0, j = 0; i < oldRoles.Length; i++) | |||
| { | |||
| if (oldRoles[i] != roleId) | |||
| newRoles[j++] = oldRoles[i]; | |||
| } | |||
| return _api.SetMemberRoles(member.ServerId, member.UserId, newRoles); | |||
| } | |||
| return TaskHelper.CompletedTask; | |||
| } | |||
| //Permissions | |||
| public Task SetChannelUserPermissions(Channel channel, Member member, PackedPermissions allow, PackedPermissions deny) | |||
| => SetChannelPermissions(channel?.Id, member?.UserId, "member", allow, deny); | |||
| @@ -585,7 +472,7 @@ namespace Discord | |||
| if (channelId == null) throw new NullReferenceException(nameof(channelId)); | |||
| if (userOrRoleId == null) throw new NullReferenceException(nameof(userOrRoleId)); | |||
| return _api.SetChannelPermissions(channelId, userOrRoleId, idType, allow, deny); | |||
| return _api.SetChannelPermissions(channelId, userOrRoleId, idType, allow.RawValue, deny.RawValue); | |||
| //TODO: Remove permission from cache | |||
| } | |||
| @@ -625,128 +512,90 @@ namespace Discord | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| } | |||
| //Voice | |||
| /// <summary> Mutes a user on the provided server. </summary> | |||
| public Task Mute(Member member) | |||
| => Mute(member?.ServerId, member?.UserId); | |||
| /// <summary> Mutes a user on the provided server. </summary> | |||
| public Task Mute(Server server, User user) | |||
| => Mute(server?.Id, user?.Id); | |||
| /// <summary> Mutes a user on the provided server. </summary> | |||
| public Task Mute(Server server, string userId) | |||
| => Mute(server?.Id, userId); | |||
| /// <summary> Mutes a user on the provided server. </summary> | |||
| public Task Mute(string server, User user) | |||
| => Mute(server, user?.Id); | |||
| /// <summary> Mutes a user on the provided server. </summary> | |||
| public Task Mute(string serverId, string userId) | |||
| //Profile | |||
| public Task<EditProfileResponse> EditProfile(string currentPassword, | |||
| string username = null, string email = null, string password = null, | |||
| AvatarImageType avatarType = AvatarImageType.Png, byte[] avatar = null) | |||
| { | |||
| CheckReady(); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); | |||
| return _api.Mute(serverId, userId); | |||
| return _api.EditProfile(currentPassword, username: username, email: email, password: password, | |||
| avatarType: avatarType, avatar: avatar); | |||
| } | |||
| /// <summary> Mutes a user on the provided server. </summary> | |||
| public Task Unmute(Member member) | |||
| => Unmute(member?.ServerId, member?.UserId); | |||
| /// <summary> Unmutes a user on the provided server. </summary> | |||
| public Task Unmute(Server server, User user) | |||
| => Unmute(server?.Id, user?.Id); | |||
| /// <summary> Unmutes a user on the provided server. </summary> | |||
| public Task Unmute(Server server, string userId) | |||
| => Unmute(server?.Id, userId); | |||
| /// <summary> Unmutes a user on the provided server. </summary> | |||
| public Task Unmute(string server, User user) | |||
| => Unmute(server, user?.Id); | |||
| /// <summary> Unmutes a user on the provided server. </summary> | |||
| public Task Unmute(string serverId, string userId) | |||
| //Roles | |||
| /// <summary> Note: due to current API limitations, the created role cannot be returned. </summary> | |||
| public Task CreateRole(Server server) | |||
| => CreateRole(server?.Id); | |||
| /// <summary> Note: due to current API limitations, the created role cannot be returned. </summary> | |||
| public Task CreateRole(string serverId) | |||
| { | |||
| CheckReady(); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| if (serverId == null) throw new NullReferenceException(nameof(serverId)); | |||
| return _api.Unmute(serverId, userId); | |||
| return _api.CreateRole(serverId); | |||
| } | |||
| /// <summary> Deafens a user on the provided server. </summary> | |||
| public Task Deafen(Member member) | |||
| => Deafen(member?.ServerId, member?.UserId); | |||
| /// <summary> Deafens a user on the provided server. </summary> | |||
| public Task Deafen(Server server, User user) | |||
| => Deafen(server?.Id, user?.Id); | |||
| /// <summary> Deafens a user on the provided server. </summary> | |||
| public Task Deafen(Server server, string userId) | |||
| => Deafen(server?.Id, userId); | |||
| /// <summary> Deafens a user on the provided server. </summary> | |||
| public Task Deafen(string server, User user) | |||
| => Deafen(server, user?.Id); | |||
| /// <summary> Deafens a user on the provided server. </summary> | |||
| public Task Deafen(string serverId, string userId) | |||
| public Task EditRole(Role role, string newName) | |||
| => EditRole(role?.ServerId, role?.Id, newName); | |||
| public Task EditRole(string serverId, string roleId, string name = null, PackedPermissions permissions = null) | |||
| { | |||
| CheckReady(); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| if (serverId == null) throw new NullReferenceException(nameof(serverId)); | |||
| if (roleId == null) throw new NullReferenceException(nameof(roleId)); | |||
| return _api.Deafen(serverId, userId); | |||
| return _api.EditRole(serverId, roleId, name: name, permissions: permissions?.RawValue); | |||
| } | |||
| /// <summary> Undeafens a user on the provided server. </summary> | |||
| public Task Undeafen(Member member) | |||
| => Undeafen(member?.ServerId, member?.UserId); | |||
| /// <summary> Undeafens a user on the provided server. </summary> | |||
| public Task Undeafen(Server server, User user) | |||
| => Undeafen(server?.Id, user?.Id); | |||
| /// <summary> Undeafens a user on the provided server. </summary> | |||
| public Task Undeafen(Server server, string userId) | |||
| => Undeafen(server?.Id, userId); | |||
| /// <summary> Undeafens a user on the provided server. </summary> | |||
| public Task Undeafen(string server, User user) | |||
| => Undeafen(server, user?.Id); | |||
| /// <summary> Undeafens a user on the provided server. </summary> | |||
| public Task Undeafen(string serverId, string userId) | |||
| public Task DeleteRole(Role role) | |||
| => DeleteRole(role?.ServerId, role?.Id); | |||
| public Task DeleteRole(string serverId, string roleId) | |||
| { | |||
| CheckReady(); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||
| if (serverId == null) throw new NullReferenceException(nameof(serverId)); | |||
| if (roleId == null) throw new NullReferenceException(nameof(roleId)); | |||
| return _api.Undeafen(serverId, userId); | |||
| return _api.DeleteRole(serverId, roleId); | |||
| } | |||
| //Profile | |||
| /// <summary> Changes your username to newName. </summary> | |||
| public async Task ChangeUsername(string newName, string currentEmail, string currentPassword) | |||
| { | |||
| CheckReady(); | |||
| var response = await _api.ChangeUsername(newName, currentEmail, currentPassword).ConfigureAwait(false); | |||
| _currentUser.Update(response); | |||
| foreach (var membership in _currentUser.Memberships) | |||
| membership.Update(response); | |||
| } | |||
| /// <summary> Changes your email to newEmail. </summary> | |||
| public async Task ChangeEmail(string newEmail, string currentPassword) | |||
| //Servers | |||
| /// <summary> Creates a new server with the provided name and region (see Regions). </summary> | |||
| public async Task<Server> CreateServer(string name, string region) | |||
| { | |||
| CheckReady(); | |||
| var response = await _api.ChangeEmail(newEmail, currentPassword).ConfigureAwait(false); | |||
| _currentUser.Update(response); | |||
| if (name == null) throw new ArgumentNullException(nameof(name)); | |||
| if (region == null) throw new ArgumentNullException(nameof(region)); | |||
| var response = await _api.CreateServer(name, region).ConfigureAwait(false); | |||
| var server = _servers.GetOrAdd(response.Id); | |||
| server.Update(response); | |||
| return server; | |||
| } | |||
| /// <summary> Changes your password to newPassword. </summary> | |||
| public async Task ChangePassword(string newPassword, string currentEmail, string currentPassword) | |||
| /// <summary> Edits the provided server, changing only non-null attributes. </summary> | |||
| public Task EditServer(Server server) | |||
| => EditServer(server?.Id); | |||
| /// <summary> Edits the provided server, changing only non-null attributes. </summary> | |||
| public Task EditServer(string serverId, string name = null, string region = null) | |||
| { | |||
| CheckReady(); | |||
| await _api.ChangePassword(newPassword, currentEmail, currentPassword).ConfigureAwait(false); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| return _api.EditServer(serverId, name: name, region: region); | |||
| } | |||
| /// <summary> Changes your avatar. </summary> | |||
| /// <remarks>Only supports PNG and JPEG (see AvatarImageType)</remarks> | |||
| public async Task ChangeAvatar(AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword) | |||
| /// <summary> Leaves the provided server, destroying it if you are the owner. </summary> | |||
| public Task<Server> LeaveServer(Server server) | |||
| => LeaveServer(server?.Id); | |||
| /// <summary> Leaves the provided server, destroying it if you are the owner. </summary> | |||
| public async Task<Server> LeaveServer(string serverId) | |||
| { | |||
| CheckReady(); | |||
| var response = await _api.ChangeAvatar(imageType, bytes, currentEmail, currentPassword).ConfigureAwait(false); | |||
| _currentUser.Update(response); | |||
| foreach (var membership in _currentUser.Memberships) | |||
| membership.Update(response); | |||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
| try { await _api.LeaveServer(serverId).ConfigureAwait(false); } | |||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | |||
| return _servers.TryRemove(serverId); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| using Discord.Helpers; | |||
| using Discord.WebSockets; | |||
| using System; | |||
| using System.Threading.Tasks; | |||
| @@ -36,7 +37,7 @@ namespace Discord | |||
| { | |||
| CheckReady(checkVoice: true); | |||
| if (_voiceSocket.State != Net.WebSockets.WebSocketState.Disconnected) | |||
| if (_voiceSocket.State != WebSocketState.Disconnected) | |||
| { | |||
| var serverId = _voiceSocket.CurrentVoiceServerId; | |||
| if (serverId != null) | |||
| @@ -1,8 +1,8 @@ | |||
| using Discord.Collections; | |||
| using Discord.API; | |||
| using Discord.Collections; | |||
| using Discord.Helpers; | |||
| using Discord.Net; | |||
| using Discord.Net.API; | |||
| using Discord.Net.WebSockets; | |||
| using Discord.WebSockets; | |||
| using Discord.WebSockets.Data; | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| using System.Collections.Concurrent; | |||
| @@ -10,6 +10,7 @@ using System.Net; | |||
| using System.Runtime.ExceptionServices; | |||
| using System.Threading; | |||
| using System.Threading.Tasks; | |||
| using VoiceWebSocket = Discord.WebSockets.Voice.VoiceWebSocket; | |||
| namespace Discord | |||
| { | |||
| @@ -279,7 +280,7 @@ namespace Discord | |||
| //Global | |||
| case "READY": //Resync | |||
| { | |||
| var data = e.Payload.ToObject<Events.Ready>(_serializer); | |||
| var data = e.Payload.ToObject<ReadyEvent>(_serializer); | |||
| _currentUserId = data.User.Id; | |||
| _currentUser = _users.GetOrAdd(data.User.Id); | |||
| _currentUser.Update(data.User); | |||
| @@ -303,7 +304,7 @@ namespace Discord | |||
| //Servers | |||
| case "GUILD_CREATE": | |||
| { | |||
| var model = e.Payload.ToObject<Events.GuildCreate>(_serializer); | |||
| var model = e.Payload.ToObject<GuildCreateEvent>(_serializer); | |||
| var server = _servers.GetOrAdd(model.Id); | |||
| server.Update(model); | |||
| RaiseServerCreated(server); | |||
| @@ -311,7 +312,7 @@ namespace Discord | |||
| break; | |||
| case "GUILD_UPDATE": | |||
| { | |||
| var model = e.Payload.ToObject<Events.GuildUpdate>(_serializer); | |||
| var model = e.Payload.ToObject<GuildUpdateEvent>(_serializer); | |||
| var server = _servers[model.Id]; | |||
| if (server != null) | |||
| { | |||
| @@ -322,7 +323,7 @@ namespace Discord | |||
| break; | |||
| case "GUILD_DELETE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildDelete>(_serializer); | |||
| var data = e.Payload.ToObject<GuildDeleteEvent>(_serializer); | |||
| var server = _servers.TryRemove(data.Id); | |||
| if (server != null) | |||
| RaiseServerDestroyed(server); | |||
| @@ -332,7 +333,7 @@ namespace Discord | |||
| //Channels | |||
| case "CHANNEL_CREATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.ChannelCreate>(_serializer); | |||
| var data = e.Payload.ToObject<ChannelCreateEvent>(_serializer); | |||
| Channel channel; | |||
| if (data.IsPrivate) | |||
| { | |||
| @@ -348,7 +349,7 @@ namespace Discord | |||
| break; | |||
| case "CHANNEL_UPDATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.ChannelUpdate>(_serializer); | |||
| var data = e.Payload.ToObject<ChannelUpdateEvent>(_serializer); | |||
| var channel = _channels[data.Id]; | |||
| if (channel != null) | |||
| { | |||
| @@ -359,7 +360,7 @@ namespace Discord | |||
| break; | |||
| case "CHANNEL_DELETE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.ChannelDelete>(_serializer); | |||
| var data = e.Payload.ToObject<ChannelDeleteEvent>(_serializer); | |||
| var channel = _channels.TryRemove(data.Id); | |||
| if (channel != null) | |||
| RaiseChannelDestroyed(channel); | |||
| @@ -369,7 +370,7 @@ namespace Discord | |||
| //Members | |||
| case "GUILD_MEMBER_ADD": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildMemberAdd>(_serializer); | |||
| var data = e.Payload.ToObject<GuildMemberAddEvent>(_serializer); | |||
| var user = _users.GetOrAdd(data.User.Id); | |||
| var member = _members.GetOrAdd(data.User.Id, data.GuildId); | |||
| user.Update(data.User); | |||
| @@ -381,7 +382,7 @@ namespace Discord | |||
| break; | |||
| case "GUILD_MEMBER_UPDATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildMemberUpdate>(_serializer); | |||
| var data = e.Payload.ToObject<GuildMemberUpdateEvent>(_serializer); | |||
| var member = _members[data.User.Id, data.GuildId]; | |||
| if (member != null) | |||
| { | |||
| @@ -392,7 +393,7 @@ namespace Discord | |||
| break; | |||
| case "GUILD_MEMBER_REMOVE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildMemberRemove>(_serializer); | |||
| var data = e.Payload.ToObject<GuildMemberRemoveEvent>(_serializer); | |||
| var member = _members.TryRemove(data.UserId, data.GuildId); | |||
| if (member != null) | |||
| RaiseUserRemoved(member); | |||
| @@ -402,7 +403,7 @@ namespace Discord | |||
| //Roles | |||
| case "GUILD_ROLE_CREATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildRoleCreate>(_serializer); | |||
| var data = e.Payload.ToObject<GuildRoleCreateEvent>(_serializer); | |||
| var role = _roles.GetOrAdd(data.Data.Id, data.GuildId); | |||
| role.Update(data.Data); | |||
| RaiseRoleUpdated(role); | |||
| @@ -410,7 +411,7 @@ namespace Discord | |||
| break; | |||
| case "GUILD_ROLE_UPDATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildRoleUpdate>(_serializer); | |||
| var data = e.Payload.ToObject<GuildRoleUpdateEvent>(_serializer); | |||
| var role = _roles[data.Data.Id]; | |||
| if (role != null) | |||
| role.Update(data.Data); | |||
| @@ -419,7 +420,7 @@ namespace Discord | |||
| break; | |||
| case "GUILD_ROLE_DELETE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildRoleDelete>(_serializer); | |||
| var data = e.Payload.ToObject<GuildRoleDeleteEvent>(_serializer); | |||
| var role = _roles.TryRemove(data.RoleId); | |||
| if (role != null) | |||
| RaiseRoleDeleted(role); | |||
| @@ -429,7 +430,7 @@ namespace Discord | |||
| //Bans | |||
| case "GUILD_BAN_ADD": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildBanAdd>(_serializer); | |||
| var data = e.Payload.ToObject<GuildBanAddEvent>(_serializer); | |||
| var server = _servers[data.GuildId]; | |||
| if (server != null) | |||
| { | |||
| @@ -440,7 +441,7 @@ namespace Discord | |||
| break; | |||
| case "GUILD_BAN_REMOVE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.GuildBanRemove>(_serializer); | |||
| var data = e.Payload.ToObject<GuildBanRemoveEvent>(_serializer); | |||
| var server = _servers[data.GuildId]; | |||
| if (server != null && server.RemoveBan(data.UserId)) | |||
| RaiseBanRemoved(data.UserId, server); | |||
| @@ -450,7 +451,7 @@ namespace Discord | |||
| //Messages | |||
| case "MESSAGE_CREATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.MessageCreate>(_serializer); | |||
| var data = e.Payload.ToObject<MessageCreateEvent>(_serializer); | |||
| Message msg = null; | |||
| bool wasLocal = _config.UseMessageQueue && data.Author.Id == _currentUserId && data.Nonce != null; | |||
| @@ -490,7 +491,7 @@ namespace Discord | |||
| break; | |||
| case "MESSAGE_UPDATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.MessageUpdate>(_serializer); | |||
| var data = e.Payload.ToObject<MessageUpdateEvent>(_serializer); | |||
| var msg = _messages[data.Id]; | |||
| if (msg != null) | |||
| { | |||
| @@ -501,7 +502,7 @@ namespace Discord | |||
| break; | |||
| case "MESSAGE_DELETE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.MessageDelete>(_serializer); | |||
| var data = e.Payload.ToObject<MessageDeleteEvent>(_serializer); | |||
| var msg = _messages.TryRemove(data.Id); | |||
| if (msg != null) | |||
| RaiseMessageDeleted(msg); | |||
| @@ -509,7 +510,7 @@ namespace Discord | |||
| break; | |||
| case "MESSAGE_ACK": | |||
| { | |||
| var data = e.Payload.ToObject<Events.MessageAck>(_serializer); | |||
| var data = e.Payload.ToObject<MessageAckEvent>(_serializer); | |||
| var msg = GetMessage(data.MessageId); | |||
| if (msg != null) | |||
| RaiseMessageReadRemotely(msg); | |||
| @@ -519,7 +520,7 @@ namespace Discord | |||
| //Statuses | |||
| case "PRESENCE_UPDATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.PresenceUpdate>(_serializer); | |||
| var data = e.Payload.ToObject<PresenceUpdateEvent>(_serializer); | |||
| var member = _members[data.User.Id, data.GuildId]; | |||
| /*if (_config.TrackActivity) | |||
| { | |||
| @@ -536,7 +537,7 @@ namespace Discord | |||
| break; | |||
| case "VOICE_STATE_UPDATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.VoiceStateUpdate>(_serializer); | |||
| var data = e.Payload.ToObject<VoiceStateUpdateEvent>(_serializer); | |||
| var member = _members[data.UserId, data.GuildId]; | |||
| /*if (_config.TrackActivity) | |||
| { | |||
| @@ -558,7 +559,7 @@ namespace Discord | |||
| break; | |||
| case "TYPING_START": | |||
| { | |||
| var data = e.Payload.ToObject<Events.TypingStart>(_serializer); | |||
| var data = e.Payload.ToObject<TypingStartEvent>(_serializer); | |||
| var channel = _channels[data.ChannelId]; | |||
| var user = _users[data.UserId]; | |||
| @@ -587,7 +588,7 @@ namespace Discord | |||
| //Voice | |||
| case "VOICE_SERVER_UPDATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.VoiceServerUpdate>(_serializer); | |||
| var data = e.Payload.ToObject<VoiceServerUpdateEvent>(_serializer); | |||
| if (data.GuildId == _voiceSocket.CurrentVoiceServerId) | |||
| { | |||
| var server = _servers[data.GuildId]; | |||
| @@ -603,7 +604,7 @@ namespace Discord | |||
| //Settings | |||
| case "USER_UPDATE": | |||
| { | |||
| var data = e.Payload.ToObject<Events.UserUpdate>(_serializer); | |||
| var data = e.Payload.ToObject<UserUpdateEvent>(_serializer); | |||
| var user = _users[data.Id]; | |||
| if (user != null) | |||
| { | |||
| @@ -670,8 +671,8 @@ namespace Discord | |||
| _token = token; | |||
| _state = (int)DiscordClientState.Connecting; | |||
| string url = (await _api.GetWebSocketEndpoint().ConfigureAwait(false)).Url; | |||
| if (_config.LogLevel >= LogMessageSeverity.Verbose) | |||
| string url = (await _api.Gateway().ConfigureAwait(false)).Url; | |||
| if (_config.LogLevel >= LogMessageSeverity.Verbose) | |||
| RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client, $"Websocket endpoint: {url}"); | |||
| _dataSocket.Host = url; | |||
| @@ -838,7 +839,7 @@ namespace Discord | |||
| while (_pendingMessages.TryDequeue(out msg)) | |||
| { | |||
| bool hasFailed = false; | |||
| Responses.SendMessage response = null; | |||
| SendMessageResponse response = null; | |||
| try | |||
| { | |||
| response = await _api.SendMessage(msg.ChannelId, msg.RawText, msg.MentionIds, msg.Nonce, msg.IsTTS).ConfigureAwait(false); | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.Net.API; | |||
| using System.Text; | |||
| using System.Text; | |||
| namespace Discord | |||
| { | |||
| @@ -11,7 +10,7 @@ namespace Discord | |||
| static Format() | |||
| { | |||
| _patterns = new string[] { "__", "_", "**", "*", "~~" }; | |||
| _builder = new StringBuilder(DiscordAPIClient.MaxMessageSize); | |||
| _builder = new StringBuilder(DiscordClient.MaxMessageSize); | |||
| } | |||
| /// <summary> Removes all special formatting characters from the provided text. </summary> | |||
| @@ -1,9 +1,4 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| namespace Discord | |||
| { | |||
| public static class Mention | |||
| { | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.Net.API; | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json; | |||
| using System.Collections.Concurrent; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| @@ -64,14 +63,14 @@ namespace Discord | |||
| _messages = new ConcurrentDictionary<string, bool>(); | |||
| } | |||
| internal void Update(ChannelReference model) | |||
| internal void Update(API.ChannelReference model) | |||
| { | |||
| Name = model.Name; | |||
| Type = model.Type; | |||
| } | |||
| internal void Update(ChannelInfo model) | |||
| internal void Update(API.ChannelInfo model) | |||
| { | |||
| Update(model as ChannelReference); | |||
| Update(model as API.ChannelReference); | |||
| Position = model.Position; | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.Net.API; | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json; | |||
| namespace Discord | |||
| { | |||
| @@ -24,7 +23,7 @@ namespace Discord | |||
| public string XkcdPass { get; } | |||
| /// <summary> Returns a URL for this invite using XkcdPass if available or Id if not. </summary> | |||
| public string Url => Endpoints.InviteUrl(XkcdPass ?? Id); | |||
| public string Url => API.Endpoints.InviteUrl(XkcdPass ?? Id); | |||
| /// <summary> Returns the id of the user that created this invite. </summary> | |||
| public string InviterId { get; internal set; } | |||
| @@ -54,16 +53,16 @@ namespace Discord | |||
| public override string ToString() => XkcdPass ?? Id; | |||
| internal void Update(Net.API.Invite model) | |||
| internal void Update(API.Invite model) | |||
| { | |||
| ChannelId = model.Channel.Id; | |||
| InviterId = model.Inviter?.Id; | |||
| ServerId = model.Guild.Id; | |||
| } | |||
| internal void Update(Net.API.ExtendedInvite model) | |||
| internal void Update(API.ExtendedInvite model) | |||
| { | |||
| Update(model as Net.API.Invite); | |||
| Update(model as API.Invite); | |||
| IsRevoked = model.IsRevoked; | |||
| IsTemporary = model.IsTemporary; | |||
| MaxAge = model.MaxAge; | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.Net.API; | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| @@ -17,7 +16,7 @@ namespace Discord | |||
| /// <summary> Returns the unique identifier for this user's current avatar. </summary> | |||
| public string AvatarId { get; internal set; } | |||
| /// <summary> Returns the URL to this user's current avatar. </summary> | |||
| public string AvatarUrl => Endpoints.UserAvatar(UserId, AvatarId); | |||
| public string AvatarUrl => API.Endpoints.UserAvatar(UserId, AvatarId); | |||
| /// <summary> Returns the datetime that this user joined this server. </summary> | |||
| public DateTime JoinedAt { get; internal set; } | |||
| @@ -70,7 +69,7 @@ namespace Discord | |||
| public override string ToString() => UserId; | |||
| internal void Update(UserReference model) | |||
| internal void Update(API.UserReference model) | |||
| { | |||
| if (model.Avatar != null) | |||
| AvatarId = model.Avatar; | |||
| @@ -79,7 +78,7 @@ namespace Discord | |||
| if (model.Username != null) | |||
| Name = model.Username; | |||
| } | |||
| internal void Update(MemberInfo model) | |||
| internal void Update(API.MemberInfo model) | |||
| { | |||
| if (model.User != null) | |||
| Update(model.User); | |||
| @@ -87,13 +86,13 @@ namespace Discord | |||
| if (model.JoinedAt.HasValue) | |||
| JoinedAt = model.JoinedAt.Value; | |||
| } | |||
| internal void Update(ExtendedMemberInfo model) | |||
| internal void Update(API.ExtendedMemberInfo model) | |||
| { | |||
| Update(model as MemberInfo); | |||
| Update(model as API.MemberInfo); | |||
| IsDeafened = model.IsDeafened; | |||
| IsMuted = model.IsMuted; | |||
| } | |||
| internal void Update(PresenceMemberInfo model) | |||
| internal void Update(API.PresenceMemberInfo model) | |||
| { | |||
| if (Status != model.Status) | |||
| { | |||
| @@ -103,7 +102,7 @@ namespace Discord | |||
| } | |||
| GameId = model.GameId; | |||
| } | |||
| internal void Update(VoiceMemberInfo model) | |||
| internal void Update(API.VoiceMemberInfo model) | |||
| { | |||
| IsDeafened = model.IsDeafened; | |||
| IsMuted = model.IsMuted; | |||
| @@ -122,7 +122,7 @@ namespace Discord | |||
| UserId = userId; | |||
| } | |||
| internal void Update(Net.API.Message model) | |||
| internal void Update(API.Message model) | |||
| { | |||
| if (model.Attachments != null) | |||
| { | |||
| @@ -71,8 +71,7 @@ namespace Discord | |||
| else | |||
| _rawValue &= ~(1U << (pos - 1)); | |||
| } | |||
| public static implicit operator uint (PackedPermissions perms) => perms._rawValue; | |||
| public PackedPermissions Copy() => new PackedPermissions(false, _rawValue); | |||
| } | |||
| } | |||
| @@ -1,5 +1,4 @@ | |||
| using Newtonsoft.Json; | |||
| using System.Threading; | |||
| namespace Discord | |||
| { | |||
| @@ -29,7 +28,7 @@ namespace Discord | |||
| Permissions = new PackedPermissions(true); | |||
| } | |||
| internal void Update(Net.API.RoleInfo model) | |||
| internal void Update(API.RoleInfo model) | |||
| { | |||
| Name = model.Name; | |||
| Permissions.RawValue = (uint)model.Permissions; | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.Net.API; | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| using System.Collections.Concurrent; | |||
| using System.Collections.Generic; | |||
| @@ -100,7 +99,7 @@ namespace Discord | |||
| _roles = new ConcurrentDictionary<string, bool>(); | |||
| } | |||
| internal void Update(GuildInfo model) | |||
| internal void Update(API.GuildInfo model) | |||
| { | |||
| AFKChannelId = model.AFKChannelId; | |||
| AFKTimeout = model.AFKTimeout; | |||
| @@ -117,9 +116,9 @@ namespace Discord | |||
| role.Update(subModel); | |||
| } | |||
| } | |||
| internal void Update(ExtendedGuildInfo model) | |||
| internal void Update(API.ExtendedGuildInfo model) | |||
| { | |||
| Update(model as GuildInfo); | |||
| Update(model as API.GuildInfo); | |||
| var channels = _client.Channels; | |||
| foreach (var subModel in model.Channels) | |||
| @@ -1,5 +1,4 @@ | |||
| using Discord.Net.API; | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| using System.Collections.Concurrent; | |||
| using System.Collections.Generic; | |||
| @@ -24,7 +23,7 @@ namespace Discord | |||
| /// <summary> Returns the unique identifier for this user's current avatar. </summary> | |||
| public string AvatarId { get; internal set; } | |||
| /// <summary> Returns the URL to this user's current avatar. </summary> | |||
| public string AvatarUrl => Endpoints.UserAvatar(Id, AvatarId); | |||
| public string AvatarUrl => API.Endpoints.UserAvatar(Id, AvatarId); | |||
| /// <summary> Returns the email for this user. </summary> | |||
| /// <remarks> This field is only ever populated for the current logged in user. </remarks> | |||
| @@ -78,7 +77,7 @@ namespace Discord | |||
| _servers = new ConcurrentDictionary<string, bool>(); | |||
| } | |||
| internal void Update(UserReference model) | |||
| internal void Update(API.UserReference model) | |||
| { | |||
| if (model.Avatar != null) | |||
| AvatarId = model.Avatar; | |||
| @@ -87,9 +86,9 @@ namespace Discord | |||
| if (model.Username != null) | |||
| Name = model.Username; | |||
| } | |||
| internal void Update(SelfUserInfo model) | |||
| internal void Update(API.SelfUserInfo model) | |||
| { | |||
| Update(model as UserReference); | |||
| Update(model as API.UserReference); | |||
| Email = model.Email; | |||
| IsVerified = model.IsVerified; | |||
| } | |||
| @@ -1,295 +0,0 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| namespace Discord.Net.API | |||
| { | |||
| //User | |||
| internal class UserReference | |||
| { | |||
| [JsonProperty(PropertyName = "username")] | |||
| public string Username; | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| [JsonProperty(PropertyName = "discriminator")] | |||
| public string Discriminator; | |||
| [JsonProperty(PropertyName = "avatar")] | |||
| public string Avatar; | |||
| } | |||
| internal class SelfUserInfo : UserReference | |||
| { | |||
| [JsonProperty(PropertyName = "email")] | |||
| public string Email; | |||
| [JsonProperty(PropertyName = "verified")] | |||
| public bool IsVerified; | |||
| } | |||
| //Members | |||
| internal class MemberReference | |||
| { | |||
| [JsonProperty(PropertyName = "user_id")] | |||
| public string UserId; | |||
| [JsonProperty(PropertyName = "user")] | |||
| public UserReference User; | |||
| [JsonProperty(PropertyName = "guild_id")] | |||
| public string GuildId; | |||
| } | |||
| internal class MemberInfo : MemberReference | |||
| { | |||
| [JsonProperty(PropertyName = "joined_at")] | |||
| public DateTime? JoinedAt; | |||
| [JsonProperty(PropertyName = "roles")] | |||
| public string[] Roles; | |||
| } | |||
| internal class ExtendedMemberInfo : MemberInfo | |||
| { | |||
| [JsonProperty(PropertyName = "mute")] | |||
| public bool IsMuted; | |||
| [JsonProperty(PropertyName = "deaf")] | |||
| public bool IsDeafened; | |||
| } | |||
| internal class PresenceMemberInfo : MemberReference | |||
| { | |||
| [JsonProperty(PropertyName = "game_id")] | |||
| public string GameId; | |||
| [JsonProperty(PropertyName = "status")] | |||
| public string Status; | |||
| } | |||
| internal class VoiceMemberInfo : MemberReference | |||
| { | |||
| [JsonProperty(PropertyName = "channel_id")] | |||
| public string ChannelId; | |||
| [JsonProperty(PropertyName = "suppress")] | |||
| public bool? IsSuppressed; | |||
| [JsonProperty(PropertyName = "session_id")] | |||
| public string SessionId; | |||
| [JsonProperty(PropertyName = "self_mute")] | |||
| public bool? IsSelfMuted; | |||
| [JsonProperty(PropertyName = "self_deaf")] | |||
| public bool? IsSelfDeafened; | |||
| [JsonProperty(PropertyName = "mute")] | |||
| public bool IsMuted; | |||
| [JsonProperty(PropertyName = "deaf")] | |||
| public bool IsDeafened; | |||
| [JsonProperty(PropertyName = "token")] | |||
| public string Token; | |||
| } | |||
| //Channels | |||
| internal class ChannelReference | |||
| { | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| [JsonProperty(PropertyName = "guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty(PropertyName = "name")] | |||
| public string Name; | |||
| [JsonProperty(PropertyName = "type")] | |||
| public string Type; | |||
| } | |||
| internal class ChannelInfo : ChannelReference | |||
| { | |||
| public sealed class PermissionOverwrite | |||
| { | |||
| [JsonProperty(PropertyName = "type")] | |||
| public string Type; | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| [JsonProperty(PropertyName = "deny")] | |||
| public uint Deny; | |||
| [JsonProperty(PropertyName = "allow")] | |||
| public uint Allow; | |||
| } | |||
| [JsonProperty(PropertyName = "last_message_id")] | |||
| public string LastMessageId; | |||
| [JsonProperty(PropertyName = "is_private")] | |||
| public bool IsPrivate; | |||
| [JsonProperty(PropertyName = "position")] | |||
| public int Position; | |||
| [JsonProperty(PropertyName = "permission_overwrites")] | |||
| public PermissionOverwrite[] PermissionOverwrites; | |||
| [JsonProperty(PropertyName = "recipient")] | |||
| public UserReference Recipient; | |||
| } | |||
| //Guilds (Servers) | |||
| internal class GuildReference | |||
| { | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| [JsonProperty(PropertyName = "name")] | |||
| public string Name; | |||
| } | |||
| internal class GuildInfo : GuildReference | |||
| { | |||
| [JsonProperty(PropertyName = "afk_channel_id")] | |||
| public string AFKChannelId; | |||
| [JsonProperty(PropertyName = "afk_timeout")] | |||
| public int AFKTimeout; | |||
| [JsonProperty(PropertyName = "embed_channel_id")] | |||
| public string EmbedChannelId; | |||
| [JsonProperty(PropertyName = "embed_enabled")] | |||
| public bool EmbedEnabled; | |||
| [JsonProperty(PropertyName = "icon")] | |||
| public string Icon; | |||
| [JsonProperty(PropertyName = "joined_at")] | |||
| public DateTime? JoinedAt; | |||
| [JsonProperty(PropertyName = "owner_id")] | |||
| public string OwnerId; | |||
| [JsonProperty(PropertyName = "region")] | |||
| public string Region; | |||
| [JsonProperty(PropertyName = "roles")] | |||
| public RoleInfo[] Roles; | |||
| } | |||
| internal class ExtendedGuildInfo : GuildInfo | |||
| { | |||
| [JsonProperty(PropertyName = "channels")] | |||
| public ChannelInfo[] Channels; | |||
| [JsonProperty(PropertyName = "members")] | |||
| public ExtendedMemberInfo[] Members; | |||
| [JsonProperty(PropertyName = "presences")] | |||
| public PresenceMemberInfo[] Presences; | |||
| [JsonProperty(PropertyName = "voice_states")] | |||
| public VoiceMemberInfo[] VoiceStates; | |||
| } | |||
| //Messages | |||
| internal class MessageReference | |||
| { | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| [JsonProperty(PropertyName = "channel_id")] | |||
| public string ChannelId; | |||
| [JsonProperty(PropertyName = "message_id")] | |||
| public string MessageId { get { return Id; } set { Id = value; } } | |||
| } | |||
| internal class Message : MessageReference | |||
| { | |||
| public sealed class Attachment | |||
| { | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| [JsonProperty(PropertyName = "url")] | |||
| public string Url; | |||
| [JsonProperty(PropertyName = "proxy_url")] | |||
| public string ProxyUrl; | |||
| [JsonProperty(PropertyName = "size")] | |||
| public int Size; | |||
| [JsonProperty(PropertyName = "filename")] | |||
| public string Filename; | |||
| [JsonProperty(PropertyName = "width")] | |||
| public int Width; | |||
| [JsonProperty(PropertyName = "height")] | |||
| public int Height; | |||
| } | |||
| public sealed class Embed | |||
| { | |||
| public sealed class Reference | |||
| { | |||
| [JsonProperty(PropertyName = "url")] | |||
| public string Url; | |||
| [JsonProperty(PropertyName = "name")] | |||
| public string Name; | |||
| } | |||
| public sealed class ThumbnailInfo | |||
| { | |||
| [JsonProperty(PropertyName = "url")] | |||
| public string Url; | |||
| [JsonProperty(PropertyName = "proxy_url")] | |||
| public string ProxyUrl; | |||
| [JsonProperty(PropertyName = "width")] | |||
| public int Width; | |||
| [JsonProperty(PropertyName = "height")] | |||
| public int Height; | |||
| } | |||
| [JsonProperty(PropertyName = "url")] | |||
| public string Url; | |||
| [JsonProperty(PropertyName = "type")] | |||
| public string Type; | |||
| [JsonProperty(PropertyName = "title")] | |||
| public string Title; | |||
| [JsonProperty(PropertyName = "description")] | |||
| public string Description; | |||
| [JsonProperty(PropertyName = "author")] | |||
| public Reference Author; | |||
| [JsonProperty(PropertyName = "provider")] | |||
| public Reference Provider; | |||
| [JsonProperty(PropertyName = "thumbnail")] | |||
| public ThumbnailInfo Thumbnail; | |||
| } | |||
| [JsonProperty(PropertyName = "tts")] | |||
| public bool IsTextToSpeech; | |||
| [JsonProperty(PropertyName = "mention_everyone")] | |||
| public bool IsMentioningEveryone; | |||
| [JsonProperty(PropertyName = "timestamp")] | |||
| public DateTime Timestamp; | |||
| [JsonProperty(PropertyName = "edited_timestamp")] | |||
| public DateTime? EditedTimestamp; | |||
| [JsonProperty(PropertyName = "mentions")] | |||
| public UserReference[] Mentions; | |||
| [JsonProperty(PropertyName = "embeds")] | |||
| public Embed[] Embeds; //TODO: Parse this | |||
| [JsonProperty(PropertyName = "attachments")] | |||
| public Attachment[] Attachments; | |||
| [JsonProperty(PropertyName = "content")] | |||
| public string Content; | |||
| [JsonProperty(PropertyName = "author")] | |||
| public UserReference Author; | |||
| [JsonProperty(PropertyName = "nonce")] | |||
| public string Nonce; | |||
| } | |||
| //Roles | |||
| internal class RoleReference | |||
| { | |||
| [JsonProperty(PropertyName = "guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty(PropertyName = "role_id")] | |||
| public string RoleId; | |||
| } | |||
| internal class RoleInfo | |||
| { | |||
| [JsonProperty(PropertyName = "permissions")] | |||
| public int Permissions; | |||
| [JsonProperty(PropertyName = "name")] | |||
| public string Name; | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| } | |||
| //Invites | |||
| internal class Invite | |||
| { | |||
| [JsonProperty(PropertyName = "inviter")] | |||
| public UserReference Inviter; | |||
| [JsonProperty(PropertyName = "guild")] | |||
| public GuildReference Guild; | |||
| [JsonProperty(PropertyName = "channel")] | |||
| public ChannelReference Channel; | |||
| [JsonProperty(PropertyName = "code")] | |||
| public string Code; | |||
| [JsonProperty(PropertyName = "xkcdpass")] | |||
| public string XkcdPass; | |||
| } | |||
| internal class ExtendedInvite : Invite | |||
| { | |||
| [JsonProperty(PropertyName = "max_age")] | |||
| public int MaxAge; | |||
| [JsonProperty(PropertyName = "max_uses")] | |||
| public int MaxUses; | |||
| [JsonProperty(PropertyName = "revoked")] | |||
| public bool IsRevoked; | |||
| [JsonProperty(PropertyName = "temporary")] | |||
| public bool IsTemporary; | |||
| [JsonProperty(PropertyName = "uses")] | |||
| public int Uses; | |||
| [JsonProperty(PropertyName = "created_at")] | |||
| public DateTime CreatedAt; | |||
| } | |||
| } | |||
| @@ -1,212 +0,0 @@ | |||
| using System; | |||
| using System.Threading; | |||
| using System.Threading.Tasks; | |||
| namespace Discord.Net.API | |||
| { | |||
| internal class DiscordAPIClient | |||
| { | |||
| public const int MaxMessageSize = 2000; | |||
| public RestClient RestClient => _rest; | |||
| private readonly RestClient _rest; | |||
| public DiscordAPIClient(LogMessageSeverity logLevel, int timeout) | |||
| { | |||
| _rest = new RestClient(logLevel, timeout); | |||
| } | |||
| private string _token; | |||
| public string Token | |||
| { | |||
| get { return _token; } | |||
| set { _token = value; _rest.SetToken(value); } | |||
| } | |||
| private CancellationToken _cancelToken; | |||
| public CancellationToken CancelToken | |||
| { | |||
| get { return _cancelToken; } | |||
| set { _cancelToken = value; _rest.SetCancelToken(value); } | |||
| } | |||
| //Auth | |||
| public Task<Responses.Gateway> GetWebSocketEndpoint() | |||
| => _rest.Get<Responses.Gateway>(Endpoints.Gateway); | |||
| public async Task<Responses.AuthRegister> LoginAnonymous(string username) | |||
| { | |||
| var fingerprintResponse = await _rest.Post<Responses.AuthFingerprint>(Endpoints.AuthFingerprint).ConfigureAwait(false); | |||
| var registerRequest = new Requests.AuthRegister { Fingerprint = fingerprintResponse.Fingerprint, Username = username }; | |||
| var registerResponse = await _rest.Post<Responses.AuthRegister>(Endpoints.AuthRegister, registerRequest).ConfigureAwait(false); | |||
| return registerResponse; | |||
| } | |||
| public async Task<Responses.AuthLogin> Login(string email, string password) | |||
| { | |||
| var request = new Requests.AuthLogin { Email = email, Password = password }; | |||
| var response = await _rest.Post<Responses.AuthLogin>(Endpoints.AuthLogin, request).ConfigureAwait(false); | |||
| return response; | |||
| } | |||
| public Task Logout() | |||
| => _rest.Post(Endpoints.AuthLogout); | |||
| //Servers | |||
| public Task<Responses.CreateServer> CreateServer(string name, string region) | |||
| { | |||
| var request = new Requests.CreateServer { Name = name, Region = region }; | |||
| return _rest.Post<Responses.CreateServer>(Endpoints.Servers, request); | |||
| } | |||
| public Task LeaveServer(string id) | |||
| => _rest.Delete<Responses.DeleteServer>(Endpoints.Server(id)); | |||
| //Channels | |||
| public Task<Responses.CreateChannel> CreateChannel(string serverId, string name, string channelType) | |||
| { | |||
| var request = new Requests.CreateChannel { Name = name, Type = channelType }; | |||
| return _rest.Post<Responses.CreateChannel>(Endpoints.ServerChannels(serverId), request); | |||
| } | |||
| public Task<Responses.CreateChannel> CreatePMChannel(string myId, string recipientId) | |||
| { | |||
| var request = new Requests.CreatePMChannel { RecipientId = recipientId }; | |||
| return _rest.Post<Responses.CreateChannel>(Endpoints.UserChannels(myId), request); | |||
| } | |||
| public Task<Responses.DestroyChannel> DestroyChannel(string channelId) | |||
| => _rest.Delete<Responses.DestroyChannel>(Endpoints.Channel(channelId)); | |||
| public Task<Responses.GetMessages[]> GetMessages(string channelId, int count) | |||
| => _rest.Get<Responses.GetMessages[]>(Endpoints.ChannelMessages(channelId, count)); | |||
| //Members | |||
| public Task Kick(string serverId, string userId) | |||
| => _rest.Delete(Endpoints.ServerMember(serverId, userId)); | |||
| public Task Ban(string serverId, string userId) | |||
| => _rest.Put(Endpoints.ServerBan(serverId, userId)); | |||
| public Task Unban(string serverId, string userId) | |||
| => _rest.Delete(Endpoints.ServerBan(serverId, userId)); | |||
| public Task SetMemberRoles(string serverId, string userId, string[] roles) | |||
| { | |||
| var request = new Requests.ModifyMember { Roles = roles }; | |||
| return _rest.Patch(Endpoints.ServerMember(serverId, userId)); | |||
| } | |||
| //Invites | |||
| public Task<Responses.CreateInvite> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool withXkcdPass) | |||
| { | |||
| var request = new Requests.CreateInvite { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = isTemporary, WithXkcdPass = withXkcdPass }; | |||
| return _rest.Post<Responses.CreateInvite>(Endpoints.ChannelInvites(channelId), request); | |||
| } | |||
| public Task<Responses.GetInvite> GetInvite(string id) | |||
| => _rest.Get<Responses.GetInvite>(Endpoints.Invite(id)); | |||
| public Task AcceptInvite(string id) | |||
| => _rest.Post<Responses.AcceptInvite>(Endpoints.Invite(id)); | |||
| public Task DeleteInvite(string id) | |||
| => _rest.Delete(Endpoints.Invite(id)); | |||
| //Roles | |||
| public Task CreateRole(string serverId) | |||
| { | |||
| //TODO: Return a result when Discord starts giving us one | |||
| return _rest.Post(Endpoints.ServerRoles(serverId)); | |||
| } | |||
| public Task RenameRole(string serverId, string roleId, string newName) | |||
| { | |||
| var request = new Requests.ModifyRole { Name = newName }; | |||
| return _rest.Patch(Endpoints.ServerRole(serverId, roleId), request); | |||
| } | |||
| public Task SetRolePermissions(string serverId, string roleId, PackedPermissions permissions) | |||
| { | |||
| var request = new Requests.ModifyRole { Permissions = permissions.RawValue }; | |||
| return _rest.Patch(Endpoints.ServerRole(serverId, roleId), request); | |||
| } | |||
| public Task DeleteRole(string serverId, string roleId) | |||
| { | |||
| return _rest.Delete(Endpoints.ServerRole(serverId, roleId)); | |||
| } | |||
| //Permissions | |||
| public Task SetChannelPermissions(string channelId, string userOrRoleId, string idType, PackedPermissions allow, PackedPermissions deny) | |||
| { | |||
| var request = new Requests.SetChannelPermissions { Id = userOrRoleId, Type = idType, Allow = allow.RawValue, Deny = deny.RawValue }; | |||
| return _rest.Put(Endpoints.ChannelPermission(channelId, userOrRoleId), request); | |||
| } | |||
| public Task DeleteChannelPermissions(string channelId, string userOrRoleId) | |||
| { | |||
| return _rest.Delete(Endpoints.ChannelPermission(channelId, userOrRoleId), null); | |||
| } | |||
| //Chat | |||
| public Task<Responses.SendMessage> SendMessage(string channelId, string message, string[] mentions, string nonce, bool isTTS) | |||
| { | |||
| var request = new Requests.SendMessage { Content = message, Mentions = mentions, Nonce = nonce, IsTTS = isTTS }; | |||
| return _rest.Post<Responses.SendMessage>(Endpoints.ChannelMessages(channelId), request); | |||
| } | |||
| public Task<Responses.EditMessage> EditMessage(string messageId, string channelId, string message, string[] mentions) | |||
| { | |||
| var request = new Requests.EditMessage { Content = message, Mentions = mentions }; | |||
| return _rest.Patch<Responses.EditMessage>(Endpoints.ChannelMessage(channelId, messageId), request); | |||
| } | |||
| public Task SendIsTyping(string channelId) | |||
| => _rest.Post(Endpoints.ChannelTyping(channelId)); | |||
| public Task DeleteMessage(string channelId, string msgId) | |||
| => _rest.Delete(Endpoints.ChannelMessage(channelId, msgId)); | |||
| public Task SendFile(string channelId, string filePath) | |||
| => _rest.PostFile<Responses.SendMessage>(Endpoints.ChannelMessages(channelId), filePath); | |||
| //Voice | |||
| public Task<Responses.GetRegions[]> GetVoiceRegions() | |||
| => _rest.Get<Responses.GetRegions[]>(Endpoints.VoiceRegions); | |||
| public Task<Responses.GetIce> GetVoiceIce() | |||
| => _rest.Get<Responses.GetIce>(Endpoints.VoiceIce); | |||
| public Task Mute(string serverId, string memberId) | |||
| { | |||
| var request = new Requests.SetMemberMute { Value = true }; | |||
| return _rest.Patch(Endpoints.ServerMember(serverId, memberId)); | |||
| } | |||
| public Task Unmute(string serverId, string memberId) | |||
| { | |||
| var request = new Requests.SetMemberMute { Value = false }; | |||
| return _rest.Patch(Endpoints.ServerMember(serverId, memberId)); | |||
| } | |||
| public Task Deafen(string serverId, string memberId) | |||
| { | |||
| var request = new Requests.SetMemberDeaf { Value = true }; | |||
| return _rest.Patch(Endpoints.ServerMember(serverId, memberId)); | |||
| } | |||
| public Task Undeafen(string serverId, string memberId) | |||
| { | |||
| var request = new Requests.SetMemberDeaf { Value = false }; | |||
| return _rest.Patch(Endpoints.ServerMember(serverId, memberId)); | |||
| } | |||
| //Profile | |||
| public Task<Responses.ChangeProfile> ChangeUsername(string newUsername, string currentEmail, string currentPassword) | |||
| { | |||
| var request = new Requests.ChangeUsername { Username = newUsername, CurrentEmail = currentEmail, CurrentPassword = currentPassword }; | |||
| return _rest.Patch<Responses.ChangeProfile>(Endpoints.UserMe, request); | |||
| } | |||
| public Task<Responses.ChangeProfile> ChangeEmail(string newEmail, string currentPassword) | |||
| { | |||
| var request = new Requests.ChangeEmail { NewEmail = newEmail, CurrentPassword = currentPassword }; | |||
| return _rest.Patch<Responses.ChangeProfile>(Endpoints.UserMe, request); | |||
| } | |||
| public Task<Responses.ChangeProfile> ChangePassword(string newPassword, string currentEmail, string currentPassword) | |||
| { | |||
| var request = new Requests.ChangePassword { NewPassword = newPassword, CurrentEmail = currentEmail, CurrentPassword = currentPassword }; | |||
| return _rest.Patch<Responses.ChangeProfile>(Endpoints.UserMe, request); | |||
| } | |||
| public Task<Responses.ChangeProfile> ChangeAvatar(AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword) | |||
| { | |||
| string base64 = Convert.ToBase64String(bytes); | |||
| string type = imageType == AvatarImageType.Jpeg ? "image/jpeg;base64" : "image/png;base64"; | |||
| var request = new Requests.ChangeAvatar { Avatar = $"data:{type},/9j/{base64}", CurrentEmail = currentEmail, CurrentPassword = currentPassword }; | |||
| return _rest.Patch<Responses.ChangeProfile>(Endpoints.UserMe, request); | |||
| } | |||
| //Other | |||
| /*public Task<Responses.Status> GetUnresolvedIncidents() | |||
| { | |||
| return _rest.Get<Responses.Status>(Endpoints.StatusUnresolvedMaintenance); | |||
| } | |||
| public Task<Responses.Status> GetActiveIncidents() | |||
| { | |||
| return _rest.Get<Responses.Status>(Endpoints.StatusActiveMaintenance); | |||
| }*/ | |||
| } | |||
| } | |||
| @@ -1,158 +0,0 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| namespace Discord.Net.API | |||
| { | |||
| internal static class Requests | |||
| { | |||
| //Auth | |||
| public sealed class AuthRegister | |||
| { | |||
| [JsonProperty(PropertyName = "fingerprint")] | |||
| public string Fingerprint; | |||
| [JsonProperty(PropertyName = "username")] | |||
| public string Username; | |||
| } | |||
| public sealed class AuthLogin | |||
| { | |||
| [JsonProperty(PropertyName = "email")] | |||
| public string Email; | |||
| [JsonProperty(PropertyName = "password")] | |||
| public string Password; | |||
| } | |||
| //Servers | |||
| public sealed class CreateServer | |||
| { | |||
| [JsonProperty(PropertyName = "name")] | |||
| public string Name; | |||
| [JsonProperty(PropertyName = "region")] | |||
| public string Region; | |||
| } | |||
| //Channels | |||
| public sealed class CreateChannel | |||
| { | |||
| [JsonProperty(PropertyName = "name")] | |||
| public string Name; | |||
| [JsonProperty(PropertyName = "type")] | |||
| public string Type; | |||
| } | |||
| public sealed class CreatePMChannel | |||
| { | |||
| [JsonProperty(PropertyName = "recipient_id")] | |||
| public string RecipientId; | |||
| } | |||
| //Invites | |||
| public sealed class CreateInvite | |||
| { | |||
| [JsonProperty(PropertyName = "max_age")] | |||
| public int MaxAge; | |||
| [JsonProperty(PropertyName = "max_uses")] | |||
| public int MaxUses; | |||
| [JsonProperty(PropertyName = "temporary")] | |||
| public bool IsTemporary; | |||
| [JsonProperty(PropertyName = "xkcdpass")] | |||
| public bool WithXkcdPass; | |||
| } | |||
| //Messages | |||
| public sealed class SendMessage | |||
| { | |||
| [JsonProperty(PropertyName = "content")] | |||
| public string Content; | |||
| [JsonProperty(PropertyName = "mentions")] | |||
| public string[] Mentions; | |||
| [JsonProperty(PropertyName = "nonce")] | |||
| public string Nonce; | |||
| [JsonProperty(PropertyName = "tts")] | |||
| public bool IsTTS; | |||
| } | |||
| public sealed class EditMessage | |||
| { | |||
| [JsonProperty(PropertyName = "content")] | |||
| public string Content; | |||
| [JsonProperty(PropertyName = "mentions")] | |||
| public string[] Mentions; | |||
| } | |||
| //Members | |||
| public sealed class SetMemberMute | |||
| { | |||
| [JsonProperty(PropertyName = "mute")] | |||
| public bool Value; | |||
| } | |||
| public sealed class SetMemberDeaf | |||
| { | |||
| [JsonProperty(PropertyName = "deaf")] | |||
| public bool Value; | |||
| } | |||
| public sealed class ModifyMember | |||
| { | |||
| [JsonProperty(PropertyName = "roles")] | |||
| public string[] Roles; | |||
| } | |||
| //Profile | |||
| public sealed class ChangeUsername | |||
| { | |||
| [JsonProperty(PropertyName = "email")] | |||
| public string CurrentEmail; | |||
| [JsonProperty(PropertyName = "password")] | |||
| public string CurrentPassword; | |||
| [JsonProperty(PropertyName = "username")] | |||
| public string Username; | |||
| } | |||
| public sealed class ChangeEmail | |||
| { | |||
| [JsonProperty(PropertyName = "email")] | |||
| public string NewEmail; | |||
| [JsonProperty(PropertyName = "password")] | |||
| public string CurrentPassword; | |||
| } | |||
| public sealed class ChangePassword | |||
| { | |||
| [JsonProperty(PropertyName = "email")] | |||
| public string CurrentEmail; | |||
| [JsonProperty(PropertyName = "password")] | |||
| public string CurrentPassword; | |||
| [JsonProperty(PropertyName = "new_password")] | |||
| public string NewPassword; | |||
| } | |||
| public sealed class ChangeAvatar | |||
| { | |||
| [JsonProperty(PropertyName = "email")] | |||
| public string CurrentEmail; | |||
| [JsonProperty(PropertyName = "password")] | |||
| public string CurrentPassword; | |||
| [JsonProperty(PropertyName = "avatar")] | |||
| public string Avatar; | |||
| } | |||
| //Roles | |||
| public sealed class ModifyRole | |||
| { | |||
| [JsonProperty(PropertyName = "name", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Name; | |||
| [JsonProperty(PropertyName = "permissions", NullValueHandling = NullValueHandling.Ignore)] | |||
| public uint Permissions; | |||
| } | |||
| //Permissions | |||
| public sealed class SetChannelPermissions | |||
| { | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| [JsonProperty(PropertyName = "type")] | |||
| public string Type; | |||
| [JsonProperty(PropertyName = "allow")] | |||
| public uint Allow; | |||
| [JsonProperty(PropertyName = "deny")] | |||
| public uint Deny; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,85 +0,0 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| namespace Discord.Net.API | |||
| { | |||
| internal static class Responses | |||
| { | |||
| //Auth | |||
| public sealed class Gateway | |||
| { | |||
| [JsonProperty(PropertyName = "url")] | |||
| public string Url; | |||
| } | |||
| public sealed class AuthFingerprint | |||
| { | |||
| [JsonProperty(PropertyName = "fingerprint")] | |||
| public string Fingerprint; | |||
| } | |||
| public sealed class AuthRegister | |||
| { | |||
| [JsonProperty(PropertyName = "token")] | |||
| public string Token; | |||
| } | |||
| public sealed class AuthLogin | |||
| { | |||
| [JsonProperty(PropertyName = "token")] | |||
| public string Token; | |||
| } | |||
| //Users | |||
| public sealed class ChangeProfile : SelfUserInfo { } | |||
| //Servers | |||
| public sealed class CreateServer : GuildInfo { } | |||
| public sealed class DeleteServer : GuildInfo { } | |||
| //Channels | |||
| public sealed class CreateChannel : ChannelInfo { } | |||
| public sealed class DestroyChannel : ChannelInfo { } | |||
| //Invites | |||
| public sealed class CreateInvite : ExtendedInvite { } | |||
| public sealed class GetInvite : Invite { } | |||
| public sealed class AcceptInvite : Invite { } | |||
| //Messages | |||
| public sealed class SendMessage : Message { } | |||
| public sealed class EditMessage : Message { } | |||
| public sealed class GetMessages : Message { } | |||
| //Voice | |||
| public sealed class GetRegions | |||
| { | |||
| [JsonProperty(PropertyName = "sample_hostname")] | |||
| public string Hostname; | |||
| [JsonProperty(PropertyName = "sample_port")] | |||
| public int Port; | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string Id; | |||
| [JsonProperty(PropertyName = "name")] | |||
| public string Name; | |||
| } | |||
| public sealed class GetIce | |||
| { | |||
| [JsonProperty(PropertyName = "ttl")] | |||
| public string TTL; | |||
| [JsonProperty(PropertyName = "servers")] | |||
| public Server[] Servers; | |||
| public sealed class Server | |||
| { | |||
| [JsonProperty(PropertyName = "url")] | |||
| public string URL; | |||
| [JsonProperty(PropertyName = "username")] | |||
| public string Username; | |||
| [JsonProperty(PropertyName = "credential")] | |||
| public string Credential; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,71 +0,0 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json.Linq; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| namespace Discord.Net.WebSockets | |||
| { | |||
| internal static class Commands | |||
| { | |||
| public sealed class KeepAlive : WebSocketMessage<ulong> | |||
| { | |||
| public KeepAlive() : base(1, GetTimestamp()) { } | |||
| private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | |||
| private static ulong GetTimestamp() => (ulong)(DateTime.UtcNow - epoch).TotalMilliseconds; | |||
| } | |||
| public sealed class Login : WebSocketMessage<Login.Data> | |||
| { | |||
| public Login() : base(2) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty(PropertyName = "token")] | |||
| public string Token; | |||
| [JsonProperty(PropertyName = "v")] | |||
| public int Version = 3; | |||
| [JsonProperty(PropertyName = "properties")] | |||
| public Dictionary<string, string> Properties = new Dictionary<string, string>(); | |||
| } | |||
| } | |||
| public sealed class UpdateStatus : WebSocketMessage<UpdateStatus.Data> | |||
| { | |||
| public UpdateStatus() : base(3) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty(PropertyName = "idle_since")] | |||
| public string IdleSince; | |||
| [JsonProperty(PropertyName = "game_id")] | |||
| public string GameId; | |||
| } | |||
| } | |||
| public sealed class JoinVoice : WebSocketMessage<JoinVoice.Data> | |||
| { | |||
| public JoinVoice() : base(4) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty(PropertyName = "guild_id")] | |||
| public string ServerId; | |||
| [JsonProperty(PropertyName = "channel_id")] | |||
| public string ChannelId; | |||
| [JsonProperty(PropertyName = "self_mute")] | |||
| public string SelfMute; | |||
| [JsonProperty(PropertyName = "self_deaf")] | |||
| public string SelfDeaf; | |||
| } | |||
| } | |||
| public sealed class Resume : WebSocketMessage<Resume.Data> | |||
| { | |||
| public Resume() : base(6) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty(PropertyName = "session_id")] | |||
| public string SessionId; | |||
| [JsonProperty(PropertyName = "seq")] | |||
| public int Sequence; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,118 +0,0 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Discord.Net.API; | |||
| using Newtonsoft.Json; | |||
| namespace Discord.Net.WebSockets | |||
| { | |||
| internal static class Events | |||
| { | |||
| public sealed class Ready | |||
| { | |||
| public sealed class ReadStateInfo | |||
| { | |||
| [JsonProperty(PropertyName = "id")] | |||
| public string ChannelId; | |||
| [JsonProperty(PropertyName = "mention_count")] | |||
| public int MentionCount; | |||
| [JsonProperty(PropertyName = "last_message_id")] | |||
| public string LastMessageId; | |||
| } | |||
| [JsonProperty(PropertyName = "v")] | |||
| public int Version; | |||
| [JsonProperty(PropertyName = "user")] | |||
| public SelfUserInfo User; | |||
| [JsonProperty(PropertyName = "session_id")] | |||
| public string SessionId; | |||
| [JsonProperty(PropertyName = "read_state")] | |||
| public ReadStateInfo[] ReadState; | |||
| [JsonProperty(PropertyName = "guilds")] | |||
| public ExtendedGuildInfo[] Guilds; | |||
| [JsonProperty(PropertyName = "private_channels")] | |||
| public ChannelInfo[] PrivateChannels; | |||
| [JsonProperty(PropertyName = "heartbeat_interval")] | |||
| public int HeartbeatInterval; | |||
| } | |||
| public sealed class Resumed | |||
| { | |||
| [JsonProperty(PropertyName = "heartbeat_interval")] | |||
| public int HeartbeatInterval; | |||
| } | |||
| public sealed class Redirect | |||
| { | |||
| [JsonProperty(PropertyName = "url")] | |||
| public string Url; | |||
| } | |||
| //Servers | |||
| public sealed class GuildCreate : ExtendedGuildInfo { } | |||
| public sealed class GuildUpdate : GuildInfo { } | |||
| public sealed class GuildDelete : ExtendedGuildInfo { } | |||
| //Channels | |||
| public sealed class ChannelCreate : ChannelInfo { } | |||
| public sealed class ChannelDelete : ChannelInfo { } | |||
| public sealed class ChannelUpdate : ChannelInfo { } | |||
| //Memberships | |||
| public sealed class GuildMemberAdd : MemberInfo { } | |||
| public sealed class GuildMemberUpdate : MemberInfo { } | |||
| public sealed class GuildMemberRemove : MemberInfo { } | |||
| //Roles | |||
| public sealed class GuildRoleCreate | |||
| { | |||
| [JsonProperty(PropertyName = "guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty(PropertyName = "role")] | |||
| public RoleInfo Data; | |||
| } | |||
| public sealed class GuildRoleUpdate | |||
| { | |||
| [JsonProperty(PropertyName = "guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty(PropertyName = "role")] | |||
| public RoleInfo Data; | |||
| } | |||
| public sealed class GuildRoleDelete : RoleReference { } | |||
| //Bans | |||
| public sealed class GuildBanAdd : MemberReference { } | |||
| public sealed class GuildBanRemove : MemberReference { } | |||
| //User | |||
| public sealed class UserUpdate : SelfUserInfo { } | |||
| public sealed class PresenceUpdate : PresenceMemberInfo { } | |||
| public sealed class VoiceStateUpdate : VoiceMemberInfo { } | |||
| //Chat | |||
| public sealed class MessageCreate : API.Message { } | |||
| public sealed class MessageUpdate : API.Message { } | |||
| public sealed class MessageDelete : MessageReference { } | |||
| public sealed class MessageAck : MessageReference { } | |||
| public sealed class TypingStart | |||
| { | |||
| [JsonProperty(PropertyName = "user_id")] | |||
| public string UserId; | |||
| [JsonProperty(PropertyName = "channel_id")] | |||
| public string ChannelId; | |||
| [JsonProperty(PropertyName = "timestamp")] | |||
| public int Timestamp; | |||
| } | |||
| //Voice | |||
| public sealed class VoiceServerUpdate | |||
| { | |||
| [JsonProperty(PropertyName = "guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty(PropertyName = "endpoint")] | |||
| public string Endpoint; | |||
| [JsonProperty(PropertyName = "token")] | |||
| public string Token; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,62 +0,0 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| namespace Discord.Net.WebSockets | |||
| { | |||
| internal static class VoiceCommands | |||
| { | |||
| public sealed class Login : WebSocketMessage<Login.Data> | |||
| { | |||
| public Login() : base(0) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty(PropertyName = "server_id")] | |||
| public string ServerId; | |||
| [JsonProperty(PropertyName = "user_id")] | |||
| public string UserId; | |||
| [JsonProperty(PropertyName = "session_id")] | |||
| public string SessionId; | |||
| [JsonProperty(PropertyName = "token")] | |||
| public string Token; | |||
| } | |||
| } | |||
| public sealed class Login2 : WebSocketMessage<Login2.Data> | |||
| { | |||
| public Login2() : base(1) { } | |||
| public class Data | |||
| { | |||
| public class SocketInfo | |||
| { | |||
| [JsonProperty(PropertyName = "address")] | |||
| public string Address; | |||
| [JsonProperty(PropertyName = "port")] | |||
| public int Port; | |||
| [JsonProperty(PropertyName = "mode")] | |||
| public string Mode = "xsalsa20_poly1305"; | |||
| } | |||
| [JsonProperty(PropertyName = "protocol")] | |||
| public string Protocol = "udp"; | |||
| [JsonProperty(PropertyName = "data")] | |||
| public SocketInfo SocketData = new SocketInfo(); | |||
| } | |||
| } | |||
| public sealed class KeepAlive : WebSocketMessage<object> | |||
| { | |||
| public KeepAlive() : base(3, null) { } | |||
| } | |||
| public sealed class IsTalking : WebSocketMessage<IsTalking.Data> | |||
| { | |||
| public IsTalking() : base(5) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty(PropertyName = "delay")] | |||
| public int Delay; | |||
| [JsonProperty(PropertyName = "speaking")] | |||
| public bool IsSpeaking; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,41 +0,0 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| namespace Discord.Net.WebSockets | |||
| { | |||
| internal static class VoiceEvents | |||
| { | |||
| public sealed class Ready | |||
| { | |||
| [JsonProperty(PropertyName = "ssrc")] | |||
| public uint SSRC; | |||
| [JsonProperty(PropertyName = "port")] | |||
| public ushort Port; | |||
| [JsonProperty(PropertyName = "modes")] | |||
| public string[] Modes; | |||
| [JsonProperty(PropertyName = "heartbeat_interval")] | |||
| public int HeartbeatInterval; | |||
| } | |||
| public sealed class JoinServer | |||
| { | |||
| [JsonProperty(PropertyName = "secret_key")] | |||
| public byte[] SecretKey; | |||
| [JsonProperty(PropertyName = "mode")] | |||
| public string Mode; | |||
| } | |||
| public sealed class IsTalking | |||
| { | |||
| [JsonProperty(PropertyName = "user_id")] | |||
| public string UserId; | |||
| [JsonProperty(PropertyName = "ssrc")] | |||
| public uint SSRC; | |||
| [JsonProperty(PropertyName = "speaking")] | |||
| public bool IsSpeaking; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,8 +1,4 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| @@ -0,0 +1,67 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| namespace Discord.WebSockets.Data | |||
| { | |||
| internal sealed class KeepAliveCommand : WebSocketMessage<ulong> | |||
| { | |||
| public KeepAliveCommand() : base(1, GetTimestamp()) { } | |||
| private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | |||
| private static ulong GetTimestamp() => (ulong)(DateTime.UtcNow - epoch).TotalMilliseconds; | |||
| } | |||
| internal sealed class LoginCommand : WebSocketMessage<LoginCommand.Data> | |||
| { | |||
| public LoginCommand() : base(2) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty("token")] | |||
| public string Token; | |||
| [JsonProperty("v")] | |||
| public int Version = 3; | |||
| [JsonProperty("properties")] | |||
| public Dictionary<string, string> Properties = new Dictionary<string, string>(); | |||
| } | |||
| } | |||
| internal sealed class UpdateStatusCommand : WebSocketMessage<UpdateStatusCommand.Data> | |||
| { | |||
| public UpdateStatusCommand() : base(3) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty("idle_since")] | |||
| public string IdleSince; | |||
| [JsonProperty("game_id")] | |||
| public string GameId; | |||
| } | |||
| } | |||
| internal sealed class JoinVoiceCommand : WebSocketMessage<JoinVoiceCommand.Data> | |||
| { | |||
| public JoinVoiceCommand() : base(4) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public string ServerId; | |||
| [JsonProperty("channel_id")] | |||
| public string ChannelId; | |||
| [JsonProperty("self_mute")] | |||
| public string SelfMute; | |||
| [JsonProperty("self_deaf")] | |||
| public string SelfDeaf; | |||
| } | |||
| } | |||
| internal sealed class ResumeCommand : WebSocketMessage<ResumeCommand.Data> | |||
| { | |||
| public ResumeCommand() : base(6) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty("session_id")] | |||
| public string SessionId; | |||
| [JsonProperty("seq")] | |||
| public int Sequence; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,10 +1,9 @@ | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json.Linq; | |||
| using System; | |||
| using System.Threading; | |||
| using System.Threading.Tasks; | |||
| namespace Discord.Net.WebSockets | |||
| namespace Discord.WebSockets.Data | |||
| { | |||
| internal partial class DataWebSocket : WebSocket | |||
| { | |||
| @@ -22,7 +21,7 @@ namespace Discord.Net.WebSockets | |||
| { | |||
| await Connect().ConfigureAwait(false); | |||
| Commands.Login msg = new Commands.Login(); | |||
| LoginCommand msg = new LoginCommand(); | |||
| msg.Payload.Token = token; | |||
| msg.Payload.Properties["$device"] = "Discord.Net"; | |||
| QueueMessage(msg); | |||
| @@ -32,7 +31,7 @@ namespace Discord.Net.WebSockets | |||
| await DisconnectInternal(isUnexpected: false).ConfigureAwait(false); | |||
| await Connect().ConfigureAwait(false); | |||
| var resumeMsg = new Commands.Resume(); | |||
| var resumeMsg = new ResumeCommand(); | |||
| resumeMsg.Payload.SessionId = _sessionId; | |||
| resumeMsg.Payload.Sequence = _lastSeq; | |||
| QueueMessage(resumeMsg); | |||
| @@ -75,16 +74,16 @@ namespace Discord.Net.WebSockets | |||
| JToken token = msg.Payload as JToken; | |||
| if (msg.Type == "READY") | |||
| { | |||
| var payload = token.ToObject<Events.Ready>(); | |||
| var payload = token.ToObject<ReadyEvent>(); | |||
| _sessionId = payload.SessionId; | |||
| _heartbeatInterval = payload.HeartbeatInterval; | |||
| QueueMessage(new Commands.UpdateStatus()); | |||
| QueueMessage(new UpdateStatusCommand()); | |||
| } | |||
| else if (msg.Type == "RESUMED") | |||
| { | |||
| var payload = token.ToObject<Events.Resumed>(); | |||
| var payload = token.ToObject<ResumedEvent>(); | |||
| _heartbeatInterval = payload.HeartbeatInterval; | |||
| QueueMessage(new Commands.UpdateStatus()); | |||
| QueueMessage(new UpdateStatusCommand()); | |||
| } | |||
| RaiseReceivedEvent(msg.Type, token); | |||
| if (msg.Type == "READY" || msg.Type == "RESUMED") | |||
| @@ -93,7 +92,7 @@ namespace Discord.Net.WebSockets | |||
| break; | |||
| case 7: //Redirect | |||
| { | |||
| var payload = (msg.Payload as JToken).ToObject<Events.Redirect>(); | |||
| var payload = (msg.Payload as JToken).ToObject<RedirectEvent>(); | |||
| Host = payload.Url; | |||
| if (_logLevel >= LogMessageSeverity.Info) | |||
| RaiseOnLog(LogMessageSeverity.Info, "Redirected to " + payload.Url); | |||
| @@ -109,19 +108,19 @@ namespace Discord.Net.WebSockets | |||
| protected override object GetKeepAlive() | |||
| { | |||
| return new Commands.KeepAlive(); | |||
| return new KeepAliveCommand(); | |||
| } | |||
| public void SendJoinVoice(string serverId, string channelId) | |||
| { | |||
| var joinVoice = new Commands.JoinVoice(); | |||
| var joinVoice = new JoinVoiceCommand(); | |||
| joinVoice.Payload.ServerId = serverId; | |||
| joinVoice.Payload.ChannelId = channelId; | |||
| QueueMessage(joinVoice); | |||
| } | |||
| public void SendLeaveVoice(string serverId) | |||
| { | |||
| var leaveVoice = new Commands.JoinVoice(); | |||
| var leaveVoice = new JoinVoiceCommand(); | |||
| leaveVoice.Payload.ServerId = serverId; | |||
| QueueMessage(leaveVoice); | |||
| } | |||
| @@ -1,9 +1,9 @@ | |||
| using Newtonsoft.Json.Linq; | |||
| using System; | |||
| namespace Discord.Net.WebSockets | |||
| namespace Discord.WebSockets.Data | |||
| { | |||
| public sealed class WebSocketEventEventArgs : EventArgs | |||
| internal sealed class WebSocketEventEventArgs : EventArgs | |||
| { | |||
| public readonly string Type; | |||
| public readonly JToken Payload; | |||
| @@ -0,0 +1,115 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Discord.API; | |||
| using Newtonsoft.Json; | |||
| namespace Discord.WebSockets.Data | |||
| { | |||
| internal sealed class ReadyEvent | |||
| { | |||
| public sealed class ReadStateInfo | |||
| { | |||
| [JsonProperty("id")] | |||
| public string ChannelId; | |||
| [JsonProperty("mention_count")] | |||
| public int MentionCount; | |||
| [JsonProperty("last_message_id")] | |||
| public string LastMessageId; | |||
| } | |||
| [JsonProperty("v")] | |||
| public int Version; | |||
| [JsonProperty("user")] | |||
| public SelfUserInfo User; | |||
| [JsonProperty("session_id")] | |||
| public string SessionId; | |||
| [JsonProperty("read_state")] | |||
| public ReadStateInfo[] ReadState; | |||
| [JsonProperty("guilds")] | |||
| public ExtendedGuildInfo[] Guilds; | |||
| [JsonProperty("private_channels")] | |||
| public ChannelInfo[] PrivateChannels; | |||
| [JsonProperty("heartbeat_interval")] | |||
| public int HeartbeatInterval; | |||
| } | |||
| internal sealed class ResumedEvent | |||
| { | |||
| [JsonProperty("heartbeat_interval")] | |||
| public int HeartbeatInterval; | |||
| } | |||
| internal sealed class RedirectEvent | |||
| { | |||
| [JsonProperty("url")] | |||
| public string Url; | |||
| } | |||
| //Servers | |||
| internal sealed class GuildCreateEvent : ExtendedGuildInfo { } | |||
| internal sealed class GuildUpdateEvent : GuildInfo { } | |||
| internal sealed class GuildDeleteEvent : ExtendedGuildInfo { } | |||
| //Channels | |||
| internal sealed class ChannelCreateEvent : ChannelInfo { } | |||
| internal sealed class ChannelDeleteEvent : ChannelInfo { } | |||
| internal sealed class ChannelUpdateEvent : ChannelInfo { } | |||
| //Memberships | |||
| internal sealed class GuildMemberAddEvent : MemberInfo { } | |||
| internal sealed class GuildMemberUpdateEvent : MemberInfo { } | |||
| internal sealed class GuildMemberRemoveEvent : MemberInfo { } | |||
| //Roles | |||
| internal sealed class GuildRoleCreateEvent | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty("role")] | |||
| public RoleInfo Data; | |||
| } | |||
| internal sealed class GuildRoleUpdateEvent | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty("role")] | |||
| public RoleInfo Data; | |||
| } | |||
| internal sealed class GuildRoleDeleteEvent : RoleReference { } | |||
| //Bans | |||
| internal sealed class GuildBanAddEvent : MemberReference { } | |||
| internal sealed class GuildBanRemoveEvent : MemberReference { } | |||
| //User | |||
| internal sealed class UserUpdateEvent : SelfUserInfo { } | |||
| internal sealed class PresenceUpdateEvent : PresenceMemberInfo { } | |||
| internal sealed class VoiceStateUpdateEvent : VoiceMemberInfo { } | |||
| //Chat | |||
| internal sealed class MessageCreateEvent : API.Message { } | |||
| internal sealed class MessageUpdateEvent : API.Message { } | |||
| internal sealed class MessageDeleteEvent : MessageReference { } | |||
| internal sealed class MessageAckEvent : MessageReference { } | |||
| internal sealed class TypingStartEvent | |||
| { | |||
| [JsonProperty("user_id")] | |||
| public string UserId; | |||
| [JsonProperty("channel_id")] | |||
| public string ChannelId; | |||
| [JsonProperty("timestamp")] | |||
| public int Timestamp; | |||
| } | |||
| //Voice | |||
| internal sealed class VoiceServerUpdateEvent | |||
| { | |||
| [JsonProperty("guild_id")] | |||
| public string GuildId; | |||
| [JsonProperty("endpoint")] | |||
| public string Endpoint; | |||
| [JsonProperty("token")] | |||
| public string Token; | |||
| } | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| namespace Discord.WebSockets.Voice | |||
| { | |||
| internal sealed class LoginCommand : WebSocketMessage<LoginCommand.Data> | |||
| { | |||
| public LoginCommand() : base(0) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty("server_id")] | |||
| public string ServerId; | |||
| [JsonProperty("user_id")] | |||
| public string UserId; | |||
| [JsonProperty("session_id")] | |||
| public string SessionId; | |||
| [JsonProperty("token")] | |||
| public string Token; | |||
| } | |||
| } | |||
| internal sealed class Login2Command : WebSocketMessage<Login2Command.Data> | |||
| { | |||
| public Login2Command() : base(1) { } | |||
| public class Data | |||
| { | |||
| public class SocketInfo | |||
| { | |||
| [JsonProperty("address")] | |||
| public string Address; | |||
| [JsonProperty("port")] | |||
| public int Port; | |||
| [JsonProperty("mode")] | |||
| public string Mode = "xsalsa20_poly1305"; | |||
| } | |||
| [JsonProperty("protocol")] | |||
| public string Protocol = "udp"; | |||
| [JsonProperty("data")] | |||
| public SocketInfo SocketData = new SocketInfo(); | |||
| } | |||
| } | |||
| internal sealed class KeepAliveCommand : WebSocketMessage<object> | |||
| { | |||
| public KeepAliveCommand() : base(3, null) { } | |||
| } | |||
| internal sealed class IsTalkingCommand : WebSocketMessage<IsTalkingCommand.Data> | |||
| { | |||
| public IsTalkingCommand() : base(5) { } | |||
| public class Data | |||
| { | |||
| [JsonProperty("delay")] | |||
| public int Delay; | |||
| [JsonProperty("speaking")] | |||
| public bool IsSpeaking; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| //Ignore unused/unassigned variable warnings | |||
| #pragma warning disable CS0649 | |||
| #pragma warning disable CS0169 | |||
| using Newtonsoft.Json; | |||
| namespace Discord.WebSockets.Voice | |||
| { | |||
| internal sealed class ReadyEvent | |||
| { | |||
| [JsonProperty("ssrc")] | |||
| public uint SSRC; | |||
| [JsonProperty("port")] | |||
| public ushort Port; | |||
| [JsonProperty("modes")] | |||
| public string[] Modes; | |||
| [JsonProperty("heartbeat_interval")] | |||
| public int HeartbeatInterval; | |||
| } | |||
| internal sealed class JoinServerEvent | |||
| { | |||
| [JsonProperty("secret_key")] | |||
| public byte[] SecretKey; | |||
| [JsonProperty("mode")] | |||
| public string Mode; | |||
| } | |||
| internal sealed class IsTalkingEvent | |||
| { | |||
| [JsonProperty("user_id")] | |||
| public string UserId; | |||
| [JsonProperty("ssrc")] | |||
| public uint SSRC; | |||
| [JsonProperty("speaking")] | |||
| public bool IsSpeaking; | |||
| } | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| using System; | |||
| namespace Discord.Net.WebSockets | |||
| namespace Discord.WebSockets.Voice | |||
| { | |||
| public sealed class IsTalkingEventArgs : EventArgs | |||
| { | |||
| @@ -12,7 +12,7 @@ using System.Text; | |||
| using System.Threading; | |||
| using System.Threading.Tasks; | |||
| namespace Discord.Net.WebSockets | |||
| namespace Discord.WebSockets.Voice | |||
| { | |||
| internal partial class VoiceWebSocket : WebSocket | |||
| { | |||
| @@ -106,7 +106,7 @@ namespace Discord.Net.WebSockets | |||
| _udp.AllowNatTraversal(true); | |||
| #endif | |||
| VoiceCommands.Login msg = new VoiceCommands.Login(); | |||
| LoginCommand msg = new LoginCommand(); | |||
| msg.Payload.ServerId = _serverId; | |||
| msg.Payload.SessionId = _sessionId; | |||
| msg.Payload.Token = _token; | |||
| @@ -294,7 +294,7 @@ namespace Discord.Net.WebSockets | |||
| { | |||
| if (_state != (int)WebSocketState.Connected) | |||
| { | |||
| var payload = (msg.Payload as JToken).ToObject<VoiceEvents.Ready>(); | |||
| var payload = (msg.Payload as JToken).ToObject<ReadyEvent>(); | |||
| _heartbeatInterval = payload.HeartbeatInterval; | |||
| _ssrc = payload.SSRC; | |||
| _endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(Host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port); | |||
| @@ -319,7 +319,7 @@ namespace Discord.Net.WebSockets | |||
| break; | |||
| case 4: //SESSION_DESCRIPTION | |||
| { | |||
| var payload = (msg.Payload as JToken).ToObject<VoiceEvents.JoinServer>(); | |||
| var payload = (msg.Payload as JToken).ToObject<JoinServerEvent>(); | |||
| _secretKey = payload.SecretKey; | |||
| SendIsTalking(true); | |||
| _connectWaitOnLogin.Set(); | |||
| @@ -327,7 +327,7 @@ namespace Discord.Net.WebSockets | |||
| break; | |||
| case 5: | |||
| { | |||
| var payload = (msg.Payload as JToken).ToObject<VoiceEvents.IsTalking>(); | |||
| var payload = (msg.Payload as JToken).ToObject<IsTalkingEvent>(); | |||
| RaiseIsSpeaking(payload.UserId, payload.IsSpeaking); | |||
| } | |||
| break; | |||
| @@ -358,7 +358,7 @@ namespace Discord.Net.WebSockets | |||
| CompleteConnect(); | |||
| var login2 = new VoiceCommands.Login2(); | |||
| var login2 = new Login2Command(); | |||
| login2.Payload.Protocol = "udp"; | |||
| login2.Payload.SocketData.Address = ip; | |||
| login2.Payload.SocketData.Mode = _isEncrypted ? EncryptedMode : UnencryptedMode; | |||
| @@ -503,7 +503,7 @@ namespace Discord.Net.WebSockets | |||
| private void SendIsTalking(bool value) | |||
| { | |||
| var isTalking = new VoiceCommands.IsTalking(); | |||
| var isTalking = new IsTalkingCommand(); | |||
| isTalking.Payload.IsSpeaking = value; | |||
| isTalking.Payload.Delay = 0; | |||
| QueueMessage(isTalking); | |||
| @@ -511,7 +511,7 @@ namespace Discord.Net.WebSockets | |||
| protected override object GetKeepAlive() | |||
| { | |||
| return new VoiceCommands.KeepAlive(); | |||
| return new KeepAliveCommand(); | |||
| } | |||
| public void WaitForQueue() | |||
| @@ -8,7 +8,7 @@ using System.Threading; | |||
| using System.Threading.Tasks; | |||
| using State = System.Net.WebSockets.WebSocketState; | |||
| namespace Discord.Net.WebSockets | |||
| namespace Discord.WebSockets | |||
| { | |||
| internal class BuiltInWebSocketEngine : IWebSocketEngine | |||
| { | |||
| @@ -1,6 +1,6 @@ | |||
| using System; | |||
| namespace Discord.Net.WebSockets | |||
| namespace Discord.WebSockets | |||
| { | |||
| internal partial class WebSocket | |||
| { | |||
| @@ -7,7 +7,7 @@ using System.Text; | |||
| using System.Threading; | |||
| using System.Threading.Tasks; | |||
| namespace Discord.Net.WebSockets | |||
| namespace Discord.WebSockets | |||
| { | |||
| public enum WebSocketState : byte | |||
| { | |||
| @@ -88,7 +88,7 @@ namespace Discord.Net.WebSockets | |||
| _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken).Token; | |||
| else | |||
| _cancelToken = _cancelTokenSource.Token; | |||
| await _engine.Connect(Host, _cancelToken).ConfigureAwait(false); | |||
| _lastHeartbeat = DateTime.UtcNow; | |||
| @@ -107,8 +107,6 @@ namespace Discord.Net.WebSockets | |||
| _connectedEvent.Set(); | |||
| RaiseConnected(); | |||
| } | |||
| /*public Task Reconnect(CancellationToken cancelToken) | |||
| => Connect(_host, _cancelToken);*/ | |||
| public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false); | |||
| protected async Task DisconnectInternal(Exception ex = null, bool isUnexpected = true, bool skipAwait = false) | |||
| @@ -1,17 +1,17 @@ | |||
| using Newtonsoft.Json; | |||
| using Newtonsoft.Json.Linq; | |||
| namespace Discord.Net.WebSockets | |||
| namespace Discord.WebSockets | |||
| { | |||
| public class WebSocketMessage | |||
| { | |||
| [JsonProperty(PropertyName = "op")] | |||
| [JsonProperty("op")] | |||
| public int Operation; | |||
| [JsonProperty(PropertyName = "d")] | |||
| [JsonProperty("d")] | |||
| public object Payload; | |||
| [JsonProperty(PropertyName = "t", NullValueHandling = NullValueHandling.Ignore)] | |||
| [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)] | |||
| public string Type; | |||
| [JsonProperty(PropertyName = "s", NullValueHandling = NullValueHandling.Ignore)] | |||
| [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)] | |||
| public int? Sequence; | |||
| } | |||
| internal abstract class WebSocketMessage<T> : WebSocketMessage | |||