Refactored DiscordClient, added argument validations to API and connection functions
This commit is contained in:
@@ -28,7 +28,7 @@ namespace Discord
|
||||
MessageCreated += async (s, e) =>
|
||||
{
|
||||
//Ignore messages from ourselves
|
||||
if (e.Message.UserId == UserId)
|
||||
if (e.Message.UserId == _myId)
|
||||
return;
|
||||
|
||||
//Check for the command character
|
||||
|
||||
@@ -80,6 +80,12 @@
|
||||
<Compile Include="..\Discord.Net\ChannelTypes.cs">
|
||||
<Link>ChannelTypes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\DiscordClient.API.cs">
|
||||
<Link>DiscordClient.API.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\DiscordClient.Cache.cs">
|
||||
<Link>DiscordClient.Cache.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\DiscordClient.cs">
|
||||
<Link>DiscordClient.cs</Link>
|
||||
</Compile>
|
||||
@@ -107,6 +113,9 @@
|
||||
<Compile Include="..\Discord.Net\Helpers\AsyncCache.cs">
|
||||
<Link>Helpers\AsyncCache.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Helpers\Extensions.cs">
|
||||
<Link>Helpers\Extensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Helpers\JsonHttpClient.cs">
|
||||
<Link>Helpers\JsonHttpClient.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -141,10 +141,10 @@ namespace Discord.API
|
||||
var request = new APIRequests.ChangePassword { NewPassword = newPassword, CurrentEmail = currentEmail, CurrentPassword = currentPassword };
|
||||
return _http.Patch<SelfUserInfo>(Endpoints.UserMe, request);
|
||||
}
|
||||
public Task<SelfUserInfo> ChangeAvatar(DiscordClient.AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword)
|
||||
public Task<SelfUserInfo> ChangeAvatar(AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword)
|
||||
{
|
||||
string base64 = Convert.ToBase64String(bytes);
|
||||
string type = imageType == DiscordClient.AvatarImageType.Jpeg ? "image/jpeg;base64" : "image/png;base64";
|
||||
string type = imageType == AvatarImageType.Jpeg ? "image/jpeg;base64" : "image/png;base64";
|
||||
var request = new APIRequests.ChangeAvatar { Avatar = $"data:{type},/9j/{base64}", CurrentEmail = currentEmail, CurrentPassword = currentPassword };
|
||||
return _http.Patch<SelfUserInfo>(Endpoints.UserMe, request);
|
||||
}
|
||||
|
||||
559
src/Discord.Net/DiscordClient.API.cs
Normal file
559
src/Discord.Net/DiscordClient.API.cs
Normal file
@@ -0,0 +1,559 @@
|
||||
using Discord.API;
|
||||
using Discord.API.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public enum AvatarImageType
|
||||
{
|
||||
Jpeg,
|
||||
Png
|
||||
}
|
||||
public partial class DiscordClient
|
||||
{
|
||||
//Servers
|
||||
/// <summary> Creates a new server with the provided name and region (see Regions). </summary>
|
||||
public async Task<Server> CreateServer(string name, string region)
|
||||
{
|
||||
CheckReady();
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (region == null) throw new ArgumentNullException(nameof(region));
|
||||
|
||||
var response = await _api.CreateServer(name, region);
|
||||
return _servers.Update(response.Id, response);
|
||||
}
|
||||
|
||||
/// <summary> Leaves the provided server, destroying it if you are the owner. </summary>
|
||||
public Task<Server> LeaveServer(Server server)
|
||||
=> LeaveServer(server?.Id);
|
||||
/// <summary> Leaves the provided server, destroying it if you are the owner. </summary>
|
||||
public async Task<Server> LeaveServer(string serverId)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
|
||||
try { await _api.LeaveServer(serverId); }
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
return _servers.Remove(serverId);
|
||||
}
|
||||
|
||||
//Channels
|
||||
/// <summary> Creates a new channel with the provided name and type (see ChannelTypes). </summary>
|
||||
public Task<Channel> CreateChannel(Server server, string name, string type)
|
||||
=> 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)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||
|
||||
var response = await _api.CreateChannel(serverId, name, type);
|
||||
return _channels.Update(response.Id, serverId, response);
|
||||
}
|
||||
|
||||
/// <summary> Creates a new private channel with the provided user. </summary>
|
||||
public Task<Channel> CreatePMChannel(User user)
|
||||
=> CreatePMChannel(user?.Id);
|
||||
/// <summary> Creates a new private channel with the provided user. </summary>
|
||||
public async Task<Channel> CreatePMChannel(string userId)
|
||||
{
|
||||
CheckReady();
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
|
||||
var response = await _api.CreatePMChannel(_myId, userId);
|
||||
return _channels.Update(response.Id, response);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
|
||||
try { await _api.DestroyChannel(channelId); }
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
return _channels.Remove(channelId);
|
||||
}
|
||||
|
||||
//Bans
|
||||
/// <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));
|
||||
|
||||
return _api.Ban(serverId, 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)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
|
||||
try { await _api.Unban(serverId, userId); }
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
}
|
||||
|
||||
//Invites
|
||||
/// <summary> Creates a new invite to the default channel of the provided server. </summary>
|
||||
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to 0 to never expire. </param>
|
||||
/// <param name="isTemporary"> If true, a user accepting this invite will be kicked from the server after closing their client. </param>
|
||||
/// <param name="hasXkcdPass"> 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. </param>
|
||||
public Task<Invite> CreateInvite(Server server, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass)
|
||||
=> CreateInvite(server?.DefaultChannelId, maxAge, maxUses, isTemporary, hasXkcdPass);
|
||||
/// <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="isTemporary"> If true, a user accepting this invite will be kicked from the server after closing their client. </param>
|
||||
/// <param name="hasXkcdPass"> 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. </param>
|
||||
public Task<Invite> CreateInvite(Channel channel, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass)
|
||||
=> CreateInvite(channel?.Id, maxAge, maxUses, isTemporary, hasXkcdPass);
|
||||
/// <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="isTemporary"> If true, a user accepting this invite will be kicked from the server after closing their client. </param>
|
||||
/// <param name="hasXkcdPass"> 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. </param>
|
||||
public async Task<Invite> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (maxAge <= 0) throw new ArgumentOutOfRangeException(nameof(maxAge));
|
||||
if (maxUses <= 0) throw new ArgumentOutOfRangeException(nameof(maxUses));
|
||||
|
||||
var response = await _api.CreateInvite(channelId, maxAge, maxUses, isTemporary, hasXkcdPass);
|
||||
_channels.Update(response.Channel.Id, response.Server.Id, response.Channel);
|
||||
_servers.Update(response.Server.Id, response.Server);
|
||||
_users.Update(response.Inviter.Id, response.Inviter);
|
||||
return new Invite(response.Code, response.XkcdPass, this)
|
||||
{
|
||||
ChannelId = response.Channel.Id,
|
||||
InviterId = response.Inviter.Id,
|
||||
ServerId = response.Server.Id,
|
||||
IsRevoked = response.IsRevoked,
|
||||
IsTemporary = response.IsTemporary,
|
||||
MaxAge = response.MaxAge,
|
||||
MaxUses = response.MaxUses,
|
||||
Uses = response.Uses
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary> Gets more info about the provided invite. </summary>
|
||||
/// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks>
|
||||
public async Task<Invite> GetInvite(string id)
|
||||
{
|
||||
CheckReady();
|
||||
if (id == null) throw new ArgumentNullException(nameof(id));
|
||||
|
||||
var response = await _api.GetInvite(id);
|
||||
return new Invite(response.Code, response.XkcdPass, this)
|
||||
{
|
||||
ChannelId = response.Channel.Id,
|
||||
InviterId = response.Inviter.Id,
|
||||
ServerId = response.Server.Id
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary> Accepts the provided invite. </summary>
|
||||
public Task AcceptInvite(Invite invite)
|
||||
{
|
||||
CheckReady();
|
||||
if (invite == null) throw new ArgumentNullException(nameof(invite));
|
||||
|
||||
return _api.AcceptInvite(invite.Id);
|
||||
}
|
||||
/// <summary> Accepts the provided invite. </summary>
|
||||
public async Task AcceptInvite(string id)
|
||||
{
|
||||
CheckReady();
|
||||
if (id == null) throw new ArgumentNullException(nameof(id));
|
||||
|
||||
//Remove Url Parts
|
||||
if (id.StartsWith(Endpoints.BaseShortHttps))
|
||||
id = id.Substring(Endpoints.BaseShortHttps.Length);
|
||||
if (id.Length > 0 && id[0] == '/')
|
||||
id = id.Substring(1);
|
||||
if (id.Length > 0 && id[id.Length - 1] == '/')
|
||||
id = id.Substring(0, id.Length - 1);
|
||||
|
||||
//Check if this is a human-readable link and get its ID
|
||||
var response = await _api.GetInvite(id);
|
||||
await _api.AcceptInvite(response.Code);
|
||||
}
|
||||
|
||||
/// <summary> Deletes the provided invite. </summary>
|
||||
public async Task DeleteInvite(string id)
|
||||
{
|
||||
CheckReady();
|
||||
if (id == null) throw new ArgumentNullException(nameof(id));
|
||||
|
||||
try
|
||||
{
|
||||
//Check if this is a human-readable link and get its ID
|
||||
var response = await _api.GetInvite(id);
|
||||
await _api.DeleteInvite(response.Code);
|
||||
}
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
}
|
||||
|
||||
//Chat
|
||||
/// <summary> Sends a message to the provided channel. </summary>
|
||||
public Task<Message[]> SendMessage(Channel channel, string text)
|
||||
=> SendMessage(channel?.Id, text, new string[0]);
|
||||
/// <summary> Sends a message to the provided channel. </summary>
|
||||
public Task<Message[]> SendMessage(string channelId, string text)
|
||||
=> SendMessage(channelId, text, new string[0]);
|
||||
/// <summary> Sends a message to the provided channel, mentioning certain users. </summary>
|
||||
/// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks>
|
||||
public Task<Message[]> SendMessage(Channel channel, string text, string[] mentions)
|
||||
=> SendMessage(channel?.Id, text, mentions);
|
||||
/// <summary> Sends a message to the provided channel, mentioning certain users. </summary>
|
||||
/// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks>
|
||||
public async Task<Message[]> SendMessage(string channelId, string text, string[] mentions)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
if (mentions == null) throw new ArgumentNullException(nameof(mentions));
|
||||
|
||||
int blockCount = (int)Math.Ceiling(text.Length / (double)DiscordAPI.MaxMessageSize);
|
||||
Message[] result = new Message[blockCount];
|
||||
for (int i = 0; i < blockCount; i++)
|
||||
{
|
||||
int index = i * DiscordAPI.MaxMessageSize;
|
||||
string blockText = text.Substring(index, Math.Min(2000, text.Length - index));
|
||||
var nonce = GenerateNonce();
|
||||
if (_config.UseMessageQueue)
|
||||
{
|
||||
var msg = _messages.Update("nonce_" + nonce, channelId, new API.Models.Message
|
||||
{
|
||||
Content = blockText,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Author = new UserReference { Avatar = User.AvatarId, Discriminator = User.Discriminator, Id = User.Id, Username = User.Name },
|
||||
ChannelId = channelId
|
||||
});
|
||||
msg.IsQueued = true;
|
||||
msg.Nonce = nonce;
|
||||
result[i] = msg;
|
||||
_pendingMessages.Enqueue(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = await _api.SendMessage(channelId, blockText, mentions, nonce);
|
||||
result[i] = _messages.Update(msg.Id, channelId, msg);
|
||||
result[i].Nonce = nonce;
|
||||
try { RaiseMessageSent(result[i]); } catch { }
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary> Edits a message the provided message. </summary>
|
||||
public Task EditMessage(Message message, string text)
|
||||
=> EditMessage(message?.ChannelId, message?.Id, text, new string[0]);
|
||||
/// <summary> Edits a message the provided message. </summary>
|
||||
public Task EditMessage(Channel channel, string messageId, string text)
|
||||
=> EditMessage(channel?.Id, messageId, text, new string[0]);
|
||||
/// <summary> Edits a message the provided message. </summary>
|
||||
public Task EditMessage(string channelId, string messageId, string text)
|
||||
=> EditMessage(channelId, messageId, text, new string[0]);
|
||||
/// <summary> Edits a message the provided message, mentioning certain users. </summary>
|
||||
/// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks>
|
||||
public Task EditMessage(Message message, string text, string[] mentions)
|
||||
=> EditMessage(message?.ChannelId, message?.Id, text, mentions);
|
||||
/// <summary> Edits a message the provided message, mentioning certain users. </summary>
|
||||
/// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks>
|
||||
public Task EditMessage(Channel channel, string messageId, string text, string[] mentions)
|
||||
=> EditMessage(channel?.Id, messageId, text, mentions);
|
||||
/// <summary> Edits a message the provided message, mentioning certain users. </summary>
|
||||
/// <remarks> While not required, it is recommended to include a mention reference in the text (see User.Mention). </remarks>
|
||||
public async Task EditMessage(string channelId, string messageId, string text, string[] mentions)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (messageId == null) throw new ArgumentNullException(nameof(messageId));
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
if (mentions == null) throw new ArgumentNullException(nameof(mentions));
|
||||
|
||||
if (text.Length > DiscordAPI.MaxMessageSize)
|
||||
text = text.Substring(0, DiscordAPI.MaxMessageSize);
|
||||
|
||||
var msg = await _api.EditMessage(channelId, messageId, text, mentions);
|
||||
_messages.Update(msg.Id, channelId, msg);
|
||||
}
|
||||
|
||||
/// <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<Message> DeleteMessage(string channelId, string msgId)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (msgId == null) throw new ArgumentNullException(nameof(msgId));
|
||||
|
||||
try
|
||||
{
|
||||
await _api.DeleteMessage(channelId, msgId);
|
||||
return _messages.Remove(msgId);
|
||||
}
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.InternalServerError) { } //TODO: Remove me - temporary fix for deleting nonexisting messages
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary> Sends a file to the provided channel. </summary>
|
||||
public Task SendFile(Channel channel, string path)
|
||||
=> SendFile(channel?.Id, path);
|
||||
/// <summary> Sends a file to the provided channel. </summary>
|
||||
public Task SendFile(string channelId, string path)
|
||||
{
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
return SendFile(channelId, File.OpenRead(path), Path.GetFileName(path));
|
||||
}
|
||||
/// <summary> Reads a stream and sends it to the provided channel as a file. </summary>
|
||||
/// <remarks> It is highly recommended that this stream be cached in memory or on disk, or the request may time out. </remarks>
|
||||
public Task SendFile(Channel channel, Stream stream, string filename = null)
|
||||
=> SendFile(channel?.Id, stream, filename);
|
||||
/// <summary> Reads a stream and sends it to the provided channel as a file. </summary>
|
||||
/// <remarks> It is highly recommended that this stream be cached in memory or on disk, or the request may time out. </remarks>
|
||||
public Task SendFile(string channelId, Stream stream, string filename = null)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||
if (filename == null) throw new ArgumentNullException(nameof(filename));
|
||||
|
||||
return _api.SendFile(channelId, stream, filename);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
=> DownloadMessages(channel.Id, count);
|
||||
/// <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)
|
||||
{
|
||||
CheckReady();
|
||||
if (channelId == null) throw new NullReferenceException(nameof(channelId));
|
||||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
if (count == 0) return new Message[0];
|
||||
|
||||
Channel channel = GetChannel(channelId);
|
||||
if (channel != null && channel.Type == ChannelTypes.Text)
|
||||
{
|
||||
try
|
||||
{
|
||||
var msgs = await _api.GetMessages(channel.Id, count);
|
||||
return msgs.OrderBy(x => x.Timestamp)
|
||||
.Select(x =>
|
||||
{
|
||||
var msg = _messages.Update(x.Id, x.ChannelId, x);
|
||||
var user = msg.User;
|
||||
if (user != null)
|
||||
user.UpdateActivity(x.Timestamp);
|
||||
return msg;
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
catch (HttpException) { } //Bad Permissions?
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//Voice
|
||||
/// <summary> Mutes a user on the provided server. </summary>
|
||||
public Task Mute(Server server, User user)
|
||||
=> Mute(server?.Id, user?.Id);
|
||||
/// <summary> Mutes a user on the provided server. </summary>
|
||||
public Task Mute(Server server, string userId)
|
||||
=> Mute(server?.Id, userId);
|
||||
/// <summary> Mutes a user on the provided server. </summary>
|
||||
public Task Mute(string server, User user)
|
||||
=> Mute(server, user?.Id);
|
||||
/// <summary> Mutes a user on the provided server. </summary>
|
||||
public Task Mute(string serverId, string userId)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
|
||||
return _api.Mute(serverId, userId);
|
||||
}
|
||||
|
||||
/// <summary> Unmutes a user on the provided server. </summary>
|
||||
public Task Unmute(Server server, User user)
|
||||
=> Unmute(server?.Id, user?.Id);
|
||||
/// <summary> Unmutes a user on the provided server. </summary>
|
||||
public Task Unmute(Server server, string userId)
|
||||
=> Unmute(server?.Id, userId);
|
||||
/// <summary> Unmutes a user on the provided server. </summary>
|
||||
public Task Unmute(string server, User user)
|
||||
=> Unmute(server, user?.Id);
|
||||
/// <summary> Unmutes a user on the provided server. </summary>
|
||||
public Task Unmute(string serverId, string userId)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
|
||||
return _api.Unmute(serverId, userId);
|
||||
}
|
||||
|
||||
/// <summary> Deafens a user on the provided server. </summary>
|
||||
public Task Deafen(Server server, User user)
|
||||
=> Deafen(server?.Id, user?.Id);
|
||||
/// <summary> Deafens a user on the provided server. </summary>
|
||||
public Task Deafen(Server server, string userId)
|
||||
=> Deafen(server?.Id, userId);
|
||||
/// <summary> Deafens a user on the provided server. </summary>
|
||||
public Task Deafen(string server, User user)
|
||||
=> Deafen(server, user?.Id);
|
||||
/// <summary> Deafens a user on the provided server. </summary>
|
||||
public Task Deafen(string serverId, string userId)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
|
||||
return _api.Deafen(serverId, userId);
|
||||
}
|
||||
|
||||
/// <summary> Undeafens a user on the provided server. </summary>
|
||||
public Task Undeafen(Server server, User user)
|
||||
=> Undeafen(server?.Id, user?.Id);
|
||||
/// <summary> Undeafens a user on the provided server. </summary>
|
||||
public Task Undeafen(Server server, string userId)
|
||||
=> Undeafen(server?.Id, userId);
|
||||
/// <summary> Undeafens a user on the provided server. </summary>
|
||||
public Task Undeafen(string server, User user)
|
||||
=> Undeafen(server, user?.Id);
|
||||
/// <summary> Undeafens a user on the provided server. </summary>
|
||||
public Task Undeafen(string serverId, string userId)
|
||||
{
|
||||
CheckReady();
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (userId == null) throw new ArgumentNullException(nameof(userId));
|
||||
|
||||
return _api.Undeafen(serverId, userId);
|
||||
}
|
||||
|
||||
#if !DNXCORE50
|
||||
public Task JoinVoiceServer(Server server, Channel channel)
|
||||
=> JoinVoiceServer(server?.Id, channel?.Id);
|
||||
public Task JoinVoiceServer(Server server, string channelId)
|
||||
=> JoinVoiceServer(server?.Id, channelId);
|
||||
public Task JoinVoiceServer(string serverId, Channel channel)
|
||||
=> JoinVoiceServer(serverId, channel?.Id);
|
||||
public async Task JoinVoiceServer(string serverId, string channelId)
|
||||
{
|
||||
CheckReady();
|
||||
if (!_config.EnableVoice) throw new InvalidOperationException("Voice is not enabled for this client.");
|
||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
|
||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId));
|
||||
|
||||
await LeaveVoiceServer();
|
||||
_currentVoiceServerId = serverId;
|
||||
_webSocket.JoinVoice(serverId, channelId);
|
||||
}
|
||||
|
||||
public async Task LeaveVoiceServer()
|
||||
{
|
||||
if (!_config.EnableVoice) throw new InvalidOperationException("Voice is not enabled for this client.");
|
||||
|
||||
await _voiceWebSocket.DisconnectAsync();
|
||||
if (_currentVoiceServerId != null)
|
||||
_webSocket.LeaveVoice();
|
||||
_currentVoiceServerId = null;
|
||||
_currentVoiceToken = null;
|
||||
}
|
||||
|
||||
/// <summary> Sends a PCM frame to the voice server. </summary>
|
||||
/// <param name="data">PCM frame to send.</param>
|
||||
/// <param name="count">Number of bytes in this frame. </param>
|
||||
public void SendVoicePCM(byte[] data, int count)
|
||||
{
|
||||
CheckReady();
|
||||
if (!_config.EnableVoice) throw new InvalidOperationException("Voice is not enabled for this client.");
|
||||
if (count == 0) return;
|
||||
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.VoiceOutput, $"Queued {count} bytes for voice output.");
|
||||
_voiceWebSocket.SendPCMFrame(data, count);
|
||||
}
|
||||
|
||||
/// <summary> Clears the PCM buffer. </summary>
|
||||
public void ClearVoicePCM()
|
||||
{
|
||||
CheckReady();
|
||||
if (!_config.EnableVoice) throw new InvalidOperationException("Voice is not enabled for this client.");
|
||||
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.VoiceOutput, $"Cleared the voice buffer.");
|
||||
_voiceWebSocket.ClearPCMFrames();
|
||||
}
|
||||
#endif
|
||||
|
||||
//Profile
|
||||
/// <summary> Changes your username to newName. </summary>
|
||||
public async Task ChangeUsername(string newName, string currentEmail, string currentPassword)
|
||||
{
|
||||
CheckReady();
|
||||
var response = await _api.ChangeUsername(newName, currentEmail, currentPassword);
|
||||
_users.Update(response.Id, response);
|
||||
}
|
||||
/// <summary> Changes your email to newEmail. </summary>
|
||||
public async Task ChangeEmail(string newEmail, string currentPassword)
|
||||
{
|
||||
CheckReady();
|
||||
var response = await _api.ChangeEmail(newEmail, currentPassword);
|
||||
_users.Update(response.Id, response);
|
||||
}
|
||||
/// <summary> Changes your password to newPassword. </summary>
|
||||
public async Task ChangePassword(string newPassword, string currentEmail, string currentPassword)
|
||||
{
|
||||
CheckReady();
|
||||
var response = await _api.ChangePassword(newPassword, currentEmail, currentPassword);
|
||||
_users.Update(response.Id, response);
|
||||
}
|
||||
|
||||
/// <summary> Changes your avatar. </summary>
|
||||
/// <remarks>Only supports PNG and JPEG (see AvatarImageType)</remarks>
|
||||
public async Task ChangeAvatar(AvatarImageType imageType, byte[] bytes, string currentEmail, string currentPassword)
|
||||
{
|
||||
CheckReady();
|
||||
var response = await _api.ChangeAvatar(imageType, bytes, currentEmail, currentPassword);
|
||||
_users.Update(response.Id, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
457
src/Discord.Net/DiscordClient.Cache.cs
Normal file
457
src/Discord.Net/DiscordClient.Cache.cs
Normal file
@@ -0,0 +1,457 @@
|
||||
using Discord.API.Models;
|
||||
using Discord.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public partial class DiscordClient
|
||||
{
|
||||
/// <summary> Returns a collection of all users the client can see across all servers. </summary>
|
||||
/// <remarks> This collection does not guarantee any ordering. </remarks>
|
||||
public IEnumerable<User> Users => _users;
|
||||
private AsyncCache<User, API.Models.UserReference> _users;
|
||||
|
||||
/// <summary> Returns a collection of all servers the client is a member of. </summary>
|
||||
/// <remarks> This collection does not guarantee any ordering. </remarks>
|
||||
public IEnumerable<Server> Servers => _servers;
|
||||
private AsyncCache<Server, API.Models.ServerReference> _servers;
|
||||
|
||||
/// <summary> Returns a collection of all channels the client can see across all servers. </summary>
|
||||
/// <remarks> This collection does not guarantee any ordering. </remarks>
|
||||
public IEnumerable<Channel> Channels => _channels;
|
||||
private AsyncCache<Channel, API.Models.ChannelReference> _channels;
|
||||
|
||||
/// <summary> Returns a collection of all messages the client has in cache. </summary>
|
||||
/// <remarks> This collection does not guarantee any ordering. </remarks>
|
||||
public IEnumerable<Message> Messages => _messages;
|
||||
private AsyncCache<Message, API.Models.MessageReference> _messages;
|
||||
|
||||
/// <summary> Returns a collection of all roles the client can see across all servers. </summary>
|
||||
/// <remarks> This collection does not guarantee any ordering. </remarks>
|
||||
public IEnumerable<Role> Roles => _roles;
|
||||
private AsyncCache<Role, API.Models.Role> _roles;
|
||||
|
||||
private void CreateCaches()
|
||||
{
|
||||
_servers = new AsyncCache<Server, API.Models.ServerReference>(
|
||||
(key, parentKey) =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created server {key}.");
|
||||
return new Server(key, this);
|
||||
},
|
||||
(server, model) =>
|
||||
{
|
||||
server.Name = model.Name;
|
||||
_channels.Update(server.DefaultChannelId, server.Id, null);
|
||||
if (model is ExtendedServerInfo)
|
||||
{
|
||||
var extendedModel = model as ExtendedServerInfo;
|
||||
server.AFKChannelId = extendedModel.AFKChannelId;
|
||||
server.AFKTimeout = extendedModel.AFKTimeout;
|
||||
server.JoinedAt = extendedModel.JoinedAt ?? DateTime.MinValue;
|
||||
server.OwnerId = extendedModel.OwnerId;
|
||||
server.Region = extendedModel.Region;
|
||||
|
||||
foreach (var role in extendedModel.Roles)
|
||||
_roles.Update(role.Id, model.Id, role);
|
||||
foreach (var channel in extendedModel.Channels)
|
||||
_channels.Update(channel.Id, model.Id, channel);
|
||||
foreach (var membership in extendedModel.Members)
|
||||
{
|
||||
_users.Update(membership.User.Id, membership.User);
|
||||
server.UpdateMember(membership);
|
||||
}
|
||||
foreach (var membership in extendedModel.VoiceStates)
|
||||
server.UpdateMember(membership);
|
||||
foreach (var membership in extendedModel.Presences)
|
||||
server.UpdateMember(membership);
|
||||
}
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated server {server.Name} ({server.Id}).");
|
||||
},
|
||||
server =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed server {server.Name} ({server.Id}).");
|
||||
}
|
||||
);
|
||||
|
||||
_channels = new AsyncCache<Channel, API.Models.ChannelReference>(
|
||||
(key, parentKey) =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
{
|
||||
if (parentKey != null)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created channel {key} in server {parentKey}.");
|
||||
else
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created private channel {key}.");
|
||||
}
|
||||
return new Channel(key, parentKey, this);
|
||||
},
|
||||
(channel, model) =>
|
||||
{
|
||||
channel.Name = model.Name;
|
||||
channel.Type = model.Type;
|
||||
if (model is ChannelInfo)
|
||||
{
|
||||
var extendedModel = model as ChannelInfo;
|
||||
channel.Position = extendedModel.Position;
|
||||
|
||||
if (extendedModel.IsPrivate)
|
||||
{
|
||||
var user = _users.Update(extendedModel.Recipient.Id, extendedModel.Recipient);
|
||||
channel.RecipientId = user.Id;
|
||||
user.PrivateChannelId = channel.Id;
|
||||
}
|
||||
|
||||
if (extendedModel.PermissionOverwrites != null)
|
||||
{
|
||||
channel.PermissionOverwrites = extendedModel.PermissionOverwrites.Select(x => new Channel.PermissionOverwrite
|
||||
{
|
||||
Type = x.Type,
|
||||
Id = x.Id,
|
||||
Deny = new PackedPermissions(x.Deny),
|
||||
Allow = new PackedPermissions(x.Allow)
|
||||
}).ToArray();
|
||||
}
|
||||
else
|
||||
channel.PermissionOverwrites = null;
|
||||
}
|
||||
if (_isDebugMode)
|
||||
{
|
||||
if (channel.IsPrivate)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated private channel {channel.Name} ({channel.Id}).");
|
||||
else
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated channel {channel.Name} ({channel.Id}) in server {channel.Server?.Name} ({channel.ServerId}).");
|
||||
}
|
||||
},
|
||||
channel =>
|
||||
{
|
||||
if (channel.IsPrivate)
|
||||
{
|
||||
var user = channel.Recipient;
|
||||
if (user.PrivateChannelId == channel.Id)
|
||||
user.PrivateChannelId = null;
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed private channel {channel.Name} ({channel.Id}).");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed channel {channel.Name} ({channel.Id}) in server {channel.Server?.Name} ({channel.ServerId}).");
|
||||
}
|
||||
});
|
||||
|
||||
_messages = new AsyncCache<Message, API.Models.MessageReference>(
|
||||
(key, parentKey) =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created message {key} in channel {parentKey}.");
|
||||
return new Message(key, parentKey, this);
|
||||
},
|
||||
(message, model) =>
|
||||
{
|
||||
if (model is API.Models.Message)
|
||||
{
|
||||
var extendedModel = model as API.Models.Message;
|
||||
if (extendedModel.Attachments != null)
|
||||
{
|
||||
message.Attachments = extendedModel.Attachments.Select(x => new Message.Attachment
|
||||
{
|
||||
Id = x.Id,
|
||||
Url = x.Url,
|
||||
ProxyUrl = x.ProxyUrl,
|
||||
Size = x.Size,
|
||||
Filename = x.Filename,
|
||||
Width = x.Width,
|
||||
Height = x.Height
|
||||
}).ToArray();
|
||||
}
|
||||
else
|
||||
message.Attachments = new Message.Attachment[0];
|
||||
if (extendedModel.Embeds != null)
|
||||
{
|
||||
message.Embeds = extendedModel.Embeds.Select(x =>
|
||||
{
|
||||
var embed = new Message.Embed
|
||||
{
|
||||
Url = x.Url,
|
||||
Type = x.Type,
|
||||
Description = x.Description,
|
||||
Title = x.Title
|
||||
};
|
||||
if (x.Provider != null)
|
||||
{
|
||||
embed.Provider = new Message.EmbedReference
|
||||
{
|
||||
Url = x.Provider.Url,
|
||||
Name = x.Provider.Name
|
||||
};
|
||||
}
|
||||
if (x.Author != null)
|
||||
{
|
||||
embed.Author = new Message.EmbedReference
|
||||
{
|
||||
Url = x.Author.Url,
|
||||
Name = x.Author.Name
|
||||
};
|
||||
}
|
||||
if (x.Thumbnail != null)
|
||||
{
|
||||
embed.Thumbnail = new Message.File
|
||||
{
|
||||
Url = x.Thumbnail.Url,
|
||||
ProxyUrl = x.Thumbnail.ProxyUrl,
|
||||
Width = x.Thumbnail.Width,
|
||||
Height = x.Thumbnail.Height
|
||||
};
|
||||
}
|
||||
return embed;
|
||||
}).ToArray();
|
||||
}
|
||||
else
|
||||
message.Embeds = new Message.Embed[0];
|
||||
message.IsMentioningEveryone = extendedModel.IsMentioningEveryone;
|
||||
message.IsTTS = extendedModel.IsTextToSpeech;
|
||||
message.MentionIds = extendedModel.Mentions?.Select(x => x.Id)?.ToArray() ?? new string[0];
|
||||
message.IsMentioningMe = message.MentionIds.Contains(_myId);
|
||||
message.RawText = extendedModel.Content;
|
||||
message.Timestamp = extendedModel.Timestamp;
|
||||
message.EditedTimestamp = extendedModel.EditedTimestamp;
|
||||
if (extendedModel.Author != null)
|
||||
message.UserId = extendedModel.Author.Id;
|
||||
}
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated message {message.Id} in channel {message.Channel?.Name} ({message.ChannelId}).");
|
||||
},
|
||||
message =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed message {message.Id} in channel {message.Channel?.Name} ({message.ChannelId}).");
|
||||
}
|
||||
);
|
||||
|
||||
_roles = new AsyncCache<Role, API.Models.Role>(
|
||||
(key, parentKey) =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created role {key} in server {parentKey}.");
|
||||
return new Role(key, parentKey, this);
|
||||
},
|
||||
(role, model) =>
|
||||
{
|
||||
role.Name = model.Name;
|
||||
role.Permissions.RawValue = (uint)model.Permissions;
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated role {role.Name} ({role.Id}) in server {role.Server?.Name} ({role.ServerId}).");
|
||||
},
|
||||
role =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed role {role.Name} ({role.Id}) in server {role.Server?.Name} ({role.ServerId}).");
|
||||
}
|
||||
);
|
||||
|
||||
_users = new AsyncCache<User, API.Models.UserReference>(
|
||||
(key, parentKey) =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Created user {key}.");
|
||||
return new User(key, this);
|
||||
},
|
||||
(user, model) =>
|
||||
{
|
||||
user.AvatarId = model.Avatar;
|
||||
user.Discriminator = model.Discriminator;
|
||||
user.Name = model.Username;
|
||||
if (model is SelfUserInfo)
|
||||
{
|
||||
var extendedModel = model as SelfUserInfo;
|
||||
user.Email = extendedModel.Email;
|
||||
user.IsVerified = extendedModel.IsVerified;
|
||||
}
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated user {user?.Name} ({user.Id}).");
|
||||
},
|
||||
user =>
|
||||
{
|
||||
if (_isDebugMode)
|
||||
RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed user {user?.Name} ({user.Id}).");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <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 name, string discriminator)
|
||||
{
|
||||
if (name == null || discriminator == null)
|
||||
return null;
|
||||
|
||||
if (name.StartsWith("@"))
|
||||
name = name.Substring(1);
|
||||
|
||||
return _users
|
||||
.Where(x =>
|
||||
string.Equals(x.Name, name, 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)
|
||||
return new User[0];
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary>
|
||||
public Membership GetMember(string serverId, User user)
|
||||
=> GetMember(_servers[serverId], user?.Id);
|
||||
/// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary>
|
||||
public Membership GetMember(string serverId, string userId)
|
||||
=> GetMember(_servers[serverId], userId);
|
||||
/// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary>
|
||||
public Membership GetMember(Server server, User user)
|
||||
=> GetMember(server, user?.Id);
|
||||
/// <summary> Returns the user with the specified id, along with their server-specific data, or null if none was found. </summary>
|
||||
public Membership GetMember(Server server, string userId)
|
||||
{
|
||||
if (server == null || userId == null)
|
||||
return null;
|
||||
return server.GetMember(userId);
|
||||
}
|
||||
|
||||
/// <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<Membership> FindMembers(string serverId, string name)
|
||||
=> FindMembers(GetServer(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<Membership> FindMembers(Server server, string name)
|
||||
{
|
||||
if (server == null || name == null)
|
||||
return new Membership[0];
|
||||
|
||||
if (name.StartsWith("@"))
|
||||
{
|
||||
string name2 = name.Substring(1);
|
||||
return server.Members.Where(x =>
|
||||
{
|
||||
var user = x.User;
|
||||
return string.Equals(user.Name, name, StringComparison.OrdinalIgnoreCase) || string.Equals(user.Name, name2, StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return server.Members.Where(x =>
|
||||
string.Equals(x.User.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the server with the specified id, or null if none was found. </summary>
|
||||
public Server GetServer(string id) => _servers[id];
|
||||
/// <summary> Returns all servers with the specified name. </summary>
|
||||
/// <remarks> Search is case-insensitive. </remarks>
|
||||
public IEnumerable<Server> FindServers(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return new Server[0];
|
||||
return _servers.Where(x =>
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary> Returns the channel with the specified id, or null if none was found. </summary>
|
||||
public Channel GetChannel(string id) => _channels[id];
|
||||
/// <summary> Returns a private channel with the provided user. </summary>
|
||||
public Task<Channel> GetPMChannel(string userId, bool createIfNotExists = false)
|
||||
=> GetPMChannel(_users[userId], createIfNotExists);
|
||||
/// <summary> Returns a private channel with the provided user. </summary>
|
||||
public async Task<Channel> GetPMChannel(User user, bool createIfNotExists = false)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
if (createIfNotExists)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
var channel = user.PrivateChannel;
|
||||
if (channel == null && createIfNotExists)
|
||||
await CreatePMChannel(user);
|
||||
return channel;
|
||||
}
|
||||
/// <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)
|
||||
=> FindChannels(server?.Id, name);
|
||||
/// <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)
|
||||
{
|
||||
if (serverId == null || name == null)
|
||||
return new Channel[0];
|
||||
|
||||
if (name.StartsWith("#"))
|
||||
{
|
||||
string name2 = name.Substring(1);
|
||||
return _channels.Where(x => x.ServerId == serverId &&
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) || string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
return _channels.Where(x => x.ServerId == serverId &&
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the role with the specified id, or null if none was found. </summary>
|
||||
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)
|
||||
{
|
||||
if (serverId == null || name == null)
|
||||
return new Role[0];
|
||||
|
||||
if (name.StartsWith("@"))
|
||||
{
|
||||
string name2 = name.Substring(1);
|
||||
return _roles.Where(x => x.ServerId == serverId &&
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) || string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else
|
||||
{
|
||||
return _roles.Where(x => x.ServerId == serverId &&
|
||||
string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns the message with the specified id, or null if none was found. </summary>
|
||||
public Message GetMessage(string id) => _messages[id];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,12 @@
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public class DisconnectedEventArgs : EventArgs
|
||||
{
|
||||
public readonly bool WasUnexpected;
|
||||
internal DisconnectedEventArgs(bool wasUnexpected) { WasUnexpected = wasUnexpected; }
|
||||
}
|
||||
|
||||
internal abstract partial class DiscordWebSocket
|
||||
{
|
||||
//Debug
|
||||
@@ -19,11 +25,11 @@ namespace Discord
|
||||
if (Connected != null)
|
||||
Connected(this, EventArgs.Empty);
|
||||
}
|
||||
public event EventHandler Disconnected;
|
||||
private void RaiseDisconnected()
|
||||
public event EventHandler<DisconnectedEventArgs> Disconnected;
|
||||
private void RaiseDisconnected(bool wasUnexpected)
|
||||
{
|
||||
if (Disconnected != null)
|
||||
Disconnected(this, EventArgs.Empty);
|
||||
Disconnected(this, new DisconnectedEventArgs(wasUnexpected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@ namespace Discord
|
||||
protected readonly bool _isDebug;
|
||||
private readonly ConcurrentQueue<byte[]> _sendQueue;
|
||||
|
||||
protected volatile CancellationTokenSource _disconnectToken;
|
||||
private volatile ClientWebSocket _webSocket;
|
||||
private volatile Task _tasks;
|
||||
protected CancellationTokenSource _disconnectToken;
|
||||
private ClientWebSocket _webSocket;
|
||||
private DateTime _lastHeartbeat;
|
||||
private Task _task;
|
||||
protected string _host;
|
||||
protected int _timeout, _heartbeatInterval;
|
||||
private DateTime _lastHeartbeat;
|
||||
private bool _isConnected;
|
||||
private bool _isConnected, _wasDisconnectedUnexpected;
|
||||
|
||||
public DiscordWebSocket(DiscordClient client, int timeout, int interval, bool isDebug)
|
||||
{
|
||||
@@ -54,7 +54,7 @@ namespace Discord
|
||||
OnConnect();
|
||||
|
||||
_lastHeartbeat = DateTime.UtcNow;
|
||||
_tasks = Task.Factory.ContinueWhenAll(CreateTasks(), x =>
|
||||
_task = Task.Factory.ContinueWhenAll(CreateTasks(), x =>
|
||||
{
|
||||
if (_isDebug)
|
||||
RaiseOnDebugMessage(DebugMessageType.Connection, $"Disconnected.");
|
||||
@@ -64,6 +64,7 @@ namespace Discord
|
||||
|
||||
_disconnectToken.Dispose();
|
||||
_disconnectToken = null;
|
||||
_wasDisconnectedUnexpected = false;
|
||||
|
||||
//Clear send queue
|
||||
_heartbeatInterval = 0;
|
||||
@@ -76,20 +77,20 @@ namespace Discord
|
||||
if (_isConnected)
|
||||
{
|
||||
_isConnected = false;
|
||||
RaiseDisconnected();
|
||||
RaiseDisconnected(_wasDisconnectedUnexpected);
|
||||
}
|
||||
|
||||
_tasks = null;
|
||||
_task = null;
|
||||
});
|
||||
}
|
||||
public Task ReconnectAsync()
|
||||
=> ConnectAsync(_host);
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
if (_tasks != null)
|
||||
if (_task != null)
|
||||
{
|
||||
try { _disconnectToken.Cancel(); } catch (NullReferenceException) { }
|
||||
try { await _tasks; } catch (NullReferenceException) { }
|
||||
try { await _task; } catch (NullReferenceException) { }
|
||||
}
|
||||
}
|
||||
protected virtual void OnConnect() { }
|
||||
@@ -130,6 +131,7 @@ namespace Discord
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
_wasDisconnectedUnexpected = true;
|
||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
|
||||
RaiseOnDebugMessage(DebugMessageType.Connection, $"Got Close Message ({result.CloseStatus?.ToString() ?? "Unexpected"}, {result.CloseStatusDescription ?? "No Reason"})");
|
||||
return;
|
||||
|
||||
21
src/Discord.Net/Helpers/Extensions.cs
Normal file
21
src/Discord.Net/Helpers/Extensions.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Helpers
|
||||
{
|
||||
internal static class Extensions
|
||||
{
|
||||
public static async Task Wait(this CancellationTokenSource tokenSource)
|
||||
{
|
||||
var token = tokenSource.Token;
|
||||
try { await Task.Delay(-1, token); }
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
public static async Task Wait(this CancellationToken token)
|
||||
{
|
||||
try { await Task.Delay(-1, token); }
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ namespace Discord
|
||||
[JsonIgnore]
|
||||
public User User => _client.GetUser(UserId);
|
||||
/// <summary> Returns true if the current user created this message. </summary>
|
||||
public bool IsAuthor => _client.UserId == UserId;
|
||||
public bool IsAuthor => _client.User?.Id == UserId;
|
||||
|
||||
internal Message(string id, string channelId, DiscordClient client)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Discord
|
||||
/// <summary> Returns the user that first created this server. </summary>
|
||||
public User Owner => _client.GetUser(OwnerId);
|
||||
/// <summary> Returns true if the current user created this server. </summary>
|
||||
public bool IsOwner => _client.UserId == OwnerId;
|
||||
public bool IsOwner => _client.User?.Id == OwnerId;
|
||||
|
||||
/// <summary> Returns the id of the AFK voice channel for this server (see AFKTimeout). </summary>
|
||||
public string AFKChannelId { get; internal set; }
|
||||
@@ -69,7 +69,7 @@ namespace Discord
|
||||
_members = new AsyncCache<Membership, API.Models.MemberInfo>(
|
||||
(key, parentKey) =>
|
||||
{
|
||||
if (_client.Config.EnableDebug)
|
||||
if (_client.IsDebugMode)
|
||||
_client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Created user {key} in server {parentKey}.");
|
||||
return new Membership(parentKey, key, _client);
|
||||
},
|
||||
@@ -108,12 +108,12 @@ namespace Discord
|
||||
member.IsDeafened = extendedModel.IsDeafened;
|
||||
member.IsMuted = extendedModel.IsMuted;
|
||||
}
|
||||
if (_client.Config.EnableDebug)
|
||||
if (_client.IsDebugMode)
|
||||
_client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated user {member.User?.Name} ({member.UserId}) in server {member.Server?.Name} ({member.ServerId}).");
|
||||
},
|
||||
(member) =>
|
||||
{
|
||||
if (_client.Config.EnableDebug)
|
||||
if (_client.IsDebugMode)
|
||||
_client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed user {member.User?.Name} ({member.UserId}) in server {member.Server?.Name} ({member.ServerId}).");
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user