diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 2cdff662c..e83121efb 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1262,7 +1262,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty var data = (payload as JToken).ToObject(_serializer); var channel = GetChannel(data.ChannelId) as ISocketMessageChannel; - var guild = (channel as SocketGuildChannel)?.Guild; + var guild = data.GuildId.IsSpecified ? State.GetGuild(data.GuildId.Value) : null; if (guild != null && !guild.IsSynced) { await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); @@ -1277,8 +1277,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty } else { - await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); - return; + channel = CreateUnknownChannel(data.ChannelId, data.GuildId.IsSpecified ? data.GuildId.Value : null, State); } } @@ -1326,7 +1325,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty var data = (payload as JToken).ToObject(_serializer); var channel = GetChannel(data.ChannelId) as ISocketMessageChannel; - var guild = (channel as SocketGuildChannel)?.Guild; + var guild = data.GuildId.IsSpecified ? State.GetGuild(data.GuildId.Value) : null; if (guild != null && !guild.IsSynced) { await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false); @@ -1393,8 +1392,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty } else { - await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); - return; + channel = CreateUnknownChannel(data.ChannelId, data.GuildId.IsSpecified ? data.GuildId.Value : null, State); } } @@ -2017,6 +2015,10 @@ internal void RemoveDMChannels() foreach (var channel in channels) channel.Recipient.GlobalUser.RemoveRef(this); } + internal SocketUnknownChannel CreateUnknownChannel(ulong channelId, ulong? guildId, ClientState state) + { + return SocketUnknownChannel.Create(this, state, channelId, guildId); + } private async Task GuildAvailableAsync(SocketGuild guild) { diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketChannelHelper.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketChannelHelper.cs index 5cfbcc1a8..26ac11e40 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketChannelHelper.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketChannelHelper.cs @@ -71,6 +71,7 @@ public static void AddMessage(ISocketMessageChannel channel, DiscordSocketClient case SocketDMChannel dmChannel: dmChannel.AddMessage(msg); break; case SocketGroupChannel groupChannel: groupChannel.AddMessage(msg); break; case SocketTextChannel textChannel: textChannel.AddMessage(msg); break; + case SocketUnknownChannel unknownChannel: unknownChannel.AddMessage(msg); break; default: throw new NotSupportedException($"Unexpected {nameof(ISocketMessageChannel)} type."); } } @@ -83,6 +84,7 @@ public static SocketMessage RemoveMessage(ISocketMessageChannel channel, Discord case SocketDMChannel dmChannel: return dmChannel.RemoveMessage(id); case SocketGroupChannel groupChannel: return groupChannel.RemoveMessage(id); case SocketTextChannel textChannel: return textChannel.RemoveMessage(id); + case SocketUnknownChannel unknownChannel: return unknownChannel.RemoveMessage(id); default: throw new NotSupportedException($"Unexpected {nameof(ISocketMessageChannel)} type."); } } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketUnknownChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketUnknownChannel.cs new file mode 100644 index 000000000..1fa5ee759 --- /dev/null +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketUnknownChannel.cs @@ -0,0 +1,222 @@ +using Discord.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Discord.WebSocket +{ + public class SocketUnknownChannel : SocketChannel, ISocketMessageChannel + { + public string Name => null; + + public ulong? GuildId { get; private set; } + + public IReadOnlyCollection CachedMessages => ImmutableArray.Create(); + + internal SocketUnknownChannel(DiscordSocketClient discord, ulong id) + : base(discord, id) + { + } + internal static SocketUnknownChannel Create(DiscordSocketClient discord, ClientState state, ulong channelId, ulong? guildId) + { + var entity = new SocketUnknownChannel(discord, channelId); + entity.Update(state, guildId); + return entity; + } + internal override void Update(ClientState state, API.Channel model) + { + } + internal void Update(ClientState state, ulong? guildId) + { + GuildId = guildId; + } + + internal override SocketUser GetUserInternal(ulong id) + => null; + internal override IReadOnlyCollection GetUsersInternal() + => ImmutableArray.Create(); + internal void AddMessage(SocketMessage msg) + { + } + internal SocketMessage RemoveMessage(ulong id) + => null; + + /// + /// Gets a message from this message channel. + /// + /// + /// This method follows the same behavior as described in . + /// Please visit its documentation for more details on this method. + /// + /// The snowflake identifier of the message. + /// The options to be used when sending the request. + /// + /// A task that represents an asynchronous get operation for retrieving the message. The task result contains + /// the retrieved message; null if no message is found with the specified identifier. + /// + public async Task GetMessageAsync(ulong id, RequestOptions options = null) + => await ChannelHelper.GetMessageAsync(this, Discord, id, options).ConfigureAwait(false); + + /// + /// Gets the last N messages from this message channel. + /// + /// + /// This method follows the same behavior as described in . + /// Please visit its documentation for more details on this method. + /// + /// The numbers of message to be gotten from. + /// The options to be used when sending the request. + /// + /// Paged collection of messages. + /// + public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => SocketChannelHelper.GetMessagesAsync(this, Discord, null, null, Direction.Before, limit, CacheMode.AllowDownload, options); + /// + /// Gets a collection of messages in this channel. + /// + /// + /// This method follows the same behavior as described in . + /// Please visit its documentation for more details on this method. + /// + /// The ID of the starting message to get the messages from. + /// The direction of the messages to be gotten from. + /// The numbers of message to be gotten from. + /// The options to be used when sending the request. + /// + /// Paged collection of messages. + /// + public IAsyncEnumerable> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => SocketChannelHelper.GetMessagesAsync(this, Discord, null, fromMessageId, dir, limit, CacheMode.AllowDownload, options); + /// + /// Gets a collection of messages in this channel. + /// + /// + /// This method follows the same behavior as described in . + /// Please visit its documentation for more details on this method. + /// + /// The starting message to get the messages from. + /// The direction of the messages to be gotten from. + /// The numbers of message to be gotten from. + /// The options to be used when sending the request. + /// + /// Paged collection of messages. + /// + public IAsyncEnumerable> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) + => SocketChannelHelper.GetMessagesAsync(this, Discord, null, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options); + + /// + /// Message content is too long, length must be less or equal to . + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null, MessageReference messageReference = null) + => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, allowedMentions, messageReference, options); + + /// + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null) + => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, allowedMentions, messageReference, options, isSpoiler); + + /// + /// Message content is too long, length must be less or equal to . + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null, bool isSpoiler = false, AllowedMentions allowedMentions = null, MessageReference messageReference = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, allowedMentions, messageReference, options, isSpoiler); + + /// + public async Task ModifyMessageAsync(ulong messageId, Action func, RequestOptions options = null) + => await ChannelHelper.ModifyMessageAsync(this, messageId, func, Discord, options).ConfigureAwait(false); + + /// + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + /// + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + + /// + public Task TriggerTypingAsync(RequestOptions options = null) + => ChannelHelper.TriggerTypingAsync(this, Discord, options); + /// + public IDisposable EnterTypingState(RequestOptions options = null) + => ChannelHelper.EnterTypingState(this, Discord, options); + + /// + public Task> GetPinnedMessagesAsync(RequestOptions options = null) + => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); + + /// + /// Gets a user in this channel. + /// + /// The snowflake identifier of the user. + /// The options to be used when sending the request. + /// + /// Resolving permissions requires the parent guild to be downloaded. + /// + /// + /// A task representing the asynchronous get operation. The task result contains a guild user object that + /// represents the user; null if none is found. + /// + public Task GetUserAsync(ulong id, RequestOptions options = null) + => Task.FromResult(null); + + /// + /// Gets a collection of users that are able to view the channel. + /// + /// The options to be used when sending the request. + /// + /// Resolving permissions requires the parent guild to be downloaded. + /// + /// + /// A paged collection containing a collection of guild users that can access this channel. Flattening the + /// paginated response into a collection of users with + /// is required if you wish to access the users. + /// + public IAsyncEnumerable> GetUsersAsync(RequestOptions options = null) + => AsyncEnumerable.Empty>(); + + /// + public SocketMessage GetCachedMessage(ulong id) + => null; + /// + public IReadOnlyCollection GetCachedMessages(int limit = 100) + => ImmutableArray.Create(); + /// + public IReadOnlyCollection GetCachedMessages(ulong fromMessageId, Direction dir, int limit = 100) + => ImmutableArray.Create(); + /// + public IReadOnlyCollection GetCachedMessages(IMessage fromMessage, Direction dir, int limit = 100) + => ImmutableArray.Create(); + + //IMessageChannel + /// + async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetMessageAsync(id, options).ConfigureAwait(false); + else + return GetCachedMessage(id); + } + /// + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) + => SocketChannelHelper.GetMessagesAsync(this, Discord, null, null, Direction.Before, limit, mode, options); + /// + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) + => SocketChannelHelper.GetMessagesAsync(this, Discord, null, fromMessageId, dir, limit, mode, options); + /// + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) + => SocketChannelHelper.GetMessagesAsync(this, Discord, null, fromMessage.Id, dir, limit, mode, options); + + /// + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference) + => await SendFileAsync(filePath, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference).ConfigureAwait(false); + /// + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageReference messageReference) + => await SendFileAsync(stream, filename, text, isTTS, embed, options, isSpoiler, allowedMentions, messageReference).ConfigureAwait(false); + /// + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageReference messageReference) + => await SendMessageAsync(text, isTTS, embed, options, allowedMentions, messageReference).ConfigureAwait(false); + + /// + async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) + => await GetPinnedMessagesAsync(options).ConfigureAwait(false); + } +}