Added remaining gateway events, added IAudioChannel, added CacheModes
This commit is contained in:
@@ -30,7 +30,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Discord.Net": "1.0.0-*"
|
"Discord.Net.Core": {
|
||||||
|
"target": "project"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
|||||||
8
src/Discord.Net.Core/Entities/CacheMode.cs
Normal file
8
src/Discord.Net.Core/Entities/CacheMode.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
public enum CacheMode
|
||||||
|
{
|
||||||
|
AllowDownload,
|
||||||
|
CacheOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs
Normal file
6
src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
public interface IAudioChannel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,10 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
public interface IChannel : ISnowflakeEntity
|
public interface IChannel : ISnowflakeEntity
|
||||||
{
|
{
|
||||||
IReadOnlyCollection<IUser> CachedUsers { get; }
|
|
||||||
|
|
||||||
/// <summary> Gets a collection of all users in this channel. </summary>
|
/// <summary> Gets a collection of all users in this channel. </summary>
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> GetUsersAsync();
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload);
|
||||||
|
|
||||||
/// <summary> Gets a user in this channel with the provided id.</summary>
|
/// <summary> Gets a user in this channel with the provided id.</summary>
|
||||||
Task<IUser> GetUserAsync(ulong id);
|
Task<IUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload);
|
||||||
IUser GetCachedUser(ulong id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
public interface IGroupChannel : IMessageChannel, IPrivateChannel
|
public interface IGroupChannel : IMessageChannel, IPrivateChannel, IAudioChannel
|
||||||
{
|
{
|
||||||
///// <summary> Adds a user to this group. </summary>
|
|
||||||
//Task AddUserAsync(IUser user);
|
|
||||||
|
|
||||||
//new IReadOnlyCollection<IGroupUser> CachedUsers { get; }
|
|
||||||
|
|
||||||
/// <summary> Leaves this group. </summary>
|
/// <summary> Leaves this group. </summary>
|
||||||
Task LeaveAsync();
|
Task LeaveAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ namespace Discord
|
|||||||
|
|
||||||
/// <summary> Gets the id of the guild this channel is a member of. </summary>
|
/// <summary> Gets the id of the guild this channel is a member of. </summary>
|
||||||
ulong GuildId { get; }
|
ulong GuildId { get; }
|
||||||
new IReadOnlyCollection<IGuildUser> CachedUsers { get; }
|
/// <summary> Gets a collection of permission overwrites for this channel. </summary>
|
||||||
|
IReadOnlyCollection<Overwrite> PermissionOverwrites { get; }
|
||||||
|
|
||||||
/// <summary> Creates a new invite to this channel. </summary>
|
/// <summary> Creates a new invite to this channel. </summary>
|
||||||
/// <param name="maxAge"> The time (in seconds) until the invite expires. Set to null to never expire. </param>
|
/// <param name="maxAge"> The time (in seconds) until the invite expires. Set to null to never expire. </param>
|
||||||
@@ -23,9 +24,6 @@ namespace Discord
|
|||||||
Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false);
|
Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false);
|
||||||
/// <summary> Returns a collection of all invites to this channel. </summary>
|
/// <summary> Returns a collection of all invites to this channel. </summary>
|
||||||
Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync();
|
Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync();
|
||||||
|
|
||||||
/// <summary> Gets a collection of permission overwrites for this channel. </summary>
|
|
||||||
IReadOnlyCollection<Overwrite> PermissionOverwrites { get; }
|
|
||||||
|
|
||||||
/// <summary> Modifies this guild channel. </summary>
|
/// <summary> Modifies this guild channel. </summary>
|
||||||
Task ModifyAsync(Action<ModifyGuildChannelParams> func);
|
Task ModifyAsync(Action<ModifyGuildChannelParams> func);
|
||||||
@@ -44,9 +42,8 @@ namespace Discord
|
|||||||
Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions);
|
Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions);
|
||||||
|
|
||||||
/// <summary> Gets a collection of all users in this channel. </summary>
|
/// <summary> Gets a collection of all users in this channel. </summary>
|
||||||
new IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> GetUsersAsync();
|
new IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload);
|
||||||
/// <summary> Gets a user in this channel with the provided id.</summary>
|
/// <summary> Gets a user in this channel with the provided id.</summary>
|
||||||
new Task<IGuildUser> GetUserAsync(ulong id);
|
new Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload);
|
||||||
new IGuildUser GetCachedUser(ulong id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,6 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
public interface IMessageChannel : IChannel
|
public interface IMessageChannel : IChannel
|
||||||
{
|
{
|
||||||
/// <summary> Gets all messages in this channel's cache. </summary>
|
|
||||||
IReadOnlyCollection<IMessage> CachedMessages { get; }
|
|
||||||
|
|
||||||
/// <summary> Sends a message to this message channel. </summary>
|
/// <summary> Sends a message to this message channel. </summary>
|
||||||
Task<IUserMessage> SendMessageAsync(string text, bool isTTS = false);
|
Task<IUserMessage> SendMessageAsync(string text, bool isTTS = false);
|
||||||
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
|
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
|
||||||
@@ -18,13 +15,11 @@ namespace Discord
|
|||||||
Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false);
|
Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false);
|
||||||
|
|
||||||
/// <summary> Gets a message from this message channel with the given id, or null if not found. </summary>
|
/// <summary> Gets a message from this message channel with the given id, or null if not found. </summary>
|
||||||
Task<IMessage> GetMessageAsync(ulong id);
|
Task<IMessage> GetMessageAsync(ulong id, CacheMode mode = CacheMode.AllowDownload);
|
||||||
/// <summary> Gets the message from this channel's cache with the given id, or null if not found. </summary>
|
|
||||||
IMessage GetCachedMessage(ulong id);
|
|
||||||
/// <summary> Gets the last N messages from this message channel. </summary>
|
/// <summary> Gets the last N messages from this message channel. </summary>
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch);
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, CacheMode mode = CacheMode.AllowDownload);
|
||||||
/// <summary> Gets a collection of messages in this channel. </summary>
|
/// <summary> Gets a collection of messages in this channel. </summary>
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch);
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, CacheMode mode = CacheMode.AllowDownload);
|
||||||
/// <summary> Gets a collection of pinned messages in this channel. </summary>
|
/// <summary> Gets a collection of pinned messages in this channel. </summary>
|
||||||
Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync();
|
Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync();
|
||||||
/// <summary> Bulk deletes multiple messages. </summary>
|
/// <summary> Bulk deletes multiple messages. </summary>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
public interface IVoiceChannel : IGuildChannel
|
public interface IVoiceChannel : IGuildChannel, IAudioChannel
|
||||||
{
|
{
|
||||||
/// <summary> Gets the bitrate, in bits per second, clients in this voice channel are requested to use. </summary>
|
/// <summary> Gets the bitrate, in bits per second, clients in this voice channel are requested to use. </summary>
|
||||||
int Bitrate { get; }
|
int Bitrate { get; }
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
//using Discord.Rest;
|
namespace Discord
|
||||||
using System.Diagnostics;
|
|
||||||
using Model = Discord.API.Ban;
|
|
||||||
|
|
||||||
namespace Discord
|
|
||||||
{
|
{
|
||||||
|
|
||||||
public interface IBan
|
public interface IBan
|
||||||
{
|
{
|
||||||
IUser User { get; }
|
IUser User { get; }
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ namespace Discord
|
|||||||
ulong OwnerId { get; }
|
ulong OwnerId { get; }
|
||||||
/// <summary> Gets the id of the region hosting this guild's voice channels. </summary>
|
/// <summary> Gets the id of the region hosting this guild's voice channels. </summary>
|
||||||
string VoiceRegionId { get; }
|
string VoiceRegionId { get; }
|
||||||
|
|
||||||
/// <summary> Gets the IAudioClient currently associated with this guild. </summary>
|
/// <summary> Gets the IAudioClient currently associated with this guild. </summary>
|
||||||
IAudioClient AudioClient { get; }
|
IAudioClient AudioClient { get; }
|
||||||
/// <summary> Gets the built-in role containing all users in this guild. </summary>
|
/// <summary> Gets the built-in role containing all users in this guild. </summary>
|
||||||
@@ -52,7 +51,6 @@ namespace Discord
|
|||||||
IReadOnlyCollection<string> Features { get; }
|
IReadOnlyCollection<string> Features { get; }
|
||||||
/// <summary> Gets a collection of all roles in this guild. </summary>
|
/// <summary> Gets a collection of all roles in this guild. </summary>
|
||||||
IReadOnlyCollection<IRole> Roles { get; }
|
IReadOnlyCollection<IRole> Roles { get; }
|
||||||
IReadOnlyCollection<IGuildUser> CachedUsers { get; }
|
|
||||||
|
|
||||||
/// <summary> Modifies this guild. </summary>
|
/// <summary> Modifies this guild. </summary>
|
||||||
Task ModifyAsync(Action<ModifyGuildParams> func);
|
Task ModifyAsync(Action<ModifyGuildParams> func);
|
||||||
@@ -77,10 +75,9 @@ namespace Discord
|
|||||||
Task RemoveBanAsync(ulong userId);
|
Task RemoveBanAsync(ulong userId);
|
||||||
|
|
||||||
/// <summary> Gets a collection of all channels in this guild. </summary>
|
/// <summary> Gets a collection of all channels in this guild. </summary>
|
||||||
Task<IReadOnlyCollection<IGuildChannel>> GetChannelsAsync();
|
Task<IReadOnlyCollection<IGuildChannel>> GetChannelsAsync(CacheMode mode = CacheMode.AllowDownload);
|
||||||
/// <summary> Gets the channel in this guild with the provided id, or null if not found. </summary>
|
/// <summary> Gets the channel in this guild with the provided id, or null if not found. </summary>
|
||||||
Task<IGuildChannel> GetChannelAsync(ulong id);
|
Task<IGuildChannel> GetChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload);
|
||||||
IGuildChannel GetCachedChannel(ulong id);
|
|
||||||
/// <summary> Creates a new text channel. </summary>
|
/// <summary> Creates a new text channel. </summary>
|
||||||
Task<ITextChannel> CreateTextChannelAsync(string name);
|
Task<ITextChannel> CreateTextChannelAsync(string name);
|
||||||
/// <summary> Creates a new voice channel. </summary>
|
/// <summary> Creates a new voice channel. </summary>
|
||||||
@@ -98,12 +95,11 @@ namespace Discord
|
|||||||
Task<IRole> CreateRoleAsync(string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false);
|
Task<IRole> CreateRoleAsync(string name, GuildPermissions? permissions = null, Color? color = null, bool isHoisted = false);
|
||||||
|
|
||||||
/// <summary> Gets a collection of all users in this guild. </summary>
|
/// <summary> Gets a collection of all users in this guild. </summary>
|
||||||
Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync();
|
Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload); //TODO: shouldnt this be paged?
|
||||||
/// <summary> Gets the user in this guild with the provided id, or null if not found. </summary>
|
/// <summary> Gets the user in this guild with the provided id, or null if not found. </summary>
|
||||||
Task<IGuildUser> GetUserAsync(ulong id);
|
Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload);
|
||||||
IGuildUser GetCachedUser(ulong id);
|
|
||||||
/// <summary> Gets the current user for this guild. </summary>
|
/// <summary> Gets the current user for this guild. </summary>
|
||||||
Task<IGuildUser> GetCurrentUserAsync();
|
Task<IGuildUser> GetCurrentUserAsync(CacheMode mode = CacheMode.AllowDownload);
|
||||||
/// <summary> Downloads all users for this guild if the current list is incomplete. </summary>
|
/// <summary> Downloads all users for this guild if the current list is incomplete. </summary>
|
||||||
Task DownloadUsersAsync();
|
Task DownloadUsersAsync();
|
||||||
/// <summary> Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. </summary>
|
/// <summary> Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. </summary>
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ namespace Discord
|
|||||||
/// <summary> Gets the username for this user. </summary>
|
/// <summary> Gets the username for this user. </summary>
|
||||||
string Username { get; }
|
string Username { get; }
|
||||||
|
|
||||||
/// <summary> Returns a private message channel to this user, returning null if one does not exist or is not cached. </summary>
|
/// <summary> Returns a private message channel to this user, creating one if it does not already exist. </summary>
|
||||||
IDMChannel GetCachedDMChannel();
|
Task<IDMChannel> GetDMChannelAsync(CacheMode mode = CacheMode.AllowDownload);
|
||||||
/// <summary> Returns a private message channel to this user, creating one if it does not already exist. </summary>
|
/// <summary> Returns a private message channel to this user, creating one if it does not already exist. </summary>
|
||||||
Task<IDMChannel> CreateDMChannelAsync();
|
Task<IDMChannel> CreateDMChannelAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,18 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
internal static class TaskCompletionSourceExtensions
|
internal static class TaskCompletionSourceExtensions
|
||||||
{
|
{
|
||||||
|
public static Task SetResultAsync<T>(this TaskCompletionSource<T> source, T result)
|
||||||
|
=> Task.Run(() => source.SetResult(result));
|
||||||
public static Task<bool> TrySetResultAsync<T>(this TaskCompletionSource<T> source, T result)
|
public static Task<bool> TrySetResultAsync<T>(this TaskCompletionSource<T> source, T result)
|
||||||
=> Task.Run(() => source.TrySetResult(result));
|
=> Task.Run(() => source.TrySetResult(result));
|
||||||
|
|
||||||
|
public static Task SetExceptionAsync<T>(this TaskCompletionSource<T> source, Exception ex)
|
||||||
|
=> Task.Run(() => source.SetException(ex));
|
||||||
public static Task<bool> TrySetExceptionAsync<T>(this TaskCompletionSource<T> source, Exception ex)
|
public static Task<bool> TrySetExceptionAsync<T>(this TaskCompletionSource<T> source, Exception ex)
|
||||||
=> Task.Run(() => source.TrySetException(ex));
|
=> Task.Run(() => source.TrySetException(ex));
|
||||||
|
|
||||||
|
public static Task SetCanceledAsync<T>(this TaskCompletionSource<T> source)
|
||||||
|
=> Task.Run(() => source.SetCanceled());
|
||||||
public static Task<bool> TrySetCanceledAsync<T>(this TaskCompletionSource<T> source)
|
public static Task<bool> TrySetCanceledAsync<T>(this TaskCompletionSource<T> source)
|
||||||
=> Task.Run(() => source.TrySetCanceled());
|
=> Task.Run(() => source.TrySetCanceled());
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
internal bool IgnoreState { get; set; }
|
internal bool IgnoreState { get; set; }
|
||||||
|
|
||||||
public static RequestOptions CreateOrClone(RequestOptions options)
|
internal static RequestOptions CreateOrClone(RequestOptions options)
|
||||||
{
|
{
|
||||||
if (options == null)
|
if (options == null)
|
||||||
return new RequestOptions();
|
return new RequestOptions();
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
//Based on https://github.com/dotnet/corefx/blob/master/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs
|
//Based on https://github.com/dotnet/corefx/blob/master/src/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs
|
||||||
//Copyright (c) .NET Foundation and Contributors
|
//Copyright (c) .NET Foundation and Contributors
|
||||||
|
public static class ConcurrentHashSet
|
||||||
|
{
|
||||||
|
public static int DefaultConcurrencyLevel => PlatformHelper.ProcessorCount;
|
||||||
|
}
|
||||||
|
|
||||||
[DebuggerDisplay("Count = {Count}")]
|
[DebuggerDisplay("Count = {Count}")]
|
||||||
internal class ConcurrentHashSet<T> : IReadOnlyCollection<T>
|
internal class ConcurrentHashSet<T> : IReadOnlyCollection<T>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,152 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Discord.Extensions
|
|
||||||
{
|
|
||||||
internal static class Permissions
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static PermValue GetValue(ulong allow, ulong deny, ChannelPermission bit)
|
|
||||||
=> GetValue(allow, deny, (byte)bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static PermValue GetValue(ulong allow, ulong deny, GuildPermission bit)
|
|
||||||
=> GetValue(allow, deny, (byte)bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static PermValue GetValue(ulong allow, ulong deny, byte bit)
|
|
||||||
{
|
|
||||||
if (HasBit(allow, bit))
|
|
||||||
return PermValue.Allow;
|
|
||||||
else if (HasBit(deny, bit))
|
|
||||||
return PermValue.Deny;
|
|
||||||
else
|
|
||||||
return PermValue.Inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool GetValue(ulong value, ChannelPermission bit)
|
|
||||||
=> GetValue(value, (byte)bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool GetValue(ulong value, GuildPermission bit)
|
|
||||||
=> GetValue(value, (byte)bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool GetValue(ulong value, byte bit) => HasBit(value, bit);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SetValue(ref ulong rawValue, bool? value, ChannelPermission bit)
|
|
||||||
=> SetValue(ref rawValue, value, (byte)bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SetValue(ref ulong rawValue, bool? value, GuildPermission bit)
|
|
||||||
=> SetValue(ref rawValue, value, (byte)bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SetValue(ref ulong rawValue, bool? value, byte bit)
|
|
||||||
{
|
|
||||||
if (value.HasValue)
|
|
||||||
{
|
|
||||||
if (value == true)
|
|
||||||
SetBit(ref rawValue, bit);
|
|
||||||
else
|
|
||||||
UnsetBit(ref rawValue, bit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, ChannelPermission bit)
|
|
||||||
=> SetValue(ref allow, ref deny, value, (byte)bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, GuildPermission bit)
|
|
||||||
=> SetValue(ref allow, ref deny, value, (byte)bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SetValue(ref ulong allow, ref ulong deny, PermValue? value, byte bit)
|
|
||||||
{
|
|
||||||
if (value.HasValue)
|
|
||||||
{
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case PermValue.Allow:
|
|
||||||
SetBit(ref allow, bit);
|
|
||||||
UnsetBit(ref deny, bit);
|
|
||||||
break;
|
|
||||||
case PermValue.Deny:
|
|
||||||
UnsetBit(ref allow, bit);
|
|
||||||
SetBit(ref deny, bit);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
UnsetBit(ref allow, bit);
|
|
||||||
UnsetBit(ref deny, bit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static bool HasBit(ulong value, byte bit) => (value & (1U << bit)) != 0;
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SetBit(ref ulong value, byte bit) => value |= (1U << bit);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void UnsetBit(ref ulong value, byte bit) => value &= ~(1U << bit);
|
|
||||||
|
|
||||||
public static ulong ResolveGuild(IGuild guild, IGuildUser user)
|
|
||||||
{
|
|
||||||
ulong resolvedPermissions = 0;
|
|
||||||
|
|
||||||
if (user.Id == guild.OwnerId)
|
|
||||||
resolvedPermissions = GuildPermissions.All.RawValue; //Owners always have all permissions
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var role in user.RoleIds)
|
|
||||||
resolvedPermissions |= guild.GetRole(role).Permissions.RawValue;
|
|
||||||
if (GetValue(resolvedPermissions, GuildPermission.Administrator))
|
|
||||||
resolvedPermissions = GuildPermissions.All.RawValue; //Administrators always have all permissions
|
|
||||||
}
|
|
||||||
return resolvedPermissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*public static ulong ResolveChannel(IGuildUser user, IGuildChannel channel)
|
|
||||||
{
|
|
||||||
return ResolveChannel(user, channel, ResolveGuild(user));
|
|
||||||
}*/
|
|
||||||
public static ulong ResolveChannel(IGuild guild, IGuildChannel channel, IGuildUser user, ulong guildPermissions)
|
|
||||||
{
|
|
||||||
ulong resolvedPermissions = 0;
|
|
||||||
|
|
||||||
ulong mask = ChannelPermissions.All(channel).RawValue;
|
|
||||||
if (/*user.Id == user.Guild.OwnerId || */GetValue(guildPermissions, GuildPermission.Administrator))
|
|
||||||
resolvedPermissions = mask; //Owners and administrators always have all permissions
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Start with this user's guild permissions
|
|
||||||
resolvedPermissions = guildPermissions;
|
|
||||||
|
|
||||||
OverwritePermissions? perms;
|
|
||||||
var roleIds = user.RoleIds;
|
|
||||||
if (roleIds.Count > 0)
|
|
||||||
{
|
|
||||||
ulong deniedPermissions = 0UL, allowedPermissions = 0UL;
|
|
||||||
foreach (var roleId in roleIds)
|
|
||||||
{
|
|
||||||
perms = channel.GetPermissionOverwrite(guild.GetRole(roleId));
|
|
||||||
if (perms != null)
|
|
||||||
{
|
|
||||||
deniedPermissions |= perms.Value.DenyValue;
|
|
||||||
allowedPermissions |= perms.Value.AllowValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolvedPermissions = (resolvedPermissions & ~deniedPermissions) | allowedPermissions;
|
|
||||||
}
|
|
||||||
perms = channel.GetPermissionOverwrite(user);
|
|
||||||
if (perms != null)
|
|
||||||
resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue;
|
|
||||||
|
|
||||||
//TODO: C#7 Typeswitch candidate
|
|
||||||
var textChannel = channel as ITextChannel;
|
|
||||||
var voiceChannel = channel as IVoiceChannel;
|
|
||||||
if (textChannel != null && !GetValue(resolvedPermissions, ChannelPermission.ReadMessages))
|
|
||||||
resolvedPermissions = 0; //No read permission on a text channel removes all other permissions
|
|
||||||
else if (voiceChannel != null && !GetValue(resolvedPermissions, ChannelPermission.Connect))
|
|
||||||
resolvedPermissions = 0; //No connect permission on a voice channel removes all other permissions
|
|
||||||
resolvedPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from guildPerms, for example)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolvedPermissions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ using System.Collections.Immutable;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
@@ -34,7 +35,7 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
if (userMention.Id == id)
|
if (userMention.Id == id)
|
||||||
{
|
{
|
||||||
user = channel?.GetCachedUser(id) as TUser;
|
user = channel?.GetUserAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult() as TUser;
|
||||||
if (user == null) //User not found, fallback to basic mention info
|
if (user == null) //User not found, fallback to basic mention info
|
||||||
user = userMention;
|
user = userMention;
|
||||||
break;
|
break;
|
||||||
@@ -136,7 +137,7 @@ namespace Discord
|
|||||||
return "";
|
return "";
|
||||||
case ChannelMentionHandling.Name:
|
case ChannelMentionHandling.Name:
|
||||||
IGuildChannel channel = null;
|
IGuildChannel channel = null;
|
||||||
channel = guild.GetCachedChannel(id);
|
channel = guild.GetChannelAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult();
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
return $"#{channel.Name}";
|
return $"#{channel.Name}";
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Discord.Rest
|
||||||
|
{
|
||||||
|
public interface IRestAudioChannel : IAudioChannel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Discord.Rest
|
||||||
|
{
|
||||||
|
public interface IRestMessageChannel : IMessageChannel
|
||||||
|
{
|
||||||
|
/// <summary> Sends a message to this message channel. </summary>
|
||||||
|
new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false);
|
||||||
|
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
|
||||||
|
new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false);
|
||||||
|
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
|
||||||
|
new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false);
|
||||||
|
|
||||||
|
/// <summary> Gets a message from this message channel with the given id, or null if not found. </summary>
|
||||||
|
Task<RestMessage> GetMessageAsync(ulong id);
|
||||||
|
/// <summary> Gets the last N messages from this message channel. </summary>
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch);
|
||||||
|
/// <summary> Gets a collection of messages in this channel. </summary>
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch);
|
||||||
|
/// <summary> Gets a collection of pinned messages in this channel. </summary>
|
||||||
|
new Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Discord.Rest
|
||||||
|
{
|
||||||
|
public interface IRestPrivateChannel : IPrivateChannel
|
||||||
|
{
|
||||||
|
new IReadOnlyCollection<RestUser> Recipients { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,17 @@ namespace Discord.Rest
|
|||||||
return RestTextChannel.Create(discord, model);
|
return RestTextChannel.Create(discord, model);
|
||||||
case ChannelType.Voice:
|
case ChannelType.Voice:
|
||||||
return RestVoiceChannel.Create(discord, model);
|
return RestVoiceChannel.Create(discord, model);
|
||||||
|
case ChannelType.DM:
|
||||||
|
case ChannelType.Group:
|
||||||
|
return CreatePrivate(discord, model) as RestChannel;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"Unexpected channel type: {model.Type}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal static IRestPrivateChannel CreatePrivate(BaseDiscordClient discord, Model model)
|
||||||
|
{
|
||||||
|
switch (model.Type)
|
||||||
|
{
|
||||||
case ChannelType.DM:
|
case ChannelType.DM:
|
||||||
return RestDMChannel.Create(discord, model);
|
return RestDMChannel.Create(discord, model);
|
||||||
case ChannelType.Group:
|
case ChannelType.Group:
|
||||||
@@ -35,14 +46,10 @@ namespace Discord.Rest
|
|||||||
|
|
||||||
public abstract Task UpdateAsync();
|
public abstract Task UpdateAsync();
|
||||||
|
|
||||||
//IChannel
|
//IChannel
|
||||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => ImmutableArray.Create<IUser>();
|
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
|
=> Task.FromResult<IUser>(null); //Overriden
|
||||||
IUser IChannel.GetCachedUser(ulong id)
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> null;
|
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable(); //Overriden
|
||||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
|
||||||
=> Task.FromResult<IUser>(null);
|
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using Model = Discord.API.Channel;
|
|||||||
namespace Discord.Rest
|
namespace Discord.Rest
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public class RestDMChannel : RestChannel, IDMChannel, IUpdateable
|
public class RestDMChannel : RestChannel, IDMChannel, IRestPrivateChannel, IRestMessageChannel, IUpdateable
|
||||||
{
|
{
|
||||||
public RestUser CurrentUser { get; private set; }
|
public RestUser CurrentUser { get; private set; }
|
||||||
public RestUser Recipient { get; private set; }
|
public RestUser Recipient { get; private set; }
|
||||||
@@ -75,23 +75,39 @@ namespace Discord.Rest
|
|||||||
|
|
||||||
public override string ToString() => $"@{Recipient}";
|
public override string ToString() => $"@{Recipient}";
|
||||||
private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)";
|
private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)";
|
||||||
|
|
||||||
|
|
||||||
//IDMChannel
|
//IDMChannel
|
||||||
IUser IDMChannel.Recipient => Recipient;
|
IUser IDMChannel.Recipient => Recipient;
|
||||||
|
|
||||||
|
//IRestPrivateChannel
|
||||||
|
IReadOnlyCollection<RestUser> IRestPrivateChannel.Recipients => ImmutableArray.Create(Recipient);
|
||||||
|
|
||||||
//IPrivateChannel
|
//IPrivateChannel
|
||||||
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create<IUser>(Recipient);
|
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create<IUser>(Recipient);
|
||||||
|
|
||||||
//IMessageChannel
|
//IMessageChannel
|
||||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages => ImmutableArray.Create<IMessage>();
|
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||||
IMessage IMessageChannel.GetCachedMessage(ulong id) => null;
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
return await GetMessageAsync(id);
|
||||||
=> await GetMessageAsync(id);
|
else
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
return null;
|
||||||
=> GetMessagesAsync(limit);
|
}
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return GetMessagesAsync(limit);
|
||||||
|
else
|
||||||
|
return ImmutableArray.Create<IReadOnlyCollection<IMessage>>().ToAsyncEnumerable();
|
||||||
|
}
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return GetMessagesAsync(fromMessageId, dir, limit);
|
||||||
|
else
|
||||||
|
return ImmutableArray.Create<IReadOnlyCollection<IMessage>>().ToAsyncEnumerable();
|
||||||
|
}
|
||||||
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
||||||
=> await GetPinnedMessagesAsync().ConfigureAwait(false);
|
=> await GetPinnedMessagesAsync().ConfigureAwait(false);
|
||||||
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS)
|
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS)
|
||||||
@@ -103,14 +119,10 @@ namespace Discord.Rest
|
|||||||
IDisposable IMessageChannel.EnterTypingState()
|
IDisposable IMessageChannel.EnterTypingState()
|
||||||
=> EnterTypingState();
|
=> EnterTypingState();
|
||||||
|
|
||||||
//IChannel
|
//IChannel
|
||||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => Users;
|
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
|
|
||||||
IUser IChannel.GetCachedUser(ulong id)
|
|
||||||
=> GetUser(id);
|
|
||||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
|
||||||
=> Task.FromResult<IUser>(GetUser(id));
|
=> Task.FromResult<IUser>(GetUser(id));
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable();
|
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ using Model = Discord.API.Channel;
|
|||||||
namespace Discord.Rest
|
namespace Discord.Rest
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public class RestGroupChannel : RestChannel, IGroupChannel, IUpdateable
|
public class RestGroupChannel : RestChannel, IGroupChannel, IRestPrivateChannel, IRestMessageChannel, IRestAudioChannel, IUpdateable
|
||||||
{
|
{
|
||||||
private string _iconId;
|
private string _iconId;
|
||||||
private ImmutableDictionary<ulong, RestGroupUser> _users;
|
private ImmutableDictionary<ulong, RestGroupUser> _users;
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
public IReadOnlyCollection<RestGroupUser> Users => _users.ToReadOnlyCollection();
|
public IReadOnlyCollection<RestGroupUser> Users => _users.ToReadOnlyCollection();
|
||||||
@@ -86,20 +86,34 @@ namespace Discord.Rest
|
|||||||
public IDisposable EnterTypingState()
|
public IDisposable EnterTypingState()
|
||||||
=> ChannelHelper.EnterTypingState(this, Discord);
|
=> ChannelHelper.EnterTypingState(this, Discord);
|
||||||
|
|
||||||
|
//ISocketPrivateChannel
|
||||||
|
IReadOnlyCollection<RestUser> IRestPrivateChannel.Recipients => Recipients;
|
||||||
|
|
||||||
//IPrivateChannel
|
//IPrivateChannel
|
||||||
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => Recipients;
|
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => Recipients;
|
||||||
|
|
||||||
//IMessageChannel
|
//IMessageChannel
|
||||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages => ImmutableArray.Create<IMessage>();
|
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||||
|
{
|
||||||
IMessage IMessageChannel.GetCachedMessage(ulong id)
|
if (mode == CacheMode.AllowDownload)
|
||||||
=> null;
|
return await GetMessageAsync(id);
|
||||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
else
|
||||||
=> await GetMessageAsync(id);
|
return null;
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
}
|
||||||
=> GetMessagesAsync(limit);
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
{
|
||||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return GetMessagesAsync(limit);
|
||||||
|
else
|
||||||
|
return ImmutableArray.Create<IReadOnlyCollection<IMessage>>().ToAsyncEnumerable();
|
||||||
|
}
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return GetMessagesAsync(fromMessageId, dir, limit);
|
||||||
|
else
|
||||||
|
return ImmutableArray.Create<IReadOnlyCollection<IMessage>>().ToAsyncEnumerable();
|
||||||
|
}
|
||||||
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
||||||
=> await GetPinnedMessagesAsync();
|
=> await GetPinnedMessagesAsync();
|
||||||
|
|
||||||
@@ -112,14 +126,10 @@ namespace Discord.Rest
|
|||||||
IDisposable IMessageChannel.EnterTypingState()
|
IDisposable IMessageChannel.EnterTypingState()
|
||||||
=> EnterTypingState();
|
=> EnterTypingState();
|
||||||
|
|
||||||
//IChannel
|
//IChannel
|
||||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => Users;
|
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
|
|
||||||
IUser IChannel.GetCachedUser(ulong id)
|
|
||||||
=> GetUser(id);
|
|
||||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
|
||||||
=> Task.FromResult(GetUser(id));
|
=> Task.FromResult(GetUser(id));
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable();
|
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,24 +135,16 @@ namespace Discord.Rest
|
|||||||
=> await RemovePermissionOverwriteAsync(role);
|
=> await RemovePermissionOverwriteAsync(role);
|
||||||
async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user)
|
async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user)
|
||||||
=> await RemovePermissionOverwriteAsync(user);
|
=> await RemovePermissionOverwriteAsync(user);
|
||||||
|
|
||||||
IReadOnlyCollection<IGuildUser> IGuildChannel.CachedUsers
|
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IGuildUser>();
|
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||||
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id)
|
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> Task.FromResult<IGuildUser>(null); //Overriden in Text/Voice //TODO: Does this actually override?
|
=> Task.FromResult<IGuildUser>(null); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||||
IGuildUser IGuildChannel.GetCachedUser(ulong id)
|
|
||||||
=> null;
|
|
||||||
|
|
||||||
//IChannel
|
//IChannel
|
||||||
IReadOnlyCollection<IUser> IChannel.CachedUsers
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IUser>();
|
|
||||||
IUser IChannel.GetCachedUser(ulong id)
|
|
||||||
=> null;
|
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> Task.FromResult<IUser>(null); //Overriden in Text/Voice //TODO: Does this actually override?
|
=> Task.FromResult<IUser>(null); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Model = Discord.API.Channel;
|
using Model = Discord.API.Channel;
|
||||||
|
|
||||||
namespace Discord.Rest
|
namespace Discord.Rest
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public class RestTextChannel : RestGuildChannel, ITextChannel
|
public class RestTextChannel : RestGuildChannel, IRestMessageChannel, ITextChannel
|
||||||
{
|
{
|
||||||
public string Topic { get; private set; }
|
public string Topic { get; private set; }
|
||||||
|
|
||||||
@@ -67,22 +68,43 @@ namespace Discord.Rest
|
|||||||
=> ChannelHelper.EnterTypingState(this, Discord);
|
=> ChannelHelper.EnterTypingState(this, Discord);
|
||||||
|
|
||||||
//IGuildChannel
|
//IGuildChannel
|
||||||
async Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id)
|
async Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> await GetUserAsync(id);
|
{
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
if (mode == CacheMode.AllowDownload)
|
||||||
=> GetUsersAsync();
|
return await GetUserAsync(id);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return GetUsersAsync();
|
||||||
|
else
|
||||||
|
return ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable();
|
||||||
|
}
|
||||||
|
|
||||||
//IMessageChannel
|
//IMessageChannel
|
||||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages
|
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||||
=> ImmutableArray.Create<IMessage>();
|
{
|
||||||
IMessage IMessageChannel.GetCachedMessage(ulong id)
|
if (mode == CacheMode.AllowDownload)
|
||||||
=> null;
|
return await GetMessageAsync(id);
|
||||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
else
|
||||||
=> await GetMessageAsync(id);
|
return null;
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
}
|
||||||
=> GetMessagesAsync(limit);
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
{
|
||||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return GetMessagesAsync(limit);
|
||||||
|
else
|
||||||
|
return ImmutableArray.Create<IReadOnlyCollection<IMessage>>().ToAsyncEnumerable();
|
||||||
|
}
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return GetMessagesAsync(fromMessageId, dir, limit);
|
||||||
|
else
|
||||||
|
return ImmutableArray.Create<IReadOnlyCollection<IMessage>>().ToAsyncEnumerable();
|
||||||
|
}
|
||||||
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
||||||
=> await GetPinnedMessagesAsync();
|
=> await GetPinnedMessagesAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ using Model = Discord.API.Channel;
|
|||||||
namespace Discord.Rest
|
namespace Discord.Rest
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public class RestVoiceChannel : RestGuildChannel, IVoiceChannel
|
public class RestVoiceChannel : RestGuildChannel, IVoiceChannel, IRestAudioChannel
|
||||||
{
|
{
|
||||||
public int Bitrate { get; private set; }
|
public int Bitrate { get; private set; }
|
||||||
public int UserLimit { get; private set; }
|
public int UserLimit { get; private set; }
|
||||||
@@ -41,9 +41,9 @@ namespace Discord.Rest
|
|||||||
Task<IAudioClient> IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); }
|
Task<IAudioClient> IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); }
|
||||||
|
|
||||||
//IGuildChannel
|
//IGuildChannel
|
||||||
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id)
|
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> Task.FromResult<IGuildUser>(null);
|
=> Task.FromResult<IGuildUser>(null);
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable();
|
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Discord.API.Rest;
|
using Discord.API.Rest;
|
||||||
using Model = Discord.API.Guild;
|
using Model = Discord.API.Guild;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Discord.Rest
|
namespace Discord.Rest
|
||||||
{
|
{
|
||||||
@@ -149,7 +150,7 @@ namespace Discord.Rest
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IRole> CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false)
|
public async Task<RestRole> CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false)
|
||||||
{
|
{
|
||||||
var role = await GuildHelper.CreateRoleAsync(this, Discord, name, permissions, color, isHoisted);
|
var role = await GuildHelper.CreateRoleAsync(this, Discord, name, permissions, color, isHoisted);
|
||||||
_roles = _roles.Add(role.Id, role);
|
_roles = _roles.Add(role.Id, role);
|
||||||
@@ -157,8 +158,8 @@ namespace Discord.Rest
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Users
|
//Users
|
||||||
public Task<IReadOnlyCollection<RestGuildUser>> GetUsersAsync()
|
public IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync()
|
||||||
=> GuildHelper.GetUsersAsync(this, Discord);
|
=> GuildHelper.GetUsersAsync(this, Discord).ToAsyncEnumerable();
|
||||||
public Task<RestGuildUser> GetUserAsync(ulong id)
|
public Task<RestGuildUser> GetUserAsync(ulong id)
|
||||||
=> GuildHelper.GetUserAsync(this, Discord, id);
|
=> GuildHelper.GetUserAsync(this, Discord, id);
|
||||||
public Task<RestGuildUser> GetCurrentUserAsync()
|
public Task<RestGuildUser> GetCurrentUserAsync()
|
||||||
@@ -170,19 +171,26 @@ namespace Discord.Rest
|
|||||||
//IGuild
|
//IGuild
|
||||||
bool IGuild.Available => true;
|
bool IGuild.Available => true;
|
||||||
IAudioClient IGuild.AudioClient => null;
|
IAudioClient IGuild.AudioClient => null;
|
||||||
IReadOnlyCollection<IGuildUser> IGuild.CachedUsers => ImmutableArray.Create<IGuildUser>();
|
|
||||||
IRole IGuild.EveryoneRole => EveryoneRole;
|
IRole IGuild.EveryoneRole => EveryoneRole;
|
||||||
IReadOnlyCollection<IRole> IGuild.Roles => Roles;
|
IReadOnlyCollection<IRole> IGuild.Roles => Roles;
|
||||||
|
|
||||||
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync()
|
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync()
|
||||||
=> await GetBansAsync();
|
=> await GetBansAsync();
|
||||||
|
|
||||||
async Task<IReadOnlyCollection<IGuildChannel>> IGuild.GetChannelsAsync()
|
async Task<IReadOnlyCollection<IGuildChannel>> IGuild.GetChannelsAsync(CacheMode mode)
|
||||||
=> await GetChannelsAsync();
|
{
|
||||||
async Task<IGuildChannel> IGuild.GetChannelAsync(ulong id)
|
if (mode == CacheMode.AllowDownload)
|
||||||
=> await GetChannelAsync(id);
|
return await GetChannelsAsync();
|
||||||
IGuildChannel IGuild.GetCachedChannel(ulong id)
|
else
|
||||||
=> null;
|
return ImmutableArray.Create<IGuildChannel>();
|
||||||
|
}
|
||||||
|
async Task<IGuildChannel> IGuild.GetChannelAsync(ulong id, CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return await GetChannelAsync(id);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name)
|
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name)
|
||||||
=> await CreateTextChannelAsync(name);
|
=> await CreateTextChannelAsync(name);
|
||||||
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name)
|
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name)
|
||||||
@@ -198,15 +206,30 @@ namespace Discord.Rest
|
|||||||
|
|
||||||
IRole IGuild.GetRole(ulong id)
|
IRole IGuild.GetRole(ulong id)
|
||||||
=> GetRole(id);
|
=> GetRole(id);
|
||||||
|
async Task<IRole> IGuild.CreateRoleAsync(string name, GuildPermissions? permissions, Color? color, bool isHoisted)
|
||||||
|
=> await CreateRoleAsync(name, permissions, color, isHoisted);
|
||||||
|
|
||||||
async Task<IReadOnlyCollection<IGuildUser>> IGuild.GetUsersAsync()
|
async Task<IGuildUser> IGuild.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> await GetUsersAsync();
|
{
|
||||||
async Task<IGuildUser> IGuild.GetUserAsync(ulong id)
|
if (mode == CacheMode.AllowDownload)
|
||||||
=> await GetUserAsync(id);
|
return await GetUserAsync(id);
|
||||||
IGuildUser IGuild.GetCachedUser(ulong id)
|
else
|
||||||
=> null;
|
return null;
|
||||||
async Task<IGuildUser> IGuild.GetCurrentUserAsync()
|
}
|
||||||
=> await GetCurrentUserAsync();
|
async Task<IGuildUser> IGuild.GetCurrentUserAsync(CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return await GetCurrentUserAsync();
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
async Task<IReadOnlyCollection<IGuildUser>> IGuild.GetUsersAsync(CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return (await GetUsersAsync().Flatten()).ToImmutableArray();
|
||||||
|
else
|
||||||
|
return ImmutableArray.Create<IGuildUser>();
|
||||||
|
}
|
||||||
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }
|
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Model = Discord.API.User;
|
using Model = Discord.API.User;
|
||||||
|
|
||||||
@@ -42,10 +43,13 @@ namespace Discord.Rest
|
|||||||
|
|
||||||
public virtual async Task UpdateAsync()
|
public virtual async Task UpdateAsync()
|
||||||
=> Update(await UserHelper.GetAsync(this, Discord));
|
=> Update(await UserHelper.GetAsync(this, Discord));
|
||||||
|
|
||||||
public Task<IDMChannel> CreateDMChannelAsync()
|
public Task<RestDMChannel> CreateDMChannelAsync()
|
||||||
=> UserHelper.CreateDMChannelAsync(this, Discord);
|
=> UserHelper.CreateDMChannelAsync(this, Discord);
|
||||||
|
|
||||||
IDMChannel IUser.GetCachedDMChannel() => null;
|
Task<IDMChannel> IUser.GetDMChannelAsync(CacheMode mode)
|
||||||
|
=> Task.FromResult<IDMChannel>(null);
|
||||||
|
async Task<IDMChannel> IUser.CreateDMChannelAsync()
|
||||||
|
=> await CreateDMChannelAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Discord.Rest
|
|||||||
await client.ApiClient.RemoveGuildMemberAsync(user.GuildId, user.Id);
|
await client.ApiClient.RemoveGuildMemberAsync(user.GuildId, user.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<IDMChannel> CreateDMChannelAsync(IUser user, BaseDiscordClient client)
|
public static async Task<RestDMChannel> CreateDMChannelAsync(IUser user, BaseDiscordClient client)
|
||||||
{
|
{
|
||||||
var args = new CreateDMChannelParams(user.Id);
|
var args = new CreateDMChannelParams(user.Id);
|
||||||
return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args));
|
return RestDMChannel.Create(client, await client.ApiClient.CreateDMChannelAsync(args));
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Discord.Audio
|
namespace Discord.Audio
|
||||||
{
|
{
|
||||||
internal class AudioClient : IAudioClient, IDisposable
|
public class AudioClient : IAudioClient, IDisposable
|
||||||
{
|
{
|
||||||
public event Func<Task> Connected
|
public event Func<Task> Connected
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@ namespace Discord.Audio
|
|||||||
private DiscordSocketClient Discord => Guild.Discord;
|
private DiscordSocketClient Discord => Guild.Discord;
|
||||||
|
|
||||||
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
||||||
public AudioClient(SocketGuild guild, int id)
|
internal AudioClient(SocketGuild guild, int id)
|
||||||
{
|
{
|
||||||
Guild = guild;
|
Guild = guild;
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ namespace Discord.Audio
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task ConnectAsync(string url, ulong userId, string sessionId, string token)
|
internal async Task ConnectAsync(string url, ulong userId, string sessionId, string token)
|
||||||
{
|
{
|
||||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ namespace Discord.WebSocket
|
|||||||
internal IReadOnlyCollection<SocketGuild> Guilds => _guilds.ToReadOnlyCollection();
|
internal IReadOnlyCollection<SocketGuild> Guilds => _guilds.ToReadOnlyCollection();
|
||||||
internal IReadOnlyCollection<SocketGlobalUser> Users => _users.ToReadOnlyCollection();
|
internal IReadOnlyCollection<SocketGlobalUser> Users => _users.ToReadOnlyCollection();
|
||||||
|
|
||||||
internal IReadOnlyCollection<IPrivateChannel> PrivateChannels =>
|
internal IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels =>
|
||||||
_dmChannels.Select(x => x.Value as IPrivateChannel).Concat(
|
_dmChannels.Select(x => x.Value as ISocketPrivateChannel).Concat(
|
||||||
_groupChannels.Select(x => GetChannel(x) as IPrivateChannel))
|
_groupChannels.Select(x => GetChannel(x) as ISocketPrivateChannel))
|
||||||
.ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count);
|
.ToReadOnlyCollection(() => _dmChannels.Count + _groupChannels.Count);
|
||||||
|
|
||||||
public ClientState(int guildCount, int dmChannelCount)
|
public ClientState(int guildCount, int dmChannelCount)
|
||||||
|
|||||||
@@ -33,170 +33,170 @@ namespace Discord.WebSocket
|
|||||||
private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>();
|
private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>();
|
||||||
|
|
||||||
//Channels
|
//Channels
|
||||||
public event Func<IChannel, Task> ChannelCreated
|
public event Func<SocketChannel, Task> ChannelCreated
|
||||||
{
|
{
|
||||||
add { _channelCreatedEvent.Add(value); }
|
add { _channelCreatedEvent.Add(value); }
|
||||||
remove { _channelCreatedEvent.Remove(value); }
|
remove { _channelCreatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IChannel, Task>> _channelCreatedEvent = new AsyncEvent<Func<IChannel, Task>>();
|
private readonly AsyncEvent<Func<SocketChannel, Task>> _channelCreatedEvent = new AsyncEvent<Func<SocketChannel, Task>>();
|
||||||
public event Func<IChannel, Task> ChannelDestroyed
|
public event Func<SocketChannel, Task> ChannelDestroyed
|
||||||
{
|
{
|
||||||
add { _channelDestroyedEvent.Add(value); }
|
add { _channelDestroyedEvent.Add(value); }
|
||||||
remove { _channelDestroyedEvent.Remove(value); }
|
remove { _channelDestroyedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IChannel, Task>> _channelDestroyedEvent = new AsyncEvent<Func<IChannel, Task>>();
|
private readonly AsyncEvent<Func<SocketChannel, Task>> _channelDestroyedEvent = new AsyncEvent<Func<SocketChannel, Task>>();
|
||||||
public event Func<IChannel, IChannel, Task> ChannelUpdated
|
public event Func<SocketChannel, SocketChannel, Task> ChannelUpdated
|
||||||
{
|
{
|
||||||
add { _channelUpdatedEvent.Add(value); }
|
add { _channelUpdatedEvent.Add(value); }
|
||||||
remove { _channelUpdatedEvent.Remove(value); }
|
remove { _channelUpdatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IChannel, IChannel, Task>> _channelUpdatedEvent = new AsyncEvent<Func<IChannel, IChannel, Task>>();
|
private readonly AsyncEvent<Func<SocketChannel, SocketChannel, Task>> _channelUpdatedEvent = new AsyncEvent<Func<SocketChannel, SocketChannel, Task>>();
|
||||||
|
|
||||||
//Messages
|
//Messages
|
||||||
public event Func<IMessage, Task> MessageReceived
|
public event Func<SocketMessage, Task> MessageReceived
|
||||||
{
|
{
|
||||||
add { _messageReceivedEvent.Add(value); }
|
add { _messageReceivedEvent.Add(value); }
|
||||||
remove { _messageReceivedEvent.Remove(value); }
|
remove { _messageReceivedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IMessage, Task>> _messageReceivedEvent = new AsyncEvent<Func<IMessage, Task>>();
|
private readonly AsyncEvent<Func<SocketMessage, Task>> _messageReceivedEvent = new AsyncEvent<Func<SocketMessage, Task>>();
|
||||||
public event Func<ulong, Optional<IMessage>, Task> MessageDeleted
|
public event Func<ulong, Optional<SocketMessage>, Task> MessageDeleted
|
||||||
{
|
{
|
||||||
add { _messageDeletedEvent.Add(value); }
|
add { _messageDeletedEvent.Add(value); }
|
||||||
remove { _messageDeletedEvent.Remove(value); }
|
remove { _messageDeletedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<ulong, Optional<IMessage>, Task>> _messageDeletedEvent = new AsyncEvent<Func<ulong, Optional<IMessage>, Task>>();
|
private readonly AsyncEvent<Func<ulong, Optional<SocketMessage>, Task>> _messageDeletedEvent = new AsyncEvent<Func<ulong, Optional<SocketMessage>, Task>>();
|
||||||
public event Func<Optional<IMessage>, IMessage, Task> MessageUpdated
|
public event Func<Optional<SocketMessage>, SocketMessage, Task> MessageUpdated
|
||||||
{
|
{
|
||||||
add { _messageUpdatedEvent.Add(value); }
|
add { _messageUpdatedEvent.Add(value); }
|
||||||
remove { _messageUpdatedEvent.Remove(value); }
|
remove { _messageUpdatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<Optional<IMessage>, IMessage, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Optional<IMessage>, IMessage, Task>>();
|
private readonly AsyncEvent<Func<Optional<SocketMessage>, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Optional<SocketMessage>, SocketMessage, Task>>();
|
||||||
|
|
||||||
//Roles
|
//Roles
|
||||||
public event Func<IRole, Task> RoleCreated
|
public event Func<SocketRole, Task> RoleCreated
|
||||||
{
|
{
|
||||||
add { _roleCreatedEvent.Add(value); }
|
add { _roleCreatedEvent.Add(value); }
|
||||||
remove { _roleCreatedEvent.Remove(value); }
|
remove { _roleCreatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IRole, Task>> _roleCreatedEvent = new AsyncEvent<Func<IRole, Task>>();
|
private readonly AsyncEvent<Func<SocketRole, Task>> _roleCreatedEvent = new AsyncEvent<Func<SocketRole, Task>>();
|
||||||
public event Func<IRole, Task> RoleDeleted
|
public event Func<SocketRole, Task> RoleDeleted
|
||||||
{
|
{
|
||||||
add { _roleDeletedEvent.Add(value); }
|
add { _roleDeletedEvent.Add(value); }
|
||||||
remove { _roleDeletedEvent.Remove(value); }
|
remove { _roleDeletedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IRole, Task>> _roleDeletedEvent = new AsyncEvent<Func<IRole, Task>>();
|
private readonly AsyncEvent<Func<SocketRole, Task>> _roleDeletedEvent = new AsyncEvent<Func<SocketRole, Task>>();
|
||||||
public event Func<IRole, IRole, Task> RoleUpdated
|
public event Func<SocketRole, SocketRole, Task> RoleUpdated
|
||||||
{
|
{
|
||||||
add { _roleUpdatedEvent.Add(value); }
|
add { _roleUpdatedEvent.Add(value); }
|
||||||
remove { _roleUpdatedEvent.Remove(value); }
|
remove { _roleUpdatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IRole, IRole, Task>> _roleUpdatedEvent = new AsyncEvent<Func<IRole, IRole, Task>>();
|
private readonly AsyncEvent<Func<SocketRole, SocketRole, Task>> _roleUpdatedEvent = new AsyncEvent<Func<SocketRole, SocketRole, Task>>();
|
||||||
|
|
||||||
//Guilds
|
//Guilds
|
||||||
public event Func<IGuild, Task> JoinedGuild
|
public event Func<SocketGuild, Task> JoinedGuild
|
||||||
{
|
{
|
||||||
add { _joinedGuildEvent.Add(value); }
|
add { _joinedGuildEvent.Add(value); }
|
||||||
remove { _joinedGuildEvent.Remove(value); }
|
remove { _joinedGuildEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private AsyncEvent<Func<IGuild, Task>> _joinedGuildEvent = new AsyncEvent<Func<IGuild, Task>>();
|
private AsyncEvent<Func<SocketGuild, Task>> _joinedGuildEvent = new AsyncEvent<Func<SocketGuild, Task>>();
|
||||||
public event Func<IGuild, Task> LeftGuild
|
public event Func<SocketGuild, Task> LeftGuild
|
||||||
{
|
{
|
||||||
add { _leftGuildEvent.Add(value); }
|
add { _leftGuildEvent.Add(value); }
|
||||||
remove { _leftGuildEvent.Remove(value); }
|
remove { _leftGuildEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private AsyncEvent<Func<IGuild, Task>> _leftGuildEvent = new AsyncEvent<Func<IGuild, Task>>();
|
private AsyncEvent<Func<SocketGuild, Task>> _leftGuildEvent = new AsyncEvent<Func<SocketGuild, Task>>();
|
||||||
public event Func<IGuild, Task> GuildAvailable
|
public event Func<SocketGuild, Task> GuildAvailable
|
||||||
{
|
{
|
||||||
add { _guildAvailableEvent.Add(value); }
|
add { _guildAvailableEvent.Add(value); }
|
||||||
remove { _guildAvailableEvent.Remove(value); }
|
remove { _guildAvailableEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private AsyncEvent<Func<IGuild, Task>> _guildAvailableEvent = new AsyncEvent<Func<IGuild, Task>>();
|
private AsyncEvent<Func<SocketGuild, Task>> _guildAvailableEvent = new AsyncEvent<Func<SocketGuild, Task>>();
|
||||||
public event Func<IGuild, Task> GuildUnavailable
|
public event Func<SocketGuild, Task> GuildUnavailable
|
||||||
{
|
{
|
||||||
add { _guildUnavailableEvent.Add(value); }
|
add { _guildUnavailableEvent.Add(value); }
|
||||||
remove { _guildUnavailableEvent.Remove(value); }
|
remove { _guildUnavailableEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private AsyncEvent<Func<IGuild, Task>> _guildUnavailableEvent = new AsyncEvent<Func<IGuild, Task>>();
|
private AsyncEvent<Func<SocketGuild, Task>> _guildUnavailableEvent = new AsyncEvent<Func<SocketGuild, Task>>();
|
||||||
public event Func<IGuild, Task> GuildMembersDownloaded
|
public event Func<SocketGuild, Task> GuildMembersDownloaded
|
||||||
{
|
{
|
||||||
add { _guildMembersDownloadedEvent.Add(value); }
|
add { _guildMembersDownloadedEvent.Add(value); }
|
||||||
remove { _guildMembersDownloadedEvent.Remove(value); }
|
remove { _guildMembersDownloadedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private AsyncEvent<Func<IGuild, Task>> _guildMembersDownloadedEvent = new AsyncEvent<Func<IGuild, Task>>();
|
private AsyncEvent<Func<SocketGuild, Task>> _guildMembersDownloadedEvent = new AsyncEvent<Func<SocketGuild, Task>>();
|
||||||
public event Func<IGuild, IGuild, Task> GuildUpdated
|
public event Func<SocketGuild, SocketGuild, Task> GuildUpdated
|
||||||
{
|
{
|
||||||
add { _guildUpdatedEvent.Add(value); }
|
add { _guildUpdatedEvent.Add(value); }
|
||||||
remove { _guildUpdatedEvent.Remove(value); }
|
remove { _guildUpdatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private AsyncEvent<Func<IGuild, IGuild, Task>> _guildUpdatedEvent = new AsyncEvent<Func<IGuild, IGuild, Task>>();
|
private AsyncEvent<Func<SocketGuild, SocketGuild, Task>> _guildUpdatedEvent = new AsyncEvent<Func<SocketGuild, SocketGuild, Task>>();
|
||||||
|
|
||||||
//Users
|
//Users
|
||||||
public event Func<IGuildUser, Task> UserJoined
|
public event Func<SocketGuildUser, Task> UserJoined
|
||||||
{
|
{
|
||||||
add { _userJoinedEvent.Add(value); }
|
add { _userJoinedEvent.Add(value); }
|
||||||
remove { _userJoinedEvent.Remove(value); }
|
remove { _userJoinedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IGuildUser, Task>> _userJoinedEvent = new AsyncEvent<Func<IGuildUser, Task>>();
|
private readonly AsyncEvent<Func<SocketGuildUser, Task>> _userJoinedEvent = new AsyncEvent<Func<SocketGuildUser, Task>>();
|
||||||
public event Func<IGuildUser, Task> UserLeft
|
public event Func<SocketGuildUser, Task> UserLeft
|
||||||
{
|
{
|
||||||
add { _userLeftEvent.Add(value); }
|
add { _userLeftEvent.Add(value); }
|
||||||
remove { _userLeftEvent.Remove(value); }
|
remove { _userLeftEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IGuildUser, Task>> _userLeftEvent = new AsyncEvent<Func<IGuildUser, Task>>();
|
private readonly AsyncEvent<Func<SocketGuildUser, Task>> _userLeftEvent = new AsyncEvent<Func<SocketGuildUser, Task>>();
|
||||||
public event Func<IUser, IGuild, Task> UserBanned
|
public event Func<SocketUser, SocketGuild, Task> UserBanned
|
||||||
{
|
{
|
||||||
add { _userBannedEvent.Add(value); }
|
add { _userBannedEvent.Add(value); }
|
||||||
remove { _userBannedEvent.Remove(value); }
|
remove { _userBannedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IUser, IGuild, Task>> _userBannedEvent = new AsyncEvent<Func<IUser, IGuild, Task>>();
|
private readonly AsyncEvent<Func<SocketUser, SocketGuild, Task>> _userBannedEvent = new AsyncEvent<Func<SocketUser, SocketGuild, Task>>();
|
||||||
public event Func<IUser, IGuild, Task> UserUnbanned
|
public event Func<SocketUser, SocketGuild, Task> UserUnbanned
|
||||||
{
|
{
|
||||||
add { _userUnbannedEvent.Add(value); }
|
add { _userUnbannedEvent.Add(value); }
|
||||||
remove { _userUnbannedEvent.Remove(value); }
|
remove { _userUnbannedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IUser, IGuild, Task>> _userUnbannedEvent = new AsyncEvent<Func<IUser, IGuild, Task>>();
|
private readonly AsyncEvent<Func<SocketUser, SocketGuild, Task>> _userUnbannedEvent = new AsyncEvent<Func<SocketUser, SocketGuild, Task>>();
|
||||||
public event Func<IGuildUser, IGuildUser, Task> UserUpdated
|
public event Func<SocketGuildUser, SocketGuildUser, Task> UserUpdated
|
||||||
{
|
{
|
||||||
add { _userUpdatedEvent.Add(value); }
|
add { _userUpdatedEvent.Add(value); }
|
||||||
remove { _userUpdatedEvent.Remove(value); }
|
remove { _userUpdatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IGuildUser, IGuildUser, Task>> _userUpdatedEvent = new AsyncEvent<Func<IGuildUser, IGuildUser, Task>>();
|
private readonly AsyncEvent<Func<SocketGuildUser, SocketGuildUser, Task>> _userUpdatedEvent = new AsyncEvent<Func<SocketGuildUser, SocketGuildUser, Task>>();
|
||||||
public event Func<IGuildUser, IPresence, IPresence, Task> UserPresenceUpdated
|
public event Func<SocketUser, SocketPresence, SocketPresence, Task> UserPresenceUpdated
|
||||||
{
|
{
|
||||||
add { _userPresenceUpdatedEvent.Add(value); }
|
add { _userPresenceUpdatedEvent.Add(value); }
|
||||||
remove { _userPresenceUpdatedEvent.Remove(value); }
|
remove { _userPresenceUpdatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IGuildUser, IPresence, IPresence, Task>> _userPresenceUpdatedEvent = new AsyncEvent<Func<IGuildUser, IPresence, IPresence, Task>>();
|
private readonly AsyncEvent<Func<SocketUser, SocketPresence, SocketPresence, Task>> _userPresenceUpdatedEvent = new AsyncEvent<Func<SocketUser, SocketPresence, SocketPresence, Task>>();
|
||||||
public event Func<IUser, IVoiceState, IVoiceState, Task> UserVoiceStateUpdated
|
public event Func<SocketUser, SocketVoiceState, SocketVoiceState, Task> UserVoiceStateUpdated
|
||||||
{
|
{
|
||||||
add { _userVoiceStateUpdatedEvent.Add(value); }
|
add { _userVoiceStateUpdatedEvent.Add(value); }
|
||||||
remove { _userVoiceStateUpdatedEvent.Remove(value); }
|
remove { _userVoiceStateUpdatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IUser, IVoiceState, IVoiceState, Task>> _userVoiceStateUpdatedEvent = new AsyncEvent<Func<IUser, IVoiceState, IVoiceState, Task>>();
|
private readonly AsyncEvent<Func<SocketUser, SocketVoiceState, SocketVoiceState, Task>> _userVoiceStateUpdatedEvent = new AsyncEvent<Func<SocketUser, SocketVoiceState, SocketVoiceState, Task>>();
|
||||||
public event Func<ISelfUser, ISelfUser, Task> CurrentUserUpdated
|
public event Func<SocketSelfUser, SocketSelfUser, Task> CurrentUserUpdated
|
||||||
{
|
{
|
||||||
add { _selfUpdatedEvent.Add(value); }
|
add { _selfUpdatedEvent.Add(value); }
|
||||||
remove { _selfUpdatedEvent.Remove(value); }
|
remove { _selfUpdatedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<ISelfUser, ISelfUser, Task>> _selfUpdatedEvent = new AsyncEvent<Func<ISelfUser, ISelfUser, Task>>();
|
private readonly AsyncEvent<Func<SocketSelfUser, SocketSelfUser, Task>> _selfUpdatedEvent = new AsyncEvent<Func<SocketSelfUser, SocketSelfUser, Task>>();
|
||||||
public event Func<IUser, IChannel, Task> UserIsTyping
|
public event Func<SocketUser, ISocketMessageChannel, Task> UserIsTyping
|
||||||
{
|
{
|
||||||
add { _userIsTypingEvent.Add(value); }
|
add { _userIsTypingEvent.Add(value); }
|
||||||
remove { _userIsTypingEvent.Remove(value); }
|
remove { _userIsTypingEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IUser, IChannel, Task>> _userIsTypingEvent = new AsyncEvent<Func<IUser, IChannel, Task>>();
|
private readonly AsyncEvent<Func<SocketUser, ISocketMessageChannel, Task>> _userIsTypingEvent = new AsyncEvent<Func<SocketUser, ISocketMessageChannel, Task>>();
|
||||||
public event Func<IGroupUser, Task> RecipientAdded
|
public event Func<SocketGroupUser, Task> RecipientAdded
|
||||||
{
|
{
|
||||||
add { _recipientAddedEvent.Add(value); }
|
add { _recipientAddedEvent.Add(value); }
|
||||||
remove { _recipientAddedEvent.Remove(value); }
|
remove { _recipientAddedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IGroupUser, Task>> _recipientAddedEvent = new AsyncEvent<Func<IGroupUser, Task>>();
|
private readonly AsyncEvent<Func<SocketGroupUser, Task>> _recipientAddedEvent = new AsyncEvent<Func<SocketGroupUser, Task>>();
|
||||||
public event Func<IGroupUser, Task> RecipientRemoved
|
public event Func<SocketGroupUser, Task> RecipientRemoved
|
||||||
{
|
{
|
||||||
add { _recipientRemovedEvent.Add(value); }
|
add { _recipientRemovedEvent.Add(value); }
|
||||||
remove { _recipientRemovedEvent.Remove(value); }
|
remove { _recipientRemovedEvent.Remove(value); }
|
||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<IGroupUser, Task>> _recipientRemovedEvent = new AsyncEvent<Func<IGroupUser, Task>>();
|
private readonly AsyncEvent<Func<SocketGroupUser, Task>> _recipientRemovedEvent = new AsyncEvent<Func<SocketGroupUser, Task>>();
|
||||||
|
|
||||||
//TODO: Add PresenceUpdated? VoiceStateUpdated?, VoiceConnected, VoiceDisconnected;
|
//TODO: Add PresenceUpdated? VoiceStateUpdated?, VoiceConnected, VoiceDisconnected;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ namespace Discord.WebSocket
|
|||||||
internal WebSocketProvider WebSocketProvider { get; private set; }
|
internal WebSocketProvider WebSocketProvider { get; private set; }
|
||||||
|
|
||||||
public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
|
public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
|
||||||
public new SocketSelfUser CurrentUser => base.CurrentUser as SocketSelfUser;
|
public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } }
|
||||||
public IReadOnlyCollection<IPrivateChannel> PrivateChannels => State.PrivateChannels;
|
public IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels;
|
||||||
internal IReadOnlyCollection<SocketGuild> Guilds => State.Guilds;
|
internal IReadOnlyCollection<SocketGuild> Guilds => State.Guilds;
|
||||||
|
|
||||||
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
||||||
@@ -167,14 +167,23 @@ namespace Discord.WebSocket
|
|||||||
connectTask.TrySetException(new TimeoutException());
|
connectTask.TrySetException(new TimeoutException());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false);
|
||||||
await ApiClient.ConnectAsync().ConfigureAwait(false);
|
await ApiClient.ConnectAsync().ConfigureAwait(false);
|
||||||
|
await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false);
|
||||||
await _connectedEvent.InvokeAsync().ConfigureAwait(false);
|
await _connectedEvent.InvokeAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
if (_sessionId != null)
|
if (_sessionId != null)
|
||||||
|
{
|
||||||
|
await _gatewayLogger.DebugAsync("Resuming").ConfigureAwait(false);
|
||||||
await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false);
|
await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards:TotalShards).ConfigureAwait(false);
|
{
|
||||||
|
await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false);
|
||||||
|
await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false);
|
||||||
await _connectTask.Task.ConfigureAwait(false);
|
await _connectTask.Task.ConfigureAwait(false);
|
||||||
if (!isReconnecting)
|
if (!isReconnecting)
|
||||||
_canReconnect = true;
|
_canReconnect = true;
|
||||||
@@ -216,32 +225,33 @@ namespace Discord.WebSocket
|
|||||||
ConnectionState = ConnectionState.Disconnecting;
|
ConnectionState = ConnectionState.Disconnecting;
|
||||||
await _gatewayLogger.InfoAsync("Disconnecting").ConfigureAwait(false);
|
await _gatewayLogger.InfoAsync("Disconnecting").ConfigureAwait(false);
|
||||||
|
|
||||||
await _gatewayLogger.DebugAsync("Disconnecting - CancelToken").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Cancelling current tasks").ConfigureAwait(false);
|
||||||
//Signal tasks to complete
|
//Signal tasks to complete
|
||||||
try { _cancelToken.Cancel(); } catch { }
|
try { _cancelToken.Cancel(); } catch { }
|
||||||
|
|
||||||
await _gatewayLogger.DebugAsync("Disconnecting - ApiClient").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false);
|
||||||
//Disconnect from server
|
//Disconnect from server
|
||||||
await ApiClient.DisconnectAsync().ConfigureAwait(false);
|
await ApiClient.DisconnectAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
//Wait for tasks to complete
|
//Wait for tasks to complete
|
||||||
await _gatewayLogger.DebugAsync("Disconnecting - Heartbeat").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false);
|
||||||
var heartbeatTask = _heartbeatTask;
|
var heartbeatTask = _heartbeatTask;
|
||||||
if (heartbeatTask != null)
|
if (heartbeatTask != null)
|
||||||
await heartbeatTask.ConfigureAwait(false);
|
await heartbeatTask.ConfigureAwait(false);
|
||||||
_heartbeatTask = null;
|
_heartbeatTask = null;
|
||||||
|
|
||||||
await _gatewayLogger.DebugAsync("Disconnecting - Guild Downloader").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false);
|
||||||
var guildDownloadTask = _guildDownloadTask;
|
var guildDownloadTask = _guildDownloadTask;
|
||||||
if (guildDownloadTask != null)
|
if (guildDownloadTask != null)
|
||||||
await guildDownloadTask.ConfigureAwait(false);
|
await guildDownloadTask.ConfigureAwait(false);
|
||||||
_guildDownloadTask = null;
|
_guildDownloadTask = null;
|
||||||
|
|
||||||
//Clear large guild queue
|
//Clear large guild queue
|
||||||
await _gatewayLogger.DebugAsync("Disconnecting - Clean Large Guilds").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Clearing large guild queue").ConfigureAwait(false);
|
||||||
while (_largeGuilds.TryDequeue(out guildId)) { }
|
while (_largeGuilds.TryDequeue(out guildId)) { }
|
||||||
|
|
||||||
//Raise virtual GUILD_UNAVAILABLEs
|
//Raise virtual GUILD_UNAVAILABLEs
|
||||||
|
await _gatewayLogger.DebugAsync("Raising virtual GuildUnavailables").ConfigureAwait(false);
|
||||||
foreach (var guild in State.Guilds)
|
foreach (var guild in State.Guilds)
|
||||||
{
|
{
|
||||||
if (guild._available)
|
if (guild._available)
|
||||||
@@ -318,7 +328,6 @@ namespace Discord.WebSocket
|
|||||||
public Task<RestApplication> GetApplicationInfoAsync()
|
public Task<RestApplication> GetApplicationInfoAsync()
|
||||||
=> ClientHelper.GetApplicationInfoAsync(this);
|
=> ClientHelper.GetApplicationInfoAsync(this);
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public SocketGuild GetGuild(ulong id)
|
public SocketGuild GetGuild(ulong id)
|
||||||
{
|
{
|
||||||
@@ -329,7 +338,7 @@ namespace Discord.WebSocket
|
|||||||
=> ClientHelper.CreateGuildAsync(this, name, region, jpegIcon);
|
=> ClientHelper.CreateGuildAsync(this, name, region, jpegIcon);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IChannel GetChannel(ulong id)
|
public SocketChannel GetChannel(ulong id)
|
||||||
{
|
{
|
||||||
return State.GetChannel(id);
|
return State.GetChannel(id);
|
||||||
}
|
}
|
||||||
@@ -343,15 +352,38 @@ namespace Discord.WebSocket
|
|||||||
=> ClientHelper.GetInviteAsync(this, inviteId);
|
=> ClientHelper.GetInviteAsync(this, inviteId);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IUser GetUser(ulong id)
|
public SocketUser GetUser(ulong id)
|
||||||
{
|
{
|
||||||
return State.GetUser(id);
|
return State.GetUser(id);
|
||||||
}
|
}
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IUser GetUser(string username, string discriminator)
|
public SocketUser GetUser(string username, string discriminator)
|
||||||
{
|
{
|
||||||
return State.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault();
|
return State.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
internal SocketGlobalUser GetOrCreateUser(ClientState state, Discord.API.User model)
|
||||||
|
{
|
||||||
|
return state.GetOrAddUser(model.Id, x =>
|
||||||
|
{
|
||||||
|
var user = SocketGlobalUser.Create(this, state, model);
|
||||||
|
user.GlobalUser.AddRef();
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
internal SocketGlobalUser GetOrCreateSelfUser(ClientState state, Discord.API.User model)
|
||||||
|
{
|
||||||
|
return state.GetOrAddUser(model.Id, x =>
|
||||||
|
{
|
||||||
|
var user = SocketGlobalUser.Create(this, state, model);
|
||||||
|
user.GlobalUser.AddRef();
|
||||||
|
user.Presence = new SocketPresence(null, UserStatus.Online);
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
internal void RemoveUser(ulong id)
|
||||||
|
{
|
||||||
|
State.RemoveUser(id);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public RestVoiceRegion GetVoiceRegion(string id)
|
public RestVoiceRegion GetVoiceRegion(string id)
|
||||||
@@ -363,8 +395,8 @@ namespace Discord.WebSocket
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Downloads the users list for all large guilds. </summary>
|
/// <summary> Downloads the users list for all large guilds. </summary>
|
||||||
/*public Task DownloadAllUsersAsync()
|
public Task DownloadAllUsersAsync()
|
||||||
=> DownloadUsersAsync(DataStore.Guilds.Where(x => !x.HasAllMembers));
|
=> DownloadUsersAsync(State.Guilds.Where(x => !x.HasAllMembers));
|
||||||
/// <summary> Downloads the users list for the provided guilds, if they don't have a complete list. </summary>
|
/// <summary> Downloads the users list for the provided guilds, if they don't have a complete list. </summary>
|
||||||
public Task DownloadUsersAsync(IEnumerable<IGuild> guilds)
|
public Task DownloadUsersAsync(IEnumerable<IGuild> guilds)
|
||||||
=> DownloadUsersAsync(guilds.Select(x => x as SocketGuild).Where(x => x != null));
|
=> DownloadUsersAsync(guilds.Select(x => x as SocketGuild).Where(x => x != null));
|
||||||
@@ -414,7 +446,7 @@ namespace Discord.WebSocket
|
|||||||
else
|
else
|
||||||
await Task.WhenAll(batchTasks).ConfigureAwait(false);
|
await Task.WhenAll(batchTasks).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string type, object payload)
|
private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string type, object payload)
|
||||||
{
|
{
|
||||||
@@ -486,26 +518,26 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);
|
var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);
|
||||||
var dataStore = new ClientState(data.Guilds.Length, data.PrivateChannels.Length);
|
var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length);
|
||||||
|
|
||||||
var currentUser = SocketSelfUser.Create(this, data.User);
|
var currentUser = SocketSelfUser.Create(this, state, data.User);
|
||||||
int unavailableGuilds = 0;
|
int unavailableGuilds = 0;
|
||||||
/*for (int i = 0; i < data.Guilds.Length; i++)
|
for (int i = 0; i < data.Guilds.Length; i++)
|
||||||
{
|
{
|
||||||
var model = data.Guilds[i];
|
var model = data.Guilds[i];
|
||||||
var guild = AddGuild(model, dataStore);
|
var guild = AddGuild(model, state);
|
||||||
if (!guild._available || ApiClient.AuthTokenType == TokenType.User)
|
if (!guild._available || ApiClient.AuthTokenType == TokenType.User)
|
||||||
unavailableGuilds++;
|
unavailableGuilds++;
|
||||||
else
|
else
|
||||||
await _guildAvailableEvent.InvokeAsync(guild).ConfigureAwait(false);
|
await _guildAvailableEvent.InvokeAsync(guild).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < data.PrivateChannels.Length; i++)
|
for (int i = 0; i < data.PrivateChannels.Length; i++)
|
||||||
AddPrivateChannel(data.PrivateChannels[i], dataStore);*/
|
AddPrivateChannel(data.PrivateChannels[i], state);
|
||||||
|
|
||||||
_sessionId = data.SessionId;
|
_sessionId = data.SessionId;
|
||||||
base.CurrentUser = currentUser;
|
|
||||||
_unavailableGuilds = unavailableGuilds;
|
_unavailableGuilds = unavailableGuilds;
|
||||||
State = dataStore;
|
CurrentUser = currentUser;
|
||||||
|
State = state;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -525,14 +557,14 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false);
|
await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/*case "RESUMED":
|
case "RESUMED":
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false);
|
||||||
|
|
||||||
var _ = _connectTask.TrySetResultAsync(true); //Signal the .Connect() call to complete
|
var _ = _connectTask.TrySetResultAsync(true); //Signal the .Connect() call to complete
|
||||||
|
|
||||||
//Notify the client that these guilds are available again
|
//Notify the client that these guilds are available again
|
||||||
foreach (var guild in DataStore.Guilds)
|
foreach (var guild in State.Guilds)
|
||||||
{
|
{
|
||||||
if (guild._available)
|
if (guild._available)
|
||||||
await _guildAvailableEvent.InvokeAsync(guild).ConfigureAwait(false);
|
await _guildAvailableEvent.InvokeAsync(guild).ConfigureAwait(false);
|
||||||
@@ -553,10 +585,10 @@ namespace Discord.WebSocket
|
|||||||
_lastGuildAvailableTime = Environment.TickCount;
|
_lastGuildAvailableTime = Environment.TickCount;
|
||||||
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_AVAILABLE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_AVAILABLE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var guild = DataStore.GetGuild(data.Id);
|
var guild = State.GetGuild(data.Id);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
guild.Update(data, UpdateSource.WebSocket, DataStore);
|
guild.Update(State, data);
|
||||||
|
|
||||||
var unavailableGuilds = _unavailableGuilds;
|
var unavailableGuilds = _unavailableGuilds;
|
||||||
if (unavailableGuilds != 0)
|
if (unavailableGuilds != 0)
|
||||||
@@ -573,7 +605,7 @@ namespace Discord.WebSocket
|
|||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_CREATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_CREATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var guild = AddGuild(data, DataStore);
|
var guild = AddGuild(data, State);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
if (ApiClient.AuthTokenType == TokenType.User)
|
if (ApiClient.AuthTokenType == TokenType.User)
|
||||||
@@ -593,11 +625,11 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UPDATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UPDATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<API.Guild>(_serializer);
|
var data = (payload as JToken).ToObject<API.Guild>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.Id);
|
var guild = State.GetGuild(data.Id);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var before = guild.Clone();
|
var before = guild.Clone();
|
||||||
guild.Update(data, UpdateSource.WebSocket);
|
guild.Update(State, data);
|
||||||
await _guildUpdatedEvent.InvokeAsync(before, guild).ConfigureAwait(false);
|
await _guildUpdatedEvent.InvokeAsync(before, guild).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -612,11 +644,11 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_EMOJIS_UPDATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_EMOJIS_UPDATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<API.Gateway.GuildEmojiUpdateEvent>(_serializer);
|
var data = (payload as JToken).ToObject<API.Gateway.GuildEmojiUpdateEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var before = guild.Clone();
|
var before = guild.Clone();
|
||||||
guild.Update(data, UpdateSource.WebSocket);
|
guild.Update(State, data);
|
||||||
await _guildUpdatedEvent.InvokeAsync(before, guild).ConfigureAwait(false);
|
await _guildUpdatedEvent.InvokeAsync(before, guild).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -626,20 +658,15 @@ namespace Discord.WebSocket
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case "GUILD_INTEGRATIONS_UPDATE":
|
|
||||||
{
|
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case "GUILD_SYNC":
|
case "GUILD_SYNC":
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_SYNC)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_SYNC)").ConfigureAwait(false);
|
||||||
var data = (payload as JToken).ToObject<GuildSyncEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildSyncEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.Id);
|
var guild = State.GetGuild(data.Id);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var before = guild.Clone();
|
var before = guild.Clone();
|
||||||
guild.Update(data, UpdateSource.WebSocket, DataStore);
|
guild.Update(State, data);
|
||||||
//This is treated as an extension of GUILD_AVAILABLE
|
//This is treated as an extension of GUILD_AVAILABLE
|
||||||
_unavailableGuilds--;
|
_unavailableGuilds--;
|
||||||
_lastGuildAvailableTime = Environment.TickCount;
|
_lastGuildAvailableTime = Environment.TickCount;
|
||||||
@@ -661,11 +688,9 @@ namespace Discord.WebSocket
|
|||||||
type = "GUILD_UNAVAILABLE";
|
type = "GUILD_UNAVAILABLE";
|
||||||
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var guild = DataStore.GetGuild(data.Id);
|
var guild = State.GetGuild(data.Id);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
foreach (var member in guild.Members)
|
|
||||||
member.User.RemoveRef(this);
|
|
||||||
await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false);
|
await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false);
|
||||||
_unavailableGuilds++;
|
_unavailableGuilds++;
|
||||||
}
|
}
|
||||||
@@ -700,14 +725,13 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
||||||
ISocketChannel channel = null;
|
SocketChannel channel = null;
|
||||||
if (data.GuildId.IsSpecified)
|
if (data.GuildId.IsSpecified)
|
||||||
{
|
{
|
||||||
var guild = DataStore.GetGuild(data.GuildId.Value);
|
var guild = State.GetGuild(data.GuildId.Value);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
guild.AddChannel(data, DataStore);
|
channel = guild.AddChannel(State, data);
|
||||||
channel = guild.AddChannel(data, DataStore);
|
|
||||||
|
|
||||||
if (!guild.IsSynced)
|
if (!guild.IsSynced)
|
||||||
{
|
{
|
||||||
@@ -722,7 +746,7 @@ namespace Discord.WebSocket
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
channel = AddPrivateChannel(data, DataStore);
|
channel = AddPrivateChannel(data, State) as SocketChannel;
|
||||||
|
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false);
|
await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false);
|
||||||
@@ -733,13 +757,13 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_UPDATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_UPDATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
||||||
var channel = DataStore.GetChannel(data.Id);
|
var channel = State.GetChannel(data.Id);
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
var before = channel.Clone();
|
var before = channel.Clone();
|
||||||
channel.Update(data, UpdateSource.WebSocket);
|
channel.Update(State, data);
|
||||||
|
|
||||||
if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true))
|
if (!((channel as SocketGuildChannel)?.Guild.IsSynced ?? true))
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Ignored CHANNEL_UPDATE, guild is not synced yet.").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored CHANNEL_UPDATE, guild is not synced yet.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
@@ -758,14 +782,14 @@ namespace Discord.WebSocket
|
|||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false);
|
||||||
|
|
||||||
ISocketChannel channel = null;
|
SocketChannel channel = null;
|
||||||
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
var data = (payload as JToken).ToObject<API.Channel>(_serializer);
|
||||||
if (data.GuildId.IsSpecified)
|
if (data.GuildId.IsSpecified)
|
||||||
{
|
{
|
||||||
var guild = DataStore.GetGuild(data.GuildId.Value);
|
var guild = State.GetGuild(data.GuildId.Value);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
channel = guild.RemoveChannel(data.Id);
|
channel = guild.RemoveChannel(State, data.Id);
|
||||||
|
|
||||||
if (!guild.IsSynced)
|
if (!guild.IsSynced)
|
||||||
{
|
{
|
||||||
@@ -780,7 +804,7 @@ namespace Discord.WebSocket
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
channel = RemovePrivateChannel(data.Id);
|
channel = RemovePrivateChannel(data.Id) as SocketChannel;
|
||||||
|
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
await _channelDestroyedEvent.InvokeAsync(channel).ConfigureAwait(false);
|
await _channelDestroyedEvent.InvokeAsync(channel).ConfigureAwait(false);
|
||||||
@@ -798,10 +822,10 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_ADD)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_ADD)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildMemberAddEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildMemberAddEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var user = guild.AddOrUpdateUser(data, DataStore);
|
var user = guild.AddOrUpdateUser(data);
|
||||||
guild.MemberCount++;
|
guild.MemberCount++;
|
||||||
|
|
||||||
if (!guild.IsSynced)
|
if (!guild.IsSynced)
|
||||||
@@ -824,7 +848,7 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_UPDATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_UPDATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildMemberUpdateEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildMemberUpdateEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var user = guild.GetUser(data.User.Id);
|
var user = guild.GetUser(data.User.Id);
|
||||||
@@ -838,7 +862,7 @@ namespace Discord.WebSocket
|
|||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
var before = user.Clone();
|
var before = user.Clone();
|
||||||
user.Update(data, UpdateSource.WebSocket);
|
user.Update(State, data);
|
||||||
await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false);
|
await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -865,7 +889,7 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_REMOVE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_REMOVE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildMemberRemoveEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildMemberRemoveEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var user = guild.RemoveUser(data.User.Id);
|
var user = guild.RemoveUser(data.User.Id);
|
||||||
@@ -878,10 +902,7 @@ namespace Discord.WebSocket
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
|
||||||
user.User.RemoveRef(this);
|
|
||||||
await _userLeftEvent.InvokeAsync(user).ConfigureAwait(false);
|
await _userLeftEvent.InvokeAsync(user).ConfigureAwait(false);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!guild.HasAllMembers)
|
if (!guild.HasAllMembers)
|
||||||
@@ -906,15 +927,15 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBERS_CHUNK)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBERS_CHUNK)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildMembersChunkEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildMembersChunkEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
foreach (var memberModel in data.Members)
|
foreach (var memberModel in data.Members)
|
||||||
guild.AddOrUpdateUser(memberModel, DataStore);
|
guild.AddOrUpdateUser(memberModel);
|
||||||
|
|
||||||
if (guild.DownloadedMemberCount >= guild.MemberCount) //Finished downloading for there
|
if (guild.DownloadedMemberCount >= guild.MemberCount) //Finished downloading for there
|
||||||
{
|
{
|
||||||
guild.CompleteDownloadMembers();
|
guild.CompleteDownloadUsers();
|
||||||
await _guildMembersDownloadedEvent.InvokeAsync(guild).ConfigureAwait(false);
|
await _guildMembersDownloadedEvent.InvokeAsync(guild).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -930,10 +951,10 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_ADD)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_ADD)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<RecipientEvent>(_serializer);
|
var data = (payload as JToken).ToObject<RecipientEvent>(_serializer);
|
||||||
var channel = DataStore.GetChannel(data.ChannelId) as SocketGroupChannel;
|
var channel = State.GetChannel(data.ChannelId) as SocketGroupChannel;
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
var user = channel.AddUser(data.User, DataStore);
|
var user = channel.AddUser(data.User);
|
||||||
await _recipientAddedEvent.InvokeAsync(user).ConfigureAwait(false);
|
await _recipientAddedEvent.InvokeAsync(user).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -948,15 +969,12 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_REMOVE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_REMOVE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<RecipientEvent>(_serializer);
|
var data = (payload as JToken).ToObject<RecipientEvent>(_serializer);
|
||||||
var channel = DataStore.GetChannel(data.ChannelId) as SocketGroupChannel;
|
var channel = State.GetChannel(data.ChannelId) as SocketGroupChannel;
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
var user = channel.RemoveUser(data.User.Id);
|
var user = channel.RemoveUser(data.User.Id);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
|
||||||
user.User.RemoveRef(this);
|
|
||||||
await _recipientRemovedEvent.InvokeAsync(user).ConfigureAwait(false);
|
await _recipientRemovedEvent.InvokeAsync(user).ConfigureAwait(false);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _gatewayLogger.WarningAsync("CHANNEL_RECIPIENT_REMOVE referenced an unknown user.").ConfigureAwait(false);
|
await _gatewayLogger.WarningAsync("CHANNEL_RECIPIENT_REMOVE referenced an unknown user.").ConfigureAwait(false);
|
||||||
@@ -977,7 +995,7 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_CREATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_CREATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildRoleCreateEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildRoleCreateEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var role = guild.AddRole(data.Role);
|
var role = guild.AddRole(data.Role);
|
||||||
@@ -1001,14 +1019,14 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_UPDATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_UPDATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildRoleUpdateEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildRoleUpdateEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var role = guild.GetRole(data.Role.Id);
|
var role = guild.GetRole(data.Role.Id);
|
||||||
if (role != null)
|
if (role != null)
|
||||||
{
|
{
|
||||||
var before = role.Clone();
|
var before = role.Clone();
|
||||||
role.Update(data.Role, UpdateSource.WebSocket);
|
role.Update(State, data.Role);
|
||||||
|
|
||||||
if (!guild.IsSynced)
|
if (!guild.IsSynced)
|
||||||
{
|
{
|
||||||
@@ -1036,7 +1054,7 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_DELETE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_DELETE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildRoleDeleteEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildRoleDeleteEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
var role = guild.RemoveRole(data.RoleId);
|
var role = guild.RemoveRole(data.RoleId);
|
||||||
@@ -1070,7 +1088,7 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_BAN_ADD)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_BAN_ADD)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
if (!guild.IsSynced)
|
if (!guild.IsSynced)
|
||||||
@@ -1078,8 +1096,8 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Ignored GUILD_BAN_ADD, guild is not synced yet.").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored GUILD_BAN_ADD, guild is not synced yet.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _userBannedEvent.InvokeAsync(new User(data.User), guild).ConfigureAwait(false);
|
await _userBannedEvent.InvokeAsync(SocketSimpleUser.Create(this, State, data.User), guild).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1093,7 +1111,7 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_BAN_REMOVE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_BAN_REMOVE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
|
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
if (!guild.IsSynced)
|
if (!guild.IsSynced)
|
||||||
@@ -1102,7 +1120,10 @@ namespace Discord.WebSocket
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _userUnbannedEvent.InvokeAsync(new User(data.User), guild).ConfigureAwait(false);
|
SocketUser user = State.GetUser(data.User.Id);
|
||||||
|
if (user == null)
|
||||||
|
user = SocketSimpleUser.Create(this, State, data.User);
|
||||||
|
await _userUnbannedEvent.InvokeAsync(user, guild).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1118,20 +1139,28 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<API.Message>(_serializer);
|
var data = (payload as JToken).ToObject<API.Message>(_serializer);
|
||||||
var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true))
|
var guild = (channel as SocketGuildChannel)?.Guild;
|
||||||
|
if (guild != null && !guild.IsSynced)
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Ignored MESSAGE_CREATE, guild is not synced yet.").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored MESSAGE_CREATE, guild is not synced yet.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var author = channel.GetUser(data.Author.Value.Id, true);
|
SocketUser author;
|
||||||
|
if (guild != null)
|
||||||
|
author = guild.GetUser(data.Author.Value.Id);
|
||||||
|
else
|
||||||
|
author = (channel as SocketChannel).GetUser(data.Author.Value.Id);
|
||||||
|
if (author == null)
|
||||||
|
author = SocketSimpleUser.Create(this, State, data.Author.Value);
|
||||||
|
|
||||||
if (author != null)
|
if (author != null)
|
||||||
{
|
{
|
||||||
var msg = channel.AddMessage(author, data);
|
var msg = SocketMessage.Create(this, State, author, data);
|
||||||
|
SocketChannelHelper.AddMessage(channel, this, msg);
|
||||||
await _messageReceivedEvent.InvokeAsync(msg).ConfigureAwait(false);
|
await _messageReceivedEvent.InvokeAsync(msg).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1152,38 +1181,41 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<API.Message>(_serializer);
|
var data = (payload as JToken).ToObject<API.Message>(_serializer);
|
||||||
var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true))
|
var guild = (channel as SocketGuildChannel)?.Guild;
|
||||||
|
if (guild != null && !guild.IsSynced)
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Ignored MESSAGE_UPDATE, guild is not synced yet.").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored MESSAGE_UPDATE, guild is not synced yet.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMessage before = null, after = null;
|
SocketMessage before = null, after = null;
|
||||||
ISocketMessage cachedMsg = channel.GetMessage(data.Id);
|
SocketMessage cachedMsg = channel.GetCachedMessage(data.Id);
|
||||||
if (cachedMsg != null)
|
if (cachedMsg != null)
|
||||||
{
|
{
|
||||||
before = cachedMsg.Clone();
|
before = cachedMsg.Clone();
|
||||||
cachedMsg.Update(data, UpdateSource.WebSocket);
|
cachedMsg.Update(State, data);
|
||||||
after = cachedMsg;
|
after = cachedMsg;
|
||||||
}
|
}
|
||||||
else if (data.Author.IsSpecified)
|
else if (data.Author.IsSpecified)
|
||||||
{
|
{
|
||||||
//Edited message isnt in cache, create a detached one
|
//Edited message isnt in cache, create a detached one
|
||||||
var author = channel.GetUser(data.Author.Value.Id, true);
|
SocketUser author;
|
||||||
if (author != null)
|
if (guild != null)
|
||||||
after = channel.CreateMessage(author, data);
|
author = guild.GetUser(data.Author.Value.Id);
|
||||||
}
|
|
||||||
if (after != null)
|
|
||||||
await _messageUpdatedEvent.InvokeAsync(Optional.Create(before), after).ConfigureAwait(false);
|
|
||||||
{
|
|
||||||
if (before != null)
|
|
||||||
await _messageUpdatedEvent.InvokeAsync(Optional.Create<IMessage>(before), after).ConfigureAwait(false);
|
|
||||||
else
|
else
|
||||||
await _messageUpdatedEvent.InvokeAsync(Optional.Create<IMessage>(), after).ConfigureAwait(false);
|
author = (channel as SocketChannel).GetUser(data.Author.Value.Id);
|
||||||
|
if (author == null)
|
||||||
|
author = SocketSimpleUser.Create(this, State, data.Author.Value);
|
||||||
|
|
||||||
|
after = SocketMessage.Create(this, State, author, data);
|
||||||
}
|
}
|
||||||
|
if (before != null)
|
||||||
|
await _messageUpdatedEvent.InvokeAsync(before, after).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await _messageUpdatedEvent.InvokeAsync(Optional.Create<SocketMessage>(), after).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1197,20 +1229,20 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<API.Message>(_serializer);
|
var data = (payload as JToken).ToObject<API.Message>(_serializer);
|
||||||
var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true))
|
if (!((channel as SocketGuildChannel)?.Guild.IsSynced ?? true))
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE, guild is not synced yet.").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE, guild is not synced yet.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg = channel.RemoveMessage(data.Id);
|
var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id);
|
||||||
if (msg != null)
|
if (msg != null)
|
||||||
await _messageDeletedEvent.InvokeAsync(data.Id, Optional.Create<IMessage>(msg)).ConfigureAwait(false);
|
await _messageDeletedEvent.InvokeAsync(data.Id, msg).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await _messageDeletedEvent.InvokeAsync(data.Id, Optional.Create<IMessage>()).ConfigureAwait(false);
|
await _messageDeletedEvent.InvokeAsync(data.Id, Optional.Create<SocketMessage>()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1224,10 +1256,10 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<MessageDeleteBulkEvent>(_serializer);
|
var data = (payload as JToken).ToObject<MessageDeleteBulkEvent>(_serializer);
|
||||||
var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true))
|
if (!((channel as SocketGuildChannel)?.Guild.IsSynced ?? true))
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE_BULK, guild is not synced yet.").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE_BULK, guild is not synced yet.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
@@ -1235,11 +1267,11 @@ namespace Discord.WebSocket
|
|||||||
|
|
||||||
foreach (var id in data.Ids)
|
foreach (var id in data.Ids)
|
||||||
{
|
{
|
||||||
var msg = channel.RemoveMessage(id);
|
var msg = SocketChannelHelper.RemoveMessage(channel, this, id);
|
||||||
if (msg != null)
|
if (msg != null)
|
||||||
await _messageDeletedEvent.InvokeAsync(id, Optional.Create<IMessage>(msg)).ConfigureAwait(false);
|
await _messageDeletedEvent.InvokeAsync(id, msg).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await _messageDeletedEvent.InvokeAsync(id, Optional.Create<IMessage>()).ConfigureAwait(false);
|
await _messageDeletedEvent.InvokeAsync(id, Optional.Create<SocketMessage>()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1258,39 +1290,43 @@ namespace Discord.WebSocket
|
|||||||
var data = (payload as JToken).ToObject<API.Presence>(_serializer);
|
var data = (payload as JToken).ToObject<API.Presence>(_serializer);
|
||||||
if (data.GuildId.IsSpecified)
|
if (data.GuildId.IsSpecified)
|
||||||
{
|
{
|
||||||
var guild = DataStore.GetGuild(data.GuildId.Value);
|
var guild = State.GetGuild(data.GuildId.Value);
|
||||||
if (guild == null)
|
if (guild == null)
|
||||||
{
|
{
|
||||||
await _gatewayLogger.WarningAsync("PRESENCE_UPDATE referenced an unknown guild.").ConfigureAwait(false);
|
await _gatewayLogger.WarningAsync("PRESENCE_UPDATE referenced an unknown guild.").ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!guild.IsSynced)
|
if (!guild.IsSynced)
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Ignored PRESENCE_UPDATE, guild is not synced yet.").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored PRESENCE_UPDATE, guild is not synced yet.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPresence before;
|
SocketPresence before;
|
||||||
var user = guild.GetUser(data.User.Id);
|
SocketUser user = guild.GetUser(data.User.Id);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
before = user.Presence.Clone();
|
before = user.Presence.Clone();
|
||||||
user.Update(data, UpdateSource.WebSocket);
|
user.Update(State, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
before = new SocketPresence(null, UserStatus.Offline);
|
before = new SocketPresence(null, UserStatus.Offline);
|
||||||
user = guild.AddOrUpdateUser(data, DataStore);
|
user = guild.AddOrUpdateUser(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _userPresenceUpdatedEvent.InvokeAsync(user, before, user).ConfigureAwait(false);
|
await _userPresenceUpdatedEvent.InvokeAsync(user, before, user.Presence).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var channel = DataStore.GetDMChannel(data.User.Id);
|
var channel = State.GetChannel(data.User.Id);
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
channel.Recipient.Update(data, UpdateSource.WebSocket);
|
{
|
||||||
|
var user = channel.GetUser(data.User.Id);
|
||||||
|
var before = user.Presence.Clone();
|
||||||
|
user.Update(State, data);
|
||||||
|
await _userPresenceUpdatedEvent.InvokeAsync(user, before, user.Presence).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1299,16 +1335,16 @@ namespace Discord.WebSocket
|
|||||||
await _gatewayLogger.DebugAsync("Received Dispatch (TYPING_START)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (TYPING_START)").ConfigureAwait(false);
|
||||||
|
|
||||||
var data = (payload as JToken).ToObject<TypingStartEvent>(_serializer);
|
var data = (payload as JToken).ToObject<TypingStartEvent>(_serializer);
|
||||||
var channel = DataStore.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel;
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
{
|
{
|
||||||
if (!((channel as ISocketGuildChannel)?.Guild.IsSynced ?? true))
|
if (!((channel as SocketGuildChannel)?.Guild.IsSynced ?? true))
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Ignored TYPING_START, guild is not synced yet.").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored TYPING_START, guild is not synced yet.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = channel.GetUser(data.UserId, true);
|
var user = (channel as SocketChannel).GetUser(data.UserId);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
await _userIsTypingEvent.InvokeAsync(user, channel).ConfigureAwait(false);
|
await _userIsTypingEvent.InvokeAsync(user, channel).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -1324,7 +1360,7 @@ namespace Discord.WebSocket
|
|||||||
if (data.Id == CurrentUser.Id)
|
if (data.Id == CurrentUser.Id)
|
||||||
{
|
{
|
||||||
var before = CurrentUser.Clone();
|
var before = CurrentUser.Clone();
|
||||||
CurrentUser.Update(data, UpdateSource.WebSocket);
|
CurrentUser.Update(State, data);
|
||||||
await _selfUpdatedEvent.InvokeAsync(before, CurrentUser).ConfigureAwait(false);
|
await _selfUpdatedEvent.InvokeAsync(before, CurrentUser).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1343,56 +1379,53 @@ namespace Discord.WebSocket
|
|||||||
var data = (payload as JToken).ToObject<API.VoiceState>(_serializer);
|
var data = (payload as JToken).ToObject<API.VoiceState>(_serializer);
|
||||||
if (data.GuildId.HasValue)
|
if (data.GuildId.HasValue)
|
||||||
{
|
{
|
||||||
ISocketUser user;
|
SocketUser user;
|
||||||
SocketVoiceState before, after;
|
SocketVoiceState before, after;
|
||||||
if (data.GuildId != null)
|
if (data.GuildId != null)
|
||||||
{
|
{
|
||||||
var guild = DataStore.GetGuild(data.GuildId.Value);
|
var guild = State.GetGuild(data.GuildId.Value);
|
||||||
if (guild != null)
|
if (guild == null)
|
||||||
{
|
|
||||||
if (!guild.IsSynced)
|
|
||||||
{
|
|
||||||
await _gatewayLogger.DebugAsync("Ignored VOICE_STATE_UPDATE, guild is not synced yet.").ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.ChannelId != null)
|
|
||||||
{
|
|
||||||
before = guild.GetVoiceState(data.UserId)?.Clone() ?? new SocketVoiceState(null, null, false, false, false);
|
|
||||||
after = guild.AddOrUpdateVoiceState(data, DataStore);
|
|
||||||
if (data.UserId == _currentUser.Id)
|
|
||||||
{
|
|
||||||
var _ = guild.FinishJoinAudioChannel().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
before = guild.RemoveVoiceState(data.UserId) ?? new SocketVoiceState(null, null, false, false, false);
|
|
||||||
after = new SocketVoiceState(null, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
user = guild.GetUser(data.UserId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
await _gatewayLogger.WarningAsync("VOICE_STATE_UPDATE referenced an unknown guild.").ConfigureAwait(false);
|
await _gatewayLogger.WarningAsync("VOICE_STATE_UPDATE referenced an unknown guild.").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (!guild.IsSynced)
|
||||||
|
{
|
||||||
|
await _gatewayLogger.DebugAsync("Ignored VOICE_STATE_UPDATE, guild is not synced yet.").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.ChannelId != null)
|
||||||
|
{
|
||||||
|
before = guild.GetVoiceState(data.UserId)?.Clone() ?? new SocketVoiceState(null, null, false, false, false);
|
||||||
|
after = guild.AddOrUpdateVoiceState(State, data);
|
||||||
|
if (data.UserId == CurrentUser.Id)
|
||||||
|
{
|
||||||
|
var _ = guild.FinishJoinAudioChannel().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
before = guild.RemoveVoiceState(data.UserId) ?? new SocketVoiceState(null, null, false, false, false);
|
||||||
|
after = SocketVoiceState.Create(null, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
user = guild.GetUser(data.UserId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var groupChannel = DataStore.GetChannel(data.ChannelId.Value) as SocketGroupChannel;
|
var groupChannel = State.GetChannel(data.ChannelId.Value) as SocketGroupChannel;
|
||||||
if (groupChannel != null)
|
if (groupChannel != null)
|
||||||
{
|
{
|
||||||
if (data.ChannelId != null)
|
if (data.ChannelId != null)
|
||||||
{
|
{
|
||||||
before = groupChannel.GetVoiceState(data.UserId)?.Clone() ?? new SocketVoiceState(null, null, false, false, false);
|
before = groupChannel.GetVoiceState(data.UserId)?.Clone() ?? new SocketVoiceState(null, null, false, false, false);
|
||||||
after = groupChannel.AddOrUpdateVoiceState(data, DataStore);
|
after = groupChannel.AddOrUpdateVoiceState(State, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
before = groupChannel.RemoveVoiceState(data.UserId) ?? new SocketVoiceState(null, null, false, false, false);
|
before = groupChannel.RemoveVoiceState(data.UserId) ?? new SocketVoiceState(null, null, false, false, false);
|
||||||
after = new SocketVoiceState(null, data);
|
after = SocketVoiceState.Create(null, data);
|
||||||
}
|
}
|
||||||
user = groupChannel.GetUser(data.UserId);
|
user = groupChannel.GetUser(data.UserId);
|
||||||
}
|
}
|
||||||
@@ -1419,7 +1452,7 @@ namespace Discord.WebSocket
|
|||||||
if (AudioMode != AudioMode.Disabled)
|
if (AudioMode != AudioMode.Disabled)
|
||||||
{
|
{
|
||||||
var data = (payload as JToken).ToObject<VoiceServerUpdateEvent>(_serializer);
|
var data = (payload as JToken).ToObject<VoiceServerUpdateEvent>(_serializer);
|
||||||
var guild = DataStore.GetGuild(data.GuildId);
|
var guild = State.GetGuild(data.GuildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
{
|
{
|
||||||
string endpoint = data.Endpoint.Substring(0, data.Endpoint.LastIndexOf(':'));
|
string endpoint = data.Endpoint.Substring(0, data.Endpoint.LastIndexOf(':'));
|
||||||
@@ -1431,8 +1464,7 @@ namespace Discord.WebSocket
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
return;*/
|
|
||||||
|
|
||||||
//Ignored (User only)
|
//Ignored (User only)
|
||||||
case "CHANNEL_PINS_ACK":
|
case "CHANNEL_PINS_ACK":
|
||||||
@@ -1441,12 +1473,15 @@ namespace Discord.WebSocket
|
|||||||
case "CHANNEL_PINS_UPDATE":
|
case "CHANNEL_PINS_UPDATE":
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_UPDATE)");
|
await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_UPDATE)");
|
||||||
break;
|
break;
|
||||||
case "USER_SETTINGS_UPDATE":
|
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
case "MESSAGE_ACK":
|
case "MESSAGE_ACK":
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
|
case "GUILD_INTEGRATIONS_UPDATE":
|
||||||
|
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
case "USER_SETTINGS_UPDATE":
|
||||||
|
await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
|
||||||
//Others
|
//Others
|
||||||
default:
|
default:
|
||||||
@@ -1532,6 +1567,42 @@ namespace Discord.WebSocket
|
|||||||
await ApiClient.SendGuildSyncAsync(guildIds).ConfigureAwait(false);
|
await ApiClient.SendGuildSyncAsync(guildIds).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal SocketGuild AddGuild(ExtendedGuild model, ClientState state)
|
||||||
|
{
|
||||||
|
var guild = SocketGuild.Create(this, state, model);
|
||||||
|
state.AddGuild(guild);
|
||||||
|
if (model.Large)
|
||||||
|
_largeGuilds.Enqueue(model.Id);
|
||||||
|
return guild;
|
||||||
|
}
|
||||||
|
internal SocketGuild RemoveGuild(ulong id)
|
||||||
|
{
|
||||||
|
var guild = State.RemoveGuild(id);
|
||||||
|
if (guild != null)
|
||||||
|
{
|
||||||
|
foreach (var channel in guild.Channels)
|
||||||
|
State.RemoveChannel(id);
|
||||||
|
foreach (var user in guild.Users)
|
||||||
|
user.GlobalUser.RemoveRef(this);
|
||||||
|
}
|
||||||
|
return guild;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ISocketPrivateChannel AddPrivateChannel(API.Channel model, ClientState state)
|
||||||
|
{
|
||||||
|
return SocketChannel.CreatePrivate(this, state, model);
|
||||||
|
}
|
||||||
|
internal ISocketPrivateChannel RemovePrivateChannel(ulong id)
|
||||||
|
{
|
||||||
|
var channel = State.RemoveChannel(id) as ISocketPrivateChannel;
|
||||||
|
if (channel != null)
|
||||||
|
{
|
||||||
|
foreach (var recipient in channel.Recipients)
|
||||||
|
recipient.GlobalUser.RemoveRef(this);
|
||||||
|
}
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
//IDiscordClient
|
//IDiscordClient
|
||||||
DiscordRestApiClient IDiscordClient.ApiClient => ApiClient;
|
DiscordRestApiClient IDiscordClient.ApiClient => ApiClient;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Discord.WebSocket
|
||||||
|
{
|
||||||
|
public interface ISocketAudioChannel : IAudioChannel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using Discord.Rest;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Discord.WebSocket
|
||||||
|
{
|
||||||
|
public interface ISocketMessageChannel : IMessageChannel
|
||||||
|
{
|
||||||
|
/// <summary> Gets all messages in this channel's cache. </summary>
|
||||||
|
IReadOnlyCollection<SocketMessage> CachedMessages { get; }
|
||||||
|
|
||||||
|
/// <summary> Sends a message to this message channel. </summary>
|
||||||
|
new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false);
|
||||||
|
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
|
||||||
|
new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false);
|
||||||
|
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
|
||||||
|
new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false);
|
||||||
|
|
||||||
|
SocketMessage GetCachedMessage(ulong id);
|
||||||
|
/// <summary> Gets a message from this message channel with the given id, or null if not found. </summary>
|
||||||
|
Task<IMessage> GetMessageAsync(ulong id);
|
||||||
|
/// <summary> Gets the last N messages from this message channel. </summary>
|
||||||
|
Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch);
|
||||||
|
/// <summary> Gets a collection of messages in this channel. </summary>
|
||||||
|
Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch);
|
||||||
|
/// <summary> Gets a collection of messages in this channel. </summary>
|
||||||
|
Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch);
|
||||||
|
/// <summary> Gets a collection of pinned messages in this channel. </summary>
|
||||||
|
new Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Discord.WebSocket
|
||||||
|
{
|
||||||
|
public interface ISocketPrivateChannel : IPrivateChannel
|
||||||
|
{
|
||||||
|
new IReadOnlyCollection<SocketUser> Recipients { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,35 +11,37 @@ namespace Discord.WebSocket
|
|||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public abstract class SocketChannel : SocketEntity<ulong>, IChannel
|
public abstract class SocketChannel : SocketEntity<ulong>, IChannel
|
||||||
{
|
{
|
||||||
|
public IReadOnlyCollection<SocketUser> Users => GetUsersInternal();
|
||||||
|
|
||||||
internal SocketChannel(DiscordSocketClient discord, ulong id)
|
internal SocketChannel(DiscordSocketClient discord, ulong id)
|
||||||
: base(discord, id)
|
: base(discord, id)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
internal static SocketChannel Create(DiscordSocketClient discord, Model model)
|
internal static ISocketPrivateChannel CreatePrivate(DiscordSocketClient discord, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
switch (model.Type)
|
switch (model.Type)
|
||||||
{
|
{
|
||||||
case ChannelType.Text:
|
|
||||||
return SocketTextChannel.Create(discord, model);
|
|
||||||
case ChannelType.Voice:
|
|
||||||
return SocketVoiceChannel.Create(discord, model);
|
|
||||||
case ChannelType.DM:
|
case ChannelType.DM:
|
||||||
return SocketDMChannel.Create(discord, model);
|
return SocketDMChannel.Create(discord, state, model);
|
||||||
case ChannelType.Group:
|
case ChannelType.Group:
|
||||||
return SocketGroupChannel.Create(discord, model);
|
return SocketGroupChannel.Create(discord, state, model);
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException($"Unexpected channel type: {model.Type}");
|
throw new InvalidOperationException($"Unexpected channel type: {model.Type}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal abstract void Update(ClientState state, Model model);
|
||||||
|
|
||||||
//IChannel
|
//User
|
||||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => ImmutableArray.Create<IUser>();
|
public SocketUser GetUser(ulong id) => GetUserInternal(id);
|
||||||
|
internal abstract SocketUser GetUserInternal(ulong id);
|
||||||
|
internal abstract IReadOnlyCollection<SocketUser> GetUsersInternal();
|
||||||
|
|
||||||
IUser IChannel.GetCachedUser(ulong id)
|
internal SocketChannel Clone() => MemberwiseClone() as SocketChannel;
|
||||||
=> null;
|
|
||||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
//IChannel
|
||||||
=> Task.FromResult<IUser>(null);
|
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
=> Task.FromResult<IUser>(null); //Overridden
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable();
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||||
|
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable(); //Overridden
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
using Discord.Rest;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Discord.WebSocket
|
||||||
|
{
|
||||||
|
internal static class SocketChannelHelper
|
||||||
|
{
|
||||||
|
public static async Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(SocketChannel channel, DiscordSocketClient discord, MessageCache messages,
|
||||||
|
ulong? fromMessageId, Direction dir, int limit)
|
||||||
|
{
|
||||||
|
if (messages == null) //Cache disabled
|
||||||
|
{
|
||||||
|
var msgs = await ChannelHelper.GetMessagesAsync(channel, discord, fromMessageId, dir, limit).Flatten();
|
||||||
|
return msgs.ToImmutableArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cachedMessages = messages.GetMany(fromMessageId, dir, limit);
|
||||||
|
limit -= cachedMessages.Count;
|
||||||
|
if (limit == 0)
|
||||||
|
return cachedMessages;
|
||||||
|
|
||||||
|
if (dir == Direction.Before)
|
||||||
|
fromMessageId = cachedMessages.Min(x => x.Id);
|
||||||
|
else
|
||||||
|
fromMessageId = cachedMessages.Max(x => x.Id);
|
||||||
|
var downloadedMessages = await ChannelHelper.GetMessagesAsync(channel, discord, fromMessageId, dir, limit).Flatten();
|
||||||
|
return cachedMessages.Concat<IMessage>(downloadedMessages).ToImmutableArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IAsyncEnumerable<IReadOnlyCollection<IMessage>> PagedGetMessagesAsync(SocketChannel channel, DiscordSocketClient discord, MessageCache messages,
|
||||||
|
ulong? fromMessageId, Direction dir, int limit)
|
||||||
|
{
|
||||||
|
if (messages == null) //Cache disabled
|
||||||
|
return ChannelHelper.GetMessagesAsync(channel, discord, fromMessageId, dir, limit);
|
||||||
|
|
||||||
|
var cachedMessages = messages.GetMany(fromMessageId, dir, limit);
|
||||||
|
var result = ImmutableArray.Create(cachedMessages).ToAsyncEnumerable<IReadOnlyCollection<IMessage>>();
|
||||||
|
limit -= cachedMessages.Count;
|
||||||
|
if (limit == 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (dir == Direction.Before)
|
||||||
|
fromMessageId = cachedMessages.Min(x => x.Id);
|
||||||
|
else
|
||||||
|
fromMessageId = cachedMessages.Max(x => x.Id);
|
||||||
|
var downloadedMessages = ChannelHelper.GetMessagesAsync(channel, discord, fromMessageId, dir, limit);
|
||||||
|
return result.Concat(downloadedMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddMessage(ISocketMessageChannel channel, DiscordSocketClient discord,
|
||||||
|
SocketMessage msg)
|
||||||
|
{
|
||||||
|
//C#7 Candidate for pattern matching
|
||||||
|
if (channel is SocketDMChannel)
|
||||||
|
(channel as SocketDMChannel).AddMessage(msg);
|
||||||
|
else if (channel is SocketGroupChannel)
|
||||||
|
(channel as SocketDMChannel).AddMessage(msg);
|
||||||
|
else if (channel is SocketTextChannel)
|
||||||
|
(channel as SocketDMChannel).AddMessage(msg);
|
||||||
|
else
|
||||||
|
throw new NotSupportedException("Unexpected ISocketMessageChannel type");
|
||||||
|
}
|
||||||
|
public static SocketMessage RemoveMessage(ISocketMessageChannel channel, DiscordSocketClient discord,
|
||||||
|
ulong id)
|
||||||
|
{
|
||||||
|
//C#7 Candidate for pattern matching
|
||||||
|
if (channel is SocketDMChannel)
|
||||||
|
return (channel as SocketDMChannel).RemoveMessage(id);
|
||||||
|
else if (channel is SocketGroupChannel)
|
||||||
|
return (channel as SocketDMChannel).RemoveMessage(id);
|
||||||
|
else if (channel is SocketTextChannel)
|
||||||
|
return (channel as SocketDMChannel).RemoveMessage(id);
|
||||||
|
else
|
||||||
|
throw new NotSupportedException("Unexpected ISocketMessageChannel type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,60 +12,52 @@ using Model = Discord.API.Channel;
|
|||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public class SocketDMChannel : SocketChannel, IDMChannel
|
public class SocketDMChannel : SocketChannel, IDMChannel, ISocketPrivateChannel, ISocketMessageChannel
|
||||||
{
|
{
|
||||||
private readonly MessageCache _messages;
|
private readonly MessageCache _messages;
|
||||||
|
|
||||||
public SocketUser Recipient { get; private set; }
|
public SocketUser Recipient { get; private set; }
|
||||||
|
|
||||||
public IReadOnlyCollection<SocketUser> Users => ImmutableArray.Create(Discord.CurrentUser, Recipient);
|
public IReadOnlyCollection<SocketMessage> CachedMessages => _messages?.Messages ?? ImmutableArray.Create<SocketMessage>();
|
||||||
|
public new IReadOnlyCollection<SocketUser> Users => ImmutableArray.Create(Discord.CurrentUser, Recipient);
|
||||||
|
|
||||||
internal SocketDMChannel(DiscordSocketClient discord, ulong id, ulong recipientId)
|
internal SocketDMChannel(DiscordSocketClient discord, ulong id, SocketGlobalUser recipient)
|
||||||
: base(discord, id)
|
: base(discord, id)
|
||||||
{
|
{
|
||||||
Recipient = new SocketUser(Discord, recipientId);
|
Recipient = recipient;
|
||||||
if (Discord.MessageCacheSize > 0)
|
if (Discord.MessageCacheSize > 0)
|
||||||
_messages = new MessageCache(Discord, this);
|
_messages = new MessageCache(Discord, this);
|
||||||
}
|
}
|
||||||
internal new static SocketDMChannel Create(DiscordSocketClient discord, Model model)
|
internal static SocketDMChannel Create(DiscordSocketClient discord, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketDMChannel(discord, model.Id, model.Recipients.Value[0].Id);
|
var entity = new SocketDMChannel(discord, model.Id, discord.GetOrCreateUser(state, model.Recipients.Value[0]));
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
internal void Update(Model model)
|
internal override void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
Recipient.Update(model.Recipients.Value[0]);
|
Recipient.Update(state, model.Recipients.Value[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task CloseAsync()
|
public Task CloseAsync()
|
||||||
=> ChannelHelper.DeleteAsync(this, Discord);
|
=> ChannelHelper.DeleteAsync(this, Discord);
|
||||||
|
|
||||||
public SocketUser GetUser(ulong id)
|
//Messages
|
||||||
{
|
public SocketMessage GetCachedMessage(ulong id)
|
||||||
if (id == Recipient.Id)
|
|
||||||
return Recipient;
|
|
||||||
else if (id == Discord.CurrentUser.Id)
|
|
||||||
return Discord.CurrentUser as SocketSelfUser;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SocketMessage GetMessage(ulong id)
|
|
||||||
=> _messages?.Get(id);
|
=> _messages?.Get(id);
|
||||||
public async Task<IMessage> GetMessageAsync(ulong id, bool allowDownload = true)
|
public async Task<IMessage> GetMessageAsync(ulong id)
|
||||||
{
|
{
|
||||||
IMessage msg = _messages?.Get(id);
|
IMessage msg = _messages?.Get(id);
|
||||||
if (msg == null && allowDownload)
|
if (msg == null)
|
||||||
msg = await ChannelHelper.GetMessageAsync(this, Discord, id);
|
msg = await ChannelHelper.GetMessageAsync(this, Discord, id);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, limit: limit);
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit);
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit);
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit);
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit);
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit);
|
||||||
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync()
|
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync()
|
||||||
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
||||||
|
|
||||||
@@ -82,38 +74,61 @@ namespace Discord.WebSocket
|
|||||||
public IDisposable EnterTypingState()
|
public IDisposable EnterTypingState()
|
||||||
=> ChannelHelper.EnterTypingState(this, Discord);
|
=> ChannelHelper.EnterTypingState(this, Discord);
|
||||||
|
|
||||||
internal SocketMessage AddMessage(SocketUser author, MessageModel model)
|
internal void AddMessage(SocketMessage msg)
|
||||||
{
|
=> _messages.Add(msg);
|
||||||
var msg = SocketMessage.Create(Discord, author, model);
|
|
||||||
_messages.Add(msg);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
internal SocketMessage RemoveMessage(ulong id)
|
internal SocketMessage RemoveMessage(ulong id)
|
||||||
{
|
=> _messages.Remove(id);
|
||||||
return _messages.Remove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SocketDMChannel Clone() => MemberwiseClone() as SocketDMChannel;
|
//Users
|
||||||
|
public new SocketUser GetUser(ulong id)
|
||||||
|
{
|
||||||
|
if (id == Recipient.Id)
|
||||||
|
return Recipient;
|
||||||
|
else if (id == Discord.CurrentUser.Id)
|
||||||
|
return Discord.CurrentUser as SocketSelfUser;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString() => $"@{Recipient}";
|
public override string ToString() => $"@{Recipient}";
|
||||||
private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)";
|
private string DebuggerDisplay => $"@{Recipient} ({Id}, DM)";
|
||||||
|
internal new SocketDMChannel Clone() => MemberwiseClone() as SocketDMChannel;
|
||||||
|
|
||||||
|
//SocketChannel
|
||||||
|
internal override IReadOnlyCollection<SocketUser> GetUsersInternal() => Users;
|
||||||
|
internal override SocketUser GetUserInternal(ulong id) => GetUser(id);
|
||||||
|
|
||||||
//IDMChannel
|
//IDMChannel
|
||||||
IUser IDMChannel.Recipient => Recipient;
|
IUser IDMChannel.Recipient => Recipient;
|
||||||
|
|
||||||
|
//ISocketPrivateChannel
|
||||||
|
IReadOnlyCollection<SocketUser> ISocketPrivateChannel.Recipients => ImmutableArray.Create(Recipient);
|
||||||
|
|
||||||
//IPrivateChannel
|
//IPrivateChannel
|
||||||
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create<IUser>(Recipient);
|
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create<IUser>(Recipient);
|
||||||
|
|
||||||
//IMessageChannel
|
//IMessageChannel
|
||||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages => ImmutableArray.Create<IMessage>();
|
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||||
IMessage IMessageChannel.GetCachedMessage(ulong id) => null;
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
return await GetMessageAsync(id);
|
||||||
=> await GetMessageAsync(id);
|
else
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
return GetCachedMessage(id);
|
||||||
=> GetMessagesAsync(limit);
|
}
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return SocketChannelHelper.PagedGetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit);
|
||||||
|
else
|
||||||
|
return ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit);
|
||||||
|
}
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return SocketChannelHelper.PagedGetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit);
|
||||||
|
else
|
||||||
|
return ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit);
|
||||||
|
}
|
||||||
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
||||||
=> await GetPinnedMessagesAsync().ConfigureAwait(false);
|
=> await GetPinnedMessagesAsync().ConfigureAwait(false);
|
||||||
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS)
|
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS)
|
||||||
@@ -125,14 +140,10 @@ namespace Discord.WebSocket
|
|||||||
IDisposable IMessageChannel.EnterTypingState()
|
IDisposable IMessageChannel.EnterTypingState()
|
||||||
=> EnterTypingState();
|
=> EnterTypingState();
|
||||||
|
|
||||||
//IChannel
|
//IChannel
|
||||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => Users;
|
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
|
|
||||||
IUser IChannel.GetCachedUser(ulong id)
|
|
||||||
=> GetUser(id);
|
|
||||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
|
||||||
=> Task.FromResult<IUser>(GetUser(id));
|
=> Task.FromResult<IUser>(GetUser(id));
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable();
|
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MessageModel = Discord.API.Message;
|
|
||||||
using Model = Discord.API.Channel;
|
using Model = Discord.API.Channel;
|
||||||
using UserModel = Discord.API.User;
|
using UserModel = Discord.API.User;
|
||||||
using VoiceStateModel = Discord.API.VoiceState;
|
using VoiceStateModel = Discord.API.VoiceState;
|
||||||
@@ -15,7 +14,7 @@ using VoiceStateModel = Discord.API.VoiceState;
|
|||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public class SocketGroupChannel : SocketChannel, IGroupChannel
|
public class SocketGroupChannel : SocketChannel, IGroupChannel, ISocketPrivateChannel, ISocketMessageChannel, ISocketAudioChannel
|
||||||
{
|
{
|
||||||
private readonly MessageCache _messages;
|
private readonly MessageCache _messages;
|
||||||
|
|
||||||
@@ -25,7 +24,8 @@ namespace Discord.WebSocket
|
|||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
public IReadOnlyCollection<SocketGroupUser> Users => _users.ToReadOnlyCollection();
|
public IReadOnlyCollection<SocketMessage> CachedMessages => _messages?.Messages ?? ImmutableArray.Create<SocketMessage>();
|
||||||
|
public new IReadOnlyCollection<SocketGroupUser> Users => _users.ToReadOnlyCollection();
|
||||||
public IReadOnlyCollection<SocketGroupUser> Recipients
|
public IReadOnlyCollection<SocketGroupUser> Recipients
|
||||||
=> _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1);
|
=> _users.Select(x => x.Value).Where(x => x.Id != Discord.CurrentUser.Id).ToReadOnlyCollection(() => _users.Count - 1);
|
||||||
|
|
||||||
@@ -37,13 +37,13 @@ namespace Discord.WebSocket
|
|||||||
_voiceStates = new ConcurrentDictionary<ulong, SocketVoiceState>(1, 5);
|
_voiceStates = new ConcurrentDictionary<ulong, SocketVoiceState>(1, 5);
|
||||||
_users = new ConcurrentDictionary<ulong, SocketGroupUser>(1, 5);
|
_users = new ConcurrentDictionary<ulong, SocketGroupUser>(1, 5);
|
||||||
}
|
}
|
||||||
internal new static SocketGroupChannel Create(DiscordSocketClient discord, Model model)
|
internal static SocketGroupChannel Create(DiscordSocketClient discord, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketGroupChannel(discord, model.Id);
|
var entity = new SocketGroupChannel(discord, model.Id);
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
internal void Update(Model model)
|
internal override void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
if (model.Name.IsSpecified)
|
if (model.Name.IsSpecified)
|
||||||
Name = model.Name.Value;
|
Name = model.Name.Value;
|
||||||
@@ -51,37 +51,35 @@ namespace Discord.WebSocket
|
|||||||
_iconId = model.Icon.Value;
|
_iconId = model.Icon.Value;
|
||||||
|
|
||||||
if (model.Recipients.IsSpecified)
|
if (model.Recipients.IsSpecified)
|
||||||
UpdateUsers(model.Recipients.Value);
|
UpdateUsers(state, model.Recipients.Value);
|
||||||
}
|
}
|
||||||
internal virtual void UpdateUsers(API.User[] models)
|
internal virtual void UpdateUsers(ClientState state, UserModel[] models)
|
||||||
{
|
{
|
||||||
var users = new ConcurrentDictionary<ulong, SocketGroupUser>(1, (int)(models.Length * 1.05));
|
var users = new ConcurrentDictionary<ulong, SocketGroupUser>(1, (int)(models.Length * 1.05));
|
||||||
for (int i = 0; i < models.Length; i++)
|
for (int i = 0; i < models.Length; i++)
|
||||||
users[models[i].Id] = SocketGroupUser.Create(Discord, models[i]);
|
users[models[i].Id] = SocketGroupUser.Create(this, state, models[i]);
|
||||||
_users = users;
|
_users = users;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync()
|
|
||||||
=> Update(await ChannelHelper.GetAsync(this, Discord));
|
|
||||||
public Task LeaveAsync()
|
public Task LeaveAsync()
|
||||||
=> ChannelHelper.DeleteAsync(this, Discord);
|
=> ChannelHelper.DeleteAsync(this, Discord);
|
||||||
|
|
||||||
public SocketGroupUser GetUser(ulong id)
|
//Messages
|
||||||
|
public SocketMessage GetCachedMessage(ulong id)
|
||||||
|
=> _messages?.Get(id);
|
||||||
|
public async Task<IMessage> GetMessageAsync(ulong id)
|
||||||
{
|
{
|
||||||
SocketGroupUser user;
|
IMessage msg = _messages?.Get(id);
|
||||||
if (_users.TryGetValue(id, out user))
|
if (msg == null)
|
||||||
return user;
|
msg = await ChannelHelper.GetMessageAsync(this, Discord, id);
|
||||||
return null;
|
return msg;
|
||||||
}
|
}
|
||||||
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
public Task<RestMessage> GetMessageAsync(ulong id)
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit);
|
||||||
=> ChannelHelper.GetMessageAsync(this, Discord, id);
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit);
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, limit: limit);
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit);
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit);
|
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit);
|
|
||||||
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync()
|
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync()
|
||||||
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
||||||
|
|
||||||
@@ -98,20 +96,101 @@ namespace Discord.WebSocket
|
|||||||
public IDisposable EnterTypingState()
|
public IDisposable EnterTypingState()
|
||||||
=> ChannelHelper.EnterTypingState(this, Discord);
|
=> ChannelHelper.EnterTypingState(this, Discord);
|
||||||
|
|
||||||
|
internal void AddMessage(SocketMessage msg)
|
||||||
|
=> _messages.Add(msg);
|
||||||
|
internal SocketMessage RemoveMessage(ulong id)
|
||||||
|
=> _messages.Remove(id);
|
||||||
|
|
||||||
|
//Users
|
||||||
|
public new SocketGroupUser GetUser(ulong id)
|
||||||
|
{
|
||||||
|
SocketGroupUser user;
|
||||||
|
if (_users.TryGetValue(id, out user))
|
||||||
|
return user;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
internal SocketGroupUser AddUser(UserModel model)
|
||||||
|
{
|
||||||
|
SocketGroupUser user;
|
||||||
|
if (_users.TryGetValue(model.Id, out user))
|
||||||
|
return user as SocketGroupUser;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var privateUser = SocketGroupUser.Create(this, Discord.State, model);
|
||||||
|
_users[privateUser.Id] = privateUser;
|
||||||
|
return privateUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal SocketGroupUser RemoveUser(ulong id)
|
||||||
|
{
|
||||||
|
SocketGroupUser user;
|
||||||
|
if (_users.TryRemove(id, out user))
|
||||||
|
{
|
||||||
|
user.GlobalUser.RemoveRef(Discord);
|
||||||
|
return user as SocketGroupUser;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Voice States
|
||||||
|
internal SocketVoiceState AddOrUpdateVoiceState(ClientState state, VoiceStateModel model)
|
||||||
|
{
|
||||||
|
var voiceChannel = state.GetChannel(model.ChannelId.Value) as SocketVoiceChannel;
|
||||||
|
var voiceState = SocketVoiceState.Create(voiceChannel, model);
|
||||||
|
_voiceStates[model.UserId] = voiceState;
|
||||||
|
return voiceState;
|
||||||
|
}
|
||||||
|
internal SocketVoiceState? GetVoiceState(ulong id)
|
||||||
|
{
|
||||||
|
SocketVoiceState voiceState;
|
||||||
|
if (_voiceStates.TryGetValue(id, out voiceState))
|
||||||
|
return voiceState;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
internal SocketVoiceState? RemoveVoiceState(ulong id)
|
||||||
|
{
|
||||||
|
SocketVoiceState voiceState;
|
||||||
|
if (_voiceStates.TryRemove(id, out voiceState))
|
||||||
|
return voiceState;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => Name;
|
||||||
|
private string DebuggerDisplay => $"{Name} ({Id}, Group)";
|
||||||
|
internal new SocketGroupChannel Clone() => MemberwiseClone() as SocketGroupChannel;
|
||||||
|
|
||||||
|
//SocketChannel
|
||||||
|
internal override IReadOnlyCollection<SocketUser> GetUsersInternal() => Users;
|
||||||
|
internal override SocketUser GetUserInternal(ulong id) => GetUser(id);
|
||||||
|
|
||||||
|
//ISocketPrivateChannel
|
||||||
|
IReadOnlyCollection<SocketUser> ISocketPrivateChannel.Recipients => Recipients;
|
||||||
|
|
||||||
//IPrivateChannel
|
//IPrivateChannel
|
||||||
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => Recipients;
|
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => Recipients;
|
||||||
|
|
||||||
//IMessageChannel
|
//IMessageChannel
|
||||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages => ImmutableArray.Create<IMessage>();
|
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||||
|
{
|
||||||
IMessage IMessageChannel.GetCachedMessage(ulong id)
|
if (mode == CacheMode.AllowDownload)
|
||||||
=> null;
|
return await GetMessageAsync(id);
|
||||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
else
|
||||||
=> await GetMessageAsync(id);
|
return GetCachedMessage(id);
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
}
|
||||||
=> GetMessagesAsync(limit);
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
{
|
||||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return SocketChannelHelper.PagedGetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit);
|
||||||
|
else
|
||||||
|
return ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit);
|
||||||
|
}
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return SocketChannelHelper.PagedGetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit);
|
||||||
|
else
|
||||||
|
return ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit);
|
||||||
|
}
|
||||||
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
||||||
=> await GetPinnedMessagesAsync();
|
=> await GetPinnedMessagesAsync();
|
||||||
|
|
||||||
@@ -124,14 +203,10 @@ namespace Discord.WebSocket
|
|||||||
IDisposable IMessageChannel.EnterTypingState()
|
IDisposable IMessageChannel.EnterTypingState()
|
||||||
=> EnterTypingState();
|
=> EnterTypingState();
|
||||||
|
|
||||||
//IChannel
|
//IChannel
|
||||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => Users;
|
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
|
|
||||||
IUser IChannel.GetCachedUser(ulong id)
|
|
||||||
=> GetUser(id);
|
|
||||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
|
||||||
=> Task.FromResult<IUser>(GetUser(id));
|
=> Task.FromResult<IUser>(GetUser(id));
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable();
|
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,31 +15,31 @@ namespace Discord.WebSocket
|
|||||||
{
|
{
|
||||||
private ImmutableArray<Overwrite> _overwrites;
|
private ImmutableArray<Overwrite> _overwrites;
|
||||||
|
|
||||||
public IReadOnlyCollection<Overwrite> PermissionOverwrites => _overwrites;
|
public SocketGuild Guild { get; }
|
||||||
|
|
||||||
public ulong GuildId { get; }
|
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
public int Position { get; private set; }
|
public int Position { get; private set; }
|
||||||
|
|
||||||
internal SocketGuildChannel(DiscordSocketClient discord, ulong id, ulong guildId)
|
public IReadOnlyCollection<Overwrite> PermissionOverwrites => _overwrites;
|
||||||
|
public new abstract IReadOnlyCollection<SocketGuildUser> Users { get; }
|
||||||
|
|
||||||
|
internal SocketGuildChannel(DiscordSocketClient discord, ulong id, SocketGuild guild)
|
||||||
: base(discord, id)
|
: base(discord, id)
|
||||||
{
|
{
|
||||||
GuildId = guildId;
|
Guild = guild;
|
||||||
}
|
}
|
||||||
internal new static SocketGuildChannel Create(DiscordSocketClient discord, Model model)
|
internal static SocketGuildChannel Create(SocketGuild guild, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
switch (model.Type)
|
switch (model.Type)
|
||||||
{
|
{
|
||||||
case ChannelType.Text:
|
case ChannelType.Text:
|
||||||
return SocketTextChannel.Create(discord, model);
|
return SocketTextChannel.Create(guild, state, model);
|
||||||
case ChannelType.Voice:
|
case ChannelType.Voice:
|
||||||
return SocketVoiceChannel.Create(discord, model);
|
return SocketVoiceChannel.Create(guild, state, model);
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException("Unknown guild channel type");
|
throw new InvalidOperationException("Unknown guild channel type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal virtual void Update(Model model)
|
internal override void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
Name = model.Name.Value;
|
Name = model.Name.Value;
|
||||||
Position = model.Position.Value;
|
Position = model.Position.Value;
|
||||||
@@ -50,9 +50,7 @@ namespace Discord.WebSocket
|
|||||||
newOverwrites.Add(new Overwrite(overwrites[i]));
|
newOverwrites.Add(new Overwrite(overwrites[i]));
|
||||||
_overwrites = newOverwrites.ToImmutable();
|
_overwrites = newOverwrites.ToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync()
|
|
||||||
=> Update(await ChannelHelper.GetAsync(this, Discord));
|
|
||||||
public Task ModifyAsync(Action<ModifyGuildChannelParams> func)
|
public Task ModifyAsync(Action<ModifyGuildChannelParams> func)
|
||||||
=> ChannelHelper.ModifyAsync(this, Discord, func);
|
=> ChannelHelper.ModifyAsync(this, Discord, func);
|
||||||
public Task DeleteAsync()
|
public Task DeleteAsync()
|
||||||
@@ -118,7 +116,18 @@ namespace Discord.WebSocket
|
|||||||
public async Task<RestInviteMetadata> CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = true)
|
public async Task<RestInviteMetadata> CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = true)
|
||||||
=> await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary);
|
=> await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary);
|
||||||
|
|
||||||
|
public new abstract SocketGuildUser GetUser(ulong id);
|
||||||
|
|
||||||
|
public override string ToString() => Name;
|
||||||
|
internal new SocketGuildChannel Clone() => MemberwiseClone() as SocketGuildChannel;
|
||||||
|
|
||||||
|
//SocketChannel
|
||||||
|
internal override IReadOnlyCollection<SocketUser> GetUsersInternal() => Users;
|
||||||
|
internal override SocketUser GetUserInternal(ulong id) => GetUser(id);
|
||||||
|
|
||||||
//IGuildChannel
|
//IGuildChannel
|
||||||
|
ulong IGuildChannel.GuildId => Guild.Id;
|
||||||
|
|
||||||
async Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync()
|
async Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync()
|
||||||
=> await GetInvitesAsync();
|
=> await GetInvitesAsync();
|
||||||
async Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary)
|
async Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary)
|
||||||
@@ -136,24 +145,16 @@ namespace Discord.WebSocket
|
|||||||
=> await RemovePermissionOverwriteAsync(role);
|
=> await RemovePermissionOverwriteAsync(role);
|
||||||
async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user)
|
async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user)
|
||||||
=> await RemovePermissionOverwriteAsync(user);
|
=> await RemovePermissionOverwriteAsync(user);
|
||||||
|
|
||||||
IReadOnlyCollection<IGuildUser> IGuildChannel.CachedUsers
|
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IGuildUser>();
|
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>(Users).ToAsyncEnumerable();
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
=> Task.FromResult<IGuildUser>(GetUser(id));
|
||||||
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id)
|
|
||||||
=> Task.FromResult<IGuildUser>(null); //Overriden in Text/Voice //TODO: Does this actually override?
|
|
||||||
IGuildUser IGuildChannel.GetCachedUser(ulong id)
|
|
||||||
=> null;
|
|
||||||
|
|
||||||
//IChannel
|
//IChannel
|
||||||
IReadOnlyCollection<IUser> IChannel.CachedUsers
|
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IUser>();
|
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||||
IUser IChannel.GetCachedUser(ulong id)
|
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> null;
|
=> Task.FromResult<IUser>(GetUser(id)); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
|
||||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
|
||||||
=> Task.FromResult<IUser>(null); //Overriden in Text/Voice //TODO: Does this actually override?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,29 +13,34 @@ using Model = Discord.API.Channel;
|
|||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public class SocketTextChannel : SocketGuildChannel, ITextChannel
|
public class SocketTextChannel : SocketGuildChannel, ITextChannel, ISocketMessageChannel
|
||||||
{
|
{
|
||||||
private readonly MessageCache _messages;
|
private readonly MessageCache _messages;
|
||||||
|
|
||||||
public string Topic { get; private set; }
|
public string Topic { get; private set; }
|
||||||
|
|
||||||
public string Mention => MentionUtils.MentionChannel(Id);
|
public string Mention => MentionUtils.MentionChannel(Id);
|
||||||
|
public IReadOnlyCollection<SocketMessage> CachedMessages => _messages?.Messages ?? ImmutableArray.Create<SocketMessage>();
|
||||||
internal SocketTextChannel(DiscordSocketClient discord, ulong id, ulong guildId)
|
public override IReadOnlyCollection<SocketGuildUser> Users
|
||||||
: base(discord, id, guildId)
|
=> Guild.Users.Where(x => Permissions.GetValue(
|
||||||
|
Permissions.ResolveChannel(Guild, x, this, Permissions.ResolveGuild(Guild, x)),
|
||||||
|
ChannelPermission.ReadMessages)).ToImmutableArray();
|
||||||
|
|
||||||
|
internal SocketTextChannel(DiscordSocketClient discord, ulong id, SocketGuild guild)
|
||||||
|
: base(discord, id, guild)
|
||||||
{
|
{
|
||||||
if (Discord.MessageCacheSize > 0)
|
if (Discord.MessageCacheSize > 0)
|
||||||
_messages = new MessageCache(Discord, this);
|
_messages = new MessageCache(Discord, this);
|
||||||
}
|
}
|
||||||
internal new static SocketTextChannel Create(DiscordSocketClient discord, Model model)
|
internal new static SocketTextChannel Create(SocketGuild guild, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketTextChannel(discord, model.Id, model.GuildId.Value);
|
var entity = new SocketTextChannel(guild.Discord, model.Id, guild);
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
internal override void Update(Model model)
|
internal override void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
base.Update(model);
|
base.Update(state, model);
|
||||||
|
|
||||||
Topic = model.Topic.Value;
|
Topic = model.Topic.Value;
|
||||||
}
|
}
|
||||||
@@ -43,19 +48,22 @@ namespace Discord.WebSocket
|
|||||||
public Task ModifyAsync(Action<ModifyTextChannelParams> func)
|
public Task ModifyAsync(Action<ModifyTextChannelParams> func)
|
||||||
=> ChannelHelper.ModifyAsync(this, Discord, func);
|
=> ChannelHelper.ModifyAsync(this, Discord, func);
|
||||||
|
|
||||||
public Task<RestGuildUser> GetUserAsync(ulong id)
|
//Messages
|
||||||
=> ChannelHelper.GetUserAsync(this, Discord, id);
|
public SocketMessage GetCachedMessage(ulong id)
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync()
|
=> _messages?.Get(id);
|
||||||
=> ChannelHelper.GetUsersAsync(this, Discord);
|
public async Task<IMessage> GetMessageAsync(ulong id)
|
||||||
|
{
|
||||||
public Task<RestMessage> GetMessageAsync(ulong id)
|
IMessage msg = _messages?.Get(id);
|
||||||
=> ChannelHelper.GetMessageAsync(this, Discord, id);
|
if (msg == null)
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
msg = await ChannelHelper.GetMessageAsync(this, Discord, id);
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, limit: limit);
|
return msg;
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
}
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit);
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit);
|
||||||
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit);
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit);
|
||||||
|
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
|
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit);
|
||||||
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync()
|
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync()
|
||||||
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
||||||
|
|
||||||
@@ -72,36 +80,56 @@ namespace Discord.WebSocket
|
|||||||
public IDisposable EnterTypingState()
|
public IDisposable EnterTypingState()
|
||||||
=> ChannelHelper.EnterTypingState(this, Discord);
|
=> ChannelHelper.EnterTypingState(this, Discord);
|
||||||
|
|
||||||
internal SocketMessage AddMessage(SocketUser author, MessageModel model)
|
internal void AddMessage(SocketMessage msg)
|
||||||
{
|
=> _messages.Add(msg);
|
||||||
var msg = SocketMessage.Create(Discord, author, model);
|
|
||||||
_messages.Add(msg);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
internal SocketMessage RemoveMessage(ulong id)
|
internal SocketMessage RemoveMessage(ulong id)
|
||||||
{
|
=> _messages.Remove(id);
|
||||||
return _messages.Remove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => Name;
|
//Users
|
||||||
private string DebuggerDisplay => $"@{Name} ({Id}, Text)";
|
public override SocketGuildUser GetUser(ulong id)
|
||||||
|
{
|
||||||
|
var user = Guild.GetUser(id);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
var guildPerms = Permissions.ResolveGuild(Guild, user);
|
||||||
|
var channelPerms = Permissions.ResolveChannel(Guild, user, this, guildPerms);
|
||||||
|
if (Permissions.GetValue(channelPerms, ChannelPermission.ReadMessages))
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DebuggerDisplay => $"{Name} ({Id}, Text)";
|
||||||
|
internal new SocketTextChannel Clone() => MemberwiseClone() as SocketTextChannel;
|
||||||
|
|
||||||
//IGuildChannel
|
//IGuildChannel
|
||||||
async Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id)
|
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> await GetUserAsync(id);
|
=> Task.FromResult<IGuildUser>(GetUser(id));
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> GetUsersAsync();
|
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>(Users).ToAsyncEnumerable();
|
||||||
|
|
||||||
//IMessageChannel
|
//IMessageChannel
|
||||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages => ImmutableArray.Create<IMessage>();
|
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||||
IMessage IMessageChannel.GetCachedMessage(ulong id) => null;
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
return await GetMessageAsync(id);
|
||||||
=> await GetMessageAsync(id);
|
else
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
throw new NotImplementedException();
|
||||||
=> GetMessagesAsync(limit);
|
}
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return SocketChannelHelper.PagedGetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit);
|
||||||
|
else
|
||||||
|
return ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit);
|
||||||
|
}
|
||||||
|
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode)
|
||||||
|
{
|
||||||
|
if (mode == CacheMode.AllowDownload)
|
||||||
|
return SocketChannelHelper.PagedGetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit);
|
||||||
|
else
|
||||||
|
return ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit);
|
||||||
|
}
|
||||||
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync()
|
||||||
=> await GetPinnedMessagesAsync().ConfigureAwait(false);
|
=> await GetPinnedMessagesAsync().ConfigureAwait(false);
|
||||||
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS)
|
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS)
|
||||||
|
|||||||
@@ -12,24 +12,27 @@ using Model = Discord.API.Channel;
|
|||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public class SocketVoiceChannel : SocketGuildChannel, IVoiceChannel
|
public class SocketVoiceChannel : SocketGuildChannel, IVoiceChannel, ISocketAudioChannel
|
||||||
{
|
{
|
||||||
public int Bitrate { get; private set; }
|
public int Bitrate { get; private set; }
|
||||||
public int UserLimit { get; private set; }
|
public int UserLimit { get; private set; }
|
||||||
|
|
||||||
internal SocketVoiceChannel(DiscordSocketClient discord, ulong id, ulong guildId)
|
public override IReadOnlyCollection<SocketGuildUser> Users
|
||||||
: base(discord, id, guildId)
|
=> Guild.Users.Where(x => x.VoiceChannel?.Id == Id).ToImmutableArray();
|
||||||
|
|
||||||
|
internal SocketVoiceChannel(DiscordSocketClient discord, ulong id, SocketGuild guild)
|
||||||
|
: base(discord, id, guild)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
internal new static SocketVoiceChannel Create(DiscordSocketClient discord, Model model)
|
internal new static SocketVoiceChannel Create(SocketGuild guild, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketVoiceChannel(discord, model.Id, model.GuildId.Value);
|
var entity = new SocketVoiceChannel(guild.Discord, model.Id, guild);
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
internal override void Update(Model model)
|
internal override void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
base.Update(model);
|
base.Update(state, model);
|
||||||
|
|
||||||
Bitrate = model.Bitrate.Value;
|
Bitrate = model.Bitrate.Value;
|
||||||
UserLimit = model.UserLimit.Value;
|
UserLimit = model.UserLimit.Value;
|
||||||
@@ -38,13 +41,24 @@ namespace Discord.WebSocket
|
|||||||
public Task ModifyAsync(Action<ModifyVoiceChannelParams> func)
|
public Task ModifyAsync(Action<ModifyVoiceChannelParams> func)
|
||||||
=> ChannelHelper.ModifyAsync(this, Discord, func);
|
=> ChannelHelper.ModifyAsync(this, Discord, func);
|
||||||
|
|
||||||
|
public override SocketGuildUser GetUser(ulong id)
|
||||||
|
{
|
||||||
|
var user = Guild.GetUser(id);
|
||||||
|
if (user?.VoiceChannel?.Id == Id)
|
||||||
|
return user;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DebuggerDisplay => $"{Name} ({Id}, Voice)";
|
||||||
|
internal new SocketVoiceChannel Clone() => MemberwiseClone() as SocketVoiceChannel;
|
||||||
|
|
||||||
//IVoiceChannel
|
//IVoiceChannel
|
||||||
Task<IAudioClient> IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); }
|
Task<IAudioClient> IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); }
|
||||||
|
|
||||||
//IGuildChannel
|
//IGuildChannel
|
||||||
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id)
|
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> Task.FromResult<IGuildUser>(null);
|
=> Task.FromResult<IGuildUser>(GetUser(id));
|
||||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable();
|
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>(Users).ToAsyncEnumerable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,14 @@ namespace Discord.WebSocket
|
|||||||
{
|
{
|
||||||
public class SocketGuild : SocketEntity<ulong>, IGuild
|
public class SocketGuild : SocketEntity<ulong>, IGuild
|
||||||
{
|
{
|
||||||
private ImmutableDictionary<ulong, RestRole> _roles;
|
private readonly SemaphoreSlim _audioLock;
|
||||||
|
private TaskCompletionSource<bool> _syncPromise, _downloaderPromise;
|
||||||
|
private TaskCompletionSource<AudioClient> _audioConnectPromise;
|
||||||
|
private ConcurrentHashSet<ulong> _channels;
|
||||||
|
private ConcurrentDictionary<ulong, SocketGuildUser> _members;
|
||||||
|
private ConcurrentDictionary<ulong, SocketRole> _roles;
|
||||||
|
private ConcurrentDictionary<ulong, SocketVoiceState> _voiceStates;
|
||||||
|
private ConcurrentDictionary<ulong, PresenceModel> _cachedPresences;
|
||||||
private ImmutableArray<Emoji> _emojis;
|
private ImmutableArray<Emoji> _emojis;
|
||||||
private ImmutableArray<string> _features;
|
private ImmutableArray<string> _features;
|
||||||
internal bool _available;
|
internal bool _available;
|
||||||
@@ -33,6 +40,9 @@ namespace Discord.WebSocket
|
|||||||
public VerificationLevel VerificationLevel { get; private set; }
|
public VerificationLevel VerificationLevel { get; private set; }
|
||||||
public MfaLevel MfaLevel { get; private set; }
|
public MfaLevel MfaLevel { get; private set; }
|
||||||
public DefaultMessageNotifications DefaultMessageNotifications { get; private set; }
|
public DefaultMessageNotifications DefaultMessageNotifications { get; private set; }
|
||||||
|
public int MemberCount { get; set; }
|
||||||
|
public int DownloadedMemberCount { get; private set; }
|
||||||
|
public AudioClient AudioClient { get; private set; }
|
||||||
|
|
||||||
public ulong? AFKChannelId { get; private set; }
|
public ulong? AFKChannelId { get; private set; }
|
||||||
public ulong? EmbedChannelId { get; private set; }
|
public ulong? EmbedChannelId { get; private set; }
|
||||||
@@ -44,24 +54,117 @@ namespace Discord.WebSocket
|
|||||||
public ulong DefaultChannelId => Id;
|
public ulong DefaultChannelId => Id;
|
||||||
public string IconUrl => API.CDN.GetGuildIconUrl(Id, IconId);
|
public string IconUrl => API.CDN.GetGuildIconUrl(Id, IconId);
|
||||||
public string SplashUrl => API.CDN.GetGuildSplashUrl(Id, SplashId);
|
public string SplashUrl => API.CDN.GetGuildSplashUrl(Id, SplashId);
|
||||||
public bool IsSynced => false;
|
public bool HasAllMembers => _downloaderPromise.Task.IsCompleted;
|
||||||
|
public bool IsSynced => _syncPromise.Task.IsCompleted;
|
||||||
|
public Task SyncPromise => _syncPromise.Task;
|
||||||
|
public Task DownloaderPromise => _downloaderPromise.Task;
|
||||||
|
|
||||||
public RestRole EveryoneRole => GetRole(Id);
|
public SocketRole EveryoneRole => GetRole(Id);
|
||||||
public IReadOnlyCollection<RestRole> Roles => _roles.ToReadOnlyCollection();
|
public IReadOnlyCollection<SocketGuildChannel> Channels
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var channels = _channels;
|
||||||
|
var state = Discord.State;
|
||||||
|
return channels.Select(x => state.GetChannel(x) as SocketGuildChannel).Where(x => x != null).ToReadOnlyCollection(channels);
|
||||||
|
}
|
||||||
|
}
|
||||||
public IReadOnlyCollection<Emoji> Emojis => _emojis;
|
public IReadOnlyCollection<Emoji> Emojis => _emojis;
|
||||||
public IReadOnlyCollection<string> Features => _features;
|
public IReadOnlyCollection<string> Features => _features;
|
||||||
|
public IReadOnlyCollection<SocketGuildUser> Users => _members.ToReadOnlyCollection();
|
||||||
|
public IReadOnlyCollection<SocketRole> Roles => _roles.ToReadOnlyCollection();
|
||||||
|
public IReadOnlyCollection<SocketVoiceState> VoiceStates => _voiceStates.ToReadOnlyCollection();
|
||||||
|
|
||||||
internal SocketGuild(DiscordSocketClient client, ulong id)
|
internal SocketGuild(DiscordSocketClient client, ulong id)
|
||||||
: base(client, id)
|
: base(client, id)
|
||||||
{
|
{
|
||||||
|
_emojis = ImmutableArray.Create<Emoji>();
|
||||||
|
_features = ImmutableArray.Create<string>();
|
||||||
}
|
}
|
||||||
internal static SocketGuild Create(DiscordSocketClient discord, Model model)
|
internal static SocketGuild Create(DiscordSocketClient discord, ClientState state, ExtendedModel model)
|
||||||
{
|
{
|
||||||
var entity = new SocketGuild(discord, model.Id);
|
var entity = new SocketGuild(discord, model.Id);
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
internal void Update(Model model)
|
internal void Update(ClientState state, ExtendedModel model)
|
||||||
|
{
|
||||||
|
_available = !(model.Unavailable ?? false);
|
||||||
|
if (!_available)
|
||||||
|
{
|
||||||
|
if (_channels == null)
|
||||||
|
_channels = new ConcurrentHashSet<ulong>();
|
||||||
|
if (_members == null)
|
||||||
|
_members = new ConcurrentDictionary<ulong, SocketGuildUser>();
|
||||||
|
if (_roles == null)
|
||||||
|
_roles = new ConcurrentDictionary<ulong, SocketRole>();
|
||||||
|
/*if (Emojis == null)
|
||||||
|
_emojis = ImmutableArray.Create<Emoji>();
|
||||||
|
if (Features == null)
|
||||||
|
_features = ImmutableArray.Create<string>();*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Update(state, model as Model);
|
||||||
|
|
||||||
|
var channels = new ConcurrentHashSet<ulong>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Channels.Length * 1.05));
|
||||||
|
{
|
||||||
|
for (int i = 0; i < model.Channels.Length; i++)
|
||||||
|
{
|
||||||
|
var channel = SocketGuildChannel.Create(this, state, model.Channels[i]);
|
||||||
|
state.AddChannel(channel);
|
||||||
|
channels.TryAdd(channel.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_channels = channels;
|
||||||
|
|
||||||
|
var members = new ConcurrentDictionary<ulong, SocketGuildUser>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Members.Length * 1.05));
|
||||||
|
{
|
||||||
|
for (int i = 0; i < model.Members.Length; i++)
|
||||||
|
{
|
||||||
|
var member = SocketGuildUser.Create(this, state, model.Members[i]);
|
||||||
|
members.TryAdd(member.Id, member);
|
||||||
|
}
|
||||||
|
DownloadedMemberCount = members.Count;
|
||||||
|
}
|
||||||
|
var cachedPresences = new ConcurrentDictionary<ulong, PresenceModel>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Presences.Length * 1.05));
|
||||||
|
{
|
||||||
|
for (int i = 0; i < model.Presences.Length; i++)
|
||||||
|
{
|
||||||
|
SocketGuildUser member;
|
||||||
|
if (_members.TryGetValue(model.Presences[i].User.Id, out member))
|
||||||
|
member.Update(state, model.Presences[i]);
|
||||||
|
else
|
||||||
|
cachedPresences.TryAdd(model.Presences[i].User.Id, model.Presences[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_members = members;
|
||||||
|
_cachedPresences = cachedPresences;
|
||||||
|
MemberCount = model.MemberCount;
|
||||||
|
|
||||||
|
var voiceStates = new ConcurrentDictionary<ulong, SocketVoiceState>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.VoiceStates.Length * 1.05));
|
||||||
|
{
|
||||||
|
for (int i = 0; i < model.VoiceStates.Length; i++)
|
||||||
|
{
|
||||||
|
SocketVoiceChannel channel = null;
|
||||||
|
if (model.VoiceStates[i].ChannelId.HasValue)
|
||||||
|
channel = state.GetChannel(model.VoiceStates[i].ChannelId.Value) as SocketVoiceChannel;
|
||||||
|
var voiceState = SocketVoiceState.Create(channel, model.VoiceStates[i]);
|
||||||
|
voiceStates.TryAdd(model.VoiceStates[i].UserId, voiceState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_voiceStates = voiceStates;
|
||||||
|
|
||||||
|
_syncPromise = new TaskCompletionSource<bool>();
|
||||||
|
_downloaderPromise = new TaskCompletionSource<bool>();
|
||||||
|
if (Discord.ApiClient.AuthTokenType != TokenType.User)
|
||||||
|
{
|
||||||
|
var _ = _syncPromise.TrySetResultAsync(true);
|
||||||
|
if (!model.Large)
|
||||||
|
_ = _downloaderPromise.TrySetResultAsync(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
AFKChannelId = model.AFKChannelId;
|
AFKChannelId = model.AFKChannelId;
|
||||||
EmbedChannelId = model.EmbedChannelId;
|
EmbedChannelId = model.EmbedChannelId;
|
||||||
@@ -81,7 +184,7 @@ namespace Discord.WebSocket
|
|||||||
var emojis = ImmutableArray.CreateBuilder<Emoji>(model.Emojis.Length);
|
var emojis = ImmutableArray.CreateBuilder<Emoji>(model.Emojis.Length);
|
||||||
for (int i = 0; i < model.Emojis.Length; i++)
|
for (int i = 0; i < model.Emojis.Length; i++)
|
||||||
emojis.Add(Emoji.Create(model.Emojis[i]));
|
emojis.Add(Emoji.Create(model.Emojis[i]));
|
||||||
_emojis = emojis.ToImmutableArray();
|
_emojis = emojis.ToImmutable();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_emojis = ImmutableArray.Create<Emoji>();
|
_emojis = ImmutableArray.Create<Emoji>();
|
||||||
@@ -91,17 +194,52 @@ namespace Discord.WebSocket
|
|||||||
else
|
else
|
||||||
_features = ImmutableArray.Create<string>();
|
_features = ImmutableArray.Create<string>();
|
||||||
|
|
||||||
var roles = ImmutableDictionary.CreateBuilder<ulong, RestRole>();
|
var roles = new ConcurrentDictionary<ulong, SocketRole>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Roles.Length * 1.05));
|
||||||
if (model.Roles != null)
|
if (model.Roles != null)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
for (int i = 0; i < model.Roles.Length; i++)
|
||||||
|
{
|
||||||
|
var role = SocketRole.Create(this, state, model.Roles[i]);
|
||||||
|
roles.TryAdd(role.Id, role);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_roles = roles.ToImmutable();
|
_roles = roles;
|
||||||
|
}
|
||||||
|
internal void Update(ClientState state, GuildSyncModel model)
|
||||||
|
{
|
||||||
|
var members = new ConcurrentDictionary<ulong, SocketGuildUser>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Members.Length * 1.05));
|
||||||
|
{
|
||||||
|
for (int i = 0; i < model.Members.Length; i++)
|
||||||
|
{
|
||||||
|
var member = SocketGuildUser.Create(this, state, model.Members[i]);
|
||||||
|
members.TryAdd(member.Id, member);
|
||||||
|
}
|
||||||
|
DownloadedMemberCount = members.Count;
|
||||||
|
}
|
||||||
|
var cachedPresences = new ConcurrentDictionary<ulong, PresenceModel>(ConcurrentHashSet.DefaultConcurrencyLevel, (int)(model.Presences.Length * 1.05));
|
||||||
|
{
|
||||||
|
for (int i = 0; i < model.Presences.Length; i++)
|
||||||
|
{
|
||||||
|
SocketGuildUser member;
|
||||||
|
if (_members.TryGetValue(model.Presences[i].User.Id, out member))
|
||||||
|
member.Update(state, model.Presences[i]);
|
||||||
|
else
|
||||||
|
cachedPresences.TryAdd(model.Presences[i].User.Id, model.Presences[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_members = members;
|
||||||
|
_cachedPresences = cachedPresences;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Update(ClientState state, EmojiUpdateModel model)
|
||||||
|
{
|
||||||
|
var emojis = ImmutableArray.CreateBuilder<Emoji>(model.Emojis.Length);
|
||||||
|
for (int i = 0; i < model.Emojis.Length; i++)
|
||||||
|
emojis.Add(Emoji.Create(model.Emojis[i]));
|
||||||
|
_emojis = emojis.ToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
//General
|
//General
|
||||||
public async Task UpdateAsync()
|
|
||||||
=> Update(await Discord.ApiClient.GetGuildAsync(Id));
|
|
||||||
public Task DeleteAsync()
|
public Task DeleteAsync()
|
||||||
=> GuildHelper.DeleteAsync(this, Discord);
|
=> GuildHelper.DeleteAsync(this, Discord);
|
||||||
|
|
||||||
@@ -132,14 +270,30 @@ namespace Discord.WebSocket
|
|||||||
=> GuildHelper.RemoveBanAsync(this, Discord, userId);
|
=> GuildHelper.RemoveBanAsync(this, Discord, userId);
|
||||||
|
|
||||||
//Channels
|
//Channels
|
||||||
public Task<IReadOnlyCollection<RestGuildChannel>> GetChannelsAsync()
|
public SocketGuildChannel GetChannel(ulong id)
|
||||||
=> GuildHelper.GetChannelsAsync(this, Discord);
|
{
|
||||||
public Task<RestGuildChannel> GetChannelAsync(ulong id)
|
var channel = Discord.State.GetChannel(id) as SocketGuildChannel;
|
||||||
=> GuildHelper.GetChannelAsync(this, Discord, id);
|
if (channel?.Guild.Id == Id)
|
||||||
|
return channel;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
public Task<RestTextChannel> CreateTextChannelAsync(string name)
|
public Task<RestTextChannel> CreateTextChannelAsync(string name)
|
||||||
=> GuildHelper.CreateTextChannelAsync(this, Discord, name);
|
=> GuildHelper.CreateTextChannelAsync(this, Discord, name);
|
||||||
public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name)
|
public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name)
|
||||||
=> GuildHelper.CreateVoiceChannelAsync(this, Discord, name);
|
=> GuildHelper.CreateVoiceChannelAsync(this, Discord, name);
|
||||||
|
internal SocketGuildChannel AddChannel(ClientState state, ChannelModel model)
|
||||||
|
{
|
||||||
|
var channel = SocketGuildChannel.Create(this, state, model);
|
||||||
|
_channels.TryAdd(model.Id);
|
||||||
|
state.AddChannel(channel);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
internal SocketGuildChannel RemoveChannel(ClientState state, ulong id)
|
||||||
|
{
|
||||||
|
if (_channels.TryRemove(id))
|
||||||
|
return state.RemoveChannel(id) as SocketGuildChannel;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//Integrations
|
//Integrations
|
||||||
public Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync()
|
public Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync()
|
||||||
@@ -152,48 +306,238 @@ namespace Discord.WebSocket
|
|||||||
=> GuildHelper.GetInvitesAsync(this, Discord);
|
=> GuildHelper.GetInvitesAsync(this, Discord);
|
||||||
|
|
||||||
//Roles
|
//Roles
|
||||||
public RestRole GetRole(ulong id)
|
public SocketRole GetRole(ulong id)
|
||||||
{
|
{
|
||||||
RestRole value;
|
SocketRole value;
|
||||||
if (_roles.TryGetValue(id, out value))
|
if (_roles.TryGetValue(id, out value))
|
||||||
return value;
|
return value;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
public Task<RestRole> CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false)
|
||||||
public async Task<IRole> CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false)
|
=> GuildHelper.CreateRoleAsync(this, Discord, name, permissions, color, isHoisted);
|
||||||
|
internal SocketRole AddRole(RoleModel model)
|
||||||
{
|
{
|
||||||
var role = await GuildHelper.CreateRoleAsync(this, Discord, name, permissions, color, isHoisted);
|
var role = SocketRole.Create(this, Discord.State, model);
|
||||||
_roles = _roles.Add(role.Id, role);
|
_roles[model.Id] = role;
|
||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
internal SocketRole RemoveRole(ulong id)
|
||||||
|
{
|
||||||
|
SocketRole role;
|
||||||
|
if (_roles.TryRemove(id, out role))
|
||||||
|
return role;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//Users
|
//Users
|
||||||
public Task<IReadOnlyCollection<RestGuildUser>> GetUsersAsync()
|
public SocketGuildUser GetUser(ulong id)
|
||||||
=> GuildHelper.GetUsersAsync(this, Discord);
|
{
|
||||||
public Task<RestGuildUser> GetUserAsync(ulong id)
|
SocketGuildUser member;
|
||||||
=> GuildHelper.GetUserAsync(this, Discord, id);
|
if (_members.TryGetValue(id, out member))
|
||||||
public Task<RestGuildUser> GetCurrentUserAsync()
|
return member;
|
||||||
=> GuildHelper.GetUserAsync(this, Discord, Discord.CurrentUser.Id);
|
return null;
|
||||||
|
}
|
||||||
|
public SocketGuildUser GetCurrentUser()
|
||||||
|
{
|
||||||
|
SocketGuildUser member;
|
||||||
|
if (_members.TryGetValue(Discord.CurrentUser.Id, out member))
|
||||||
|
return member;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
public Task<int> PruneUsersAsync(int days = 30, bool simulate = false)
|
public Task<int> PruneUsersAsync(int days = 30, bool simulate = false)
|
||||||
=> GuildHelper.PruneUsersAsync(this, Discord, days, simulate);
|
=> GuildHelper.PruneUsersAsync(this, Discord, days, simulate);
|
||||||
|
|
||||||
|
internal SocketGuildUser AddOrUpdateUser(MemberModel model)
|
||||||
|
{
|
||||||
|
SocketGuildUser member;
|
||||||
|
if (_members.TryGetValue(model.User.Id, out member))
|
||||||
|
member.Update(Discord.State, model);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
member = SocketGuildUser.Create(this, Discord.State, model);
|
||||||
|
_members[member.Id] = member;
|
||||||
|
DownloadedMemberCount++;
|
||||||
|
}
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
internal SocketGuildUser AddOrUpdateUser(PresenceModel model)
|
||||||
|
{
|
||||||
|
SocketGuildUser member;
|
||||||
|
if (_members.TryGetValue(model.User.Id, out member))
|
||||||
|
member.Update(Discord.State, model);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
member = SocketGuildUser.Create(this, Discord.State, model);
|
||||||
|
_members[member.Id] = member;
|
||||||
|
DownloadedMemberCount++;
|
||||||
|
}
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
internal SocketGuildUser RemoveUser(ulong id)
|
||||||
|
{
|
||||||
|
SocketGuildUser member;
|
||||||
|
if (_members.TryRemove(id, out member))
|
||||||
|
{
|
||||||
|
DownloadedMemberCount--;
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
member.GlobalUser.RemoveRef(Discord);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DownloadUsersAsync()
|
||||||
|
{
|
||||||
|
await Discord.DownloadUsersAsync(new[] { this });
|
||||||
|
}
|
||||||
|
internal void CompleteDownloadUsers()
|
||||||
|
{
|
||||||
|
_downloaderPromise.TrySetResultAsync(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Voice States
|
||||||
|
internal SocketVoiceState AddOrUpdateVoiceState(ClientState state, VoiceStateModel model)
|
||||||
|
{
|
||||||
|
var voiceChannel = state.GetChannel(model.ChannelId.Value) as SocketVoiceChannel;
|
||||||
|
var voiceState = SocketVoiceState.Create(voiceChannel, model);
|
||||||
|
_voiceStates[model.UserId] = voiceState;
|
||||||
|
return voiceState;
|
||||||
|
}
|
||||||
|
internal SocketVoiceState? GetVoiceState(ulong id)
|
||||||
|
{
|
||||||
|
SocketVoiceState voiceState;
|
||||||
|
if (_voiceStates.TryGetValue(id, out voiceState))
|
||||||
|
return voiceState;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
internal SocketVoiceState? RemoveVoiceState(ulong id)
|
||||||
|
{
|
||||||
|
SocketVoiceState voiceState;
|
||||||
|
if (_voiceStates.TryRemove(id, out voiceState))
|
||||||
|
return voiceState;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Audio
|
||||||
|
public async Task DisconnectAudioAsync(AudioClient client = null)
|
||||||
|
{
|
||||||
|
await _audioLock.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await DisconnectAudioInternalAsync(client).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_audioLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private async Task DisconnectAudioInternalAsync(AudioClient client = null)
|
||||||
|
{
|
||||||
|
var oldClient = AudioClient;
|
||||||
|
if (oldClient != null)
|
||||||
|
{
|
||||||
|
if (client == null || oldClient == client)
|
||||||
|
{
|
||||||
|
_audioConnectPromise?.TrySetCanceledAsync(); //Cancel any previous audio connection
|
||||||
|
_audioConnectPromise = null;
|
||||||
|
}
|
||||||
|
if (oldClient == client)
|
||||||
|
{
|
||||||
|
AudioClient = null;
|
||||||
|
await oldClient.DisconnectAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal async Task FinishConnectAudio(int id, string url, string token)
|
||||||
|
{
|
||||||
|
var voiceState = GetVoiceState(Discord.CurrentUser.Id).Value;
|
||||||
|
|
||||||
|
await _audioLock.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (AudioClient == null)
|
||||||
|
{
|
||||||
|
var audioClient = new AudioClient(this, id);
|
||||||
|
audioClient.Disconnected += async ex =>
|
||||||
|
{
|
||||||
|
await _audioLock.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (AudioClient == audioClient) //Only reconnect if we're still assigned as this guild's audio client
|
||||||
|
{
|
||||||
|
if (ex != null)
|
||||||
|
{
|
||||||
|
//Reconnect if we still have channel info.
|
||||||
|
//TODO: Is this threadsafe? Could channel data be deleted before we access it?
|
||||||
|
var voiceState2 = GetVoiceState(Discord.CurrentUser.Id);
|
||||||
|
if (voiceState2.HasValue)
|
||||||
|
{
|
||||||
|
var voiceChannelId = voiceState2.Value.VoiceChannel?.Id;
|
||||||
|
if (voiceChannelId != null)
|
||||||
|
await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, voiceChannelId, voiceState2.Value.IsSelfDeafened, voiceState2.Value.IsSelfMuted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try { AudioClient.Dispose(); } catch { }
|
||||||
|
AudioClient = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_audioLock.Release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AudioClient = audioClient;
|
||||||
|
}
|
||||||
|
await AudioClient.ConnectAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false);
|
||||||
|
await _audioConnectPromise.TrySetResultAsync(AudioClient).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
await DisconnectAudioAsync();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await _audioConnectPromise.SetExceptionAsync(e).ConfigureAwait(false);
|
||||||
|
await DisconnectAudioAsync();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_audioLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal async Task FinishJoinAudioChannel()
|
||||||
|
{
|
||||||
|
await _audioLock.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (AudioClient != null)
|
||||||
|
await _audioConnectPromise.TrySetResultAsync(AudioClient).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_audioLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => Name;
|
||||||
|
private string DebuggerDisplay => $"{Name} ({Id})";
|
||||||
|
internal SocketGuild Clone() => MemberwiseClone() as SocketGuild;
|
||||||
|
|
||||||
//IGuild
|
//IGuild
|
||||||
bool IGuild.Available => true;
|
bool IGuild.Available => true;
|
||||||
IAudioClient IGuild.AudioClient => null;
|
IAudioClient IGuild.AudioClient => null;
|
||||||
IReadOnlyCollection<IGuildUser> IGuild.CachedUsers => ImmutableArray.Create<IGuildUser>();
|
|
||||||
IRole IGuild.EveryoneRole => EveryoneRole;
|
IRole IGuild.EveryoneRole => EveryoneRole;
|
||||||
IReadOnlyCollection<IRole> IGuild.Roles => Roles;
|
IReadOnlyCollection<IRole> IGuild.Roles => Roles;
|
||||||
|
|
||||||
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync()
|
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync()
|
||||||
=> await GetBansAsync();
|
=> await GetBansAsync();
|
||||||
|
|
||||||
async Task<IReadOnlyCollection<IGuildChannel>> IGuild.GetChannelsAsync()
|
Task<IReadOnlyCollection<IGuildChannel>> IGuild.GetChannelsAsync(CacheMode mode)
|
||||||
=> await GetChannelsAsync();
|
=> Task.FromResult<IReadOnlyCollection<IGuildChannel>>(Channels);
|
||||||
async Task<IGuildChannel> IGuild.GetChannelAsync(ulong id)
|
Task<IGuildChannel> IGuild.GetChannelAsync(ulong id, CacheMode mode)
|
||||||
=> await GetChannelAsync(id);
|
=> Task.FromResult<IGuildChannel>(GetChannel(id));
|
||||||
IGuildChannel IGuild.GetCachedChannel(ulong id)
|
|
||||||
=> null;
|
|
||||||
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name)
|
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name)
|
||||||
=> await CreateTextChannelAsync(name);
|
=> await CreateTextChannelAsync(name);
|
||||||
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name)
|
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name)
|
||||||
@@ -209,15 +553,15 @@ namespace Discord.WebSocket
|
|||||||
|
|
||||||
IRole IGuild.GetRole(ulong id)
|
IRole IGuild.GetRole(ulong id)
|
||||||
=> GetRole(id);
|
=> GetRole(id);
|
||||||
|
async Task<IRole> IGuild.CreateRoleAsync(string name, GuildPermissions? permissions, Color? color, bool isHoisted)
|
||||||
|
=> await CreateRoleAsync(name, permissions, color, isHoisted);
|
||||||
|
|
||||||
async Task<IReadOnlyCollection<IGuildUser>> IGuild.GetUsersAsync()
|
Task<IReadOnlyCollection<IGuildUser>> IGuild.GetUsersAsync(CacheMode mode)
|
||||||
=> await GetUsersAsync();
|
=> Task.FromResult<IReadOnlyCollection<IGuildUser>>(Users);
|
||||||
async Task<IGuildUser> IGuild.GetUserAsync(ulong id)
|
Task<IGuildUser> IGuild.GetUserAsync(ulong id, CacheMode mode)
|
||||||
=> await GetUserAsync(id);
|
=> Task.FromResult<IGuildUser>(GetUser(id));
|
||||||
IGuildUser IGuild.GetCachedUser(ulong id)
|
Task<IGuildUser> IGuild.GetCurrentUserAsync(CacheMode mode)
|
||||||
=> null;
|
=> Task.FromResult<IGuildUser>(GetCurrentUser());
|
||||||
async Task<IGuildUser> IGuild.GetCurrentUserAsync()
|
|
||||||
=> await GetCurrentUserAsync();
|
|
||||||
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }
|
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
using Discord.Rest;
|
using System;
|
||||||
using Discord.WebSocket;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
@@ -51,7 +48,7 @@ namespace Discord.WebSocket
|
|||||||
return result;
|
return result;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public IImmutableList<SocketMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
public IReadOnlyCollection<SocketMessage> GetMany(ulong? fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||||
{
|
{
|
||||||
if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit));
|
if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit));
|
||||||
if (limit == 0) return ImmutableArray<SocketMessage>.Empty;
|
if (limit == 0) return ImmutableArray<SocketMessage>.Empty;
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Model = Discord.API.Message;
|
using Model = Discord.API.Message;
|
||||||
|
|
||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
public abstract class SocketMessage : SocketEntity<ulong>, IMessage, IUpdateable
|
public abstract class SocketMessage : SocketEntity<ulong>, IMessage
|
||||||
{
|
{
|
||||||
private long _timestampTicks;
|
private long _timestampTicks;
|
||||||
|
|
||||||
@@ -35,14 +34,14 @@ namespace Discord.WebSocket
|
|||||||
ChannelId = channelId;
|
ChannelId = channelId;
|
||||||
Author = author;
|
Author = author;
|
||||||
}
|
}
|
||||||
internal static SocketMessage Create(DiscordSocketClient discord, SocketUser author, Model model)
|
internal static SocketMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, Model model)
|
||||||
{
|
{
|
||||||
if (model.Type == MessageType.Default)
|
if (model.Type == MessageType.Default)
|
||||||
return SocketUserMessage.Create(discord, author, model);
|
return SocketUserMessage.Create(discord, state, author, model);
|
||||||
else
|
else
|
||||||
return SocketSystemMessage.Create(discord, author, model);
|
return SocketSystemMessage.Create(discord, state, author, model);
|
||||||
}
|
}
|
||||||
internal virtual void Update(Model model)
|
internal virtual void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
if (model.Timestamp.IsSpecified)
|
if (model.Timestamp.IsSpecified)
|
||||||
_timestampTicks = model.Timestamp.Value.UtcTicks;
|
_timestampTicks = model.Timestamp.Value.UtcTicks;
|
||||||
@@ -50,12 +49,8 @@ namespace Discord.WebSocket
|
|||||||
if (model.Content.IsSpecified)
|
if (model.Content.IsSpecified)
|
||||||
Content = model.Content.Value;
|
Content = model.Content.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync()
|
internal SocketMessage Clone() => MemberwiseClone() as SocketMessage;
|
||||||
{
|
|
||||||
var model = await Discord.ApiClient.GetChannelMessageAsync(ChannelId, Id).ConfigureAwait(false);
|
|
||||||
Update(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
//IMessage
|
//IMessage
|
||||||
IUser IMessage.Author => Author;
|
IUser IMessage.Author => Author;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Discord.Rest;
|
using Model = Discord.API.Message;
|
||||||
using Model = Discord.API.Message;
|
|
||||||
|
|
||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
@@ -11,17 +10,21 @@ namespace Discord.WebSocket
|
|||||||
: base(discord, id, channelId, author)
|
: base(discord, id, channelId, author)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
internal new static SocketSystemMessage Create(DiscordSocketClient discord, SocketUser author, Model model)
|
internal new static SocketSystemMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketSystemMessage(discord, model.Id, model.ChannelId, author);
|
var entity = new SocketSystemMessage(discord, model.Id, model.ChannelId, author);
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
internal override void Update(Model model)
|
internal override void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
base.Update(model);
|
base.Update(state, model);
|
||||||
|
|
||||||
Type = model.Type;
|
Type = model.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString() => Content;
|
||||||
|
private string DebuggerDisplay => $"{Author}: {Content} ({Id}, {Type})";
|
||||||
|
internal new SocketSystemMessage Clone() => MemberwiseClone() as SocketSystemMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using Model = Discord.API.Message;
|
|||||||
|
|
||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
internal class SocketUserMessage : SocketMessage, IUserMessage
|
public class SocketUserMessage : SocketMessage, IUserMessage
|
||||||
{
|
{
|
||||||
private bool _isMentioningEveryone, _isTTS, _isPinned;
|
private bool _isMentioningEveryone, _isTTS, _isPinned;
|
||||||
private long? _editedTimestampTicks;
|
private long? _editedTimestampTicks;
|
||||||
@@ -32,16 +32,16 @@ namespace Discord.WebSocket
|
|||||||
: base(discord, id, channelId, author)
|
: base(discord, id, channelId, author)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
internal new static SocketUserMessage Create(DiscordSocketClient discord, SocketUser author, Model model)
|
internal new static SocketUserMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketUserMessage(discord, model.Id, model.ChannelId, author);
|
var entity = new SocketUserMessage(discord, model.Id, model.ChannelId, author);
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Update(Model model)
|
internal override void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
base.Update(model);
|
base.Update(state, model);
|
||||||
|
|
||||||
if (model.IsTextToSpeech.IsSpecified)
|
if (model.IsTextToSpeech.IsSpecified)
|
||||||
_isTTS = model.IsTextToSpeech.Value;
|
_isTTS = model.IsTextToSpeech.Value;
|
||||||
@@ -129,5 +129,9 @@ namespace Discord.WebSocket
|
|||||||
text = MentionsHelper.ResolveEveryoneMentions(text, everyoneHandling);
|
text = MentionsHelper.ResolveEveryoneMentions(text, everyoneHandling);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString() => Content;
|
||||||
|
private string DebuggerDisplay => $"{Author}: {Content} ({Id}{(Attachments.Count > 0 ? $", {Attachments.Count} Attachments" : "")}";
|
||||||
|
internal new SocketUserMessage Clone() => MemberwiseClone() as SocketUserMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs
Normal file
58
src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using Discord.API.Rest;
|
||||||
|
using Discord.Rest;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Model = Discord.API.Role;
|
||||||
|
|
||||||
|
namespace Discord.WebSocket
|
||||||
|
{
|
||||||
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||||
|
public class SocketRole : SocketEntity<ulong>, IRole
|
||||||
|
{
|
||||||
|
public SocketGuild Guild { get; }
|
||||||
|
|
||||||
|
public Color Color { get; private set; }
|
||||||
|
public bool IsHoisted { get; private set; }
|
||||||
|
public bool IsManaged { get; private set; }
|
||||||
|
public string Name { get; private set; }
|
||||||
|
public GuildPermissions Permissions { get; private set; }
|
||||||
|
public int Position { get; private set; }
|
||||||
|
|
||||||
|
public bool IsEveryone => Id == Guild.Id;
|
||||||
|
public string Mention => MentionUtils.MentionRole(Id);
|
||||||
|
|
||||||
|
internal SocketRole(SocketGuild guild, ulong id)
|
||||||
|
: base(guild.Discord, id)
|
||||||
|
{
|
||||||
|
Guild = guild;
|
||||||
|
}
|
||||||
|
internal static SocketRole Create(SocketGuild guild, ClientState state, Model model)
|
||||||
|
{
|
||||||
|
var entity = new SocketRole(guild, model.Id);
|
||||||
|
entity.Update(state, model);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
internal void Update(ClientState state, Model model)
|
||||||
|
{
|
||||||
|
Name = model.Name;
|
||||||
|
IsHoisted = model.Hoist;
|
||||||
|
IsManaged = model.Managed;
|
||||||
|
Position = model.Position;
|
||||||
|
Color = new Color(model.Color);
|
||||||
|
Permissions = new GuildPermissions(model.Permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ModifyAsync(Action<ModifyGuildRoleParams> func)
|
||||||
|
=> RoleHelper.ModifyAsync(this, Discord, func);
|
||||||
|
public Task DeleteAsync()
|
||||||
|
=> RoleHelper.DeleteAsync(this, Discord);
|
||||||
|
|
||||||
|
public override string ToString() => Name;
|
||||||
|
private string DebuggerDisplay => $"{Name} ({Id})";
|
||||||
|
internal SocketRole Clone() => MemberwiseClone() as SocketRole;
|
||||||
|
|
||||||
|
//IRole
|
||||||
|
IGuild IRole.Guild => Guild;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,56 @@
|
|||||||
namespace Discord.WebSocket
|
using Model = Discord.API.User;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
internal class SocketGlobalUser : SocketUser
|
internal class SocketGlobalUser : SocketUser
|
||||||
{
|
{
|
||||||
internal SocketGlobalUser(DiscordSocketClient discord, ulong id)
|
public override bool IsBot { get; internal set; }
|
||||||
|
public override string Username { get; internal set; }
|
||||||
|
public override ushort DiscriminatorValue { get; internal set; }
|
||||||
|
public override string AvatarId { get; internal set; }
|
||||||
|
public SocketDMChannel DMChannel { get; internal set; }
|
||||||
|
|
||||||
|
internal override SocketGlobalUser GlobalUser => this;
|
||||||
|
internal override SocketPresence Presence { get; set; }
|
||||||
|
|
||||||
|
private readonly object _lockObj = new object();
|
||||||
|
private ushort _references;
|
||||||
|
|
||||||
|
private SocketGlobalUser(DiscordSocketClient discord, ulong id)
|
||||||
: base(discord, id)
|
: base(discord, id)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
internal static SocketGlobalUser Create(DiscordSocketClient discord, ClientState state, Model model)
|
||||||
|
{
|
||||||
|
var entity = new SocketGlobalUser(discord, model.Id);
|
||||||
|
entity.Update(state, model);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AddRef()
|
||||||
|
{
|
||||||
|
checked
|
||||||
|
{
|
||||||
|
lock (_lockObj)
|
||||||
|
_references++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal void RemoveRef(DiscordSocketClient discord)
|
||||||
|
{
|
||||||
|
lock (_lockObj)
|
||||||
|
{
|
||||||
|
if (--_references <= 0)
|
||||||
|
discord.RemoveUser(Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal new SocketGlobalUser Clone() => MemberwiseClone() as SocketGlobalUser;
|
||||||
|
|
||||||
|
//Updates are only ever called from the gateway thread, thus threadsafe
|
||||||
|
internal override void Update(ClientState state, Model model)
|
||||||
|
{
|
||||||
|
base.Update(state, model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Discord.Rest;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics;
|
|
||||||
using Model = Discord.API.User;
|
using Model = Discord.API.User;
|
||||||
|
|
||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
@@ -7,17 +6,30 @@ namespace Discord.WebSocket
|
|||||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||||
public class SocketGroupUser : SocketUser, IGroupUser
|
public class SocketGroupUser : SocketUser, IGroupUser
|
||||||
{
|
{
|
||||||
internal SocketGroupUser(DiscordSocketClient discord, ulong id)
|
public SocketGroupChannel Channel { get; }
|
||||||
: base(discord, id)
|
internal override SocketGlobalUser GlobalUser { get; }
|
||||||
|
|
||||||
|
public override bool IsBot { get { return GlobalUser.IsBot; } internal set { GlobalUser.IsBot = value; } }
|
||||||
|
public override string Username { get { return GlobalUser.Username; } internal set { GlobalUser.Username = value; } }
|
||||||
|
public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } }
|
||||||
|
public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } }
|
||||||
|
internal override SocketPresence Presence { get { return GlobalUser.Presence; } set { GlobalUser.Presence = value; } }
|
||||||
|
|
||||||
|
internal SocketGroupUser(SocketGroupChannel channel, SocketGlobalUser globalUser)
|
||||||
|
: base(channel.Discord, globalUser.Id)
|
||||||
{
|
{
|
||||||
|
Channel = channel;
|
||||||
|
GlobalUser = globalUser;
|
||||||
}
|
}
|
||||||
internal new static SocketGroupUser Create(DiscordSocketClient discord, Model model)
|
internal static SocketGroupUser Create(SocketGroupChannel channel, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketGroupUser(discord, model.Id);
|
var entity = new SocketGroupUser(channel, channel.Discord.GetOrCreateUser(state, model));
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal new SocketGroupUser Clone() => MemberwiseClone() as SocketGroupUser;
|
||||||
|
|
||||||
//IVoiceState
|
//IVoiceState
|
||||||
bool IVoiceState.IsDeafened => false;
|
bool IVoiceState.IsDeafened => false;
|
||||||
bool IVoiceState.IsMuted => false;
|
bool IVoiceState.IsMuted => false;
|
||||||
|
|||||||
@@ -9,46 +9,76 @@ using PresenceModel = Discord.API.Presence;
|
|||||||
|
|
||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
internal class SocketGuildUser : SocketUser, IGuildUser
|
public class SocketGuildUser : SocketUser, IGuildUser
|
||||||
{
|
{
|
||||||
private long? _joinedAtTicks;
|
private long? _joinedAtTicks;
|
||||||
private ImmutableArray<ulong> _roleIds;
|
private ImmutableArray<ulong> _roleIds;
|
||||||
|
|
||||||
|
internal override SocketGlobalUser GlobalUser { get; }
|
||||||
|
public SocketGuild Guild { get; }
|
||||||
public string Nickname { get; private set; }
|
public string Nickname { get; private set; }
|
||||||
public ulong GuildId { get; private set; }
|
|
||||||
|
|
||||||
|
public override bool IsBot { get { return GlobalUser.IsBot; } internal set { GlobalUser.IsBot = value; } }
|
||||||
|
public override string Username { get { return GlobalUser.Username; } internal set { GlobalUser.Username = value; } }
|
||||||
|
public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } }
|
||||||
|
public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } }
|
||||||
|
internal override SocketPresence Presence { get { return GlobalUser.Presence; } set { GlobalUser.Presence = value; } }
|
||||||
public IReadOnlyCollection<ulong> RoleIds => _roleIds;
|
public IReadOnlyCollection<ulong> RoleIds => _roleIds;
|
||||||
|
|
||||||
|
public SocketVoiceState? VoiceState => Guild.GetVoiceState(Id);
|
||||||
|
public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false;
|
||||||
|
public bool IsSelfMuted => VoiceState?.IsSelfMuted ?? false;
|
||||||
|
public bool IsSuppressed => VoiceState?.IsSuppressed ?? false;
|
||||||
|
public SocketVoiceChannel VoiceChannel => VoiceState?.VoiceChannel;
|
||||||
|
public bool IsDeafened => VoiceState?.IsDeafened ?? false;
|
||||||
|
public bool IsMuted => VoiceState?.IsMuted ?? false;
|
||||||
|
public string VoiceSessionId => VoiceState?.VoiceSessionId ?? "";
|
||||||
|
|
||||||
public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks);
|
public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks);
|
||||||
|
|
||||||
internal SocketGuildUser(DiscordSocketClient discord, ulong id)
|
internal SocketGuildUser(SocketGuild guild, SocketGlobalUser globalUser)
|
||||||
: base(discord, id)
|
: base(guild.Discord, globalUser.Id)
|
||||||
{
|
{
|
||||||
|
Guild = guild;
|
||||||
|
GlobalUser = globalUser;
|
||||||
}
|
}
|
||||||
internal static SocketGuildUser Create(DiscordSocketClient discord, Model model)
|
internal static SocketGuildUser Create(SocketGuild guild, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketGuildUser(discord, model.User.Id);
|
var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User));
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
internal void Update(Model model)
|
internal static SocketGuildUser Create(SocketGuild guild, ClientState state, PresenceModel model)
|
||||||
{
|
{
|
||||||
|
var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User));
|
||||||
|
entity.Update(state, model);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
internal void Update(ClientState state, Model model)
|
||||||
|
{
|
||||||
|
base.Update(state, model.User);
|
||||||
_joinedAtTicks = model.JoinedAt.UtcTicks;
|
_joinedAtTicks = model.JoinedAt.UtcTicks;
|
||||||
if (model.Nick.IsSpecified)
|
if (model.Nick.IsSpecified)
|
||||||
Nickname = model.Nick.Value;
|
Nickname = model.Nick.Value;
|
||||||
UpdateRoles(model.Roles);
|
UpdateRoles(model.Roles);
|
||||||
}
|
}
|
||||||
|
internal override void Update(ClientState state, PresenceModel model)
|
||||||
|
{
|
||||||
|
base.Update(state, model);
|
||||||
|
if (model.Roles.IsSpecified)
|
||||||
|
UpdateRoles(model.Roles.Value);
|
||||||
|
if (model.Nick.IsSpecified)
|
||||||
|
Nickname = model.Nick.Value;
|
||||||
|
}
|
||||||
private void UpdateRoles(ulong[] roleIds)
|
private void UpdateRoles(ulong[] roleIds)
|
||||||
{
|
{
|
||||||
var roles = ImmutableArray.CreateBuilder<ulong>(roleIds.Length + 1);
|
var roles = ImmutableArray.CreateBuilder<ulong>(roleIds.Length + 1);
|
||||||
roles.Add(GuildId);
|
roles.Add(Guild.Id);
|
||||||
for (int i = 0; i < roleIds.Length; i++)
|
for (int i = 0; i < roleIds.Length; i++)
|
||||||
roles.Add(roleIds[i]);
|
roles.Add(roleIds[i]);
|
||||||
_roleIds = roles.ToImmutable();
|
_roleIds = roles.ToImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task UpdateAsync()
|
|
||||||
=> Update(await UserHelper.GetAsync(this, Discord));
|
|
||||||
public Task ModifyAsync(Action<ModifyGuildMemberParams> func)
|
public Task ModifyAsync(Action<ModifyGuildMemberParams> func)
|
||||||
=> UserHelper.ModifyAsync(this, Discord, func);
|
=> UserHelper.ModifyAsync(this, Discord, func);
|
||||||
public Task KickAsync()
|
public Task KickAsync()
|
||||||
@@ -59,16 +89,17 @@ namespace Discord.WebSocket
|
|||||||
throw new NotImplementedException(); //TODO: Impl
|
throw new NotImplementedException(); //TODO: Impl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal new SocketGuildUser Clone() => MemberwiseClone() as SocketGuildUser;
|
||||||
|
|
||||||
//IGuildUser
|
//IGuildUser
|
||||||
|
ulong IGuildUser.GuildId => Guild.Id;
|
||||||
IReadOnlyCollection<ulong> IGuildUser.RoleIds => RoleIds;
|
IReadOnlyCollection<ulong> IGuildUser.RoleIds => RoleIds;
|
||||||
|
|
||||||
|
//IUser
|
||||||
|
Task<IDMChannel> IUser.GetDMChannelAsync(CacheMode mode)
|
||||||
|
=> Task.FromResult<IDMChannel>(GlobalUser.DMChannel);
|
||||||
|
|
||||||
//IVoiceState
|
//IVoiceState
|
||||||
bool IVoiceState.IsDeafened => false;
|
IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel;
|
||||||
bool IVoiceState.IsMuted => false;
|
|
||||||
bool IVoiceState.IsSelfDeafened => false;
|
|
||||||
bool IVoiceState.IsSelfMuted => false;
|
|
||||||
bool IVoiceState.IsSuppressed => false;
|
|
||||||
IVoiceChannel IVoiceState.VoiceChannel => null;
|
|
||||||
string IVoiceState.VoiceSessionId => null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
//TODO: C#7 Candidate for record type
|
//TODO: C#7 Candidate for record type
|
||||||
internal struct SocketPresence : IPresence
|
public struct SocketPresence : IPresence
|
||||||
{
|
{
|
||||||
public Game? Game { get; }
|
public Game? Game { get; }
|
||||||
public UserStatus Status { get; }
|
public UserStatus Status { get; }
|
||||||
@@ -13,11 +13,11 @@ namespace Discord.WebSocket
|
|||||||
Game = game;
|
Game = game;
|
||||||
Status = status;
|
Status = status;
|
||||||
}
|
}
|
||||||
internal SocketPresence Create(Model model)
|
internal static SocketPresence Create(Model model)
|
||||||
{
|
{
|
||||||
return new SocketPresence(model.Game != null ? Discord.Game.Create(model.Game) : (Game?)null, model.Status);
|
return new SocketPresence(model.Game != null ? Discord.Game.Create(model.Game) : (Game?)null, model.Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocketPresence Clone() => this;
|
internal SocketPresence Clone() => this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,21 +11,28 @@ namespace Discord.WebSocket
|
|||||||
public string Email { get; private set; }
|
public string Email { get; private set; }
|
||||||
public bool IsVerified { get; private set; }
|
public bool IsVerified { get; private set; }
|
||||||
public bool IsMfaEnabled { get; private set; }
|
public bool IsMfaEnabled { get; private set; }
|
||||||
|
internal override SocketGlobalUser GlobalUser { get; }
|
||||||
|
|
||||||
internal SocketSelfUser(DiscordSocketClient discord, ulong id)
|
public override bool IsBot { get { return GlobalUser.IsBot; } internal set { GlobalUser.IsBot = value; } }
|
||||||
: base(discord, id)
|
public override string Username { get { return GlobalUser.Username; } internal set { GlobalUser.Username = value; } }
|
||||||
|
public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } }
|
||||||
|
public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } }
|
||||||
|
internal override SocketPresence Presence { get { return GlobalUser.Presence; } set { GlobalUser.Presence = value; } }
|
||||||
|
|
||||||
|
internal SocketSelfUser(DiscordSocketClient discord, SocketGlobalUser globalUser)
|
||||||
|
: base(discord, globalUser.Id)
|
||||||
{
|
{
|
||||||
Status = UserStatus.Online;
|
GlobalUser = globalUser;
|
||||||
}
|
}
|
||||||
internal new static SocketSelfUser Create(DiscordSocketClient discord, Model model)
|
internal static SocketSelfUser Create(DiscordSocketClient discord, ClientState state, Model model)
|
||||||
{
|
{
|
||||||
var entity = new SocketSelfUser(discord, model.Id);
|
var entity = new SocketSelfUser(discord, discord.GetOrCreateSelfUser(state, model));
|
||||||
entity.Update(model);
|
entity.Update(state, model);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
internal override void Update(Model model)
|
internal override void Update(ClientState state, Model model)
|
||||||
{
|
{
|
||||||
base.Update(model);
|
base.Update(state, model);
|
||||||
|
|
||||||
if (model.Email.IsSpecified)
|
if (model.Email.IsSpecified)
|
||||||
Email = model.Email.Value;
|
Email = model.Email.Value;
|
||||||
@@ -34,12 +41,13 @@ namespace Discord.WebSocket
|
|||||||
if (model.MfaEnabled.IsSpecified)
|
if (model.MfaEnabled.IsSpecified)
|
||||||
IsMfaEnabled = model.MfaEnabled.Value;
|
IsMfaEnabled = model.MfaEnabled.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task UpdateAsync()
|
|
||||||
=> Update(await UserHelper.GetAsync(this, Discord));
|
|
||||||
public Task ModifyAsync(Action<ModifyCurrentUserParams> func)
|
public Task ModifyAsync(Action<ModifyCurrentUserParams> func)
|
||||||
=> UserHelper.ModifyAsync(this, Discord, func);
|
=> UserHelper.ModifyAsync(this, Discord, func);
|
||||||
|
|
||||||
|
internal new SocketSelfUser Clone() => MemberwiseClone() as SocketSelfUser;
|
||||||
|
|
||||||
|
//ISelfUser
|
||||||
Task ISelfUser.ModifyStatusAsync(Action<ModifyPresenceParams> func) { throw new NotSupportedException(); }
|
Task ISelfUser.ModifyStatusAsync(Action<ModifyPresenceParams> func) { throw new NotSupportedException(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
src/Discord.Net.WebSocket/Entities/Users/SocketSimpleUser.cs
Normal file
34
src/Discord.Net.WebSocket/Entities/Users/SocketSimpleUser.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using Model = Discord.API.User;
|
||||||
|
using PresenceModel = Discord.API.Presence;
|
||||||
|
|
||||||
|
namespace Discord.WebSocket
|
||||||
|
{
|
||||||
|
public class SocketSimpleUser : SocketUser
|
||||||
|
{
|
||||||
|
public override bool IsBot { get; internal set; }
|
||||||
|
public override string Username { get; internal set; }
|
||||||
|
public override ushort DiscriminatorValue { get; internal set; }
|
||||||
|
public override string AvatarId { get; internal set; }
|
||||||
|
internal override SocketPresence Presence { get { return new SocketPresence(null, UserStatus.Offline); } set { } }
|
||||||
|
|
||||||
|
internal override SocketGlobalUser GlobalUser { get { throw new NotSupportedException(); } }
|
||||||
|
|
||||||
|
internal SocketSimpleUser(DiscordSocketClient discord, ulong id)
|
||||||
|
: base(discord, id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
internal static SocketSimpleUser Create(DiscordSocketClient discord, ClientState state, Model model)
|
||||||
|
{
|
||||||
|
var entity = new SocketSimpleUser(discord, model.Id);
|
||||||
|
entity.Update(state, model);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void Update(ClientState state, PresenceModel model)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal new SocketSimpleUser Clone() => MemberwiseClone() as SocketSimpleUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +1,30 @@
|
|||||||
using Discord.Rest;
|
using Discord.Rest;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Model = Discord.API.User;
|
using Model = Discord.API.User;
|
||||||
|
using PresenceModel = Discord.API.Presence;
|
||||||
|
|
||||||
namespace Discord.WebSocket
|
namespace Discord.WebSocket
|
||||||
{
|
{
|
||||||
public class SocketUser : SocketEntity<ulong>, IUser
|
public abstract class SocketUser : SocketEntity<ulong>, IUser
|
||||||
{
|
{
|
||||||
public bool IsBot { get; private set; }
|
public abstract bool IsBot { get; internal set; }
|
||||||
public string Username { get; private set; }
|
public abstract string Username { get; internal set; }
|
||||||
public ushort DiscriminatorValue { get; private set; }
|
public abstract ushort DiscriminatorValue { get; internal set; }
|
||||||
public string AvatarId { get; private set; }
|
public abstract string AvatarId { get; internal set; }
|
||||||
|
internal abstract SocketGlobalUser GlobalUser { get; }
|
||||||
|
internal abstract SocketPresence Presence { get; set; }
|
||||||
|
|
||||||
public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, AvatarId);
|
public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, AvatarId);
|
||||||
public string Discriminator => DiscriminatorValue.ToString("D4");
|
public string Discriminator => DiscriminatorValue.ToString("D4");
|
||||||
public string Mention => MentionUtils.MentionUser(Id);
|
public string Mention => MentionUtils.MentionUser(Id);
|
||||||
public virtual Game? Game => null;
|
public Game? Game => Presence.Game;
|
||||||
public virtual UserStatus Status { get; internal set; }
|
public UserStatus Status => Presence.Status;
|
||||||
|
|
||||||
internal SocketUser(DiscordSocketClient discord, ulong id)
|
internal SocketUser(DiscordSocketClient discord, ulong id)
|
||||||
: base(discord, id)
|
: base(discord, id)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
internal static SocketUser Create(DiscordSocketClient discord, Model model)
|
internal virtual void Update(ClientState state, Model model)
|
||||||
{
|
|
||||||
var entity = new SocketUser(discord, model.Id);
|
|
||||||
entity.Update(model);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
internal virtual void Update(Model model)
|
|
||||||
{
|
{
|
||||||
if (model.Avatar.IsSpecified)
|
if (model.Avatar.IsSpecified)
|
||||||
AvatarId = model.Avatar.Value;
|
AvatarId = model.Avatar.Value;
|
||||||
@@ -38,13 +35,22 @@ namespace Discord.WebSocket
|
|||||||
if (model.Username.IsSpecified)
|
if (model.Username.IsSpecified)
|
||||||
Username = model.Username.Value;
|
Username = model.Username.Value;
|
||||||
}
|
}
|
||||||
|
internal virtual void Update(ClientState state, PresenceModel model)
|
||||||
|
{
|
||||||
|
Presence = SocketPresence.Create(model);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual async Task UpdateAsync()
|
public Task<RestDMChannel> CreateDMChannelAsync()
|
||||||
=> Update(await UserHelper.GetAsync(this, Discord));
|
|
||||||
|
|
||||||
public Task<IDMChannel> CreateDMChannelAsync()
|
|
||||||
=> UserHelper.CreateDMChannelAsync(this, Discord);
|
=> UserHelper.CreateDMChannelAsync(this, Discord);
|
||||||
|
|
||||||
IDMChannel IUser.GetCachedDMChannel() => null;
|
public override string ToString() => $"{Username}#{Discriminator}";
|
||||||
|
private string DebuggerDisplay => $"{Username}#{Discriminator} (Id{(IsBot ? ", Bot" : "")})";
|
||||||
|
internal SocketUser Clone() => MemberwiseClone() as SocketUser;
|
||||||
|
|
||||||
|
//IUser
|
||||||
|
Task<IDMChannel> IUser.GetDMChannelAsync(CacheMode mode)
|
||||||
|
=> Task.FromResult<IDMChannel>(GlobalUser.DMChannel);
|
||||||
|
async Task<IDMChannel> IUser.CreateDMChannelAsync()
|
||||||
|
=> await CreateDMChannelAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace Discord.WebSocket
|
|||||||
return new SocketVoiceState(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Suppress);
|
return new SocketVoiceState(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Suppress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocketVoiceState Clone() => this;
|
internal SocketVoiceState Clone() => this;
|
||||||
|
|
||||||
IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel;
|
IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user