1
# PowerShell Console App for GitHub File Mirroring via Task Scheduler

param(
    [switch]$RunMirror
)

function List-GitHubMirrorTasks {
    Get-ScheduledTask | Where-Object { $_.Description -like "*GitHubMirrorApp*" }
}

function Parse-TaskAction {
    param($action)
    if ($action -match "-Uri '(.*?)'.*-OutFile '(.*?)'") {
        return @{ URL = $matches[1]; Path = $matches[2] }
    } else {
        return @{ URL = ""; Path = "" }
    }
}

function Extract-TaskInfo {
    param($task)
    $urlPath = @{ URL = ""; Path = "" }
    if ($task.Description -match "GitHubMirrorApp — (.*?) => (.*)") {
        $urlPath = @{ URL = $matches[1]; Path = $matches[2] }
    }
    return $urlPath
}

function Create-SmartDownloadArgs {
    param($url, $path)
    
    # Check if this is a GitHub URL and extract repo information
    if ($url -match "https://raw.githubusercontent.com/([^/]+)/([^/]+)/([^/]+)/(.+)") {
        $owner = $matches[1]
        $repo = $matches[2]
        $branch = $matches[3]
        $filePath = $matches[4]
        
        # Create a more sophisticated download script using GitHub API
        return "-NoProfile -WindowStyle Hidden -Command `"
            `$url = '$url'
            `$path = '$path'
            `$owner = '$owner'
            `$repo = '$repo'
            `$filePath = '$filePath'
            
            # Function to get the latest commit date for a file
            function Get-GitHubLastCommit {
                param(`$owner, `$repo, `$path)
                `$apiUrl = `"https://api.github.com/repos/`$owner/`$repo/commits?path=`$path&page=1&per_page=1`"
                try {
                    `$response = Invoke-RestMethod -Uri `$apiUrl -Headers @{'Accept'='application/vnd.github.v3+json'} -Method Get
                    if (`$response.Count -gt 0) {
                        return `$response[0].commit.committer.date
                    }
                    return `$null
                } catch {
                    Write-Host `"Error accessing GitHub API: `$_`"
                    return `$null
                }
            }
            
            `$needUpdate = `$true
            
            # Check if local file exists
            if (Test-Path `$path) {
                `$localFile = Get-Item `$path
                `$localDate = `$localFile.LastWriteTime
                
                # Get last commit date from GitHub
                `$lastCommitDate = Get-GitHubLastCommit -owner `$owner -repo `$repo -path `$filePath
                
                if (`$lastCommitDate) {
                    `$remoteDate = [DateTime]`$lastCommitDate
                    
                    Write-Host `"Local file date: `$localDate`"
                    Write-Host `"GitHub last commit date: `$remoteDate`"
                    
                    # If local file is newer or equal to remote, no need to update
                    if (`$localDate -ge `$remoteDate) {
                        Write-Host `"File is up to date. No download needed.`"
                        `$needUpdate = `$false
                    } else {
                        Write-Host `"Remote file is newer. Downloading...`"
                    }
                } else {
                    Write-Host `"Could not determine last commit date. Proceeding with download.`"
                }
            } else {
                Write-Host `"Local file does not exist. Downloading...`"
            }
            
            # Download file if needed
            if (`$needUpdate) {
                try {
                    `$webClient = New-Object System.Net.WebClient
                    `$webClient.DownloadFile(`$url, `$path)
                    Write-Host `"File downloaded successfully to `$path`"
                    
                    # Update file timestamp to match GitHub commit date if available
                    `$lastCommitDate = Get-GitHubLastCommit -owner `$owner -repo `$repo -path `$filePath
                    if (`$lastCommitDate) {
                        `$commitDateTime = [DateTime]`$lastCommitDate
                        (Get-Item `$path).LastWriteTime = `$commitDateTime
                        Write-Host `"Updated file timestamp to match GitHub commit date`"
                    }
                } catch {
                    Write-Host `"Error downloading file: `$_`"
                }
            }
        `""
    } else {
        # Fall back to the regular HTTP-based approach for non-GitHub URLs
        return "-NoProfile -WindowStyle Hidden -Command `"`$url = '$url'; `$file = '$path'; `$req = [System.Net.HttpWebRequest]::Create(`$url); `$req.Method = 'GET'; if (Test-Path `$file) { `$req.IfModifiedSince = (Get-Item `$file).LastWriteTime }; try { `$res = `$req.GetResponse(); [System.IO.File]::WriteAllBytes(`$file, (New-Object System.IO.BinaryReader(`$res.GetResponseStream())).ReadBytes(`$res.ContentLength)); `$res.Close(); Write-Host 'File downloaded successfully.' } catch [System.Net.WebException] { if (`$_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotModified) { Write-Host 'File has not been modified since the last download.' } else { Write-Host 'An error occurred: ' + `$_.Exception.Message } }`""
    }
}

function Create-TaskComponents {
    param($url, $path, $username)
    
    $psArgs = Create-SmartDownloadArgs -url $url -path $path
    $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $psArgs
    $trigger = New-ScheduledTaskTrigger -Daily -At 5am
    
    if ([string]::IsNullOrWhiteSpace($username)) {
        $username = "$env:USERDOMAIN\$env:USERNAME"
    }
    
    $principal = New-ScheduledTaskPrincipal -UserId $username -LogonType S4U -RunLevel Highest
    $description = "GitHubMirrorApp — $url => $path"
    
    return @{
        Action = $action
        Trigger = $trigger
        Principal = $principal
        Description = $description
    }
}

function Mirror-Files {
    $tasks = List-GitHubMirrorTasks
    foreach ($task in $tasks) {
        $parsed = Parse-TaskAction $task.Actions.Arguments
        try {
            Invoke-WebRequest -Uri $parsed.URL -OutFile $parsed.Path
            Write-Host "Updated: $($parsed.Path)"
        } catch {
            Write-Warning "Failed to update $($parsed.URL): $_"
        }
    }
}

if ($RunMirror) {
    Mirror-Files
    exit
}

function Show-Menu {
    Clear-Host
    Write-Host "Currently Registered GitHubMirror Tasks:" -ForegroundColor Yellow
    $i = 0
    List-GitHubMirrorTasks | ForEach-Object {
        $desc = $_.Description
        
        # Extract URL and path from description rather than action arguments
        $urlPath = Extract-TaskInfo $_
        
        # Check if task is disabled and set color accordingly
        $taskColor = [System.ConsoleColor]::White
        $stateInfo = ""
        if ($_.State -eq "Disabled") {
            $taskColor = [System.ConsoleColor]::Red
            $stateInfo = " [DISABLED]"
        }
        
        # Simplified display - all tasks run daily at 5 AM
        Write-Host "[$i] $($_.TaskName)$stateInfo" -ForegroundColor $taskColor
        Write-Host "     => $($urlPath.URL)" -ForegroundColor $taskColor
        $i++
    }
    Write-Host ""
    Write-Host "GitHub File Mirror Tool" -ForegroundColor Cyan
    Write-Host "[A] Add Entry" -ForegroundColor Green
    Write-Host "[E] Edit Entry (press a number first, then E)" -ForegroundColor Green
    Write-Host "[C] Copy Entry (press a number first, then C)" -ForegroundColor Green
    Write-Host "[D] Delete Entry (press a number first, then D)" -ForegroundColor Green
    Write-Host "[T] Toggle Enable/Disable (press a number first, then T)" -ForegroundColor Green
    Write-Host "[B] Create Batch File (press a number first, then B)" -ForegroundColor Green
    Write-Host "[R] Run Task Now (press a number first, then R)" -ForegroundColor Green
    Write-Host "[Q] Quit" -ForegroundColor Green
    Write-Host ""
    Write-Host "Tip: Type a task number followed by an action key (e.g. '0B' to create a batch file for task 0)" -ForegroundColor Cyan
    Write-Host ""
    Write-Host "Press a key to select an option..." -ForegroundColor Yellow
}

function Get-TaskAction {
    $numberBuffer = ""
    $waitingForAction = $false
    
    while ($true) {
        $key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
        $keyChar = $key.Character
        
        # Handle numeric keys
        if ($keyChar -ge '0' -and $keyChar -le '9') {
            $numberBuffer += $keyChar
            Write-Host $keyChar -NoNewline
            $waitingForAction = $true
            continue
        }
        
        # Handle action keys
        $action = $keyChar.ToString().ToUpper()
        
        # If we have a number in the buffer and an action key
        if ($waitingForAction -and $numberBuffer -ne "" -and $action -match '[ECDTBR]') {
            Write-Host $action
            $index = [int]$numberBuffer
            
            switch ($action) {
                'E' { Edit-Entry -Index $index }
                'C' { Copy-Entry -Index $index }
                'D' { Delete-Entry -Index $index }
                'T' { Toggle-Active -Index $index }
                'B' { Create-BatchFile -Index $index }
                'R' { Run-Task -Index $index }
            }
            
            return $true
        }
        # Handle single action keys
        elseif ($action -eq 'A') {
            Write-Host "A"
            Add-Entry
            return $true
        }
        elseif ($action -eq 'Q') {
            Write-Host "Q"
            return $false
        }
        elseif ($action -eq 'C' -and $numberBuffer -eq "") {
            Write-Host "C"
            $index = Read-Host "Enter the number of the task to copy"
            if ($index -match '^\d+$') {
                Copy-Entry -Index ([int]$index)
            } else {
                Write-Host "Invalid index. Please enter a number." -ForegroundColor Red
            }
            return $true
        }
        elseif ($action -eq 'B' -and $numberBuffer -eq "") {
            Write-Host "B"
            $index = Read-Host "Enter the number of the task to create a batch file for"
            if ($index -match '^\d+$') {
                Create-BatchFile -Index ([int]$index)
            } else {
                Write-Host "Invalid index. Please enter a number." -ForegroundColor Red
            }
            return $true
        }
        
        # Clear buffer on invalid input
        $numberBuffer = ""
        $waitingForAction = $false
    }
}

function Get-CommonEntryInfo {
    param(
        [string]$ExistingURL = "",
        [string]$ExistingPath = ""
    )
    $url = Read-Host "Enter GitHub URL (leave blank to keep current: $ExistingURL)"
    if ([string]::IsNullOrWhiteSpace($url)) { $url = $ExistingURL }
    if ($url -match "github.com/(.+)/blob/(.+)") {
        $url = "https://raw.githubusercontent.com/$($matches[1])/$($matches[2])"
        Write-Host "Auto-converted to raw GitHub URL: $url" -ForegroundColor Green
    }
    try {
        $response = Invoke-WebRequest -Uri $url -Method Head -UseBasicParsing -TimeoutSec 10
        if ($response.StatusCode -ne 200) {
            Write-Warning "URL is not accessible (Status: $($response.StatusCode)). Aborting."
            return $null
        }
    } catch {
        Write-Warning "Failed to reach URL: $_.Exception.Message"
        return $null
    }
    
    # Parse existing path into directory and filename if provided
    $existingDirectory = ""
    $existingFileName = ""
    if (-not [string]::IsNullOrWhiteSpace($ExistingPath)) {
        $existingDirectory = Split-Path -Parent $ExistingPath
        $existingFileName = Split-Path -Leaf $ExistingPath
    }
    
    # Get new filename or use existing/default
    $defaultFileName = [System.Web.HttpUtility]::UrlDecode(($url -split '/')[-1])
    if (-not [string]::IsNullOrWhiteSpace($existingFileName)) {
        $promptFileName = $existingFileName
    } else {
        $promptFileName = $defaultFileName
    }
    
    $fileName = Read-Host "Enter file name (default: $promptFileName)"
    if ([string]::IsNullOrWhiteSpace($fileName)) { $fileName = $promptFileName }
    
    # Get directory or use existing
    $directoryPrompt = if (-not [string]::IsNullOrWhiteSpace($existingDirectory)) {
        "Enter full output directory path (default: $existingDirectory)"
    } else {
        "Enter full output directory path"
    }
    
    $directory = Read-Host $directoryPrompt
    if ([string]::IsNullOrWhiteSpace($directory) -and -not [string]::IsNullOrWhiteSpace($existingDirectory)) {
        $directory = $existingDirectory
    }
    
    if (-not (Test-Path $directory)) {
        Write-Host "Invalid directory. Aborting."
        return $null
    }
    
    $path = Join-Path $directory $fileName
    return @{ URL = $url; Path = $path }
}

function Add-Entry {
    $info = Get-CommonEntryInfo
    if ($null -eq $info) { return }
    $url = $info.URL
    $path = $info.Path
    $hash = [System.BitConverter]::ToString((New-Object Security.Cryptography.SHA256Managed).ComputeHash([Text.Encoding]::UTF8.GetBytes($url))).Replace("-", "").Substring(0, 8)
    $taskName = "GitHubMirror_$hash"
    $username = Read-Host "Enter username to run task as (leave blank to use current user)"
    $useCurrentUser = [string]::IsNullOrWhiteSpace($username)
    if ($useCurrentUser) {
        $username = "$env:USERDOMAIN\$env:USERNAME"
    }
    
    $taskComponents = Create-TaskComponents -url $url -path $path -username $username
    
    if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {
        Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
    }
    
    if ($useCurrentUser) {
        Register-ScheduledTask -TaskName $taskName -Action $taskComponents.Action -Trigger $taskComponents.Trigger -Principal $taskComponents.Principal -Description $taskComponents.Description
    } else {
        $plainPassword = Read-Host "Enter password (visible)"
        Register-ScheduledTask -TaskName $taskName -Action $taskComponents.Action -Trigger $taskComponents.Trigger -User $username -Password $plainPassword -Description $taskComponents.Description -RunLevel Highest
    }
    Write-Host "Task '$taskName' created."
}

function Edit-Entry {
    param([int]$Index)
    $task = (List-GitHubMirrorTasks)[$Index]
    if ($null -eq $task) { Write-Host "Invalid index."; return }
    
    # Extract URL and path from description
    $urlPath = Extract-TaskInfo $task
    $url = $urlPath.URL
    $path = $urlPath.Path
    
    $info = Get-CommonEntryInfo -ExistingURL $url -ExistingPath $path
    if ($null -eq $info) { return }
    $url = $info.URL
    $path = $info.Path
    
    Unregister-ScheduledTask -TaskName $task.TaskName -Confirm:$false
    
    $taskComponents = Create-TaskComponents -url $url -path $path -username ""
    
    Register-ScheduledTask -TaskName $task.TaskName -Action $taskComponents.Action -Trigger $taskComponents.Trigger -Principal $taskComponents.Principal -Description $taskComponents.Description
    Write-Host "Task '$($task.TaskName)' updated."
}

function Delete-Entry {
    param([int]$Index)
    $task = (List-GitHubMirrorTasks)[$Index]
    if ($null -eq $task) { Write-Host "Invalid index."; return }
    Unregister-ScheduledTask -TaskName $task.TaskName -Confirm:$false
    Write-Host "Task '$($task.TaskName)' deleted."
}

function Toggle-Active {
    param([int]$Index)
    $task = (List-GitHubMirrorTasks)[$Index]
    if ($null -eq $task) { Write-Host "Invalid index."; return }
    if ($task.State -eq 'Ready') {
        Disable-ScheduledTask -TaskName $task.TaskName
        Write-Host "Task '$($task.TaskName)' disabled."
    } else {
        Enable-ScheduledTask -TaskName $task.TaskName
        Write-Host "Task '$($task.TaskName)' enabled."
    }
}

function Copy-Entry {
    param([int]$Index)
    $task = (List-GitHubMirrorTasks)[$Index]
    if ($null -eq $task) { Write-Host "Invalid index."; return }
    
    # Extract URL and path from description
    $urlPath = Extract-TaskInfo $task
    $url = $urlPath.URL
    $path = $urlPath.Path
    
    Write-Host "Creating a new task based on task #$Index" -ForegroundColor Cyan
    Write-Host "You can modify the URL, path, and other settings as needed" -ForegroundColor Cyan
    
    # Use existing values as defaults but allow changes
    $info = Get-CommonEntryInfo -ExistingURL $url -ExistingPath $path
    if ($null -eq $info) { return }
    $url = $info.URL
    $path = $info.Path
    
    # Generate a new hash/task name with timestamp to ensure uniqueness
    $timestamp = Get-Date -Format "yyMMddHHmmss"
    $hash = [System.BitConverter]::ToString((New-Object Security.Cryptography.SHA256Managed).ComputeHash([Text.Encoding]::UTF8.GetBytes("$url$timestamp"))).Replace("-", "").Substring(0, 8)
    $taskName = "GitHubMirror_$hash"
    
    # Get user credentials
    $username = Read-Host "Enter username to run task as (leave blank to use current user)"
    $useCurrentUser = [string]::IsNullOrWhiteSpace($username)
    if ($useCurrentUser) {
        $username = "$env:USERDOMAIN\$env:USERNAME"
    }
    
    $taskComponents = Create-TaskComponents -url $url -path $path -username $username
    
    if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {
        Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
    }
    
    if ($useCurrentUser) {
        Register-ScheduledTask -TaskName $taskName -Action $taskComponents.Action -Trigger $taskComponents.Trigger -Principal $taskComponents.Principal -Description $taskComponents.Description
    } else {
        $plainPassword = Read-Host "Enter password (visible)"
        Register-ScheduledTask -TaskName $taskName -Action $taskComponents.Action -Trigger $taskComponents.Trigger -User $username -Password $plainPassword -Description $taskComponents.Description -RunLevel Highest
    }
    Write-Host "New task '$taskName' created based on existing task." -ForegroundColor Green
}

function Generate-BatchFileContent {
    param($taskName, $url, $path)
    
    # Create a more sophisticated script that uses the GitHub API if applicable
    $scriptContent = @"
# PowerShell script to create a scheduled task for GitHub file mirroring
`$url = '$url'
`$path = '$path'

# Create directory if it doesn't exist
`$directory = Split-Path -Parent `$path
if (-not (Test-Path `$directory)) {
    Write-Host "Creating directory: `$directory"
    New-Item -ItemType Directory -Path `$directory | Out-Null
}

# Check if this is a GitHub URL
`$useGitHubAPI = `$false
`$owner = ""
`$repo = ""
`$filePath = ""

if (`$url -match "https://raw.githubusercontent.com/([^/]+)/([^/]+)/([^/]+)/(.+)") {
    `$useGitHubAPI = `$true
    `$owner = `$matches[1]
    `$repo = `$matches[2]
    `$branch = `$matches[3]
    `$filePath = `$matches[4]
    Write-Host "Detected GitHub URL: Owner=`$owner, Repo=`$repo, Path=`$filePath"
}

# Function to get the latest commit date for a file
function Get-GitHubLastCommit {
    param(`$owner, `$repo, `$path)
    `$apiUrl = "https://api.github.com/repos/`$owner/`$repo/commits?path=`$path`&page=1`&per_page=1"
    try {
        `$response = Invoke-RestMethod -Uri `$apiUrl -Headers @{'Accept'='application/vnd.github.v3+json'} -Method Get
        if (`$response.Count -gt 0) {
            return `$response[0].commit.committer.date
        }
        return `$null
    } catch {
        Write-Host "Error accessing GitHub API: `$_"
        return `$null
    }
}

# Download file immediately
Write-Host "Downloading file..."

if (`$useGitHubAPI) {
    try {
        # Use the GitHub API approach
        `$webClient = New-Object System.Net.WebClient
        `$webClient.DownloadFile(`$url, `$path)
        Write-Host "File downloaded successfully to `$path"
        
        # Update file timestamp to match GitHub commit date if available
        `$lastCommitDate = Get-GitHubLastCommit -owner `$owner -repo `$repo -path `$filePath
        if (`$lastCommitDate) {
            `$commitDateTime = [DateTime]`$lastCommitDate
            (Get-Item `$path).LastWriteTime = `$commitDateTime
            Write-Host "Updated file timestamp to match GitHub commit date"
        }
    } catch {
        Write-Host "Error downloading file: `$_"
    }
} else {
    # Use the regular HTTP approach
    `$req = [System.Net.HttpWebRequest]::Create(`$url)
    `$req.Method = "GET"
    try {
        `$res = `$req.GetResponse()
        [System.IO.File]::WriteAllBytes(`$path, (New-Object System.IO.BinaryReader(`$res.GetResponseStream())).ReadBytes(`$res.ContentLength))
        `$res.Close()
        Write-Host "File downloaded successfully to `$path"
    } catch {
        Write-Host "Error downloading file: `$_"
    }
}

# Create the scheduled task
`$taskName = "$taskName"

# Define a script block for the scheduled task
`$scriptBlock = {
    param(`$url, `$path, `$owner, `$repo, `$filePath, `$useGitHubAPI)
    
    # Function definition if using GitHub API
    if (`$useGitHubAPI) {
        function Get-GitHubLastCommit {
            param(`$owner, `$repo, `$path)
            `$apiUrl = "https://api.github.com/repos/`$owner/`$repo/commits?path=`$path`&page=1`&per_page=1"
            try {
                `$response = Invoke-RestMethod -Uri `$apiUrl -Headers @{'Accept'='application/vnd.github.v3+json'} -Method Get
                if (`$response.Count -gt 0) {
                    return `$response[0].commit.committer.date
                }
                return `$null
            } catch {
                Write-Host "Error accessing GitHub API: `$_"
                return `$null
            }
        }
    }
    
    `$needUpdate = `$true
    
    if (`$useGitHubAPI) {
        # GitHub API logic
        if (Test-Path `$path) {
            `$localFile = Get-Item `$path
            `$localDate = `$localFile.LastWriteTime
            
            `$lastCommitDate = Get-GitHubLastCommit -owner `$owner -repo `$repo -path `$filePath
            
            if (`$lastCommitDate) {
                `$remoteDate = [DateTime]`$lastCommitDate
                
                Write-Host "Local file date: `$localDate"
                Write-Host "GitHub last commit date: `$remoteDate"
                
                if (`$localDate -ge `$remoteDate) {
                    Write-Host "File is up to date. No download needed."
                    `$needUpdate = `$false
                } else {
                    Write-Host "Remote file is newer. Downloading..."
                }
            } else {
                Write-Host "Could not determine last commit date. Proceeding with download."
            }
        } else {
            Write-Host "Local file does not exist. Downloading..."
        }
        
        if (`$needUpdate) {
            try {
                `$webClient = New-Object System.Net.WebClient
                `$webClient.DownloadFile(`$url, `$path)
                Write-Host "File downloaded successfully to `$path"
                
                `$lastCommitDate = Get-GitHubLastCommit -owner `$owner -repo `$repo -path `$filePath
                if (`$lastCommitDate) {
                    `$commitDateTime = [DateTime]`$lastCommitDate
                    (Get-Item `$path).LastWriteTime = `$commitDateTime
                    Write-Host "Updated file timestamp to match GitHub commit date"
                }
            } catch {
                Write-Host "Error downloading file: `$_"
            }
        }
    } else {
        # Standard HTTP logic for non-GitHub URLs
        `$req = [System.Net.HttpWebRequest]::Create(`$url)
        `$req.Method = 'GET'
        if (Test-Path `$path) {
            `$req.IfModifiedSince = (Get-Item `$path).LastWriteTime
        }
        try {
            `$res = `$req.GetResponse()
            [System.IO.File]::WriteAllBytes(`$path, (New-Object System.IO.BinaryReader(`$res.GetResponseStream())).ReadBytes(`$res.ContentLength))
            `$res.Close()
            Write-Host 'File downloaded successfully.'
        } catch [System.Net.WebException] {
            if (`$_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotModified) {
                Write-Host 'File has not been modified since the last download.'
            } else {
                Write-Host 'An error occurred: ' + `$_.Exception.Message
            }
        }
    }
}

# Detect if GitHub URL and create command string
if (`$useGitHubAPI) {
    # For GitHub URLs
    `$command = "`$scriptBlock.Invoke('`$url', '`$path', '`$owner', '`$repo', '`$filePath', `$true)"
} else {
    # For standard URLs
    `$command = "`$scriptBlock.Invoke('`$url', '`$path', '', '', '', `$false)"
}

# Create a scriptblock string
`$scriptBlockStr = `$scriptBlock.ToString()

# Create final command that combines scriptblock definition and invocation
`$fullCommand = "```$scriptBlock = {`n`$scriptBlockStr`n}`n`$command"

# Prepare the scheduled task
`$psArgs = "-NoProfile -WindowStyle Hidden -Command `$fullCommand"
`$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument `$psArgs
`$trigger = New-ScheduledTaskTrigger -Daily -At 5am
`$principal = New-ScheduledTaskPrincipal -UserId "`$env:USERDOMAIN\`$env:USERNAME" -LogonType S4U -RunLevel Highest
`$description = "GitHubMirrorApp — `$url => `$path"

# Remove existing task if it exists
if (Get-ScheduledTask -TaskName `$taskName -ErrorAction SilentlyContinue) {
    Write-Host "Removing existing task: `$taskName"
    Unregister-ScheduledTask -TaskName `$taskName -Confirm:`$false
}

# Register the task
Write-Host "Creating scheduled task: `$taskName"
Register-ScheduledTask -TaskName `$taskName -Action `$action -Trigger `$trigger -Principal `$principal -Description `$description

Write-Host "Task created successfully!"
"@

    $batchContent = @"
@echo off
:: Self-elevate if not already running as administrator
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' goto :already_admin
echo Requesting administrative privileges...
powershell -Command "Start-Process -FilePath '%~f0' -Verb RunAs"
exit /b
:already_admin
echo Creating scheduled task for GitHub file mirroring...
powershell.exe -ExecutionPolicy Bypass -File "%~dp0TaskScript_$($taskName.Replace("GitHubMirror_", "")).ps1"
echo.
echo Task setup complete!
"@

    return @{
        ScriptContent = $scriptContent
        BatchContent = $batchContent
    }
}

function Create-BatchFile {
    param([int]$Index)
    $task = (List-GitHubMirrorTasks)[$Index]
    if ($null -eq $task) { Write-Host "Invalid index."; return }
    
    # Extract URL and path from description
    $urlPath = Extract-TaskInfo $task
    $url = $urlPath.URL
    $path = $urlPath.Path
    
    if ([string]::IsNullOrWhiteSpace($url) -or [string]::IsNullOrWhiteSpace($path)) {
        Write-Host "Could not extract URL and path from task." -ForegroundColor Red
        return
    }
    
    # Create a directory for batch files if it doesn't exist
    $batchDir = Join-Path $PSScriptRoot "TaskBatches"
    if (-not (Test-Path $batchDir)) {
        New-Item -ItemType Directory -Path $batchDir | Out-Null
    }
    
    # Create a name for the batch file based on the task name
    $taskShortName = $task.TaskName.Replace("GitHubMirror_", "")
    $batchFile = Join-Path $batchDir "Install_$taskShortName.bat"
    
    # Create a PowerShell script file with the task creation logic
    $psFile = Join-Path $batchDir "TaskScript_$taskShortName.ps1"
    
    $fileContents = Generate-BatchFileContent -taskName $task.TaskName -url $url -path $path
    
    # Write the PowerShell script with UTF-8 encoding with BOM to ensure proper encoding
    [System.IO.File]::WriteAllText($psFile, $fileContents.ScriptContent, [System.Text.Encoding]::UTF8)
    
    # Write the batch file with ASCII encoding
    [System.IO.File]::WriteAllText($batchFile, $fileContents.BatchContent, [System.Text.Encoding]::ASCII)
    
    Write-Host "Batch file created: $batchFile" -ForegroundColor Green
    Write-Host "PowerShell script created: $psFile" -ForegroundColor Green
    
    # Ask if user wants to open the batch file's folder
    Write-Host "Do you want to open the folder containing the batch file? (Y/N): " -NoNewline -ForegroundColor Yellow
    $key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    $keyChar = $key.Character.ToString().ToUpper()
    Write-Host $keyChar
    if ($keyChar -eq "Y") {
        Start-Process "explorer.exe" -ArgumentList $batchDir
    }
}

function Run-Task {
    param([int]$Index)
    $task = (List-GitHubMirrorTasks)[$Index]
    if ($null -eq $task) { Write-Host "Invalid index."; return }
    
    Write-Host "Running task '$($task.TaskName)'..." -ForegroundColor Yellow
    
    # Extract URL and path information
    $urlPath = Extract-TaskInfo $task
    $url = $urlPath.URL
    $path = $urlPath.Path
    
    # Download the file
    try {
        if ($url -match "https://raw.githubusercontent.com/([^/]+)/([^/]+)/([^/]+)/(.+)") {
            $owner = $matches[1]
            $repo = $matches[2]
            $filePath = $matches[4]
            
            # Use GitHub API to check if file needs updating
            $apiUrl = "https://api.github.com/repos/$owner/$repo/commits?path=$filePath&page=1&per_page=1"
            $needUpdate = $true
            
            if (Test-Path $path) {
                $localFile = Get-Item $path
                $localDate = $localFile.LastWriteTime
                
                try {
                    $response = Invoke-RestMethod -Uri $apiUrl -Headers @{'Accept'='application/vnd.github.v3+json'} -Method Get
                    if ($response.Count -gt 0) {
                        $remoteDate = [DateTime]$response[0].commit.committer.date
                        
                        Write-Host "Local file date: $localDate" -ForegroundColor Cyan
                        Write-Host "GitHub last commit date: $remoteDate" -ForegroundColor Cyan
                        
                        if ($localDate -ge $remoteDate) {
                            Write-Host "File is up to date. No download needed." -ForegroundColor Green
                            $needUpdate = $false
                        } else {
                            Write-Host "Remote file is newer. Downloading..." -ForegroundColor Yellow
                        }
                    }
                } catch {
                    Write-Host "Could not determine last commit date. Proceeding with download." -ForegroundColor Yellow
                }
            } else {
                Write-Host "Local file does not exist. Downloading..." -ForegroundColor Yellow
            }
            
            if ($needUpdate) {
                $webClient = New-Object System.Net.WebClient
                $webClient.DownloadFile($url, $path)
                Write-Host "File downloaded successfully to $path" -ForegroundColor Green
                
                # Update file timestamp to match GitHub commit date if available
                try {
                    $response = Invoke-RestMethod -Uri $apiUrl -Headers @{'Accept'='application/vnd.github.v3+json'} -Method Get
                    if ($response.Count -gt 0) {
                        $commitDateTime = [DateTime]$response[0].commit.committer.date
                        (Get-Item $path).LastWriteTime = $commitDateTime
                        Write-Host "Updated file timestamp to match GitHub commit date" -ForegroundColor Green
                    }
                } catch {
                    Write-Host "Error updating timestamp: $_" -ForegroundColor Red
                }
            }
        } else {
            # Use standard HTTP download for non-GitHub URLs
            $req = [System.Net.HttpWebRequest]::Create($url)
            $req.Method = 'GET'
            if (Test-Path $path) {
                $req.IfModifiedSince = (Get-Item $path).LastWriteTime
            }
            try {
                $res = $req.GetResponse()
                [System.IO.File]::WriteAllBytes($path, (New-Object System.IO.BinaryReader($res.GetResponseStream())).ReadBytes($res.ContentLength))
                $res.Close()
                Write-Host "File downloaded successfully to $path" -ForegroundColor Green
            } catch [System.Net.WebException] {
                if ($_.Exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotModified) {
                    Write-Host "File has not been modified since the last download." -ForegroundColor Green
                } else {
                    throw
                }
            }
        }
    } catch {
        Write-Host "Error downloading file: $_" -ForegroundColor Red
    }
}

# Main loop
while ($true) {
    Show-Menu
    $continue = Get-TaskAction
    if (-not $continue) {
        break
    }
}

For immediate assistance, please email our customer support: [email protected]

Download RAW File