Added remaining gateway events, added IAudioChannel, added CacheModes
This commit is contained in:
@@ -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}")]
|
||||
public abstract class SocketChannel : SocketEntity<ulong>, IChannel
|
||||
{
|
||||
public IReadOnlyCollection<SocketUser> Users => GetUsersInternal();
|
||||
|
||||
internal SocketChannel(DiscordSocketClient discord, ulong 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)
|
||||
{
|
||||
case ChannelType.Text:
|
||||
return SocketTextChannel.Create(discord, model);
|
||||
case ChannelType.Voice:
|
||||
return SocketVoiceChannel.Create(discord, model);
|
||||
case ChannelType.DM:
|
||||
return SocketDMChannel.Create(discord, model);
|
||||
return SocketDMChannel.Create(discord, state, model);
|
||||
case ChannelType.Group:
|
||||
return SocketGroupChannel.Create(discord, model);
|
||||
return SocketGroupChannel.Create(discord, state, model);
|
||||
default:
|
||||
throw new InvalidOperationException($"Unexpected channel type: {model.Type}");
|
||||
}
|
||||
}
|
||||
internal abstract void Update(ClientState state, Model model);
|
||||
|
||||
//IChannel
|
||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => ImmutableArray.Create<IUser>();
|
||||
//User
|
||||
public SocketUser GetUser(ulong id) => GetUserInternal(id);
|
||||
internal abstract SocketUser GetUserInternal(ulong id);
|
||||
internal abstract IReadOnlyCollection<SocketUser> GetUsersInternal();
|
||||
|
||||
IUser IChannel.GetCachedUser(ulong id)
|
||||
=> null;
|
||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
||||
=> Task.FromResult<IUser>(null);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable();
|
||||
internal SocketChannel Clone() => MemberwiseClone() as SocketChannel;
|
||||
|
||||
//IChannel
|
||||
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IUser>(null); //Overridden
|
||||
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
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class SocketDMChannel : SocketChannel, IDMChannel
|
||||
public class SocketDMChannel : SocketChannel, IDMChannel, ISocketPrivateChannel, ISocketMessageChannel
|
||||
{
|
||||
private readonly MessageCache _messages;
|
||||
|
||||
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)
|
||||
{
|
||||
Recipient = new SocketUser(Discord, recipientId);
|
||||
Recipient = recipient;
|
||||
if (Discord.MessageCacheSize > 0)
|
||||
_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);
|
||||
entity.Update(model);
|
||||
var entity = new SocketDMChannel(discord, model.Id, discord.GetOrCreateUser(state, model.Recipients.Value[0]));
|
||||
entity.Update(state, model);
|
||||
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()
|
||||
=> ChannelHelper.DeleteAsync(this, Discord);
|
||||
|
||||
public 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 SocketMessage GetMessage(ulong id)
|
||||
//Messages
|
||||
public SocketMessage GetCachedMessage(ulong 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);
|
||||
if (msg == null && allowDownload)
|
||||
if (msg == null)
|
||||
msg = await ChannelHelper.GetMessageAsync(this, Discord, id);
|
||||
return msg;
|
||||
}
|
||||
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> ChannelHelper.GetMessagesAsync(this, Discord, limit: limit);
|
||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> 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<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, 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()
|
||||
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
||||
|
||||
@@ -82,38 +74,61 @@ namespace Discord.WebSocket
|
||||
public IDisposable EnterTypingState()
|
||||
=> ChannelHelper.EnterTypingState(this, Discord);
|
||||
|
||||
internal SocketMessage AddMessage(SocketUser author, MessageModel model)
|
||||
{
|
||||
var msg = SocketMessage.Create(Discord, author, model);
|
||||
_messages.Add(msg);
|
||||
return msg;
|
||||
}
|
||||
internal void AddMessage(SocketMessage msg)
|
||||
=> _messages.Add(msg);
|
||||
internal SocketMessage RemoveMessage(ulong id)
|
||||
{
|
||||
return _messages.Remove(id);
|
||||
}
|
||||
=> _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}";
|
||||
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
|
||||
IUser IDMChannel.Recipient => Recipient;
|
||||
|
||||
//ISocketPrivateChannel
|
||||
IReadOnlyCollection<SocketUser> ISocketPrivateChannel.Recipients => ImmutableArray.Create(Recipient);
|
||||
|
||||
//IPrivateChannel
|
||||
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => ImmutableArray.Create<IUser>(Recipient);
|
||||
|
||||
//IMessageChannel
|
||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages => ImmutableArray.Create<IMessage>();
|
||||
IMessage IMessageChannel.GetCachedMessage(ulong id) => null;
|
||||
|
||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
||||
=> await GetMessageAsync(id);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
||||
=> GetMessagesAsync(limit);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||
{
|
||||
if (mode == CacheMode.AllowDownload)
|
||||
return await GetMessageAsync(id);
|
||||
else
|
||||
return GetCachedMessage(id);
|
||||
}
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||
{
|
||||
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()
|
||||
=> await GetPinnedMessagesAsync().ConfigureAwait(false);
|
||||
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS)
|
||||
@@ -125,14 +140,10 @@ namespace Discord.WebSocket
|
||||
IDisposable IMessageChannel.EnterTypingState()
|
||||
=> EnterTypingState();
|
||||
|
||||
//IChannel
|
||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => Users;
|
||||
|
||||
IUser IChannel.GetCachedUser(ulong id)
|
||||
=> GetUser(id);
|
||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
||||
//IChannel
|
||||
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IUser>(GetUser(id));
|
||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>().ToAsyncEnumerable();
|
||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MessageModel = Discord.API.Message;
|
||||
using Model = Discord.API.Channel;
|
||||
using UserModel = Discord.API.User;
|
||||
using VoiceStateModel = Discord.API.VoiceState;
|
||||
@@ -15,7 +14,7 @@ using VoiceStateModel = Discord.API.VoiceState;
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class SocketGroupChannel : SocketChannel, IGroupChannel
|
||||
public class SocketGroupChannel : SocketChannel, IGroupChannel, ISocketPrivateChannel, ISocketMessageChannel, ISocketAudioChannel
|
||||
{
|
||||
private readonly MessageCache _messages;
|
||||
|
||||
@@ -25,7 +24,8 @@ namespace Discord.WebSocket
|
||||
|
||||
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
|
||||
=> _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);
|
||||
_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);
|
||||
entity.Update(model);
|
||||
entity.Update(state, model);
|
||||
return entity;
|
||||
}
|
||||
internal void Update(Model model)
|
||||
internal override void Update(ClientState state, Model model)
|
||||
{
|
||||
if (model.Name.IsSpecified)
|
||||
Name = model.Name.Value;
|
||||
@@ -51,37 +51,35 @@ namespace Discord.WebSocket
|
||||
_iconId = model.Icon.Value;
|
||||
|
||||
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));
|
||||
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;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync()
|
||||
=> Update(await ChannelHelper.GetAsync(this, Discord));
|
||||
|
||||
public Task LeaveAsync()
|
||||
=> 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;
|
||||
if (_users.TryGetValue(id, out user))
|
||||
return user;
|
||||
return null;
|
||||
IMessage msg = _messages?.Get(id);
|
||||
if (msg == null)
|
||||
msg = await ChannelHelper.GetMessageAsync(this, Discord, id);
|
||||
return msg;
|
||||
}
|
||||
|
||||
public Task<RestMessage> GetMessageAsync(ulong id)
|
||||
=> ChannelHelper.GetMessageAsync(this, Discord, id);
|
||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> ChannelHelper.GetMessagesAsync(this, Discord, limit: limit);
|
||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> 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<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, 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()
|
||||
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
||||
|
||||
@@ -98,20 +96,101 @@ namespace Discord.WebSocket
|
||||
public IDisposable EnterTypingState()
|
||||
=> 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
|
||||
IReadOnlyCollection<IUser> IPrivateChannel.Recipients => Recipients;
|
||||
|
||||
//IMessageChannel
|
||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages => ImmutableArray.Create<IMessage>();
|
||||
|
||||
IMessage IMessageChannel.GetCachedMessage(ulong id)
|
||||
=> null;
|
||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
||||
=> await GetMessageAsync(id);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
||||
=> GetMessagesAsync(limit);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||
{
|
||||
if (mode == CacheMode.AllowDownload)
|
||||
return await GetMessageAsync(id);
|
||||
else
|
||||
return GetCachedMessage(id);
|
||||
}
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||
{
|
||||
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()
|
||||
=> await GetPinnedMessagesAsync();
|
||||
|
||||
@@ -124,14 +203,10 @@ namespace Discord.WebSocket
|
||||
IDisposable IMessageChannel.EnterTypingState()
|
||||
=> EnterTypingState();
|
||||
|
||||
//IChannel
|
||||
IReadOnlyCollection<IUser> IChannel.CachedUsers => Users;
|
||||
|
||||
IUser IChannel.GetCachedUser(ulong id)
|
||||
=> GetUser(id);
|
||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
||||
//IChannel
|
||||
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IUser>(GetUser(id));
|
||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync()
|
||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,31 +15,31 @@ namespace Discord.WebSocket
|
||||
{
|
||||
private ImmutableArray<Overwrite> _overwrites;
|
||||
|
||||
public IReadOnlyCollection<Overwrite> PermissionOverwrites => _overwrites;
|
||||
|
||||
public ulong GuildId { get; }
|
||||
|
||||
public SocketGuild Guild { get; }
|
||||
public string Name { 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
case ChannelType.Text:
|
||||
return SocketTextChannel.Create(discord, model);
|
||||
return SocketTextChannel.Create(guild, state, model);
|
||||
case ChannelType.Voice:
|
||||
return SocketVoiceChannel.Create(discord, model);
|
||||
return SocketVoiceChannel.Create(guild, state, model);
|
||||
default:
|
||||
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;
|
||||
Position = model.Position.Value;
|
||||
@@ -50,9 +50,7 @@ namespace Discord.WebSocket
|
||||
newOverwrites.Add(new Overwrite(overwrites[i]));
|
||||
_overwrites = newOverwrites.ToImmutable();
|
||||
}
|
||||
|
||||
public async Task UpdateAsync()
|
||||
=> Update(await ChannelHelper.GetAsync(this, Discord));
|
||||
|
||||
public Task ModifyAsync(Action<ModifyGuildChannelParams> func)
|
||||
=> ChannelHelper.ModifyAsync(this, Discord, func);
|
||||
public Task DeleteAsync()
|
||||
@@ -118,7 +116,18 @@ namespace Discord.WebSocket
|
||||
public async Task<RestInviteMetadata> CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = true)
|
||||
=> 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
|
||||
ulong IGuildChannel.GuildId => Guild.Id;
|
||||
|
||||
async Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync()
|
||||
=> await GetInvitesAsync();
|
||||
async Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary)
|
||||
@@ -136,24 +145,16 @@ namespace Discord.WebSocket
|
||||
=> await RemovePermissionOverwriteAsync(role);
|
||||
async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user)
|
||||
=> await RemovePermissionOverwriteAsync(user);
|
||||
|
||||
IReadOnlyCollection<IGuildUser> IGuildChannel.CachedUsers
|
||||
=> ImmutableArray.Create<IGuildUser>();
|
||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||
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;
|
||||
|
||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>(Users).ToAsyncEnumerable();
|
||||
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IGuildUser>(GetUser(id));
|
||||
|
||||
//IChannel
|
||||
IReadOnlyCollection<IUser> IChannel.CachedUsers
|
||||
=> 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?
|
||||
Task<IUser> IChannel.GetUserAsync(ulong id)
|
||||
=> Task.FromResult<IUser>(null); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode)
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IUser>>(Users).ToAsyncEnumerable(); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||
Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IUser>(GetUser(id)); //Overriden in Text/Voice //TODO: Does this actually override?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,29 +13,34 @@ using Model = Discord.API.Channel;
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class SocketTextChannel : SocketGuildChannel, ITextChannel
|
||||
public class SocketTextChannel : SocketGuildChannel, ITextChannel, ISocketMessageChannel
|
||||
{
|
||||
private readonly MessageCache _messages;
|
||||
|
||||
public string Topic { get; private set; }
|
||||
|
||||
public string Mention => MentionUtils.MentionChannel(Id);
|
||||
|
||||
internal SocketTextChannel(DiscordSocketClient discord, ulong id, ulong guildId)
|
||||
: base(discord, id, guildId)
|
||||
public IReadOnlyCollection<SocketMessage> CachedMessages => _messages?.Messages ?? ImmutableArray.Create<SocketMessage>();
|
||||
public override IReadOnlyCollection<SocketGuildUser> Users
|
||||
=> 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)
|
||||
_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);
|
||||
entity.Update(model);
|
||||
var entity = new SocketTextChannel(guild.Discord, model.Id, guild);
|
||||
entity.Update(state, model);
|
||||
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;
|
||||
}
|
||||
@@ -43,19 +48,22 @@ namespace Discord.WebSocket
|
||||
public Task ModifyAsync(Action<ModifyTextChannelParams> func)
|
||||
=> ChannelHelper.ModifyAsync(this, Discord, func);
|
||||
|
||||
public Task<RestGuildUser> GetUserAsync(ulong id)
|
||||
=> ChannelHelper.GetUserAsync(this, Discord, id);
|
||||
public IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync()
|
||||
=> ChannelHelper.GetUsersAsync(this, Discord);
|
||||
|
||||
public Task<RestMessage> GetMessageAsync(ulong id)
|
||||
=> ChannelHelper.GetMessageAsync(this, Discord, id);
|
||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> ChannelHelper.GetMessagesAsync(this, Discord, limit: limit);
|
||||
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> 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);
|
||||
//Messages
|
||||
public SocketMessage GetCachedMessage(ulong id)
|
||||
=> _messages?.Get(id);
|
||||
public async Task<IMessage> GetMessageAsync(ulong id)
|
||||
{
|
||||
IMessage msg = _messages?.Get(id);
|
||||
if (msg == null)
|
||||
msg = await ChannelHelper.GetMessageAsync(this, Discord, id);
|
||||
return msg;
|
||||
}
|
||||
public Task<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch)
|
||||
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, 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()
|
||||
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord);
|
||||
|
||||
@@ -72,36 +80,56 @@ namespace Discord.WebSocket
|
||||
public IDisposable EnterTypingState()
|
||||
=> ChannelHelper.EnterTypingState(this, Discord);
|
||||
|
||||
internal SocketMessage AddMessage(SocketUser author, MessageModel model)
|
||||
{
|
||||
var msg = SocketMessage.Create(Discord, author, model);
|
||||
_messages.Add(msg);
|
||||
return msg;
|
||||
}
|
||||
internal void AddMessage(SocketMessage msg)
|
||||
=> _messages.Add(msg);
|
||||
internal SocketMessage RemoveMessage(ulong id)
|
||||
{
|
||||
return _messages.Remove(id);
|
||||
}
|
||||
=> _messages.Remove(id);
|
||||
|
||||
public override string ToString() => Name;
|
||||
private string DebuggerDisplay => $"@{Name} ({Id}, Text)";
|
||||
//Users
|
||||
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
|
||||
async Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id)
|
||||
=> await GetUserAsync(id);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
||||
=> GetUsersAsync();
|
||||
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IGuildUser>(GetUser(id));
|
||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>(Users).ToAsyncEnumerable();
|
||||
|
||||
//IMessageChannel
|
||||
IReadOnlyCollection<IMessage> IMessageChannel.CachedMessages => ImmutableArray.Create<IMessage>();
|
||||
IMessage IMessageChannel.GetCachedMessage(ulong id) => null;
|
||||
|
||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id)
|
||||
=> await GetMessageAsync(id);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit)
|
||||
=> GetMessagesAsync(limit);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit)
|
||||
=> GetMessagesAsync(fromMessageId, dir, limit);
|
||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode)
|
||||
{
|
||||
if (mode == CacheMode.AllowDownload)
|
||||
return await GetMessageAsync(id);
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode)
|
||||
{
|
||||
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()
|
||||
=> await GetPinnedMessagesAsync().ConfigureAwait(false);
|
||||
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS)
|
||||
|
||||
@@ -12,24 +12,27 @@ using Model = Discord.API.Channel;
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class SocketVoiceChannel : SocketGuildChannel, IVoiceChannel
|
||||
public class SocketVoiceChannel : SocketGuildChannel, IVoiceChannel, ISocketAudioChannel
|
||||
{
|
||||
public int Bitrate { get; private set; }
|
||||
public int UserLimit { get; private set; }
|
||||
|
||||
internal SocketVoiceChannel(DiscordSocketClient discord, ulong id, ulong guildId)
|
||||
: base(discord, id, guildId)
|
||||
public override IReadOnlyCollection<SocketGuildUser> Users
|
||||
=> 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);
|
||||
entity.Update(model);
|
||||
var entity = new SocketVoiceChannel(guild.Discord, model.Id, guild);
|
||||
entity.Update(state, model);
|
||||
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;
|
||||
UserLimit = model.UserLimit.Value;
|
||||
@@ -38,13 +41,24 @@ namespace Discord.WebSocket
|
||||
public Task ModifyAsync(Action<ModifyVoiceChannelParams> 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
|
||||
Task<IAudioClient> IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); }
|
||||
|
||||
//IGuildChannel
|
||||
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id)
|
||||
=> Task.FromResult<IGuildUser>(null);
|
||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync()
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>().ToAsyncEnumerable();
|
||||
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IGuildUser>(GetUser(id));
|
||||
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode)
|
||||
=> ImmutableArray.Create<IReadOnlyCollection<IGuildUser>>(Users).ToAsyncEnumerable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,14 @@ namespace Discord.WebSocket
|
||||
{
|
||||
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<string> _features;
|
||||
internal bool _available;
|
||||
@@ -33,6 +40,9 @@ namespace Discord.WebSocket
|
||||
public VerificationLevel VerificationLevel { get; private set; }
|
||||
public MfaLevel MfaLevel { 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? EmbedChannelId { get; private set; }
|
||||
@@ -44,24 +54,117 @@ namespace Discord.WebSocket
|
||||
public ulong DefaultChannelId => Id;
|
||||
public string IconUrl => API.CDN.GetGuildIconUrl(Id, IconId);
|
||||
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 IReadOnlyCollection<RestRole> Roles => _roles.ToReadOnlyCollection();
|
||||
public SocketRole EveryoneRole => GetRole(Id);
|
||||
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<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)
|
||||
: 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);
|
||||
entity.Update(model);
|
||||
entity.Update(state, model);
|
||||
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;
|
||||
EmbedChannelId = model.EmbedChannelId;
|
||||
@@ -81,7 +184,7 @@ namespace Discord.WebSocket
|
||||
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.ToImmutableArray();
|
||||
_emojis = emojis.ToImmutable();
|
||||
}
|
||||
else
|
||||
_emojis = ImmutableArray.Create<Emoji>();
|
||||
@@ -91,17 +194,52 @@ namespace Discord.WebSocket
|
||||
else
|
||||
_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)
|
||||
{
|
||||
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
|
||||
public async Task UpdateAsync()
|
||||
=> Update(await Discord.ApiClient.GetGuildAsync(Id));
|
||||
public Task DeleteAsync()
|
||||
=> GuildHelper.DeleteAsync(this, Discord);
|
||||
|
||||
@@ -132,14 +270,30 @@ namespace Discord.WebSocket
|
||||
=> GuildHelper.RemoveBanAsync(this, Discord, userId);
|
||||
|
||||
//Channels
|
||||
public Task<IReadOnlyCollection<RestGuildChannel>> GetChannelsAsync()
|
||||
=> GuildHelper.GetChannelsAsync(this, Discord);
|
||||
public Task<RestGuildChannel> GetChannelAsync(ulong id)
|
||||
=> GuildHelper.GetChannelAsync(this, Discord, id);
|
||||
public SocketGuildChannel GetChannel(ulong id)
|
||||
{
|
||||
var channel = Discord.State.GetChannel(id) as SocketGuildChannel;
|
||||
if (channel?.Guild.Id == Id)
|
||||
return channel;
|
||||
return null;
|
||||
}
|
||||
public Task<RestTextChannel> CreateTextChannelAsync(string name)
|
||||
=> GuildHelper.CreateTextChannelAsync(this, Discord, name);
|
||||
public Task<RestVoiceChannel> CreateVoiceChannelAsync(string 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
|
||||
public Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync()
|
||||
@@ -152,48 +306,238 @@ namespace Discord.WebSocket
|
||||
=> GuildHelper.GetInvitesAsync(this, Discord);
|
||||
|
||||
//Roles
|
||||
public RestRole GetRole(ulong id)
|
||||
public SocketRole GetRole(ulong id)
|
||||
{
|
||||
RestRole value;
|
||||
SocketRole value;
|
||||
if (_roles.TryGetValue(id, out value))
|
||||
return value;
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IRole> CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false)
|
||||
public Task<RestRole> 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);
|
||||
_roles = _roles.Add(role.Id, role);
|
||||
var role = SocketRole.Create(this, Discord.State, model);
|
||||
_roles[model.Id] = role;
|
||||
return role;
|
||||
}
|
||||
internal SocketRole RemoveRole(ulong id)
|
||||
{
|
||||
SocketRole role;
|
||||
if (_roles.TryRemove(id, out role))
|
||||
return role;
|
||||
return null;
|
||||
}
|
||||
|
||||
//Users
|
||||
public Task<IReadOnlyCollection<RestGuildUser>> GetUsersAsync()
|
||||
=> GuildHelper.GetUsersAsync(this, Discord);
|
||||
public Task<RestGuildUser> GetUserAsync(ulong id)
|
||||
=> GuildHelper.GetUserAsync(this, Discord, id);
|
||||
public Task<RestGuildUser> GetCurrentUserAsync()
|
||||
=> GuildHelper.GetUserAsync(this, Discord, Discord.CurrentUser.Id);
|
||||
|
||||
public SocketGuildUser GetUser(ulong id)
|
||||
{
|
||||
SocketGuildUser member;
|
||||
if (_members.TryGetValue(id, out member))
|
||||
return member;
|
||||
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)
|
||||
=> 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
|
||||
bool IGuild.Available => true;
|
||||
IAudioClient IGuild.AudioClient => null;
|
||||
IReadOnlyCollection<IGuildUser> IGuild.CachedUsers => ImmutableArray.Create<IGuildUser>();
|
||||
IRole IGuild.EveryoneRole => EveryoneRole;
|
||||
IReadOnlyCollection<IRole> IGuild.Roles => Roles;
|
||||
|
||||
async Task<IReadOnlyCollection<IBan>> IGuild.GetBansAsync()
|
||||
=> await GetBansAsync();
|
||||
|
||||
async Task<IReadOnlyCollection<IGuildChannel>> IGuild.GetChannelsAsync()
|
||||
=> await GetChannelsAsync();
|
||||
async Task<IGuildChannel> IGuild.GetChannelAsync(ulong id)
|
||||
=> await GetChannelAsync(id);
|
||||
IGuildChannel IGuild.GetCachedChannel(ulong id)
|
||||
=> null;
|
||||
Task<IReadOnlyCollection<IGuildChannel>> IGuild.GetChannelsAsync(CacheMode mode)
|
||||
=> Task.FromResult<IReadOnlyCollection<IGuildChannel>>(Channels);
|
||||
Task<IGuildChannel> IGuild.GetChannelAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IGuildChannel>(GetChannel(id));
|
||||
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name)
|
||||
=> await CreateTextChannelAsync(name);
|
||||
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name)
|
||||
@@ -209,15 +553,15 @@ namespace Discord.WebSocket
|
||||
|
||||
IRole IGuild.GetRole(ulong 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()
|
||||
=> await GetUsersAsync();
|
||||
async Task<IGuildUser> IGuild.GetUserAsync(ulong id)
|
||||
=> await GetUserAsync(id);
|
||||
IGuildUser IGuild.GetCachedUser(ulong id)
|
||||
=> null;
|
||||
async Task<IGuildUser> IGuild.GetCurrentUserAsync()
|
||||
=> await GetCurrentUserAsync();
|
||||
Task<IReadOnlyCollection<IGuildUser>> IGuild.GetUsersAsync(CacheMode mode)
|
||||
=> Task.FromResult<IReadOnlyCollection<IGuildUser>>(Users);
|
||||
Task<IGuildUser> IGuild.GetUserAsync(ulong id, CacheMode mode)
|
||||
=> Task.FromResult<IGuildUser>(GetUser(id));
|
||||
Task<IGuildUser> IGuild.GetCurrentUserAsync(CacheMode mode)
|
||||
=> Task.FromResult<IGuildUser>(GetCurrentUser());
|
||||
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
using Discord.Rest;
|
||||
using Discord.WebSocket;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
@@ -51,7 +48,7 @@ namespace Discord.WebSocket
|
||||
return result;
|
||||
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) return ImmutableArray<SocketMessage>.Empty;
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Model = Discord.API.Message;
|
||||
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public abstract class SocketMessage : SocketEntity<ulong>, IMessage, IUpdateable
|
||||
public abstract class SocketMessage : SocketEntity<ulong>, IMessage
|
||||
{
|
||||
private long _timestampTicks;
|
||||
|
||||
@@ -35,14 +34,14 @@ namespace Discord.WebSocket
|
||||
ChannelId = channelId;
|
||||
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)
|
||||
return SocketUserMessage.Create(discord, author, model);
|
||||
return SocketUserMessage.Create(discord, state, author, model);
|
||||
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)
|
||||
_timestampTicks = model.Timestamp.Value.UtcTicks;
|
||||
@@ -50,12 +49,8 @@ namespace Discord.WebSocket
|
||||
if (model.Content.IsSpecified)
|
||||
Content = model.Content.Value;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync()
|
||||
{
|
||||
var model = await Discord.ApiClient.GetChannelMessageAsync(ChannelId, Id).ConfigureAwait(false);
|
||||
Update(model);
|
||||
}
|
||||
|
||||
internal SocketMessage Clone() => MemberwiseClone() as SocketMessage;
|
||||
|
||||
//IMessage
|
||||
IUser IMessage.Author => Author;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Discord.Rest;
|
||||
using Model = Discord.API.Message;
|
||||
using Model = Discord.API.Message;
|
||||
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
@@ -11,17 +10,21 @@ namespace Discord.WebSocket
|
||||
: 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);
|
||||
entity.Update(model);
|
||||
entity.Update(state, model);
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
internal class SocketUserMessage : SocketMessage, IUserMessage
|
||||
public class SocketUserMessage : SocketMessage, IUserMessage
|
||||
{
|
||||
private bool _isMentioningEveryone, _isTTS, _isPinned;
|
||||
private long? _editedTimestampTicks;
|
||||
@@ -32,16 +32,16 @@ namespace Discord.WebSocket
|
||||
: 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);
|
||||
entity.Update(model);
|
||||
entity.Update(state, model);
|
||||
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)
|
||||
_isTTS = model.IsTextToSpeech.Value;
|
||||
@@ -129,5 +129,9 @@ namespace Discord.WebSocket
|
||||
text = MentionsHelper.ResolveEveryoneMentions(text, everyoneHandling);
|
||||
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 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)
|
||||
{
|
||||
}
|
||||
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;
|
||||
|
||||
namespace Discord.WebSocket
|
||||
@@ -7,17 +6,30 @@ namespace Discord.WebSocket
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
public class SocketGroupUser : SocketUser, IGroupUser
|
||||
{
|
||||
internal SocketGroupUser(DiscordSocketClient discord, ulong id)
|
||||
: base(discord, id)
|
||||
public SocketGroupChannel Channel { get; }
|
||||
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);
|
||||
entity.Update(model);
|
||||
var entity = new SocketGroupUser(channel, channel.Discord.GetOrCreateUser(state, model));
|
||||
entity.Update(state, model);
|
||||
return entity;
|
||||
}
|
||||
|
||||
internal new SocketGroupUser Clone() => MemberwiseClone() as SocketGroupUser;
|
||||
|
||||
//IVoiceState
|
||||
bool IVoiceState.IsDeafened => false;
|
||||
bool IVoiceState.IsMuted => false;
|
||||
|
||||
@@ -9,46 +9,76 @@ using PresenceModel = Discord.API.Presence;
|
||||
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
internal class SocketGuildUser : SocketUser, IGuildUser
|
||||
public class SocketGuildUser : SocketUser, IGuildUser
|
||||
{
|
||||
private long? _joinedAtTicks;
|
||||
private ImmutableArray<ulong> _roleIds;
|
||||
|
||||
internal override SocketGlobalUser GlobalUser { get; }
|
||||
public SocketGuild Guild { get; }
|
||||
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 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);
|
||||
|
||||
internal SocketGuildUser(DiscordSocketClient discord, ulong id)
|
||||
: base(discord, id)
|
||||
internal SocketGuildUser(SocketGuild guild, SocketGlobalUser globalUser)
|
||||
: 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);
|
||||
entity.Update(model);
|
||||
var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User));
|
||||
entity.Update(state, model);
|
||||
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;
|
||||
if (model.Nick.IsSpecified)
|
||||
Nickname = model.Nick.Value;
|
||||
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)
|
||||
{
|
||||
var roles = ImmutableArray.CreateBuilder<ulong>(roleIds.Length + 1);
|
||||
roles.Add(GuildId);
|
||||
roles.Add(Guild.Id);
|
||||
for (int i = 0; i < roleIds.Length; i++)
|
||||
roles.Add(roleIds[i]);
|
||||
_roleIds = roles.ToImmutable();
|
||||
}
|
||||
|
||||
public override async Task UpdateAsync()
|
||||
=> Update(await UserHelper.GetAsync(this, Discord));
|
||||
|
||||
public Task ModifyAsync(Action<ModifyGuildMemberParams> func)
|
||||
=> UserHelper.ModifyAsync(this, Discord, func);
|
||||
public Task KickAsync()
|
||||
@@ -59,16 +89,17 @@ namespace Discord.WebSocket
|
||||
throw new NotImplementedException(); //TODO: Impl
|
||||
}
|
||||
|
||||
internal new SocketGuildUser Clone() => MemberwiseClone() as SocketGuildUser;
|
||||
|
||||
//IGuildUser
|
||||
ulong IGuildUser.GuildId => Guild.Id;
|
||||
IReadOnlyCollection<ulong> IGuildUser.RoleIds => RoleIds;
|
||||
|
||||
//IUser
|
||||
Task<IDMChannel> IUser.GetDMChannelAsync(CacheMode mode)
|
||||
=> Task.FromResult<IDMChannel>(GlobalUser.DMChannel);
|
||||
|
||||
//IVoiceState
|
||||
bool IVoiceState.IsDeafened => false;
|
||||
bool IVoiceState.IsMuted => false;
|
||||
bool IVoiceState.IsSelfDeafened => false;
|
||||
bool IVoiceState.IsSelfMuted => false;
|
||||
bool IVoiceState.IsSuppressed => false;
|
||||
IVoiceChannel IVoiceState.VoiceChannel => null;
|
||||
string IVoiceState.VoiceSessionId => null;
|
||||
IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
//TODO: C#7 Candidate for record type
|
||||
internal struct SocketPresence : IPresence
|
||||
public struct SocketPresence : IPresence
|
||||
{
|
||||
public Game? Game { get; }
|
||||
public UserStatus Status { get; }
|
||||
@@ -13,11 +13,11 @@ namespace Discord.WebSocket
|
||||
Game = game;
|
||||
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);
|
||||
}
|
||||
|
||||
public SocketPresence Clone() => this;
|
||||
internal SocketPresence Clone() => this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,21 +11,28 @@ namespace Discord.WebSocket
|
||||
public string Email { get; private set; }
|
||||
public bool IsVerified { get; private set; }
|
||||
public bool IsMfaEnabled { get; private set; }
|
||||
internal override SocketGlobalUser GlobalUser { get; }
|
||||
|
||||
internal SocketSelfUser(DiscordSocketClient discord, ulong id)
|
||||
: base(discord, id)
|
||||
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 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);
|
||||
entity.Update(model);
|
||||
var entity = new SocketSelfUser(discord, discord.GetOrCreateSelfUser(state, model));
|
||||
entity.Update(state, model);
|
||||
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)
|
||||
Email = model.Email.Value;
|
||||
@@ -34,12 +41,13 @@ namespace Discord.WebSocket
|
||||
if (model.MfaEnabled.IsSpecified)
|
||||
IsMfaEnabled = model.MfaEnabled.Value;
|
||||
}
|
||||
|
||||
public override async Task UpdateAsync()
|
||||
=> Update(await UserHelper.GetAsync(this, Discord));
|
||||
|
||||
public Task ModifyAsync(Action<ModifyCurrentUserParams> func)
|
||||
=> UserHelper.ModifyAsync(this, Discord, func);
|
||||
|
||||
internal new SocketSelfUser Clone() => MemberwiseClone() as SocketSelfUser;
|
||||
|
||||
//ISelfUser
|
||||
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 System.Threading.Tasks;
|
||||
using Model = Discord.API.User;
|
||||
using PresenceModel = Discord.API.Presence;
|
||||
|
||||
namespace Discord.WebSocket
|
||||
{
|
||||
public class SocketUser : SocketEntity<ulong>, IUser
|
||||
public abstract class SocketUser : SocketEntity<ulong>, IUser
|
||||
{
|
||||
public bool IsBot { get; private set; }
|
||||
public string Username { get; private set; }
|
||||
public ushort DiscriminatorValue { get; private set; }
|
||||
public string AvatarId { get; private set; }
|
||||
public abstract bool IsBot { get; internal set; }
|
||||
public abstract string Username { get; internal set; }
|
||||
public abstract ushort DiscriminatorValue { get; internal 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 Discriminator => DiscriminatorValue.ToString("D4");
|
||||
public string Mention => MentionUtils.MentionUser(Id);
|
||||
public virtual Game? Game => null;
|
||||
public virtual UserStatus Status { get; internal set; }
|
||||
public Game? Game => Presence.Game;
|
||||
public UserStatus Status => Presence.Status;
|
||||
|
||||
internal SocketUser(DiscordSocketClient discord, ulong id)
|
||||
: base(discord, id)
|
||||
{
|
||||
}
|
||||
internal static SocketUser Create(DiscordSocketClient discord, Model model)
|
||||
{
|
||||
var entity = new SocketUser(discord, model.Id);
|
||||
entity.Update(model);
|
||||
return entity;
|
||||
}
|
||||
internal virtual void Update(Model model)
|
||||
internal virtual void Update(ClientState state, Model model)
|
||||
{
|
||||
if (model.Avatar.IsSpecified)
|
||||
AvatarId = model.Avatar.Value;
|
||||
@@ -38,13 +35,22 @@ namespace Discord.WebSocket
|
||||
if (model.Username.IsSpecified)
|
||||
Username = model.Username.Value;
|
||||
}
|
||||
internal virtual void Update(ClientState state, PresenceModel model)
|
||||
{
|
||||
Presence = SocketPresence.Create(model);
|
||||
}
|
||||
|
||||
public virtual async Task UpdateAsync()
|
||||
=> Update(await UserHelper.GetAsync(this, Discord));
|
||||
|
||||
public Task<IDMChannel> CreateDMChannelAsync()
|
||||
public Task<RestDMChannel> CreateDMChannelAsync()
|
||||
=> 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);
|
||||
}
|
||||
|
||||
public SocketVoiceState Clone() => this;
|
||||
internal SocketVoiceState Clone() => this;
|
||||
|
||||
IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user