@@ -13,32 +13,34 @@ | |||||
public const string Channels = "channels"; | public const string Channels = "channels"; | ||||
public static string Channel(string channelId) => $"channels/{channelId}"; | public static string Channel(string channelId) => $"channels/{channelId}"; | ||||
public static string ChannelTyping(string channelId) => $"channels/{channelId}/typing"; | |||||
public static string ChannelInvites(string channelId) => $"channels/{channelId}/invites"; | |||||
public static string ChannelMessages(string channelId) => $"channels/{channelId}/messages"; | public static string ChannelMessages(string channelId) => $"channels/{channelId}/messages"; | ||||
public static string ChannelMessages(string channelId, int limit) => $"channels/{channelId}/messages?limit={limit}"; | public static string ChannelMessages(string channelId, int limit) => $"channels/{channelId}/messages?limit={limit}"; | ||||
public static string ChannelMessages(string channelId, int limit, string beforeId) => $"channels/{channelId}/messages?limit={limit}&before={beforeId}"; | |||||
public static string ChannelMessage(string channelId, string msgId) => $"channels/{channelId}/messages/{msgId}"; | public static string ChannelMessage(string channelId, string msgId) => $"channels/{channelId}/messages/{msgId}"; | ||||
public static string ChannelMessageAck(string channelId, string msgId) => $"channels/{channelId}/messages/{msgId}/ack"; | public static string ChannelMessageAck(string channelId, string msgId) => $"channels/{channelId}/messages/{msgId}/ack"; | ||||
public static string ChannelInvites(string channelId) => $"channels/{channelId}/invites"; | |||||
public static string ChannelPermission(string channelId, string userOrRoleId) => $"channels/{channelId}/permissions/{userOrRoleId}"; | public static string ChannelPermission(string channelId, string userOrRoleId) => $"channels/{channelId}/permissions/{userOrRoleId}"; | ||||
public static string ChannelTyping(string channelId) => $"channels/{channelId}/typing"; | |||||
public const string Servers = "guilds"; | public const string Servers = "guilds"; | ||||
public static string Server(string serverId) => $"guilds/{serverId}"; | public static string Server(string serverId) => $"guilds/{serverId}"; | ||||
public static string ServerBan(string serverId, string userId) => $"guilds/{serverId}/bans/{userId}"; | |||||
public static string ServerChannels(string serverId) => $"guilds/{serverId}/channels"; | public static string ServerChannels(string serverId) => $"guilds/{serverId}/channels"; | ||||
public static string ServerInvites(string serverId) => $"guilds/{serverId}/invites"; | |||||
public static string ServerMember(string serverId, string userId) => $"guilds/{serverId}/members/{userId}"; | public static string ServerMember(string serverId, string userId) => $"guilds/{serverId}/members/{userId}"; | ||||
public static string ServerBan(string serverId, string userId) => $"guilds/{serverId}/bans/{userId}"; | |||||
public static string ServerPrune(string serverId, int days) => $"guilds/{serverId}/prune?days={days}"; | |||||
public static string ServerRoles(string serverId) => $"guilds/{serverId}/roles"; | public static string ServerRoles(string serverId) => $"guilds/{serverId}/roles"; | ||||
public static string ServerRole(string serverId, string roleId) => $"guilds/{serverId}/roles/{roleId}"; | public static string ServerRole(string serverId, string roleId) => $"guilds/{serverId}/roles/{roleId}"; | ||||
public static string ServerPrune(string serverId, int days) => $"guilds/{serverId}/prune?days={days}"; | |||||
public const string Invites = "invite"; | public const string Invites = "invite"; | ||||
public static string Invite(string inviteId) => $"invite/{inviteId}"; | public static string Invite(string inviteId) => $"invite/{inviteId}"; | ||||
public static string InviteUrl(string inviteId) => $"https://discord.gg/{inviteId}"; | public static string InviteUrl(string inviteId) => $"https://discord.gg/{inviteId}"; | ||||
public const string Users = "users"; | public const string Users = "users"; | ||||
public static string UserMe => $"users/@me"; | |||||
public static string UserChannels(string userId) => $"users/{userId}/channels"; | |||||
public static string UserAvatar(string userId, string avatarId) => $"users/{userId}/avatars/{avatarId}.jpg"; | public static string UserAvatar(string userId, string avatarId) => $"users/{userId}/avatars/{avatarId}.jpg"; | ||||
public static string UserChannels(string userId) => $"users/{userId}/channels"; | |||||
public static string UserMe => $"users/@me"; | |||||
public const string Voice = "voice"; | public const string Voice = "voice"; | ||||
public const string VoiceRegions = "voice/regions"; | public const string VoiceRegions = "voice/regions"; | ||||
//public const string VoiceIce = "voice/ice"; | //public const string VoiceIce = "voice/ice"; | ||||
@@ -4,6 +4,7 @@ | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
namespace Discord.API | namespace Discord.API | ||||
{ | { | ||||
@@ -53,6 +54,7 @@ namespace Discord.API | |||||
//Get | //Get | ||||
public class GetInviteResponse : InviteReference { } | public class GetInviteResponse : InviteReference { } | ||||
public class GetInvitesResponse : List<InviteReference> { } | |||||
//Accept | //Accept | ||||
public class AcceptInviteResponse : InviteReference { } | public class AcceptInviteResponse : InviteReference { } | ||||
@@ -92,11 +92,14 @@ namespace Discord | |||||
var request = new ReorderChannelsRequest(channels); | var request = new ReorderChannelsRequest(channels); | ||||
return _rest.Patch(Endpoints.ServerChannels(serverId), request); | return _rest.Patch(Endpoints.ServerChannels(serverId), request); | ||||
} | } | ||||
public Task<GetMessagesResponse> GetMessages(string channelId, int count) | |||||
public Task<GetMessagesResponse> GetMessages(string channelId, int count, string beforeMessageId = null) | |||||
{ | { | ||||
if (channelId == null) throw new ArgumentNullException(nameof(channelId)); | if (channelId == null) throw new ArgumentNullException(nameof(channelId)); | ||||
return _rest.Get<GetMessagesResponse>(Endpoints.ChannelMessages(channelId, count)); | |||||
if (beforeMessageId != null) | |||||
return _rest.Get<GetMessagesResponse>(Endpoints.ChannelMessages(channelId, count, beforeMessageId)); | |||||
else | |||||
return _rest.Get<GetMessagesResponse>(Endpoints.ChannelMessages(channelId, count)); | |||||
} | } | ||||
//Incidents | //Incidents | ||||
@@ -123,6 +126,12 @@ namespace Discord | |||||
return _rest.Get<GetInviteResponse>(Endpoints.Invite(inviteIdOrXkcd)); | return _rest.Get<GetInviteResponse>(Endpoints.Invite(inviteIdOrXkcd)); | ||||
} | } | ||||
public Task<GetInvitesResponse> GetInvites(string serverId) | |||||
{ | |||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||||
return _rest.Get<GetInvitesResponse>(Endpoints.ServerInvites(serverId)); | |||||
} | |||||
public Task<AcceptInviteResponse> AcceptInvite(string inviteId) | public Task<AcceptInviteResponse> AcceptInvite(string inviteId) | ||||
{ | { | ||||
if (inviteId == null) throw new ArgumentNullException(nameof(inviteId)); | if (inviteId == null) throw new ArgumentNullException(nameof(inviteId)); | ||||
@@ -1,4 +1,5 @@ | |||||
using System; | using System; | ||||
using System.Linq; | |||||
using System.Net; | using System.Net; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
@@ -10,8 +11,6 @@ namespace Discord | |||||
/// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks> | /// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks> | ||||
public async Task<Invite> GetInvite(string inviteIdOrXkcd) | public async Task<Invite> GetInvite(string inviteIdOrXkcd) | ||||
{ | { | ||||
//This doesn't work well if it's an invite to a different server! | |||||
if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd)); | if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd)); | ||||
CheckReady(); | CheckReady(); | ||||
@@ -30,6 +29,22 @@ namespace Discord | |||||
return invite; | return invite; | ||||
} | } | ||||
/// <summary> Gets all active (non-expired) invites to a provided server. </summary> | |||||
public async Task<Invite[]> GetInvites(Server server) | |||||
{ | |||||
if (server == null) throw new ArgumentNullException(nameof(server)); | |||||
CheckReady(); | |||||
var response = await _api.GetInvites(server.Id).ConfigureAwait(false); | |||||
return response.Select(x => | |||||
{ | |||||
var invite = new Invite(this, x.Code, x.XkcdPass, x.Guild.Id, x.Inviter?.Id, x.Channel?.Id); | |||||
invite.Cache(); //Builds references | |||||
invite.Update(x); | |||||
return invite; | |||||
}).ToArray(); | |||||
} | |||||
/// <summary> Creates a new invite to the default channel of the provided server. </summary> | /// <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="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="tempMembership"> If true, a user accepting this invite will be kicked from the server after closing their client. </param> | ||||
@@ -93,6 +93,7 @@ namespace Discord | |||||
{ | { | ||||
if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
if (text == null) throw new ArgumentNullException(nameof(text)); | if (text == null) throw new ArgumentNullException(nameof(text)); | ||||
if (text.Length > MaxMessageSize) throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {MaxMessageSize} characters or less."); | |||||
CheckReady(); | CheckReady(); | ||||
return SendMessage(channel, text, false); | return SendMessage(channel, text, false); | ||||
@@ -102,6 +103,7 @@ namespace Discord | |||||
{ | { | ||||
if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
if (text == null) throw new ArgumentNullException(nameof(text)); | if (text == null) throw new ArgumentNullException(nameof(text)); | ||||
if (text.Length > MaxMessageSize) throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {MaxMessageSize} characters or less."); | |||||
CheckReady(); | CheckReady(); | ||||
return SendMessage(channel, text, false); | return SendMessage(channel, text, false); | ||||
@@ -111,7 +113,8 @@ namespace Discord | |||||
{ | { | ||||
if (user == null) throw new ArgumentNullException(nameof(user)); | if (user == null) throw new ArgumentNullException(nameof(user)); | ||||
if (text == null) throw new ArgumentNullException(nameof(text)); | if (text == null) throw new ArgumentNullException(nameof(text)); | ||||
CheckReady(); | |||||
if (text.Length > MaxMessageSize) throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {MaxMessageSize} characters or less."); | |||||
CheckReady(); | |||||
var channel = await CreatePMChannel(user).ConfigureAwait(false); | var channel = await CreatePMChannel(user).ConfigureAwait(false); | ||||
return await SendMessage(channel, text).ConfigureAwait(false); | return await SendMessage(channel, text).ConfigureAwait(false); | ||||
@@ -164,6 +167,8 @@ namespace Discord | |||||
public Task EditMessage(Message message, string text) | public Task EditMessage(Message message, string text) | ||||
{ | { | ||||
if (message == null) throw new ArgumentNullException(nameof(message)); | if (message == null) throw new ArgumentNullException(nameof(message)); | ||||
if (text == null) throw new ArgumentNullException(nameof(text)); | |||||
if (text.Length > MaxMessageSize) throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {MaxMessageSize} characters or less."); | |||||
CheckReady(); | CheckReady(); | ||||
if (text != null && text.Length > MaxMessageSize) | if (text != null && text.Length > MaxMessageSize) | ||||
@@ -210,7 +215,7 @@ namespace Discord | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var msgs = await _api.GetMessages(channel.Id, count).ConfigureAwait(false); | |||||
var msgs = await _api.GetMessages(channel.Id, count, beforeMessageId).ConfigureAwait(false); | |||||
return msgs.Select(x => | return msgs.Select(x => | ||||
{ | { | ||||
Message msg = null; | Message msg = null; | ||||
@@ -172,6 +172,26 @@ namespace Discord | |||||
return _api.EditUser(user.Server?.Id, user.Id, mute: mute, deaf: deaf, roles: roles.Select(x => x.Id)); | return _api.EditUser(user.Server?.Id, user.Id, mute: mute, deaf: deaf, roles: roles.Select(x => x.Id)); | ||||
} | } | ||||
public Task KickUser(User user) | |||||
{ | |||||
if (user == null) throw new ArgumentNullException(nameof(user)); | |||||
return _api.KickUser(user.Server?.Id, user.Id); | |||||
} | |||||
public Task BanUser(User user) | |||||
{ | |||||
if (user == null) throw new ArgumentNullException(nameof(user)); | |||||
return _api.BanUser(user.Server?.Id, user.Id); | |||||
} | |||||
public Task UnbanUser(Server server, string userId) | |||||
{ | |||||
if (server == null) throw new ArgumentNullException(nameof(server)); | |||||
if (userId == null) throw new ArgumentNullException(nameof(userId)); | |||||
return _api.UnbanUser(server.Id, userId); | |||||
} | |||||
public async Task<int> PruneUsers(string serverId, int days, bool simulate = false) | public async Task<int> PruneUsers(string serverId, int days, bool simulate = false) | ||||
{ | { | ||||
if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | ||||
@@ -106,13 +106,13 @@ namespace Discord | |||||
UserUnbanned += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserUnbanned += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"Unbanned User: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | $"Unbanned User: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | ||||
UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"User Joined: {e.Server?.Name ?? "[Private]"}/{e.User.Id}"); | |||||
$"User Joined: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); | |||||
UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"User Left: {e.Server?.Name ?? "[Private]"}/{e.User.Id}"); | |||||
$"User Left: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); | |||||
UserUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"User Updated: {e.Server?.Name ?? "[Private]"}/{e.User.Id}"); | |||||
$"User Updated: {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); | |||||
UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
$"User Updated (Voice State): {e.Server?.Name ?? "[Private]"}/{e.User.Id}"); | |||||
$"User Updated (Voice State): {e.Server?.Name ?? "[Private]"}/{e.User.Name}"); | |||||
ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | ||||
"Profile Updated"); | "Profile Updated"); | ||||
} | } | ||||
@@ -55,11 +55,6 @@ namespace Discord | |||||
public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); | public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); | ||||
private ConcurrentDictionary<string, Channel> _channels; | private ConcurrentDictionary<string, Channel> _channels; | ||||
/// <summary> Returns a collection of all invites to this server. </summary> | |||||
[JsonIgnore] | |||||
public IEnumerable<Invite> Invites => _invites.Values; | |||||
private ConcurrentDictionary<string, Invite> _invites; | |||||
/// <summary> Returns a collection of all users within this server with their server-specific data. </summary> | /// <summary> Returns a collection of all users within this server with their server-specific data. </summary> | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<User> Members => _members.Select(x => x.Value); | public IEnumerable<User> Members => _members.Select(x => x.Value); | ||||
@@ -85,7 +80,6 @@ namespace Discord | |||||
//Local Cache | //Local Cache | ||||
_bans = new ConcurrentDictionary<string, bool>(); | _bans = new ConcurrentDictionary<string, bool>(); | ||||
_invites = new ConcurrentDictionary<string, Invite>(); | |||||
} | } | ||||
internal override void LoadReferences() | internal override void LoadReferences() | ||||
{ | { | ||||
@@ -113,11 +107,6 @@ namespace Discord | |||||
roles.Clear(); | roles.Clear(); | ||||
//Local Cache | //Local Cache | ||||
var invites = _invites; | |||||
foreach (var invite in invites) | |||||
invite.Value.Uncache(); | |||||
invites.Clear(); | |||||
_bans.Clear(); | _bans.Clear(); | ||||
_afkChannel.Unload(); | _afkChannel.Unload(); | ||||
@@ -218,9 +207,6 @@ namespace Discord | |||||
_channels.TryRemove(channel.Id, out channel); | _channels.TryRemove(channel.Id, out channel); | ||||
} | } | ||||
internal void AddInvite(Invite invite) => _invites.TryAdd(invite.Id, invite); | |||||
internal void RemoveInvite(Invite invite) => _invites.TryRemove(invite.Id, out invite); | |||||
internal void AddMember(User user) | internal void AddMember(User user) | ||||
{ | { | ||||
if (_members.TryAdd(user.Id, user)) | if (_members.TryAdd(user.Id, user)) | ||||
@@ -61,7 +61,8 @@ namespace Discord | |||||
private readonly Reference<Server> _server; | private readonly Reference<Server> _server; | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public Channel VoiceChannel { get; private set; } | |||||
public Channel VoiceChannel => _voiceChannel.Value; | |||||
private Reference<Channel> _voiceChannel; | |||||
[JsonIgnore] | [JsonIgnore] | ||||
public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | ||||
@@ -130,6 +131,8 @@ namespace Discord | |||||
if (Id == _client.CurrentUserId) | if (Id == _client.CurrentUserId) | ||||
x.CurrentUser = null; | x.CurrentUser = null; | ||||
}); | }); | ||||
_voiceChannel = new Reference<Channel>(x => _client.Channels[x]); | |||||
Status = UserStatus.Offline; | Status = UserStatus.Offline; | ||||
_channels = new ConcurrentDictionary<string, Channel>(); | _channels = new ConcurrentDictionary<string, Channel>(); | ||||
if (serverId != null) | if (serverId != null) | ||||
@@ -210,16 +213,16 @@ namespace Discord | |||||
SessionId = model.SessionId; | SessionId = model.SessionId; | ||||
if (model.Token != null) | if (model.Token != null) | ||||
Token = model.Token; | Token = model.Token; | ||||
if (model.ChannelId != null) | |||||
VoiceChannel = _client.Channels[model.ChannelId]; | |||||
if (model.IsSelfDeafened != null) | if (model.IsSelfDeafened != null) | ||||
IsSelfDeafened = model.IsSelfDeafened.Value; | IsSelfDeafened = model.IsSelfDeafened.Value; | ||||
if (model.IsSelfMuted != null) | if (model.IsSelfMuted != null) | ||||
IsSelfMuted = model.IsSelfMuted.Value; | IsSelfMuted = model.IsSelfMuted.Value; | ||||
if (model.IsServerSuppressed != null) | if (model.IsServerSuppressed != null) | ||||
IsServerSuppressed = model.IsServerSuppressed.Value; | IsServerSuppressed = model.IsServerSuppressed.Value; | ||||
} | |||||
_voiceChannel.Id = model.ChannelId; //Can be null | |||||
} | |||||
private void UpdateRoles(IEnumerable<Role> roles) | private void UpdateRoles(IEnumerable<Role> roles) | ||||
{ | { | ||||
if (_server.Id != null) | if (_server.Id != null) | ||||