diff --git a/Discord.Net.targets b/Discord.Net.targets
index 410e21cb8..dc0209940 100644
--- a/Discord.Net.targets
+++ b/Discord.Net.targets
@@ -1,6 +1,6 @@
- 2.0.2
+ 2.1.0
dev
RogueException
discord;discordapp
diff --git a/README.md b/README.md
index 7dc8cd788..bf04dd5eb 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
# Discord.Net
[](https://www.nuget.org/packages/Discord.Net)
[](https://www.myget.org/feed/Packages/discord-net)
-[](https://ci.appveyor.com/project/RogueException/discord-net/branch/dev)
+[](https://dev.azure.com/discord-net/Discord.Net/_build/latest?definitionId=1&branchName=dev)
[](https://discord.gg/jkrBmQR)
An unofficial .NET API Wrapper for the Discord client (http://discordapp.com).
-Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/jkrBmQR).
+Check out the [documentation](https://discord.foxbot.me/) or join the [Discord API Chat](https://discord.gg/jkrBmQR).
## Installation
### Stable (NuGet)
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index ef5f1667e..000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,94 +0,0 @@
-version: build-{build}
-branches:
- only:
- - dev
-image:
-- Visual Studio 2017
-- Ubuntu
-
-nuget:
- disable_publish_on_pr: true
-pull_requests:
- do_not_increment_build_number: true
-# Use the default clone_folder
-# Windows: C:\Projects\discord-net
-# Ubuntu: /home/appveyor/projects/discord-net
-cache: test/Discord.Net.Tests/cache.db
-
-environment:
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DNET_TEST_TOKEN:
- secure: l7h5e7UE7yRd70hAB97kjPiQpPOShwqoBbOzEAYQ+XBd/Pre5OA33IXa3uisdUeQJP/nPFhcOsI+yn7WpuFaoQ==
- DNET_TEST_GUILDID: 273160668180381696
-init:
-- ps: $Env:BUILD = "$($Env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0"))"
-
-build_script:
-- ps: >-
- if ($isLinux)
- {
- # AppVeyor Linux images do not have appveyor-retry, which retries the commands a few times
- # until the command exits with code 0.
- # So, this is done with a short script.
- $code = 0
- $counter = 0
- do
- {
- dotnet restore Discord.Net.sln -v Minimal /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG"
- $code = $LASTEXITCODE
- $counter++
- if ($code -ne 0)
- {
- # Wait 5s before attempting to run again
- Start-sleep -Seconds 5
- }
- }
- until ($counter -eq 5 -or $code -eq 0)
- }
- else
- {
- appveyor-retry dotnet restore Discord.Net.sln -v Minimal /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG"
- }
-- ps: dotnet build Discord.Net.sln -c "Release" /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG"
-after_build:
-- ps: if ($isWindows) { dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" }
-- ps: if ($isWindows) { dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" }
-- ps: if ($isWindows) { dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" }
-- ps: if ($isWindows) { dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" }
-- ps: if ($isWindows) { dotnet pack "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" }
-- ps: if ($isWindows) { dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" }
-- ps: if ($isWindows) { dotnet pack "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" }
-- ps: >-
- if ($isWindows)
- {
- if ($Env:APPVEYOR_REPO_TAG -eq "true") {
- nuget pack src/Discord.Net/Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix=""
- } else {
- nuget pack src/Discord.Net/Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="-$Env:BUILD"
- }
- }
-- ps: if ($isWindows) { Get-ChildItem artifacts/*.nupkg | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } }
-
-test_script:
-- ps: >-
- if ($APPVEYOR_PULL_REQUEST_NUMBER -eq "") {
- dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG"
- }
-
-deploy:
-- provider: NuGet
- server: https://www.myget.org/F/discord-net/api/v2/package
- api_key:
- secure: Jl7BXeUjRnkVHDMBuUWSXcEOkrli1PBleW2IiLyUs5j63UNUNp1hcjaUJRujx9lz
- symbol_server: https://www.myget.org/F/discord-net/symbols/api/v2/package
- on:
- branch: dev
- CI_WINDOWS: true
-- provider: NuGet
- server: https://www.myget.org/F/rogueexception/api/v2/package
- api_key:
- secure: D+vW2O2LBf/iJb4f+q8fkyIW2VdIYIGxSYLWNrOD4BHlDBZQlJipDbNarWjUr2Kn
- symbol_server: https://www.myget.org/F/rogueexception/symbols/api/v2/package
- on:
- branch: dev
- CI_WINDOWS: true
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 000000000..5a2ee8465
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,31 @@
+variables:
+ buildConfiguration: Release
+ buildTag: $[ startsWith(variables['Build.SourceBranch'], 'refs/tags') ]
+ buildNumber: $[ variables['Build.BuildNumber'] ]
+
+
+jobs:
+- job: Linux
+ pool:
+ vmImage: 'ubuntu-16.04'
+ steps:
+ - template: azure/build.yml
+
+- job: Windows_build
+ pool:
+ vmImage: 'vs2017-win2016'
+ condition: ne(variables['Build.SourceBranch'], 'refs/heads/dev')
+ steps:
+ - template: azure/build.yml
+
+- job: Windows_deploy
+ pool:
+ vmImage: 'vs2017-win2016'
+ condition: |
+ and (
+ succeeded(),
+ eq(variables['Build.SourceBranch'], 'refs/heads/dev')
+ )
+ steps:
+ - template: azure/build.yml
+ - template: azure/deploy.yml
diff --git a/azure/build.yml b/azure/build.yml
new file mode 100644
index 000000000..ff32eae2d
--- /dev/null
+++ b/azure/build.yml
@@ -0,0 +1,17 @@
+steps:
+- script: dotnet restore -v minimal Discord.Net.sln
+ displayName: Restore packages
+
+- script: dotnet build "Discord.Net.sln" --no-restore -v minimal -c $(buildConfiguration) /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ displayName: Build projects
+
+- script: dotnet test "test/Discord.Net.Tests/Discord.Net.Tests.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) --logger trx
+ # TODO: update this to support multiple tests
+ displayName: Test projects
+
+- task: PublishTestResults@2
+ displayName: Publish test results
+ condition: succeededOrFailed()
+ inputs:
+ testRunner: VSTest
+ testResultsFiles: '**/*.trx'
diff --git a/azure/deploy.yml b/azure/deploy.yml
new file mode 100644
index 000000000..f2affe667
--- /dev/null
+++ b/azure/deploy.yml
@@ -0,0 +1,32 @@
+steps:
+- script: |
+ dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "../../artifacts/" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "../../artifacts/" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "../../artifacts/" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "../../artifacts/" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ dotnet pack "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "../../artifacts/" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "../../artifacts/" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ dotnet pack "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj" --no-restore --no-build -v minimal -c $(buildConfiguration) -o "../../artifacts/" /p:BuildNumber=$(buildNumber) /p:IsTagBuild=$(buildTag)
+ displayName: Pack projects
+
+- task: NuGet@0
+ inputs:
+ command: pack
+ arguments: src/Discord.Net/Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix=""
+ displayName: Pack metapackage (release mode)
+ condition: eq(variables['buildTag'], True)
+
+- task: NuGet@0
+ inputs:
+ command: pack
+ arguments: src/Discord.Net/Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="-$(buildNumber)"
+ displayName: Pack metapackage
+ condition: eq(variables['buildTag'], False)
+
+- task: NuGetCommand@2
+ displayName: Push to NuGet
+ inputs:
+ command: push
+ nuGetFeedType: external
+ packagesToPush: 'artifacts/*.nupkg'
+ publishFeedCredentials: myget-discord
diff --git a/docs/_overwrites/Common/EmbedBuilder.Overwrites.md b/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
index 2dcb1e004..409a78e94 100644
--- a/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
+++ b/docs/_overwrites/Common/EmbedBuilder.Overwrites.md
@@ -40,9 +40,10 @@ public async Task SendRichEmbedAsync()
.WithTitle("I overwrote \"Hello world!\"")
.WithDescription("I am a description.")
.WithUrl("https://example.com")
- .WithCurrentTimestamp()
- .Build();
- await ReplyAsync(embed: embed);
+ .WithCurrentTimestamp();
+
+ //Your embed needs to be built before it is able to be sent
+ await ReplyAsync(embed: embed.Build());
}
```
@@ -65,4 +66,4 @@ public async Task SendEmbedWithImageAsync()
}.Build();
await Context.Channel.SendFileAsync(fileName, embed: embed);
}
-```
\ No newline at end of file
+```
diff --git a/docs/_template/light-dark-theme/styles/dark.css b/docs/_template/light-dark-theme/styles/dark.css
index 8ae0049d1..54ad76c79 100644
--- a/docs/_template/light-dark-theme/styles/dark.css
+++ b/docs/_template/light-dark-theme/styles/dark.css
@@ -304,5 +304,5 @@ span.arrow-r{
}
.logo-switcher {
- background: url("/marketing/logo/SVG/Combinationmark White.svg") no-repeat;
+ background: url("../marketing/logo/SVG/Combinationmark White.svg") no-repeat;
}
diff --git a/docs/_template/light-dark-theme/styles/gray.css b/docs/_template/light-dark-theme/styles/gray.css
index 32ff7d208..4cb658788 100644
--- a/docs/_template/light-dark-theme/styles/gray.css
+++ b/docs/_template/light-dark-theme/styles/gray.css
@@ -311,5 +311,5 @@ span.arrow-r{
}
.logo-switcher {
- background: url("/marketing/logo/SVG/Combinationmark White.svg") no-repeat;
+ background: url("../marketing/logo/SVG/Combinationmark White.svg") no-repeat;
}
diff --git a/docs/_template/light-dark-theme/styles/light.css b/docs/_template/light-dark-theme/styles/light.css
index 71910e774..a2ba30788 100644
--- a/docs/_template/light-dark-theme/styles/light.css
+++ b/docs/_template/light-dark-theme/styles/light.css
@@ -113,5 +113,5 @@ span.arrow-r{
}
.logo-switcher {
- background: url("/marketing/logo/SVG/Combinationmark.svg") no-repeat;
+ background: url("../marketing/logo/SVG/Combinationmark.svg") no-repeat;
}
diff --git a/samples/02_commands_framework/Modules/PublicModule.cs b/samples/02_commands_framework/Modules/PublicModule.cs
index 8d55d8ba8..b9263649f 100644
--- a/samples/02_commands_framework/Modules/PublicModule.cs
+++ b/samples/02_commands_framework/Modules/PublicModule.cs
@@ -60,6 +60,7 @@ namespace _02_commands_framework.Modules
public Task ListAsync(params string[] objects)
=> ReplyAsync("You listed: " + string.Join("; ", objects));
+ // Setting a custom ErrorMessage property will help clarify the precondition error
[Command("guild_only")]
[RequireContext(ContextType.Guild, ErrorMessage = "Sorry, this command must be ran from within a server, not a DM!")]
public Task GuildOnlyCommand()
diff --git a/samples/02_commands_framework/Program.cs b/samples/02_commands_framework/Program.cs
index 76c11f9f0..ccbc8e165 100644
--- a/samples/02_commands_framework/Program.cs
+++ b/samples/02_commands_framework/Program.cs
@@ -37,10 +37,12 @@ namespace _02_commands_framework
client.Log += LogAsync;
services.GetRequiredService().Log += LogAsync;
- // Tokens should be considered secret data, and never hard-coded.
+ // Tokens should be considered secret data and never hard-coded.
+ // We can read from the environment variable to avoid hardcoding.
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
await client.StartAsync();
+ // Here we initialize the logic required to register our commands.
await services.GetRequiredService().InitializeAsync();
await Task.Delay(-1);
diff --git a/samples/02_commands_framework/Services/CommandHandlingService.cs b/samples/02_commands_framework/Services/CommandHandlingService.cs
index d29be9201..5ec496f78 100644
--- a/samples/02_commands_framework/Services/CommandHandlingService.cs
+++ b/samples/02_commands_framework/Services/CommandHandlingService.cs
@@ -20,12 +20,16 @@ namespace _02_commands_framework.Services
_discord = services.GetRequiredService();
_services = services;
+ // Hook CommandExecuted to handle post-command-execution logic.
_commands.CommandExecuted += CommandExecutedAsync;
+ // Hook MessageReceived so we can process each message to see
+ // if it qualifies as a command.
_discord.MessageReceived += MessageReceivedAsync;
}
public async Task InitializeAsync()
{
+ // Register modules that are public and inherit ModuleBase.
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
}
@@ -37,10 +41,18 @@ namespace _02_commands_framework.Services
// This value holds the offset where the prefix ends
var argPos = 0;
+ // Perform prefix check. You may want to replace this with
+ // (!message.HasCharPrefix('!', ref argPos))
+ // for a more traditional command format like !help.
if (!message.HasMentionPrefix(_discord.CurrentUser, ref argPos)) return;
var context = new SocketCommandContext(_discord, message);
- await _commands.ExecuteAsync(context, argPos, _services); // we will handle the result in CommandExecutedAsync
+ // Perform the execution of the command. In this method,
+ // the command service will perform precondition and parsing check
+ // then execute the command if one is matched.
+ await _commands.ExecuteAsync(context, argPos, _services);
+ // Note that normally a result will be returned by this format, but here
+ // we will handle the result in CommandExecutedAsync,
}
public async Task CommandExecutedAsync(Optional command, ICommandContext context, IResult result)
@@ -49,12 +61,12 @@ namespace _02_commands_framework.Services
if (!command.IsSpecified)
return;
- // the command was succesful, we don't care about this result, unless we want to log that a command succeeded.
+ // the command was successful, we don't care about this result, unless we want to log that a command succeeded.
if (result.IsSuccess)
return;
// the command failed, let's notify the user that something happened.
- await context.Channel.SendMessageAsync($"error: {result.ToString()}");
+ await context.Channel.SendMessageAsync($"error: {result}");
}
}
}
diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj
index ff9b3c5e0..72a958b4f 100644
--- a/src/Discord.Net.Core/Discord.Net.Core.csproj
+++ b/src/Discord.Net.Core/Discord.Net.Core.csproj
@@ -11,8 +11,8 @@
-
-
-
+
+ all
+
diff --git a/src/Discord.Net.Core/Entities/Channels/ChannelType.cs b/src/Discord.Net.Core/Entities/Channels/ChannelType.cs
index 7759622c2..6dd910ba6 100644
--- a/src/Discord.Net.Core/Entities/Channels/ChannelType.cs
+++ b/src/Discord.Net.Core/Entities/Channels/ChannelType.cs
@@ -12,6 +12,8 @@ namespace Discord
/// The channel is a group channel.
Group = 3,
/// The channel is a category channel.
- Category = 4
+ Category = 4,
+ /// The channel is a news channel.
+ News = 5
}
}
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageType.cs b/src/Discord.Net.Core/Entities/Messages/MessageType.cs
index 5056da8ea..8f3af843b 100644
--- a/src/Discord.Net.Core/Entities/Messages/MessageType.cs
+++ b/src/Discord.Net.Core/Entities/Messages/MessageType.cs
@@ -32,6 +32,10 @@ namespace Discord
///
/// The message when another message is pinned.
///
- ChannelPinnedMessage = 6
+ ChannelPinnedMessage = 6,
+ ///
+ /// The message when a new member joined.
+ ///
+ GuildMemberJoin = 7
}
}
diff --git a/src/Discord.Net.Core/Utils/TokenUtils.cs b/src/Discord.Net.Core/Utils/TokenUtils.cs
index 68aad5d96..b52ba3dd6 100644
--- a/src/Discord.Net.Core/Utils/TokenUtils.cs
+++ b/src/Discord.Net.Core/Utils/TokenUtils.cs
@@ -17,6 +17,47 @@ namespace Discord
///
internal const int MinBotTokenLength = 58;
+ internal const char Base64Padding = '=';
+
+ ///
+ /// Pads a base64-encoded string with 0, 1, or 2 '=' characters,
+ /// if the string is not a valid multiple of 4.
+ /// Does not ensure that the provided string contains only valid base64 characters.
+ /// Strings that already contain padding will not have any more padding applied.
+ ///
+ ///
+ /// A string that would require 3 padding characters is considered to be already corrupt.
+ /// Some older bot tokens may require padding, as the format provided by Discord
+ /// does not include this padding in the token.
+ ///
+ /// The base64 encoded string to pad with characters.
+ /// A string containing the base64 padding.
+ ///
+ /// Thrown if would require an invalid number of padding characters.
+ ///
+ ///
+ /// Thrown if is null, empty, or whitespace.
+ ///
+ internal static string PadBase64String(string encodedBase64)
+ {
+ if (string.IsNullOrWhiteSpace(encodedBase64))
+ throw new ArgumentNullException(paramName: encodedBase64,
+ message: "The supplied base64-encoded string was null or whitespace.");
+
+ // do not pad if already contains padding characters
+ if (encodedBase64.IndexOf(Base64Padding) != -1)
+ return encodedBase64;
+
+ // based from https://stackoverflow.com/a/1228744
+ var padding = (4 - (encodedBase64.Length % 4)) % 4;
+ if (padding == 3)
+ // can never have 3 characters of padding
+ throw new FormatException("The provided base64 string is corrupt, as it requires an invalid amount of padding.");
+ else if (padding == 0)
+ return encodedBase64;
+ return encodedBase64.PadRight(encodedBase64.Length + padding, Base64Padding);
+ }
+
///
/// Decodes a base 64 encoded string into a ulong value.
///
@@ -29,6 +70,8 @@ namespace Discord
try
{
+ // re-add base64 padding if missing
+ encoded = PadBase64String(encoded);
// decode the base64 string
var bytes = Convert.FromBase64String(encoded);
var idStr = Encoding.UTF8.GetString(bytes);
@@ -46,7 +89,7 @@ namespace Discord
}
catch (ArgumentException)
{
- // ignore exception, can be thrown by BitConverter
+ // ignore exception, can be thrown by BitConverter, or by PadBase64String
}
return null;
}
diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs
index 317d47648..c6b2e1053 100644
--- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs
+++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs
@@ -1,4 +1,4 @@
-using Model = Discord.API.AuditLog;
+using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry;
namespace Discord.Rest
@@ -8,15 +8,16 @@ namespace Discord.Rest
///
public class MessageDeleteAuditLogData : IAuditLogData
{
- private MessageDeleteAuditLogData(ulong channelId, int count)
+ private MessageDeleteAuditLogData(ulong channelId, int count, ulong authorId)
{
ChannelId = channelId;
MessageCount = count;
+ AuthorId = authorId;
}
internal static MessageDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry)
{
- return new MessageDeleteAuditLogData(entry.Options.MessageDeleteChannelId.Value, entry.Options.MessageDeleteCount.Value);
+ return new MessageDeleteAuditLogData(entry.Options.MessageDeleteChannelId.Value, entry.Options.MessageDeleteCount.Value, entry.TargetId.Value);
}
///
@@ -34,5 +35,12 @@ namespace Discord.Rest
/// deleted from.
///
public ulong ChannelId { get; }
+ ///
+ /// Gets the author of the messages that were deleted.
+ ///
+ ///
+ /// A representing the snowflake identifier for the user that created the deleted messages.
+ ///
+ public ulong AuthorId { get; }
}
}
diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs
index 21388f985..81d902fc0 100644
--- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs
+++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs
@@ -10,9 +10,10 @@ namespace Discord.Rest
///
public class WebhookCreateAuditLogData : IAuditLogData
{
- private WebhookCreateAuditLogData(IWebhook webhook, WebhookType type, string name, ulong channelId)
+ private WebhookCreateAuditLogData(IWebhook webhook, ulong webhookId, WebhookType type, string name, ulong channelId)
{
Webhook = webhook;
+ WebhookId = webhookId;
Name = name;
Type = type;
ChannelId = channelId;
@@ -31,23 +32,31 @@ namespace Discord.Rest
var name = nameModel.NewValue.ToObject(discord.ApiClient.Serializer);
var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId);
- var webhook = RestWebhook.Create(discord, (IGuild)null, webhookInfo);
+ var webhook = webhookInfo == null ? null : RestWebhook.Create(discord, (IGuild)null, webhookInfo);
- return new WebhookCreateAuditLogData(webhook, type, name, channelId);
+ return new WebhookCreateAuditLogData(webhook, entry.TargetId.Value, type, name, channelId);
}
// Doc Note: Corresponds to the *current* data
///
- /// Gets the webhook that was created.
+ /// Gets the webhook that was created if it still exists.
///
///
- /// A webhook object representing the webhook that was created.
+ /// A webhook object representing the webhook that was created if it still exists, otherwise returns null.
///
public IWebhook Webhook { get; }
// Doc Note: Corresponds to the *audit log* data
+ ///
+ /// Gets the webhook id.
+ ///
+ ///
+ /// The webhook identifier.
+ ///
+ public ulong WebhookId { get; }
+
///
/// Gets the type of webhook that was created.
///
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
index dd190199f..6f6a1f0d3 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
@@ -23,6 +23,7 @@ namespace Discord.Rest
{
switch (model.Type)
{
+ case ChannelType.News:
case ChannelType.Text:
case ChannelType.Voice:
return RestGuildChannel.Create(discord, new RestGuild(discord, model.GuildId.Value), model);
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
index 5f4db2eea..fdfee39ea 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
@@ -15,7 +15,7 @@ namespace Discord.Rest
private ImmutableArray _overwrites;
///
- public IReadOnlyCollection PermissionOverwrites => _overwrites;
+ public virtual IReadOnlyCollection PermissionOverwrites => _overwrites;
internal IGuild Guild { get; }
///
@@ -34,6 +34,8 @@ namespace Discord.Rest
{
switch (model.Type)
{
+ case ChannelType.News:
+ return RestNewsChannel.Create(discord, guild, model);
case ChannelType.Text:
return RestTextChannel.Create(discord, guild, model);
case ChannelType.Voice:
@@ -79,7 +81,7 @@ namespace Discord.Rest
///
/// An overwrite object for the targeted user; null if none is set.
///
- public OverwritePermissions? GetPermissionOverwrite(IUser user)
+ public virtual OverwritePermissions? GetPermissionOverwrite(IUser user)
{
for (int i = 0; i < _overwrites.Length; i++)
{
@@ -96,7 +98,7 @@ namespace Discord.Rest
///
/// An overwrite object for the targeted role; null if none is set.
///
- public OverwritePermissions? GetPermissionOverwrite(IRole role)
+ public virtual OverwritePermissions? GetPermissionOverwrite(IRole role)
{
for (int i = 0; i < _overwrites.Length; i++)
{
@@ -115,7 +117,7 @@ namespace Discord.Rest
///
/// A task representing the asynchronous permission operation for adding the specified permissions to the channel.
///
- public async Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null)
+ public virtual async Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null)
{
await ChannelHelper.AddPermissionOverwriteAsync(this, Discord, user, permissions, options).ConfigureAwait(false);
_overwrites = _overwrites.Add(new Overwrite(user.Id, PermissionTarget.User, new OverwritePermissions(permissions.AllowValue, permissions.DenyValue)));
@@ -129,7 +131,7 @@ namespace Discord.Rest
///
/// A task representing the asynchronous permission operation for adding the specified permissions to the channel.
///
- public async Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null)
+ public virtual async Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null)
{
await ChannelHelper.AddPermissionOverwriteAsync(this, Discord, role, permissions, options).ConfigureAwait(false);
_overwrites = _overwrites.Add(new Overwrite(role.Id, PermissionTarget.Role, new OverwritePermissions(permissions.AllowValue, permissions.DenyValue)));
@@ -143,7 +145,7 @@ namespace Discord.Rest
///
/// A task representing the asynchronous operation for removing the specified permissions from the channel.
///
- public async Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null)
+ public virtual async Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null)
{
await ChannelHelper.RemovePermissionOverwriteAsync(this, Discord, user, options).ConfigureAwait(false);
@@ -164,7 +166,7 @@ namespace Discord.Rest
///
/// A task representing the asynchronous operation for removing the specified permissions from the channel.
///
- public async Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null)
+ public virtual async Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null)
{
await ChannelHelper.RemovePermissionOverwriteAsync(this, Discord, role, options).ConfigureAwait(false);
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestNewsChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestNewsChannel.cs
new file mode 100644
index 000000000..f4984a0d2
--- /dev/null
+++ b/src/Discord.Net.Rest/Entities/Channels/RestNewsChannel.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Model = Discord.API.Channel;
+
+namespace Discord.Rest
+{
+ ///
+ /// Represents a REST-based news channel in a guild that has the same properties as a .
+ ///
+ [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
+ public class RestNewsChannel : RestTextChannel
+ {
+ internal RestNewsChannel(BaseDiscordClient discord, IGuild guild, ulong id)
+ :base(discord, guild, id)
+ {
+ }
+ internal new static RestNewsChannel Create(BaseDiscordClient discord, IGuild guild, Model model)
+ {
+ var entity = new RestNewsChannel(discord, guild, model.Id);
+ entity.Update(model);
+ return entity;
+ }
+ public override int SlowModeInterval => throw new NotSupportedException("News channels do not support Slow Mode.");
+ public override Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null)
+ {
+ throw new NotSupportedException("News channels do not support Overwrite Permissions.");
+ }
+ public override Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null)
+ {
+ throw new NotSupportedException("News channels do not support Overwrite Permissions.");
+ }
+ public override OverwritePermissions? GetPermissionOverwrite(IRole role)
+ {
+ throw new NotSupportedException("News channels do not support Overwrite Permissions.");
+ }
+ public override OverwritePermissions? GetPermissionOverwrite(IUser user)
+ {
+ throw new NotSupportedException("News channels do not support Overwrite Permissions.");
+ }
+ public override Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null)
+ {
+ throw new NotSupportedException("News channels do not support Overwrite Permissions.");
+ }
+ public override Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null)
+ {
+ throw new NotSupportedException("News channels do not support Overwrite Permissions.");
+ }
+ }
+}
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
index e95b6e877..78bc7393f 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
@@ -17,7 +17,7 @@ namespace Discord.Rest
///
public string Topic { get; private set; }
///
- public int SlowModeInterval { get; private set; }
+ public virtual int SlowModeInterval { get; private set; }
///
public ulong? CategoryId { get; private set; }
diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
index 5ec908fde..dfef960c2 100644
--- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
+++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
@@ -16,10 +16,10 @@ namespace Discord.Rest
{
private bool _isMentioningEveryone, _isTTS, _isPinned;
private long? _editedTimestampTicks;
- private ImmutableArray _attachments;
- private ImmutableArray