@@ -13,32 +13,34 @@ | |||
public const string Channels = "channels"; | |||
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, 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 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 ChannelTyping(string channelId) => $"channels/{channelId}/typing"; | |||
public const string Servers = "guilds"; | |||
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 ServerInvites(string serverId) => $"guilds/{serverId}/invites"; | |||
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 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 static string Invite(string inviteId) => $"invite/{inviteId}"; | |||
public static string InviteUrl(string inviteId) => $"https://discord.gg/{inviteId}"; | |||
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 UserChannels(string userId) => $"users/{userId}/channels"; | |||
public static string UserMe => $"users/@me"; | |||
public const string Voice = "voice"; | |||
public const string VoiceRegions = "voice/regions"; | |||
//public const string VoiceIce = "voice/ice"; | |||
@@ -4,6 +4,7 @@ | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
namespace Discord.API | |||
{ | |||
@@ -53,6 +54,7 @@ namespace Discord.API | |||
//Get | |||
public class GetInviteResponse : InviteReference { } | |||
public class GetInvitesResponse : List<InviteReference> { } | |||
//Accept | |||
public class AcceptInviteResponse : InviteReference { } | |||
@@ -92,11 +92,14 @@ namespace Discord | |||
var request = new ReorderChannelsRequest(channels); | |||
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)); | |||
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 | |||
@@ -123,6 +126,12 @@ namespace Discord | |||
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) | |||
{ | |||
if (inviteId == null) throw new ArgumentNullException(nameof(inviteId)); | |||
@@ -1,4 +1,5 @@ | |||
using System; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Threading.Tasks; | |||
@@ -10,8 +11,6 @@ namespace Discord | |||
/// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks> | |||
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)); | |||
CheckReady(); | |||
@@ -30,6 +29,22 @@ namespace Discord | |||
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> | |||
/// <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> | |||
@@ -93,6 +93,7 @@ namespace Discord | |||
{ | |||
if (channel == null) throw new ArgumentNullException(nameof(channel)); | |||
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(); | |||
return SendMessage(channel, text, false); | |||
@@ -102,6 +103,7 @@ namespace Discord | |||
{ | |||
if (channel == null) throw new ArgumentNullException(nameof(channel)); | |||
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(); | |||
return SendMessage(channel, text, false); | |||
@@ -111,7 +113,8 @@ namespace Discord | |||
{ | |||
if (user == null) throw new ArgumentNullException(nameof(user)); | |||
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); | |||
return await SendMessage(channel, text).ConfigureAwait(false); | |||
@@ -164,6 +167,8 @@ namespace Discord | |||
public Task EditMessage(Message message, string text) | |||
{ | |||
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(); | |||
if (text != null && text.Length > MaxMessageSize) | |||
@@ -210,7 +215,7 @@ namespace Discord | |||
{ | |||
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 => | |||
{ | |||
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)); | |||
} | |||
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) | |||
{ | |||
if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||
@@ -106,13 +106,13 @@ namespace Discord | |||
UserUnbanned += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, | |||
$"Unbanned User: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); | |||
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, | |||
$"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, | |||
$"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, | |||
$"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, | |||
"Profile Updated"); | |||
} | |||
@@ -55,11 +55,6 @@ namespace Discord | |||
public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); | |||
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> | |||
[JsonIgnore] | |||
public IEnumerable<User> Members => _members.Select(x => x.Value); | |||
@@ -85,7 +80,6 @@ namespace Discord | |||
//Local Cache | |||
_bans = new ConcurrentDictionary<string, bool>(); | |||
_invites = new ConcurrentDictionary<string, Invite>(); | |||
} | |||
internal override void LoadReferences() | |||
{ | |||
@@ -113,11 +107,6 @@ namespace Discord | |||
roles.Clear(); | |||
//Local Cache | |||
var invites = _invites; | |||
foreach (var invite in invites) | |||
invite.Value.Uncache(); | |||
invites.Clear(); | |||
_bans.Clear(); | |||
_afkChannel.Unload(); | |||
@@ -218,9 +207,6 @@ namespace Discord | |||
_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) | |||
{ | |||
if (_members.TryAdd(user.Id, user)) | |||
@@ -61,7 +61,8 @@ namespace Discord | |||
private readonly Reference<Server> _server; | |||
[JsonIgnore] | |||
public Channel VoiceChannel { get; private set; } | |||
public Channel VoiceChannel => _voiceChannel.Value; | |||
private Reference<Channel> _voiceChannel; | |||
[JsonIgnore] | |||
public IEnumerable<Role> Roles => _roles.Select(x => x.Value); | |||
@@ -130,6 +131,8 @@ namespace Discord | |||
if (Id == _client.CurrentUserId) | |||
x.CurrentUser = null; | |||
}); | |||
_voiceChannel = new Reference<Channel>(x => _client.Channels[x]); | |||
Status = UserStatus.Offline; | |||
_channels = new ConcurrentDictionary<string, Channel>(); | |||
if (serverId != null) | |||
@@ -210,16 +213,16 @@ namespace Discord | |||
SessionId = model.SessionId; | |||
if (model.Token != null) | |||
Token = model.Token; | |||
if (model.ChannelId != null) | |||
VoiceChannel = _client.Channels[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; | |||
} | |||
_voiceChannel.Id = model.ChannelId; //Can be null | |||
} | |||
private void UpdateRoles(IEnumerable<Role> roles) | |||
{ | |||
if (_server.Id != null) | |||