| @@ -154,6 +154,7 @@ namespace Discord.Net.WebSockets | |||||
| while (!cancelToken.IsCancellationRequested) | while (!cancelToken.IsCancellationRequested) | ||||
| { | { | ||||
| WebSocketReceiveResult socketResult = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false); | WebSocketReceiveResult socketResult = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false); | ||||
| System.Diagnostics.Debug.WriteLine("Got " + socketResult.Count); | |||||
| byte[] result; | byte[] result; | ||||
| int resultCount; | int resultCount; | ||||
| @@ -193,6 +194,7 @@ namespace Discord.Net.WebSockets | |||||
| result = buffer.Array; | result = buffer.Array; | ||||
| } | } | ||||
| System.Diagnostics.Debug.WriteLine("Start"); | |||||
| if (socketResult.MessageType == WebSocketMessageType.Text) | if (socketResult.MessageType == WebSocketMessageType.Text) | ||||
| { | { | ||||
| string text = Encoding.UTF8.GetString(result, 0, resultCount); | string text = Encoding.UTF8.GetString(result, 0, resultCount); | ||||
| @@ -200,6 +202,7 @@ namespace Discord.Net.WebSockets | |||||
| } | } | ||||
| else | else | ||||
| await BinaryMessage(result, 0, resultCount).ConfigureAwait(false); | await BinaryMessage(result, 0, resultCount).ConfigureAwait(false); | ||||
| System.Diagnostics.Debug.WriteLine("Stop"); | |||||
| } | } | ||||
| } | } | ||||
| catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT) | catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT) | ||||
| @@ -269,14 +269,14 @@ namespace Discord.API | |||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| return await SendRpcAsync<GetGuildsResponse>("GET_GUILDS", null, options: options).ConfigureAwait(false); | return await SendRpcAsync<GetGuildsResponse>("GET_GUILDS", null, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<Rpc.RpcGuild> SendGetGuildAsync(ulong guildId, RequestOptions options = null) | |||||
| public async Task<Rpc.Guild> SendGetGuildAsync(ulong guildId, RequestOptions options = null) | |||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| var msg = new GetGuildParams | var msg = new GetGuildParams | ||||
| { | { | ||||
| GuildId = guildId | GuildId = guildId | ||||
| }; | }; | ||||
| return await SendRpcAsync<Rpc.RpcGuild>("GET_GUILD", msg, options: options).ConfigureAwait(false); | |||||
| return await SendRpcAsync<Rpc.Guild>("GET_GUILD", msg, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<GetChannelsResponse> SendGetChannelsAsync(ulong guildId, RequestOptions options = null) | public async Task<GetChannelsResponse> SendGetChannelsAsync(ulong guildId, RequestOptions options = null) | ||||
| { | { | ||||
| @@ -287,33 +287,34 @@ namespace Discord.API | |||||
| }; | }; | ||||
| return await SendRpcAsync<GetChannelsResponse>("GET_CHANNELS", msg, options: options).ConfigureAwait(false); | return await SendRpcAsync<GetChannelsResponse>("GET_CHANNELS", msg, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<Rpc.RpcChannel> SendGetChannelAsync(ulong channelId, RequestOptions options = null) | |||||
| public async Task<Rpc.Channel> SendGetChannelAsync(ulong channelId, RequestOptions options = null) | |||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| var msg = new GetChannelParams | var msg = new GetChannelParams | ||||
| { | { | ||||
| ChannelId = channelId | ChannelId = channelId | ||||
| }; | }; | ||||
| return await SendRpcAsync<Rpc.RpcChannel>("GET_CHANNEL", msg, options: options).ConfigureAwait(false); | |||||
| return await SendRpcAsync<Rpc.Channel>("GET_CHANNEL", msg, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<SetLocalVolumeResponse> SendSetLocalVolumeAsync(int volume, RequestOptions options = null) | |||||
| public async Task<Rpc.Channel> SendSelectTextChannelAsync(ulong channelId, RequestOptions options = null) | |||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| var msg = new SetLocalVolumeParams | |||||
| var msg = new SelectChannelParams | |||||
| { | { | ||||
| Volume = volume | |||||
| ChannelId = channelId | |||||
| }; | }; | ||||
| return await SendRpcAsync<SetLocalVolumeResponse>("SET_LOCAL_VOLUME", msg, options: options).ConfigureAwait(false); | |||||
| return await SendRpcAsync<Rpc.Channel>("SELECT_TEXT_CHANNEL", msg, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<Rpc.RpcChannel> SendSelectVoiceChannelAsync(ulong channelId, RequestOptions options = null) | |||||
| public async Task<Rpc.Channel> SendSelectVoiceChannelAsync(ulong channelId, bool force = false, RequestOptions options = null) | |||||
| { | { | ||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| var msg = new SelectVoiceChannelParams | |||||
| var msg = new SelectChannelParams | |||||
| { | { | ||||
| ChannelId = channelId | |||||
| ChannelId = channelId, | |||||
| Force = force | |||||
| }; | }; | ||||
| return await SendRpcAsync<Rpc.RpcChannel>("SELECT_VOICE_CHANNEL", msg, options: options).ConfigureAwait(false); | |||||
| return await SendRpcAsync<Rpc.Channel>("SELECT_VOICE_CHANNEL", msg, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| public async Task<SubscriptionResponse> SendGlobalSubscribeAsync(string evt, RequestOptions options = null) | public async Task<SubscriptionResponse> SendGlobalSubscribeAsync(string evt, RequestOptions options = null) | ||||
| @@ -370,10 +371,16 @@ namespace Discord.API | |||||
| options = RequestOptions.CreateOrClone(options); | options = RequestOptions.CreateOrClone(options); | ||||
| return await SendRpcAsync<API.Rpc.VoiceSettings>("GET_VOICE_SETTINGS", null, options: options).ConfigureAwait(false); | return await SendRpcAsync<API.Rpc.VoiceSettings>("GET_VOICE_SETTINGS", null, options: options).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<API.Rpc.VoiceSettings> 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<API.Rpc.VoiceSettings>("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); | options = RequestOptions.CreateOrClone(options); | ||||
| return await SendRpcAsync<API.Rpc.VoiceSettings>("SET_VOICE_SETTINGS", settings, options: options).ConfigureAwait(false); | |||||
| settings.UserId = userId; | |||||
| await SendRpcAsync<API.Rpc.UserVoiceSettings>("SET_USER_VOICE_SETTINGS", settings, options: options).ConfigureAwait(false); | |||||
| } | } | ||||
| private bool ProcessMessage(API.Rpc.RpcFrame msg) | private bool ProcessMessage(API.Rpc.RpcFrame msg) | ||||
| @@ -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<ulong> GuildId { get; set; } | |||||
| [JsonProperty("name")] | |||||
| public Optional<string> Name { get; set; } | |||||
| [JsonProperty("position")] | |||||
| public Optional<int> Position { get; set; } | |||||
| //IMessageChannel | |||||
| [JsonProperty("messages")] | |||||
| public Message[] Messages { get; set; } | |||||
| //VoiceChannel | |||||
| [JsonProperty("bitrate")] | |||||
| public Optional<int> Bitrate { get; set; } | |||||
| [JsonProperty("user_limit")] | |||||
| public Optional<int> UserLimit { get; set; } | |||||
| [JsonProperty("voice_states")] | |||||
| public ExtendedVoiceState[] VoiceStates { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class ChannelCreatedEvent | |||||
| public class ChannelSummary | |||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | public ulong Id { get; set; } | ||||
| @@ -3,7 +3,7 @@ using Newtonsoft.Json; | |||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class VoiceStateEvent | |||||
| public class ExtendedVoiceState | |||||
| { | { | ||||
| [JsonProperty("user")] | [JsonProperty("user")] | ||||
| public User User { get; set; } | public User User { get; set; } | ||||
| @@ -1,11 +1,12 @@ | |||||
| #pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System.Collections.Generic; | |||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class GetChannelsResponse | public class GetChannelsResponse | ||||
| { | { | ||||
| [JsonProperty("channels")] | [JsonProperty("channels")] | ||||
| public RpcChannel[] Channels { get; set; } | |||||
| public IReadOnlyCollection<ChannelSummary> Channels { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| #pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class GetGuildsParams | public class GetGuildsParams | ||||
| @@ -6,6 +6,6 @@ namespace Discord.API.Rpc | |||||
| public class GetGuildsResponse | public class GetGuildsResponse | ||||
| { | { | ||||
| [JsonProperty("guilds")] | [JsonProperty("guilds")] | ||||
| public RpcUserGuild[] Guilds { get; set; } | |||||
| public GuildSummary[] Guilds { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,13 +1,18 @@ | |||||
| #pragma warning disable CS1591 | #pragma warning disable CS1591 | ||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System.Collections.Generic; | |||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class RpcUserGuild | |||||
| public class Guild | |||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | public ulong Id { get; set; } | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name { get; set; } | public string Name { get; set; } | ||||
| [JsonProperty("icon_url")] | |||||
| public string IconUrl { get; set; } | |||||
| [JsonProperty("members")] | |||||
| public IEnumerable<GuildMember> Members { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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; }*/ | |||||
| } | |||||
| } | |||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class GuildCreatedEvent | |||||
| public class GuildSummary | |||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | public ulong Id { get; set; } | ||||
| @@ -2,7 +2,7 @@ | |||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class RpcMessage : Message | |||||
| public class Message : Discord.API.Message | |||||
| { | { | ||||
| [JsonProperty("blocked")] | [JsonProperty("blocked")] | ||||
| public Optional<bool> IsBlocked { get; } | public Optional<bool> IsBlocked { get; } | ||||
| @@ -7,6 +7,6 @@ namespace Discord.API.Rpc | |||||
| [JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
| public ulong ChannelId { get; set; } | public ulong ChannelId { get; set; } | ||||
| [JsonProperty("message")] | [JsonProperty("message")] | ||||
| public RpcMessage Message { get; set; } | |||||
| public Message Message { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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; } | |||||
| } | |||||
| } | |||||
| @@ -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; } | |||||
| } | |||||
| } | |||||
| @@ -3,9 +3,11 @@ using Newtonsoft.Json; | |||||
| namespace Discord.API.Rpc | namespace Discord.API.Rpc | ||||
| { | { | ||||
| public class SelectVoiceChannelParams | |||||
| public class SelectChannelParams | |||||
| { | { | ||||
| [JsonProperty("channel_id")] | [JsonProperty("channel_id")] | ||||
| public ulong? ChannelId { get; set; } | public ulong? ChannelId { get; set; } | ||||
| [JsonProperty("force")] | |||||
| public Optional<bool> Force { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -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> Pan { get; set; } | |||||
| [JsonProperty("volume")] | |||||
| public Optional<int> Volume { get; set; } | |||||
| [JsonProperty("mute")] | |||||
| public Optional<bool> Mute { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -26,20 +26,20 @@ namespace Discord.Rpc | |||||
| private readonly AsyncEvent<Func<Task>> _readyEvent = new AsyncEvent<Func<Task>>(); | private readonly AsyncEvent<Func<Task>> _readyEvent = new AsyncEvent<Func<Task>>(); | ||||
| //Channel | //Channel | ||||
| public event Func<RpcChannel, Task> ChannelCreated | |||||
| public event Func<RpcChannelSummary, Task> ChannelCreated | |||||
| { | { | ||||
| add { _channelCreatedEvent.Add(value); } | add { _channelCreatedEvent.Add(value); } | ||||
| remove { _channelCreatedEvent.Remove(value); } | remove { _channelCreatedEvent.Remove(value); } | ||||
| } | } | ||||
| private readonly AsyncEvent<Func<RpcChannel, Task>> _channelCreatedEvent = new AsyncEvent<Func<RpcChannel, Task>>(); | |||||
| private readonly AsyncEvent<Func<RpcChannelSummary, Task>> _channelCreatedEvent = new AsyncEvent<Func<RpcChannelSummary, Task>>(); | |||||
| //Guild | //Guild | ||||
| public event Func<RpcGuild, Task> GuildCreated | |||||
| public event Func<RpcGuildSummary, Task> GuildCreated | |||||
| { | { | ||||
| add { _guildCreatedEvent.Add(value); } | add { _guildCreatedEvent.Add(value); } | ||||
| remove { _guildCreatedEvent.Remove(value); } | remove { _guildCreatedEvent.Remove(value); } | ||||
| } | } | ||||
| private readonly AsyncEvent<Func<RpcGuild, Task>> _guildCreatedEvent = new AsyncEvent<Func<RpcGuild, Task>>(); | |||||
| private readonly AsyncEvent<Func<RpcGuildSummary, Task>> _guildCreatedEvent = new AsyncEvent<Func<RpcGuildSummary, Task>>(); | |||||
| public event Func<RpcGuildStatus, Task> GuildStatusUpdated | public event Func<RpcGuildStatus, Task> GuildStatusUpdated | ||||
| { | { | ||||
| add { _guildStatusUpdatedEvent.Add(value); } | add { _guildStatusUpdatedEvent.Add(value); } | ||||
| @@ -8,6 +8,7 @@ using Newtonsoft.Json.Linq; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Linq; | |||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -265,6 +266,59 @@ namespace Discord.Rpc | |||||
| await ApiClient.SendChannelUnsubscribeAsync(GetEventName(events[i]), channelId); | await ApiClient.SendChannelUnsubscribeAsync(GetEventName(events[i]), channelId); | ||||
| } | } | ||||
| public async Task<RpcGuild> GetRpcGuildAsync(ulong id) | |||||
| { | |||||
| var model = await ApiClient.SendGetGuildAsync(id).ConfigureAwait(false); | |||||
| return RpcGuild.Create(this, model); | |||||
| } | |||||
| public async Task<IReadOnlyCollection<RpcGuildSummary>> GetRpcGuildsAsync() | |||||
| { | |||||
| var models = await ApiClient.SendGetGuildsAsync().ConfigureAwait(false); | |||||
| return models.Guilds.Select(x => RpcGuildSummary.Create(x)).ToImmutableArray(); | |||||
| } | |||||
| public async Task<RpcChannel> GetRpcChannelAsync(ulong id) | |||||
| { | |||||
| var model = await ApiClient.SendGetChannelAsync(id).ConfigureAwait(false); | |||||
| return RpcChannel.Create(this, model); | |||||
| } | |||||
| public async Task<IReadOnlyCollection<RpcChannelSummary>> GetRpcChannelsAsync(ulong guildId) | |||||
| { | |||||
| var models = await ApiClient.SendGetChannelsAsync(guildId).ConfigureAwait(false); | |||||
| return models.Channels.Select(x => RpcChannelSummary.Create(x)).ToImmutableArray(); | |||||
| } | |||||
| public async Task<IMessageChannel> SelectTextChannelAsync(IChannel channel) | |||||
| { | |||||
| var model = await ApiClient.SendSelectTextChannelAsync(channel.Id).ConfigureAwait(false); | |||||
| return RpcChannel.Create(this, model) as IMessageChannel; | |||||
| } | |||||
| public async Task<IMessageChannel> SelectTextChannelAsync(RpcChannelSummary channel) | |||||
| { | |||||
| var model = await ApiClient.SendSelectTextChannelAsync(channel.Id).ConfigureAwait(false); | |||||
| return RpcChannel.Create(this, model) as IMessageChannel; | |||||
| } | |||||
| public async Task<IMessageChannel> SelectTextChannelAsync(ulong channelId) | |||||
| { | |||||
| var model = await ApiClient.SendSelectTextChannelAsync(channelId).ConfigureAwait(false); | |||||
| return RpcChannel.Create(this, model) as IMessageChannel; | |||||
| } | |||||
| public async Task<IRpcAudioChannel> 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<IRpcAudioChannel> 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<IRpcAudioChannel> 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<VoiceSettings> GetVoiceSettingsAsync() | public async Task<VoiceSettings> GetVoiceSettingsAsync() | ||||
| { | { | ||||
| var model = await ApiClient.GetVoiceSettingsAsync().ConfigureAwait(false); | var model = await ApiClient.GetVoiceSettingsAsync().ConfigureAwait(false); | ||||
| @@ -279,6 +333,12 @@ namespace Discord.Rpc | |||||
| func(settings); | func(settings); | ||||
| await ApiClient.SetVoiceSettingsAsync(settings).ConfigureAwait(false); | await ApiClient.SetVoiceSettingsAsync(settings).ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task SetUserVoiceSettingsAsync(ulong userId, Action<API.Rpc.UserVoiceSettings> func) | |||||
| { | |||||
| var settings = new API.Rpc.UserVoiceSettings(); | |||||
| func(settings); | |||||
| await ApiClient.SetUserVoiceSettingsAsync(userId, settings).ConfigureAwait(false); | |||||
| } | |||||
| private static string GetEventName(RpcGlobalEvent rpcEvent) | private static string GetEventName(RpcGlobalEvent rpcEvent) | ||||
| { | { | ||||
| @@ -363,8 +423,8 @@ namespace Discord.Rpc | |||||
| case "CHANNEL_CREATE": | case "CHANNEL_CREATE": | ||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<ChannelCreatedEvent>(_serializer); | |||||
| var channel = RpcChannel.Create(data); | |||||
| var data = (payload.Value as JToken).ToObject<ChannelSummary>(_serializer); | |||||
| var channel = RpcChannelSummary.Create(data); | |||||
| await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false); | await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -374,8 +434,8 @@ namespace Discord.Rpc | |||||
| case "GUILD_CREATE": | case "GUILD_CREATE": | ||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<GuildCreatedEvent>(_serializer); | |||||
| var guild = RpcGuild.Create(data); | |||||
| var data = (payload.Value as JToken).ToObject<GuildSummary>(_serializer); | |||||
| var guild = RpcGuildSummary.Create(data); | |||||
| await _guildCreatedEvent.InvokeAsync(guild).ConfigureAwait(false); | await _guildCreatedEvent.InvokeAsync(guild).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -394,7 +454,7 @@ namespace Discord.Rpc | |||||
| case "VOICE_STATE_CREATE": | case "VOICE_STATE_CREATE": | ||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_CREATE)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_CREATE)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<VoiceStateEvent>(_serializer); | |||||
| var data = (payload.Value as JToken).ToObject<ExtendedVoiceState>(_serializer); | |||||
| var voiceState = RpcVoiceState.Create(this, data); | var voiceState = RpcVoiceState.Create(this, data); | ||||
| await _voiceStateCreatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | await _voiceStateCreatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | ||||
| @@ -403,7 +463,7 @@ namespace Discord.Rpc | |||||
| case "VOICE_STATE_UPDATE": | case "VOICE_STATE_UPDATE": | ||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<VoiceStateEvent>(_serializer); | |||||
| var data = (payload.Value as JToken).ToObject<ExtendedVoiceState>(_serializer); | |||||
| var voiceState = RpcVoiceState.Create(this, data); | var voiceState = RpcVoiceState.Create(this, data); | ||||
| await _voiceStateUpdatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | await _voiceStateUpdatedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | ||||
| @@ -412,7 +472,7 @@ namespace Discord.Rpc | |||||
| case "VOICE_STATE_DELETE": | case "VOICE_STATE_DELETE": | ||||
| { | { | ||||
| await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_DELETE)").ConfigureAwait(false); | await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_DELETE)").ConfigureAwait(false); | ||||
| var data = (payload.Value as JToken).ToObject<VoiceStateEvent>(_serializer); | |||||
| var data = (payload.Value as JToken).ToObject<ExtendedVoiceState>(_serializer); | |||||
| var voiceState = RpcVoiceState.Create(this, data); | var voiceState = RpcVoiceState.Create(this, data); | ||||
| await _voiceStateDeletedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | await _voiceStateDeletedEvent.InvokeAsync(voiceState).ConfigureAwait(false); | ||||
| @@ -0,0 +1,9 @@ | |||||
| using System.Collections.Generic; | |||||
| namespace Discord.Rpc | |||||
| { | |||||
| public interface IRpcAudioChannel : IAudioChannel | |||||
| { | |||||
| IReadOnlyCollection<RpcVoiceState> VoiceStates { get; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| using System.Collections.Generic; | |||||
| namespace Discord.Rpc | |||||
| { | |||||
| public interface IRpcMessageChannel : IMessageChannel | |||||
| { | |||||
| IReadOnlyCollection<RpcMessage> CachedMessages { get; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| namespace Discord.Rpc | |||||
| { | |||||
| public interface IRpcPrivateChannel | |||||
| { | |||||
| } | |||||
| } | |||||
| @@ -1,32 +1,40 @@ | |||||
| using System.Diagnostics; | |||||
| using Model = Discord.API.Rpc.ChannelCreatedEvent; | |||||
| using System; | |||||
| using Model = Discord.API.Rpc.Channel; | |||||
| namespace Discord.Rpc | namespace Discord.Rpc | ||||
| { | { | ||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||||
| public class RpcChannel | |||||
| public class RpcChannel : RpcEntity<ulong> | |||||
| { | { | ||||
| 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})"; | |||||
| } | } | ||||
| } | } | ||||
| @@ -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})"; | |||||
| } | |||||
| } | |||||
| @@ -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<RpcMessage> 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<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); | |||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> 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<IMessage> 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<IUser> IPrivateChannel.Recipients { get { throw new NotSupportedException(); } } | |||||
| //IMessageChannel | |||||
| async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| if (mode == CacheMode.AllowDownload) | |||||
| return await GetMessageAsync(id, options); | |||||
| else | |||||
| return null; | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| if (mode == CacheMode.AllowDownload) | |||||
| return GetMessagesAsync(limit, options); | |||||
| else | |||||
| return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> 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<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> 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<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | |||||
| => await GetPinnedMessagesAsync(options); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options); | |||||
| async Task<IUserMessage> 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<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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<RpcMessage> CachedMessages { get; private set; } | |||||
| public IReadOnlyCollection<RpcVoiceState> 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<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); | |||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> 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<IMessage> 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<IUser> IPrivateChannel.Recipients { get { throw new NotSupportedException(); } } | |||||
| //IMessageChannel | |||||
| async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| if (mode == CacheMode.AllowDownload) | |||||
| return await GetMessageAsync(id, options); | |||||
| else | |||||
| return null; | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| if (mode == CacheMode.AllowDownload) | |||||
| return GetMessagesAsync(limit, options); | |||||
| else | |||||
| return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> 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<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> 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<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | |||||
| => await GetPinnedMessagesAsync(options); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options); | |||||
| async Task<IUserMessage> 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<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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<ModifyGuildChannelParams> 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<IReadOnlyCollection<RestInviteMetadata>> GetInvitesAsync(RequestOptions options = null) | |||||
| => await ChannelHelper.GetInvitesAsync(this, Discord, options); | |||||
| public async Task<RestInviteMetadata> 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<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync(RequestOptions options) | |||||
| => await GetInvitesAsync(options); | |||||
| async Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, RequestOptions options) | |||||
| => await CreateInviteAsync(maxAge, maxUses, isTemporary, options); | |||||
| IReadOnlyCollection<Overwrite> IGuildChannel.PermissionOverwrites { get { throw new NotSupportedException(); } } | |||||
| OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IUser user) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IRole role) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| //IChannel | |||||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| throw new NotSupportedException(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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<RpcMessage> 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<ModifyTextChannelParams> func, RequestOptions options = null) | |||||
| => ChannelHelper.ModifyAsync(this, Discord, func, options); | |||||
| //TODO: Use RPC cache | |||||
| public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessageAsync(this, Discord, id, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, null, options); | |||||
| public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) | |||||
| => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, null, options); | |||||
| public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) | |||||
| => ChannelHelper.GetPinnedMessagesAsync(this, Discord, null, options); | |||||
| public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, RequestOptions options = null) | |||||
| => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) | |||||
| => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, null, options); | |||||
| public Task<RestUserMessage> 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<IMessage> 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<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| if (mode == CacheMode.AllowDownload) | |||||
| return await GetMessageAsync(id, options); | |||||
| else | |||||
| return null; | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) | |||||
| { | |||||
| if (mode == CacheMode.AllowDownload) | |||||
| return GetMessagesAsync(limit, options); | |||||
| else | |||||
| return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> 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<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| IAsyncEnumerable<IReadOnlyCollection<IMessage>> 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<IReadOnlyCollection<IMessage>>(); | |||||
| } | |||||
| async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) | |||||
| => await GetPinnedMessagesAsync(options); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(filePath, text, isTTS, options); | |||||
| async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) | |||||
| => await SendFileAsync(stream, filename, text, isTTS, options); | |||||
| async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, RequestOptions options) | |||||
| => await SendMessageAsync(text, isTTS, options); | |||||
| IDisposable IMessageChannel.EnterTypingState(RequestOptions options) | |||||
| => EnterTypingState(options); | |||||
| } | |||||
| } | |||||
| @@ -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<RpcVoiceState> 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<ModifyVoiceChannelParams> func, RequestOptions options = null) | |||||
| => ChannelHelper.ModifyAsync(this, Discord, func, options); | |||||
| private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; | |||||
| //IVoiceChannel | |||||
| Task<IAudioClient> IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); } | |||||
| } | |||||
| } | |||||
| @@ -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 | namespace Discord.Rpc | ||||
| { | { | ||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RpcGuild | |||||
| public class RpcGuild : RpcEntity<ulong> | |||||
| { | { | ||||
| public ulong Id { get; } | |||||
| public string Name { get; set; } | |||||
| public string Name { get; private set; } | |||||
| public string IconUrl { get; private set; } | |||||
| public IReadOnlyCollection<RpcGuildUser> 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); | entity.Update(model); | ||||
| return entity; | return entity; | ||||
| } | } | ||||
| internal void Update(Model model) | internal void Update(Model model) | ||||
| { | { | ||||
| Name = model.Name; | Name = model.Name; | ||||
| IconUrl = model.IconUrl; | |||||
| Users = model.Members.Select(x => RpcGuildUser.Create(Discord, x)).ToImmutableArray(); | |||||
| } | } | ||||
| public override string ToString() => Name; | public override string ToString() => Name; | ||||
| @@ -6,12 +6,12 @@ namespace Discord.Rpc | |||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RpcGuildStatus | public class RpcGuildStatus | ||||
| { | { | ||||
| public RpcGuild Guild { get; } | |||||
| public RpcGuildSummary Guild { get; } | |||||
| public int Online { get; private set; } | public int Online { get; private set; } | ||||
| internal RpcGuildStatus(ulong guildId) | internal RpcGuildStatus(ulong guildId) | ||||
| { | { | ||||
| Guild = new RpcGuild(guildId); | |||||
| Guild = new RpcGuildSummary(guildId); | |||||
| } | } | ||||
| internal static RpcGuildStatus Create(Model model) | internal static RpcGuildStatus Create(Model model) | ||||
| { | { | ||||
| @@ -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})"; | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,7 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using Model = Discord.API.Rpc.RpcMessage; | |||||
| using Model = Discord.API.Rpc.Message; | |||||
| namespace Discord.Rpc | namespace Discord.Rpc | ||||
| { | { | ||||
| @@ -1,6 +1,6 @@ | |||||
| using Discord.Rest; | using Discord.Rest; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using Model = Discord.API.Rpc.RpcMessage; | |||||
| using Model = Discord.API.Rpc.Message; | |||||
| namespace Discord.Rpc | namespace Discord.Rpc | ||||
| { | { | ||||
| @@ -5,7 +5,7 @@ using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.Rpc.RpcMessage; | |||||
| using Model = Discord.API.Rpc.Message; | |||||
| namespace Discord.Rpc | namespace Discord.Rpc | ||||
| { | { | ||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -6,7 +6,7 @@ using Model = Discord.API.User; | |||||
| namespace Discord.Rpc | namespace Discord.Rpc | ||||
| { | { | ||||
| [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | [DebuggerDisplay(@"{DebuggerDisplay,nq}")] | ||||
| public class RpcUser : RpcEntity<ulong>, IUser, IUpdateable | |||||
| public class RpcUser : RpcEntity<ulong>, IUser | |||||
| { | { | ||||
| public bool IsBot { get; private set; } | public bool IsBot { get; private set; } | ||||
| public string Username { get; private set; } | public string Username { get; private set; } | ||||
| @@ -40,12 +40,6 @@ namespace Discord.Rpc | |||||
| if (model.Username.IsSpecified) | if (model.Username.IsSpecified) | ||||
| Username = model.Username.Value; | Username = model.Username.Value; | ||||
| } | } | ||||
| public virtual async Task UpdateAsync(RequestOptions options = null) | |||||
| { | |||||
| var model = await Discord.ApiClient.GetUserAsync(Id, options); | |||||
| Update(model); | |||||
| } | |||||
| public Task<RestDMChannel> CreateDMChannelAsync(RequestOptions options = null) | public Task<RestDMChannel> CreateDMChannelAsync(RequestOptions options = null) | ||||
| => UserHelper.CreateDMChannelAsync(this, Discord, options); | => UserHelper.CreateDMChannelAsync(this, Discord, options); | ||||
| @@ -1,6 +1,6 @@ | |||||
| using System; | using System; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using Model = Discord.API.Rpc.VoiceStateEvent; | |||||
| using Model = Discord.API.Rpc.ExtendedVoiceState; | |||||
| namespace Discord.Rpc | namespace Discord.Rpc | ||||
| { | { | ||||
| @@ -1,6 +1,5 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||