Compare commits

...

51 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
5cf41dae3c fix typo 2022-08-11 17:32:42 +00:00
fbc917bfbf bump version 2022-07-11 21:27:06 -04:00
2ebc596321 add file hash to mirror list 2022-07-11 21:22:26 -04:00
acbebe3821 version bump 2022-07-09 13:28:38 -04:00
138bc5e140 update upload tasks 2022-07-09 13:24:50 -04:00
52ef069f7b add mirror list upload tasks 2022-07-09 00:36:49 -04:00
b28850f8b5 version bump 2022-07-04 12:28:17 -04:00
4141385ea7 add upload tasks, fix options not setting target client correctly 2022-07-04 12:26:54 -04:00
a879f686ff blah 2022-06-23 18:30:17 -04:00
8af477e98b version bump 2022-06-17 18:20:06 -04:00
99f661a101 updating fancy 2022-06-17 18:18:48 -04:00
38 changed files with 1298 additions and 156 deletions

View File

@ -2,26 +2,30 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyVersion>1.3.0</AssemblyVersion>
<FileVersion>1.3.0</FileVersion>
<AssemblyVersion>1.6.1</AssemblyVersion>
<FileVersion>1.6.1</FileVersion>
</PropertyGroup>
<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.HttpClient" Version="4.0.8" />
<PackageReference Include="GoFileSharp" Version="1.0.2" />
<PackageReference Include="MegaApiClient" Version="1.10.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Spectre.Console" Version="0.44.0" />
<PackageReference Include="Squid-Box.SevenZipSharp" Version="1.6.1.23" />
</ItemGroup>
<ItemGroup>
<Reference Include="Gitea">
<HintPath>Resources\Gitea.dll</HintPath>
<Reference Include="WinSCPnet">
<HintPath>Resources\WinSCPnet.dll</HintPath>
</Reference>
</ItemGroup>
@ -31,4 +35,9 @@
</None>
</ItemGroup>
<ItemGroup>
<None Remove="Resources\7z.dll" />
<EmbeddedResource Include="Resources\7z.dll" />
</ItemGroup>
</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>()
{
Title = Prompt,
MoreChoicesText = "Move cursor to see more versions",
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);

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

@ -21,7 +21,7 @@ namespace EftPatchHelper.Helpers
public static void Clean(string FolderPath)
{
AnsiConsole.Status()
.Spinner(Spinner.Known.Default)
.Spinner(Spinner.Known.Dots2)
.Start($"Cleaning Folder ...", ctx =>
{
AnsiConsole.MarkupLine($"[blue]INFO:[/] [gray]Getting folders to remove for {FolderPath} ...[/]");

View File

@ -55,7 +55,7 @@ namespace EftPatchHelper.Helpers
new ProgressBarColumn(),
new PercentageColumn(),
new ElapsedTimeColumn(),
new SpinnerColumn()
new SpinnerColumn(Spinner.Known.Dots2)
})
.Start(ctx =>
{

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

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EftPatchHelper.Interfaces
{
public interface IFileUpload
{
public string DisplayName { get; set; }
public string ServiceName { get; set; }
public string HubEntryText { get; set; }
public FileInfo UploadFileInfo { get; }
public bool AddHubEntry { get; }
public string GetLink();
public Task<bool> UploadAsync(IProgress<double>? progress = null);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,103 @@
using EftPatchHelper.Interfaces;
using CG.Web.MegaApiClient;
using Spectre.Console;
namespace EftPatchHelper.Model
{
public class MegaUpload : IFileUpload, IDisposable
{
public FileInfo UploadFileInfo { get; private set; }
private MegaApiClient _client;
private string _email;
private string _password;
private string _mfaKey;
private INode _uploadFolder;
private INode _uploadedFile;
public string DisplayName { get; set; }
public string ServiceName { get; set; }
public string HubEntryText { get; set; }
public bool AddHubEntry { get; }
public MegaUpload(FileInfo file, string email, string password, string mfaKey = null)
{
_client = new MegaApiClient();
UploadFileInfo = file;
_email = email;
_password = password;
ServiceName = "Mega";
DisplayName = $"{ServiceName} Upload";
HubEntryText = $"Download from {ServiceName}";
AddHubEntry = true;
}
private async Task<bool> CheckLoginStatus()
{
if (!_client.IsLoggedIn)
{
AnsiConsole.Markup("[blue]Logging into mega ... [/]");
await _client.LoginAsync(_email, _password, _mfaKey);
if (!_client.IsLoggedIn)
{
AnsiConsole.MarkupLine("[red]failed[/]");
return false;
}
AnsiConsole.MarkupLine("[green]ok[/]");
}
return true;
}
public async Task<bool> SetUploadFolder(string folderName)
{
if (!await CheckLoginStatus())
{
return false;
}
AnsiConsole.Markup("[blue]Getting node ... [/]");
var nodes = await _client.GetNodesAsync();
var trashNode = nodes.SingleOrDefault(x => x.Type == NodeType.Trash);
_uploadFolder = nodes.SingleOrDefault(x => x.Name == folderName && x.ParentId != trashNode.Id);
bool nodeSet = _uploadFolder != null;
AnsiConsole.MarkupLine(nodeSet != false ? "[green]node set[/]" : "[red]failed to set node[/]");
return nodeSet;
}
public string GetLink()
{
return _client.GetDownloadLink(_uploadedFile).ToString();
}
public async Task<bool> UploadAsync(IProgress<double>? progress = null)
{
UploadFileInfo.Refresh();
if (!UploadFileInfo.Exists) return false;
if(!await CheckLoginStatus())
{
return false;
}
using var fileStream = UploadFileInfo.OpenRead();
_uploadedFile = await _client.UploadAsync(fileStream, UploadFileInfo.Name, _uploadFolder, progress);
return _uploadedFile != null;
}
public void Dispose()
{
_client.Logout();
}
}
}

View File

@ -25,8 +25,28 @@ namespace EftPatchHelper.Model
public string OutputPatchPath = null;
/// <summary>
/// Whether or not the user opted to create a release on gitea
/// Whether or not to upload the patcher to gofile.io
/// </summary>
public bool CreateRelease = false;
public bool UploadToGoFile = false;
/// <summary>
/// Whether or not to upload the patcher to mega.io
/// </summary>
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>
/// List of mirrors to upload to Gitea
/// </summary>
public Dictionary<string, DownloadMirror> MirrorList = new Dictionary<string, DownloadMirror>();
}
}

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,41 @@ namespace EftPatchHelper.Model
[JsonPropertyName("patcherExePath")]
public string PatcherEXEPath { get; set; } = "";
[JsonPropertyName("giteaApiBasePath")]
public string GiteaApiBasePath { get; set; } = "";
[JsonPropertyName("latestReleaseUrl")]
public string LatestReleaseUrl { get; set; } = "";
[JsonPropertyName("giteaApiKey")]
public string GiteaApiKey { get; set; } = "";
[JsonPropertyName("megaEmail")]
public string MegaEmail { get; set; } = "";
[JsonPropertyName("giteaReleaseRepoOwner")]
public string GiteaReleaseRepoOwner { get; set; } = "";
[JsonPropertyName("megaPassword")]
public string MegaPassword { get; set; } = "";
[JsonPropertyName("giteaReleaseRepoName")]
public string GiteaReleaseRepoName { get; set; } = "";
[JsonPropertyName("megaUploadFolder")]
public string MegaUploadFolder { get; set; } = "";
[JsonPropertyName("goFileApiKey")]
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()
{
@ -65,15 +89,31 @@ namespace EftPatchHelper.Model
}
}
public bool UsingGitea()
public bool UsingMega()
{
if (string.IsNullOrWhiteSpace(GiteaApiBasePath)) return false;
if (string.IsNullOrWhiteSpace(MegaEmail)) return false;
if (string.IsNullOrWhiteSpace(GiteaReleaseRepoOwner)) return false;
if (string.IsNullOrWhiteSpace(MegaPassword)) return false;
if (string.IsNullOrWhiteSpace(GiteaReleaseRepoName)) return false;
return true;
}
if (string.IsNullOrWhiteSpace(GiteaApiKey)) return false;
public bool UsingGoFile()
{
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;
}

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
using System.Reflection;
using EftPatchHelper.Helpers;
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
@ -14,17 +16,31 @@ namespace EftPatchHelper
{
ITaskable _settingsTasks;
ITaskable _clientSelectionTasks;
ITaskable _cleanupTasks;
ITaskable _fileProcessingTasks;
ITaskable _patchGenTasks;
ITaskable _patchTestingTasks;
ITaskable _createReleaseTasks;
ITaskable _compressPatcherTasks;
ITaskable _uploadTasks;
ITaskable _uploadMirrorList;
public static void Main(string[] args)
{
AnsiConsole.Write(new FigletText("EFT Patch Helper").Centered().Color(Color.Blue));
Console.OutputEncoding = System.Text.Encoding.UTF8;
var host = ConfigureHost(args);
host.Services.GetRequiredService<Program>().Run();
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"));
try
{
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 ...");
Console.ReadLine();
@ -33,35 +49,47 @@ namespace EftPatchHelper
public Program(
ISettingsTask settingsTasks,
IClientSelectionTask clientSelectionTasks,
ICleanupTask cleanupTasks,
IFileProcessingTasks fileProcessingTasks,
IPatchGenTasks patchGenTasks,
IPatchTestingTasks patchTestingTasks,
IReleaseCreator createReleaseTasks
ICompressPatcherTasks compressPatcherTasks,
IUploadTasks uploadTasks,
IMirrorUploader uploadMirrorList
)
{
_settingsTasks = settingsTasks;
_clientSelectionTasks = clientSelectionTasks;
_cleanupTasks = cleanupTasks;
_fileProcessingTasks = fileProcessingTasks;
_patchGenTasks = patchGenTasks;
_patchTestingTasks = patchTestingTasks;
_createReleaseTasks = createReleaseTasks;
_compressPatcherTasks = compressPatcherTasks;
_uploadMirrorList = uploadMirrorList;
_uploadTasks = uploadTasks;
}
public void Run()
{
_settingsTasks.Run();
_clientSelectionTasks.Run();
_cleanupTasks.Run();
_fileProcessingTasks.Run();
_patchGenTasks.Run();
_patchTestingTasks.Run();
_createReleaseTasks.Run();
_compressPatcherTasks.Run();
_uploadTasks.Run();
_uploadMirrorList.Run();
}
private static IHost ConfigureHost(string[] args)
{
return Host.CreateDefaultBuilder(args).ConfigureServices((_, services) =>
{
HttpClient client = new HttpClient() { Timeout = TimeSpan.FromHours(1) };
services.AddSingleton<Options>();
services.AddSingleton(client);
services.AddSingleton<Settings>(serviceProvider =>
{
var configuration = serviceProvider.GetRequiredService<IConfiguration>();
@ -70,14 +98,21 @@ namespace EftPatchHelper
return settings;
});
services.AddSingleton<FileHelper>();
services.AddSingleton<ZipHelper>();
services.AddSingleton<R2Helper>();
services.AddScoped<EftClientSelector>();
services.AddTransient<ISettingsTask, StartupSettingsTask>();
services.AddTransient<ICleanupTask, CleanupTask>();
services.AddTransient<IClientSelectionTask, ClientSelectionTask>();
services.AddTransient<IFileProcessingTasks, FileProcessingTasks>();
services.AddTransient<IPatchGenTasks, PatchGenTasks>();
services.AddTransient<IPatchTestingTasks, PatchTestingTasks>();
services.AddTransient<IReleaseCreator, CreateReleaseTasks>();
services.AddTransient<ICompressPatcherTasks, CompressPatcherTasks>();
services.AddTransient<IUploadTasks, UploadTasks>();
services.AddTransient<IMirrorUploader, UploadMirrorListTasks>();
services.AddTransient<Program>();
})
.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.Interfaces;
using EftPatchHelper.Model;
@ -11,24 +12,26 @@ namespace EftPatchHelper.Tasks
private Settings _settings;
private Options _options;
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;
_options = options;
_http = client;
_clientSelector = clientSelector;
}
private bool ChangeSettingsTargetVersion()
private bool ChangeSettingsTargetVersion(string currentReleaseVersion)
{
var targetClient = _clientSelector.GetClientSelection("Select [yellow]Target[/] Version");
_options.TargetClient = _clientSelector.GetClientSelection("Select [yellow]Target[/] Version", currentReleaseVersion);
AnsiConsole.WriteLine();
ConfirmationPrompt changeVersion = new ConfirmationPrompt($"Update settings target version to use [purple]{targetClient.Version}[/]?");
ConfirmationPrompt changeVersion = new ConfirmationPrompt($"Update settings target version to use [purple]{_options.TargetClient.Version}[/]?");
if (changeVersion.Show(AnsiConsole.Console))
{
_settings.TargetEftVersion = targetClient.Version;
_settings.TargetEftVersion = _options.TargetClient.Version;
return _settings.Save();
}
@ -36,13 +39,13 @@ namespace EftPatchHelper.Tasks
return true;
}
private bool ConfirmExistingTargetVersion()
private bool ConfirmExistingTargetVersion(string currentReleaseVersion)
{
_clientSelector.LoadClientList();
_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.
return _options.TargetClient == null || !confirmTarget.Show(AnsiConsole.Console);
@ -55,11 +58,27 @@ namespace EftPatchHelper.Tasks
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()
{
if (ConfirmExistingTargetVersion())
var currentReleaseVersion = GetCurrentReleaseVersion();
if (ConfirmExistingTargetVersion(currentReleaseVersion))
{
ChangeSettingsTargetVersion().ValidateOrExit();
ChangeSettingsTargetVersion(currentReleaseVersion).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,103 +0,0 @@
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Gitea.Api;
using Gitea.Model;
using Gitea.Client;
using Spectre.Console;
using EftPatchHelper.Extensions;
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().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 void Run()
{
AnsiConsole.WriteLine();
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>();
var fileInfo = new FileInfo(_options.OutputPatchPath + ".zip");
UploadAsset(fileInfo, release, repo);
}
}
}

View File

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

View File

@ -37,7 +37,7 @@ namespace EftPatchHelper.Tasks
_options.OutputPatchPath = Path.Join(patcherPath, patcherOutputName);
return AnsiConsole.Status().Start("Staring Patch Generator ...", (StatusContext context) =>
return AnsiConsole.Status().Spinner(Spinner.Known.Shark).Start("Staring Patch Generator ...", (StatusContext context) =>
{
var genProc = Process.Start(new ProcessStartInfo()
{
@ -48,7 +48,7 @@ namespace EftPatchHelper.Tasks
$"OutputFolderName::{patcherOutputName}",
$"SourceFolderPath::{_options.SourceClient.PrepPath}",
$"TargetFolderPath::{_options.TargetClient.PrepPath}",
$"AutoZip::{_settings.AutoZip}",
$"AutoZip::false",
$"AutoClose::{_settings.AutoClose}"
}
});

View File

@ -34,7 +34,7 @@ namespace EftPatchHelper.Tasks
patcherCopy.Start(false, true);
return AnsiConsole.Status().Start("Starting Patcher ...", (StatusContext context) =>
return AnsiConsole.Status().Spinner(Spinner.Known.Shark).Start("Starting Patcher ...", (StatusContext context) =>
{
var patchProcess = Process.Start(new ProcessStartInfo()
{

View File

@ -62,9 +62,26 @@ namespace EftPatchHelper.Tasks
{
_options.IgnoreExistingDirectories = new ConfirmationPrompt("Skip existing directories? (you will be prompted if no)").Show(AnsiConsole.Console);
if (!_settings.UsingGitea()) return;
if (_settings.UsingMega())
{
_options.UploadToMega = new ConfirmationPrompt("Upload to Mega?").Show(AnsiConsole.Console);
}
_options.CreateRelease = new ConfirmationPrompt("Create a release on gitea?").Show(AnsiConsole.Console);
if (_settings.UsingGoFile())
{
_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()

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

@ -0,0 +1,210 @@
using EftPatchHelper.Extensions;
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Spectre.Console;
using System.Security.Cryptography;
using EftPatchHelper.Helpers;
namespace EftPatchHelper.Tasks
{
public class UploadTasks : IUploadTasks
{
private readonly Options _options;
private readonly Settings _settings;
private readonly R2Helper _r2;
private readonly List<IFileUpload> _fileUploads = new();
private readonly Dictionary<IFileUpload, ProgressTask> _uploadTasks = new();
public UploadTasks(Options options, Settings settings, R2Helper r2)
{
_options = options;
_settings = settings;
_r2 = r2;
}
private static string GetFileHash(FileInfo file)
{
using (MD5 md5Service = MD5.Create())
using (var sourceStream = file.OpenRead())
{
byte[] sourceHash = md5Service.ComputeHash(sourceStream);
return Convert.ToBase64String(sourceHash);
}
}
private async Task<bool> BuildUploadList()
{
var patcherFile = new FileInfo(_options.OutputPatchPath + ".7z");
if (!patcherFile.Exists)
{
return false;
}
AnsiConsole.WriteLine("Building mirrors list ...");
if(_settings.UsingGoFile() && _options.UploadToGoFile)
{
var gofile = new GoFileUpload(patcherFile, _settings.GoFileApiKey, _settings.GoFileFolderId);
_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)
{
var mega = new MegaUpload(patcherFile, _settings.MegaEmail, _settings.MegaPassword);
await mega.SetUploadFolder(_settings.MegaUploadFolder);
_fileUploads.Add(mega);
AnsiConsole.WriteLine("Added MEGA");
}
return true;
}
private void CreateHubEntrySource()
{
string output = $"<p>Downgrade EFT Client files from version {_options.SourceClient.Version} to {_options.TargetClient.Version}</p>\n<p><br></p>";
foreach (var pair in _options.MirrorList)
{
if (!pair.Value.AddHubEntry)
{
continue;
}
var displayText = pair.Key;
var link = pair.Value.Link;
if(link.Contains("gofile.io/download/direct/"))
{
// gofile direct link is only for the mirror list, the hub entry should use the normal link
link = link.Replace("gofile.io/download/direct/", "gofile.io/download/");
}
output += $"\n<p><a href=\"{link}\">{displayText}</a></p>";
}
AnsiConsole.WriteLine(output);
var unixTimestamp = (int)DateTime.UtcNow.Subtract(DateTime.UnixEpoch).TotalSeconds;
string outputPath = $"{Environment.CurrentDirectory}\\hubEntry_{unixTimestamp}.txt";
File.WriteAllText(outputPath, output);
if(File.Exists(outputPath))
{
AnsiConsole.MarkupLine($"[green]Hub Entry Source saved: {outputPath.EscapeMarkup()}[/]");
}
else
{
AnsiConsole.MarkupLine($"[red]Hub Entry Source saved failed[/]");
}
}
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()
{
if(!await BuildUploadList())
{
return false;
}
AnsiConsole.MarkupLine($"[blue]Starting {_fileUploads[0].UploadFileInfo.Name} uploads ...[/]");
var succeeded = await AnsiConsole.Progress().Columns(
new TaskDescriptionColumn() { Alignment = Justify.Left},
new ProgressBarColumn(),
new PercentageColumn(),
new RemainingTimeColumn(),
new SpinnerColumn(Spinner.Known.Dots2)
).StartAsync<bool>(async context =>
{
foreach(var file in _fileUploads)
{
var task = context.AddTask($"[purple][[Pending]][/] {file.DisplayName} - [blue]{BytesToString(file.UploadFileInfo.Length)}[/]");
task.IsIndeterminate = true;
_uploadTasks.Add(file, task);
}
foreach(var pair in _uploadTasks)
{
// set the value of the progress task object
var progress = new System.Progress<double>((d) => pair.Value.Value = d);
pair.Value.IsIndeterminate = false;
pair.Value.Description = $"{pair.Key.DisplayName} - [blue]{BytesToString(pair.Key.UploadFileInfo.Length)}[/]";
if(!await pair.Key.UploadAsync(progress))
{
AnsiConsole.MarkupLine($"[red]{pair.Key.DisplayName.EscapeMarkup()} failed[/]");
}
else
{
DownloadMirror mirror = new DownloadMirror
{
AddHubEntry = pair.Key.AddHubEntry,
Link = pair.Key.GetLink(),
Hash = GetFileHash(pair.Key.UploadFileInfo)
};
_options.MirrorList.Add(pair.Key.HubEntryText, mirror);
}
}
return _options.MirrorList.Count > 0;
});
return succeeded;
}
public void Run()
{
if (!_options.UploadToGoFile && !_options.UploadToMega && !_options.UploadToSftpSites && !_options.UplaodToR2) return;
UploadAllFiles().GetAwaiter().GetResult().ValidateOrExit();
CreateHubEntrySource();
}
}
}

View File

@ -6,8 +6,27 @@
"patcherExePath": "",
"autoZip": true,
"autoClose": false,
"giteaApiBasePath": "", //You can leave the gitea settings blank if you don't need to create releases on gitea
"giteaApiKey": "",
"giteaReleaseRepoOwner": "",
"giteaReleaseRepoName": ""
"latestReleaseUrl": "",
"megaEmail": "",
"megaPassword": "",
"megaUploadFolder": "",
"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 :)
## Requirements
- .net 6
- .net 8
- PatchGenerator.exe (https://dev.sp-tarkov.com/waffle.lord/Patcher/releases)