Started major API refactor
This commit is contained in:
@@ -11,14 +11,10 @@ namespace Discord.Commands
|
||||
public string ArgText { get; }
|
||||
public int? Permissions { get; }
|
||||
public string[] Args { get; }
|
||||
|
||||
public User User => Message.User;
|
||||
public string UserId => Message.UserId;
|
||||
|
||||
public Member Member => Message.Member;
|
||||
public Channel Channel => Message.Channel;
|
||||
public string ChannelId => Message.ChannelId;
|
||||
public Server Server => Message.Channel.Server;
|
||||
public string ServerId => Message.Channel.ServerId;
|
||||
|
||||
public CommandEventArgs(Message message, Command command, string commandText, string argText, int? permissions, string[] args)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Discord.Commands
|
||||
{
|
||||
private readonly DiscordClient _client;
|
||||
private List<Command> _commands;
|
||||
private Func<User, Server, int> _getPermissions;
|
||||
private Func<Member, int> _getPermissions;
|
||||
|
||||
public IEnumerable<Command> Commands => _commands;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Discord.Commands
|
||||
public bool RequireCommandCharInPublic { get; set; }
|
||||
public bool RequireCommandCharInPrivate { get; set; }
|
||||
|
||||
public CommandsPlugin(DiscordClient client, Func<User, Server, int> getPermissions = null)
|
||||
public CommandsPlugin(DiscordClient client, Func<Member, int> getPermissions = null)
|
||||
{
|
||||
_client = client;
|
||||
_getPermissions = getPermissions;
|
||||
@@ -96,7 +96,7 @@ namespace Discord.Commands
|
||||
argText = msg.Substring(args[cmd.Parts.Length].Index);
|
||||
|
||||
//Check Permissions
|
||||
int permissions = _getPermissions != null ? _getPermissions(e.Message.User, e.Message.Channel?.Server) : 0;
|
||||
int permissions = _getPermissions != null ? _getPermissions(e.Message.Member) : 0;
|
||||
var eventArgs = new CommandEventArgs(e.Message, cmd, msg, argText, permissions, newArgs);
|
||||
if (permissions < cmd.MinPerms)
|
||||
{
|
||||
|
||||
@@ -202,6 +202,9 @@
|
||||
<Compile Include="..\Discord.Net\DiscordWSClientConfig.cs">
|
||||
<Link>DiscordWSClientConfig.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Helpers\AsyncCollection.cs">
|
||||
<Link>Helpers\AsyncCollection.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Helpers\EpochTime.cs">
|
||||
<Link>Helpers\EpochTime.cs</Link>
|
||||
</Compile>
|
||||
@@ -214,14 +217,8 @@
|
||||
<Compile Include="..\Discord.Net\Helpers\Mention.cs">
|
||||
<Link>Helpers\Mention.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Helpers\MentionHelper.cs">
|
||||
<Link>Helpers\MentionHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Helpers\Shared\CollectionHelper.cs">
|
||||
<Link>Helpers\Shared\CollectionHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Helpers\Shared\TaskHelper.cs">
|
||||
<Link>Helpers\Shared\TaskHelper.cs</Link>
|
||||
<Link>Helpers\TaskHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Helpers\TimeoutException.cs">
|
||||
<Link>Helpers\TimeoutException.cs</Link>
|
||||
@@ -229,8 +226,8 @@
|
||||
<Compile Include="..\Discord.Net\HttpException.cs">
|
||||
<Link>HttpException.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Models\AsyncCollection.cs">
|
||||
<Link>Models\AsyncCollection.cs</Link>
|
||||
<Compile Include="..\Discord.Net\Models\CachedObject.cs">
|
||||
<Link>Models\CachedObject.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Models\Channel.cs">
|
||||
<Link>Models\Channel.cs</Link>
|
||||
@@ -238,12 +235,12 @@
|
||||
<Compile Include="..\Discord.Net\Models\Color.cs">
|
||||
<Link>Models\Color.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Models\GlobalUser.cs">
|
||||
<Link>Models\GlobalUser.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Models\Invite.cs">
|
||||
<Link>Models\Invite.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Models\Member.cs">
|
||||
<Link>Models\Member.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Models\Message.cs">
|
||||
<Link>Models\Message.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -110,11 +110,11 @@ namespace Discord
|
||||
}
|
||||
|
||||
//Invites
|
||||
public Task<CreateInviteResponse> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool withXkcdPass)
|
||||
public Task<CreateInviteResponse> CreateInvite(string channelId, int maxAge, int maxUses, bool tempMembership, bool hasXkcd)
|
||||
{
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
|
||||
var request = new CreateInviteRequest { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = isTemporary, WithXkcdPass = withXkcdPass };
|
||||
var request = new CreateInviteRequest { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = tempMembership, WithXkcdPass = hasXkcd };
|
||||
return _rest.Post<CreateInviteResponse>(Endpoints.ChannelInvites(channelId), request);
|
||||
}
|
||||
public Task<GetInviteResponse> GetInvite(string inviteIdOrXkcd)
|
||||
|
||||
@@ -7,14 +7,11 @@ namespace Discord
|
||||
{
|
||||
public class BanEventArgs : EventArgs
|
||||
{
|
||||
public User User { get; }
|
||||
public string UserId { get; }
|
||||
public Server Server { get; }
|
||||
public string ServerId => Server.Id;
|
||||
|
||||
internal BanEventArgs(User user, string userId, Server server)
|
||||
internal BanEventArgs(string userId, Server server)
|
||||
{
|
||||
User = user;
|
||||
UserId = userId;
|
||||
Server = server;
|
||||
}
|
||||
@@ -26,57 +23,31 @@ namespace Discord
|
||||
private void RaiseBanAdded(string userId, Server server)
|
||||
{
|
||||
if (BanAdded != null)
|
||||
RaiseEvent(nameof(BanAdded), () => BanAdded(this, new BanEventArgs(_users[userId], userId, server)));
|
||||
RaiseEvent(nameof(BanAdded), () => BanAdded(this, new BanEventArgs(userId, server)));
|
||||
}
|
||||
public event EventHandler<BanEventArgs> BanRemoved;
|
||||
private void RaiseBanRemoved(string userId, Server server)
|
||||
{
|
||||
if (BanRemoved != null)
|
||||
RaiseEvent(nameof(BanRemoved), () => BanRemoved(this, new BanEventArgs(_users[userId], userId, server)));
|
||||
RaiseEvent(nameof(BanRemoved), () => BanRemoved(this, new BanEventArgs(userId, server)));
|
||||
}
|
||||
|
||||
/// <summary> Bans a user from the provided server. </summary>
|
||||
public Task Ban(Member member)
|
||||
=> Ban(member?.ServerId, member?.UserId);
|
||||
/// <summary> Bans a user from the provided server. </summary>
|
||||
public Task Ban(Server server, User user)
|
||||
=> Ban(server?.Id, user?.Id);
|
||||
/// <summary> Bans a user from the provided server. </summary>
|
||||
public Task Ban(Server server, string userId)
|
||||
=> Ban(server?.Id, userId);
|
||||
/// <summary> Bans a user from the provided server. </summary>
|
||||
public Task Ban(string server, User user)
|
||||
=> Ban(server, user?.Id);
|
||||
/// <summary> Bans a user from the provided server. </summary>
|
||||
public Task Ban(string serverId, string userId)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
if (member == null) throw new ArgumentNullException(nameof(member));
|
||||
|
||||
return _api.Ban(serverId, userId);
|
||||
return _api.Ban(member.ServerId, member.Id);
|
||||
}
|
||||
|
||||
/// <summary> Unbans a user from the provided server. </summary>
|
||||
public Task Unban(Member member)
|
||||
=> Unban(member?.ServerId, member?.UserId);
|
||||
/// <summary> Unbans a user from the provided server. </summary>
|
||||
public Task Unban(Server server, User user)
|
||||
=> Unban(server?.Id, user?.Id);
|
||||
/// <summary> Unbans a user from the provided server. </summary>
|
||||
public Task Unban(Server server, string userId)
|
||||
=> Unban(server?.Id, userId);
|
||||
/// <summary> Unbans a user from the provided server. </summary>
|
||||
public Task Unban(string server, User user)
|
||||
=> Unban(server, user?.Id);
|
||||
/// <summary> Unbans a user from the provided server. </summary>
|
||||
public async Task Unban(string serverId, string userId)
|
||||
public async Task Unban(Member member)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
if (member == null) throw new ArgumentNullException(nameof(member));
|
||||
|
||||
try { await _api.Unban(serverId, userId).ConfigureAwait(false); }
|
||||
try { await _api.Unban(member.ServerId, member.Id).ConfigureAwait(false); }
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ namespace Discord
|
||||
public class ChannelEventArgs : EventArgs
|
||||
{
|
||||
public Channel Channel { get; }
|
||||
public string ChannelId => Channel.Id;
|
||||
public Server Server => Channel.Server;
|
||||
public string ServerId => Channel.ServerId;
|
||||
|
||||
internal ChannelEventArgs(Channel channel) { Channel = channel; }
|
||||
}
|
||||
@@ -49,29 +47,30 @@ namespace Discord
|
||||
RaiseEvent(nameof(ChannelUpdated), () => ChannelUpdated(this, new ChannelEventArgs(channel)));
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the channel with the specified id, or null if none was found. </summary>
|
||||
public Channel GetChannel(string id) => _channels[id];
|
||||
/// <summary> Returns all channels with the specified server and name. </summary>
|
||||
/// <remarks> Name formats supported: Name and #Name. Search is case-insensitive. </remarks>
|
||||
public IEnumerable<Channel> FindChannels(Server server, string name, string type = null) => FindChannels(server?.Id, name, type);
|
||||
/// <summary> Returns all channels with the specified server and name. </summary>
|
||||
/// <remarks> Name formats supported: Name and #Name. Search is case-insensitive. </remarks>
|
||||
public IEnumerable<Channel> FindChannels(string serverId, string name, string type = null)
|
||||
public Channel GetChannel(string id)
|
||||
{
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (id == null) throw new ArgumentNullException(nameof(id));
|
||||
return _channels[id];
|
||||
}
|
||||
|
||||
/// <summary> Returns all channels with the specified server and name. </summary>
|
||||
/// <remarks> Name formats supported: Name and #Name. Search is case-insensitive. </remarks>
|
||||
public IEnumerable<Channel> FindChannels(Server server, string name, string type = null)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
|
||||
IEnumerable<Channel> result;
|
||||
if (name.StartsWith("#"))
|
||||
{
|
||||
string name2 = name.Substring(1);
|
||||
result = _channels.Where(x => x.ServerId == serverId &&
|
||||
result = _channels.Where(x => x.Server == server &&
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _channels.Where(x => x.ServerId == serverId &&
|
||||
result = _channels.Where(x => x.Server == server &&
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
@@ -82,55 +81,44 @@ namespace Discord
|
||||
}
|
||||
|
||||
/// <summary> Creates a new channel with the provided name and type (see ChannelTypes). </summary>
|
||||
public Task<Channel> CreateChannel(Server server, string name, string type = ChannelTypes.Text)
|
||||
=> CreateChannel(server?.Id, name, type);
|
||||
/// <summary> Creates a new channel with the provided name and type (see ChannelTypes). </summary>
|
||||
public async Task<Channel> CreateChannel(string serverId, string name, string type = ChannelTypes.Text)
|
||||
public async Task<Channel> CreateChannel(Server server, string name, string type = ChannelTypes.Text)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||
CheckReady();
|
||||
|
||||
var response = await _api.CreateChannel(serverId, name, type).ConfigureAwait(false);
|
||||
var response = await _api.CreateChannel(server.Id, name, type).ConfigureAwait(false);
|
||||
var channel = _channels.GetOrAdd(response.Id, response.GuildId, response.Recipient?.Id);
|
||||
channel.Update(response);
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Returns the private channel with the provided user, creating one if it does not currently exist. </summary>
|
||||
public Task<Channel> CreatePMChannel(string userId) => CreatePMChannel(_users[userId], userId);
|
||||
/// <summary> Returns the private channel with the provided user, creating one if it does not currently exist. </summary>
|
||||
public Task<Channel> CreatePMChannel(User user) => CreatePMChannel(user, user?.Id);
|
||||
/// <summary> Returns the private channel with the provided user, creating one if it does not currently exist. </summary>
|
||||
public Task<Channel> CreatePMChannel(Member member) => CreatePMChannel(member.User, member.UserId);
|
||||
private async Task<Channel> CreatePMChannel(User user, string userId)
|
||||
public async Task<Channel> CreatePMChannel(Member member)
|
||||
{
|
||||
if (member == null) throw new ArgumentNullException(nameof(member));
|
||||
CheckReady();
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
|
||||
Channel channel = null;
|
||||
if (user != null)
|
||||
channel = user.PrivateChannel;
|
||||
if (member != null)
|
||||
channel = member.GlobalUser.PrivateChannel;
|
||||
if (channel == null)
|
||||
{
|
||||
var response = await _api.CreatePMChannel(CurrentUserId, userId).ConfigureAwait(false);
|
||||
user = _users.GetOrAdd(response.Recipient?.Id);
|
||||
user.Update(response.Recipient);
|
||||
var response = await _api.CreatePMChannel(_userId, member.Id).ConfigureAwait(false);
|
||||
var recipient = _members.GetOrAdd(response.Recipient?.Id, _servers.PMServer.Id);
|
||||
recipient.Update(response.Recipient);
|
||||
channel = _channels.GetOrAdd(response.Id, response.GuildId, response.Recipient?.Id);
|
||||
channel.Update(response);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
/// <summary> Edits the provided channel, changing only non-null attributes. </summary>
|
||||
public Task EditChannel(string channelId, string name = null, string topic = null, int? position = null)
|
||||
=> EditChannel(_channels[channelId], name: name, topic: topic, position: position);
|
||||
|
||||
/// <summary> Edits the provided channel, changing only non-null attributes. </summary>
|
||||
public async Task EditChannel(Channel channel, string name = null, string topic = null, int? position = null)
|
||||
{
|
||||
CheckReady();
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
CheckReady();
|
||||
|
||||
await _api.EditChannel(channel.Id, name: name, topic: topic);
|
||||
|
||||
@@ -155,34 +143,29 @@ namespace Discord
|
||||
channels[i] = channels[i - 1];
|
||||
channels[newPos] = channel;
|
||||
}
|
||||
await _api.ReorderChannels(channel.ServerId, channels.Skip(minPos).Select(x => x.Id), minPos);
|
||||
Channel after = minPos > 0 ? channels.Skip(minPos - 1).FirstOrDefault() : null;
|
||||
await ReorderChannels(channel.Server, channels.Skip(minPos), after);
|
||||
}
|
||||
}
|
||||
|
||||
public Task ReorderChannels(Server server, IEnumerable<object> channels, int startPos = 0)
|
||||
=> ReorderChannels(server.Id, channels, startPos);
|
||||
public Task ReorderChannels(string serverId, IEnumerable<object> channels, int startPos = 0)
|
||||
|
||||
/// <summary> Reorders the provided channels in the server's channel list and places them after a certain channel. </summary>
|
||||
public Task ReorderChannels(Server server, IEnumerable<Channel> channels, Channel after = null)
|
||||
{
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
if (channels == null) throw new ArgumentNullException(nameof(channels));
|
||||
if (startPos < 0) throw new ArgumentOutOfRangeException(nameof(startPos), "startPos must be a positive integer.");
|
||||
|
||||
var channelIds = CollectionHelper.FlattenChannels(channels);
|
||||
return _api.ReorderChannels(serverId, channelIds, startPos);
|
||||
|
||||
return _api.ReorderChannels(server.Id, channels.Select(x => x.Id), after.Position);
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Destroys the provided channel. </summary>
|
||||
public Task<Channel> DestroyChannel(Channel channel)
|
||||
=> DestroyChannel(channel?.Id);
|
||||
/// <summary> Destroys the provided channel. </summary>
|
||||
public async Task<Channel> DestroyChannel(string channelId)
|
||||
public async Task<Channel> DestroyChannel(Channel channel)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
|
||||
try { await _api.DestroyChannel(channelId).ConfigureAwait(false); }
|
||||
try { await _api.DestroyChannel(channel.Id).ConfigureAwait(false); }
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
return _channels.TryRemove(channelId);
|
||||
return _channels.TryRemove(channel.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using Discord.Net;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
@@ -14,6 +13,14 @@ namespace Discord
|
||||
CheckReady();
|
||||
if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd));
|
||||
|
||||
//Remove trailing slash
|
||||
if (inviteIdOrXkcd.Length > 0 && inviteIdOrXkcd[inviteIdOrXkcd.Length - 1] == '/')
|
||||
inviteIdOrXkcd = inviteIdOrXkcd.Substring(0, inviteIdOrXkcd.Length - 1);
|
||||
//Remove leading URL
|
||||
int index = inviteIdOrXkcd.LastIndexOf('/');
|
||||
if (index >= 0)
|
||||
inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1);
|
||||
|
||||
var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false);
|
||||
var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id);
|
||||
invite.Update(response);
|
||||
@@ -26,44 +33,36 @@ namespace Discord
|
||||
/// <param name="hasXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to 0. </param>
|
||||
/// <param name="maxUses"> The max amount of times this invite may be used. Set to 0 to have unlimited uses. </param>
|
||||
public Task<Invite> CreateInvite(Server server, int maxAge = 1800, int maxUses = 0, bool tempMembership = false, bool hasXkcd = false)
|
||||
=> CreateInvite(server?.DefaultChannelId, maxAge, maxUses, tempMembership, hasXkcd);
|
||||
/// <summary> Creates a new invite to the provided channel. </summary>
|
||||
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to 0 to never expire. </param>
|
||||
/// <param name="tempMembership"> If true, a user accepting this invite will be kicked from the server after closing their client. </param>
|
||||
/// <param name="hasXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to 0. </param>
|
||||
/// <param name="maxUses"> The max amount of times this invite may be used. Set to 0 to have unlimited uses. </param>
|
||||
public Task<Invite> CreateInvite(Channel channel, int maxAge = 1800, int maxUses = 0, bool tempMembership = false, bool hasXkcd = false)
|
||||
=> CreateInvite(channel?.Id, maxAge, maxUses, tempMembership, hasXkcd);
|
||||
/// <summary> Creates a new invite to the provided channel. </summary>
|
||||
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to 0 to never expire. </param>
|
||||
/// <param name="tempMembership"> If true, a user accepting this invite will be kicked from the server after closing their client. </param>
|
||||
/// <param name="hasXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to 0. </param>
|
||||
/// <param name="maxUses"> The max amount of times this invite may be used. Set to 0 to have unlimited uses. </param>
|
||||
public async Task<Invite> CreateInvite(string serverOrChannelId, int maxAge = 1800, int maxUses = 0, bool tempMembership = false, bool hasXkcd = false)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverOrChannelId == null) throw new ArgumentNullException(nameof(serverOrChannelId));
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
return CreateInvite(server.DefaultChannel, maxAge, maxUses, tempMembership, hasXkcd);
|
||||
}
|
||||
/// <summary> Creates a new invite to the provided channel. </summary>
|
||||
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to 0 to never expire. </param>
|
||||
/// <param name="tempMembership"> If true, a user accepting this invite will be kicked from the server after closing their client. </param>
|
||||
/// <param name="hasXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to 0. </param>
|
||||
/// <param name="maxUses"> The max amount of times this invite may be used. Set to 0 to have unlimited uses. </param>
|
||||
public async Task<Invite> CreateInvite(Channel channel, int maxAge = 1800, int maxUses = 0, bool tempMembership = false, bool hasXkcd = false)
|
||||
{
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
if (maxAge <= 0) throw new ArgumentOutOfRangeException(nameof(maxAge));
|
||||
if (maxUses <= 0) throw new ArgumentOutOfRangeException(nameof(maxUses));
|
||||
CheckReady();
|
||||
|
||||
var response = await _api.CreateInvite(serverOrChannelId, maxAge, maxUses, tempMembership, hasXkcd).ConfigureAwait(false);
|
||||
var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses,
|
||||
tempMembership: tempMembership, hasXkcd: hasXkcd).ConfigureAwait(false);
|
||||
var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id);
|
||||
invite.Update(response);
|
||||
return invite;
|
||||
}
|
||||
|
||||
/// <summary> Deletes the provided invite. </summary>
|
||||
public async Task DestroyInvite(string inviteId)
|
||||
public async Task DestroyInvite(Invite invite)
|
||||
{
|
||||
CheckReady();
|
||||
if (inviteId == null) throw new ArgumentNullException(nameof(inviteId));
|
||||
if (invite == null) throw new ArgumentNullException(nameof(invite));
|
||||
|
||||
try
|
||||
{
|
||||
//Check if this is a human-readable link and get its ID
|
||||
var response = await _api.GetInvite(inviteId).ConfigureAwait(false);
|
||||
await _api.DeleteInvite(response.Code).ConfigureAwait(false);
|
||||
}
|
||||
try { await _api.DeleteInvite(invite.Id).ConfigureAwait(false); }
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
}
|
||||
|
||||
@@ -75,22 +74,5 @@ namespace Discord
|
||||
|
||||
return _api.AcceptInvite(invite.Id);
|
||||
}
|
||||
/// <summary> Accepts the provided invite. </summary>
|
||||
public async Task AcceptInvite(string inviteId)
|
||||
{
|
||||
CheckReady();
|
||||
if (inviteId == null) throw new ArgumentNullException(nameof(inviteId));
|
||||
|
||||
//Remove trailing slash and any non-code url parts
|
||||
if (inviteId.Length > 0 && inviteId[inviteId.Length - 1] == '/')
|
||||
inviteId = inviteId.Substring(0, inviteId.Length - 1);
|
||||
int index = inviteId.LastIndexOf('/');
|
||||
if (index >= 0)
|
||||
inviteId = inviteId.Substring(index + 1);
|
||||
|
||||
//Check if this is a human-readable link and get its ID
|
||||
var invite = await GetInvite(inviteId).ConfigureAwait(false);
|
||||
await _api.AcceptInvite(invite.Id).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,7 @@ namespace Discord
|
||||
public class MemberEventArgs : EventArgs
|
||||
{
|
||||
public Member Member { get; }
|
||||
public User User => Member.User;
|
||||
public string UserId => Member.UserId;
|
||||
public string UserId => Member.Id;
|
||||
public Server Server => Member.Server;
|
||||
public string ServerId => Member.ServerId;
|
||||
|
||||
@@ -66,80 +65,65 @@ namespace Discord
|
||||
if (UserIsSpeaking != null)
|
||||
RaiseEvent(nameof(UserIsSpeaking), () => UserIsSpeaking(this, new MemberIsSpeakingEventArgs(member, channel, isSpeaking)));
|
||||
}
|
||||
|
||||
|
||||
private Member _currentUser;
|
||||
|
||||
internal Members Members => _members;
|
||||
private readonly Members _members;
|
||||
|
||||
|
||||
/// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary>
|
||||
public Member GetMember(Server server, User user) => _members[user?.Id, server?.Id];
|
||||
/// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary>
|
||||
public Member GetMember(Server server, string userId) => _members[userId, server?.Id];
|
||||
/// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary>
|
||||
public Member GetMember(string serverId, User user) => _members[user?.Id, serverId];
|
||||
/// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary>
|
||||
public Member GetMember(string serverId, string userId) => _members[userId, serverId];
|
||||
/// <summary> Returns the user with the specified name and discriminator, along withtheir server-specific data, or null if they couldn't be found. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks>
|
||||
public Member GetMember(Server server, string username, string discriminator)
|
||||
=> GetMember(server?.Id, username, discriminator);
|
||||
/// <summary> Returns the user with the specified name and discriminator, along withtheir server-specific data, or null if they couldn't be found. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks>
|
||||
public Member GetMember(string serverId, string username, string discriminator)
|
||||
public Member GetMember(Server server, string userId)
|
||||
{
|
||||
User user = GetUser(username, discriminator);
|
||||
return _members[user?.Id, serverId];
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
CheckReady();
|
||||
|
||||
return _members[userId, server.Id];
|
||||
}
|
||||
/// <summary> Returns the user with the specified name and discriminator, along withtheir server-specific data, or null if they couldn't be found. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks>
|
||||
public Member GetMember(Server server, string username, string discriminator)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
if (username == null) throw new ArgumentNullException(nameof(username));
|
||||
if (discriminator == null) throw new ArgumentNullException(nameof(discriminator));
|
||||
CheckReady();
|
||||
|
||||
Member member = FindMembers(server, username, discriminator, true).FirstOrDefault();
|
||||
return _members[member?.Id, server.Id];
|
||||
}
|
||||
|
||||
/// <summary> Returns all users in with the specified server and name, along with their server-specific data. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive.</remarks>
|
||||
public IEnumerable<Member> FindMembers(string serverId, string name) => FindMembers(_servers[serverId], name);
|
||||
/// <summary> Returns all users in with the specified server and name, along with their server-specific data. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive.</remarks>
|
||||
public IEnumerable<Member> FindMembers(Server server, string name)
|
||||
public IEnumerable<Member> FindMembers(Server server, string name, string discriminator = null, bool exactMatch = false)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
CheckReady();
|
||||
|
||||
if (name.StartsWith("@"))
|
||||
IEnumerable<Member> query;
|
||||
if (!exactMatch && name.StartsWith("@"))
|
||||
{
|
||||
string name2 = name.Substring(1);
|
||||
return server.Members.Where(x =>
|
||||
{
|
||||
var user = x.User;
|
||||
if (user == null)
|
||||
return false;
|
||||
return string.Equals(user.Name, name, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(user.Name, name2, StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
query = server.Members.Where(x =>
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
return server.Members.Where(x =>
|
||||
{
|
||||
var user = x.User;
|
||||
if (user == null)
|
||||
return false;
|
||||
return string.Equals(x.User.Name, name, StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
query = server.Members.Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
if (discriminator != null)
|
||||
query = query.Where(x => x.Discriminator == discriminator);
|
||||
return query;
|
||||
}
|
||||
|
||||
public Task EditMember(Member member, bool? mute = null, bool? deaf = null, IEnumerable<object> roles = null)
|
||||
=> EditMember(member?.ServerId, member?.UserId, mute, deaf, roles);
|
||||
public Task EditMember(Server server, User user, bool? mute = null, bool? deaf = null, IEnumerable<object> roles = null)
|
||||
=> EditMember(server?.Id, user?.Id, mute, deaf, roles);
|
||||
public Task EditMember(Server server, string userId, bool? mute = null, bool? deaf = null, IEnumerable<string> roles = null)
|
||||
=> EditMember(server?.Id, userId, mute, deaf, roles);
|
||||
public Task EditMember(string serverId, User user, bool? mute = null, bool? deaf = null, IEnumerable<object> roles = null)
|
||||
=> EditMember(serverId, user?.Id, mute, deaf, roles);
|
||||
public Task EditMember(string serverId, string userId, bool? mute = null, bool? deaf = null, IEnumerable<object> roles = null)
|
||||
public Task EditMember(Member member, bool? mute = null, bool? deaf = null, IEnumerable<Role> roles = null)
|
||||
{
|
||||
if (member == null) throw new ArgumentNullException(nameof(member));
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
|
||||
var newRoles = CollectionHelper.FlattenRoles(roles);
|
||||
return _api.EditMember(serverId, userId, mute: mute, deaf: deaf, roles: newRoles);
|
||||
|
||||
return _api.EditMember(member.ServerId, member.Id, mute: mute, deaf: deaf, roles: roles.Select(x => x.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,9 @@ namespace Discord
|
||||
public class MessageEventArgs : EventArgs
|
||||
{
|
||||
public Message Message { get; }
|
||||
public string MessageId => Message.Id;
|
||||
public Member Member => Message.Member;
|
||||
public Channel Channel => Message.Channel;
|
||||
public string ChannelId => Message.ChannelId;
|
||||
public Server Server => Message.Server;
|
||||
public string ServerId => Message.ServerId;
|
||||
public User User => Member.User;
|
||||
public string UserId => Message.UserId;
|
||||
|
||||
internal MessageEventArgs(Message msg) { Message = msg; }
|
||||
}
|
||||
@@ -74,165 +69,127 @@ namespace Discord
|
||||
public Message GetMessage(string id) => _messages[id];
|
||||
|
||||
/// <summary> Sends a message to the provided channel. To include a mention, see the Mention static helper class. </summary>
|
||||
public Task<Message[]> SendMessage(Channel channel, string text)
|
||||
=> SendMessage(channel, text, MentionHelper.GetUserIds(text), false);
|
||||
/// <summary> Sends a message to the provided channel. To include a mention, see the Mention static helper class. </summary>
|
||||
public Task<Message[]> SendMessage(string channelId, string text)
|
||||
=> SendMessage(_channels[channelId], text, MentionHelper.GetUserIds(text), false);
|
||||
private async Task<Message[]> SendMessage(Channel channel, string text, IEnumerable<object> mentionedUsers = null, bool isTextToSpeech = false)
|
||||
public Task<Message> SendMessage(Channel channel, string text)
|
||||
{
|
||||
CheckReady();
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
var mentionedUserIds = CollectionHelper.FlattenUsers(mentionedUsers);
|
||||
CheckReady();
|
||||
|
||||
int blockCount = (int)Math.Ceiling(text.Length / (double)MaxMessageSize);
|
||||
Message[] result = new Message[blockCount];
|
||||
for (int i = 0; i < blockCount; i++)
|
||||
{
|
||||
int index = i * MaxMessageSize;
|
||||
string blockText = text.Substring(index, Math.Min(2000, text.Length - index));
|
||||
var nonce = GenerateNonce();
|
||||
if (Config.UseMessageQueue)
|
||||
{
|
||||
var msg = _messages.GetOrAdd("nonce_" + nonce, channel.Id, CurrentUserId);
|
||||
var currentUser = msg.User;
|
||||
msg.Update(new MessageInfo
|
||||
{
|
||||
Content = blockText,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Author = new UserReference { Avatar = currentUser.AvatarId, Discriminator = currentUser.Discriminator, Id = CurrentUserId, Username = currentUser.Name },
|
||||
ChannelId = channel.Id,
|
||||
IsTextToSpeech = isTextToSpeech
|
||||
});
|
||||
msg.IsQueued = true;
|
||||
msg.Nonce = nonce;
|
||||
result[i] = msg;
|
||||
_pendingMessages.Enqueue(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
var model = await _api.SendMessage(channel.Id, blockText, mentionedUserIds, nonce, isTextToSpeech).ConfigureAwait(false);
|
||||
var msg = _messages.GetOrAdd(model.Id, channel.Id, model.Author.Id);
|
||||
msg.Update(model);
|
||||
RaiseMessageSent(msg);
|
||||
result[i] = msg;
|
||||
}
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
return result;
|
||||
return SendMessage(channel, text, false);
|
||||
}
|
||||
|
||||
/// <summary> Sends a private message to the provided user. </summary>
|
||||
public Task<Message[]> SendPrivateMessage(Member member, string text)
|
||||
=> SendPrivateMessage(member?.UserId, text);
|
||||
/// <summary> Sends a private message to the provided user. </summary>
|
||||
public Task<Message[]> SendPrivateMessage(User user, string text)
|
||||
=> SendPrivateMessage(user?.Id, text);
|
||||
/// <summary> Sends a private message to the provided user. </summary>
|
||||
public async Task<Message[]> SendPrivateMessage(string userId, string text)
|
||||
/// <summary> Sends a text-to-speech message to the provided channel. To include a mention, see the Mention static helper class. </summary>
|
||||
public Task<Message> SendTTSMessage(Channel channel, string text)
|
||||
{
|
||||
var channel = await CreatePMChannel(userId).ConfigureAwait(false);
|
||||
return await SendMessage(channel, text, new string[0]).ConfigureAwait(false);
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
CheckReady();
|
||||
|
||||
return SendMessage(channel, text, false);
|
||||
}
|
||||
/// <summary> Sends a private message to the provided user. </summary>
|
||||
public async Task<Message> SendPrivateMessage(Member member, string text)
|
||||
{
|
||||
if (member == null) throw new ArgumentNullException(nameof(member));
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
CheckReady();
|
||||
|
||||
var channel = await CreatePMChannel(member).ConfigureAwait(false);
|
||||
return await SendMessage(channel, text).ConfigureAwait(false);
|
||||
}
|
||||
private async Task<Message> SendMessage(Channel channel, string text, bool isTextToSpeech)
|
||||
{
|
||||
Message msg;
|
||||
var userIds = !channel.IsPrivate ? Mention.GetUserIds(text) : new string[0];
|
||||
if (Config.UseMessageQueue)
|
||||
{
|
||||
var nonce = GenerateNonce();
|
||||
msg = _messages.GetOrAdd("nonce_" + nonce, channel.Id, _userId);
|
||||
var currentUser = msg.Member;
|
||||
msg.Update(new MessageInfo
|
||||
{
|
||||
Content = text,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Author = new UserReference { Avatar = currentUser.AvatarId, Discriminator = currentUser.Discriminator, Id = _userId, Username = currentUser.Name },
|
||||
ChannelId = channel.Id,
|
||||
IsTextToSpeech = isTextToSpeech
|
||||
});
|
||||
msg.Mentions = userIds.Select(x => _members[x, channel.Server.Id]).Where(x => x != null).ToArray();
|
||||
msg.IsQueued = true;
|
||||
msg.Nonce = nonce;
|
||||
_pendingMessages.Enqueue(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
var model = await _api.SendMessage(channel.Id, text, userIds, null, isTextToSpeech).ConfigureAwait(false);
|
||||
msg = _messages.GetOrAdd(model.Id, channel.Id, model.Author.Id);
|
||||
msg.Update(model);
|
||||
RaiseMessageSent(msg);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Sends a file to the provided channel. </summary>
|
||||
public Task SendFile(Channel channel, string filePath)
|
||||
=> SendFile(channel?.Id, filePath);
|
||||
/// <summary> Sends a file to the provided channel. </summary>
|
||||
public Task SendFile(string channelId, string filePath)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
if (filePath == null) throw new ArgumentNullException(nameof(filePath));
|
||||
CheckReady();
|
||||
|
||||
return _api.SendFile(channelId, filePath);
|
||||
return _api.SendFile(channel.Id, filePath);
|
||||
}
|
||||
|
||||
/// <summary> Edits the provided message, changing only non-null attributes. </summary>
|
||||
/// <remarks> While not required, it is recommended to include a mention reference in the text (see Mention.User). </remarks>
|
||||
public Task EditMessage(Message message, string text = null, IEnumerable<object> mentionedUsers = null)
|
||||
=> EditMessage(message?.ChannelId, message?.Id, text, mentionedUsers);
|
||||
/// <summary> Edits the provided message, changing only non-null attributes. </summary>
|
||||
/// <remarks> While not required, it is recommended to include a mention reference in the text (see Mention.User). </remarks>
|
||||
public Task EditMessage(Channel channel, string messageId, string text = null, IEnumerable<object> mentionedUsers = null)
|
||||
=> EditMessage(channel?.Id, messageId, text, mentionedUsers);
|
||||
/// <summary> Edits the provided message, changing only non-null attributes. </summary>
|
||||
/// <remarks> While not required, it is recommended to include a mention reference in the text (see Mention.User). </remarks>
|
||||
public async Task EditMessage(string channelId, string messageId, string text = null, IEnumerable<object> mentionedUsers = null)
|
||||
public async Task EditMessage(Message message, string text)
|
||||
{
|
||||
if (message == null) throw new ArgumentNullException(nameof(message));
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (messageId == null) throw new ArgumentNullException(nameof(messageId));
|
||||
var mentionedUserIds = CollectionHelper.FlattenUsers(mentionedUsers);
|
||||
|
||||
if (text != null && text.Length > MaxMessageSize)
|
||||
text = text.Substring(0, MaxMessageSize);
|
||||
|
||||
var model = await _api.EditMessage(messageId, channelId, text, mentionedUserIds).ConfigureAwait(false);
|
||||
var msg = _messages[messageId];
|
||||
if (msg != null)
|
||||
msg.Update(model);
|
||||
var model = await _api.EditMessage(message.Id, message.Channel.Id, text, Mention.GetUserIds(text)).ConfigureAwait(false);
|
||||
message.Update(model);
|
||||
}
|
||||
|
||||
/// <summary> Deletes the provided message. </summary>
|
||||
public Task DeleteMessage(Message msg)
|
||||
=> DeleteMessage(msg?.ChannelId, msg?.Id);
|
||||
/// <summary> Deletes the provided message. </summary>
|
||||
public async Task DeleteMessage(string channelId, string msgId)
|
||||
public async Task DeleteMessage(Message message)
|
||||
{
|
||||
if (message == null) throw new ArgumentNullException(nameof(message));
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (msgId == null) throw new ArgumentNullException(nameof(msgId));
|
||||
|
||||
try
|
||||
{
|
||||
await _api.DeleteMessage(msgId, channelId).ConfigureAwait(false);
|
||||
_messages.TryRemove(msgId);
|
||||
await _api.DeleteMessage(message.Id, message.Channel.Id).ConfigureAwait(false);
|
||||
_messages.TryRemove(message.Id);
|
||||
}
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
}
|
||||
public async Task DeleteMessages(IEnumerable<Message> msgs)
|
||||
public async Task DeleteMessages(IEnumerable<Message> messages)
|
||||
{
|
||||
if (messages == null) throw new ArgumentNullException(nameof(messages));
|
||||
CheckReady();
|
||||
if (msgs == null) throw new ArgumentNullException(nameof(msgs));
|
||||
|
||||
foreach (var msg in msgs)
|
||||
foreach (var message in messages)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _api.DeleteMessage(msg.Id, msg.ChannelId).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
}
|
||||
}
|
||||
public async Task DeleteMessages(string channelId, IEnumerable<string> msgIds)
|
||||
{
|
||||
CheckReady();
|
||||
if (msgIds == null) throw new ArgumentNullException(nameof(msgIds));
|
||||
|
||||
foreach (var msgId in msgIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _api.DeleteMessage(msgId, channelId).ConfigureAwait(false);
|
||||
await _api.DeleteMessage(message.Id, message.Channel.Id).ConfigureAwait(false);
|
||||
_messages.TryRemove(message.Id);
|
||||
}
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Downloads last count messages from the server, starting at beforeMessageId if it's provided. </summary>
|
||||
public Task<Message[]> DownloadMessages(Channel channel, int count, string beforeMessageId = null, bool cache = true)
|
||||
=> DownloadMessages(channel.Id, count, beforeMessageId, cache);
|
||||
/// <summary> Downloads last count messages from the server, starting at beforeMessageId if it's provided. </summary>
|
||||
public async Task<Message[]> DownloadMessages(string channelId, int count, string beforeMessageId = null, bool cache = true)
|
||||
public async Task<Message[]> DownloadMessages(Channel channel, int count, string beforeMessageId = null, bool cache = true)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
if (count < 0) throw new ArgumentNullException(nameof(count));
|
||||
if (count == 0) return new Message[0];
|
||||
CheckReady();
|
||||
|
||||
Channel channel = _channels[channelId];
|
||||
if (count == 0) return new Message[0];
|
||||
if (channel != null && channel.Type == ChannelTypes.Text)
|
||||
{
|
||||
try
|
||||
@@ -283,7 +240,7 @@ namespace Discord
|
||||
SendMessageResponse response = null;
|
||||
try
|
||||
{
|
||||
response = await _api.SendMessage(msg.ChannelId, msg.RawText, msg.MentionIds, msg.Nonce, msg.IsTTS).ConfigureAwait(false);
|
||||
response = await _api.SendMessage(msg.Channel.Id, msg.RawText, msg.MentionIds, msg.Nonce, msg.IsTTS).ConfigureAwait(false);
|
||||
}
|
||||
catch (WebException) { break; }
|
||||
catch (HttpException) { hasFailed = true; }
|
||||
|
||||
@@ -8,47 +8,13 @@ namespace Discord
|
||||
public partial class DiscordClient
|
||||
{
|
||||
public Task SetChannelUserPermissions(Channel channel, Member member, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(channel, member?.UserId, PermissionTarget.Member, allow, deny);
|
||||
public Task SetChannelUserPermissions(string channelId, Member member, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(_channels[channelId], member?.UserId, PermissionTarget.Member, allow, deny);
|
||||
public Task SetChannelUserPermissions(Channel channel, User user, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(channel, user?.Id, PermissionTarget.Member, allow, deny);
|
||||
public Task SetChannelUserPermissions(string channelId, User user, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(_channels[channelId], user?.Id, PermissionTarget.Member, allow, deny);
|
||||
public Task SetChannelUserPermissions(Channel channel, string userId, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(channel, userId, PermissionTarget.Member, allow, deny);
|
||||
public Task SetChannelUserPermissions(string channelId, string userId, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(_channels[channelId], userId, PermissionTarget.Member, allow, deny);
|
||||
=> SetChannelPermissions(channel, member?.Id, PermissionTarget.Member, allow, deny);
|
||||
public Task SetChannelUserPermissions(Channel channel, Member member, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(channel, member?.UserId, PermissionTarget.Member, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelUserPermissions(string channelId, Member member, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(_channels[channelId], member?.UserId, PermissionTarget.Member, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelUserPermissions(Channel channel, User user, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(channel, user?.Id, PermissionTarget.Member, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelUserPermissions(string channelId, User user, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(_channels[channelId], user?.Id, PermissionTarget.Member, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelUserPermissions(Channel channel, string userId, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(channel, userId, PermissionTarget.Member, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelUserPermissions(string channelId, string userId, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(_channels[channelId], userId, PermissionTarget.Member, permissions?.Allow, permissions?.Deny);
|
||||
|
||||
=> SetChannelPermissions(channel, member?.Id, PermissionTarget.Member, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelRolePermissions(Channel channel, Role role, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(channel, role?.Id, PermissionTarget.Role, allow, deny);
|
||||
public Task SetChannelRolePermissions(string channelId, Role role, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(_channels[channelId], role?.Id, PermissionTarget.Role, allow, deny);
|
||||
public Task SetChannelRolePermissions(Channel channel, string userId, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(channel, userId, PermissionTarget.Role, allow, deny);
|
||||
public Task SetChannelRolePermissions(string channelId, string userId, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
=> SetChannelPermissions(_channels[channelId], userId, PermissionTarget.Role, allow, deny);
|
||||
public Task SetChannelRolePermissions(Channel channel, Role role, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(channel, role?.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelRolePermissions(string channelId, Role role, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(_channels[channelId], role?.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelRolePermissions(Channel channel, string userId, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(channel, userId, PermissionTarget.Role, permissions?.Allow, permissions?.Deny);
|
||||
public Task SetChannelRolePermissions(string channelId, string userId, DualChannelPermissions permissions = null)
|
||||
=> SetChannelPermissions(_channels[channelId], userId, PermissionTarget.Role, permissions?.Allow, permissions?.Deny);
|
||||
|
||||
private async Task SetChannelPermissions(Channel channel, string targetId, string targetType, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||
{
|
||||
CheckReady();
|
||||
@@ -103,34 +69,23 @@ namespace Discord
|
||||
}
|
||||
|
||||
public Task RemoveChannelUserPermissions(Channel channel, Member member)
|
||||
=> RemoveChannelPermissions(channel, member?.UserId, PermissionTarget.Member);
|
||||
public Task RemoveChannelUserPermissions(string channelId, Member member)
|
||||
=> RemoveChannelPermissions(_channels[channelId], member?.UserId, PermissionTarget.Member);
|
||||
public Task RemoveChannelUserPermissions(Channel channel, User user)
|
||||
=> RemoveChannelPermissions(channel, user?.Id, PermissionTarget.Member);
|
||||
public Task RemoveChannelUserPermissions(string channelId, User user)
|
||||
=> RemoveChannelPermissions(_channels[channelId], user?.Id, PermissionTarget.Member);
|
||||
public Task RemoveChannelUserPermissions(Channel channel, string userId)
|
||||
=> RemoveChannelPermissions(channel, userId, PermissionTarget.Member);
|
||||
public Task RemoveChannelUserPermissions(string channelId, string userId)
|
||||
=> RemoveChannelPermissions(_channels[channelId], userId, PermissionTarget.Member);
|
||||
{
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
if (member == null) throw new ArgumentNullException(nameof(member));
|
||||
CheckReady();
|
||||
|
||||
return RemoveChannelPermissions(channel, member?.Id, PermissionTarget.Member);
|
||||
}
|
||||
public Task RemoveChannelRolePermissions(Channel channel, Role role)
|
||||
=> RemoveChannelPermissions(channel, role?.Id, PermissionTarget.Role);
|
||||
public Task RemoveChannelRolePermissions(string channelId, Role role)
|
||||
=> RemoveChannelPermissions(_channels[channelId], role?.Id, PermissionTarget.Role);
|
||||
public Task RemoveChannelRolePermissions(Channel channel, string roleId)
|
||||
=> RemoveChannelPermissions(channel, roleId, PermissionTarget.Role);
|
||||
public Task RemoveChannelRolePermissions(string channelId, string roleId)
|
||||
=> RemoveChannelPermissions(_channels[channelId], roleId, PermissionTarget.Role);
|
||||
{
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||
CheckReady();
|
||||
|
||||
return RemoveChannelPermissions(channel, role?.Id, PermissionTarget.Role);
|
||||
}
|
||||
private async Task RemoveChannelPermissions(Channel channel, string userOrRoleId, string idType)
|
||||
{
|
||||
CheckReady();
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
if (userOrRoleId == null) throw new ArgumentNullException(nameof(userOrRoleId));
|
||||
if (idType == null) throw new ArgumentNullException(nameof(idType));
|
||||
|
||||
try
|
||||
{
|
||||
var perms = channel.PermissionOverwrites.Where(x => x.TargetType != idType || x.TargetId != userOrRoleId).FirstOrDefault();
|
||||
|
||||
@@ -33,9 +33,7 @@ namespace Discord
|
||||
public class RoleEventArgs : EventArgs
|
||||
{
|
||||
public Role Role { get; }
|
||||
public string RoleId => Role.Id;
|
||||
public Server Server => Role.Server;
|
||||
public string ServerId => Role.ServerId;
|
||||
|
||||
internal RoleEventArgs(Role role) { Role = role; }
|
||||
}
|
||||
@@ -68,23 +66,20 @@ namespace Discord
|
||||
public Role GetRole(string id) => _roles[id];
|
||||
/// <summary> Returns all roles with the specified server and name. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks>
|
||||
public IEnumerable<Role> FindRoles(Server server, string name) => FindRoles(server?.Id, name);
|
||||
/// <summary> Returns all roles with the specified server and name. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks>
|
||||
public IEnumerable<Role> FindRoles(string serverId, string name)
|
||||
public IEnumerable<Role> FindRoles(Server server, string name)
|
||||
{
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
if (name.StartsWith("@"))
|
||||
{
|
||||
string name2 = name.Substring(1);
|
||||
return _roles.Where(x => x.ServerId == serverId &&
|
||||
return _roles.Where(x => x.Server.Id == server.Id &&
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) || string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
return _roles.Where(x => x.ServerId == serverId &&
|
||||
return _roles.Where(x => x.Server.Id == server.Id &&
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
@@ -106,16 +101,14 @@ namespace Discord
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
public Task EditRole(string roleId, string name = null, ServerPermissions permissions = null, Color color = null, bool? hoist = null, int? position = null)
|
||||
=> EditRole(_roles[roleId], name: name, permissions: permissions, color: color, hoist: hoist, position: position);
|
||||
|
||||
public async Task EditRole(Role role, string name = null, ServerPermissions permissions = null, Color color = null, bool? hoist = null, int? position = null)
|
||||
{
|
||||
CheckReady();
|
||||
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||
CheckReady();
|
||||
|
||||
//TODO: check this null workaround later, should be fixed on Discord's end soon
|
||||
var response = await _api.EditRole(role.ServerId, role.Id,
|
||||
var response = await _api.EditRole(role.Server.Id, role.Id,
|
||||
name: name ?? role.Name,
|
||||
permissions: permissions?.RawValue ?? role.Permissions.RawValue,
|
||||
color: color?.RawValue,
|
||||
@@ -142,40 +135,26 @@ namespace Discord
|
||||
roles[i] = roles[i - 1];
|
||||
roles[newPos] = role;
|
||||
}
|
||||
await _api.ReorderRoles(role.ServerId, roles.Skip(minPos).Select(x => x.Id), minPos);
|
||||
await _api.ReorderRoles(role.Server.Id, roles.Skip(minPos).Select(x => x.Id), minPos);
|
||||
}
|
||||
}
|
||||
|
||||
public Task DeleteRole(Role role)
|
||||
=> DeleteRole(role?.ServerId, role?.Id);
|
||||
public Task DeleteRole(string serverId, string roleId)
|
||||
{
|
||||
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (roleId == null) throw new ArgumentNullException(nameof(roleId));
|
||||
|
||||
return _api.DeleteRole(serverId, roleId);
|
||||
return _api.DeleteRole(role.Server.Id, role.Id);
|
||||
}
|
||||
|
||||
public Task ReorderRoles(Server server, IEnumerable<object> roles, int startPos = 0)
|
||||
=> ReorderChannels(server.Id, roles, startPos);
|
||||
public Task ReorderRoles(string serverId, IEnumerable<object> roles, int startPos = 0)
|
||||
public Task ReorderRoles(Server server, IEnumerable<Role> roles, int startPos = 0)
|
||||
{
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
if (roles == null) throw new ArgumentNullException(nameof(roles));
|
||||
if (startPos < 0) throw new ArgumentOutOfRangeException(nameof(startPos), "startPos must be a positive integer.");
|
||||
CheckReady();
|
||||
|
||||
var roleIds = roles.Select(x =>
|
||||
{
|
||||
if (x is string)
|
||||
return x as string;
|
||||
else if (x is Role)
|
||||
return (x as Role).Id;
|
||||
else
|
||||
throw new ArgumentException("Channels must be a collection of string or Role.", nameof(roles));
|
||||
});
|
||||
|
||||
return _api.ReorderRoles(serverId, roleIds, startPos);
|
||||
return _api.ReorderRoles(server.Id, roles.Select(x => x.Id), startPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,20 +6,12 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
internal sealed class Users : AsyncCollection<User>
|
||||
internal sealed class Users : AsyncCollection<GlobalUser>
|
||||
{
|
||||
public Users(DiscordClient client, object writerLock)
|
||||
: base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { }
|
||||
|
||||
public User GetOrAdd(string id) => GetOrAdd(id, () => new User(_client, id));
|
||||
}
|
||||
|
||||
public sealed class UserEventArgs : EventArgs
|
||||
{
|
||||
public User User { get; }
|
||||
public string UserId => User.Id;
|
||||
|
||||
internal UserEventArgs(User user) { User = user; }
|
||||
public GlobalUser GetOrAdd(string id) => GetOrAdd(id, () => new GlobalUser(_client, id));
|
||||
}
|
||||
|
||||
public partial class DiscordClient
|
||||
@@ -36,11 +28,11 @@ namespace Discord
|
||||
if (UserRemoved != null)
|
||||
RaiseEvent(nameof(UserRemoved), () => UserRemoved(this, new MemberEventArgs(member)));
|
||||
}
|
||||
public event EventHandler<UserEventArgs> UserUpdated;
|
||||
private void RaiseUserUpdated(User user)
|
||||
public event EventHandler ProfileUpdated;
|
||||
private void RaiseProfileUpdated(GlobalUser user)
|
||||
{
|
||||
if (UserUpdated != null)
|
||||
RaiseEvent(nameof(UserUpdated), () => UserUpdated(this, new UserEventArgs(user)));
|
||||
if (ProfileUpdated != null)
|
||||
RaiseEvent(nameof(ProfileUpdated), () => ProfileUpdated(this, EventArgs.Empty));
|
||||
}
|
||||
public event EventHandler<MemberEventArgs> MemberUpdated;
|
||||
private void RaiseMemberUpdated(Member member)
|
||||
@@ -65,55 +57,14 @@ namespace Discord
|
||||
internal Users Users => _users;
|
||||
private readonly Users _users;
|
||||
|
||||
/// <summary> Returns the current logged-in user. </summary>
|
||||
public User CurrentUser => _currentUser;
|
||||
private User _currentUser;
|
||||
|
||||
/// <summary> Returns the user with the specified id, or null if none was found. </summary>
|
||||
public User GetUser(string id) => _users[id];
|
||||
/// <summary> Returns the user with the specified name and discriminator, or null if none was found. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks>
|
||||
public User GetUser(string username, string discriminator)
|
||||
{
|
||||
if (username == null) throw new ArgumentNullException(nameof(username));
|
||||
if (discriminator == null) throw new ArgumentNullException(nameof(discriminator));
|
||||
|
||||
if (username.StartsWith("@"))
|
||||
username = username.Substring(1);
|
||||
|
||||
return _users.Where(x =>
|
||||
string.Equals(x.Name, username, StringComparison.OrdinalIgnoreCase) &&
|
||||
x.Discriminator == discriminator
|
||||
)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary> Returns all users with the specified name across all servers. </summary>
|
||||
/// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks>
|
||||
public IEnumerable<User> FindUsers(string name)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
if (name.StartsWith("@"))
|
||||
{
|
||||
string name2 = name.Substring(1);
|
||||
return _users.Where(x =>
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) || string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
return _users.Where(x =>
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
public Task<EditUserResponse> EditProfile(string currentPassword = "",
|
||||
string username = null, string email = null, string password = null,
|
||||
ImageType avatarType = ImageType.Png, byte[] avatar = null)
|
||||
{
|
||||
if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword));
|
||||
|
||||
return _api.EditUser(currentPassword: currentPassword, username: username ?? _currentUser?.Name, email: email ?? _currentUser?.Email, password: password,
|
||||
return _api.EditUser(currentPassword: currentPassword,
|
||||
username: username ?? _currentUser?.Name, email: email ?? _currentUser?.GlobalUser.Email, password: password,
|
||||
avatarType: avatarType, avatar: avatar);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,18 +47,13 @@ namespace Discord
|
||||
return client;
|
||||
}
|
||||
|
||||
public Task<IDiscordVoiceClient> JoinVoiceServer(Channel channel)
|
||||
=> JoinVoiceServer(channel?.ServerId, channel?.Id);
|
||||
public Task<IDiscordVoiceClient> JoinVoiceServer(Server server, string channelId)
|
||||
=> JoinVoiceServer(server?.Id, channelId);
|
||||
public async Task<IDiscordVoiceClient> JoinVoiceServer(string serverId, string channelId)
|
||||
public async Task<IDiscordVoiceClient> JoinVoiceServer(Channel channel)
|
||||
{
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
CheckReady(); //checkVoice is done inside the voice client
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
|
||||
var client = await CreateVoiceClient(serverId).ConfigureAwait(false);
|
||||
await client.JoinChannel(channelId).ConfigureAwait(false);
|
||||
var client = await CreateVoiceClient(channel.Server.Id).ConfigureAwait(false);
|
||||
await client.JoinChannel(channel.Id).ConfigureAwait(false);
|
||||
return client;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ using System.Threading.Tasks;
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary> Provides a connection to the DiscordApp service. </summary>
|
||||
public partial class DiscordClient : DiscordWSClient
|
||||
public sealed partial class DiscordClient : DiscordWSClient
|
||||
{
|
||||
protected readonly DiscordAPIClient _api;
|
||||
private readonly DiscordAPIClient _api;
|
||||
private readonly Random _rand;
|
||||
private readonly JsonSerializer _serializer;
|
||||
private readonly ConcurrentQueue<Message> _pendingMessages;
|
||||
@@ -24,6 +24,9 @@ namespace Discord
|
||||
|
||||
public new DiscordClientConfig Config => _config as DiscordClientConfig;
|
||||
|
||||
/// <summary> Returns the current logged-in user. </summary>
|
||||
public Member CurrentUser => _currentUser;
|
||||
|
||||
/// <summary> Initializes a new instance of the DiscordClient class. </summary>
|
||||
public DiscordClient(DiscordClientConfig config = null)
|
||||
: base(config ?? new DiscordClientConfig())
|
||||
@@ -69,71 +72,66 @@ namespace Discord
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool showIDs = _config.LogLevel > LogMessageSeverity.Debug; //Hide this for now
|
||||
|
||||
if (_config.LogLevel >= LogMessageSeverity.Info)
|
||||
{
|
||||
ServerCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Created Server: {e.Server?.Name ?? e.ServerId}");
|
||||
$"Created Server: {e.Server?.Name}");
|
||||
ServerDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Destroyed Server: {e.Server?.Name ?? e.ServerId}");
|
||||
$"Destroyed Server: {e.Server?.Name}");
|
||||
ServerUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Updated Server: {e.Server?.Name ?? e.ServerId}");
|
||||
$"Updated Server: {e.Server?.Name}");
|
||||
ServerAvailable += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Server Available: {e.Server?.Name ?? e.ServerId}");
|
||||
$"Server Available: {e.Server?.Name}");
|
||||
ServerUnavailable += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Server Unavailable: {e.Server?.Name ?? e.ServerId}");
|
||||
$"Server Unavailable: {e.Server?.Name}");
|
||||
ChannelCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Created Channel: {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}");
|
||||
$"Created Channel: {e.Server?.Name}/{e.Channel?.Name}");
|
||||
ChannelDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Destroyed Channel: {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}");
|
||||
$"Destroyed Channel: {e.Server?.Name}/{e.Channel?.Name}");
|
||||
ChannelUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Updated Channel: {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}");
|
||||
$"Updated Channel: {e.Server?.Name}/{e.Channel?.Name}");
|
||||
MessageCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Created Message: {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}/{e.MessageId}");
|
||||
$"Created Message: {e.Server?.Name}/{e.Channel?.Name}/{e.Message?.Id}");
|
||||
MessageDeleted += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Deleted Message: {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}/{e.MessageId}");
|
||||
$"Deleted Message: {e.Server?.Name}/{e.Channel?.Name}/{e.Message?.Id}");
|
||||
MessageUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Updated Message: {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}/{e.MessageId}");
|
||||
$"Updated Message: {e.Server?.Name}/{e.Channel?.Name}/{e.Message?.Id}");
|
||||
RoleCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Created Role: {e.Server?.Name ?? e.ServerId}/{e.Role?.Name ?? e.RoleId}");
|
||||
$"Created Role: {e.Server?.Name}/{e.Role?.Name}");
|
||||
RoleUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Updated Role: {e.Server?.Name ?? e.ServerId}/{e.Role?.Name ?? e.RoleId}");
|
||||
$"Updated Role: {e.Server?.Name}/{e.Role?.Name}");
|
||||
RoleDeleted += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Deleted Role: {e.Server?.Name ?? e.ServerId}/{e.Role?.Name ?? e.RoleId}");
|
||||
$"Deleted Role: {e.Server?.Name}/{e.Role?.Name}");
|
||||
BanAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Added Ban: {e.Server?.Name ?? e.ServerId}/{e.User?.Name ?? e.UserId}");
|
||||
$"Added Ban: {e.Server?.Name }/{e.UserId}");
|
||||
BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Removed Ban: {e.Server?.Name ?? e.ServerId}/{e.User?.Name ?? e.UserId}");
|
||||
$"Removed Ban: {e.Server?.Name}/{e.UserId}");
|
||||
UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Added Member: {e.Server?.Name ?? e.ServerId}/{e.User?.Name ?? e.UserId}");
|
||||
$"Added Member: {e.Server?.Name}/{e.UserId}");
|
||||
UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Removed Member: {e.Server?.Name ?? e.ServerId}/{e.User?.Name ?? e.UserId}");
|
||||
$"Removed Member: {e.Server?.Name}/{e.UserId}");
|
||||
MemberUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Updated Member: {e.Server?.Name ?? e.ServerId}/{e.User?.Name ?? e.UserId}");
|
||||
$"Updated Member: {e.Server?.Name}/{e.UserId}");
|
||||
UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Updated Member (Voice State): {e.Server?.Name ?? e.ServerId}/{e.User?.Name ?? e.UserId}");
|
||||
UserUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
$"Updated User: {e.User.Name}");
|
||||
$"Updated Member (Voice State): {e.Server?.Name}/{e.UserId}");
|
||||
ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
|
||||
"Updated Profile");
|
||||
}
|
||||
if (_config.LogLevel >= LogMessageSeverity.Verbose)
|
||||
{
|
||||
UserIsTyping += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client,
|
||||
$"Updated User (Is Typing): {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}/{e.User?.Name ?? e.UserId}" +
|
||||
(showIDs ? $" ({e.ServerId}/{e.ChannelId}/{e.UserId})" : ""));
|
||||
$"Updated User (Is Typing): {e.Server?.Name}/{e.Channel?.Name}/{e.Member?.Name}");
|
||||
MessageReadRemotely += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client,
|
||||
$"Read Message (Remotely): {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}/{e.MessageId}" +
|
||||
(showIDs ? $" ({e.ServerId}/{e.ChannelId}/{e.MessageId})" : ""));
|
||||
$"Read Message (Remotely): {e.Server?.Name}/{e.Channel?.Name}/{e.Message?.Id}");
|
||||
MessageSent += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client,
|
||||
$"Sent Message: {e.Server?.Name ?? e.ServerId}/{e.Channel?.Name ?? e.ChannelId}/{e.MessageId}" +
|
||||
(showIDs ? $" ({e.ServerId}/{e.ChannelId}/{e.MessageId})" : ""));
|
||||
$"Sent Message: {e.Server?.Name}/{e.Channel?.Name}/{e.Message?.Id}");
|
||||
UserPresenceUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Client,
|
||||
$"Updated Member (Presence): {e.Server?.Name ?? e.ServerId}/{e.User?.Name ?? e.UserId}" +
|
||||
(showIDs ? $" ({e.ServerId}/{e.UserId})" : ""));
|
||||
$"Updated Member (Presence): {e.Server?.Name}/{e.Member?.Name}");
|
||||
|
||||
_api.RestClient.OnRequest += (s, e) =>
|
||||
{
|
||||
if (e.Payload != null)
|
||||
if (e.Payload != null)
|
||||
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Rest, $"{e.Method.Method} {e.Path}: {Math.Round(e.ElapsedMilliseconds, 2)} ms ({e.Payload})");
|
||||
else
|
||||
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Rest, $"{e.Method.Method} {e.Path}: {Math.Round(e.ElapsedMilliseconds, 2)} ms");
|
||||
@@ -141,18 +139,18 @@ namespace Discord
|
||||
}
|
||||
if (_config.LogLevel >= LogMessageSeverity.Debug)
|
||||
{
|
||||
_channels.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Channel {e.Item.ServerId}/{e.Item.Id}");
|
||||
_channels.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Channel {e.Item.ServerId}/{e.Item.Id}");
|
||||
_channels.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Channel {e.Item.Server.Id}/{e.Item.Id}");
|
||||
_channels.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Channel {e.Item.Server.Id}/{e.Item.Id}");
|
||||
_channels.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Channels");
|
||||
_members.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Member {e.Item.ServerId}/{e.Item.UserId}");
|
||||
_members.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Member {e.Item.ServerId}/{e.Item.UserId}");
|
||||
_members.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Member {e.Item.Server.Id}/{e.Item.Id}");
|
||||
_members.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Member {e.Item.Server.Id}/{e.Item.Id}");
|
||||
_members.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Members");
|
||||
_messages.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Message {e.Item.ServerId}/{e.Item.ChannelId}/{e.Item.Id}");
|
||||
_messages.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Message {e.Item.ServerId}/{e.Item.ChannelId}/{e.Item.Id}");
|
||||
_messages.ItemRemapped += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Remapped Message {e.Item.ServerId}/{e.Item.ChannelId}/[{e.OldId} -> {e.NewId}]");
|
||||
_messages.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Message {e.Item.Server.Id}/{e.Item.Channel.Id}/{e.Item.Id}");
|
||||
_messages.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Message {e.Item.Server.Id}/{e.Item.Channel.Id}/{e.Item.Id}");
|
||||
_messages.ItemRemapped += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Remapped Message {e.Item.Server.Id}/{e.Item.Channel.Id}/[{e.OldId} -> {e.NewId}]");
|
||||
_messages.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Messages");
|
||||
_roles.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Role {e.Item.ServerId}/{e.Item.Id}");
|
||||
_roles.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Role {e.Item.ServerId}/{e.Item.Id}");
|
||||
_roles.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Role {e.Item.Server.Id}/{e.Item.Id}");
|
||||
_roles.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Role {e.Item.Server.Id}/{e.Item.Id}");
|
||||
_roles.Cleared += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Cleared Roles");
|
||||
_servers.ItemCreated += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Created Server {e.Item.Id}");
|
||||
_servers.ItemDestroyed += (s, e) => RaiseOnLog(LogMessageSeverity.Debug, LogMessageSource.Cache, $"Destroyed Server {e.Item.Id}");
|
||||
@@ -295,7 +293,7 @@ namespace Discord
|
||||
case "READY": //Resync
|
||||
{
|
||||
var data = e.Payload.ToObject<ReadyEvent>(_serializer);
|
||||
_currentUser = _users.GetOrAdd(data.User.Id);
|
||||
_currentUser = _members.GetOrAdd(data.User.Id, _servers.PMServer.Id);
|
||||
_currentUser.Update(data.User);
|
||||
foreach (var model in data.Guilds)
|
||||
{
|
||||
@@ -307,7 +305,7 @@ namespace Discord
|
||||
}
|
||||
foreach (var model in data.PrivateChannels)
|
||||
{
|
||||
var user = _users.GetOrAdd(model.Recipient.Id);
|
||||
var user = _members.GetOrAdd(model.Recipient.Id, _servers.PMServer.Id);
|
||||
user.Update(model.Recipient);
|
||||
var channel = _channels.GetOrAdd(model.Id, null, user.Id);
|
||||
channel.Update(model);
|
||||
@@ -362,9 +360,9 @@ namespace Discord
|
||||
Channel channel;
|
||||
if (data.IsPrivate)
|
||||
{
|
||||
var user = _users.GetOrAdd(data.Recipient.Id);
|
||||
user.Update(data.Recipient);
|
||||
channel = _channels.GetOrAdd(data.Id, null, user.Id);
|
||||
var member = _members.GetOrAdd(data.Recipient.Id, _servers.PMServer.Id);
|
||||
member.Update(data.Recipient);
|
||||
channel = _channels.GetOrAdd(data.Id, null, member.Id);
|
||||
}
|
||||
else
|
||||
channel = _channels.GetOrAdd(data.Id, data.GuildId, null);
|
||||
@@ -396,8 +394,6 @@ namespace Discord
|
||||
case "GUILD_MEMBER_ADD":
|
||||
{
|
||||
var data = e.Payload.ToObject<MemberAddEvent>(_serializer);
|
||||
var user = _users.GetOrAdd(data.User.Id);
|
||||
user.Update(data.User);
|
||||
var member = _members.GetOrAdd(data.User.Id, data.GuildId);
|
||||
member.Update(data);
|
||||
if (Config.TrackActivity)
|
||||
@@ -433,7 +429,7 @@ namespace Discord
|
||||
role.Update(data.Data);
|
||||
var server = _servers[data.GuildId];
|
||||
if (server != null)
|
||||
server.AddRole(data.Data.Id);
|
||||
server.AddRole(role);
|
||||
RaiseRoleUpdated(role);
|
||||
}
|
||||
break;
|
||||
@@ -442,19 +438,23 @@ namespace Discord
|
||||
var data = e.Payload.ToObject<RoleUpdateEvent>(_serializer);
|
||||
var role = _roles[data.Data.Id];
|
||||
if (role != null)
|
||||
{
|
||||
role.Update(data.Data);
|
||||
RaiseRoleUpdated(role);
|
||||
RaiseRoleUpdated(role);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "GUILD_ROLE_DELETE":
|
||||
{
|
||||
var data = e.Payload.ToObject<RoleDeleteEvent>(_serializer);
|
||||
var server = _servers[data.GuildId];
|
||||
if (server != null)
|
||||
server.RemoveRole(data.RoleId);
|
||||
var role = _roles.TryRemove(data.RoleId);
|
||||
if (role != null)
|
||||
{
|
||||
RaiseRoleDeleted(role);
|
||||
var server = _servers[data.GuildId];
|
||||
if (server != null)
|
||||
server.RemoveRole(role);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -485,7 +485,7 @@ namespace Discord
|
||||
var data = e.Payload.ToObject<MessageCreateEvent>(_serializer);
|
||||
Message msg = null;
|
||||
|
||||
bool isAuthor = data.Author.Id == CurrentUserId;
|
||||
bool isAuthor = data.Author.Id == _userId;
|
||||
bool hasFinishedSending = false;
|
||||
if (Config.UseMessageQueue && isAuthor && data.Nonce != null)
|
||||
{
|
||||
@@ -566,7 +566,7 @@ namespace Discord
|
||||
var channel = _channels[data.ChannelId];
|
||||
if (channel != null)
|
||||
{
|
||||
var user = _members[data.UserId, channel.ServerId];
|
||||
var user = _members[data.UserId, channel.Server.Id];
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
@@ -577,7 +577,7 @@ namespace Discord
|
||||
{
|
||||
if (!channel.IsPrivate)
|
||||
{
|
||||
var member = _members[data.UserId, channel.ServerId];
|
||||
var member = _members[data.UserId, channel.Server.Id];
|
||||
if (member != null)
|
||||
member.UpdateActivity();
|
||||
}
|
||||
@@ -612,7 +612,7 @@ namespace Discord
|
||||
if (user != null)
|
||||
{
|
||||
user.Update(data);
|
||||
RaiseUserUpdated(user);
|
||||
RaiseProfileUpdated(user);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Discord
|
||||
|
||||
await _voiceSocket.SetChannel(_voiceServerId, channelId).ConfigureAwait(false);
|
||||
_dataSocket.SendJoinVoice(_voiceServerId, channelId);
|
||||
await _voiceSocket.WaitForConnection(_config.ConnectionTimeout);
|
||||
await _voiceSocket.WaitForConnection(_config.ConnectionTimeout).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary> Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer. </summary>
|
||||
@@ -25,12 +25,12 @@ namespace Discord
|
||||
/// <param name="count">Number of bytes in this frame. </param>
|
||||
void IDiscordVoiceClient.SendVoicePCM(byte[] data, int count)
|
||||
{
|
||||
CheckReady(checkVoice: true);
|
||||
if (data == null) throw new ArgumentException(nameof(data));
|
||||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
if (count == 0) return;
|
||||
|
||||
_voiceSocket.SendPCMFrames(data, count);
|
||||
CheckReady(checkVoice: true);
|
||||
|
||||
if (count != 0)
|
||||
_voiceSocket.SendPCMFrames(data, count);
|
||||
}
|
||||
/// <summary> Clears the PCM buffer. </summary>
|
||||
void IDiscordVoiceClient.ClearVoicePCM()
|
||||
|
||||
@@ -20,27 +20,21 @@ namespace Discord
|
||||
/// <summary> Provides a minimalistic websocket connection to the Discord service. </summary>
|
||||
public partial class DiscordWSClient
|
||||
{
|
||||
internal readonly DataWebSocket _dataSocket;
|
||||
internal readonly VoiceWebSocket _voiceSocket;
|
||||
protected readonly DiscordWSClientConfig _config;
|
||||
protected readonly ManualResetEvent _disconnectedEvent;
|
||||
protected readonly ManualResetEventSlim _connectedEvent;
|
||||
protected readonly bool _enableVoice;
|
||||
protected string _gateway, _token;
|
||||
protected string _voiceServerId;
|
||||
private Task _runTask;
|
||||
|
||||
internal readonly DataWebSocket _dataSocket;
|
||||
internal readonly VoiceWebSocket _voiceSocket;
|
||||
protected ExceptionDispatchInfo _disconnectReason;
|
||||
protected string _gateway, _token;
|
||||
protected string _userId, _voiceServerId;
|
||||
private Task _runTask;
|
||||
private bool _wasDisconnectUnexpected;
|
||||
|
||||
/// <summary> Returns the configuration object used to make this client. Note that this object cannot be edited directly - to change the configuration of this client, use the DiscordClient(DiscordClientConfig config) constructor. </summary>
|
||||
public DiscordWSClientConfig Config => _config;
|
||||
protected readonly DiscordWSClientConfig _config;
|
||||
public string CurrentUserId => _userId;
|
||||
|
||||
/// <summary> Returns the id of the current logged-in user. </summary>
|
||||
public string CurrentUserId => _currentUserId;
|
||||
private string _currentUserId;
|
||||
/*/// <summary> Returns the server this user is currently connected to for voice. </summary>
|
||||
public string CurrentVoiceServerId => _voiceSocket.CurrentServerId;*/
|
||||
/// <summary> Returns the configuration object used to make this client. Note that this object cannot be edited directly - to change the configuration of this client, use the DiscordClient(DiscordClientConfig config) constructor. </summary>
|
||||
public DiscordWSClientConfig Config => _config;
|
||||
|
||||
/// <summary> Returns the current connection state of this client. </summary>
|
||||
public DiscordClientState State => (DiscordClientState)_state;
|
||||
@@ -56,15 +50,13 @@ namespace Discord
|
||||
_config = config ?? new DiscordWSClientConfig();
|
||||
_config.Lock();
|
||||
|
||||
_enableVoice = _config.EnableVoice;
|
||||
|
||||
_state = (int)DiscordClientState.Disconnected;
|
||||
_cancelToken = new CancellationToken(true);
|
||||
_disconnectedEvent = new ManualResetEvent(true);
|
||||
_connectedEvent = new ManualResetEventSlim(false);
|
||||
|
||||
_dataSocket = CreateDataSocket();
|
||||
if (_enableVoice)
|
||||
if (_config.EnableVoice)
|
||||
_voiceSocket = CreateVoiceSocket();
|
||||
}
|
||||
internal DiscordWSClient(DiscordWSClientConfig config = null, string voiceServerId = null)
|
||||
@@ -247,7 +239,7 @@ namespace Discord
|
||||
|
||||
protected virtual async Task Cleanup()
|
||||
{
|
||||
if (_enableVoice)
|
||||
if (_config.EnableVoice)
|
||||
{
|
||||
string voiceServerId = _voiceSocket.CurrentServerId;
|
||||
if (voiceServerId != null)
|
||||
@@ -256,7 +248,7 @@ namespace Discord
|
||||
}
|
||||
await _dataSocket.Disconnect().ConfigureAwait(false);
|
||||
|
||||
_currentUserId = null;
|
||||
_userId = null;
|
||||
_gateway = null;
|
||||
_token = null;
|
||||
}
|
||||
@@ -286,7 +278,7 @@ namespace Discord
|
||||
throw new InvalidOperationException("The client is connecting.");
|
||||
}
|
||||
|
||||
if (checkVoice && !_enableVoice)
|
||||
if (checkVoice && !_config.EnableVoice)
|
||||
throw new InvalidOperationException("Voice is not enabled for this client.");
|
||||
}
|
||||
protected void RaiseEvent(string name, Action action)
|
||||
@@ -307,17 +299,17 @@ namespace Discord
|
||||
switch (e.Type)
|
||||
{
|
||||
case "READY":
|
||||
_currentUserId = e.Payload["user"].Value<string>("id");
|
||||
_userId = e.Payload["user"].Value<string>("id");
|
||||
break;
|
||||
case "VOICE_SERVER_UPDATE":
|
||||
{
|
||||
string guildId = e.Payload.Value<string>("guild_id");
|
||||
|
||||
if (_enableVoice && guildId == _voiceSocket.CurrentServerId)
|
||||
if (_config.EnableVoice && guildId == _voiceSocket.CurrentServerId)
|
||||
{
|
||||
string token = e.Payload.Value<string>("token");
|
||||
_voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0];
|
||||
return _voiceSocket.Login(_currentUserId, _dataSocket.SessionId, token, CancelToken);
|
||||
return _voiceSocket.Login(_userId, _dataSocket.SessionId, token, CancelToken);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Linq;
|
||||
namespace Discord
|
||||
{
|
||||
internal abstract class AsyncCollection<TValue> : IEnumerable<TValue>
|
||||
where TValue : class
|
||||
where TValue : CachedObject
|
||||
{
|
||||
private readonly object _writerLock;
|
||||
|
||||
@@ -1,26 +1,53 @@
|
||||
namespace Discord
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public static class Mention
|
||||
{
|
||||
/// <summary> Returns the string used to create a user mention. </summary>
|
||||
public static string User(User user)
|
||||
=> $"<@{user.Id}>";
|
||||
private static readonly Regex _userRegex = new Regex(@"<@(\d+?)>", RegexOptions.Compiled);
|
||||
private static readonly Regex _channelRegex = new Regex(@"<#(\d+?)>", RegexOptions.Compiled);
|
||||
|
||||
/// <summary> Returns the string used to create a user mention. </summary>
|
||||
public static string User(Member member)
|
||||
=> $"<@{member.UserId}>";
|
||||
/// <summary> Returns the string used to create a user mention. </summary>
|
||||
public static string User(string userId)
|
||||
=> $"<@{userId}>";
|
||||
|
||||
=> $"<@{member.Id}>";
|
||||
/// <summary> Returns the string used to create a channel mention. </summary>
|
||||
public static string Channel(Channel channel)
|
||||
=> $"<#{channel.Id}>";
|
||||
/// <summary> Returns the string used to create a channel mention. </summary>
|
||||
public static string Channel(string channelId)
|
||||
=> $"<#{channelId}>";
|
||||
|
||||
/// <summary> Returns the string used to create a channel mention. </summary>
|
||||
public static string Everyone()
|
||||
=> $"@everyone";
|
||||
|
||||
internal static string ConvertToNames(DiscordClient client, Server server, string text)
|
||||
{
|
||||
text = _userRegex.Replace(text, new MatchEvaluator(e =>
|
||||
{
|
||||
string id = e.Value.Substring(2, e.Value.Length - 3);
|
||||
var user = client.Members[id, server.Id];
|
||||
if (user != null)
|
||||
return '@' + user.Name;
|
||||
else //User not found
|
||||
return e.Value;
|
||||
}));
|
||||
text = _channelRegex.Replace(text, new MatchEvaluator(e =>
|
||||
{
|
||||
string id = e.Value.Substring(2, e.Value.Length - 3);
|
||||
var channel = client.Channels[id];
|
||||
if (channel != null && channel.Server.Id == server.Id)
|
||||
return channel.Name;
|
||||
else //Channel not found
|
||||
return e.Value;
|
||||
}));
|
||||
return text;
|
||||
}
|
||||
|
||||
internal static IEnumerable<string> GetUserIds(string text)
|
||||
{
|
||||
return _userRegex.Matches(text)
|
||||
.OfType<Match>()
|
||||
.Select(x => x.Groups[1].Value)
|
||||
.Where(x => x != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
internal static class MentionHelper
|
||||
{
|
||||
private static readonly Regex _userRegex, _channelRegex;
|
||||
|
||||
static MentionHelper()
|
||||
{
|
||||
_userRegex = new Regex(@"<@(\d+?)>", RegexOptions.Compiled);
|
||||
_channelRegex = new Regex(@"<#(\d+?)>", RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
public static string ConvertToNames(DiscordClient client, string text)
|
||||
{
|
||||
text = _userRegex.Replace(text, new MatchEvaluator(e =>
|
||||
{
|
||||
string id = e.Value.Substring(2, e.Value.Length - 3);
|
||||
var user = client.Users[id];
|
||||
if (user != null)
|
||||
return '@' + user.Name;
|
||||
else //User not found
|
||||
return e.Value;
|
||||
}));
|
||||
text = _channelRegex.Replace(text, new MatchEvaluator(e =>
|
||||
{
|
||||
string id = e.Value.Substring(2, e.Value.Length - 3);
|
||||
var channel = client.Channels[id];
|
||||
if (channel != null)
|
||||
return channel.Name;
|
||||
else //Channel not found
|
||||
return e.Value;
|
||||
}));
|
||||
return text;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetUserIds(string text)
|
||||
{
|
||||
return _userRegex.Matches(text)
|
||||
.OfType<Match>()
|
||||
.Select(x => x.Groups[1].Value)
|
||||
.Where(x => x != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
internal static class CollectionHelper
|
||||
{
|
||||
public static IEnumerable<string> FlattenChannels(IEnumerable<object> channels)
|
||||
{
|
||||
if (channels == null)
|
||||
return new string[0];
|
||||
|
||||
return channels.Select(x =>
|
||||
{
|
||||
if (x is string)
|
||||
return x as string;
|
||||
else if (x is Channel)
|
||||
return (x as Channel).Id;
|
||||
else
|
||||
throw new ArgumentException("Collection may only contain string or Channel.", nameof(channels));
|
||||
});
|
||||
}
|
||||
public static IEnumerable<string> FlattenUsers(IEnumerable<object> users)
|
||||
{
|
||||
if (users == null)
|
||||
return new string[0];
|
||||
|
||||
return users.Select(x =>
|
||||
{
|
||||
if (x is string)
|
||||
return x as string;
|
||||
else if (x is User)
|
||||
return (x as User).Id;
|
||||
else
|
||||
throw new ArgumentException("Collection may only contain string or User.", nameof(users));
|
||||
});
|
||||
}
|
||||
public static IEnumerable<string> FlattenRoles(IEnumerable<object> roles)
|
||||
{
|
||||
if (roles == null)
|
||||
return new string[0];
|
||||
|
||||
return roles.Select(x =>
|
||||
{
|
||||
if (x is string)
|
||||
return x as string;
|
||||
else if (x is Role)
|
||||
return (x as Role).Id;
|
||||
else
|
||||
throw new ArgumentException("Collection may only contain string or Role.", nameof(roles));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/Discord.Net/Models/CachedObject.cs
Normal file
35
src/Discord.Net/Models/CachedObject.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace Discord
|
||||
{
|
||||
public abstract class CachedObject
|
||||
{
|
||||
protected readonly DiscordClient _client;
|
||||
private bool _isCached;
|
||||
|
||||
internal CachedObject(DiscordClient client, string id)
|
||||
{
|
||||
_client = client;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary> Returns the unique identifier for this object. </summary>
|
||||
public string Id { get; internal set; }
|
||||
|
||||
public override string ToString() => $"{this.GetType().Name} {Id}";
|
||||
|
||||
internal void Cache()
|
||||
{
|
||||
OnCached();
|
||||
_isCached = true;
|
||||
}
|
||||
internal void Uncache()
|
||||
{
|
||||
if (_isCached)
|
||||
{
|
||||
OnUncached();
|
||||
_isCached = false;
|
||||
}
|
||||
}
|
||||
internal abstract void OnCached();
|
||||
internal abstract void OnUncached();
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System.Linq;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class Channel
|
||||
public sealed class Channel : CachedObject
|
||||
{
|
||||
public sealed class PermissionOverwrite
|
||||
{
|
||||
@@ -21,44 +21,37 @@ namespace Discord
|
||||
TargetType = targetType;
|
||||
TargetId = targetId;
|
||||
Allow = new ChannelPermissions(allow);
|
||||
Deny = new ChannelPermissions(deny);
|
||||
Allow.Lock();
|
||||
Deny = new ChannelPermissions(deny);
|
||||
Deny.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly DiscordClient _client;
|
||||
|
||||
private readonly ConcurrentDictionary<string, bool> _messages;
|
||||
private bool _areMembersStale;
|
||||
private bool _hasRef;
|
||||
|
||||
/// <summary> Returns the unique identifier for this channel. </summary>
|
||||
public string Id { get; }
|
||||
|
||||
private string _name;
|
||||
private readonly string _serverId, _recipientId;
|
||||
private Server _server;
|
||||
private Member _recipient;
|
||||
|
||||
/// <summary> Returns the name of this channel. </summary>
|
||||
public string Name { get { return !IsPrivate ? $"{_name}" : $"@{Recipient.Name}"; } internal set { _name = value; } }
|
||||
|
||||
public string Name { get; private set; }
|
||||
/// <summary> Returns the topic associated with this channel. </summary>
|
||||
public string Topic { get; private set; }
|
||||
/// <summary> Returns the position of this channel in the channel list for this server. </summary>
|
||||
public int Position { get; private set; }
|
||||
/// <summary> Returns false is this is a public chat and true if this is a private chat with another user (see Recipient). </summary>
|
||||
public bool IsPrivate => RecipientId != null;
|
||||
public bool IsPrivate => _recipientId != null;
|
||||
/// <summary> Returns the type of this channel (see ChannelTypes). </summary>
|
||||
public string Type { get; private set; }
|
||||
|
||||
/// <summary> Returns the id of the server containing this channel. </summary>
|
||||
public string ServerId { get; }
|
||||
/// <summary> Returns the server containing this channel. </summary>
|
||||
[JsonIgnore]
|
||||
public Server Server => _client.Servers[ServerId];
|
||||
|
||||
/// For private chats, returns the Id of the target user, otherwise null.
|
||||
public string RecipientId { get; set; }
|
||||
public Server Server => _client.Servers[_serverId];
|
||||
|
||||
/// For private chats, returns the target user, otherwise null.
|
||||
[JsonIgnore]
|
||||
public User Recipient => _client.Users[RecipientId];
|
||||
public Member Recipient => _client.Members[_recipientId, _serverId];
|
||||
|
||||
/// <summary> Returns a collection of the IDs of all users with read access to this channel. </summary>
|
||||
public IEnumerable<string> UserIds
|
||||
@@ -68,7 +61,7 @@ namespace Discord
|
||||
if (!_areMembersStale)
|
||||
return _userIds;
|
||||
|
||||
_userIds = Server.Members.Where(x => x.GetPermissions(Id)?.ReadMessages ?? false).Select(x => x.UserId).ToArray();
|
||||
_userIds = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).Select(x => x.Id).ToArray();
|
||||
_areMembersStale = false;
|
||||
return _userIds;
|
||||
}
|
||||
@@ -76,10 +69,7 @@ namespace Discord
|
||||
private string[] _userIds;
|
||||
/// <summary> Returns a collection of all users with read access to this channel. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Member> Members => UserIds.Select(x => _client.Members[x, ServerId]);
|
||||
/// <summary> Returns a collection of all users with read access to this channel. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<User> Users => UserIds.Select(x => _client.Users[x]);
|
||||
public IEnumerable<Member> Members => UserIds.Select(x => _client.Members[x, _serverId]);
|
||||
|
||||
/// <summary> Returns a collection of the ids of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. </summary>
|
||||
[JsonIgnore]
|
||||
@@ -94,64 +84,39 @@ namespace Discord
|
||||
public IEnumerable<PermissionOverwrite> PermissionOverwrites => _permissionOverwrites;
|
||||
|
||||
internal Channel(DiscordClient client, string id, string serverId, string recipientId)
|
||||
: base(client, id)
|
||||
{
|
||||
_client = client;
|
||||
Id = id;
|
||||
ServerId = serverId ?? _client.Servers.PMServer.Id;
|
||||
RecipientId = recipientId;
|
||||
_messages = new ConcurrentDictionary<string, bool>();
|
||||
_serverId = serverId ?? _client.Servers.PMServer.Id;
|
||||
_recipientId = recipientId;
|
||||
_permissionOverwrites = _initialPermissionsOverwrites;
|
||||
_areMembersStale = true;
|
||||
|
||||
//Local Cache
|
||||
_messages = new ConcurrentDictionary<string, bool>();
|
||||
}
|
||||
internal void OnCached()
|
||||
internal override void OnCached()
|
||||
{
|
||||
var server = Server;
|
||||
if (server != null)
|
||||
server.AddChannel(Id);
|
||||
|
||||
if (IsPrivate)
|
||||
{
|
||||
var user = Recipient;
|
||||
if (user != null)
|
||||
{
|
||||
Name = "@" + user.Name;
|
||||
user.PrivateChannelId = Id;
|
||||
user.AddRef();
|
||||
_hasRef = true;
|
||||
}
|
||||
else
|
||||
Name = "@" + RecipientId;
|
||||
var member = _client.Members.GetOrAdd(RecipientId, ServerId);
|
||||
member.Update(new ExtendedMemberInfo
|
||||
{
|
||||
GuildId = ServerId,
|
||||
UserId = RecipientId,
|
||||
JoinedAt = DateTime.UtcNow,
|
||||
Roles = new string[0]
|
||||
});
|
||||
_permissionOverwrites = new PermissionOverwrite[]
|
||||
{
|
||||
new PermissionOverwrite(PermissionTarget.Member, _client.CurrentUserId, ChannelPermissions.PrivateOnly.RawValue, 0),
|
||||
new PermissionOverwrite(PermissionTarget.Member, RecipientId, ChannelPermissions.PrivateOnly.RawValue, 0)
|
||||
};
|
||||
_recipient = _client.Members[_recipientId, _serverId];
|
||||
Name = "@" + _recipient.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_server = _client.Servers[_serverId];
|
||||
_server.AddChannel(this);
|
||||
}
|
||||
}
|
||||
internal void OnUncached()
|
||||
internal override void OnUncached()
|
||||
{
|
||||
var server = Server;
|
||||
if (server != null)
|
||||
server.RemoveChannel(Id);
|
||||
if (IsPrivate)
|
||||
{
|
||||
var user = Recipient;
|
||||
if (user != null)
|
||||
{
|
||||
user.PrivateChannelId = null;
|
||||
if (_hasRef)
|
||||
user.RemoveRef();
|
||||
}
|
||||
_client.Members.TryRemove(RecipientId, ServerId);
|
||||
}
|
||||
_hasRef = false;
|
||||
if (_server != null)
|
||||
_server.RemoveChannel(this);
|
||||
_server = null;
|
||||
|
||||
if (_recipient != null)
|
||||
_recipient.GlobalUser.PrivateChannel = null;
|
||||
_recipient = null;
|
||||
}
|
||||
|
||||
internal void Update(ChannelReference model)
|
||||
@@ -199,14 +164,14 @@ namespace Discord
|
||||
{
|
||||
_areMembersStale = true;
|
||||
foreach (var member in Members)
|
||||
member.UpdatePermissions(Id);
|
||||
member.UpdatePermissions(this);
|
||||
}
|
||||
internal void InvalidatePermissionsCache(string userId)
|
||||
{
|
||||
_areMembersStale = true;
|
||||
var member = _client.Members[userId, ServerId];
|
||||
var member = _client.Members[userId, _serverId];
|
||||
if (member != null)
|
||||
member.UpdatePermissions(Id);
|
||||
member.UpdatePermissions(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
src/Discord.Net/Models/GlobalUser.cs
Normal file
73
src/Discord.Net/Models/GlobalUser.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Discord.API;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
internal sealed class GlobalUser : CachedObject
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, bool> _servers;
|
||||
private int _refCount;
|
||||
|
||||
/// <summary> Returns the email for this user. </summary>
|
||||
/// <remarks> This field is only ever populated for the current logged in user. </remarks>
|
||||
[JsonIgnore]
|
||||
public string Email { get; private set; }
|
||||
/// <summary> Returns if the email for this user has been verified. </summary>
|
||||
/// <remarks> This field is only ever populated for the current logged in user. </remarks>
|
||||
[JsonIgnore]
|
||||
public bool? IsVerified { get; private set; }
|
||||
|
||||
/// <summary> Returns the private messaging channel with this user, if one exists. </summary>
|
||||
[JsonIgnore]
|
||||
public Channel PrivateChannel { get; internal set; }
|
||||
|
||||
/// <summary> Returns a collection of all server-specific data for every server this user is a member of. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Member> Memberships => _servers.Select(x => _client.Members[Id, x.Key]);
|
||||
/// <summary> Returns a collection of all servers this user is a member of. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Server> Servers => _servers.Select(x => _client.Servers[x.Key]);
|
||||
|
||||
internal GlobalUser(DiscordClient client, string id)
|
||||
: base(client, id)
|
||||
{
|
||||
_servers = new ConcurrentDictionary<string, bool>();
|
||||
}
|
||||
internal override void OnCached() { }
|
||||
internal override void OnUncached() { }
|
||||
|
||||
internal void Update(UserInfo model)
|
||||
{
|
||||
if (model.Email != null)
|
||||
Email = model.Email;
|
||||
if (model.IsVerified != null)
|
||||
IsVerified = model.IsVerified;
|
||||
}
|
||||
|
||||
internal void AddServer(string serverId)
|
||||
{
|
||||
_servers.TryAdd(serverId, true);
|
||||
}
|
||||
internal bool RemoveServer(string serverId)
|
||||
{
|
||||
bool ignored;
|
||||
return _servers.TryRemove(serverId, out ignored);
|
||||
}
|
||||
|
||||
internal void AddRef()
|
||||
{
|
||||
Interlocked.Increment(ref _refCount);
|
||||
}
|
||||
internal void RemoveRef()
|
||||
{
|
||||
if (Interlocked.Decrement(ref _refCount) == 0)
|
||||
_client.Users.TryRemove(Id);
|
||||
}
|
||||
|
||||
public override string ToString() => Id;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
using Discord.API;
|
||||
using System;
|
||||
using Discord.API;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class Invite
|
||||
public sealed class Invite : CachedObject
|
||||
{
|
||||
private readonly DiscordClient _client;
|
||||
|
||||
/// <summary> Returns the unique identifier for this invite. </summary>
|
||||
public string Id { get; }
|
||||
|
||||
private readonly string _serverId;
|
||||
private string _inviterId, _channelId;
|
||||
|
||||
/// <summary> Time (in seconds) until the invite expires. Set to 0 to never expire. </summary>
|
||||
public int MaxAge { get; private set; }
|
||||
/// <summary> The amount of times this invite has been used. </summary>
|
||||
@@ -25,41 +24,37 @@ namespace Discord
|
||||
|
||||
/// <summary> Returns a URL for this invite using XkcdPass if available or Id if not. </summary>
|
||||
public string Url => API.Endpoints.InviteUrl(XkcdPass ?? Id);
|
||||
|
||||
/// <summary> Returns the id of the user that created this invite. </summary>
|
||||
public string InviterId { get; private set; }
|
||||
|
||||
/// <summary> Returns the user that created this invite. </summary>
|
||||
[JsonIgnore]
|
||||
public User Inviter => _client.Users[InviterId];
|
||||
|
||||
/// <summary> Returns the id of the server this invite is to. </summary>
|
||||
public string ServerId { get; }
|
||||
public Member Inviter => _client.Members[_inviterId, _serverId];
|
||||
|
||||
/// <summary> Returns the server this invite is to. </summary>
|
||||
[JsonIgnore]
|
||||
public Server Server => _client.Servers[ServerId];
|
||||
|
||||
/// <summary> Returns the id of the channel this invite is to. </summary>
|
||||
public string ChannelId { get; private set; }
|
||||
public Server Server => _client.Servers[_serverId];
|
||||
|
||||
/// <summary> Returns the channel this invite is to. </summary>
|
||||
[JsonIgnore]
|
||||
public Channel Channel => _client.Channels[ChannelId];
|
||||
public Channel Channel => _client.Channels[_channelId];
|
||||
|
||||
internal Invite(DiscordClient client, string code, string xkcdPass, string serverId)
|
||||
: base(client, code)
|
||||
{
|
||||
_client = client;
|
||||
Id = code;
|
||||
XkcdPass = xkcdPass;
|
||||
ServerId = serverId;
|
||||
_serverId = serverId;
|
||||
}
|
||||
internal override void OnCached() { }
|
||||
internal override void OnUncached() { }
|
||||
|
||||
public override string ToString() => XkcdPass ?? Id;
|
||||
|
||||
|
||||
internal void Update(InviteReference model)
|
||||
{
|
||||
if (model.Channel != null)
|
||||
ChannelId = model.Channel.Id;
|
||||
_channelId = model.Channel.Id;
|
||||
if (model.Inviter != null)
|
||||
InviterId = model.Inviter.Id;
|
||||
_inviterId = model.Inviter.Id;
|
||||
}
|
||||
|
||||
internal void Update(InviteInfo model)
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
using Discord.API;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public class Member
|
||||
{
|
||||
private readonly DiscordClient _client;
|
||||
private ConcurrentDictionary<string, ChannelPermissions> _permissions;
|
||||
private bool _hasRef;
|
||||
|
||||
/// <summary> Returns the name of this user on this server. </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary>
|
||||
public string Discriminator { get; private set; }
|
||||
/// <summary> Returns the unique identifier for this user's current avatar. </summary>
|
||||
public string AvatarId { get; private set; }
|
||||
/// <summary> Returns the URL to this user's current avatar. </summary>
|
||||
public string AvatarUrl => API.Endpoints.UserAvatar(UserId, AvatarId);
|
||||
/// <summary> Returns the datetime that this user joined this server. </summary>
|
||||
public DateTime JoinedAt { get; private set; }
|
||||
|
||||
public bool IsSelfMuted { get; private set; }
|
||||
public bool IsSelfDeafened { get; private set; }
|
||||
public bool IsServerMuted { get; private set; }
|
||||
public bool IsServerDeafened { get; private set; }
|
||||
public bool IsServerSuppressed { get; private set; }
|
||||
public bool IsSpeaking { get; internal set; }
|
||||
|
||||
public string SessionId { get; private set; }
|
||||
public string Token { get; private set; }
|
||||
|
||||
/// <summary> Returns the id for the game this user is currently playing. </summary>
|
||||
public string GameId { get; private set; }
|
||||
/// <summary> Returns the current status for this user. </summary>
|
||||
public string Status { get; private set; }
|
||||
/// <summary> Returns the time this user last sent/edited a message, started typing or sent voice data in this server. </summary>
|
||||
public DateTime? LastActivityAt { get; private set; }
|
||||
/// <summary> Returns the time this user was last seen online in this server. </summary>
|
||||
public DateTime LastOnlineAt => Status != UserStatus.Offline ? DateTime.UtcNow : _lastOnline;
|
||||
private DateTime _lastOnline;
|
||||
|
||||
public string UserId { get; }
|
||||
[JsonIgnore]
|
||||
public User User => _client.Users[UserId];
|
||||
|
||||
public string ServerId { get; }
|
||||
[JsonIgnore]
|
||||
public Server Server => _client.Servers[ServerId];
|
||||
|
||||
public string VoiceChannelId { get; private set; }
|
||||
[JsonIgnore]
|
||||
public Channel VoiceChannel => _client.Channels[VoiceChannelId];
|
||||
|
||||
private static readonly string[] _initialRoleIds = new string[0];
|
||||
public string[] RoleIds { get; private set; }
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Role> Roles => RoleIds.Select(x => _client.Roles[x]);
|
||||
|
||||
/// <summary> Returns a collection of all messages this user has sent on this server that are still in cache. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.UserId == UserId && x.ServerId == ServerId);
|
||||
|
||||
/// <summary> Returns a collection of all channels this user is a member of. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Channel> Channels => _client.Channels.Where(x => x.ServerId == ServerId && x.UserIds.Contains(UserId));
|
||||
|
||||
internal Member(DiscordClient client, string userId, string serverId)
|
||||
{
|
||||
_client = client;
|
||||
UserId = userId;
|
||||
ServerId = serverId ?? _client.Servers.PMServer.Id;
|
||||
Status = UserStatus.Offline;
|
||||
RoleIds = _initialRoleIds;
|
||||
_permissions = new ConcurrentDictionary<string, ChannelPermissions>();
|
||||
}
|
||||
internal void OnCached()
|
||||
{
|
||||
var server = Server;
|
||||
if (server != null)
|
||||
{
|
||||
server.AddMember(this);
|
||||
if (UserId == _client.CurrentUserId)
|
||||
server.CurrentMember = this;
|
||||
}
|
||||
var user = User;
|
||||
if (user != null)
|
||||
{
|
||||
if (server == null || !server.IsVirtual)
|
||||
user.AddServer(ServerId);
|
||||
user.AddRef();
|
||||
_hasRef = true;
|
||||
}
|
||||
}
|
||||
internal void OnUncached()
|
||||
{
|
||||
var server = Server;
|
||||
if (server != null)
|
||||
{
|
||||
server.RemoveMember(this);
|
||||
if (UserId == _client.CurrentUserId)
|
||||
server.CurrentMember = null;
|
||||
}
|
||||
var user = User;
|
||||
if (user != null)
|
||||
{
|
||||
user.RemoveServer(ServerId);
|
||||
if (_hasRef)
|
||||
user.RemoveRef();
|
||||
}
|
||||
_hasRef = false;
|
||||
}
|
||||
|
||||
public override string ToString() => UserId;
|
||||
|
||||
internal void Update(UserReference model)
|
||||
{
|
||||
if (model.Avatar != null)
|
||||
AvatarId = model.Avatar;
|
||||
if (model.Discriminator != null)
|
||||
Discriminator = model.Discriminator;
|
||||
if (model.Username != null)
|
||||
Name = model.Username;
|
||||
}
|
||||
internal void Update(MemberInfo model)
|
||||
{
|
||||
if (model.User != null)
|
||||
Update(model.User);
|
||||
if (model.JoinedAt.HasValue)
|
||||
JoinedAt = model.JoinedAt.Value;
|
||||
if (model.Roles != null)
|
||||
UpdateRoles(model.Roles);
|
||||
|
||||
UpdatePermissions();
|
||||
}
|
||||
internal void Update(ExtendedMemberInfo model)
|
||||
{
|
||||
Update(model as API.MemberInfo);
|
||||
if (model.IsServerDeafened != null)
|
||||
IsServerDeafened = model.IsServerDeafened.Value;
|
||||
if (model.IsServerMuted != null)
|
||||
IsServerMuted = model.IsServerMuted.Value;
|
||||
}
|
||||
internal void Update(PresenceInfo model)
|
||||
{
|
||||
if (model.User != null)
|
||||
Update(model.User as UserReference);
|
||||
|
||||
if (model.Roles != null)
|
||||
UpdateRoles(model.Roles);
|
||||
if (model.Status != null && Status != model.Status)
|
||||
{
|
||||
Status = model.Status;
|
||||
if (Status == UserStatus.Offline)
|
||||
_lastOnline = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
//Allows null
|
||||
GameId = model.GameId;
|
||||
}
|
||||
internal void Update(VoiceMemberInfo model)
|
||||
{
|
||||
if (model.IsServerDeafened != null)
|
||||
IsServerDeafened = model.IsServerDeafened.Value;
|
||||
if (model.IsServerMuted != null)
|
||||
IsServerMuted = model.IsServerMuted.Value;
|
||||
if (model.SessionId != null)
|
||||
SessionId = model.SessionId;
|
||||
if (model.Token != null)
|
||||
Token = model.Token;
|
||||
|
||||
if (model.ChannelId != null)
|
||||
VoiceChannelId = model.ChannelId;
|
||||
if (model.IsSelfDeafened != null)
|
||||
IsSelfDeafened = model.IsSelfDeafened.Value;
|
||||
if (model.IsSelfMuted != null)
|
||||
IsSelfMuted = model.IsSelfMuted.Value;
|
||||
if (model.IsServerSuppressed != null)
|
||||
IsServerSuppressed = model.IsServerSuppressed.Value;
|
||||
}
|
||||
private void UpdateRoles(string[] roleIds)
|
||||
{
|
||||
//Set roles, with the everyone role added too
|
||||
string[] newRoles = new string[roleIds.Length + 1];
|
||||
newRoles[0] = ServerId; //Everyone
|
||||
for (int i = 0; i < roleIds.Length; i++)
|
||||
newRoles[i + 1] = roleIds[i];
|
||||
RoleIds = newRoles;
|
||||
}
|
||||
|
||||
internal void UpdateActivity(DateTime? activity = null)
|
||||
{
|
||||
if (LastActivityAt == null || activity > LastActivityAt.Value)
|
||||
LastActivityAt = activity ?? DateTime.UtcNow;
|
||||
}
|
||||
|
||||
internal void UpdatePermissions()
|
||||
{
|
||||
foreach (var channel in _permissions)
|
||||
UpdatePermissions(channel.Key);
|
||||
}
|
||||
internal void UpdatePermissions(string channelId)
|
||||
{
|
||||
if (RoleIds == null) return; // We don't have all our data processed yet, this will be called again soon
|
||||
|
||||
var server = Server;
|
||||
if (server == null) return;
|
||||
var channel = _client.Channels[channelId];
|
||||
|
||||
ChannelPermissions permissions;
|
||||
if (!_permissions.TryGetValue(channelId, out permissions)) return;
|
||||
uint newPermissions = 0x0;
|
||||
uint oldPermissions = permissions.RawValue;
|
||||
|
||||
if (UserId == server.OwnerId)
|
||||
newPermissions = ChannelPermissions.All(channel).RawValue;
|
||||
else
|
||||
{
|
||||
if (channel == null) return;
|
||||
var channelOverwrites = channel.PermissionOverwrites;
|
||||
|
||||
//var roles = Roles.OrderBy(x => x.Id);
|
||||
var roles = Roles;
|
||||
foreach (var serverRole in roles)
|
||||
newPermissions |= serverRole.Permissions.RawValue;
|
||||
foreach (var denyRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Deny.RawValue != 0 && roles.Any(y => y.Id == x.TargetId)))
|
||||
newPermissions &= ~denyRole.Deny.RawValue;
|
||||
foreach (var allowRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Allow.RawValue != 0 && roles.Any(y => y.Id == x.TargetId)))
|
||||
newPermissions |= allowRole.Allow.RawValue;
|
||||
foreach (var denyMembers in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Member && x.TargetId == UserId && x.Deny.RawValue != 0))
|
||||
newPermissions &= ~denyMembers.Deny.RawValue;
|
||||
foreach (var allowMembers in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Member && x.TargetId == UserId && x.Allow.RawValue != 0))
|
||||
newPermissions |= allowMembers.Allow.RawValue;
|
||||
}
|
||||
|
||||
permissions.SetRawValueInternal(newPermissions);
|
||||
|
||||
if (permissions.ManagePermissions)
|
||||
permissions.SetRawValueInternal(ChannelPermissions.All(channel).RawValue);
|
||||
/*else if (server.DefaultChannelId == channelId)
|
||||
permissions.SetBitInternal(PackedPermissions.Text_ReadMessagesBit, true);*/
|
||||
|
||||
if (permissions.RawValue != oldPermissions)
|
||||
channel.InvalidMembersCache();
|
||||
}
|
||||
//TODO: Add GetServerPermissions
|
||||
public ChannelPermissions GetPermissions(Channel channel)
|
||||
=> GetPermissions(channel?.Id);
|
||||
public ChannelPermissions GetPermissions(string channelId)
|
||||
{
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
|
||||
ChannelPermissions perms;
|
||||
if (_permissions.TryGetValue(channelId, out perms))
|
||||
return perms;
|
||||
return null;
|
||||
}
|
||||
|
||||
internal void AddChannel(string channelId)
|
||||
{
|
||||
var perms = new ChannelPermissions();
|
||||
perms.Lock();
|
||||
_permissions.TryAdd(channelId, perms);
|
||||
UpdatePermissions(channelId);
|
||||
}
|
||||
internal bool RemoveChannel(string channelId)
|
||||
{
|
||||
ChannelPermissions ignored;
|
||||
return _permissions.TryRemove(channelId, out ignored);
|
||||
}
|
||||
|
||||
public bool HasRole(Role role) => RoleIds.Contains(role?.Id);
|
||||
public bool HasRole(string roleId) => RoleIds.Contains(roleId);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class Message
|
||||
public sealed class Message : CachedObject
|
||||
{
|
||||
public sealed class Attachment : File
|
||||
{
|
||||
@@ -91,12 +91,9 @@ namespace Discord
|
||||
}
|
||||
}
|
||||
|
||||
private readonly DiscordClient _client;
|
||||
private string _cleanText;
|
||||
private bool _gotRef;
|
||||
|
||||
/// <summary> Returns the global unique identifier for this message. </summary>
|
||||
public string Id { get; internal set; }
|
||||
private string _channelId, _userId;
|
||||
|
||||
/// <summary> Returns the local unique identifier for this message. </summary>
|
||||
public string Nonce { get; internal set; }
|
||||
|
||||
@@ -115,7 +112,7 @@ namespace Discord
|
||||
public string RawText { get; private set; }
|
||||
/// <summary> Returns the content of this message with any special references such as mentions converted. </summary>
|
||||
/// <remarks> This value is lazy loaded and only processed on first request. Each subsequent request will pull from cache. </remarks>
|
||||
public string Text => _cleanText != null ? _cleanText : (_cleanText = MentionHelper.ConvertToNames(_client, RawText));
|
||||
public string Text => _cleanText != null ? _cleanText : (_cleanText = Mention.ConvertToNames(_client, Server, RawText));
|
||||
/// <summary> Returns the timestamp for when this message was sent. </summary>
|
||||
public DateTime Timestamp { get; private set; }
|
||||
/// <summary> Returns the timestamp for when this message was last edited. </summary>
|
||||
@@ -132,19 +129,14 @@ namespace Discord
|
||||
public string[] MentionIds { get; private set; }
|
||||
/// <summary> Returns a collection of all users mentioned in this message. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<User> Mentions => MentionIds.Select(x => _client.Users[x]).Where(x => x != null);
|
||||
|
||||
/// <summary> Returns the id of the server containing the channel this message was sent to. </summary>
|
||||
public string ServerId => Channel.ServerId;
|
||||
public IEnumerable<Member> Mentions { get; internal set; }
|
||||
|
||||
/// <summary> Returns the server containing the channel this message was sent to. </summary>
|
||||
[JsonIgnore]
|
||||
public Server Server => _client.Servers[Channel.ServerId];
|
||||
|
||||
/// <summary> Returns the id of the channel this message was sent to. </summary>
|
||||
public string ChannelId { get; }
|
||||
public Server Server => Channel.Server;
|
||||
/// <summary> Returns the channel this message was sent to. </summary>
|
||||
[JsonIgnore]
|
||||
public Channel Channel => _client.Channels[ChannelId];
|
||||
public Channel Channel { get; private set; }
|
||||
|
||||
/// <summary> Returns true if the current user created this message. </summary>
|
||||
public bool IsAuthor => _client.CurrentUserId == UserId;
|
||||
@@ -152,42 +144,28 @@ namespace Discord
|
||||
public string UserId { get; }
|
||||
/// <summary> Returns the author of this message. </summary>
|
||||
[JsonIgnore]
|
||||
public User User => _client.Users[UserId];
|
||||
/// <summary> Returns the author of this message. </summary>
|
||||
[JsonIgnore]
|
||||
public Member Member => _client.Members[UserId, ServerId];
|
||||
public Member Member => _client.Members[_userId, Channel.Server.Id];
|
||||
|
||||
internal Message(DiscordClient client, string id, string channelId, string userId)
|
||||
: base(client, id)
|
||||
{
|
||||
_client = client;
|
||||
Id = id;
|
||||
ChannelId = channelId;
|
||||
UserId = userId;
|
||||
_channelId = channelId;
|
||||
_userId = userId;
|
||||
Attachments = _initialAttachments;
|
||||
Embeds = _initialEmbeds;
|
||||
MentionIds = _initialMentions;
|
||||
}
|
||||
internal void OnCached()
|
||||
internal override void OnCached()
|
||||
{
|
||||
var channel = Channel;
|
||||
if (channel != null)
|
||||
channel.AddMessage(Id);
|
||||
var user = User;
|
||||
if (user != null)
|
||||
{
|
||||
user.AddRef();
|
||||
_gotRef = true;
|
||||
}
|
||||
var channel = _client.Channels[_channelId];
|
||||
channel.AddMessage(Id);
|
||||
Channel = channel;
|
||||
}
|
||||
internal void OnUncached()
|
||||
internal override void OnUncached()
|
||||
{
|
||||
var channel = Channel;
|
||||
if (channel != null)
|
||||
channel.RemoveMessage(Id);
|
||||
var user = User;
|
||||
if (user != null && _gotRef)
|
||||
user.RemoveRef();
|
||||
_gotRef = false;
|
||||
}
|
||||
|
||||
internal void Update(MessageInfo model)
|
||||
@@ -236,6 +214,6 @@ namespace Discord
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => User.ToString() + ": " + RawText;
|
||||
public override string ToString() => Member.Name + ": " + RawText;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@ using System.Linq;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class Role
|
||||
public sealed class Role : CachedObject
|
||||
{
|
||||
private readonly DiscordClient _client;
|
||||
|
||||
/// <summary> Returns the unique identifier for this role. </summary>
|
||||
public string Id { get; }
|
||||
private readonly string _serverId;
|
||||
private Server _server;
|
||||
|
||||
/// <summary> Returns the name of this role. </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary> If true, this role is displayed isolated from other users. </summary>
|
||||
@@ -24,27 +23,21 @@ namespace Discord
|
||||
|
||||
/// <summary> Returns the the permissions contained by this role. </summary>
|
||||
public ServerPermissions Permissions { get; }
|
||||
|
||||
/// <summary> Returns the id of the server this role is a member of. </summary>
|
||||
public string ServerId { get; }
|
||||
|
||||
/// <summary> Returns the server this role is a member of. </summary>
|
||||
[JsonIgnore]
|
||||
public Server Server => _client.Servers[ServerId];
|
||||
public Server Server => _server;
|
||||
|
||||
/// <summary> Returns true if this is the role representing all users in a server. </summary>
|
||||
public bool IsEveryone => Id == ServerId;
|
||||
/// <summary> Returns a list of the ids of all members in this role. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> MemberIds => IsEveryone ? Server.UserIds : Server.Members.Where(x => x.RoleIds.Contains(Id)).Select(x => x.UserId);
|
||||
public bool IsEveryone => Id == _serverId;
|
||||
/// <summary> Returns a list of all members in this role. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Member> Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.RoleIds.Contains(Id));
|
||||
public IEnumerable<Member> Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this));
|
||||
|
||||
internal Role(DiscordClient client, string id, string serverId)
|
||||
: base(client, id)
|
||||
{
|
||||
_client = client;
|
||||
Id = id;
|
||||
ServerId = serverId;
|
||||
_serverId = serverId;
|
||||
Permissions = new ServerPermissions(0);
|
||||
Permissions.Lock();
|
||||
Color = new Color(0);
|
||||
@@ -53,18 +46,17 @@ namespace Discord
|
||||
if (IsEveryone)
|
||||
Position = int.MinValue;
|
||||
}
|
||||
internal void OnCached()
|
||||
internal override void OnCached()
|
||||
{
|
||||
var server = Server;
|
||||
if (server != null)
|
||||
server.AddRole(Id);
|
||||
_server = _client.Servers[_serverId];
|
||||
_server.AddRole(this);
|
||||
}
|
||||
internal void OnUncached()
|
||||
internal override void OnUncached()
|
||||
{
|
||||
var server = Server;
|
||||
if (server != null)
|
||||
server.RemoveRole(Id);
|
||||
}
|
||||
if (_server != null)
|
||||
_server.RemoveRole(this);
|
||||
_server = null;
|
||||
}
|
||||
|
||||
internal void Update(RoleInfo model)
|
||||
{
|
||||
|
||||
@@ -7,13 +7,16 @@ using System.Linq;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class Server
|
||||
public sealed class Server : CachedObject
|
||||
{
|
||||
private readonly DiscordClient _client;
|
||||
private readonly ConcurrentDictionary<string, bool> _bans, _channels, _invites, _members, _roles;
|
||||
private readonly ConcurrentDictionary<string, bool> _bans;
|
||||
private readonly ConcurrentDictionary<string, Channel> _channels;
|
||||
private readonly ConcurrentDictionary<string, Member> _members;
|
||||
private readonly ConcurrentDictionary<string, Role> _roles;
|
||||
private readonly ConcurrentDictionary<string, Invite> _invites;
|
||||
|
||||
/// <summary> Returns the unique identifier for this server. </summary>
|
||||
public string Id { get; }
|
||||
private string _ownerId;
|
||||
|
||||
/// <summary> Returns the name of this channel. </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary> Returns the current logged-in user's data for this server. </summary>
|
||||
@@ -31,12 +34,10 @@ namespace Discord
|
||||
internal string VoiceServer { get; set; }*/
|
||||
|
||||
/// <summary> Returns true if the current user created this server. </summary>
|
||||
public bool IsOwner => _client.CurrentUserId == OwnerId;
|
||||
/// <summary> Returns the id of the user that first created this server. </summary>
|
||||
public string OwnerId { get; private set; }
|
||||
public bool IsOwner => _client.CurrentUserId == _ownerId;
|
||||
/// <summary> Returns the user that first created this server. </summary>
|
||||
[JsonIgnore]
|
||||
public User Owner => _client.Users[OwnerId];
|
||||
public Member Owner => _client.Members[_ownerId, Id];
|
||||
|
||||
/// <summary> Returns the id of the AFK voice channel for this server (see AFKTimeout). </summary>
|
||||
public string AFKChannelId { get; private set; }
|
||||
@@ -52,11 +53,8 @@ namespace Discord
|
||||
|
||||
/// <summary> Returns a collection of the ids of all users banned on this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> BanIds => _bans.Select(x => x.Key);
|
||||
|
||||
/// <summary> Returns a collection of the ids of all channels within this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> ChannelIds => _channels.Select(x => x.Key);
|
||||
public IEnumerable<string> Bans => _bans.Select(x => x.Key);
|
||||
|
||||
/// <summary> Returns a collection of all channels within this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Channel> Channels => _channels.Select(x => _client.Channels[x.Key]);
|
||||
@@ -67,62 +65,58 @@ namespace Discord
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Channel> VoiceChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelTypes.Voice);
|
||||
|
||||
/// <summary> Returns a collection of all invite codes to this server. </summary>
|
||||
/// <summary> Returns a collection of all invites to this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> InviteCodes => _invites.Select(x => x.Key);
|
||||
/*/// <summary> Returns a collection of all invites to this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Invite> Invites => _invites.Select(x => _client.Invites[x.Key]);*/
|
||||
|
||||
public IEnumerable<Invite> Invites => _invites.Values;
|
||||
|
||||
/// <summary> Returns a collection of all users within this server with their server-specific data. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Member> Members => _members.Select(x => _client.Members[x.Key, Id]);
|
||||
/// <summary> Returns a collection of the ids of all users within this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> UserIds => _members.Select(x => x.Key);
|
||||
/// <summary> Returns a collection of all users within this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<User> Users => _members.Select(x => _client.Users[x.Key]);
|
||||
|
||||
/// <summary> Return the id of the role representing all users in a server. </summary>
|
||||
public string EveryoneRoleId => Id;
|
||||
/// <summary> Return the the role representing all users in a server. </summary>
|
||||
[JsonIgnore]
|
||||
public Role EveryoneRole => _client.Roles[EveryoneRoleId];
|
||||
/// <summary> Returns a collection of the ids of all roles within this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> RoleIds => _roles.Select(x => x.Key);
|
||||
/// <summary> Returns a collection of all roles within this server. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Role> Roles => _roles.Select(x => _client.Roles[x.Key]);
|
||||
|
||||
internal Server(DiscordClient client, string id)
|
||||
: base(client, id)
|
||||
{
|
||||
_client = client;
|
||||
Id = id;
|
||||
//Global Cache
|
||||
_channels = new ConcurrentDictionary<string, Channel>();
|
||||
_members = new ConcurrentDictionary<string, Member>();
|
||||
_roles = new ConcurrentDictionary<string, Role>();
|
||||
|
||||
//Local Cache
|
||||
_bans = new ConcurrentDictionary<string, bool>();
|
||||
_channels = new ConcurrentDictionary<string, bool>();
|
||||
_invites = new ConcurrentDictionary<string, bool>();
|
||||
_members = new ConcurrentDictionary<string, bool>();
|
||||
_roles = new ConcurrentDictionary<string, bool>();
|
||||
_invites = new ConcurrentDictionary<string, Invite>();
|
||||
}
|
||||
internal void OnCached()
|
||||
{
|
||||
}
|
||||
internal void OnUncached()
|
||||
internal override void OnCached() { }
|
||||
internal override void OnUncached()
|
||||
{
|
||||
//Global Cache
|
||||
var channels = _client.Channels;
|
||||
foreach (var channelId in ChannelIds)
|
||||
channels.TryRemove(channelId);
|
||||
foreach (var channel in _channels)
|
||||
channels.TryRemove(channel.Key);
|
||||
|
||||
var members = _client.Members;
|
||||
foreach (var userId in UserIds)
|
||||
members.TryRemove(userId, Id);
|
||||
foreach (var user in _members)
|
||||
members.TryRemove(user.Key, Id);
|
||||
|
||||
var roles = _client.Roles;
|
||||
foreach (var roleId in RoleIds)
|
||||
roles.TryRemove(roleId);
|
||||
}
|
||||
foreach (var role in _roles)
|
||||
roles.TryRemove(role.Key);
|
||||
|
||||
//Local Cache
|
||||
foreach (var invite in _invites)
|
||||
invite.Value.Uncache();
|
||||
_invites.Clear();
|
||||
|
||||
_bans.Clear();
|
||||
}
|
||||
|
||||
internal void Update(GuildInfo model)
|
||||
{
|
||||
@@ -136,7 +130,7 @@ namespace Discord
|
||||
if (model.Name != null)
|
||||
Name = model.Name;
|
||||
if (model.OwnerId != null)
|
||||
OwnerId = model.OwnerId;
|
||||
_ownerId = model.OwnerId;
|
||||
if (model.Region != null)
|
||||
Region = model.Region;
|
||||
|
||||
@@ -165,8 +159,6 @@ namespace Discord
|
||||
var members = _client.Members;
|
||||
foreach (var subModel in model.Members)
|
||||
{
|
||||
var user = users.GetOrAdd(subModel.User.Id);
|
||||
user.Update(subModel.User);
|
||||
var member = members.GetOrAdd(subModel.User.Id, Id);
|
||||
member.Update(subModel);
|
||||
}
|
||||
@@ -196,62 +188,43 @@ namespace Discord
|
||||
return _bans.TryRemove(banId, out ignored);
|
||||
}
|
||||
|
||||
internal void AddChannel(string channelId)
|
||||
internal void AddChannel(Channel channel)
|
||||
{
|
||||
_channels.TryAdd(channelId, true);
|
||||
_channels.TryAdd(channel.Id, channel);
|
||||
foreach (var member in Members)
|
||||
member.AddChannel(channelId);
|
||||
member.AddChannel(channel);
|
||||
}
|
||||
internal bool RemoveChannel(string channelId)
|
||||
internal void RemoveChannel(Channel channel)
|
||||
{
|
||||
bool ignored;
|
||||
foreach (var member in Members)
|
||||
member.RemoveChannel(channelId);
|
||||
return _channels.TryRemove(channelId, out ignored);
|
||||
member.RemoveChannel(channel);
|
||||
_channels.TryRemove(channel.Id, out channel);
|
||||
}
|
||||
|
||||
internal void AddInvite(string inviteId)
|
||||
{
|
||||
_invites.TryAdd(inviteId, true);
|
||||
}
|
||||
internal bool RemoveInvite(string inviteId)
|
||||
{
|
||||
bool ignored;
|
||||
return _invites.TryRemove(inviteId, out ignored);
|
||||
}
|
||||
internal void AddInvite(Invite invite) => _invites.TryAdd(invite.Id, invite);
|
||||
internal void RemoveInvite(Invite invite) => _invites.TryRemove(invite.Id, out invite);
|
||||
|
||||
internal void AddMember(Member member)
|
||||
{
|
||||
_members.TryAdd(member.UserId, true);
|
||||
_members.TryAdd(member.Id, member);
|
||||
foreach (var channel in Channels)
|
||||
{
|
||||
member.AddChannel(channel.Id);
|
||||
channel.InvalidatePermissionsCache(member.UserId);
|
||||
member.AddChannel(channel);
|
||||
channel.InvalidatePermissionsCache(member.Id);
|
||||
}
|
||||
}
|
||||
internal bool RemoveMember(Member member)
|
||||
internal void RemoveMember(Member member)
|
||||
{
|
||||
bool ignored;
|
||||
foreach (var channel in Channels)
|
||||
{
|
||||
member.RemoveChannel(channel.Id);
|
||||
channel.InvalidatePermissionsCache(member.UserId);
|
||||
member.RemoveChannel(channel);
|
||||
channel.InvalidatePermissionsCache(member.Id);
|
||||
}
|
||||
return _members.TryRemove(member.UserId, out ignored);
|
||||
}
|
||||
internal bool HasMember(string userId)
|
||||
{
|
||||
return _members.ContainsKey(userId);
|
||||
_members.TryRemove(member.Id, out member);
|
||||
}
|
||||
internal void HasMember(Member user) => _members.ContainsKey(user.Id);
|
||||
|
||||
internal void AddRole(string roleId)
|
||||
{
|
||||
_roles.TryAdd(roleId, true);
|
||||
}
|
||||
internal bool RemoveRole(string roleId)
|
||||
{
|
||||
bool ignored;
|
||||
return _roles.TryRemove(roleId, out ignored);
|
||||
}
|
||||
internal void AddRole(Role role) => _roles.TryAdd(role.Id, role);
|
||||
internal void RemoveRole(Role role) => _roles.TryRemove(role.Id, out role);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
using Discord.API;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class User
|
||||
public class Member : CachedObject
|
||||
{
|
||||
private readonly DiscordClient _client;
|
||||
private readonly ConcurrentDictionary<string, bool> _servers;
|
||||
private int _refCount;
|
||||
private static readonly string[] _initialRoleIds = new string[0];
|
||||
|
||||
private ConcurrentDictionary<string, Channel> _channels;
|
||||
private ConcurrentDictionary<string, ChannelPermissions> _permissions;
|
||||
private bool _hasRef;
|
||||
private string[] _roleIds;
|
||||
|
||||
/// <summary> Returns the unique identifier for this user. </summary>
|
||||
public string Id { get; }
|
||||
/// <summary> Returns the name of this user on this server. </summary>
|
||||
public string Name { get; private set; }
|
||||
/// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary>
|
||||
@@ -23,59 +24,97 @@ namespace Discord
|
||||
public string AvatarId { get; private set; }
|
||||
/// <summary> Returns the URL to this user's current avatar. </summary>
|
||||
public string AvatarUrl => API.Endpoints.UserAvatar(Id, AvatarId);
|
||||
/// <summary> Returns the datetime that this user joined this server. </summary>
|
||||
public DateTime JoinedAt { get; private set; }
|
||||
|
||||
/// <summary> Returns the email for this user. </summary>
|
||||
/// <remarks> This field is only ever populated for the current logged in user. </remarks>
|
||||
[JsonIgnore]
|
||||
public string Email { get; private set; }
|
||||
/// <summary> Returns if the email for this user has been verified. </summary>
|
||||
/// <remarks> This field is only ever populated for the current logged in user. </remarks>
|
||||
[JsonIgnore]
|
||||
public bool? IsVerified { get; private set; }
|
||||
public bool IsSelfMuted { get; private set; }
|
||||
public bool IsSelfDeafened { get; private set; }
|
||||
public bool IsServerMuted { get; private set; }
|
||||
public bool IsServerDeafened { get; private set; }
|
||||
public bool IsServerSuppressed { get; private set; }
|
||||
public bool IsSpeaking { get; internal set; }
|
||||
|
||||
/// <summary> Returns the Id of the private messaging channel with this user, if one exists. </summary>
|
||||
public string PrivateChannelId { get; internal set; }
|
||||
/// <summary> Returns the private messaging channel with this user, if one exists. </summary>
|
||||
[JsonIgnore]
|
||||
public Channel PrivateChannel => _client.Channels[PrivateChannelId];
|
||||
|
||||
/// <summary> Returns a collection of all server-specific data for every server this user is a member of. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Member> Memberships => _servers.Select(x => _client.GetMember(x.Key, Id));
|
||||
/// <summary> Returns a collection of all servers this user is a member of. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Server> Servers => _servers.Select(x => _client.GetServer(x.Key));
|
||||
/// <summary> Returns a collection of the ids of all servers this user is a member of. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<string> ServersIds => _servers.Select(x => x.Key);
|
||||
/// <summary> Returns a collection of all messages this user has sent that are still in cache. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.UserId == Id);
|
||||
public string SessionId { get; private set; }
|
||||
public string Token { get; private set; }
|
||||
|
||||
/// <summary> Returns the id for the game this user is currently playing. </summary>
|
||||
/*public string GameId => Memberships.Where(x => x.GameId != null).Select(x => x.GameId).FirstOrDefault();
|
||||
public string GameId { get; private set; }
|
||||
/// <summary> Returns the current status for this user. </summary>
|
||||
public string Status => Memberships.OrderByDescending(x => x.StatusSince).Select(x => x.Status).FirstOrDefault();
|
||||
/// <summary> Returns the time this user's status was last changed. </summary>
|
||||
public DateTime StatusSince => Memberships.OrderByDescending(x => x.StatusSince).Select(x => x.StatusSince).First();
|
||||
/// <summary> Returns the time this user last sent/edited a message, started typing or sent voice data. </summary>
|
||||
public DateTime? LastActivity => Memberships.OrderByDescending(x => x.LastActivity).Select(x => x.LastActivity).FirstOrDefault();
|
||||
/// <summary> Returns the time this user was last seen online. </summary>
|
||||
public DateTime? LastOnline => Memberships.OrderByDescending(x => x.LastOnline).Select(x => x.LastOnline).FirstOrDefault();*/
|
||||
public string Status { get; private set; }
|
||||
/// <summary> Returns the time this user last sent/edited a message, started typing or sent voice data in this server. </summary>
|
||||
public DateTime? LastActivityAt { get; private set; }
|
||||
/// <summary> Returns the time this user was last seen online in this server. </summary>
|
||||
public DateTime LastOnlineAt => Status != UserStatus.Offline ? DateTime.UtcNow : _lastOnline;
|
||||
private DateTime _lastOnline;
|
||||
|
||||
internal User(DiscordClient client, string id)
|
||||
[JsonIgnore]
|
||||
internal GlobalUser GlobalUser => _client.Users[Id];
|
||||
|
||||
public string ServerId { get; }
|
||||
[JsonIgnore]
|
||||
public Server Server => _client.Servers[ServerId];
|
||||
|
||||
public string VoiceChannelId { get; private set; }
|
||||
[JsonIgnore]
|
||||
public Channel VoiceChannel => _client.Channels[VoiceChannelId];
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Role> Roles => _roleIds.Select(x => _client.Roles[x]);
|
||||
/// <summary> Returns a collection of all messages this user has sent on this server that are still in cache. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.UserId == Id && x.Server.Id == ServerId);
|
||||
/// <summary> Returns a collection of all channels this user is a member of. </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<Channel> Channels => _client.Channels.Where(x => x.Server.Id == ServerId && x.UserIds.Contains(Id));
|
||||
|
||||
internal Member(DiscordClient client, string id, string serverId)
|
||||
: base(client, id)
|
||||
{
|
||||
_client = client;
|
||||
Id = id;
|
||||
_servers = new ConcurrentDictionary<string, bool>();
|
||||
ServerId = serverId ?? _client.Servers.PMServer.Id;
|
||||
Status = UserStatus.Offline;
|
||||
_roleIds = _initialRoleIds;
|
||||
_channels = new ConcurrentDictionary<string, Channel>();
|
||||
_permissions = new ConcurrentDictionary<string, ChannelPermissions>();
|
||||
}
|
||||
internal void OnCached()
|
||||
internal override void OnCached()
|
||||
{
|
||||
var server = Server;
|
||||
if (server != null)
|
||||
{
|
||||
server.AddMember(this);
|
||||
if (Id == _client.CurrentUserId)
|
||||
server.CurrentMember = this;
|
||||
}
|
||||
var user = GlobalUser;
|
||||
if (user != null)
|
||||
{
|
||||
if (server == null || !server.IsVirtual)
|
||||
user.AddServer(ServerId);
|
||||
user.AddRef();
|
||||
_hasRef = true;
|
||||
}
|
||||
}
|
||||
internal void OnUncached()
|
||||
internal override void OnUncached()
|
||||
{
|
||||
var server = Server;
|
||||
if (server != null)
|
||||
{
|
||||
server.RemoveMember(this);
|
||||
if (Id == _client.CurrentUserId)
|
||||
server.CurrentMember = null;
|
||||
}
|
||||
var user = GlobalUser;
|
||||
if (user != null)
|
||||
{
|
||||
user.RemoveServer(ServerId);
|
||||
if (_hasRef)
|
||||
user.RemoveRef();
|
||||
}
|
||||
_hasRef = false;
|
||||
}
|
||||
|
||||
public override string ToString() => Id;
|
||||
|
||||
internal void Update(UserReference model)
|
||||
{
|
||||
if (model.Avatar != null)
|
||||
@@ -85,36 +124,157 @@ namespace Discord
|
||||
if (model.Username != null)
|
||||
Name = model.Username;
|
||||
}
|
||||
internal void Update(UserInfo model)
|
||||
internal void Update(MemberInfo model)
|
||||
{
|
||||
Update(model as UserReference);
|
||||
if (model.User != null)
|
||||
Update(model.User);
|
||||
if (model.JoinedAt.HasValue)
|
||||
JoinedAt = model.JoinedAt.Value;
|
||||
if (model.Roles != null)
|
||||
UpdateRoles(model.Roles);
|
||||
|
||||
if (model.Email != null)
|
||||
Email = model.Email;
|
||||
if (model.IsVerified != null)
|
||||
IsVerified = model.IsVerified;
|
||||
UpdatePermissions();
|
||||
}
|
||||
internal void Update(ExtendedMemberInfo model)
|
||||
{
|
||||
Update(model as API.MemberInfo);
|
||||
if (model.IsServerDeafened != null)
|
||||
IsServerDeafened = model.IsServerDeafened.Value;
|
||||
if (model.IsServerMuted != null)
|
||||
IsServerMuted = model.IsServerMuted.Value;
|
||||
}
|
||||
internal void Update(PresenceInfo model)
|
||||
{
|
||||
if (model.User != null)
|
||||
Update(model.User as UserReference);
|
||||
|
||||
if (model.Roles != null)
|
||||
UpdateRoles(model.Roles);
|
||||
if (model.Status != null && Status != model.Status)
|
||||
{
|
||||
Status = model.Status;
|
||||
if (Status == UserStatus.Offline)
|
||||
_lastOnline = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
//Allows null
|
||||
GameId = model.GameId;
|
||||
}
|
||||
internal void Update(VoiceMemberInfo model)
|
||||
{
|
||||
if (model.IsServerDeafened != null)
|
||||
IsServerDeafened = model.IsServerDeafened.Value;
|
||||
if (model.IsServerMuted != null)
|
||||
IsServerMuted = model.IsServerMuted.Value;
|
||||
if (model.SessionId != null)
|
||||
SessionId = model.SessionId;
|
||||
if (model.Token != null)
|
||||
Token = model.Token;
|
||||
|
||||
if (model.ChannelId != null)
|
||||
VoiceChannelId = model.ChannelId;
|
||||
if (model.IsSelfDeafened != null)
|
||||
IsSelfDeafened = model.IsSelfDeafened.Value;
|
||||
if (model.IsSelfMuted != null)
|
||||
IsSelfMuted = model.IsSelfMuted.Value;
|
||||
if (model.IsServerSuppressed != null)
|
||||
IsServerSuppressed = model.IsServerSuppressed.Value;
|
||||
}
|
||||
private void UpdateRoles(string[] roleIds)
|
||||
{
|
||||
//Set roles, with the everyone role added too
|
||||
string[] newRoles = new string[roleIds.Length + 1];
|
||||
newRoles[0] = ServerId; //Everyone
|
||||
for (int i = 0; i < roleIds.Length; i++)
|
||||
newRoles[i + 1] = roleIds[i];
|
||||
_roleIds = newRoles;
|
||||
}
|
||||
|
||||
public override string ToString() => Name;
|
||||
|
||||
internal void AddServer(string serverId)
|
||||
internal void UpdateActivity(DateTime? activity = null)
|
||||
{
|
||||
_servers.TryAdd(serverId, true);
|
||||
}
|
||||
internal bool RemoveServer(string serverId)
|
||||
{
|
||||
bool ignored;
|
||||
return _servers.TryRemove(serverId, out ignored);
|
||||
if (LastActivityAt == null || activity > LastActivityAt.Value)
|
||||
LastActivityAt = activity ?? DateTime.UtcNow;
|
||||
}
|
||||
|
||||
internal void AddRef()
|
||||
internal void UpdatePermissions()
|
||||
{
|
||||
Interlocked.Increment(ref _refCount);
|
||||
foreach (var channel in _channels)
|
||||
UpdatePermissions(channel.Value);
|
||||
}
|
||||
internal void RemoveRef()
|
||||
internal void UpdatePermissions(Channel channel)
|
||||
{
|
||||
if (Interlocked.Decrement(ref _refCount) == 0)
|
||||
_client.Users.TryRemove(Id);
|
||||
if (_roleIds == null) return; // We don't have all our data processed yet, this will be called again soon
|
||||
|
||||
var server = Server;
|
||||
if (server == null || channel.Server != server) return;
|
||||
|
||||
ChannelPermissions permissions;
|
||||
if (!_permissions.TryGetValue(channel.Id, out permissions)) return;
|
||||
uint newPermissions = 0x0;
|
||||
uint oldPermissions = permissions.RawValue;
|
||||
|
||||
if (Id == server.Owner.Id)
|
||||
newPermissions = ChannelPermissions.All(channel).RawValue;
|
||||
else
|
||||
{
|
||||
if (channel == null) return;
|
||||
var channelOverwrites = channel.PermissionOverwrites;
|
||||
|
||||
//var roles = Roles.OrderBy(x => x.Id);
|
||||
var roles = Roles;
|
||||
foreach (var serverRole in roles)
|
||||
newPermissions |= serverRole.Permissions.RawValue;
|
||||
foreach (var denyRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Deny.RawValue != 0 && roles.Any(y => y.Id == x.TargetId)))
|
||||
newPermissions &= ~denyRole.Deny.RawValue;
|
||||
foreach (var allowRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Allow.RawValue != 0 && roles.Any(y => y.Id == x.TargetId)))
|
||||
newPermissions |= allowRole.Allow.RawValue;
|
||||
foreach (var denyMembers in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Member && x.TargetId == Id && x.Deny.RawValue != 0))
|
||||
newPermissions &= ~denyMembers.Deny.RawValue;
|
||||
foreach (var allowMembers in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Member && x.TargetId == Id && x.Allow.RawValue != 0))
|
||||
newPermissions |= allowMembers.Allow.RawValue;
|
||||
}
|
||||
|
||||
permissions.SetRawValueInternal(newPermissions);
|
||||
|
||||
if (permissions.ManagePermissions)
|
||||
permissions.SetRawValueInternal(ChannelPermissions.All(channel).RawValue);
|
||||
/*else if (server.DefaultChannelId == channelId)
|
||||
permissions.SetBitInternal(PackedPermissions.Text_ReadMessagesBit, true);*/
|
||||
|
||||
if (permissions.RawValue != oldPermissions)
|
||||
channel.InvalidMembersCache();
|
||||
}
|
||||
//TODO: Add GetServerPermissions
|
||||
public ChannelPermissions GetPermissions(Channel channel)
|
||||
{
|
||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||
|
||||
ChannelPermissions perms;
|
||||
if (_permissions.TryGetValue(channel.Id, out perms))
|
||||
return perms;
|
||||
return null;
|
||||
}
|
||||
|
||||
internal void AddChannel(Channel channel)
|
||||
{
|
||||
var perms = new ChannelPermissions();
|
||||
perms.Lock();
|
||||
_channels.TryAdd(channel.Id, channel);
|
||||
_permissions.TryAdd(channel.Id, perms);
|
||||
UpdatePermissions(channel);
|
||||
}
|
||||
internal void RemoveChannel(Channel channel)
|
||||
{
|
||||
ChannelPermissions ignored;
|
||||
_channels.TryRemove(channel.Id, out channel);
|
||||
_permissions.TryRemove(channel.Id, out ignored);
|
||||
}
|
||||
|
||||
public bool HasRole(Role role)
|
||||
{
|
||||
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||
|
||||
return _roleIds.Contains(role.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user