mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-07-10 16:01:55 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9180d51e5e | |||
| a58509fda8 | |||
| fbfff4e51f | |||
| c466c17793 | |||
| 1b2bcf771d | |||
| 99b49f0b8f | |||
| 850414b017 | |||
| 92bf6eab2d |
@@ -2,17 +2,17 @@ name: main
|
|||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||||
|
DOTNET_NOLOGO: true
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
env:
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
|
||||||
DOTNET_NOLOGO: true
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -42,17 +42,11 @@ jobs:
|
|||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
pack:
|
pack:
|
||||||
needs: test
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
env:
|
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
|
||||||
DOTNET_NOLOGO: true
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -88,7 +82,9 @@ jobs:
|
|||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
|
if: ${{ github.event_name == 'push' && github.ref_type == 'tag' }}
|
||||||
needs: pack
|
needs:
|
||||||
|
- test
|
||||||
|
- pack
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v2.41.1 (28-Sep-2023)
|
||||||
|
|
||||||
|
- CLI changes:
|
||||||
|
- Fixed an issue where the export failed to export channels with an empty name.
|
||||||
|
|
||||||
## v2.41 (15-Sep-2023)
|
## v2.41 (15-Sep-2023)
|
||||||
|
|
||||||
- General changes:
|
- General changes:
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<Version>2.41</Version>
|
<Version>2.41.1</Version>
|
||||||
<Company>Tyrrrz</Company>
|
<Company>Tyrrrz</Company>
|
||||||
<Copyright>Copyright (c) Oleksii Holub</Copyright>
|
<Copyright>Copyright (c) Oleksii Holub</Copyright>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<WarningsAsErrors>nullable</WarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -12,18 +12,18 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="1.0.4" />
|
<PackageReference Include="AngleSharp" Version="1.0.4" />
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="CSharpier.MsBuild" Version="0.25.0" PrivateAssets="all" />
|
<PackageReference Include="CSharpier.MsBuild" Version="0.25.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||||
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.2" PrivateAssets="all" />
|
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" PrivateAssets="all" />
|
||||||
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||||
<PackageReference Include="ReflectionMagic" Version="5.0.0" />
|
<PackageReference Include="ReflectionMagic" Version="5.0.0" />
|
||||||
<PackageReference Include="xunit" Version="2.5.0" />
|
<PackageReference Include="xunit" Version="2.5.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0" PrivateAssets="all" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1" PrivateAssets="all" />
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Dom;
|
using AngleSharp.Dom;
|
||||||
using DiscordChatExporter.Cli.Tests.Infra;
|
using DiscordChatExporter.Cli.Tests.Infra;
|
||||||
@@ -27,7 +28,11 @@ public class HtmlAttachmentSpecs
|
|||||||
.Select(e => e.GetAttribute("href"))
|
.Select(e => e.GetAttribute("href"))
|
||||||
.Should()
|
.Should()
|
||||||
.Contain(
|
.Contain(
|
||||||
"https://cdn.discordapp.com/attachments/885587741654536192/885587844964417596/Test.txt"
|
u =>
|
||||||
|
u.StartsWith(
|
||||||
|
"https://cdn.discordapp.com/attachments/885587741654536192/885587844964417596/Test.txt",
|
||||||
|
StringComparison.Ordinal
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +53,11 @@ public class HtmlAttachmentSpecs
|
|||||||
.Select(e => e.GetAttribute("src"))
|
.Select(e => e.GetAttribute("src"))
|
||||||
.Should()
|
.Should()
|
||||||
.Contain(
|
.Contain(
|
||||||
"https://cdn.discordapp.com/attachments/885587741654536192/885654862430359613/bird-thumbnail.png"
|
u =>
|
||||||
|
u.StartsWith(
|
||||||
|
"https://cdn.discordapp.com/attachments/885587741654536192/885654862430359613/bird-thumbnail.png",
|
||||||
|
StringComparison.Ordinal
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +78,7 @@ public class HtmlAttachmentSpecs
|
|||||||
var videoUrl = message.QuerySelector("video source")?.GetAttribute("src");
|
var videoUrl = message.QuerySelector("video source")?.GetAttribute("src");
|
||||||
videoUrl
|
videoUrl
|
||||||
.Should()
|
.Should()
|
||||||
.Be(
|
.StartWith(
|
||||||
"https://cdn.discordapp.com/attachments/885587741654536192/885655761512968233/file_example_MP4_640_3MG.mp4"
|
"https://cdn.discordapp.com/attachments/885587741654536192/885655761512968233/file_example_MP4_640_3MG.mp4"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -91,7 +100,7 @@ public class HtmlAttachmentSpecs
|
|||||||
var audioUrl = message.QuerySelector("audio source")?.GetAttribute("src");
|
var audioUrl = message.QuerySelector("audio source")?.GetAttribute("src");
|
||||||
audioUrl
|
audioUrl
|
||||||
.Should()
|
.Should()
|
||||||
.Be(
|
.StartWith(
|
||||||
"https://cdn.discordapp.com/attachments/885587741654536192/885656175348187146/file_example_MP3_1MG.mp3"
|
"https://cdn.discordapp.com/attachments/885587741654536192/885656175348187146/file_example_MP3_1MG.mp3"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ public class HtmlEmbedSpecs
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var iframeUrl = message.QuerySelector("iframe")?.GetAttribute("src");
|
var iframeUrl = message.QuerySelector("iframe")?.GetAttribute("src");
|
||||||
iframeUrl.Should().Be("https://open.spotify.com/embed/track/1LHZMWefF9502NPfArRfvP");
|
iframeUrl.Should().StartWith("https://open.spotify.com/embed/track/1LHZMWefF9502NPfArRfvP");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -161,7 +161,7 @@ public class HtmlEmbedSpecs
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var iframeUrl = message.QuerySelector("iframe")?.GetAttribute("src");
|
var iframeUrl = message.QuerySelector("iframe")?.GetAttribute("src");
|
||||||
iframeUrl.Should().Be("https://www.youtube.com/embed/qOWW4OlgbvE");
|
iframeUrl.Should().StartWith("https://www.youtube.com/embed/qOWW4OlgbvE");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class HtmlStickerSpecs
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var stickerUrl = message.QuerySelector("[title='rock'] img")?.GetAttribute("src");
|
var stickerUrl = message.QuerySelector("[title='rock'] img")?.GetAttribute("src");
|
||||||
stickerUrl.Should().Be("https://cdn.discordapp.com/stickers/904215665597120572.png");
|
stickerUrl.Should().StartWith("https://cdn.discordapp.com/stickers/904215665597120572.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -35,6 +35,9 @@ public class HtmlStickerSpecs
|
|||||||
var stickerUrl = message
|
var stickerUrl = message
|
||||||
.QuerySelector("[title='Yikes'] [data-source]")
|
.QuerySelector("[title='Yikes'] [data-source]")
|
||||||
?.GetAttribute("data-source");
|
?.GetAttribute("data-source");
|
||||||
stickerUrl.Should().Be("https://cdn.discordapp.com/stickers/816087132447178774.json");
|
|
||||||
|
stickerUrl
|
||||||
|
.Should()
|
||||||
|
.StartWith("https://cdn.discordapp.com/stickers/816087132447178774.json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,10 @@ public class JsonAttachmentSpecs
|
|||||||
.GetProperty("url")
|
.GetProperty("url")
|
||||||
.GetString()
|
.GetString()
|
||||||
.Should()
|
.Should()
|
||||||
.Be(
|
.StartWith(
|
||||||
"https://cdn.discordapp.com/attachments/885587741654536192/885587844964417596/Test.txt"
|
"https://cdn.discordapp.com/attachments/885587741654536192/885587844964417596/Test.txt"
|
||||||
);
|
);
|
||||||
|
|
||||||
attachments[0].GetProperty("fileName").GetString().Should().Be("Test.txt");
|
attachments[0].GetProperty("fileName").GetString().Should().Be("Test.txt");
|
||||||
attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(11);
|
attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(11);
|
||||||
}
|
}
|
||||||
@@ -54,9 +55,10 @@ public class JsonAttachmentSpecs
|
|||||||
.GetProperty("url")
|
.GetProperty("url")
|
||||||
.GetString()
|
.GetString()
|
||||||
.Should()
|
.Should()
|
||||||
.Be(
|
.StartWith(
|
||||||
"https://cdn.discordapp.com/attachments/885587741654536192/885654862430359613/bird-thumbnail.png"
|
"https://cdn.discordapp.com/attachments/885587741654536192/885654862430359613/bird-thumbnail.png"
|
||||||
);
|
);
|
||||||
|
|
||||||
attachments[0].GetProperty("fileName").GetString().Should().Be("bird-thumbnail.png");
|
attachments[0].GetProperty("fileName").GetString().Should().Be("bird-thumbnail.png");
|
||||||
attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(466335);
|
attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(466335);
|
||||||
}
|
}
|
||||||
@@ -80,14 +82,16 @@ public class JsonAttachmentSpecs
|
|||||||
.GetProperty("url")
|
.GetProperty("url")
|
||||||
.GetString()
|
.GetString()
|
||||||
.Should()
|
.Should()
|
||||||
.Be(
|
.StartWith(
|
||||||
"https://cdn.discordapp.com/attachments/885587741654536192/885655761512968233/file_example_MP4_640_3MG.mp4"
|
"https://cdn.discordapp.com/attachments/885587741654536192/885655761512968233/file_example_MP4_640_3MG.mp4"
|
||||||
);
|
);
|
||||||
|
|
||||||
attachments[0]
|
attachments[0]
|
||||||
.GetProperty("fileName")
|
.GetProperty("fileName")
|
||||||
.GetString()
|
.GetString()
|
||||||
.Should()
|
.Should()
|
||||||
.Be("file_example_MP4_640_3MG.mp4");
|
.Be("file_example_MP4_640_3MG.mp4");
|
||||||
|
|
||||||
attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(3114374);
|
attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(3114374);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +114,10 @@ public class JsonAttachmentSpecs
|
|||||||
.GetProperty("url")
|
.GetProperty("url")
|
||||||
.GetString()
|
.GetString()
|
||||||
.Should()
|
.Should()
|
||||||
.Be(
|
.StartWith(
|
||||||
"https://cdn.discordapp.com/attachments/885587741654536192/885656175348187146/file_example_MP3_1MG.mp3"
|
"https://cdn.discordapp.com/attachments/885587741654536192/885656175348187146/file_example_MP3_1MG.mp3"
|
||||||
);
|
);
|
||||||
|
|
||||||
attachments[0].GetProperty("fileName").GetString().Should().Be("file_example_MP3_1MG.mp3");
|
attachments[0].GetProperty("fileName").GetString().Should().Be("file_example_MP3_1MG.mp3");
|
||||||
attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(1087849);
|
attachments[0].GetProperty("fileSizeBytes").GetInt64().Should().Be(1087849);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class JsonStickerSpecs
|
|||||||
.GetProperty("sourceUrl")
|
.GetProperty("sourceUrl")
|
||||||
.GetString()
|
.GetString()
|
||||||
.Should()
|
.Should()
|
||||||
.Be("https://cdn.discordapp.com/stickers/904215665597120572.png");
|
.StartWith("https://cdn.discordapp.com/stickers/904215665597120572.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -50,6 +50,6 @@ public class JsonStickerSpecs
|
|||||||
.GetProperty("sourceUrl")
|
.GetProperty("sourceUrl")
|
||||||
.GetString()
|
.GetString()
|
||||||
.Should()
|
.Should()
|
||||||
.Be("https://cdn.discordapp.com/stickers/816087132447178774.json");
|
.StartWith("https://cdn.discordapp.com/stickers/816087132447178774.json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,13 @@ internal static class ConsoleExtensions
|
|||||||
Func<ProgressTask, ValueTask> performOperationAsync
|
Func<ProgressTask, ValueTask> performOperationAsync
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
// Description cannot be empty
|
||||||
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/1133
|
||||||
|
var actualDescription = !string.IsNullOrWhiteSpace(description) ? description : "?";
|
||||||
|
|
||||||
var progressTask = progressContext.AddTask(
|
var progressTask = progressContext.AddTask(
|
||||||
// Don't recognize random square brackets as style tags
|
// Don't recognize random square brackets as style tags
|
||||||
Markup.Escape(description),
|
Markup.Escape(actualDescription),
|
||||||
new ProgressTaskSettings { MaxValue = 1 }
|
new ProgressTaskSettings { MaxValue = 1 }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class DiscordClient
|
|||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return await Http.ResponseResiliencePolicy.ExecuteAsync(
|
return await Http.ResponseResiliencePipeline.ExecuteAsync(
|
||||||
async innerCancellationToken =>
|
async innerCancellationToken =>
|
||||||
{
|
{
|
||||||
using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(_baseUri, url));
|
using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(_baseUri, url));
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
<PackageReference Include="CSharpier.MsBuild" Version="0.25.0" PrivateAssets="all" />
|
<PackageReference Include="CSharpier.MsBuild" Version="0.25.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="Gress" Version="2.1.1" />
|
<PackageReference Include="Gress" Version="2.1.1" />
|
||||||
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
||||||
<PackageReference Include="Polly" Version="7.2.4" />
|
<PackageReference Include="Polly" Version="8.0.0" />
|
||||||
<PackageReference Include="RazorBlade" Version="0.4.3" />
|
<PackageReference Include="RazorBlade" Version="0.4.4" />
|
||||||
<PackageReference Include="Superpower" Version="3.0.0" />
|
<PackageReference Include="Superpower" Version="3.0.0" />
|
||||||
<PackageReference Include="WebMarkupMin.Core" Version="2.14.0" />
|
<PackageReference Include="WebMarkupMin.Core" Version="2.14.0" />
|
||||||
<PackageReference Include="YoutubeExplode" Version="6.3.1" />
|
<PackageReference Include="YoutubeExplode" Version="6.3.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -53,44 +53,47 @@ internal partial class ExportAssetDownloader
|
|||||||
|
|
||||||
Directory.CreateDirectory(_workingDirPath);
|
Directory.CreateDirectory(_workingDirPath);
|
||||||
|
|
||||||
await Http.ResiliencePolicy.ExecuteAsync(async () =>
|
await Http.ResiliencePipeline.ExecuteAsync(
|
||||||
{
|
async innerCancellationToken =>
|
||||||
// Download the file
|
|
||||||
using var response = await Http.Client.GetAsync(url, cancellationToken);
|
|
||||||
await using (var output = File.Create(filePath))
|
|
||||||
await response.Content.CopyToAsync(output, cancellationToken);
|
|
||||||
|
|
||||||
// Try to set the file date according to the last-modified header
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var lastModified = response.Content.Headers
|
// Download the file
|
||||||
.TryGetValue("Last-Modified")
|
using var response = await Http.Client.GetAsync(url, innerCancellationToken);
|
||||||
?.Pipe(
|
await using (var output = File.Create(filePath))
|
||||||
s =>
|
await response.Content.CopyToAsync(output, innerCancellationToken);
|
||||||
DateTimeOffset.TryParse(
|
|
||||||
s,
|
|
||||||
CultureInfo.InvariantCulture,
|
|
||||||
DateTimeStyles.None,
|
|
||||||
out var instant
|
|
||||||
)
|
|
||||||
? instant
|
|
||||||
: (DateTimeOffset?)null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (lastModified is not null)
|
// Try to set the file date according to the last-modified header
|
||||||
|
try
|
||||||
{
|
{
|
||||||
File.SetCreationTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
var lastModified = response.Content.Headers
|
||||||
File.SetLastWriteTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
.TryGetValue("Last-Modified")
|
||||||
File.SetLastAccessTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
?.Pipe(
|
||||||
|
s =>
|
||||||
|
DateTimeOffset.TryParse(
|
||||||
|
s,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
DateTimeStyles.None,
|
||||||
|
out var instant
|
||||||
|
)
|
||||||
|
? instant
|
||||||
|
: (DateTimeOffset?)null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lastModified is not null)
|
||||||
|
{
|
||||||
|
File.SetCreationTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
||||||
|
File.SetLastWriteTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
||||||
|
File.SetLastAccessTimeUtc(filePath, lastModified.Value.UtcDateTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
catch
|
||||||
catch
|
{
|
||||||
{
|
// This can apparently fail for some reason.
|
||||||
// This can apparently fail for some reason.
|
// Updating the file date is not a critical task, so we'll just ignore exceptions thrown here.
|
||||||
// Updating the file date is not a critical task, so we'll just ignore exceptions thrown here.
|
// https://github.com/Tyrrrz/DiscordChatExporter/issues/585
|
||||||
// https://github.com/Tyrrrz/DiscordChatExporter/issues/585
|
}
|
||||||
}
|
},
|
||||||
});
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
return _previousPathsByUrl[url] = filePath;
|
return _previousPathsByUrl[url] = filePath;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Security.Authentication;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DiscordChatExporter.Core.Utils.Extensions;
|
using DiscordChatExporter.Core.Utils.Extensions;
|
||||||
using Polly;
|
using Polly;
|
||||||
|
using Polly.Retry;
|
||||||
|
|
||||||
namespace DiscordChatExporter.Core.Utils;
|
namespace DiscordChatExporter.Core.Utils;
|
||||||
|
|
||||||
@@ -31,29 +32,45 @@ public static class Http
|
|||||||
&& IsRetryableStatusCode(hrex.StatusCode ?? HttpStatusCode.OK)
|
&& IsRetryableStatusCode(hrex.StatusCode ?? HttpStatusCode.OK)
|
||||||
);
|
);
|
||||||
|
|
||||||
public static IAsyncPolicy ResiliencePolicy { get; } =
|
public static ResiliencePipeline ResiliencePipeline { get; } =
|
||||||
Policy
|
new ResiliencePipelineBuilder()
|
||||||
.Handle<Exception>(IsRetryableException)
|
.AddRetry(
|
||||||
.WaitAndRetryAsync(4, i => TimeSpan.FromSeconds(Math.Pow(2, i) + 1));
|
new RetryStrategyOptions
|
||||||
|
|
||||||
public static IAsyncPolicy<HttpResponseMessage> ResponseResiliencePolicy { get; } =
|
|
||||||
Policy
|
|
||||||
.Handle<Exception>(IsRetryableException)
|
|
||||||
.OrResult<HttpResponseMessage>(m => IsRetryableStatusCode(m.StatusCode))
|
|
||||||
.WaitAndRetryAsync(
|
|
||||||
8,
|
|
||||||
(i, result, _) =>
|
|
||||||
{
|
{
|
||||||
// If rate-limited, use retry-after header as the guide.
|
ShouldHandle = new PredicateBuilder().Handle<Exception>(IsRetryableException),
|
||||||
// The response can be null here if an exception was thrown.
|
MaxRetryAttempts = 4,
|
||||||
if (result.Result?.Headers.RetryAfter?.Delta is { } retryAfter)
|
BackoffType = DelayBackoffType.Exponential,
|
||||||
{
|
Delay = TimeSpan.FromSeconds(1)
|
||||||
// Add some buffer just in case
|
}
|
||||||
return retryAfter + TimeSpan.FromSeconds(1);
|
)
|
||||||
}
|
.Build();
|
||||||
|
|
||||||
return TimeSpan.FromSeconds(Math.Pow(2, i) + 1);
|
public static ResiliencePipeline<HttpResponseMessage> ResponseResiliencePipeline { get; } =
|
||||||
},
|
new ResiliencePipelineBuilder<HttpResponseMessage>()
|
||||||
(_, _, _, _) => Task.CompletedTask
|
.AddRetry(
|
||||||
);
|
new RetryStrategyOptions<HttpResponseMessage>
|
||||||
|
{
|
||||||
|
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
|
||||||
|
.Handle<Exception>(IsRetryableException)
|
||||||
|
.HandleResult(m => IsRetryableStatusCode(m.StatusCode)),
|
||||||
|
MaxRetryAttempts = 8,
|
||||||
|
DelayGenerator = args =>
|
||||||
|
{
|
||||||
|
// If rate-limited, use retry-after header as the guide.
|
||||||
|
// The response can be null here if an exception was thrown.
|
||||||
|
if (args.Outcome.Result?.Headers.RetryAfter?.Delta is { } retryAfter)
|
||||||
|
{
|
||||||
|
// Add some buffer just in case
|
||||||
|
return ValueTask.FromResult<TimeSpan?>(
|
||||||
|
retryAfter + TimeSpan.FromSeconds(1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ValueTask.FromResult<TimeSpan?>(
|
||||||
|
TimeSpan.FromSeconds(Math.Pow(2, args.AttemptNumber) + 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.Build();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user