Compare commits

..

40 Commits

Author SHA1 Message Date
3051fa69a8 fix bucket clear check 2024-05-18 17:01:10 -04:00
d4776b578a update mirror list info 2024-05-18 15:18:00 -04:00
84dd270f25 update example settings json 2024-05-15 11:09:43 -04:00
a2e8c67dbd Merge pull request 'impl/r2' (#10) from impl/r2 into main
Reviewed-on: #10
2024-05-04 14:49:53 +00:00
d9d8f49808 add check to mirror upload 2024-05-04 10:48:45 -04:00
67203037a1 do the thing 2024-05-02 19:50:39 -04:00
ec8ddeb513 r2-rework WIP 2024-04-26 21:17:08 -04:00
9f0369bbd6 add some randomness to tag
to allow multiple patches with the same source to be created
2024-03-29 11:12:31 -04:00
66580eed52 Merge pull request 'preserve root folder' (#9) from fix/compression-setup into main
Reviewed-on: #9
2024-03-28 22:15:50 +00:00
3890f633e3 preserve root folder 2024-03-28 18:14:21 -04:00
5acb29df2a Merge pull request 'add compression task to handle creating 7z file' (#8) from impl/7z-compression into main
Reviewed-on: #8
2024-03-28 20:35:39 +00:00
f478552a33 do the thing for realz this time 2024-03-28 16:30:12 -04:00
8d62adb25e add compression task to handle creating 7z file 2024-03-28 13:01:02 -04:00
1b0acdc262 bumb version 2024-03-27 14:54:52 -04:00
2060c5b3b4 Merge pull request 'move sftp before mega' (#7) from reorder-mirrors-list into main
Reviewed-on: #7
2024-03-27 18:53:43 +00:00
e226667746 move sftp before mega
mega is the most annoying mirror for people, it should be a last resort
2024-03-27 14:52:46 -04:00
3e442296f6 Update 'README.md' 2024-03-24 16:41:36 +00:00
549d41df4a Merge pull request 'update patcher extension to 7z' (#6) from change-patcher-extension into main
Reviewed-on: #6
2024-03-22 18:45:22 +00:00
a70f94dfd0 update patcher extension to 7z 2024-03-22 09:02:02 -04:00
b232425df1 fix mirror link 2024-03-21 21:15:33 -04:00
478aaa4daf Merge pull request 'add-sftp-mirror' (#5) from add-sftp-mirror into main
Reviewed-on: #5
2024-03-22 00:50:05 +00:00
21d96d113a fix validation and upload, finish stuff 2024-03-21 20:48:49 -04:00
4a10964f7f add sftp info to settings 2024-03-20 12:01:19 -04:00
ae424eaa5f setup some sftp upload stuff 2024-03-19 21:05:57 -04:00
c8d1427472 Merge pull request 'net8' (#4) from net8 into main
Reviewed-on: #4
2024-03-15 02:04:38 +00:00
7473bfd5fe update gofile 2024-03-14 22:00:07 -04:00
980d8720ee update to net 8 2024-03-07 16:02:19 -05:00
38e46d6d1f continue uploads if one fails 2024-02-15 08:48:49 -05:00
eb37ba65ba add check for patched target
not perfect, but it should find if a patcher exe exists in the patch target so the folder can be added for removal
2023-10-30 18:53:11 -04:00
6ba231287b remove transfer speed column, it sucks 2023-08-13 14:12:49 -04:00
a2800a6760 version bump 2023-08-12 16:33:25 -04:00
875072fc15 add transfer rate and size to upload tasks 2023-08-12 16:30:28 -04:00
95b6e5147d add latest release version check 2023-08-12 10:51:45 -04:00
d64595e2f9 add cleanup task 2023-08-11 22:17:09 -04:00
abb18028f9 Merge pull request 'update gofile upload to use a folder id' (#3) from feature/gofile-specify-folder into main
Reviewed-on: #3
2023-07-21 01:56:45 +00:00
2d21636fe7 fix missing param and setting 2023-07-20 19:35:27 -04:00
47f84f5cd7 update gofile upload to use a folder id 2023-07-20 13:50:50 -04:00
d51dd9b469 version bump.. again .. 2023-05-15 18:14:01 -04:00
86365114c8 version bump 2023-05-15 18:11:36 -04:00
648c2d730c updated GoFileSharp lib 2023-05-15 18:10:24 -04:00
35 changed files with 943 additions and 211 deletions

View File

@ -2,30 +2,30 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AssemblyVersion>1.3.4</AssemblyVersion> <AssemblyVersion>1.6.1</AssemblyVersion>
<FileVersion>1.3.4</FileVersion> <FileVersion>1.6.1</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.307.24" />
<PackageReference Include="FubarCoder.RestSharp.Portable.Core" Version="4.0.8" /> <PackageReference Include="FubarCoder.RestSharp.Portable.Core" Version="4.0.8" />
<PackageReference Include="FubarCoder.RestSharp.Portable.HttpClient" Version="4.0.8" /> <PackageReference Include="FubarCoder.RestSharp.Portable.HttpClient" Version="4.0.8" />
<PackageReference Include="GoFileSharp" Version="1.0.2" />
<PackageReference Include="MegaApiClient" Version="1.10.2" /> <PackageReference Include="MegaApiClient" Version="1.10.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Spectre.Console" Version="0.44.0" /> <PackageReference Include="Spectre.Console" Version="0.44.0" />
<PackageReference Include="Squid-Box.SevenZipSharp" Version="1.6.1.23" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Gitea"> <Reference Include="WinSCPnet">
<HintPath>Resources\Gitea.dll</HintPath> <HintPath>Resources\WinSCPnet.dll</HintPath>
</Reference>
<Reference Include="GoFileSharp">
<HintPath>Resources\GoFileSharp.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
@ -35,4 +35,9 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Remove="Resources\7z.dll" />
<EmbeddedResource Include="Resources\7z.dll" />
</ItemGroup>
</Project> </Project>

View File

@ -75,14 +75,20 @@ namespace EftPatchHelper.Helpers
} }
} }
public EftClient GetClientSelection(string Prompt) public EftClient GetClientSelection(string Prompt, string currentReleaseVersion = "")
{ {
SelectionPrompt<EftClient> clientPrompt = new SelectionPrompt<EftClient>() SelectionPrompt<EftClient> clientPrompt = new SelectionPrompt<EftClient>()
{ {
Title = Prompt, Title = Prompt,
MoreChoicesText = "Move cursor to see more versions", MoreChoicesText = "Move cursor to see more versions",
PageSize = 10, PageSize = 10,
Converter = (x) => x.DisplayName Converter = (x) =>
{
if (!string.IsNullOrWhiteSpace(currentReleaseVersion) && x.Version.EndsWith(currentReleaseVersion))
return $"{x.DisplayName} - Latest Release";
return x.DisplayName;
}
}; };
clientPrompt.AddChoices(_clientList); clientPrompt.AddChoices(_clientList);

View File

@ -0,0 +1,44 @@
using System.Reflection;
using Spectre.Console;
namespace EftPatchHelper.Helpers;
public class FileHelper
{
public bool StreamAssemblyResourceOut(string resourceName, string outputFilePath)
{
try
{
var assembly = Assembly.GetExecutingAssembly();
FileInfo outputFile = new FileInfo(outputFilePath);
if (outputFile.Exists)
{
outputFile.Delete();
}
if (!outputFile.Directory.Exists)
{
Directory.CreateDirectory(outputFile.Directory.FullName);
}
var resName = assembly.GetManifestResourceNames().First(x => x.EndsWith(resourceName));
using (FileStream fs = File.Create(outputFilePath))
using (Stream s = assembly.GetManifestResourceStream(resName))
{
s.CopyTo(fs);
}
outputFile.Refresh();
return outputFile.Exists;
}
catch (Exception ex)
{
AnsiConsole.WriteException(ex);
return false;
}
}
}

View File

@ -0,0 +1,122 @@
using System.Net;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using EftPatchHelper.Model;
using Spectre.Console;
namespace EftPatchHelper.Helpers;
public class R2Helper
{
private readonly AmazonS3Client? _client;
public string ConnectedDomain { get; private set; }
public string BucketName { get; private set; }
public R2Helper(Settings settings, Options options)
{
ConnectedDomain = settings.R2ConnectedDomainUrl;
BucketName = settings.R2BucketName;
if (settings.UsingR2())
{
var creds = new BasicAWSCredentials(settings.R2AccessKeyId, settings.R2SecretKeyId);
_client = new AmazonS3Client(creds, new AmazonS3Config
{
ServiceURL = settings.R2ServiceUrl,
});
}
}
/// <summary>
/// Deletes all content in the bucket
/// </summary>
/// <returns>True if all contents of the bucket were deleted, otherwise false</returns>
public async Task<bool> ClearBucketAsync()
{
if (_client == null)
{
AnsiConsole.MarkupLine("[red]Client is unavailable[/]");
return false;
}
AnsiConsole.MarkupLine($"[blue]Getting bucket contents: {BucketName}[/]");
var listBucketResponse = await _client.ListObjectsAsync(BucketName);
if (listBucketResponse.HttpStatusCode != HttpStatusCode.OK)
{
AnsiConsole.MarkupLine("[red]failed to get bucket contents[/]");
return false;
}
if (listBucketResponse.S3Objects.Count == 0)
{
AnsiConsole.MarkupLine("[green]bucket is empty[/]");
return true;
}
AnsiConsole.MarkupLine("[blue]Removing old content[/]");
foreach (var s3Object in listBucketResponse.S3Objects)
{
var deleteResponse = await _client.DeleteObjectAsync(BucketName, s3Object.Key);
if ((int)deleteResponse.HttpStatusCode < 200 || (int)deleteResponse.HttpStatusCode > 299)
{
AnsiConsole.MarkupLine($"[red]failed to delete {BucketName}::{s3Object.Key}[/]");
return false;
}
AnsiConsole.MarkupLine($"[green]{BucketName}::{s3Object.Key} removed[/]");
}
return true;
}
/// <summary>
/// Upload a file into the bucket
/// </summary>
/// <param name="file">The file to upload</param>
/// <param name="progress">A progress object to track upload progress</param>
/// <returns>True if the file was uploaded successfully, otherwise false</returns>
public async Task<bool> UploadToBucketAsync(FileInfo file, IProgress<double>? progress = null)
{
if (_client == null)
{
AnsiConsole.MarkupLine("[red]Client is unavailable[/]");
return false;
}
file.Refresh();
if (!file.Exists)
{
AnsiConsole.MarkupLine($"[red]File '{file.Name}' does not exist[/]");
return false;
}
var request = new PutObjectRequest
{
BucketName = BucketName,
FilePath = file.FullName,
DisablePayloadSigning = true,
};
if (progress != null)
{
request.StreamTransferProgress = (sender, progressArgs) =>
{
progress.Report(progressArgs.PercentDone);
};
}
var uploadResponse = await _client.PutObjectAsync(request);
if (uploadResponse.HttpStatusCode != HttpStatusCode.OK)
{
AnsiConsole.MarkupLine("[red]failed to upload file[/]");
return false;
}
return true;
}
}

View File

@ -0,0 +1,46 @@
using SevenZip;
using Spectre.Console;
namespace EftPatchHelper.Helpers;
public class ZipHelper
{
public string DllPath = Path.Join(Environment.CurrentDirectory, "7z.dll");
public bool Compress(DirectoryInfo folder, FileInfo outputArchive,
IProgress<double> progress)
{
try
{
using var outputStream = outputArchive.OpenWrite();
SevenZipBase.SetLibraryPath(DllPath);
var compressor = new SevenZipCompressor()
{
CompressionLevel = CompressionLevel.Normal,
CompressionMethod = CompressionMethod.Lzma2,
ArchiveFormat = OutArchiveFormat.SevenZip,
PreserveDirectoryRoot = true
};
compressor.Compressing += (_, args) => { progress.Report(args.PercentDone); };
compressor.CompressDirectory(folder.FullName, outputStream);
outputArchive.Refresh();
if (!outputArchive.Exists)
{
AnsiConsole.MarkupLine("output archive not found");
return false;
}
return true;
}
catch (Exception ex)
{
AnsiConsole.WriteException(ex);
return false;
}
}
}

View File

@ -0,0 +1,6 @@
namespace EftPatchHelper.Interfaces
{
public interface ICleanupTask : ITaskable
{
}
}

View File

@ -0,0 +1,5 @@
namespace EftPatchHelper.Interfaces;
public interface ICompressPatcherTasks : ITaskable
{
}

View File

@ -12,6 +12,7 @@ namespace EftPatchHelper.Interfaces
public string ServiceName { get; set; } public string ServiceName { get; set; }
public string HubEntryText { get; set; } public string HubEntryText { get; set; }
public FileInfo UploadFileInfo { get; } public FileInfo UploadFileInfo { get; }
public bool AddHubEntry { get; }
public string GetLink(); public string GetLink();
public Task<bool> UploadAsync(IProgress<double>? progress = null); public Task<bool> UploadAsync(IProgress<double>? progress = null);
} }

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace EftPatchHelper.Interfaces namespace EftPatchHelper.Interfaces
{ {
public interface IReleaseCreator : ITaskable public interface IMirrorUploader : ITaskable
{ {
} }
} }

View File

@ -1,7 +1,11 @@
namespace EftPatchHelper.Model using System.Text.Json.Serialization;
namespace EftPatchHelper.Model
{ {
public class DownloadMirror public class DownloadMirror
{ {
[JsonIgnore]
public bool AddHubEntry { get; set; }
public string Link { get; set; } public string Link { get; set; }
public string Hash { get; set; } public string Hash { get; set; }
} }

View File

@ -1,38 +1,65 @@
using EftPatchHelper.Interfaces; using EftPatchHelper.Interfaces;
using GoFileSharp; using GoFileSharp;
using GoFileSharp.Model;
using GoFileSharp.Model.GoFileData;
using GoFileSharp.Model.GoFileData.Wrappers; using GoFileSharp.Model.GoFileData.Wrappers;
namespace EftPatchHelper.Model namespace EftPatchHelper.Model
{ {
public class GoFileUpload : IFileUpload public class GoFileUpload : IFileUpload
{ {
private GoFile _goFile;
public FileInfo UploadFileInfo { get; private set; } public FileInfo UploadFileInfo { get; private set; }
private DirectLink? _directLink = null;
private GoFileFile _uploadedFile; private GoFileFile _uploadedFile;
private string _folderId;
public string DisplayName { get; set; } public string DisplayName { get; set; }
public string ServiceName { get; set; } public string ServiceName { get; set; }
public string HubEntryText { get; set; } public string HubEntryText { get; set; }
public bool AddHubEntry { get; }
public GoFileUpload(FileInfo file, string apiToken) public GoFileUpload(FileInfo file, string apiToken, string folderId)
{ {
GoFile.ApiToken = apiToken; _goFile = new GoFile(new GoFileOptions
{
ApiToken = apiToken
});
_folderId = folderId;
UploadFileInfo = file; UploadFileInfo = file;
ServiceName = "GoFile"; ServiceName = "GoFile";
DisplayName = $"{ServiceName} Upload: {UploadFileInfo.Name}"; DisplayName = $"{ServiceName} Upload";
HubEntryText = $"Download from {ServiceName}"; HubEntryText = $"Download from {ServiceName}";
AddHubEntry = true;
} }
public string GetLink() public string GetLink()
{ {
return _uploadedFile.DirectLink; return _directLink?.Link ?? "";
} }
public async Task<bool> UploadAsync(IProgress<double>? progress = null) public async Task<bool> UploadAsync(IProgress<double>? progress = null)
{ {
var uploadedFile = await GoFile.UploadFileAsync(UploadFileInfo, progress); var folder = await _goFile.GetFolderAsync(_folderId);
if (folder == null)
{
return false;
}
var uploadedFile = await folder.UploadIntoAsync(UploadFileInfo, progress);
if(uploadedFile == null) return false; if(uploadedFile == null) return false;
_directLink = await uploadedFile.AddDirectLink();
if(_directLink == null)
{
return false;
}
_uploadedFile = uploadedFile; _uploadedFile = uploadedFile;
return true; return true;

View File

@ -17,6 +17,8 @@ namespace EftPatchHelper.Model
public string DisplayName { get; set; } public string DisplayName { get; set; }
public string ServiceName { get; set; } public string ServiceName { get; set; }
public string HubEntryText { get; set; } public string HubEntryText { get; set; }
public bool AddHubEntry { get; }
public MegaUpload(FileInfo file, string email, string password, string mfaKey = null) public MegaUpload(FileInfo file, string email, string password, string mfaKey = null)
{ {
@ -25,8 +27,9 @@ namespace EftPatchHelper.Model
_email = email; _email = email;
_password = password; _password = password;
ServiceName = "Mega"; ServiceName = "Mega";
DisplayName = $"{ServiceName} Upload: {UploadFileInfo.Name}"; DisplayName = $"{ServiceName} Upload";
HubEntryText = $"Download from {ServiceName}"; HubEntryText = $"Download from {ServiceName}";
AddHubEntry = true;
} }
private async Task<bool> CheckLoginStatus() private async Task<bool> CheckLoginStatus()

View File

@ -24,21 +24,26 @@ namespace EftPatchHelper.Model
/// </summary> /// </summary>
public string OutputPatchPath = null; public string OutputPatchPath = null;
/// <summary>
/// Whether or not the user opted to create a release on gitea
/// </summary>
public bool CreateRelease = false;
/// <summary> /// <summary>
/// Whether or not to upload the patcher to gofile.io /// Whether or not to upload the patcher to gofile.io
/// </summary> /// </summary>
public bool UploadToGoFile = false; public bool UploadToGoFile = false;
/// <summary> /// <summary>
/// Whether or not to upload the pather to mega.io /// Whether or not to upload the patcher to mega.io
/// </summary> /// </summary>
public bool UploadToMega = false; public bool UploadToMega = false;
/// <summary>
/// Whether or not to upload the patcher and mirror list to r2
/// </summary>
public bool UplaodToR2 = false;
/// <summary>
/// Whether or not to upload to all sftp site listing
/// </summary>
public bool UploadToSftpSites = false;
/// <summary> /// <summary>
/// List of mirrors to upload to Gitea /// List of mirrors to upload to Gitea
/// </summary> /// </summary>

View File

@ -0,0 +1,8 @@
namespace EftPatchHelper.Model;
public class PatchInfo
{
public int SourceClientVersion { get; set; }
public int TargetClientVersion { get; set; }
public List<DownloadMirror> Mirrors { get; set; }
}

View File

@ -0,0 +1,35 @@
using EftPatchHelper.Helpers;
using EftPatchHelper.Interfaces;
namespace EftPatchHelper.Model;
public class R2Upload : IFileUpload
{
public string DisplayName { get; set; }
public string ServiceName { get; set; }
public string HubEntryText { get; set; }
public FileInfo UploadFileInfo { get; }
public bool AddHubEntry { get; }
private readonly R2Helper _r2;
public R2Upload(FileInfo file, R2Helper r2)
{
_r2 = r2;
UploadFileInfo = file;
ServiceName = $"R2::{_r2.BucketName} Upload";
DisplayName = $"{ServiceName} Upload";
HubEntryText = $"Download from {ServiceName}";
AddHubEntry = false;
}
public string GetLink()
{
return $"{_r2.ConnectedDomain}/{UploadFileInfo.Name}";
}
public async Task<bool> UploadAsync(IProgress<double>? progress = null)
{
return await _r2.UploadToBucketAsync(UploadFileInfo, progress);
}
}

View File

@ -0,0 +1,8 @@
namespace EftPatchHelper.Model;
public class ReleaseInfo
{
public string AkiVersion { get; set; }
public string ClientVersion { get; set; }
public List<ReleaseInfoMirror> Mirrors { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace EftPatchHelper.Model;
public class ReleaseInfoMirror
{
public string DownloadUrl { get; set; }
public string Hash { get; set; }
}

View File

@ -30,17 +30,8 @@ namespace EftPatchHelper.Model
[JsonPropertyName("patcherExePath")] [JsonPropertyName("patcherExePath")]
public string PatcherEXEPath { get; set; } = ""; public string PatcherEXEPath { get; set; } = "";
[JsonPropertyName("giteaApiBasePath")] [JsonPropertyName("latestReleaseUrl")]
public string GiteaApiBasePath { get; set; } = ""; public string LatestReleaseUrl { get; set; } = "";
[JsonPropertyName("giteaApiKey")]
public string GiteaApiKey { get; set; } = "";
[JsonPropertyName("giteaReleaseRepoOwner")]
public string GiteaReleaseRepoOwner { get; set; } = "";
[JsonPropertyName("giteaReleaseRepoName")]
public string GiteaReleaseRepoName { get; set; } = "";
[JsonPropertyName("megaEmail")] [JsonPropertyName("megaEmail")]
public string MegaEmail { get; set; } = ""; public string MegaEmail { get; set; } = "";
@ -54,6 +45,27 @@ namespace EftPatchHelper.Model
[JsonPropertyName("goFileApiKey")] [JsonPropertyName("goFileApiKey")]
public string GoFileApiKey { get; set; } = ""; public string GoFileApiKey { get; set; } = "";
[JsonPropertyName("goFileFolderId")]
public string GoFileFolderId { get; set; } = "";
[JsonPropertyName("r2ConnectedDomainUrl")]
public string R2ConnectedDomainUrl { get; set; } = "";
[JsonPropertyName("r2ServiceUrl")]
public string R2ServiceUrl { get; set; } = "";
[JsonPropertyName("r2BucketName")]
public string R2BucketName { get; set; } = "";
[JsonPropertyName("r2AccessKeyId")]
public string R2AccessKeyId { get; set; } = "";
[JsonPropertyName("r2SecretKeyId")]
public string R2SecretKeyId { get; set; } = "";
[JsonPropertyName("sftpUploads")]
public List<SftpUploadInfo> SftpUploads { get; set; } = new();
public bool Save() public bool Save()
{ {
try try
@ -77,19 +89,6 @@ namespace EftPatchHelper.Model
} }
} }
public bool UsingGitea()
{
if (string.IsNullOrWhiteSpace(GiteaApiBasePath)) return false;
if (string.IsNullOrWhiteSpace(GiteaReleaseRepoOwner)) return false;
if (string.IsNullOrWhiteSpace(GiteaReleaseRepoName)) return false;
if (string.IsNullOrWhiteSpace(GiteaApiKey)) return false;
return true;
}
public bool UsingMega() public bool UsingMega()
{ {
if (string.IsNullOrWhiteSpace(MegaEmail)) return false; if (string.IsNullOrWhiteSpace(MegaEmail)) return false;
@ -103,6 +102,19 @@ namespace EftPatchHelper.Model
{ {
if (string.IsNullOrWhiteSpace(GoFileApiKey)) return false; if (string.IsNullOrWhiteSpace(GoFileApiKey)) return false;
if(string.IsNullOrWhiteSpace(GoFileFolderId)) return false;
return true;
}
public bool UsingR2()
{
if (string.IsNullOrWhiteSpace(R2ConnectedDomainUrl)) return false;
if (string.IsNullOrWhiteSpace(R2ServiceUrl)) return false;
if (string.IsNullOrWhiteSpace(R2BucketName)) return false;
if (string.IsNullOrWhiteSpace(R2AccessKeyId)) return false;
if (string.IsNullOrWhiteSpace(R2SecretKeyId)) return false;
return true; return true;
} }

View File

@ -0,0 +1,79 @@
using EftPatchHelper.Interfaces;
using WinSCP;
namespace EftPatchHelper.Model;
public class SftpUpload : IFileUpload
{
private readonly SftpUploadInfo _sftpInfo;
private readonly SessionOptions _sessionOptions;
public string DisplayName { get; set; }
public string ServiceName { get; set; }
public string HubEntryText { get; set; }
public bool AddHubEntry { get; }
public FileInfo UploadFileInfo { get; }
public SftpUpload(FileInfo file, SftpUploadInfo sftpInfo)
{
UploadFileInfo = file;
_sftpInfo = sftpInfo;
_sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
UserName = _sftpInfo.Username,
Password = _sftpInfo.Password,
HostName = _sftpInfo.Hostname,
PortNumber = _sftpInfo.Port,
SshHostKeyFingerprint = _sftpInfo.HostKey
};
ServiceName = _sftpInfo.Hostname;
DisplayName = $"{ServiceName} Upload";
HubEntryText = $"Download from {ServiceName}";
AddHubEntry = sftpInfo.AllowHubEntry;
}
public string GetLink()
{
var link = _sftpInfo.HttpPath;
if (!link.EndsWith('/'))
{
link += "/";
}
return $"{link}{UploadFileInfo.Name}";
}
public Task<bool> UploadAsync(IProgress<double>? progress = null)
{
TransferOptions transferOptions = new TransferOptions
{
TransferMode = TransferMode.Binary,
};
using Session session = new Session();
if (progress != null)
{
session.FileTransferProgress += (_, args) => progress.Report(Math.Floor(args.FileProgress * 100));
}
try
{
session.Open(_sessionOptions);
session.PutFiles(UploadFileInfo.FullName, $"{_sftpInfo.UploadPath}/{UploadFileInfo.Name}", false, transferOptions).Check();
return Task.FromResult(true);
}
catch
{
// ignored
}
return Task.FromResult(false);
}
}

View File

@ -0,0 +1,56 @@
using System.Text.Json.Serialization;
namespace EftPatchHelper.Model;
public class SftpUploadInfo
{
[JsonPropertyName("username")]
public string Username { get; set; } = "";
[JsonPropertyName("password")]
public string Password { get; set; } = "";
[JsonPropertyName("hostKey")]
public string HostKey { get; set; } = "";
[JsonPropertyName("hostname")]
public string Hostname { get; set; } = "";
[JsonPropertyName("port")]
public int Port { get; set; } = 0;
[JsonPropertyName("uploadPath")]
public string UploadPath { get; set; } = "";
[JsonPropertyName("httpPath")]
public string HttpPath { get; set; } = "";
[JsonPropertyName("allowHubEntry")]
public bool AllowHubEntry { get; set; } = false;
public bool IsValid()
{
if (string.IsNullOrWhiteSpace(Username))
return false;
if (string.IsNullOrWhiteSpace(Password))
return false;
if (string.IsNullOrWhiteSpace(Hostname))
return false;
if (string.IsNullOrWhiteSpace(HostKey))
return false;
if (Port == 0)
return false;
if (string.IsNullOrWhiteSpace(UploadPath))
return false;
if (string.IsNullOrWhiteSpace(HttpPath))
return false;
return true;
}
}

View File

@ -1,4 +1,6 @@
// See https://aka.ms/new-console-template for more information // See https://aka.ms/new-console-template for more information
using System.Reflection;
using EftPatchHelper.Helpers; using EftPatchHelper.Helpers;
using EftPatchHelper.Interfaces; using EftPatchHelper.Interfaces;
using EftPatchHelper.Model; using EftPatchHelper.Model;
@ -14,20 +16,31 @@ namespace EftPatchHelper
{ {
ITaskable _settingsTasks; ITaskable _settingsTasks;
ITaskable _clientSelectionTasks; ITaskable _clientSelectionTasks;
ITaskable _cleanupTasks;
ITaskable _fileProcessingTasks; ITaskable _fileProcessingTasks;
ITaskable _patchGenTasks; ITaskable _patchGenTasks;
ITaskable _patchTestingTasks; ITaskable _patchTestingTasks;
ITaskable _createReleaseTasks; ITaskable _compressPatcherTasks;
ITaskable _uploadTasks; ITaskable _uploadTasks;
ITaskable _uploadMirrorList;
public static void Main(string[] args) public static void Main(string[] args)
{ {
Console.OutputEncoding = System.Text.Encoding.UTF8; Console.OutputEncoding = System.Text.Encoding.UTF8;
AnsiConsole.Write(new FigletText("EFT Patch Helper").Centered().Color(Color.Blue)); AnsiConsole.Write(new FigletText("EFT Patch Helper").Centered().Color(Color.Blue));
var version = Assembly.GetExecutingAssembly().GetName().Version;
AnsiConsole.Write(new Rule($"[purple]v{version}[/]").Centered().RuleStyle("blue"));
var host = ConfigureHost(args); try
host.Services.GetRequiredService<Program>().Run(); {
var host = ConfigureHost(args);
host.Services.GetRequiredService<Program>().Run();
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red]{ex.Message.EscapeMarkup()}[/]");
}
AnsiConsole.MarkupLine("Press [blue]Enter[/] to close ..."); AnsiConsole.MarkupLine("Press [blue]Enter[/] to close ...");
Console.ReadLine(); Console.ReadLine();
@ -36,19 +49,23 @@ namespace EftPatchHelper
public Program( public Program(
ISettingsTask settingsTasks, ISettingsTask settingsTasks,
IClientSelectionTask clientSelectionTasks, IClientSelectionTask clientSelectionTasks,
ICleanupTask cleanupTasks,
IFileProcessingTasks fileProcessingTasks, IFileProcessingTasks fileProcessingTasks,
IPatchGenTasks patchGenTasks, IPatchGenTasks patchGenTasks,
IPatchTestingTasks patchTestingTasks, IPatchTestingTasks patchTestingTasks,
ICompressPatcherTasks compressPatcherTasks,
IUploadTasks uploadTasks, IUploadTasks uploadTasks,
IReleaseCreator createReleaseTasks IMirrorUploader uploadMirrorList
) )
{ {
_settingsTasks = settingsTasks; _settingsTasks = settingsTasks;
_clientSelectionTasks = clientSelectionTasks; _clientSelectionTasks = clientSelectionTasks;
_cleanupTasks = cleanupTasks;
_fileProcessingTasks = fileProcessingTasks; _fileProcessingTasks = fileProcessingTasks;
_patchGenTasks = patchGenTasks; _patchGenTasks = patchGenTasks;
_patchTestingTasks = patchTestingTasks; _patchTestingTasks = patchTestingTasks;
_createReleaseTasks = createReleaseTasks; _compressPatcherTasks = compressPatcherTasks;
_uploadMirrorList = uploadMirrorList;
_uploadTasks = uploadTasks; _uploadTasks = uploadTasks;
} }
@ -56,18 +73,23 @@ namespace EftPatchHelper
{ {
_settingsTasks.Run(); _settingsTasks.Run();
_clientSelectionTasks.Run(); _clientSelectionTasks.Run();
_cleanupTasks.Run();
_fileProcessingTasks.Run(); _fileProcessingTasks.Run();
_patchGenTasks.Run(); _patchGenTasks.Run();
_patchTestingTasks.Run(); _patchTestingTasks.Run();
_compressPatcherTasks.Run();
_uploadTasks.Run(); _uploadTasks.Run();
_createReleaseTasks.Run(); _uploadMirrorList.Run();
} }
private static IHost ConfigureHost(string[] args) private static IHost ConfigureHost(string[] args)
{ {
return Host.CreateDefaultBuilder(args).ConfigureServices((_, services) => return Host.CreateDefaultBuilder(args).ConfigureServices((_, services) =>
{ {
HttpClient client = new HttpClient() { Timeout = TimeSpan.FromHours(1) };
services.AddSingleton<Options>(); services.AddSingleton<Options>();
services.AddSingleton(client);
services.AddSingleton<Settings>(serviceProvider => services.AddSingleton<Settings>(serviceProvider =>
{ {
var configuration = serviceProvider.GetRequiredService<IConfiguration>(); var configuration = serviceProvider.GetRequiredService<IConfiguration>();
@ -76,15 +98,21 @@ namespace EftPatchHelper
return settings; return settings;
}); });
services.AddSingleton<FileHelper>();
services.AddSingleton<ZipHelper>();
services.AddSingleton<R2Helper>();
services.AddScoped<EftClientSelector>(); services.AddScoped<EftClientSelector>();
services.AddTransient<ISettingsTask, StartupSettingsTask>(); services.AddTransient<ISettingsTask, StartupSettingsTask>();
services.AddTransient<ICleanupTask, CleanupTask>();
services.AddTransient<IClientSelectionTask, ClientSelectionTask>(); services.AddTransient<IClientSelectionTask, ClientSelectionTask>();
services.AddTransient<IFileProcessingTasks, FileProcessingTasks>(); services.AddTransient<IFileProcessingTasks, FileProcessingTasks>();
services.AddTransient<IPatchGenTasks, PatchGenTasks>(); services.AddTransient<IPatchGenTasks, PatchGenTasks>();
services.AddTransient<IPatchTestingTasks, PatchTestingTasks>(); services.AddTransient<IPatchTestingTasks, PatchTestingTasks>();
services.AddTransient<IReleaseCreator, CreateReleaseTasks>(); services.AddTransient<ICompressPatcherTasks, CompressPatcherTasks>();
services.AddTransient<IUploadTasks, UploadTasks>(); services.AddTransient<IUploadTasks, UploadTasks>();
services.AddTransient<IMirrorUploader, UploadMirrorListTasks>();
services.AddTransient<Program>(); services.AddTransient<Program>();
}) })
.ConfigureAppConfiguration((_, config) => .ConfigureAppConfiguration((_, config) =>

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,103 @@
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Spectre.Console;
namespace EftPatchHelper.Tasks
{
public class CleanupTask : ICleanupTask
{
private Settings _settings;
private Options _options;
List<FileSystemInfo> _fileToRemove = new List<FileSystemInfo>();
public CleanupTask(Settings settings, Options options)
{
_settings = settings;
_options = options;
}
private void GetPathsToRemove()
{
var prepFolders = Directory.GetDirectories(_settings.PrepFolderPath, "*");
foreach (var prepFolder in prepFolders)
{
if (prepFolder == _options.TargetClient.PrepPath && !File.Exists(Path.Join(prepFolder, "Patcher.exe")))
continue;
_fileToRemove.Add(new DirectoryInfo(prepFolder));
}
var mirrorsPath = Path.Join(Environment.CurrentDirectory, "mirrors.json");
var hubentries = Directory.GetFiles(Environment.CurrentDirectory, "hubEntry_*.txt");
if (File.Exists(mirrorsPath))
_fileToRemove.Add(new FileInfo(mirrorsPath));
_fileToRemove.AddRange(hubentries.Select(x => new FileInfo(x)));
var patcherDir = new FileInfo(_settings.PatcherEXEPath).Directory;
_fileToRemove.AddRange(patcherDir.GetFiles().Where(x => x.FullName != _settings.PatcherEXEPath));
_fileToRemove.AddRange(patcherDir.GetDirectories("*", SearchOption.TopDirectoryOnly));
}
private void RemoveData(Table table)
{
AnsiConsole.Live(table).Start(ctx =>
{
for (int i = 0; i < _fileToRemove.Count; i++)
{
table.UpdateCell(i, 0, "[white]Removing ...[/]");
ctx.Refresh();
var item = _fileToRemove[i];
if (item is DirectoryInfo dir)
dir.Delete(true);
if (item is FileInfo file)
file.Delete();
table.UpdateCell(i, 0, item.Exists ? "[red]Exists[/]" : "[green]Removed[/]");
ctx.Refresh();
}
});
}
public void Run()
{
GetPathsToRemove();
if (_fileToRemove.Count == 0)
return;
Table removableFilesTable = new Table()
.Alignment(Justify.Center)
.AddColumn("Status")
.AddColumn("File Name")
.AddColumn("Full Path")
.BorderStyle(Style.Parse("blue"));
foreach (var file in _fileToRemove)
{
removableFilesTable.AddRow("[grey]Exists[/]", file.Name, file.FullName);
}
var cursorPos = Console.GetCursorPosition();
AnsiConsole.Write(removableFilesTable);
var removeFiles = new ConfirmationPrompt("Run cleanup to remove files shown above?").Show(AnsiConsole.Console);
Console.SetCursorPosition(cursorPos.Left, cursorPos.Top);
if (removeFiles)
RemoveData(removableFilesTable);
}
}
}

View File

@ -1,4 +1,5 @@
using EftPatchHelper.Extensions; using System.Net.Http.Json;
using EftPatchHelper.Extensions;
using EftPatchHelper.Helpers; using EftPatchHelper.Helpers;
using EftPatchHelper.Interfaces; using EftPatchHelper.Interfaces;
using EftPatchHelper.Model; using EftPatchHelper.Model;
@ -11,17 +12,19 @@ namespace EftPatchHelper.Tasks
private Settings _settings; private Settings _settings;
private Options _options; private Options _options;
private EftClientSelector _clientSelector; private EftClientSelector _clientSelector;
private HttpClient _http;
public ClientSelectionTask(Settings settings, Options options, EftClientSelector clientSelector) public ClientSelectionTask(Settings settings, Options options, EftClientSelector clientSelector, HttpClient client)
{ {
_settings = settings; _settings = settings;
_options = options; _options = options;
_http = client;
_clientSelector = clientSelector; _clientSelector = clientSelector;
} }
private bool ChangeSettingsTargetVersion() private bool ChangeSettingsTargetVersion(string currentReleaseVersion)
{ {
_options.TargetClient = _clientSelector.GetClientSelection("Select [yellow]Target[/] Version"); _options.TargetClient = _clientSelector.GetClientSelection("Select [yellow]Target[/] Version", currentReleaseVersion);
AnsiConsole.WriteLine(); AnsiConsole.WriteLine();
ConfirmationPrompt changeVersion = new ConfirmationPrompt($"Update settings target version to use [purple]{_options.TargetClient.Version}[/]?"); ConfirmationPrompt changeVersion = new ConfirmationPrompt($"Update settings target version to use [purple]{_options.TargetClient.Version}[/]?");
@ -36,13 +39,13 @@ namespace EftPatchHelper.Tasks
return true; return true;
} }
private bool ConfirmExistingTargetVersion() private bool ConfirmExistingTargetVersion(string currentReleaseVersion)
{ {
_clientSelector.LoadClientList(); _clientSelector.LoadClientList();
_options.TargetClient = _clientSelector.GetClient(_settings.TargetEftVersion); _options.TargetClient = _clientSelector.GetClient(_settings.TargetEftVersion);
ConfirmationPrompt confirmTarget = new ConfirmationPrompt($"Use version [purple]{_settings.TargetEftVersion}[/] as target?"); ConfirmationPrompt confirmTarget = new ConfirmationPrompt($"Use version [purple]{_settings.TargetEftVersion}[/] {(_options.TargetClient.Version.EndsWith(currentReleaseVersion) ? " ([green]latest release[/])" : "")} as target?");
// If client is null or requested change, return false to ensure change settings target is called. // If client is null or requested change, return false to ensure change settings target is called.
return _options.TargetClient == null || !confirmTarget.Show(AnsiConsole.Console); return _options.TargetClient == null || !confirmTarget.Show(AnsiConsole.Console);
@ -55,11 +58,27 @@ namespace EftPatchHelper.Tasks
return _options.SourceClient != null; return _options.SourceClient != null;
} }
private string GetCurrentReleaseVersion()
{
return AnsiConsole.Status().Start("Starting...", async ctx =>
{
ctx.Spinner = Spinner.Known.Dots8;
ctx.Status = "Getting latest release ...";
var blah = await _http.GetAsync(_settings.LatestReleaseUrl);
var release = await blah.Content.ReadFromJsonAsync<ReleaseInfo>();
return release?.ClientVersion ?? "failed to get version :(";
}).GetAwaiter().GetResult();
}
public void Run() public void Run()
{ {
if (ConfirmExistingTargetVersion()) var currentReleaseVersion = GetCurrentReleaseVersion();
if (ConfirmExistingTargetVersion(currentReleaseVersion))
{ {
ChangeSettingsTargetVersion().ValidateOrExit(); ChangeSettingsTargetVersion(currentReleaseVersion).ValidateOrExit();
} }
SelectSourceVersion().ValidateOrExit(); SelectSourceVersion().ValidateOrExit();

View File

@ -0,0 +1,63 @@
using EftPatchHelper.Extensions;
using EftPatchHelper.Helpers;
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Spectre.Console;
namespace EftPatchHelper.Tasks;
public class CompressPatcherTasks : ICompressPatcherTasks
{
private Options _options;
private FileHelper _fileHelper;
private ZipHelper _zipHelper;
public CompressPatcherTasks(Options options, FileHelper fileHelper, ZipHelper zipHelper)
{
_options = options;
_fileHelper = fileHelper;
_zipHelper = zipHelper;
}
public bool CompressPatcher()
{
return AnsiConsole.Progress()
.Columns(new ProgressColumn[]
{
new TaskDescriptionColumn(),
new ProgressBarColumn(),
new PercentageColumn(),
new ElapsedTimeColumn(),
new SpinnerColumn(Spinner.Known.Dots2)
})
.Start(ctx =>
{
var compressionTask = ctx.AddTask("Compressing Patcher");
compressionTask.MaxValue = 100;
if (!_fileHelper.StreamAssemblyResourceOut("7z.dll", _zipHelper.DllPath))
{
return false;
}
var patchFolder = new DirectoryInfo(_options.OutputPatchPath);
var patchArchiveFile = new FileInfo(_options.OutputPatchPath + ".7z");
if (!patchFolder.Exists)
{
return false;
}
var progress = new Progress<double>(p => { compressionTask.Increment(p - compressionTask.Percentage);});
return _zipHelper.Compress(patchFolder, patchArchiveFile, progress);
});
}
public void Run()
{
CompressPatcher().ValidateOrExit();
}
}

View File

@ -1,119 +0,0 @@
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Gitea.Api;
using Gitea.Model;
using Gitea.Client;
using Spectre.Console;
using EftPatchHelper.Extensions;
using System.Text.Json;
namespace EftPatchHelper.Tasks
{
public class CreateReleaseTasks : IReleaseCreator
{
private Settings _settings;
private Options _options;
public CreateReleaseTasks(Settings settings, Options options)
{
_settings = settings;
_options = options;
}
private bool UploadAsset(FileInfo file, Release release, RepositoryApi repo)
{
return AnsiConsole.Status().Spinner(Spinner.Known.Point).Start("Uploading Asset", (StatusContext context) =>
{
AnsiConsole.MarkupLine($"[blue]Adding release asset: {file.Name.EscapeMarkup()}[/]");
file.Refresh();
if (!file.Exists)
{
AnsiConsole.MarkupLine($"[red]File does not exist: {file.FullName}[/]");
}
using var fileStream = file.OpenRead();
try
{
var attachment = repo.RepoCreateReleaseAttachment(_settings.GiteaReleaseRepoOwner, _settings.GiteaReleaseRepoName, release.Id, fileStream, file.Name);
AnsiConsole.MarkupLine("[green]Upload Complete[/]");
return true;
}
catch (Exception ex)
{
AnsiConsole.MarkupLine("[red]Failed to upload asset[/]");
AnsiConsole.WriteException(ex);
return false;
}
});
}
private Release? MakeRelease(RepositoryApi repo)
{
AnsiConsole.Write("Adding release to gitea ... ");
string sourceTail = _options.SourceClient.Version.Split('.').Last();
string targetTail = _options.TargetClient.Version.Split('.').Last();
string releaseName = $"{sourceTail} to {targetTail}";
try
{
var release = repo.RepoCreateRelease(_settings.GiteaReleaseRepoOwner, _settings.GiteaReleaseRepoName, new CreateReleaseOption(null, false, releaseName, false, sourceTail, null));
AnsiConsole.MarkupLine($"[green]Release added: {release.Name.EscapeMarkup()}[/]");
return release;
}
catch(Exception ex)
{
AnsiConsole.MarkupLine($"[red]Failed to create release[/]");
AnsiConsole.WriteException(ex);
return null;
}
}
public bool CreateMirrorList(FileInfo mirrorListFileInfo)
{
List<DownloadMirror> mirrors = _options.MirrorList.Values.ToList();
string json = JsonSerializer.Serialize(mirrors, new JsonSerializerOptions() { WriteIndented = true });
File.WriteAllText(mirrorListFileInfo.FullName, json);
mirrorListFileInfo.Refresh();
return mirrorListFileInfo.Exists;
}
public void Run()
{
AnsiConsole.WriteLine();
var fileInfo = new FileInfo(Path.Join(Environment.CurrentDirectory, "mirrors.json"));
CreateMirrorList(fileInfo);
if (!_options.CreateRelease) return;
Configuration.Default.BasePath = _settings.GiteaApiBasePath;
Configuration.Default.AddApiKey("token", _settings.GiteaApiKey);
var repo = new RepositoryApi(Configuration.Default);
var release = MakeRelease(repo).ValidateOrExit<Release>();
UploadAsset(fileInfo, release, repo);
}
}
}

View File

@ -44,6 +44,8 @@ namespace EftPatchHelper.Tasks
public void Run() public void Run()
{ {
AnsiConsole.Write(new Rule("Starting Tasks, this will take some time :)"));
BackupClients().ValidateOrExit(); BackupClients().ValidateOrExit();
CopyData(_options.SourceClient, "[gray]Copying[/] [blue]source[/][gray] to prep area ...[/]").ValidateOrExit(); CopyData(_options.SourceClient, "[gray]Copying[/] [blue]source[/][gray] to prep area ...[/]").ValidateOrExit();

View File

@ -48,7 +48,7 @@ namespace EftPatchHelper.Tasks
$"OutputFolderName::{patcherOutputName}", $"OutputFolderName::{patcherOutputName}",
$"SourceFolderPath::{_options.SourceClient.PrepPath}", $"SourceFolderPath::{_options.SourceClient.PrepPath}",
$"TargetFolderPath::{_options.TargetClient.PrepPath}", $"TargetFolderPath::{_options.TargetClient.PrepPath}",
$"AutoZip::{_settings.AutoZip}", $"AutoZip::false",
$"AutoClose::{_settings.AutoClose}" $"AutoClose::{_settings.AutoClose}"
} }
}); });

View File

@ -62,11 +62,6 @@ namespace EftPatchHelper.Tasks
{ {
_options.IgnoreExistingDirectories = new ConfirmationPrompt("Skip existing directories? (you will be prompted if no)").Show(AnsiConsole.Console); _options.IgnoreExistingDirectories = new ConfirmationPrompt("Skip existing directories? (you will be prompted if no)").Show(AnsiConsole.Console);
if (_settings.UsingGitea())
{
_options.CreateRelease = new ConfirmationPrompt("Create a release on gitea?").Show(AnsiConsole.Console);
}
if (_settings.UsingMega()) if (_settings.UsingMega())
{ {
_options.UploadToMega = new ConfirmationPrompt("Upload to Mega?").Show(AnsiConsole.Console); _options.UploadToMega = new ConfirmationPrompt("Upload to Mega?").Show(AnsiConsole.Console);
@ -76,6 +71,17 @@ namespace EftPatchHelper.Tasks
{ {
_options.UploadToGoFile = new ConfirmationPrompt("Upload to GoFile?").Show(AnsiConsole.Console); _options.UploadToGoFile = new ConfirmationPrompt("Upload to GoFile?").Show(AnsiConsole.Console);
} }
if (_settings.UsingR2())
{
_options.UplaodToR2 = new ConfirmationPrompt($"Upload to R2 ({_settings.R2BucketName})?").Show(AnsiConsole.Console);
}
if (_settings.SftpUploads.Count > 0)
{
_options.UploadToSftpSites =
new ConfirmationPrompt($"Upload to SFTP sites? ( {_settings.SftpUploads.Count} sites )").Show(AnsiConsole.Console);
}
} }
public void Run() public void Run()

View File

@ -0,0 +1,78 @@
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Spectre.Console;
using System.Text.Json;
using EftPatchHelper.Helpers;
namespace EftPatchHelper.Tasks
{
public class UploadMirrorListTasks : IMirrorUploader
{
private Settings _settings;
private Options _options;
private R2Helper _r2;
public UploadMirrorListTasks(Settings settigns, Options options, R2Helper r2)
{
_settings = settigns;
_options = options;
_r2 = r2;
}
private async Task<bool> UploadMirrorList(FileInfo file)
{
return await AnsiConsole.Progress().Columns(new ProgressColumn[]
{
new TaskDescriptionColumn() { Alignment = Justify.Left },
new ProgressBarColumn(),
new PercentageColumn(),
new RemainingTimeColumn(),
new SpinnerColumn(Spinner.Known.Dots2),
})
.StartAsync(async ctx =>
{
var uploadTask = ctx.AddTask("mirrors.json upload");
var progress = new Progress<double>((p) => { uploadTask.Value = p; });
return await _r2.UploadToBucketAsync(file, progress);
});
}
public bool CreateMirrorList(FileInfo mirrorListFileInfo)
{
var sourcePatchVersion = _options.SourceClient.Version.Split('.').Last();
var targetPatchVersion = _options.TargetClient.Version.Split('.').Last();
var mirrorInfo = new PatchInfo
{
SourceClientVersion = int.Parse(sourcePatchVersion),
TargetClientVersion = int.Parse(targetPatchVersion),
Mirrors = _options.MirrorList.Values.ToList()
};
string json = JsonSerializer.Serialize(mirrorInfo, new JsonSerializerOptions() { WriteIndented = true });
File.WriteAllText(mirrorListFileInfo.FullName, json);
mirrorListFileInfo.Refresh();
return mirrorListFileInfo.Exists;
}
public void Run()
{
if (!_settings.UsingR2() || !_options.UplaodToR2)
{
return;
}
AnsiConsole.WriteLine();
var fileInfo = new FileInfo(Path.Join(Environment.CurrentDirectory, "mirrors.json"));
CreateMirrorList(fileInfo);
UploadMirrorList(fileInfo).GetAwaiter().GetResult();
}
}
}

View File

@ -3,20 +3,23 @@ using EftPatchHelper.Interfaces;
using EftPatchHelper.Model; using EftPatchHelper.Model;
using Spectre.Console; using Spectre.Console;
using System.Security.Cryptography; using System.Security.Cryptography;
using EftPatchHelper.Helpers;
namespace EftPatchHelper.Tasks namespace EftPatchHelper.Tasks
{ {
public class UploadTasks : IUploadTasks public class UploadTasks : IUploadTasks
{ {
private Options _options; private readonly Options _options;
private Settings _settings; private readonly Settings _settings;
private List<IFileUpload> _fileUploads = new List<IFileUpload>(); private readonly R2Helper _r2;
private Dictionary<IFileUpload, ProgressTask> uploadTasks = new Dictionary<IFileUpload, ProgressTask>(); private readonly List<IFileUpload> _fileUploads = new();
private readonly Dictionary<IFileUpload, ProgressTask> _uploadTasks = new();
public UploadTasks(Options options, Settings settings) public UploadTasks(Options options, Settings settings, R2Helper r2)
{ {
_options = options; _options = options;
_settings = settings; _settings = settings;
_r2 = r2;
} }
private static string GetFileHash(FileInfo file) private static string GetFileHash(FileInfo file)
@ -32,14 +35,46 @@ namespace EftPatchHelper.Tasks
private async Task<bool> BuildUploadList() private async Task<bool> BuildUploadList()
{ {
var patcherFile = new FileInfo(_options.OutputPatchPath + ".zip"); var patcherFile = new FileInfo(_options.OutputPatchPath + ".7z");
if (!patcherFile.Exists) return false; if (!patcherFile.Exists)
{
return false;
}
AnsiConsole.WriteLine("Building mirrors list ...");
if(_settings.UsingGoFile() && _options.UploadToGoFile) if(_settings.UsingGoFile() && _options.UploadToGoFile)
{ {
var gofile = new GoFileUpload(patcherFile, _settings.GoFileApiKey); var gofile = new GoFileUpload(patcherFile, _settings.GoFileApiKey, _settings.GoFileFolderId);
_fileUploads.Add(gofile); _fileUploads.Add(gofile);
AnsiConsole.WriteLine("Added GoFile");
}
if (_settings.UsingR2() && _options.UplaodToR2)
{
if (!await _r2.ClearBucketAsync())
{
return false;
}
var r2 = new R2Upload(patcherFile, _r2);
_fileUploads.Add(r2);
AnsiConsole.WriteLine($"Added R2::{_r2.BucketName}");
}
if (_settings.SftpUploads.Count > 0 && _options.UploadToSftpSites)
{
foreach (var sftpInfo in _settings.SftpUploads)
{
if (!sftpInfo.IsValid())
{
continue;
}
AnsiConsole.WriteLine($"Added SFTP: {sftpInfo.Hostname}");
_fileUploads.Add(new SftpUpload(patcherFile, sftpInfo));
}
} }
if (_settings.UsingMega() && _options.UploadToMega) if (_settings.UsingMega() && _options.UploadToMega)
@ -47,6 +82,7 @@ namespace EftPatchHelper.Tasks
var mega = new MegaUpload(patcherFile, _settings.MegaEmail, _settings.MegaPassword); var mega = new MegaUpload(patcherFile, _settings.MegaEmail, _settings.MegaPassword);
await mega.SetUploadFolder(_settings.MegaUploadFolder); await mega.SetUploadFolder(_settings.MegaUploadFolder);
_fileUploads.Add(mega); _fileUploads.Add(mega);
AnsiConsole.WriteLine("Added MEGA");
} }
return true; return true;
@ -58,6 +94,11 @@ namespace EftPatchHelper.Tasks
foreach (var pair in _options.MirrorList) foreach (var pair in _options.MirrorList)
{ {
if (!pair.Value.AddHubEntry)
{
continue;
}
var displayText = pair.Key; var displayText = pair.Key;
var link = pair.Value.Link; var link = pair.Value.Link;
@ -87,12 +128,29 @@ namespace EftPatchHelper.Tasks
} }
} }
static string BytesToString(long byteCount)
{
string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
if (byteCount == 0)
{
return "0" + suf[0];
}
long bytes = Math.Abs(byteCount);
int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
double num = Math.Round(bytes / Math.Pow(1024, place), 1);
return (Math.Sign(byteCount) * num).ToString() + suf[place];
}
private async Task<bool> UploadAllFiles() private async Task<bool> UploadAllFiles()
{ {
if(!await BuildUploadList()) if(!await BuildUploadList())
{ {
return false; return false;
} }
AnsiConsole.MarkupLine($"[blue]Starting {_fileUploads[0].UploadFileInfo.Name} uploads ...[/]");
var succeeded = await AnsiConsole.Progress().Columns( var succeeded = await AnsiConsole.Progress().Columns(
new TaskDescriptionColumn() { Alignment = Justify.Left}, new TaskDescriptionColumn() { Alignment = Justify.Left},
@ -104,28 +162,28 @@ namespace EftPatchHelper.Tasks
{ {
foreach(var file in _fileUploads) foreach(var file in _fileUploads)
{ {
var task = context.AddTask($"[purple][[Pending]][/] {file.DisplayName}"); var task = context.AddTask($"[purple][[Pending]][/] {file.DisplayName} - [blue]{BytesToString(file.UploadFileInfo.Length)}[/]");
task.IsIndeterminate = true; task.IsIndeterminate = true;
uploadTasks.Add(file, task); _uploadTasks.Add(file, task);
} }
foreach(var pair in uploadTasks) foreach(var pair in _uploadTasks)
{ {
// set the value of the progress task object // set the value of the progress task object
var progress = new System.Progress<double>((d) => pair.Value.Value = d); var progress = new System.Progress<double>((d) => pair.Value.Value = d);
pair.Value.IsIndeterminate = false; pair.Value.IsIndeterminate = false;
pair.Value.Description = pair.Key.DisplayName; pair.Value.Description = $"{pair.Key.DisplayName} - [blue]{BytesToString(pair.Key.UploadFileInfo.Length)}[/]";
if(!await pair.Key.UploadAsync(progress)) if(!await pair.Key.UploadAsync(progress))
{ {
AnsiConsole.MarkupLine($"[red]{pair.Key.DisplayName.EscapeMarkup()} failed[/]"); AnsiConsole.MarkupLine($"[red]{pair.Key.DisplayName.EscapeMarkup()} failed[/]");
return false;
} }
else else
{ {
DownloadMirror mirror = new DownloadMirror() DownloadMirror mirror = new DownloadMirror
{ {
AddHubEntry = pair.Key.AddHubEntry,
Link = pair.Key.GetLink(), Link = pair.Key.GetLink(),
Hash = GetFileHash(pair.Key.UploadFileInfo) Hash = GetFileHash(pair.Key.UploadFileInfo)
}; };
@ -134,7 +192,7 @@ namespace EftPatchHelper.Tasks
} }
} }
return true; return _options.MirrorList.Count > 0;
}); });
return succeeded; return succeeded;
@ -142,7 +200,7 @@ namespace EftPatchHelper.Tasks
public void Run() public void Run()
{ {
if (!_options.UploadToGoFile && !_options.UploadToMega) return; if (!_options.UploadToGoFile && !_options.UploadToMega && !_options.UploadToSftpSites && !_options.UplaodToR2) return;
UploadAllFiles().GetAwaiter().GetResult().ValidateOrExit(); UploadAllFiles().GetAwaiter().GetResult().ValidateOrExit();

View File

@ -6,12 +6,27 @@
"patcherExePath": "", "patcherExePath": "",
"autoZip": true, "autoZip": true,
"autoClose": false, "autoClose": false,
"giteaApiBasePath": "", //You can leave the gitea settings blank if you don't need to create releases on gitea "latestReleaseUrl": "",
"giteaApiKey": "",
"giteaReleaseRepoOwner": "",
"giteaReleaseRepoName": "",
"megaEmail": "", "megaEmail": "",
"megaPassword": "", "megaPassword": "",
"megaUploadFolder": "", "megaUploadFolder": "",
"goFileApiKey": "" "goFileApiKey": "",
"goFileFolderId": "",
"r2ConnectedDomainUrl": "",
"r2ServiceUrl": "",
"r2BucketName": "",
"r2AccessKeyId": "",
"r2SecretKeyId": "",
"sftpUploads": [
{
"username": "example-remove-before-using",
"password": "password123",
"hostKey": "ssh-ed12345 SLKDJFK3928D2LDKFJ2",
"hostname": "sftp.slugma-ligma.com",
"port": 12345,
"uploadPath": "/public/patchers",
"httpPath": "https://mirror.slugma-ligma.com/patchers",
"allowHubEntry": true
}
]
} }

View File

@ -9,5 +9,5 @@ Basically, a way for me to be very lazy when making patches.
Figured I'd share :) Figured I'd share :)
## Requirements ## Requirements
- .net 6 - .net 8
- PatchGenerator.exe (https://dev.sp-tarkov.com/waffle.lord/Patcher/releases) - PatchGenerator.exe (https://dev.sp-tarkov.com/waffle.lord/Patcher/releases)