From 16c67e79e99c63671010170ba7dcebdde773f7da Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 8 Oct 2016 02:37:04 -0300 Subject: [PATCH] Added RPC channel and guild entities, Get and Select methods --- .../Net/WebSockets/DefaultWebSocketClient.cs | 3 + .../API/DiscordRpcApiClient.cs | 37 +++--- src/Discord.Net.Rpc/API/Rpc/Channel.cs | 34 +++++ ...annelCreatedEvent.cs => ChannelSummary.cs} | 2 +- ...iceStateEvent.cs => ExtendedVoiceState.cs} | 2 +- .../API/Rpc/GetChannelsResponse.cs | 3 +- .../API/Rpc/GetGuildsParams.cs | 1 + .../API/Rpc/GetGuildsResponse.cs | 2 +- .../API/Rpc/{RpcUserGuild.cs => Guild.cs} | 7 +- src/Discord.Net.Rpc/API/Rpc/GuildMember.cs | 15 +++ .../{GuildCreatedEvent.cs => GuildSummary.cs} | 2 +- .../API/Rpc/{RpcMessage.cs => Message.cs} | 2 +- src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs | 2 +- src/Discord.Net.Rpc/API/Rpc/RpcChannel.cs | 11 -- src/Discord.Net.Rpc/API/Rpc/RpcGuild.cs | 13 -- ...hannelParams.cs => SelectChannelParams.cs} | 4 +- .../API/Rpc/UserVoiceSettings.cs | 18 +++ .../DiscordRpcClient.Events.cs | 8 +- src/Discord.Net.Rpc/DiscordRpcClient.cs | 74 ++++++++++- .../Entities/Channels/IRpcAudioChannel.cs | 9 ++ .../Entities/Channels/IRpcMessageChannel.cs | 9 ++ .../Entities/Channels/IRpcPrivateChannel.cs | 6 + .../Entities/Channels/RpcChannel.cs | 46 ++++--- .../Entities/Channels/RpcChannelSummary.cs | 32 +++++ .../Entities/Channels/RpcDMChannel.cs | 124 ++++++++++++++++++ .../Entities/Channels/RpcGroupChannel.cs | 123 +++++++++++++++++ .../Entities/Channels/RpcGuildChannel.cs | 94 +++++++++++++ .../Entities/Channels/RpcTextChannel.cs | 113 ++++++++++++++++ .../Entities/Channels/RpcVoiceChannel.cs | 49 +++++++ .../Entities/Guilds/RpcGuild.cs | 24 ++-- .../Entities/Guilds/RpcGuildStatus.cs | 4 +- .../Entities/Guilds/RpcGuildSummary.cs | 30 +++++ .../Entities/Messages/RpcMessage.cs | 2 +- .../Entities/Messages/RpcSystemMessage.cs | 2 +- .../Entities/Messages/RpcUserMessage.cs | 2 +- .../Entities/Users/RpcGuildUser.cs | 29 ++++ src/Discord.Net.Rpc/Entities/Users/RpcUser.cs | 8 +- .../Entities/Users/RpcVoiceState.cs | 2 +- .../Entities/Channels/SocketChannel.cs | 1 - 39 files changed, 848 insertions(+), 101 deletions(-) create mode 100644 src/Discord.Net.Rpc/API/Rpc/Channel.cs rename src/Discord.Net.Rpc/API/Rpc/{ChannelCreatedEvent.cs => ChannelSummary.cs} (88%) rename src/Discord.Net.Rpc/API/Rpc/{VoiceStateEvent.cs => ExtendedVoiceState.cs} (94%) rename src/Discord.Net.Rpc/API/Rpc/{RpcUserGuild.cs => Guild.cs} (50%) create mode 100644 src/Discord.Net.Rpc/API/Rpc/GuildMember.cs rename src/Discord.Net.Rpc/API/Rpc/{GuildCreatedEvent.cs => GuildSummary.cs} (85%) rename src/Discord.Net.Rpc/API/Rpc/{RpcMessage.cs => Message.cs} (90%) delete mode 100644 src/Discord.Net.Rpc/API/Rpc/RpcChannel.cs delete mode 100644 src/Discord.Net.Rpc/API/Rpc/RpcGuild.cs rename src/Discord.Net.Rpc/API/Rpc/{SelectVoiceChannelParams.cs => SelectChannelParams.cs} (60%) create mode 100644 src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/IRpcAudioChannel.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/IRpcMessageChannel.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/IRpcPrivateChannel.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/RpcChannelSummary.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs create mode 100644 src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs create mode 100644 src/Discord.Net.Rpc/Entities/Guilds/RpcGuildSummary.cs create mode 100644 src/Discord.Net.Rpc/Entities/Users/RpcGuildUser.cs diff --git a/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs b/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs index 51464efd3..2e4a90ad8 100644 --- a/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs +++ b/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs @@ -154,6 +154,7 @@ namespace Discord.Net.WebSockets while (!cancelToken.IsCancellationRequested) { WebSocketReceiveResult socketResult = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false); + System.Diagnostics.Debug.WriteLine("Got " + socketResult.Count); byte[] result; int resultCount; @@ -193,6 +194,7 @@ namespace Discord.Net.WebSockets result = buffer.Array; } + System.Diagnostics.Debug.WriteLine("Start"); if (socketResult.MessageType == WebSocketMessageType.Text) { string text = Encoding.UTF8.GetString(result, 0, resultCount); @@ -200,6 +202,7 @@ namespace Discord.Net.WebSockets } else await BinaryMessage(result, 0, resultCount).ConfigureAwait(false); + System.Diagnostics.Debug.WriteLine("Stop"); } } catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT) diff --git a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs index 10fd054f5..67ead6f83 100644 --- a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs @@ -269,14 +269,14 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); return await SendRpcAsync("GET_GUILDS", null, options: options).ConfigureAwait(false); } - public async Task SendGetGuildAsync(ulong guildId, RequestOptions options = null) + public async Task SendGetGuildAsync(ulong guildId, RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); var msg = new GetGuildParams { GuildId = guildId }; - return await SendRpcAsync("GET_GUILD", msg, options: options).ConfigureAwait(false); + return await SendRpcAsync("GET_GUILD", msg, options: options).ConfigureAwait(false); } public async Task SendGetChannelsAsync(ulong guildId, RequestOptions options = null) { @@ -287,33 +287,34 @@ namespace Discord.API }; return await SendRpcAsync("GET_CHANNELS", msg, options: options).ConfigureAwait(false); } - public async Task SendGetChannelAsync(ulong channelId, RequestOptions options = null) + public async Task SendGetChannelAsync(ulong channelId, RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); var msg = new GetChannelParams { ChannelId = channelId }; - return await SendRpcAsync("GET_CHANNEL", msg, options: options).ConfigureAwait(false); + return await SendRpcAsync("GET_CHANNEL", msg, options: options).ConfigureAwait(false); } - - public async Task SendSetLocalVolumeAsync(int volume, RequestOptions options = null) + + public async Task SendSelectTextChannelAsync(ulong channelId, RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); - var msg = new SetLocalVolumeParams + var msg = new SelectChannelParams { - Volume = volume + ChannelId = channelId }; - return await SendRpcAsync("SET_LOCAL_VOLUME", msg, options: options).ConfigureAwait(false); + return await SendRpcAsync("SELECT_TEXT_CHANNEL", msg, options: options).ConfigureAwait(false); } - public async Task SendSelectVoiceChannelAsync(ulong channelId, RequestOptions options = null) + public async Task SendSelectVoiceChannelAsync(ulong channelId, bool force = false, RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); - var msg = new SelectVoiceChannelParams + var msg = new SelectChannelParams { - ChannelId = channelId + ChannelId = channelId, + Force = force }; - return await SendRpcAsync("SELECT_VOICE_CHANNEL", msg, options: options).ConfigureAwait(false); + return await SendRpcAsync("SELECT_VOICE_CHANNEL", msg, options: options).ConfigureAwait(false); } public async Task SendGlobalSubscribeAsync(string evt, RequestOptions options = null) @@ -370,10 +371,16 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); return await SendRpcAsync("GET_VOICE_SETTINGS", null, options: options).ConfigureAwait(false); } - public async Task SetVoiceSettingsAsync(API.Rpc.VoiceSettings settings, RequestOptions options = null) + public async Task SetVoiceSettingsAsync(API.Rpc.VoiceSettings settings, RequestOptions options = null) + { + options = RequestOptions.CreateOrClone(options); + await SendRpcAsync("SET_VOICE_SETTINGS", settings, options: options).ConfigureAwait(false); + } + public async Task SetUserVoiceSettingsAsync(ulong userId, API.Rpc.UserVoiceSettings settings, RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); - return await SendRpcAsync("SET_VOICE_SETTINGS", settings, options: options).ConfigureAwait(false); + settings.UserId = userId; + await SendRpcAsync("SET_USER_VOICE_SETTINGS", settings, options: options).ConfigureAwait(false); } private bool ProcessMessage(API.Rpc.RpcFrame msg) diff --git a/src/Discord.Net.Rpc/API/Rpc/Channel.cs b/src/Discord.Net.Rpc/API/Rpc/Channel.cs new file mode 100644 index 000000000..1b8f3775c --- /dev/null +++ b/src/Discord.Net.Rpc/API/Rpc/Channel.cs @@ -0,0 +1,34 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rpc +{ + public class Channel + { + //Shared + [JsonProperty("id")] + public ulong Id { get; set; } + [JsonProperty("type")] + public ChannelType Type { get; set; } + + //GuildChannel + [JsonProperty("guild_id")] + public Optional GuildId { get; set; } + [JsonProperty("name")] + public Optional Name { get; set; } + [JsonProperty("position")] + public Optional Position { get; set; } + + //IMessageChannel + [JsonProperty("messages")] + public Message[] Messages { get; set; } + + //VoiceChannel + [JsonProperty("bitrate")] + public Optional Bitrate { get; set; } + [JsonProperty("user_limit")] + public Optional UserLimit { get; set; } + [JsonProperty("voice_states")] + public ExtendedVoiceState[] VoiceStates { get; set; } + } +} diff --git a/src/Discord.Net.Rpc/API/Rpc/ChannelCreatedEvent.cs b/src/Discord.Net.Rpc/API/Rpc/ChannelSummary.cs similarity index 88% rename from src/Discord.Net.Rpc/API/Rpc/ChannelCreatedEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/ChannelSummary.cs index baa7caa6e..34acd049b 100644 --- a/src/Discord.Net.Rpc/API/Rpc/ChannelCreatedEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/ChannelSummary.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class ChannelCreatedEvent + public class ChannelSummary { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs b/src/Discord.Net.Rpc/API/Rpc/ExtendedVoiceState.cs similarity index 94% rename from src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/ExtendedVoiceState.cs index 11359fb8f..032914f0f 100644 --- a/src/Discord.Net.Rpc/API/Rpc/VoiceStateEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/ExtendedVoiceState.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class VoiceStateEvent + public class ExtendedVoiceState { [JsonProperty("user")] public User User { get; set; } diff --git a/src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs b/src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs index 5c428d7ba..e105341a1 100644 --- a/src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs @@ -1,11 +1,12 @@ #pragma warning disable CS1591 using Newtonsoft.Json; +using System.Collections.Generic; namespace Discord.API.Rpc { public class GetChannelsResponse { [JsonProperty("channels")] - public RpcChannel[] Channels { get; set; } + public IReadOnlyCollection Channels { get; set; } } } diff --git a/src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs index 9e45db44a..a1ff5f210 100644 --- a/src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 + namespace Discord.API.Rpc { public class GetGuildsParams diff --git a/src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs b/src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs index df7739709..e69bedeae 100644 --- a/src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs @@ -6,6 +6,6 @@ namespace Discord.API.Rpc public class GetGuildsResponse { [JsonProperty("guilds")] - public RpcUserGuild[] Guilds { get; set; } + public GuildSummary[] Guilds { get; set; } } } diff --git a/src/Discord.Net.Rpc/API/Rpc/RpcUserGuild.cs b/src/Discord.Net.Rpc/API/Rpc/Guild.cs similarity index 50% rename from src/Discord.Net.Rpc/API/Rpc/RpcUserGuild.cs rename to src/Discord.Net.Rpc/API/Rpc/Guild.cs index 4df8e1f7e..1d6bf3678 100644 --- a/src/Discord.Net.Rpc/API/Rpc/RpcUserGuild.cs +++ b/src/Discord.Net.Rpc/API/Rpc/Guild.cs @@ -1,13 +1,18 @@ #pragma warning disable CS1591 using Newtonsoft.Json; +using System.Collections.Generic; namespace Discord.API.Rpc { - public class RpcUserGuild + public class Guild { [JsonProperty("id")] public ulong Id { get; set; } [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("icon_url")] + public string IconUrl { get; set; } + [JsonProperty("members")] + public IEnumerable Members { get; set; } } } diff --git a/src/Discord.Net.Rpc/API/Rpc/GuildMember.cs b/src/Discord.Net.Rpc/API/Rpc/GuildMember.cs new file mode 100644 index 000000000..af74dd919 --- /dev/null +++ b/src/Discord.Net.Rpc/API/Rpc/GuildMember.cs @@ -0,0 +1,15 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rpc +{ + public class GuildMember + { + [JsonProperty("user")] + public User User { get; set; } + [JsonProperty("status")] + public UserStatus Status { get; set; } + /*[JsonProperty("activity")] + public object Activity { get; set; }*/ + } +} diff --git a/src/Discord.Net.Rpc/API/Rpc/GuildCreatedEvent.cs b/src/Discord.Net.Rpc/API/Rpc/GuildSummary.cs similarity index 85% rename from src/Discord.Net.Rpc/API/Rpc/GuildCreatedEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/GuildSummary.cs index 6cfaa862f..c36da5267 100644 --- a/src/Discord.Net.Rpc/API/Rpc/GuildCreatedEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GuildSummary.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class GuildCreatedEvent + public class GuildSummary { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.Rpc/API/Rpc/RpcMessage.cs b/src/Discord.Net.Rpc/API/Rpc/Message.cs similarity index 90% rename from src/Discord.Net.Rpc/API/Rpc/RpcMessage.cs rename to src/Discord.Net.Rpc/API/Rpc/Message.cs index 47c71bab0..a72fba123 100644 --- a/src/Discord.Net.Rpc/API/Rpc/RpcMessage.cs +++ b/src/Discord.Net.Rpc/API/Rpc/Message.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class RpcMessage : Message + public class Message : Discord.API.Message { [JsonProperty("blocked")] public Optional IsBlocked { get; } diff --git a/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs b/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs index 75b35ef56..41ff13288 100644 --- a/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs @@ -7,6 +7,6 @@ namespace Discord.API.Rpc [JsonProperty("channel_id")] public ulong ChannelId { get; set; } [JsonProperty("message")] - public RpcMessage Message { get; set; } + public Message Message { get; set; } } } diff --git a/src/Discord.Net.Rpc/API/Rpc/RpcChannel.cs b/src/Discord.Net.Rpc/API/Rpc/RpcChannel.cs deleted file mode 100644 index 074672a96..000000000 --- a/src/Discord.Net.Rpc/API/Rpc/RpcChannel.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; - -namespace Discord.API.Rpc -{ - public class RpcChannel : Channel - { - [JsonProperty("voice_states")] - public VoiceState[] VoiceStates { get; set; } - } -} diff --git a/src/Discord.Net.Rpc/API/Rpc/RpcGuild.cs b/src/Discord.Net.Rpc/API/Rpc/RpcGuild.cs deleted file mode 100644 index b470de2ba..000000000 --- a/src/Discord.Net.Rpc/API/Rpc/RpcGuild.cs +++ /dev/null @@ -1,13 +0,0 @@ -#pragma warning disable CS1591 -using Newtonsoft.Json; - -namespace Discord.API.Rpc -{ - public class RpcGuild : Guild - { - [JsonProperty("online")] - public int Online { get; set; } - [JsonProperty("members")] - public GuildMember[] Members { get; set; } - } -} diff --git a/src/Discord.Net.Rpc/API/Rpc/SelectVoiceChannelParams.cs b/src/Discord.Net.Rpc/API/Rpc/SelectChannelParams.cs similarity index 60% rename from src/Discord.Net.Rpc/API/Rpc/SelectVoiceChannelParams.cs rename to src/Discord.Net.Rpc/API/Rpc/SelectChannelParams.cs index e7f980c32..52c9b00e8 100644 --- a/src/Discord.Net.Rpc/API/Rpc/SelectVoiceChannelParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/SelectChannelParams.cs @@ -3,9 +3,11 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class SelectVoiceChannelParams + public class SelectChannelParams { [JsonProperty("channel_id")] public ulong? ChannelId { get; set; } + [JsonProperty("force")] + public Optional Force { get; set; } } } diff --git a/src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs b/src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs new file mode 100644 index 000000000..9c876a66f --- /dev/null +++ b/src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs @@ -0,0 +1,18 @@ +#pragma warning disable CS1591 + +using Newtonsoft.Json; + +namespace Discord.API.Rpc +{ + public class UserVoiceSettings + { + [JsonProperty("userId")] + internal ulong UserId { get; set; } + [JsonProperty("pan")] + public Optional Pan { get; set; } + [JsonProperty("volume")] + public Optional Volume { get; set; } + [JsonProperty("mute")] + public Optional Mute { get; set; } + } +} diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs index a966ef149..2a9ae21bf 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs @@ -26,20 +26,20 @@ namespace Discord.Rpc private readonly AsyncEvent> _readyEvent = new AsyncEvent>(); //Channel - public event Func ChannelCreated + public event Func ChannelCreated { add { _channelCreatedEvent.Add(value); } remove { _channelCreatedEvent.Remove(value); } } - private readonly AsyncEvent> _channelCreatedEvent = new AsyncEvent>(); + private readonly AsyncEvent> _channelCreatedEvent = new AsyncEvent>(); //Guild - public event Func GuildCreated + public event Func GuildCreated { add { _guildCreatedEvent.Add(value); } remove { _guildCreatedEvent.Remove(value); } } - private readonly AsyncEvent> _guildCreatedEvent = new AsyncEvent>(); + private readonly AsyncEvent> _guildCreatedEvent = new AsyncEvent>(); public event Func GuildStatusUpdated { add { _guildStatusUpdatedEvent.Add(value); } diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 708e18878..fc8aff6a4 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -265,6 +266,59 @@ namespace Discord.Rpc await ApiClient.SendChannelUnsubscribeAsync(GetEventName(events[i]), channelId); } + public async Task GetRpcGuildAsync(ulong id) + { + var model = await ApiClient.SendGetGuildAsync(id).ConfigureAwait(false); + return RpcGuild.Create(this, model); + } + public async Task> GetRpcGuildsAsync() + { + var models = await ApiClient.SendGetGuildsAsync().ConfigureAwait(false); + return models.Guilds.Select(x => RpcGuildSummary.Create(x)).ToImmutableArray(); + } + public async Task GetRpcChannelAsync(ulong id) + { + var model = await ApiClient.SendGetChannelAsync(id).ConfigureAwait(false); + return RpcChannel.Create(this, model); + } + public async Task> GetRpcChannelsAsync(ulong guildId) + { + var models = await ApiClient.SendGetChannelsAsync(guildId).ConfigureAwait(false); + return models.Channels.Select(x => RpcChannelSummary.Create(x)).ToImmutableArray(); + } + + public async Task SelectTextChannelAsync(IChannel channel) + { + var model = await ApiClient.SendSelectTextChannelAsync(channel.Id).ConfigureAwait(false); + return RpcChannel.Create(this, model) as IMessageChannel; + } + public async Task SelectTextChannelAsync(RpcChannelSummary channel) + { + var model = await ApiClient.SendSelectTextChannelAsync(channel.Id).ConfigureAwait(false); + return RpcChannel.Create(this, model) as IMessageChannel; + } + public async Task SelectTextChannelAsync(ulong channelId) + { + var model = await ApiClient.SendSelectTextChannelAsync(channelId).ConfigureAwait(false); + return RpcChannel.Create(this, model) as IMessageChannel; + } + + public async Task SelectVoiceChannelAsync(IChannel channel, bool force = false) + { + var model = await ApiClient.SendSelectVoiceChannelAsync(channel.Id, force).ConfigureAwait(false); + return RpcChannel.Create(this, model) as IRpcAudioChannel; + } + public async Task SelectVoiceChannelAsync(RpcChannelSummary channel, bool force = false) + { + var model = await ApiClient.SendSelectVoiceChannelAsync(channel.Id, force).ConfigureAwait(false); + return RpcChannel.Create(this, model) as IRpcAudioChannel; + } + public async Task SelectVoiceChannelAsync(ulong channelId, bool force = false) + { + var model = await ApiClient.SendSelectVoiceChannelAsync(channelId, force).ConfigureAwait(false); + return RpcChannel.Create(this, model) as IRpcAudioChannel; + } + public async Task GetVoiceSettingsAsync() { var model = await ApiClient.GetVoiceSettingsAsync().ConfigureAwait(false); @@ -279,6 +333,12 @@ namespace Discord.Rpc func(settings); await ApiClient.SetVoiceSettingsAsync(settings).ConfigureAwait(false); } + public async Task SetUserVoiceSettingsAsync(ulong userId, Action func) + { + var settings = new API.Rpc.UserVoiceSettings(); + func(settings); + await ApiClient.SetUserVoiceSettingsAsync(userId, settings).ConfigureAwait(false); + } private static string GetEventName(RpcGlobalEvent rpcEvent) { @@ -363,8 +423,8 @@ namespace Discord.Rpc case "CHANNEL_CREATE": { await _rpcLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false); - var data = (payload.Value as JToken).ToObject(_serializer); - var channel = RpcChannel.Create(data); + var data = (payload.Value as JToken).ToObject(_serializer); + var channel = RpcChannelSummary.Create(data); await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false); } @@ -374,8 +434,8 @@ namespace Discord.Rpc case "GUILD_CREATE": { await _rpcLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false); - var data = (payload.Value as JToken).ToObject(_serializer); - var guild = RpcGuild.Create(data); + var data = (payload.Value as JToken).ToObject(_serializer); + var guild = RpcGuildSummary.Create(data); await _guildCreatedEvent.InvokeAsync(guild).ConfigureAwait(false); } @@ -394,7 +454,7 @@ namespace Discord.Rpc case "VOICE_STATE_CREATE": { await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_CREATE)").ConfigureAwait(false); - var data = (payload.Value as JToken).ToObject(_serializer); + var data = (payload.Value as JToken).ToObject(_serializer); var voiceState = RpcVoiceState.Create(this, data); await _voiceStateCreatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); @@ -403,7 +463,7 @@ namespace Discord.Rpc case "VOICE_STATE_UPDATE": { await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false); - var data = (payload.Value as JToken).ToObject(_serializer); + var data = (payload.Value as JToken).ToObject(_serializer); var voiceState = RpcVoiceState.Create(this, data); await _voiceStateUpdatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); @@ -412,7 +472,7 @@ namespace Discord.Rpc case "VOICE_STATE_DELETE": { await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_DELETE)").ConfigureAwait(false); - var data = (payload.Value as JToken).ToObject(_serializer); + var data = (payload.Value as JToken).ToObject(_serializer); var voiceState = RpcVoiceState.Create(this, data); await _voiceStateDeletedEvent.InvokeAsync(voiceState).ConfigureAwait(false); diff --git a/src/Discord.Net.Rpc/Entities/Channels/IRpcAudioChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/IRpcAudioChannel.cs new file mode 100644 index 000000000..4fa01104a --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/IRpcAudioChannel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Discord.Rpc +{ + public interface IRpcAudioChannel : IAudioChannel + { + IReadOnlyCollection VoiceStates { get; } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/IRpcMessageChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/IRpcMessageChannel.cs new file mode 100644 index 000000000..8e69c1b30 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/IRpcMessageChannel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Discord.Rpc +{ + public interface IRpcMessageChannel : IMessageChannel + { + IReadOnlyCollection CachedMessages { get; } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/IRpcPrivateChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/IRpcPrivateChannel.cs new file mode 100644 index 000000000..ae43c8675 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/IRpcPrivateChannel.cs @@ -0,0 +1,6 @@ +namespace Discord.Rpc +{ + public interface IRpcPrivateChannel + { + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcChannel.cs index fc4d6c012..cd69fd38e 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcChannel.cs @@ -1,32 +1,40 @@ -using System.Diagnostics; -using Model = Discord.API.Rpc.ChannelCreatedEvent; +using System; + +using Model = Discord.API.Rpc.Channel; namespace Discord.Rpc { - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class RpcChannel + public class RpcChannel : RpcEntity { - public ulong Id { get; } - public string Name { get; set; } - public ChannelType Type { get; set; } + public string Name { get; private set; } - internal RpcChannel(ulong id) + internal RpcChannel(DiscordRpcClient discord, ulong id) + : base(discord, id) { - Id = id; } - internal static RpcChannel Create(Model model) + internal static RpcChannel Create(DiscordRpcClient discord, Model model) { - var entity = new RpcChannel(model.Id); - entity.Update(model); - return entity; + if (model.GuildId.IsSpecified) + return RpcGuildChannel.Create(discord, model); + else + return CreatePrivate(discord, model); } - internal void Update(Model model) + internal static RpcChannel CreatePrivate(DiscordRpcClient discord, Model model) { - Name = model.Name; - Type = model.Type; + switch (model.Type) + { + case ChannelType.DM: + return RpcDMChannel.Create(discord, model); + case ChannelType.Group: + return RpcGroupChannel.Create(discord, model); + default: + throw new InvalidOperationException($"Unexpected channel type: {model.Type}"); + } + } + internal virtual void Update(Model model) + { + if (model.Name.IsSpecified) + Name = model.Name.Value; } - - public override string ToString() => Name; - private string DebuggerDisplay => $"{Name} ({Id}, {Type})"; } } diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcChannelSummary.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcChannelSummary.cs new file mode 100644 index 000000000..72679ac58 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcChannelSummary.cs @@ -0,0 +1,32 @@ +using System.Diagnostics; +using Model = Discord.API.Rpc.ChannelSummary; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcChannelSummary + { + public ulong Id { get; } + public string Name { get; set; } + public ChannelType Type { get; set; } + + internal RpcChannelSummary(ulong id) + { + Id = id; + } + internal static RpcChannelSummary Create(Model model) + { + var entity = new RpcChannelSummary(model.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + Name = model.Name; + Type = model.Type; + } + + public override string ToString() => Name; + private string DebuggerDisplay => $"{Name} ({Id}, {Type})"; + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs new file mode 100644 index 000000000..78d38d595 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs @@ -0,0 +1,124 @@ +using Discord.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Rpc.Channel; + +namespace Discord.Rpc +{ + public class RpcDMChannel : RpcChannel, IRpcMessageChannel, IRpcPrivateChannel, IDMChannel + { + public IReadOnlyCollection CachedMessages { get; private set; } + + internal RpcDMChannel(DiscordRpcClient discord, ulong id) + : base(discord, id) + { + } + internal static new RpcDMChannel Create(DiscordRpcClient discord, Model model) + { + var entity = new RpcDMChannel(discord, model.Id); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + CachedMessages = model.Messages.Select(x => RpcMessage.Create(Discord, Id, x)).ToImmutableArray(); + } + + public Task CloseAsync(RequestOptions options = null) + => ChannelHelper.DeleteAsync(this, Discord, options); + + //TODO: Use RPC cache + public Task GetMessageAsync(ulong id, RequestOptions options = null) + => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); + public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); + public IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); + public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); + public Task> GetPinnedMessagesAsync(RequestOptions options = null) + => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); + + public Task SendMessageAsync(string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); + public Task SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, null, options); + + public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null) + => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); + + public Task TriggerTypingAsync(RequestOptions options = null) + => ChannelHelper.TriggerTypingAsync(this, Discord, options); + public IDisposable EnterTypingState(RequestOptions options = null) + => ChannelHelper.EnterTypingState(this, Discord, options); + + public override string ToString() => Id.ToString(); + private string DebuggerDisplay => $"({Id}, DM)"; + + //IDMChannel + IUser IDMChannel.Recipient { get { throw new NotSupportedException(); } } + + //IPrivateChannel + IReadOnlyCollection IPrivateChannel.Recipients { get { throw new NotSupportedException(); } } + + //IMessageChannel + async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetMessageAsync(id, options); + else + return null; + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(limit, options); + else + return AsyncEnumerable.Empty>(); + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(fromMessageId, dir, limit, options); + else + return AsyncEnumerable.Empty>(); + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(fromMessage, dir, limit, options); + else + return AsyncEnumerable.Empty>(); + } + async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) + => await GetPinnedMessagesAsync(options); + + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) + => await SendFileAsync(filePath, text, isTTS, options); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) + => await SendFileAsync(stream, filename, text, isTTS, options); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, RequestOptions options) + => await SendMessageAsync(text, isTTS, options); + IDisposable IMessageChannel.EnterTypingState(RequestOptions options) + => EnterTypingState(options); + + //IChannel + string IChannel.Name { get { throw new NotSupportedException(); } } + + Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) + { + throw new NotSupportedException(); + } + IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs new file mode 100644 index 000000000..79ff1afd3 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs @@ -0,0 +1,123 @@ +using Discord.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Rpc.Channel; + +namespace Discord.Rpc +{ + public class RpcGroupChannel : RpcChannel, IRpcMessageChannel, IRpcAudioChannel, IRpcPrivateChannel, IGroupChannel + { + public IReadOnlyCollection CachedMessages { get; private set; } + public IReadOnlyCollection VoiceStates { get; private set; } + + internal RpcGroupChannel(DiscordRpcClient discord, ulong id) + : base(discord, id) + { + } + internal new static RpcGroupChannel Create(DiscordRpcClient discord, Model model) + { + var entity = new RpcGroupChannel(discord, model.Id); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + CachedMessages = model.Messages.Select(x => RpcMessage.Create(Discord, Id, x)).ToImmutableArray(); + VoiceStates = model.VoiceStates.Select(x => RpcVoiceState.Create(Discord, x)).ToImmutableArray(); + } + + public Task LeaveAsync(RequestOptions options = null) + => ChannelHelper.DeleteAsync(this, Discord, options); + + //TODO: Use RPC cache + public Task GetMessageAsync(ulong id, RequestOptions options = null) + => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); + public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); + public IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); + public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); + public Task> GetPinnedMessagesAsync(RequestOptions options = null) + => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); + + public Task SendMessageAsync(string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); + public Task SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, null, options); + + public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null) + => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); + + public Task TriggerTypingAsync(RequestOptions options = null) + => ChannelHelper.TriggerTypingAsync(this, Discord, options); + public IDisposable EnterTypingState(RequestOptions options = null) + => ChannelHelper.EnterTypingState(this, Discord, options); + + public override string ToString() => Id.ToString(); + private string DebuggerDisplay => $"({Id}, Group)"; + + //IPrivateChannel + IReadOnlyCollection IPrivateChannel.Recipients { get { throw new NotSupportedException(); } } + + //IMessageChannel + async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetMessageAsync(id, options); + else + return null; + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(limit, options); + else + return AsyncEnumerable.Empty>(); + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(fromMessageId, dir, limit, options); + else + return AsyncEnumerable.Empty>(); + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(fromMessage, dir, limit, options); + else + return AsyncEnumerable.Empty>(); + } + async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) + => await GetPinnedMessagesAsync(options); + + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) + => await SendFileAsync(filePath, text, isTTS, options); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) + => await SendFileAsync(stream, filename, text, isTTS, options); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, RequestOptions options) + => await SendMessageAsync(text, isTTS, options); + IDisposable IMessageChannel.EnterTypingState(RequestOptions options) + => EnterTypingState(options); + + //IChannel + string IChannel.Name { get { throw new NotSupportedException(); } } + + Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) + { + throw new NotSupportedException(); + } + IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs new file mode 100644 index 000000000..897244b55 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Discord.API.Rest; +using Model = Discord.API.Rpc.Channel; +using Discord.Rest; + +namespace Discord.Rpc +{ + public class RpcGuildChannel : RpcChannel, IGuildChannel + { + public ulong GuildId { get; } + public int Position { get; private set; } + + internal RpcGuildChannel(DiscordRpcClient discord, ulong id, ulong guildId) + : base(discord, id) + { + GuildId = guildId; + } + internal new static RpcGuildChannel Create(DiscordRpcClient discord, Model model) + { + switch (model.Type) + { + case ChannelType.Text: + return RpcTextChannel.Create(discord, model); + case ChannelType.Voice: + return RpcVoiceChannel.Create(discord, model); + default: + throw new InvalidOperationException("Unknown guild channel type"); + } + } + internal override void Update(Model model) + { + base.Update(model); + if (model.Position.IsSpecified) + Position = model.Position.Value; + } + + public Task ModifyAsync(Action func, RequestOptions options = null) + => ChannelHelper.ModifyAsync(this, Discord, func, options); + public Task DeleteAsync(RequestOptions options = null) + => ChannelHelper.DeleteAsync(this, Discord, options); + + public Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions perms, RequestOptions options = null) + => ChannelHelper.AddPermissionOverwriteAsync(this, Discord, user, perms, options); + public Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions perms, RequestOptions options = null) + => ChannelHelper.AddPermissionOverwriteAsync(this, Discord, role, perms, options); + public Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null) + => ChannelHelper.RemovePermissionOverwriteAsync(this, Discord, user, options); + public Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null) + => ChannelHelper.RemovePermissionOverwriteAsync(this, Discord, role, options); + + public async Task> GetInvitesAsync(RequestOptions options = null) + => await ChannelHelper.GetInvitesAsync(this, Discord, options); + public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = true, RequestOptions options = null) + => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, options); + + public override string ToString() => Name; + + //IGuildChannel + async Task> IGuildChannel.GetInvitesAsync(RequestOptions options) + => await GetInvitesAsync(options); + async Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, RequestOptions options) + => await CreateInviteAsync(maxAge, maxUses, isTemporary, options); + + IReadOnlyCollection IGuildChannel.PermissionOverwrites { get { throw new NotSupportedException(); } } + OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IUser user) + { + throw new NotSupportedException(); + } + OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IRole role) + { + throw new NotSupportedException(); + } + IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) + { + throw new NotSupportedException(); + } + Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) + { + throw new NotSupportedException(); + } + + //IChannel + IAsyncEnumerable> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) + { + throw new NotSupportedException(); + } + Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs new file mode 100644 index 000000000..45705aa6f --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs @@ -0,0 +1,113 @@ +using Discord.API.Rest; +using Discord.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Rpc.Channel; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcTextChannel : RpcGuildChannel, IRpcMessageChannel, ITextChannel + { + public IReadOnlyCollection CachedMessages { get; private set; } + + public string Mention => MentionUtils.MentionChannel(Id); + + internal RpcTextChannel(DiscordRpcClient discord, ulong id, ulong guildId) + : base(discord, id, guildId) + { + } + internal new static RpcVoiceChannel Create(DiscordRpcClient discord, Model model) + { + var entity = new RpcVoiceChannel(discord, model.Id, model.GuildId.Value); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + CachedMessages = model.Messages.Select(x => RpcMessage.Create(Discord, Id, x)).ToImmutableArray(); + } + + public Task ModifyAsync(Action func, RequestOptions options = null) + => ChannelHelper.ModifyAsync(this, Discord, func, options); + + //TODO: Use RPC cache + public Task GetMessageAsync(ulong id, RequestOptions options = null) + => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); + public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); + public IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); + public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); + public Task> GetPinnedMessagesAsync(RequestOptions options = null) + => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); + + public Task SendMessageAsync(string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); + public Task SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, null, options); + + public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null) + => ChannelHelper.DeleteMessagesAsync(this, Discord, messages, options); + + public Task TriggerTypingAsync(RequestOptions options = null) + => ChannelHelper.TriggerTypingAsync(this, Discord, options); + public IDisposable EnterTypingState(RequestOptions options = null) + => ChannelHelper.EnterTypingState(this, Discord, options); + + private string DebuggerDisplay => $"{Name} ({Id}, Text)"; + + //ITextChannel + string ITextChannel.Topic { get { throw new NotSupportedException(); } } + + //IMessageChannel + async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetMessageAsync(id, options); + else + return null; + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(limit, options); + else + return AsyncEnumerable.Empty>(); + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(fromMessageId, dir, limit, options); + else + return AsyncEnumerable.Empty>(); + } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return GetMessagesAsync(fromMessage, dir, limit, options); + else + return AsyncEnumerable.Empty>(); + } + async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) + => await GetPinnedMessagesAsync(options); + + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) + => await SendFileAsync(filePath, text, isTTS, options); + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) + => await SendFileAsync(stream, filename, text, isTTS, options); + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, RequestOptions options) + => await SendMessageAsync(text, isTTS, options); + IDisposable IMessageChannel.EnterTypingState(RequestOptions options) + => EnterTypingState(options); + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs new file mode 100644 index 000000000..1e6510a38 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs @@ -0,0 +1,49 @@ +using Discord.API.Rest; +using Discord.Audio; +using Discord.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Rpc.Channel; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcVoiceChannel : RpcGuildChannel, IRpcAudioChannel, IVoiceChannel + { + public int UserLimit { get; private set; } + public int Bitrate { get; private set; } + public IReadOnlyCollection VoiceStates { get; private set; } + + internal RpcVoiceChannel(DiscordRpcClient discord, ulong id, ulong guildId) + : base(discord, id, guildId) + { + } + internal new static RpcVoiceChannel Create(DiscordRpcClient discord, Model model) + { + var entity = new RpcVoiceChannel(discord, model.Id, model.GuildId.Value); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + if (model.UserLimit.IsSpecified) + UserLimit = model.UserLimit.Value; + if (model.Bitrate.IsSpecified) + Bitrate = model.Bitrate.Value; + VoiceStates = model.VoiceStates.Select(x => RpcVoiceState.Create(Discord, x)).ToImmutableArray(); + } + + public Task ModifyAsync(Action func, RequestOptions options = null) + => ChannelHelper.ModifyAsync(this, Discord, func, options); + + private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; + + //IVoiceChannel + Task IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Guilds/RpcGuild.cs b/src/Discord.Net.Rpc/Entities/Guilds/RpcGuild.cs index c8ee39ffc..7352d9e92 100644 --- a/src/Discord.Net.Rpc/Entities/Guilds/RpcGuild.cs +++ b/src/Discord.Net.Rpc/Entities/Guilds/RpcGuild.cs @@ -1,27 +1,33 @@ -using System.Diagnostics; -using Model = Discord.API.Rpc.GuildCreatedEvent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Model = Discord.API.Rpc.Guild; namespace Discord.Rpc { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class RpcGuild + public class RpcGuild : RpcEntity { - public ulong Id { get; } - public string Name { get; set; } + public string Name { get; private set; } + public string IconUrl { get; private set; } + public IReadOnlyCollection Users { get; private set; } - internal RpcGuild(ulong id) + internal RpcGuild(DiscordRpcClient discord, ulong id) + : base(discord, id) { - Id = id; } - internal static RpcGuild Create(Model model) + internal static RpcGuild Create(DiscordRpcClient discord, Model model) { - var entity = new RpcGuild(model.Id); + var entity = new RpcGuild(discord, model.Id); entity.Update(model); return entity; } internal void Update(Model model) { Name = model.Name; + IconUrl = model.IconUrl; + Users = model.Members.Select(x => RpcGuildUser.Create(Discord, x)).ToImmutableArray(); } public override string ToString() => Name; diff --git a/src/Discord.Net.Rpc/Entities/Guilds/RpcGuildStatus.cs b/src/Discord.Net.Rpc/Entities/Guilds/RpcGuildStatus.cs index 4b74249cc..f443d7aa3 100644 --- a/src/Discord.Net.Rpc/Entities/Guilds/RpcGuildStatus.cs +++ b/src/Discord.Net.Rpc/Entities/Guilds/RpcGuildStatus.cs @@ -6,12 +6,12 @@ namespace Discord.Rpc [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RpcGuildStatus { - public RpcGuild Guild { get; } + public RpcGuildSummary Guild { get; } public int Online { get; private set; } internal RpcGuildStatus(ulong guildId) { - Guild = new RpcGuild(guildId); + Guild = new RpcGuildSummary(guildId); } internal static RpcGuildStatus Create(Model model) { diff --git a/src/Discord.Net.Rpc/Entities/Guilds/RpcGuildSummary.cs b/src/Discord.Net.Rpc/Entities/Guilds/RpcGuildSummary.cs new file mode 100644 index 000000000..4f9bff2c9 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Guilds/RpcGuildSummary.cs @@ -0,0 +1,30 @@ +using System.Diagnostics; +using Model = Discord.API.Rpc.GuildSummary; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcGuildSummary + { + public ulong Id { get; } + public string Name { get; private set; } + + internal RpcGuildSummary(ulong id) + { + Id = id; + } + internal static RpcGuildSummary Create(Model model) + { + var entity = new RpcGuildSummary(model.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + Name = model.Name; + } + + public override string ToString() => Name; + private string DebuggerDisplay => $"{Name} ({Id})"; + } +} diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs index d9aa2b3f0..72c2e134a 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using Model = Discord.API.Rpc.RpcMessage; +using Model = Discord.API.Rpc.Message; namespace Discord.Rpc { diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs index 7cf222dc7..734ef38bc 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs @@ -1,6 +1,6 @@ using Discord.Rest; using System.Diagnostics; -using Model = Discord.API.Rpc.RpcMessage; +using Model = Discord.API.Rpc.Message; namespace Discord.Rpc { diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index f400701f6..aa1d21973 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Threading.Tasks; -using Model = Discord.API.Rpc.RpcMessage; +using Model = Discord.API.Rpc.Message; namespace Discord.Rpc { diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcGuildUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcGuildUser.cs new file mode 100644 index 000000000..f4ca63750 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Users/RpcGuildUser.cs @@ -0,0 +1,29 @@ +using Model = Discord.API.Rpc.GuildMember; + +namespace Discord.Rpc +{ + public class RpcGuildUser : RpcUser + { + private UserStatus _status; + + public override UserStatus Status => _status; + //public object Acitivity { get; private set; } + + internal RpcGuildUser(DiscordRpcClient discord, ulong id) + : base(discord, id) + { + } + internal static RpcGuildUser Create(DiscordRpcClient discord, Model model) + { + var entity = new RpcGuildUser(discord, model.User.Id); + entity.Update(model); + return entity; + } + internal void Update(Model model) + { + base.Update(model.User); + _status = model.Status; + //Activity = model.Activity; + } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs index 33db9bba1..0ccf16f16 100644 --- a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs +++ b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs @@ -6,7 +6,7 @@ using Model = Discord.API.User; namespace Discord.Rpc { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class RpcUser : RpcEntity, IUser, IUpdateable + public class RpcUser : RpcEntity, IUser { public bool IsBot { get; private set; } public string Username { get; private set; } @@ -40,12 +40,6 @@ namespace Discord.Rpc if (model.Username.IsSpecified) Username = model.Username.Value; } - - public virtual async Task UpdateAsync(RequestOptions options = null) - { - var model = await Discord.ApiClient.GetUserAsync(Id, options); - Update(model); - } public Task CreateDMChannelAsync(RequestOptions options = null) => UserHelper.CreateDMChannelAsync(this, Discord, options); diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs b/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs index ee392f5cd..f18a51434 100644 --- a/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs +++ b/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics; -using Model = Discord.API.Rpc.VoiceStateEvent; +using Model = Discord.API.Rpc.ExtendedVoiceState; namespace Discord.Rpc { diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketChannel.cs index 9601d5323..820f3dbcb 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketChannel.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading.Tasks;