| @@ -82,8 +82,8 @@ | |||||
| <Compile Include="..\Discord.Net\API\Enums\AvatarImageType.cs"> | <Compile Include="..\Discord.Net\API\Enums\AvatarImageType.cs"> | ||||
| <Link>API\Enums\AvatarImageType.cs</Link> | <Link>API\Enums\AvatarImageType.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\API\Enums\ChannelTypes.cs"> | |||||
| <Link>API\Enums\ChannelTypes.cs</Link> | |||||
| <Compile Include="..\Discord.Net\API\Enums\ChannelType.cs"> | |||||
| <Link>API\Enums\ChannelType.cs</Link> | |||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\API\Enums\PermissionTarget.cs"> | <Compile Include="..\Discord.Net\API\Enums\PermissionTarget.cs"> | ||||
| <Link>API\Enums\PermissionTarget.cs</Link> | <Link>API\Enums\PermissionTarget.cs</Link> | ||||
| @@ -91,6 +91,9 @@ | |||||
| <Compile Include="..\Discord.Net\API\Enums\Regions.cs"> | <Compile Include="..\Discord.Net\API\Enums\Regions.cs"> | ||||
| <Link>API\Enums\Regions.cs</Link> | <Link>API\Enums\Regions.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| <Compile Include="..\Discord.Net\API\Enums\StringEnum.cs"> | |||||
| <Link>API\Enums\StringEnum.cs</Link> | |||||
| </Compile> | |||||
| <Compile Include="..\Discord.Net\API\Enums\UserStatus.cs"> | <Compile Include="..\Discord.Net\API\Enums\UserStatus.cs"> | ||||
| <Link>API\Enums\UserStatus.cs</Link> | <Link>API\Enums\UserStatus.cs</Link> | ||||
| </Compile> | </Compile> | ||||
| @@ -0,0 +1,28 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public class ChannelType : StringEnum | |||||
| { | |||||
| /// <summary> A text-only channel. </summary> | |||||
| public static readonly ChannelType Text = new ChannelType("text"); | |||||
| /// <summary> A voice-only channel. </summary> | |||||
| public static readonly ChannelType Voice = new ChannelType("voice"); | |||||
| private ChannelType(string value) | |||||
| : base(value) { } | |||||
| public static ChannelType FromString(string value) | |||||
| { | |||||
| switch (value) | |||||
| { | |||||
| case null: | |||||
| return null; | |||||
| case "text": | |||||
| return ChannelType.Text; | |||||
| case "voice": | |||||
| return ChannelType.Voice; | |||||
| default: | |||||
| return new ChannelType(value); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,10 +0,0 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public static class ChannelTypes | |||||
| { | |||||
| /// <summary> A text-only channel. </summary> | |||||
| public const string Text = "text"; | |||||
| /// <summary> A voice-only channel. </summary> | |||||
| public const string Voice = "voice"; | |||||
| } | |||||
| } | |||||
| @@ -1,8 +1,28 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public static class PermissionTarget | |||||
| { | |||||
| public const string Role = "role"; | |||||
| public const string Member = "member"; | |||||
| public class PermissionTarget : StringEnum | |||||
| { | |||||
| /// <summary> A text-only channel. </summary> | |||||
| public static readonly PermissionTarget Role = new PermissionTarget("role"); | |||||
| /// <summary> A voice-only channel. </summary> | |||||
| public static readonly PermissionTarget Member = new PermissionTarget("member"); | |||||
| private PermissionTarget(string value) | |||||
| : base(value) { } | |||||
| public static PermissionTarget FromString(string value) | |||||
| { | |||||
| switch (value) | |||||
| { | |||||
| case null: | |||||
| return null; | |||||
| case "role": | |||||
| return PermissionTarget.Role; | |||||
| case "member": | |||||
| return PermissionTarget.Member; | |||||
| default: | |||||
| return new PermissionTarget(value); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,12 +1,38 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public static class Regions | |||||
| public class Region : StringEnum | |||||
| { | { | ||||
| public const string US_West = "us-west"; | |||||
| public const string US_East = "us-east"; | |||||
| public const string Singapore = "singapore"; | |||||
| public const string London = "london"; | |||||
| public const string Sydney = "sydney"; | |||||
| public const string Amsterdam = "amsterdam"; | |||||
| public static readonly Region USWest = new Region("us-west"); | |||||
| public static readonly Region USEast = new Region("us-east"); | |||||
| public static readonly Region Singapore = new Region("singapore"); | |||||
| public static readonly Region London = new Region("london"); | |||||
| public static readonly Region Sydney = new Region("sydney"); | |||||
| public static readonly Region Amsterdam = new Region("amsterdam"); | |||||
| private Region(string value) | |||||
| : base(value) { } | |||||
| public static Region FromString(string value) | |||||
| { | |||||
| switch (value) | |||||
| { | |||||
| case null: | |||||
| return null; | |||||
| case "us-west": | |||||
| return Region.USWest; | |||||
| case "us-east": | |||||
| return Region.USEast; | |||||
| case "singapore": | |||||
| return Region.Singapore; | |||||
| case "london": | |||||
| return Region.London; | |||||
| case "sydney": | |||||
| return Region.Sydney; | |||||
| case "amsterdam": | |||||
| return Region.Amsterdam; | |||||
| default: | |||||
| return new Region(value); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,52 @@ | |||||
| namespace Discord | |||||
| { | |||||
| public abstract class StringEnum | |||||
| { | |||||
| private string _value; | |||||
| protected StringEnum(string value) | |||||
| { | |||||
| _value = value; | |||||
| } | |||||
| public string Value => _value; | |||||
| public override string ToString() => _value; | |||||
| public override bool Equals(object obj) | |||||
| { | |||||
| var enum2 = obj as StringEnum; | |||||
| if (enum2 == (StringEnum)null) | |||||
| return false; | |||||
| else | |||||
| return _value == enum2._value; | |||||
| } | |||||
| public override int GetHashCode() | |||||
| { | |||||
| return _value.GetHashCode(); | |||||
| } | |||||
| public static bool operator ==(StringEnum a, StringEnum b) | |||||
| { | |||||
| return a?._value == b?._value; | |||||
| } | |||||
| public static bool operator !=(StringEnum a, StringEnum b) | |||||
| { | |||||
| return a?._value != b?._value; | |||||
| } | |||||
| public static bool operator ==(StringEnum a, string b) | |||||
| { | |||||
| return a?._value == b; | |||||
| } | |||||
| public static bool operator !=(StringEnum a, string b) | |||||
| { | |||||
| return a?._value != b; | |||||
| } | |||||
| public static bool operator ==(string a, StringEnum b) | |||||
| { | |||||
| return a == b?._value; | |||||
| } | |||||
| public static bool operator !=(string a, StringEnum b) | |||||
| { | |||||
| return a != b?._value; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,12 +1,32 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public static class UserStatus | |||||
| public class UserStatus : StringEnum | |||||
| { | { | ||||
| /// <summary> User is currently online and active. </summary> | /// <summary> User is currently online and active. </summary> | ||||
| public const string Online = "online"; | |||||
| public static readonly UserStatus Online = new UserStatus("online"); | |||||
| /// <summary> User is currently online but inactive. </summary> | /// <summary> User is currently online but inactive. </summary> | ||||
| public const string Idle = "idle"; | |||||
| public static readonly UserStatus Idle = new UserStatus("idle"); | |||||
| /// <summary> User is offline. </summary> | /// <summary> User is offline. </summary> | ||||
| public const string Offline = "offline"; | |||||
| public static readonly UserStatus Offline = new UserStatus("offline"); | |||||
| private UserStatus(string value) | |||||
| : base(value) { } | |||||
| public static UserStatus FromString(string value) | |||||
| { | |||||
| switch (value) | |||||
| { | |||||
| case null: | |||||
| return null; | |||||
| case "online": | |||||
| return UserStatus.Online; | |||||
| case "idle": | |||||
| return UserStatus.Idle; | |||||
| case "offline": | |||||
| return UserStatus.Offline; | |||||
| default: | |||||
| return new UserStatus(value); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -35,8 +35,8 @@ namespace Discord | |||||
| /// <summary> Bans a user from the provided server. </summary> | /// <summary> Bans a user from the provided server. </summary> | ||||
| public Task Ban(Member member) | public Task Ban(Member member) | ||||
| { | { | ||||
| CheckReady(); | |||||
| if (member == null) throw new ArgumentNullException(nameof(member)); | if (member == null) throw new ArgumentNullException(nameof(member)); | ||||
| CheckReady(); | |||||
| return _api.Ban(member.ServerId, member.Id); | return _api.Ban(member.ServerId, member.Id); | ||||
| } | } | ||||
| @@ -44,8 +44,8 @@ namespace Discord | |||||
| /// <summary> Unbans a user from the provided server. </summary> | /// <summary> Unbans a user from the provided server. </summary> | ||||
| public async Task Unban(Member member) | public async Task Unban(Member member) | ||||
| { | { | ||||
| CheckReady(); | |||||
| if (member == null) throw new ArgumentNullException(nameof(member)); | if (member == null) throw new ArgumentNullException(nameof(member)); | ||||
| CheckReady(); | |||||
| try { await _api.Unban(member.ServerId, member.Id).ConfigureAwait(false); } | try { await _api.Unban(member.ServerId, member.Id).ConfigureAwait(false); } | ||||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | ||||
| @@ -56,7 +56,7 @@ namespace Discord | |||||
| /// <summary> Returns all channels with the specified server and name. </summary> | /// <summary> Returns all channels with the specified server and name. </summary> | ||||
| /// <remarks> Name formats supported: Name and #Name. Search is case-insensitive. </remarks> | /// <remarks> Name formats supported: Name and #Name. Search is case-insensitive. </remarks> | ||||
| public IEnumerable<Channel> FindChannels(Server server, string name, string type = null) | |||||
| public IEnumerable<Channel> FindChannels(Server server, string name, ChannelType type = null) | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| @@ -74,21 +74,21 @@ namespace Discord | |||||
| string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); | string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); | ||||
| } | } | ||||
| if (type != null) | |||||
| if (type != (string)null) | |||||
| result = result.Where(x => x.Type == type); | result = result.Where(x => x.Type == type); | ||||
| return result; | return result; | ||||
| } | } | ||||
| /// <summary> Creates a new channel with the provided name and type (see ChannelTypes). </summary> | |||||
| public async Task<Channel> CreateChannel(Server server, string name, string type = ChannelTypes.Text) | |||||
| /// <summary> Creates a new channel with the provided name and type. </summary> | |||||
| public async Task<Channel> CreateChannel(Server server, string name, ChannelType type) | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| if (type == null) throw new ArgumentNullException(nameof(type)); | |||||
| if (type == (string)null) throw new ArgumentNullException(nameof(type)); | |||||
| CheckReady(); | CheckReady(); | ||||
| var response = await _api.CreateChannel(server.Id, name, type).ConfigureAwait(false); | |||||
| var response = await _api.CreateChannel(server.Id, name, type.Value).ConfigureAwait(false); | |||||
| var channel = _channels.GetOrAdd(response.Id, response.GuildId, response.Recipient?.Id); | var channel = _channels.GetOrAdd(response.Id, response.GuildId, response.Recipient?.Id); | ||||
| channel.Update(response); | channel.Update(response); | ||||
| return channel; | return channel; | ||||
| @@ -153,15 +153,16 @@ namespace Discord | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| if (channels == null) throw new ArgumentNullException(nameof(channels)); | if (channels == null) throw new ArgumentNullException(nameof(channels)); | ||||
| CheckReady(); | |||||
| return _api.ReorderChannels(server.Id, channels.Select(x => x.Id), after.Position); | return _api.ReorderChannels(server.Id, channels.Select(x => x.Id), after.Position); | ||||
| } | } | ||||
| /// <summary> Destroys the provided channel. </summary> | /// <summary> Destroys the provided channel. </summary> | ||||
| public async Task<Channel> DestroyChannel(Channel channel) | public async Task<Channel> DestroyChannel(Channel channel) | ||||
| { | { | ||||
| CheckReady(); | |||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
| CheckReady(); | |||||
| try { await _api.DestroyChannel(channel.Id).ConfigureAwait(false); } | try { await _api.DestroyChannel(channel.Id).ConfigureAwait(false); } | ||||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | ||||
| @@ -10,8 +10,8 @@ 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) | ||||
| { | { | ||||
| CheckReady(); | |||||
| if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd)); | if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd)); | ||||
| CheckReady(); | |||||
| //Remove trailing slash | //Remove trailing slash | ||||
| if (inviteIdOrXkcd.Length > 0 && inviteIdOrXkcd[inviteIdOrXkcd.Length - 1] == '/') | if (inviteIdOrXkcd.Length > 0 && inviteIdOrXkcd[inviteIdOrXkcd.Length - 1] == '/') | ||||
| @@ -35,6 +35,8 @@ namespace Discord | |||||
| public Task<Invite> CreateInvite(Server server, int maxAge = 1800, int maxUses = 0, bool tempMembership = false, bool hasXkcd = false) | public Task<Invite> CreateInvite(Server server, int maxAge = 1800, int maxUses = 0, bool tempMembership = false, bool hasXkcd = false) | ||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| CheckReady(); | |||||
| return CreateInvite(server.DefaultChannel, maxAge, maxUses, tempMembership, hasXkcd); | return CreateInvite(server.DefaultChannel, maxAge, maxUses, tempMembership, hasXkcd); | ||||
| } | } | ||||
| /// <summary> Creates a new invite to the provided channel. </summary> | /// <summary> Creates a new invite to the provided channel. </summary> | ||||
| @@ -59,8 +61,8 @@ namespace Discord | |||||
| /// <summary> Deletes the provided invite. </summary> | /// <summary> Deletes the provided invite. </summary> | ||||
| public async Task DestroyInvite(Invite invite) | public async Task DestroyInvite(Invite invite) | ||||
| { | { | ||||
| CheckReady(); | |||||
| if (invite == null) throw new ArgumentNullException(nameof(invite)); | if (invite == null) throw new ArgumentNullException(nameof(invite)); | ||||
| CheckReady(); | |||||
| try { await _api.DeleteInvite(invite.Id).ConfigureAwait(false); } | try { await _api.DeleteInvite(invite.Id).ConfigureAwait(false); } | ||||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | ||||
| @@ -69,8 +71,8 @@ namespace Discord | |||||
| /// <summary> Accepts the provided invite. </summary> | /// <summary> Accepts the provided invite. </summary> | ||||
| public Task AcceptInvite(Invite invite) | public Task AcceptInvite(Invite invite) | ||||
| { | { | ||||
| CheckReady(); | |||||
| if (invite == null) throw new ArgumentNullException(nameof(invite)); | if (invite == null) throw new ArgumentNullException(nameof(invite)); | ||||
| CheckReady(); | |||||
| return _api.AcceptInvite(invite.Id); | return _api.AcceptInvite(invite.Id); | ||||
| } | } | ||||
| @@ -190,7 +190,7 @@ namespace Discord | |||||
| CheckReady(); | CheckReady(); | ||||
| if (count == 0) return new Message[0]; | if (count == 0) return new Message[0]; | ||||
| if (channel != null && channel.Type == ChannelTypes.Text) | |||||
| if (channel != null && channel.Type == ChannelType.Text) | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| @@ -8,20 +8,39 @@ namespace Discord | |||||
| public partial class DiscordClient | public partial class DiscordClient | ||||
| { | { | ||||
| public Task SetChannelUserPermissions(Channel channel, Member member, ChannelPermissions allow = null, ChannelPermissions deny = null) | public Task SetChannelUserPermissions(Channel channel, Member member, ChannelPermissions allow = null, ChannelPermissions deny = null) | ||||
| => SetChannelPermissions(channel, member?.Id, PermissionTarget.Member, allow, deny); | |||||
| { | |||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | |||||
| if (member == null) throw new ArgumentNullException(nameof(member)); | |||||
| CheckReady(); | |||||
| return SetChannelPermissions(channel, member?.Id, PermissionTarget.Member, allow, deny); | |||||
| } | |||||
| public Task SetChannelUserPermissions(Channel channel, Member member, DualChannelPermissions permissions = null) | public Task SetChannelUserPermissions(Channel channel, Member member, DualChannelPermissions permissions = null) | ||||
| => SetChannelPermissions(channel, member?.Id, PermissionTarget.Member, permissions?.Allow, permissions?.Deny); | |||||
| { | |||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | |||||
| if (member == null) throw new ArgumentNullException(nameof(member)); | |||||
| CheckReady(); | |||||
| return SetChannelPermissions(channel, member?.Id, PermissionTarget.Member, permissions?.Allow, permissions?.Deny); | |||||
| } | |||||
| public Task SetChannelRolePermissions(Channel channel, Role role, ChannelPermissions allow = null, ChannelPermissions deny = null) | public Task SetChannelRolePermissions(Channel channel, Role role, ChannelPermissions allow = null, ChannelPermissions deny = null) | ||||
| => SetChannelPermissions(channel, role?.Id, PermissionTarget.Role, allow, deny); | |||||
| public Task SetChannelRolePermissions(Channel channel, Role role, DualChannelPermissions permissions = null) | |||||
| => SetChannelPermissions(channel, role?.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny); | |||||
| private async Task SetChannelPermissions(Channel channel, string targetId, string targetType, ChannelPermissions allow = null, ChannelPermissions deny = null) | |||||
| { | { | ||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | |||||
| if (role == null) throw new ArgumentNullException(nameof(role)); | |||||
| CheckReady(); | CheckReady(); | ||||
| return SetChannelPermissions(channel, role?.Id, PermissionTarget.Role, allow, deny); | |||||
| } | |||||
| public Task SetChannelRolePermissions(Channel channel, Role role, DualChannelPermissions permissions = null) | |||||
| { | |||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
| if (targetId == null) throw new ArgumentNullException(nameof(targetId)); | |||||
| if (targetType == null) throw new ArgumentNullException(nameof(targetType)); | |||||
| if (role == null) throw new ArgumentNullException(nameof(role)); | |||||
| CheckReady(); | |||||
| return SetChannelPermissions(channel, role?.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny); | |||||
| } | |||||
| private async Task SetChannelPermissions(Channel channel, string targetId, PermissionTarget targetType, ChannelPermissions allow = null, ChannelPermissions deny = null) | |||||
| { | |||||
| uint allowValue = allow?.RawValue ?? 0; | uint allowValue = allow?.RawValue ?? 0; | ||||
| uint denyValue = deny?.RawValue ?? 0; | uint denyValue = deny?.RawValue ?? 0; | ||||
| bool changed = false; | bool changed = false; | ||||
| @@ -29,7 +48,7 @@ namespace Discord | |||||
| var perms = channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != targetId).FirstOrDefault(); | var perms = channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != targetId).FirstOrDefault(); | ||||
| if (allowValue != 0 || denyValue != 0) | if (allowValue != 0 || denyValue != 0) | ||||
| { | { | ||||
| await _api.SetChannelPermissions(channel.Id, targetId, targetType, allowValue, denyValue); | |||||
| await _api.SetChannelPermissions(channel.Id, targetId, targetType.Value, allowValue, denyValue); | |||||
| if (perms != null) | if (perms != null) | ||||
| { | { | ||||
| perms.Allow.SetRawValueInternal(allowValue); | perms.Allow.SetRawValueInternal(allowValue); | ||||
| @@ -84,19 +103,19 @@ namespace Discord | |||||
| return RemoveChannelPermissions(channel, role?.Id, PermissionTarget.Role); | return RemoveChannelPermissions(channel, role?.Id, PermissionTarget.Role); | ||||
| } | } | ||||
| private async Task RemoveChannelPermissions(Channel channel, string userOrRoleId, string idType) | |||||
| private async Task RemoveChannelPermissions(Channel channel, string userOrRoleId, PermissionTarget targetType) | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| var perms = channel.PermissionOverwrites.Where(x => x.TargetType != idType || x.TargetId != userOrRoleId).FirstOrDefault(); | |||||
| var perms = channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).FirstOrDefault(); | |||||
| await _api.DeleteChannelPermissions(channel.Id, userOrRoleId).ConfigureAwait(false); | await _api.DeleteChannelPermissions(channel.Id, userOrRoleId).ConfigureAwait(false); | ||||
| if (perms != null) | if (perms != null) | ||||
| { | { | ||||
| channel.PermissionOverwrites.Where(x => x.TargetType != idType || x.TargetId != userOrRoleId).ToArray(); | |||||
| channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).ToArray(); | |||||
| if (idType == PermissionTarget.Role) | |||||
| if (targetType == PermissionTarget.Role) | |||||
| channel.InvalidatePermissionsCache(); | channel.InvalidatePermissionsCache(); | ||||
| else if (idType == PermissionTarget.Member) | |||||
| else if (targetType == PermissionTarget.Member) | |||||
| channel.InvalidatePermissionsCache(userOrRoleId); | channel.InvalidatePermissionsCache(userOrRoleId); | ||||
| } | } | ||||
| } | } | ||||
| @@ -63,13 +63,20 @@ namespace Discord | |||||
| private readonly Roles _roles; | private readonly Roles _roles; | ||||
| /// <summary> Returns the role with the specified id, or null if none was found. </summary> | /// <summary> Returns the role with the specified id, or null if none was found. </summary> | ||||
| public Role GetRole(string id) => _roles[id]; | |||||
| public Role GetRole(string id) | |||||
| { | |||||
| if (id == null) throw new ArgumentNullException(nameof(id)); | |||||
| CheckReady(); | |||||
| return _roles[id]; | |||||
| } | |||||
| /// <summary> Returns all roles with the specified server and name. </summary> | /// <summary> Returns all roles with the specified server and name. </summary> | ||||
| /// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks> | /// <remarks> Name formats supported: Name and @Name. Search is case-insensitive. </remarks> | ||||
| public IEnumerable<Role> FindRoles(Server server, string name) | public IEnumerable<Role> FindRoles(Server server, string name) | ||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| CheckReady(); | |||||
| if (name.StartsWith("@")) | if (name.StartsWith("@")) | ||||
| { | { | ||||
| @@ -83,18 +90,16 @@ namespace Discord | |||||
| string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); | string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); | ||||
| } | } | ||||
| } | } | ||||
| /// <summary> Note: due to current API limitations, the created role cannot be returned. </summary> | |||||
| public Task<Role> CreateRole(Server server, string name) | |||||
| => CreateRole(server?.Id, name); | |||||
| /// <summary> Note: due to current API limitations, the created role cannot be returned. </summary> | /// <summary> Note: due to current API limitations, the created role cannot be returned. </summary> | ||||
| public async Task<Role> CreateRole(string serverId, string name) | |||||
| public async Task<Role> CreateRole(Server server, string name) | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | |||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | |||||
| CheckReady(); | CheckReady(); | ||||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||||
| var response = await _api.CreateRole(serverId).ConfigureAwait(false); | |||||
| var role = _roles.GetOrAdd(response.Id, serverId); | |||||
| var response = await _api.CreateRole(server.Id).ConfigureAwait(false); | |||||
| var role = _roles.GetOrAdd(response.Id, server.Id); | |||||
| role.Update(response); | role.Update(response); | ||||
| await EditRole(role, name: name); | await EditRole(role, name: name); | ||||
| @@ -98,48 +98,43 @@ namespace Discord | |||||
| public IEnumerable<Server> FindServers(string name) | public IEnumerable<Server> FindServers(string name) | ||||
| { | { | ||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| CheckReady(); | |||||
| return _servers.Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); | return _servers.Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); | ||||
| } | } | ||||
| /// <summary> Creates a new server with the provided name and region (see Regions). </summary> | /// <summary> Creates a new server with the provided name and region (see Regions). </summary> | ||||
| public async Task<Server> CreateServer(string name, string region) | |||||
| public async Task<Server> CreateServer(string name, Region region) | |||||
| { | { | ||||
| CheckReady(); | |||||
| if (name == null) throw new ArgumentNullException(nameof(name)); | if (name == null) throw new ArgumentNullException(nameof(name)); | ||||
| if (region == null) throw new ArgumentNullException(nameof(region)); | |||||
| if (region == (string)null) throw new ArgumentNullException(nameof(region)); | |||||
| CheckReady(); | |||||
| var response = await _api.CreateServer(name, region).ConfigureAwait(false); | |||||
| var response = await _api.CreateServer(name, region.Value).ConfigureAwait(false); | |||||
| var server = _servers.GetOrAdd(response.Id); | var server = _servers.GetOrAdd(response.Id); | ||||
| server.Update(response); | server.Update(response); | ||||
| return server; | return server; | ||||
| } | } | ||||
| /// <summary> Edits the provided server, changing only non-null attributes. </summary> | |||||
| public Task EditServer(string serverId, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null) | |||||
| => EditServer(_servers[serverId], name: name, region: region, iconType: iconType, icon: icon); | |||||
| /// <summary> Edits the provided server, changing only non-null attributes. </summary> | /// <summary> Edits the provided server, changing only non-null attributes. </summary> | ||||
| public async Task EditServer(Server server, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null) | |||||
| public async Task EditServer(Server server, string name = null, Region region = null, ImageType iconType = ImageType.Png, byte[] icon = null) | |||||
| { | { | ||||
| CheckReady(); | |||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | if (server == null) throw new ArgumentNullException(nameof(server)); | ||||
| CheckReady(); | |||||
| var response = await _api.EditServer(server.Id, name: name ?? server.Name, region: region, iconType: iconType, icon: icon); | |||||
| var response = await _api.EditServer(server.Id, name: name ?? server.Name, region: region.Value, iconType: iconType, icon: icon); | |||||
| server.Update(response); | server.Update(response); | ||||
| } | } | ||||
| /// <summary> Leaves the provided server, destroying it if you are the owner. </summary> | /// <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) | |||||
| public async Task<Server> LeaveServer(Server server) | |||||
| { | { | ||||
| if (server == null) throw new ArgumentNullException(nameof(server)); | |||||
| CheckReady(); | CheckReady(); | ||||
| if (serverId == null) throw new ArgumentNullException(nameof(serverId)); | |||||
| try { await _api.LeaveServer(serverId).ConfigureAwait(false); } | |||||
| try { await _api.LeaveServer(server.Id).ConfigureAwait(false); } | |||||
| catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } | ||||
| return _servers.TryRemove(serverId); | |||||
| return _servers.TryRemove(server.Id); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -62,14 +62,18 @@ namespace Discord | |||||
| ImageType avatarType = ImageType.Png, byte[] avatar = null) | ImageType avatarType = ImageType.Png, byte[] avatar = null) | ||||
| { | { | ||||
| if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); | if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword)); | ||||
| CheckReady(); | |||||
| return _api.EditUser(currentPassword: currentPassword, | return _api.EditUser(currentPassword: currentPassword, | ||||
| username: username ?? _currentUser?.Name, email: email ?? _currentUser?.GlobalUser.Email, password: password, | username: username ?? _currentUser?.Name, email: email ?? _currentUser?.GlobalUser.Email, password: password, | ||||
| avatarType: avatarType, avatar: avatar); | avatarType: avatarType, avatar: avatar); | ||||
| } | } | ||||
| public Task SetStatus(string status) | |||||
| public Task SetStatus(UserStatus status) | |||||
| { | { | ||||
| if (status == (string)null) throw new ArgumentNullException(nameof(status)); | |||||
| CheckReady(); | |||||
| if (status != UserStatus.Online && status != UserStatus.Idle) | if (status != UserStatus.Online && status != UserStatus.Idle) | ||||
| throw new ArgumentException($"Invalid status, must be {UserStatus.Online} or {UserStatus.Idle}"); | throw new ArgumentException($"Invalid status, must be {UserStatus.Online} or {UserStatus.Idle}"); | ||||
| _status = status; | _status = status; | ||||
| @@ -77,6 +81,8 @@ namespace Discord | |||||
| } | } | ||||
| public Task SetGame(int? gameId) | public Task SetGame(int? gameId) | ||||
| { | { | ||||
| CheckReady(); | |||||
| _gameId = gameId; | _gameId = gameId; | ||||
| return SendStatus(); | return SendStatus(); | ||||
| } | } | ||||
| @@ -19,7 +19,7 @@ namespace Discord | |||||
| private readonly ConcurrentDictionary<string, DiscordWSClient> _voiceClients; | private readonly ConcurrentDictionary<string, DiscordWSClient> _voiceClients; | ||||
| private bool _sentInitialLog; | private bool _sentInitialLog; | ||||
| private uint _nextVoiceClientId; | private uint _nextVoiceClientId; | ||||
| private string _status; | |||||
| private UserStatus _status; | |||||
| private int? _gameId; | private int? _gameId; | ||||
| public new DiscordClientConfig Config => _config as DiscordClientConfig; | public new DiscordClientConfig Config => _config as DiscordClientConfig; | ||||
| @@ -11,12 +11,12 @@ namespace Discord | |||||
| { | { | ||||
| public sealed class PermissionOverwrite | public sealed class PermissionOverwrite | ||||
| { | { | ||||
| public string TargetType { get; } | |||||
| public PermissionTarget TargetType { get; } | |||||
| public string TargetId { get; } | public string TargetId { get; } | ||||
| public ChannelPermissions Allow { get; } | public ChannelPermissions Allow { get; } | ||||
| public ChannelPermissions Deny { get; } | public ChannelPermissions Deny { get; } | ||||
| internal PermissionOverwrite(string targetType, string targetId, uint allow, uint deny) | |||||
| internal PermissionOverwrite(PermissionTarget targetType, string targetId, uint allow, uint deny) | |||||
| { | { | ||||
| TargetType = targetType; | TargetType = targetType; | ||||
| TargetId = targetId; | TargetId = targetId; | ||||
| @@ -138,7 +138,7 @@ namespace Discord | |||||
| if (model.PermissionOverwrites != null) | if (model.PermissionOverwrites != null) | ||||
| { | { | ||||
| _permissionOverwrites = model.PermissionOverwrites | _permissionOverwrites = model.PermissionOverwrites | ||||
| .Select(x => new PermissionOverwrite(x.Type, x.Id, x.Allow, x.Deny)) | |||||
| .Select(x => new PermissionOverwrite(PermissionTarget.FromString(x.Type), x.Id, x.Allow, x.Deny)) | |||||
| .ToArray(); | .ToArray(); | ||||
| InvalidatePermissionsCache(); | InvalidatePermissionsCache(); | ||||
| } | } | ||||
| @@ -62,8 +62,8 @@ namespace Discord | |||||
| public static ChannelPermissions All(string channelType, bool isPrivate) | public static ChannelPermissions All(string channelType, bool isPrivate) | ||||
| { | { | ||||
| if (isPrivate) return PrivateOnly; | if (isPrivate) return PrivateOnly; | ||||
| else if (channelType == ChannelTypes.Text) return TextOnly; | |||||
| else if (channelType == ChannelTypes.Voice) return VoiceOnly; | |||||
| else if (channelType == ChannelType.Text) return TextOnly; | |||||
| else if (channelType == ChannelType.Voice) return VoiceOnly; | |||||
| else return None; | else return None; | ||||
| } | } | ||||
| @@ -8,7 +8,6 @@ namespace Discord | |||||
| public sealed class Role : CachedObject | public sealed class Role : CachedObject | ||||
| { | { | ||||
| private readonly string _serverId; | private readonly string _serverId; | ||||
| private Server _server; | |||||
| /// <summary> Returns the name of this role. </summary> | /// <summary> Returns the name of this role. </summary> | ||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| @@ -26,7 +25,7 @@ namespace Discord | |||||
| /// <summary> Returns the server this role is a member of. </summary> | /// <summary> Returns the server this role is a member of. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public Server Server => _server; | |||||
| public Server Server { get; private set; } | |||||
| /// <summary> Returns true if this is the role representing all users in a server. </summary> | /// <summary> Returns true if this is the role representing all users in a server. </summary> | ||||
| public bool IsEveryone => Id == _serverId; | public bool IsEveryone => Id == _serverId; | ||||
| @@ -48,14 +47,16 @@ namespace Discord | |||||
| } | } | ||||
| internal override void OnCached() | internal override void OnCached() | ||||
| { | { | ||||
| _server = _client.Servers[_serverId]; | |||||
| _server.AddRole(this); | |||||
| var server = _client.Servers[_serverId]; | |||||
| server.AddRole(this); | |||||
| Server = server; | |||||
| } | } | ||||
| internal override void OnUncached() | internal override void OnUncached() | ||||
| { | { | ||||
| if (_server != null) | |||||
| _server.RemoveRole(this); | |||||
| _server = null; | |||||
| var server = Server; | |||||
| if (server != null) | |||||
| server.RemoveRole(this); | |||||
| Server = null; | |||||
| } | } | ||||
| internal void Update(RoleInfo model) | internal void Update(RoleInfo model) | ||||
| @@ -60,10 +60,10 @@ namespace Discord | |||||
| public IEnumerable<Channel> Channels => _channels.Select(x => _client.Channels[x.Key]); | public IEnumerable<Channel> Channels => _channels.Select(x => _client.Channels[x.Key]); | ||||
| /// <summary> Returns a collection of all channels within this server. </summary> | /// <summary> Returns a collection of all channels within this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Channel> TextChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelTypes.Text); | |||||
| public IEnumerable<Channel> TextChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelType.Text); | |||||
| /// <summary> Returns a collection of all channels within this server. </summary> | /// <summary> Returns a collection of all channels within this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Channel> VoiceChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelTypes.Voice); | |||||
| public IEnumerable<Channel> VoiceChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelType.Voice); | |||||
| /// <summary> Returns a collection of all invites to this server. </summary> | /// <summary> Returns a collection of all invites to this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| @@ -40,7 +40,7 @@ namespace Discord | |||||
| /// <summary> Returns the id for the game this user is currently playing. </summary> | /// <summary> Returns the id for the game this user is currently playing. </summary> | ||||
| public string GameId { get; private set; } | public string GameId { get; private set; } | ||||
| /// <summary> Returns the current status for this user. </summary> | /// <summary> Returns the current status for this user. </summary> | ||||
| public string Status { get; private set; } | |||||
| public UserStatus 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> | /// <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; } | public DateTime? LastActivityAt { get; private set; } | ||||
| /// <summary> Returns the time this user was last seen online in this server. </summary> | /// <summary> Returns the time this user was last seen online in this server. </summary> | ||||
| @@ -152,7 +152,7 @@ namespace Discord | |||||
| UpdateRoles(model.Roles); | UpdateRoles(model.Roles); | ||||
| if (model.Status != null && Status != model.Status) | if (model.Status != null && Status != model.Status) | ||||
| { | { | ||||
| Status = model.Status; | |||||
| Status = UserStatus.FromString(model.Status); | |||||
| if (Status == UserStatus.Offline) | if (Status == UserStatus.Offline) | ||||
| _lastOnline = DateTime.UtcNow; | _lastOnline = DateTime.UtcNow; | ||||
| } | } | ||||
| @@ -19,11 +19,14 @@ namespace Discord.Net.Rest | |||||
| _client = new RestSharp.RestClient(Endpoints.BaseApi) | _client = new RestSharp.RestClient(Endpoints.BaseApi) | ||||
| { | { | ||||
| PreAuthenticate = false, | PreAuthenticate = false, | ||||
| Proxy = new WebProxy(_config.ProxyUrl, true, new string[0], _config.ProxyCredentials), | |||||
| ReadWriteTimeout = _config.APITimeout, | ReadWriteTimeout = _config.APITimeout, | ||||
| UserAgent = _config.UserAgent | UserAgent = _config.UserAgent | ||||
| }; | }; | ||||
| _client.RemoveDefaultParameter("Accept"); | |||||
| if (_config.ProxyUrl != null) | |||||
| _client.Proxy = new WebProxy(_config.ProxyUrl, true, new string[0], _config.ProxyCredentials); | |||||
| else | |||||
| _client.Proxy = null; | |||||
| _client.RemoveDefaultParameter("Accept"); | |||||
| _client.AddDefaultHeader("accept", "*/*"); | _client.AddDefaultHeader("accept", "*/*"); | ||||
| _client.AddDefaultHeader("accept-encoding", "gzip,deflate"); | _client.AddDefaultHeader("accept-encoding", "gzip,deflate"); | ||||
| } | } | ||||
| @@ -38,7 +38,7 @@ namespace Discord.Tests | |||||
| _observerBot.AllServers.Select(x => _observerBot.LeaveServer(x))); | _observerBot.AllServers.Select(x => _observerBot.LeaveServer(x))); | ||||
| //Create new server and invite the other bots to it | //Create new server and invite the other bots to it | ||||
| _testServer = _hostClient.CreateServer("Discord.Net Testing", Regions.US_East).Result; | |||||
| _testServer = _hostClient.CreateServer("Discord.Net Testing", Region.USEast).Result; | |||||
| _testServerChannel = _testServer.DefaultChannel; | _testServerChannel = _testServer.DefaultChannel; | ||||
| Invite invite = _hostClient.CreateInvite(_testServer, 60, 1, false, false).Result; | Invite invite = _hostClient.CreateInvite(_testServer, 60, 1, false, false).Result; | ||||
| WaitAll( | WaitAll( | ||||
| @@ -49,11 +49,11 @@ namespace Discord.Tests | |||||
| //Channels | //Channels | ||||
| [TestMethod] | [TestMethod] | ||||
| public void TestCreateTextChannel() | public void TestCreateTextChannel() | ||||
| => TestCreateChannel(ChannelTypes.Text); | |||||
| => TestCreateChannel(ChannelType.Text); | |||||
| [TestMethod] | [TestMethod] | ||||
| public void TestCreateVoiceChannel() | public void TestCreateVoiceChannel() | ||||
| => TestCreateChannel(ChannelTypes.Voice); | |||||
| private void TestCreateChannel(string type) | |||||
| => TestCreateChannel(ChannelType.Voice); | |||||
| private void TestCreateChannel(ChannelType type) | |||||
| { | { | ||||
| Channel channel = null; | Channel channel = null; | ||||
| string name = $"#test_{_random.Next()}"; | string name = $"#test_{_random.Next()}"; | ||||
| @@ -76,21 +76,21 @@ namespace Discord.Tests | |||||
| [ExpectedException(typeof(InvalidOperationException))] | [ExpectedException(typeof(InvalidOperationException))] | ||||
| public async Task TestCreateChannel_NoName() | public async Task TestCreateChannel_NoName() | ||||
| { | { | ||||
| await _hostClient.CreateChannel(_testServer, $"", ChannelTypes.Text); | |||||
| await _hostClient.CreateChannel(_testServer, $"", ChannelType.Text); | |||||
| } | } | ||||
| [TestMethod] | [TestMethod] | ||||
| [ExpectedException(typeof(InvalidOperationException))] | [ExpectedException(typeof(InvalidOperationException))] | ||||
| public async Task TestCreateChannel_NoType() | public async Task TestCreateChannel_NoType() | ||||
| { | { | ||||
| string name = $"#test_{_random.Next()}"; | string name = $"#test_{_random.Next()}"; | ||||
| await _hostClient.CreateChannel(_testServer, $"", ""); | |||||
| await _hostClient.CreateChannel(_testServer, $"", ChannelType.FromString("")); | |||||
| } | } | ||||
| [TestMethod] | [TestMethod] | ||||
| [ExpectedException(typeof(InvalidOperationException))] | [ExpectedException(typeof(InvalidOperationException))] | ||||
| public async Task TestCreateChannel_BadType() | public async Task TestCreateChannel_BadType() | ||||
| { | { | ||||
| string name = $"#test_{_random.Next()}"; | string name = $"#test_{_random.Next()}"; | ||||
| await _hostClient.CreateChannel(_testServer, $"", "badtype"); | |||||
| await _hostClient.CreateChannel(_testServer, $"", ChannelType.FromString("badtype")); | |||||
| } | } | ||||
| //Messages | //Messages | ||||