@@ -16,13 +16,9 @@ Our stable builds available from NuGet through the Discord.Net metapackage: | |||
The individual components may also be installed from NuGet: | |||
- [Discord.Net.Commands](https://www.nuget.org/packages/Discord.Net.Commands/) | |||
- [Discord.Net.Rest](https://www.nuget.org/packages/Discord.Net.Rest/) | |||
- [Discord.Net.Rpc](https://www.nuget.org/packages/Discord.Net.Rpc/) | |||
- [Discord.Net.WebSocket](https://www.nuget.org/packages/Discord.Net.WebSocket/) | |||
- [Discord.Net.Webhook](https://www.nuget.org/packages/Discord.Net.Webhook/) | |||
The following provider is available for platforms not supporting .NET Standard 1.3: | |||
- [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/) | |||
### Unstable (MyGet) | |||
Nightly builds are available through our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`). | |||
@@ -41,5 +37,4 @@ The .NET Core workload must be selected during Visual Studio installation. | |||
## Known Issues | |||
### WebSockets (Win7 and earlier) | |||
.NET Core 1.1 does not support WebSockets on Win7 and earlier. It's recommended to use the Discord.Net.Providers.WS4Net package until this is resolved. | |||
Track the issue [here](https://github.com/dotnet/corefx/issues/9503). | |||
.NET Core 1.1 does not support WebSockets on Win7 and earlier. This issue has been fixed since the release of .NET Core 2.1. It is recommended to target .NET Core 2.1 or above for your project if you wish to run your bot on legacy platforms; alternatively, you may choose to install the [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/) package. |
@@ -7,7 +7,7 @@ namespace Discord.Commands | |||
{ | |||
public string Text { get; } | |||
public RunMode RunMode { get; set; } = RunMode.Default; | |||
public bool? IgnoreExtraArgs { get; set; } | |||
public bool? IgnoreExtraArgs { get; } | |||
public CommandAttribute() | |||
{ | |||
@@ -17,5 +17,10 @@ namespace Discord.Commands | |||
{ | |||
Text = text; | |||
} | |||
public CommandAttribute(string text, bool ignoreExtraArgs) | |||
{ | |||
Text = text; | |||
IgnoreExtraArgs = ignoreExtraArgs; | |||
} | |||
} | |||
} |
@@ -120,7 +120,7 @@ namespace Discord.Commands.Builders | |||
if (Name == null) | |||
Name = _aliases[0]; | |||
if (TypeInfo != null) | |||
if (TypeInfo != null && !TypeInfo.IsAbstract) | |||
{ | |||
var moduleInstance = ReflectionUtils.CreateObject<IModuleBase>(TypeInfo, service, services); | |||
moduleInstance.OnModuleBuilding(service, this); | |||
@@ -45,14 +45,7 @@ namespace Discord.Commands.Builders | |||
internal void SetType(Type type) | |||
{ | |||
var readers = Command.Module.Service.GetTypeReaders(type); | |||
if (readers != null) | |||
TypeReader = readers.FirstOrDefault().Value; | |||
else | |||
TypeReader = Command.Module.Service.GetDefaultTypeReader(type); | |||
if (TypeReader == null) | |||
throw new InvalidOperationException($"{type} does not have a TypeReader registered for it. Parameter: {Name} in {Command.PrimaryAlias}"); | |||
TypeReader = GetReader(type); | |||
if (type.GetTypeInfo().IsValueType) | |||
DefaultValue = Activator.CreateInstance(type); | |||
@@ -60,7 +53,16 @@ namespace Discord.Commands.Builders | |||
type = ParameterType.GetElementType(); | |||
ParameterType = type; | |||
} | |||
private TypeReader GetReader(Type type) | |||
{ | |||
var readers = Command.Module.Service.GetTypeReaders(type); | |||
if (readers != null) | |||
return readers.FirstOrDefault().Value; | |||
else | |||
return Command.Module.Service.GetDefaultTypeReader(type); | |||
} | |||
public ParameterBuilder WithSummary(string summary) | |||
{ | |||
Summary = summary; | |||
@@ -100,10 +102,10 @@ namespace Discord.Commands.Builders | |||
internal ParameterInfo Build(CommandInfo info) | |||
{ | |||
if (TypeReader == null) | |||
if ((TypeReader ?? (TypeReader = GetReader(ParameterType))) == null) | |||
throw new InvalidOperationException($"No type reader found for type {ParameterType.Name}, one must be specified"); | |||
return new ParameterInfo(this, info, Command.Module.Service); | |||
} | |||
} | |||
} | |||
} |
@@ -1,4 +1,4 @@ | |||
using System; | |||
using System; | |||
namespace Discord.Commands | |||
{ | |||
@@ -43,4 +43,4 @@ namespace Discord.Commands | |||
return false; | |||
} | |||
} | |||
} | |||
} |
@@ -1,4 +1,5 @@ | |||
using System.Collections.Generic; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
namespace Discord.Commands | |||
@@ -53,6 +54,8 @@ namespace Discord.Commands | |||
public static ParseResult FromError(CommandError error, string reason) | |||
=> new ParseResult(null, null, error, reason); | |||
public static ParseResult FromError(Exception ex) | |||
=> FromError(CommandError.Exception, ex.Message); | |||
public static ParseResult FromError(IResult result) | |||
=> new ParseResult(null, null, result.Error, result.ErrorReason); | |||
@@ -1,4 +1,5 @@ | |||
using System.Collections.Generic; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
namespace Discord.Commands | |||
@@ -18,6 +19,8 @@ namespace Discord.Commands | |||
=> new PreconditionGroupResult(null, null, null); | |||
public static PreconditionGroupResult FromError(string reason, ICollection<PreconditionResult> preconditions) | |||
=> new PreconditionGroupResult(CommandError.UnmetPrecondition, reason, preconditions); | |||
public static new PreconditionGroupResult FromError(Exception ex) | |||
=> new PreconditionGroupResult(CommandError.Exception, ex.Message, null); | |||
public static new PreconditionGroupResult FromError(IResult result) //needed? | |||
=> new PreconditionGroupResult(result.Error, result.ErrorReason, null); | |||
@@ -1,4 +1,5 @@ | |||
using System.Diagnostics; | |||
using System; | |||
using System.Diagnostics; | |||
namespace Discord.Commands | |||
{ | |||
@@ -20,6 +21,8 @@ namespace Discord.Commands | |||
=> new PreconditionResult(null, null); | |||
public static PreconditionResult FromError(string reason) | |||
=> new PreconditionResult(CommandError.UnmetPrecondition, reason); | |||
public static PreconditionResult FromError(Exception ex) | |||
=> new PreconditionResult(CommandError.Exception, ex.Message); | |||
public static PreconditionResult FromError(IResult result) | |||
=> new PreconditionResult(result.Error, result.ErrorReason); | |||
@@ -1,4 +1,5 @@ | |||
using System.Collections.Generic; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
namespace Discord.Commands | |||
@@ -26,6 +27,8 @@ namespace Discord.Commands | |||
=> new SearchResult(text, commands, null, null); | |||
public static SearchResult FromError(CommandError error, string reason) | |||
=> new SearchResult(null, null, error, reason); | |||
public static SearchResult FromError(Exception ex) | |||
=> FromError(CommandError.Exception, ex.Message); | |||
public static SearchResult FromError(IResult result) | |||
=> new SearchResult(null, null, result.Error, result.ErrorReason); | |||
@@ -50,6 +50,8 @@ namespace Discord.Commands | |||
=> new TypeReaderResult(values, null, null); | |||
public static TypeReaderResult FromError(CommandError error, string reason) | |||
=> new TypeReaderResult(null, error, reason); | |||
public static TypeReaderResult FromError(Exception ex) | |||
=> FromError(CommandError.Exception, ex.Message); | |||
public static TypeReaderResult FromError(IResult result) | |||
=> new TypeReaderResult(null, result.Error, result.ErrorReason); | |||
@@ -1,4 +1,4 @@ | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
@@ -9,7 +9,7 @@ namespace Discord | |||
/// <summary> | |||
/// Represents an entry in an audit log | |||
/// </summary> | |||
public interface IAuditLogEntry : IEntity<ulong> | |||
public interface IAuditLogEntry : ISnowflakeEntity | |||
{ | |||
/// <summary> | |||
/// The action which occured to create this entry | |||
@@ -1,4 +1,4 @@ | |||
using System; | |||
using System; | |||
namespace Discord | |||
{ | |||
@@ -30,6 +30,7 @@ namespace Discord | |||
DeafenMembers = 0x00_80_00_00, | |||
MoveMembers = 0x01_00_00_00, | |||
UseVAD = 0x02_00_00_00, | |||
PrioritySpeaker = 0x00_00_01_00, | |||
// More General | |||
ManageRoles = 0x10_00_00_00, | |||
@@ -12,7 +12,7 @@ namespace Discord | |||
/// <summary> Gets a ChannelPermissions that grants all permissions for text channels. </summary> | |||
public static readonly ChannelPermissions Text = new ChannelPermissions(0b01100_0000000_1111111110001_010001); | |||
/// <summary> Gets a ChannelPermissions that grants all permissions for voice channels. </summary> | |||
public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000010000_010001); | |||
public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000010100_010001); | |||
/// <summary> Gets a ChannelPermissions that grants all permissions for category channels. </summary> | |||
public static readonly ChannelPermissions Category = new ChannelPermissions(0b01100_1111110_1111111110001_010001); | |||
/// <summary> Gets a ChannelPermissions that grants all permissions for direct message channels. </summary> | |||
@@ -78,6 +78,8 @@ namespace Discord | |||
public bool MoveMembers => Permissions.GetValue(RawValue, ChannelPermission.MoveMembers); | |||
/// <summary> If True, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
public bool UseVAD => Permissions.GetValue(RawValue, ChannelPermission.UseVAD); | |||
/// <summary> If True, a user may use priority speaker in a voice channel. </summary> | |||
public bool PrioritySpeaker => Permissions.GetValue(RawValue, ChannelPermission.PrioritySpeaker); | |||
/// <summary> If True, a user may adjust role permissions. This also implictly grants all other permissions. </summary> | |||
public bool ManageRoles => Permissions.GetValue(RawValue, ChannelPermission.ManageRoles); | |||
@@ -106,6 +108,7 @@ namespace Discord | |||
bool? deafenMembers = null, | |||
bool? moveMembers = null, | |||
bool? useVoiceActivation = null, | |||
bool? prioritySpeaker = null, | |||
bool? manageRoles = null, | |||
bool? manageWebhooks = null) | |||
{ | |||
@@ -129,6 +132,7 @@ namespace Discord | |||
Permissions.SetValue(ref value, deafenMembers, ChannelPermission.DeafenMembers); | |||
Permissions.SetValue(ref value, moveMembers, ChannelPermission.MoveMembers); | |||
Permissions.SetValue(ref value, useVoiceActivation, ChannelPermission.UseVAD); | |||
Permissions.SetValue(ref value, prioritySpeaker, ChannelPermission.PrioritySpeaker); | |||
Permissions.SetValue(ref value, manageRoles, ChannelPermission.ManageRoles); | |||
Permissions.SetValue(ref value, manageWebhooks, ChannelPermission.ManageWebhooks); | |||
@@ -155,11 +159,12 @@ namespace Discord | |||
bool deafenMembers = false, | |||
bool moveMembers = false, | |||
bool useVoiceActivation = false, | |||
bool prioritySpeaker = false, | |||
bool manageRoles = false, | |||
bool manageWebhooks = false) | |||
: this(0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, | |||
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, | |||
speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks) | |||
speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, prioritySpeaker, manageRoles, manageWebhooks) | |||
{ } | |||
/// <summary> Creates a new ChannelPermissions from this one, changing the provided non-null permissions. </summary> | |||
@@ -182,6 +187,7 @@ namespace Discord | |||
bool? deafenMembers = null, | |||
bool? moveMembers = null, | |||
bool? useVoiceActivation = null, | |||
bool? prioritySpeaker = null, | |||
bool? manageRoles = null, | |||
bool? manageWebhooks = null) | |||
=> new ChannelPermissions(RawValue, | |||
@@ -203,6 +209,7 @@ namespace Discord | |||
deafenMembers, | |||
moveMembers, | |||
useVoiceActivation, | |||
prioritySpeaker, | |||
manageRoles, | |||
manageWebhooks); | |||
@@ -35,6 +35,7 @@ namespace Discord | |||
DeafenMembers = 0x00_80_00_00, | |||
MoveMembers = 0x01_00_00_00, | |||
UseVAD = 0x02_00_00_00, | |||
PrioritySpeaker = 0x00_00_01_00, | |||
// General 2 | |||
ChangeNickname = 0x04_00_00_00, | |||
@@ -12,7 +12,7 @@ namespace Discord | |||
/// <summary> Gets a GuildPermissions that grants all guild permissions for webhook users. </summary> | |||
public static readonly GuildPermissions Webhook = new GuildPermissions(0b00000_0000000_0001101100000_000000); | |||
/// <summary> Gets a GuildPermissions that grants all guild permissions. </summary> | |||
public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_1111111110011_111111); | |||
public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_1111111110111_111111); | |||
/// <summary> Gets a packed value representing all the permissions in this GuildPermissions. </summary> | |||
public ulong RawValue { get; } | |||
@@ -69,6 +69,8 @@ namespace Discord | |||
public bool MoveMembers => Permissions.GetValue(RawValue, GuildPermission.MoveMembers); | |||
/// <summary> If True, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
public bool UseVAD => Permissions.GetValue(RawValue, GuildPermission.UseVAD); | |||
/// <summary> If True, a user may use priority speaker in a voice channel. </summary> | |||
public bool PrioritySpeaker => Permissions.GetValue(RawValue, ChannelPermission.PrioritySpeaker); | |||
/// <summary> If True, a user may change their own nickname. </summary> | |||
public bool ChangeNickname => Permissions.GetValue(RawValue, GuildPermission.ChangeNickname); | |||
@@ -108,6 +110,7 @@ namespace Discord | |||
bool? deafenMembers = null, | |||
bool? moveMembers = null, | |||
bool? useVoiceActivation = null, | |||
bool? prioritySpeaker = null, | |||
bool? changeNickname = null, | |||
bool? manageNicknames = null, | |||
bool? manageRoles = null, | |||
@@ -139,6 +142,7 @@ namespace Discord | |||
Permissions.SetValue(ref value, deafenMembers, GuildPermission.DeafenMembers); | |||
Permissions.SetValue(ref value, moveMembers, GuildPermission.MoveMembers); | |||
Permissions.SetValue(ref value, useVoiceActivation, GuildPermission.UseVAD); | |||
Permissions.SetValue(ref value, prioritySpeaker, GuildPermission.PrioritySpeaker); | |||
Permissions.SetValue(ref value, changeNickname, GuildPermission.ChangeNickname); | |||
Permissions.SetValue(ref value, manageNicknames, GuildPermission.ManageNicknames); | |||
Permissions.SetValue(ref value, manageRoles, GuildPermission.ManageRoles); | |||
@@ -173,6 +177,7 @@ namespace Discord | |||
bool deafenMembers = false, | |||
bool moveMembers = false, | |||
bool useVoiceActivation = false, | |||
bool prioritySpeaker = false, | |||
bool changeNickname = false, | |||
bool manageNicknames = false, | |||
bool manageRoles = false, | |||
@@ -203,6 +208,7 @@ namespace Discord | |||
deafenMembers: deafenMembers, | |||
moveMembers: moveMembers, | |||
useVoiceActivation: useVoiceActivation, | |||
prioritySpeaker: prioritySpeaker, | |||
changeNickname: changeNickname, | |||
manageNicknames: manageNicknames, | |||
manageWebhooks: manageWebhooks, | |||
@@ -234,6 +240,7 @@ namespace Discord | |||
bool? deafenMembers = null, | |||
bool? moveMembers = null, | |||
bool? useVoiceActivation = null, | |||
bool? prioritySpeaker = null, | |||
bool? changeNickname = null, | |||
bool? manageNicknames = null, | |||
bool? manageRoles = null, | |||
@@ -242,7 +249,7 @@ namespace Discord | |||
=> new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions, | |||
viewAuditLog, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, | |||
readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, | |||
useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); | |||
useVoiceActivation, prioritySpeaker, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); | |||
public bool Has(GuildPermission permission) => Permissions.GetValue(RawValue, permission); | |||
@@ -100,6 +100,17 @@ namespace Discord | |||
(uint)(b * 255.0f); | |||
} | |||
public static bool operator ==(Color lhs, Color rhs) | |||
=> lhs.RawValue == rhs.RawValue; | |||
public static bool operator !=(Color lhs, Color rhs) | |||
=> lhs.RawValue != rhs.RawValue; | |||
public override bool Equals(object obj) | |||
=> (obj is Color c && RawValue == c.RawValue); | |||
public override int GetHashCode() => RawValue.GetHashCode(); | |||
#if NETSTANDARD2_0 || NET45 | |||
public static implicit operator StandardColor(Color color) => | |||
StandardColor.FromArgb((int)color.RawValue); | |||
@@ -0,0 +1,11 @@ | |||
namespace Discord | |||
{ | |||
public static class MessageExtensions | |||
{ | |||
public static string GetJumpUrl(this IMessage msg) | |||
{ | |||
var channel = msg.Channel; | |||
return $"https://discordapp.com/channels/{(channel is IDMChannel ? "@me" : $"{(channel as ITextChannel).GuildId}")}/{channel.Id}/{msg.Id}"; | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
public static class TokenUtils | |||
{ | |||
/// <summary> | |||
/// Checks the validity of the supplied token of a specific type. | |||
/// </summary> | |||
/// <param name="tokenType"> The type of token to validate. </param> | |||
/// <param name="token"> The token value to validate. </param> | |||
/// <exception cref="ArgumentNullException"> Thrown when the supplied token string is null, empty, or contains only whitespace.</exception> | |||
/// <exception cref="ArgumentException"> Thrown when the supplied TokenType or token value is invalid. </exception> | |||
public static void ValidateToken(TokenType tokenType, string token) | |||
{ | |||
// A Null or WhiteSpace token of any type is invalid. | |||
if (string.IsNullOrWhiteSpace(token)) | |||
throw new ArgumentNullException("A token cannot be null, empty, or contain only whitespace.", nameof(token)); | |||
switch (tokenType) | |||
{ | |||
case TokenType.Webhook: | |||
// no validation is performed on Webhook tokens | |||
break; | |||
case TokenType.Bearer: | |||
// no validation is performed on Bearer tokens | |||
break; | |||
case TokenType.Bot: | |||
// bot tokens are assumed to be at least 59 characters in length | |||
// this value was determined by referencing examples in the discord documentation, and by comparing with | |||
// pre-existing tokens | |||
if (token.Length < 59) | |||
throw new ArgumentException("A Bot token must be at least 59 characters in length.", nameof(token)); | |||
break; | |||
default: | |||
// All unrecognized TokenTypes (including User tokens) are considered to be invalid. | |||
throw new ArgumentException("Unrecognized TokenType.", nameof(token)); | |||
} | |||
} | |||
} | |||
} |
@@ -20,7 +20,7 @@ namespace Discord.API | |||
[JsonProperty("role_name")] | |||
public string OverwriteRoleName { get; set; } | |||
[JsonProperty("type")] | |||
public string OverwriteType { get; set; } | |||
public PermissionTarget OverwriteType { get; set; } | |||
[JsonProperty("id")] | |||
public ulong? OverwriteTargetId { get; set; } | |||
} | |||
@@ -55,11 +55,11 @@ namespace Discord.Rest | |||
await _stateLock.WaitAsync().ConfigureAwait(false); | |||
try | |||
{ | |||
await LoginInternalAsync(tokenType, token).ConfigureAwait(false); | |||
await LoginInternalAsync(tokenType, token, validateToken).ConfigureAwait(false); | |||
} | |||
finally { _stateLock.Release(); } | |||
} | |||
private async Task LoginInternalAsync(TokenType tokenType, string token) | |||
private async Task LoginInternalAsync(TokenType tokenType, string token, bool validateToken) | |||
{ | |||
if (_isFirstLogin) | |||
{ | |||
@@ -73,6 +73,21 @@ namespace Discord.Rest | |||
try | |||
{ | |||
// If token validation is enabled, validate the token and let it throw any ArgumentExceptions | |||
// that result from invalid parameters | |||
if (validateToken) | |||
{ | |||
try | |||
{ | |||
TokenUtils.ValidateToken(tokenType, token); | |||
} | |||
catch (ArgumentException ex) | |||
{ | |||
// log these ArgumentExceptions and allow for the client to attempt to log in anyways | |||
await LogManager.WarningAsync("Discord", "A supplied token was invalid", ex).ConfigureAwait(false); | |||
} | |||
} | |||
await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false); | |||
await OnLoginAsync(tokenType, token).ConfigureAwait(false); | |||
LoginState = LoginState.LoggedIn; | |||
@@ -43,9 +43,11 @@ namespace Discord.API | |||
public TokenType AuthTokenType { get; private set; } | |||
internal string AuthToken { get; private set; } | |||
internal IRestClient RestClient { get; private set; } | |||
internal ulong? CurrentUserId { get; set;} | |||
internal ulong? CurrentUserId { get; set; } | |||
public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, | |||
internal JsonSerializer Serializer => _serializer; | |||
public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, | |||
JsonSerializer serializer = null) | |||
{ | |||
_restClientProvider = restClientProvider; | |||
@@ -235,7 +237,7 @@ namespace Discord.API | |||
internal Task<TResponse> SendMultipartAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids, | |||
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) | |||
=> SendMultipartAsync<TResponse>(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(ids, endpointExpr, AuthTokenType, funcName), clientBucket, options); | |||
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, | |||
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, | |||
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) | |||
{ | |||
options = options ?? new RequestOptions(); | |||
@@ -414,7 +416,7 @@ namespace Discord.API | |||
var ids = new BucketIds(guildId: guildId); | |||
await SendAsync("DELETE", () => $"guilds/{guildId}/members/{userId}/roles/{roleId}", ids, options: options); | |||
} | |||
//Channel Messages | |||
public async Task<Message> GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) | |||
{ | |||
@@ -490,7 +492,7 @@ namespace Discord.API | |||
if (args.Content?.Length > DiscordConfig.MaxMessageSize) | |||
throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||
options = RequestOptions.CreateOrClone(options); | |||
return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, new BucketIds(), clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) | |||
@@ -737,7 +739,7 @@ namespace Discord.API | |||
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); | |||
Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId)); | |||
options = RequestOptions.CreateOrClone(options); | |||
return await SendJsonAsync<Guild>("POST", () => "guilds", args, new BucketIds(), options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Guild> DeleteGuildAsync(ulong guildId, RequestOptions options = null) | |||
@@ -964,7 +966,7 @@ namespace Discord.API | |||
{ | |||
Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); | |||
options = RequestOptions.CreateOrClone(options); | |||
return await SendAsync<Invite>("DELETE", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false); | |||
} | |||
@@ -1163,7 +1165,7 @@ namespace Discord.API | |||
int limit = args.Limit.GetValueOrDefault(int.MaxValue); | |||
ulong afterGuildId = args.AfterGuildId.GetValueOrDefault(0); | |||
return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", () => $"users/@me/guilds?limit={limit}&after={afterGuildId}", new BucketIds(), options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Application> GetMyApplicationAsync(RequestOptions options = null) | |||
@@ -1263,7 +1265,7 @@ namespace Discord.API | |||
Preconditions.NotNull(args, nameof(args)); | |||
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); | |||
options = RequestOptions.CreateOrClone(options); | |||
if (AuthTokenType == TokenType.Webhook) | |||
return await SendJsonAsync<Webhook>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}", args, new BucketIds(), options: options).ConfigureAwait(false); | |||
else | |||
@@ -1394,6 +1396,7 @@ namespace Discord.API | |||
string fieldName = GetFieldName(methodArgs[argId + 1]); | |||
var mappedId = BucketIds.GetIndex(fieldName); | |||
if(!mappedId.HasValue && rightIndex != endIndex && format.Length > rightIndex + 1 && format[rightIndex + 1] == '/') //Ignore the next slash | |||
rightIndex++; | |||
@@ -26,18 +26,16 @@ namespace Discord.Rest | |||
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); | |||
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var type = typeModel.NewValue.ToObject<ChannelType>(); | |||
var name = nameModel.NewValue.ToObject<string>(); | |||
var type = typeModel.NewValue.ToObject<ChannelType>(discord.ApiClient.Serializer); | |||
var name = nameModel.NewValue.ToObject<string>(discord.ApiClient.Serializer); | |||
foreach (var overwrite in overwritesModel.NewValue) | |||
{ | |||
var deny = overwrite.Value<ulong>("deny"); | |||
var _type = overwrite.Value<string>("type"); | |||
var permType = overwrite.Value<PermissionTarget>("type"); | |||
var id = overwrite.Value<ulong>("id"); | |||
var allow = overwrite.Value<ulong>("allow"); | |||
PermissionTarget permType = _type == "member" ? PermissionTarget.User : PermissionTarget.Role; | |||
overwrites.Add(new Overwrite(id, permType, new OverwritePermissions(allow, deny))); | |||
} | |||
@@ -27,11 +27,11 @@ namespace Discord.Rest | |||
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); | |||
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var overwrites = overwritesModel.OldValue.ToObject<API.Overwrite[]>() | |||
var overwrites = overwritesModel.OldValue.ToObject<API.Overwrite[]>(discord.ApiClient.Serializer) | |||
.Select(x => new Overwrite(x.TargetId, x.TargetType, new OverwritePermissions(x.Allow, x.Deny))) | |||
.ToList(); | |||
var type = typeModel.OldValue.ToObject<ChannelType>(); | |||
var name = nameModel.OldValue.ToObject<string>(); | |||
var type = typeModel.OldValue.ToObject<ChannelType>(discord.ApiClient.Serializer); | |||
var name = nameModel.OldValue.ToObject<string>(discord.ApiClient.Serializer); | |||
var id = entry.TargetId.Value; | |||
return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection()); | |||
@@ -23,14 +23,14 @@ namespace Discord.Rest | |||
var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); | |||
var userLimitModel = changes.FirstOrDefault(x => x.ChangedProperty == "user_limit"); | |||
string oldName = nameModel?.OldValue?.ToObject<string>(), | |||
newName = nameModel?.NewValue?.ToObject<string>(); | |||
string oldTopic = topicModel?.OldValue?.ToObject<string>(), | |||
newTopic = topicModel?.NewValue?.ToObject<string>(); | |||
int? oldBitrate = bitrateModel?.OldValue?.ToObject<int>(), | |||
newBitrate = bitrateModel?.NewValue?.ToObject<int>(); | |||
int? oldLimit = userLimitModel?.OldValue?.ToObject<int>(), | |||
newLimit = userLimitModel?.NewValue?.ToObject<int>(); | |||
string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
string oldTopic = topicModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newTopic = topicModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
int? oldBitrate = bitrateModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), | |||
newBitrate = bitrateModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); | |||
int? oldLimit = userLimitModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), | |||
newLimit = userLimitModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); | |||
var before = new ChannelInfo(oldName, oldTopic, oldBitrate, oldLimit); | |||
var after = new ChannelInfo(newName, newTopic, newBitrate, newLimit); | |||
@@ -21,7 +21,7 @@ namespace Discord.Rest | |||
{ | |||
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var emoteName = change.NewValue?.ToObject<string>(); | |||
var emoteName = change.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
return new EmoteCreateAuditLogData(entry.TargetId.Value, emoteName); | |||
} | |||
@@ -17,7 +17,7 @@ namespace Discord.Rest | |||
{ | |||
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var emoteName = change.OldValue?.ToObject<string>(); | |||
var emoteName = change.OldValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
return new EmoteDeleteAuditLogData(entry.TargetId.Value, emoteName); | |||
} | |||
@@ -18,8 +18,8 @@ namespace Discord.Rest | |||
{ | |||
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var newName = change.NewValue?.ToObject<string>(); | |||
var oldName = change.OldValue?.ToObject<string>(); | |||
var newName = change.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
var oldName = change.OldValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
return new EmoteUpdateAuditLogData(entry.TargetId.Value, oldName, newName); | |||
} | |||
@@ -28,26 +28,26 @@ namespace Discord.Rest | |||
var mfaLevelModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); | |||
var contentFilterModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); | |||
int? oldAfkTimeout = afkTimeoutModel?.OldValue?.ToObject<int>(), | |||
newAfkTimeout = afkTimeoutModel?.NewValue?.ToObject<int>(); | |||
DefaultMessageNotifications? oldDefaultMessageNotifications = defaultMessageNotificationsModel?.OldValue?.ToObject<DefaultMessageNotifications>(), | |||
newDefaultMessageNotifications = defaultMessageNotificationsModel?.NewValue?.ToObject<DefaultMessageNotifications>(); | |||
ulong? oldAfkChannelId = afkChannelModel?.OldValue?.ToObject<ulong>(), | |||
newAfkChannelId = afkChannelModel?.NewValue?.ToObject<ulong>(); | |||
string oldName = nameModel?.OldValue?.ToObject<string>(), | |||
newName = nameModel?.NewValue?.ToObject<string>(); | |||
string oldRegionId = regionIdModel?.OldValue?.ToObject<string>(), | |||
newRegionId = regionIdModel?.NewValue?.ToObject<string>(); | |||
string oldIconHash = iconHashModel?.OldValue?.ToObject<string>(), | |||
newIconHash = iconHashModel?.NewValue?.ToObject<string>(); | |||
VerificationLevel? oldVerificationLevel = verificationLevelModel?.OldValue?.ToObject<VerificationLevel>(), | |||
newVerificationLevel = verificationLevelModel?.NewValue?.ToObject<VerificationLevel>(); | |||
ulong? oldOwnerId = ownerIdModel?.OldValue?.ToObject<ulong>(), | |||
newOwnerId = ownerIdModel?.NewValue?.ToObject<ulong>(); | |||
MfaLevel? oldMfaLevel = mfaLevelModel?.OldValue?.ToObject<MfaLevel>(), | |||
newMfaLevel = mfaLevelModel?.NewValue?.ToObject<MfaLevel>(); | |||
int? oldContentFilter = contentFilterModel?.OldValue?.ToObject<int>(), | |||
newContentFilter = contentFilterModel?.NewValue?.ToObject<int>(); | |||
int? oldAfkTimeout = afkTimeoutModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), | |||
newAfkTimeout = afkTimeoutModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); | |||
DefaultMessageNotifications? oldDefaultMessageNotifications = defaultMessageNotificationsModel?.OldValue?.ToObject<DefaultMessageNotifications>(discord.ApiClient.Serializer), | |||
newDefaultMessageNotifications = defaultMessageNotificationsModel?.NewValue?.ToObject<DefaultMessageNotifications>(discord.ApiClient.Serializer); | |||
ulong? oldAfkChannelId = afkChannelModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer), | |||
newAfkChannelId = afkChannelModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
string oldRegionId = regionIdModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newRegionId = regionIdModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
string oldIconHash = iconHashModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newIconHash = iconHashModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
VerificationLevel? oldVerificationLevel = verificationLevelModel?.OldValue?.ToObject<VerificationLevel>(discord.ApiClient.Serializer), | |||
newVerificationLevel = verificationLevelModel?.NewValue?.ToObject<VerificationLevel>(discord.ApiClient.Serializer); | |||
ulong? oldOwnerId = ownerIdModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer), | |||
newOwnerId = ownerIdModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
MfaLevel? oldMfaLevel = mfaLevelModel?.OldValue?.ToObject<MfaLevel>(discord.ApiClient.Serializer), | |||
newMfaLevel = mfaLevelModel?.NewValue?.ToObject<MfaLevel>(discord.ApiClient.Serializer); | |||
int? oldContentFilter = contentFilterModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), | |||
newContentFilter = contentFilterModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); | |||
IUser oldOwner = null; | |||
if (oldOwnerId != null) | |||
@@ -30,13 +30,13 @@ namespace Discord.Rest | |||
var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses"); | |||
var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); | |||
var maxAge = maxAgeModel.NewValue.ToObject<int>(); | |||
var code = codeModel.NewValue.ToObject<string>(); | |||
var temporary = temporaryModel.NewValue.ToObject<bool>(); | |||
var inviterId = inviterIdModel.NewValue.ToObject<ulong>(); | |||
var channelId = channelIdModel.NewValue.ToObject<ulong>(); | |||
var uses = usesModel.NewValue.ToObject<int>(); | |||
var maxUses = maxUsesModel.NewValue.ToObject<int>(); | |||
var maxAge = maxAgeModel.NewValue.ToObject<int>(discord.ApiClient.Serializer); | |||
var code = codeModel.NewValue.ToObject<string>(discord.ApiClient.Serializer); | |||
var temporary = temporaryModel.NewValue.ToObject<bool>(discord.ApiClient.Serializer); | |||
var inviterId = inviterIdModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var channelId = channelIdModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var uses = usesModel.NewValue.ToObject<int>(discord.ApiClient.Serializer); | |||
var maxUses = maxUsesModel.NewValue.ToObject<int>(discord.ApiClient.Serializer); | |||
var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId); | |||
var inviter = RestUser.Create(discord, inviterInfo); | |||
@@ -30,13 +30,13 @@ namespace Discord.Rest | |||
var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses"); | |||
var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); | |||
var maxAge = maxAgeModel.OldValue.ToObject<int>(); | |||
var code = codeModel.OldValue.ToObject<string>(); | |||
var temporary = temporaryModel.OldValue.ToObject<bool>(); | |||
var inviterId = inviterIdModel.OldValue.ToObject<ulong>(); | |||
var channelId = channelIdModel.OldValue.ToObject<ulong>(); | |||
var uses = usesModel.OldValue.ToObject<int>(); | |||
var maxUses = maxUsesModel.OldValue.ToObject<int>(); | |||
var maxAge = maxAgeModel.OldValue.ToObject<int>(discord.ApiClient.Serializer); | |||
var code = codeModel.OldValue.ToObject<string>(discord.ApiClient.Serializer); | |||
var temporary = temporaryModel.OldValue.ToObject<bool>(discord.ApiClient.Serializer); | |||
var inviterId = inviterIdModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var channelId = channelIdModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var uses = usesModel.OldValue.ToObject<int>(discord.ApiClient.Serializer); | |||
var maxUses = maxUsesModel.OldValue.ToObject<int>(discord.ApiClient.Serializer); | |||
var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId); | |||
var inviter = RestUser.Create(discord, inviterInfo); | |||
@@ -23,16 +23,16 @@ namespace Discord.Rest | |||
var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); | |||
var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); | |||
int? oldMaxAge = maxAgeModel?.OldValue?.ToObject<int>(), | |||
newMaxAge = maxAgeModel?.NewValue?.ToObject<int>(); | |||
string oldCode = codeModel?.OldValue?.ToObject<string>(), | |||
newCode = codeModel?.NewValue?.ToObject<string>(); | |||
bool? oldTemporary = temporaryModel?.OldValue?.ToObject<bool>(), | |||
newTemporary = temporaryModel?.NewValue?.ToObject<bool>(); | |||
ulong? oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>(), | |||
newChannelId = channelIdModel?.NewValue?.ToObject<ulong>(); | |||
int? oldMaxUses = maxUsesModel?.OldValue?.ToObject<int>(), | |||
newMaxUses = maxUsesModel?.NewValue?.ToObject<int>(); | |||
int? oldMaxAge = maxAgeModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), | |||
newMaxAge = maxAgeModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); | |||
string oldCode = codeModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newCode = codeModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
bool? oldTemporary = temporaryModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer), | |||
newTemporary = temporaryModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
ulong? oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer), | |||
newChannelId = channelIdModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
int? oldMaxUses = maxUsesModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer), | |||
newMaxUses = maxUsesModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer); | |||
var before = new InviteInfo(oldMaxAge, oldCode, oldTemporary, oldChannelId, oldMaxUses); | |||
var after = new InviteInfo(newMaxAge, newCode, newTemporary, newChannelId, newMaxUses); | |||
@@ -0,0 +1,18 @@ | |||
namespace Discord.Rest | |||
{ | |||
public struct MemberInfo | |||
{ | |||
internal MemberInfo(string nick, bool? deaf, bool? mute, string avatar_hash) | |||
{ | |||
Nickname = nick; | |||
Deaf = deaf; | |||
Mute = mute; | |||
AvatarHash = avatar_hash; | |||
} | |||
public string Nickname { get; } | |||
public bool? Deaf { get; } | |||
public bool? Mute { get; } | |||
public string AvatarHash { get; } | |||
} | |||
} |
@@ -19,7 +19,7 @@ namespace Discord.Rest | |||
{ | |||
var changes = entry.Changes; | |||
var roleInfos = changes.SelectMany(x => x.NewValue.ToObject<API.Role[]>(), | |||
var roleInfos = changes.SelectMany(x => x.NewValue.ToObject<API.Role[]>(discord.ApiClient.Serializer), | |||
(model, role) => new { model.ChangedProperty, Role = role }) | |||
.Select(x => new MemberRoleEditInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add")) | |||
.ToList(); | |||
@@ -8,28 +8,42 @@ namespace Discord.Rest | |||
{ | |||
public class MemberUpdateAuditLogData : IAuditLogData | |||
{ | |||
private MemberUpdateAuditLogData(IUser target, string newNick, string oldNick) | |||
private MemberUpdateAuditLogData(IUser target, MemberInfo before, MemberInfo after) | |||
{ | |||
Target = target; | |||
NewNick = newNick; | |||
OldNick = oldNick; | |||
Before = before; | |||
After = after; | |||
} | |||
internal static MemberUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | |||
{ | |||
var changes = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "nick"); | |||
var changes = entry.Changes; | |||
var newNick = changes.NewValue?.ToObject<string>(); | |||
var oldNick = changes.OldValue?.ToObject<string>(); | |||
var nickModel = changes.FirstOrDefault(x => x.ChangedProperty == "nick"); | |||
var deafModel = changes.FirstOrDefault(x => x.ChangedProperty == "deaf"); | |||
var muteModel = changes.FirstOrDefault(x => x.ChangedProperty == "mute"); | |||
var avatarModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); | |||
string oldNick = nickModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newNick = nickModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
bool? oldDeaf = deafModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer), | |||
newDeaf = deafModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
bool? oldMute = muteModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer), | |||
newMute = muteModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
string oldAvatar = avatarModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newAvatar = avatarModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
var targetInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); | |||
var user = RestUser.Create(discord, targetInfo); | |||
return new MemberUpdateAuditLogData(user, newNick, oldNick); | |||
var before = new MemberInfo(oldNick, oldDeaf, oldMute, oldAvatar); | |||
var after = new MemberInfo(newNick, newDeaf, newMute, newAvatar); | |||
return new MemberUpdateAuditLogData(user, before, after); | |||
} | |||
public IUser Target { get; } | |||
public string NewNick { get; } | |||
public string OldNick { get; } | |||
public MemberInfo Before { get; } | |||
public MemberInfo After { get; } | |||
} | |||
} |
@@ -19,17 +19,15 @@ namespace Discord.Rest | |||
var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny"); | |||
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); | |||
var deny = denyModel.NewValue.ToObject<ulong>(); | |||
var allow = allowModel.NewValue.ToObject<ulong>(); | |||
var deny = denyModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var allow = allowModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var permissions = new OverwritePermissions(allow, deny); | |||
var id = entry.Options.OverwriteTargetId.Value; | |||
var type = entry.Options.OverwriteType; | |||
PermissionTarget target = type == "member" ? PermissionTarget.User : PermissionTarget.Role; | |||
return new OverwriteCreateAuditLogData(new Overwrite(id, target, permissions)); | |||
return new OverwriteCreateAuditLogData(new Overwrite(id, type, permissions)); | |||
} | |||
public Overwrite Overwrite { get; } | |||
@@ -27,14 +27,12 @@ namespace Discord.Rest | |||
var idModel = changes.FirstOrDefault(x => x.ChangedProperty == "id"); | |||
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); | |||
var deny = denyModel.OldValue.ToObject<ulong>(); | |||
var type = typeModel.OldValue.ToObject<string>(); | |||
var id = idModel.OldValue.ToObject<ulong>(); | |||
var allow = allowModel.OldValue.ToObject<ulong>(); | |||
var deny = denyModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var type = typeModel.OldValue.ToObject<PermissionTarget>(discord.ApiClient.Serializer); | |||
var id = idModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var allow = allowModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
PermissionTarget target = type == "member" ? PermissionTarget.User : PermissionTarget.Role; | |||
return new OverwriteDeleteAuditLogData(new Overwrite(id, target, new OverwritePermissions(allow, deny))); | |||
return new OverwriteDeleteAuditLogData(new Overwrite(id, type, new OverwritePermissions(allow, deny))); | |||
} | |||
public Overwrite Overwrite { get; } | |||
@@ -22,17 +22,17 @@ namespace Discord.Rest | |||
var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny"); | |||
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); | |||
var beforeAllow = allowModel?.OldValue?.ToObject<ulong>(); | |||
var afterAllow = allowModel?.NewValue?.ToObject<ulong>(); | |||
var beforeDeny = denyModel?.OldValue?.ToObject<ulong>(); | |||
var afterDeny = denyModel?.OldValue?.ToObject<ulong>(); | |||
var beforeAllow = allowModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var afterAllow = allowModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var beforeDeny = denyModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var afterDeny = denyModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var beforePermissions = new OverwritePermissions(beforeAllow ?? 0, beforeDeny ?? 0); | |||
var afterPermissions = new OverwritePermissions(afterAllow ?? 0, afterDeny ?? 0); | |||
PermissionTarget target = entry.Options.OverwriteType == "member" ? PermissionTarget.User : PermissionTarget.Role; | |||
var type = entry.Options.OverwriteType; | |||
return new OverwriteUpdateAuditLogData(beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, target); | |||
return new OverwriteUpdateAuditLogData(beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, type); | |||
} | |||
public OverwritePermissions OldPermissions { get; } | |||
@@ -23,11 +23,11 @@ namespace Discord.Rest | |||
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); | |||
uint? colorRaw = colorModel?.NewValue?.ToObject<uint>(); | |||
bool? mentionable = mentionableModel?.NewValue?.ToObject<bool>(); | |||
bool? hoist = hoistModel?.NewValue?.ToObject<bool>(); | |||
string name = nameModel?.NewValue?.ToObject<string>(); | |||
ulong? permissionsRaw = permissionsModel?.NewValue?.ToObject<ulong>(); | |||
uint? colorRaw = colorModel?.NewValue?.ToObject<uint>(discord.ApiClient.Serializer); | |||
bool? mentionable = mentionableModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
bool? hoist = hoistModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
string name = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
ulong? permissionsRaw = permissionsModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
Color? color = null; | |||
GuildPermissions? permissions = null; | |||
@@ -23,11 +23,11 @@ namespace Discord.Rest | |||
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); | |||
uint? colorRaw = colorModel?.OldValue?.ToObject<uint>(); | |||
bool? mentionable = mentionableModel?.OldValue?.ToObject<bool>(); | |||
bool? hoist = hoistModel?.OldValue?.ToObject<bool>(); | |||
string name = nameModel?.OldValue?.ToObject<string>(); | |||
ulong? permissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(); | |||
uint? colorRaw = colorModel?.OldValue?.ToObject<uint>(discord.ApiClient.Serializer); | |||
bool? mentionable = mentionableModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
bool? hoist = hoistModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
string name = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
ulong? permissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
Color? color = null; | |||
GuildPermissions? permissions = null; | |||
@@ -24,16 +24,16 @@ namespace Discord.Rest | |||
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); | |||
uint? oldColorRaw = colorModel?.OldValue?.ToObject<uint>(), | |||
newColorRaw = colorModel?.NewValue?.ToObject<uint>(); | |||
bool? oldMentionable = mentionableModel?.OldValue?.ToObject<bool>(), | |||
newMentionable = mentionableModel?.NewValue?.ToObject<bool>(); | |||
bool? oldHoist = hoistModel?.OldValue?.ToObject<bool>(), | |||
newHoist = hoistModel?.NewValue?.ToObject<bool>(); | |||
string oldName = nameModel?.OldValue?.ToObject<string>(), | |||
newName = nameModel?.NewValue?.ToObject<string>(); | |||
ulong? oldPermissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(), | |||
newPermissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(); | |||
uint? oldColorRaw = colorModel?.OldValue?.ToObject<uint>(discord.ApiClient.Serializer), | |||
newColorRaw = colorModel?.NewValue?.ToObject<uint>(discord.ApiClient.Serializer); | |||
bool? oldMentionable = mentionableModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer), | |||
newMentionable = mentionableModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
bool? oldHoist = hoistModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer), | |||
newHoist = hoistModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer); | |||
string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer), | |||
newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
ulong? oldPermissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer), | |||
newPermissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
Color? oldColor = null, | |||
newColor = null; | |||
@@ -23,9 +23,9 @@ namespace Discord.Rest | |||
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); | |||
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var channelId = channelIdModel.NewValue.ToObject<ulong>(); | |||
var type = typeModel.NewValue.ToObject<WebhookType>(); | |||
var name = nameModel.NewValue.ToObject<string>(); | |||
var channelId = channelIdModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var type = typeModel.NewValue.ToObject<WebhookType>(discord.ApiClient.Serializer); | |||
var name = nameModel.NewValue.ToObject<string>(discord.ApiClient.Serializer); | |||
var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId); | |||
var webhook = RestWebhook.Create(discord, (IGuild)null, webhookInfo); | |||
@@ -29,10 +29,10 @@ namespace Discord.Rest | |||
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); | |||
var channelId = channelIdModel.OldValue.ToObject<ulong>(); | |||
var type = typeModel.OldValue.ToObject<WebhookType>(); | |||
var name = nameModel.OldValue.ToObject<string>(); | |||
var avatarHash = avatarHashModel?.OldValue?.ToObject<string>(); | |||
var channelId = channelIdModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var type = typeModel.OldValue.ToObject<WebhookType>(discord.ApiClient.Serializer); | |||
var name = nameModel.OldValue.ToObject<string>(discord.ApiClient.Serializer); | |||
var avatarHash = avatarHashModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
return new WebhookDeleteAuditLogData(entry.TargetId.Value, channelId, type, name, avatarHash); | |||
} | |||
@@ -26,18 +26,18 @@ namespace Discord.Rest | |||
var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); | |||
var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); | |||
var oldName = nameModel?.OldValue?.ToObject<string>(); | |||
var oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>(); | |||
var oldAvatar = avatarHashModel?.OldValue?.ToObject<string>(); | |||
var oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
var oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var oldAvatar = avatarHashModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
var before = new WebhookInfo(oldName, oldChannelId, oldAvatar); | |||
var newName = nameModel?.NewValue?.ToObject<string>(); | |||
var newChannelId = channelIdModel?.NewValue?.ToObject<ulong>(); | |||
var newAvatar = avatarHashModel?.NewValue?.ToObject<string>(); | |||
var newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
var newChannelId = channelIdModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer); | |||
var newAvatar = avatarHashModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer); | |||
var after = new WebhookInfo(newName, newChannelId, newAvatar); | |||
var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId); | |||
var webhook = RestWebhook.Create(discord, (IGuild)null, webhookInfo); | |||
var webhook = webhookInfo != null ? RestWebhook.Create(discord, (IGuild)null, webhookInfo) : null; | |||
return new WebhookUpdateAuditLogData(webhook, before, after); | |||
} | |||
@@ -1,4 +1,5 @@ | |||
using System.Linq; | |||
using System; | |||
using System.Linq; | |||
using Model = Discord.API.AuditLog; | |||
using EntryModel = Discord.API.AuditLogEntry; | |||
@@ -26,6 +27,8 @@ namespace Discord.Rest | |||
return new RestAuditLogEntry(discord, fullLog, model, user); | |||
} | |||
/// <inheritdoc/> | |||
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | |||
/// <inheritdoc/> | |||
public ActionType Action { get; } | |||
/// <inheritdoc/> | |||
@@ -0,0 +1,124 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Xunit; | |||
namespace Discord | |||
{ | |||
public class TokenUtilsTests | |||
{ | |||
/// <summary> | |||
/// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | |||
/// to see that when a null, empty or whitespace-only string is passed as the token, | |||
/// it will throw an ArgumentNullException. | |||
/// </summary> | |||
[Theory] | |||
[InlineData(null)] | |||
[InlineData("")] // string.Empty isn't a constant type | |||
[InlineData(" ")] | |||
[InlineData(" ")] | |||
[InlineData("\t")] | |||
public void TestNullOrWhitespaceToken(string token) | |||
{ | |||
// an ArgumentNullException should be thrown, regardless of the TokenType | |||
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bearer, token)); | |||
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bot, token)); | |||
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Webhook, token)); | |||
} | |||
/// <summary> | |||
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | |||
/// to see that valid Webhook tokens do not throw Exceptions. | |||
/// </summary> | |||
/// <param name="token"></param> | |||
[Theory] | |||
[InlineData("123123123")] | |||
// bot token | |||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] | |||
// bearer token taken from discord docs | |||
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] | |||
// client secret | |||
[InlineData("937it3ow87i4ery69876wqire")] | |||
public void TestWebhookTokenDoesNotThrowExceptions(string token) | |||
{ | |||
TokenUtils.ValidateToken(TokenType.Webhook, token); | |||
} | |||
// No tests for invalid webhook token behavior, because there is nothing there yet. | |||
/// <summary> | |||
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | |||
/// to see that valid Webhook tokens do not throw Exceptions. | |||
/// </summary> | |||
/// <param name="token"></param> | |||
[Theory] | |||
[InlineData("123123123")] | |||
// bot token | |||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] | |||
// bearer token taken from discord docs | |||
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] | |||
// client secret | |||
[InlineData("937it3ow87i4ery69876wqire")] | |||
public void TestBearerTokenDoesNotThrowExceptions(string token) | |||
{ | |||
TokenUtils.ValidateToken(TokenType.Bearer, token); | |||
} | |||
// No tests for invalid bearer token behavior, because there is nothing there yet. | |||
/// <summary> | |||
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | |||
/// to see that valid Bot tokens do not throw Exceptions. | |||
/// Valid Bot tokens can be strings of length 59 or above. | |||
/// </summary> | |||
[Theory] | |||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] | |||
[InlineData("This appears to be completely invalid, however the current validation rules are not very strict.")] | |||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWss")] | |||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] | |||
public void TestBotTokenDoesNotThrowExceptions(string token) | |||
{ | |||
// This example token is pulled from the Discord Docs | |||
// https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header | |||
// should not throw any exception | |||
TokenUtils.ValidateToken(TokenType.Bot, token); | |||
} | |||
/// <summary> | |||
/// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> with | |||
/// a Bot token that is invalid. | |||
/// </summary> | |||
[Theory] | |||
[InlineData("This is invalid")] | |||
// missing a single character from the end | |||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")] | |||
// bearer token | |||
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] | |||
// client secret | |||
[InlineData("937it3ow87i4ery69876wqire")] | |||
public void TestBotTokenInvalidThrowsArgumentException(string token) | |||
{ | |||
Assert.Throws<ArgumentException>(() => TokenUtils.ValidateToken(TokenType.Bot, token)); | |||
} | |||
/// <summary> | |||
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | |||
/// to see that an <see cref="ArgumentException"/> is thrown when an invalid | |||
/// <see cref="TokenType"/> is supplied as a parameter. | |||
/// </summary> | |||
/// <remarks> | |||
/// The <see cref="TokenType.User"/> type is treated as an invalid <see cref="TokenType"/>. | |||
/// </remarks> | |||
[Theory] | |||
// TokenType.User | |||
[InlineData(0)] | |||
// out of range TokenType | |||
[InlineData(4)] | |||
[InlineData(7)] | |||
public void TestUnrecognizedTokenType(int type) | |||
{ | |||
Assert.Throws<ArgumentException>(() => | |||
TokenUtils.ValidateToken((TokenType)type, "MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")); | |||
} | |||
} | |||
} |