# --- Author: zetod1ce (github.com/ztd38f) --- # # --- TG-Sort: Fixes file timestamps from Telegram exports (JSON & HTML) --- # param( [string]$ExportFolder = "." ) # -- ANSI Colors -- # $ESC = [char]27 $C_RESET = "$ESC[0m" $C_BOLD = "$ESC[1m" $C_CYAN = "$ESC[96m" $C_GREEN = "$ESC[92m" $C_YEL = "$ESC[93m" $C_RED = "$ESC[91m" $C_GRAY = "$ESC[90m" $C_MAG = "$ESC[95m" $C_WHITE = "$ESC[97m" # ───────────────────────────────────────── # UI PRIMITIVES (inline, no external deps) # ───────────────────────────────────────── function Write-Status([string]$msg, [string]$type = "info") { $icon = switch ($type) { "info" { "${C_CYAN}[i]${C_RESET}" } "ok" { "${C_GREEN}[✓]${C_RESET}" } "warn" { "${C_YEL}[!]${C_RESET}" } "error" { "${C_RED}[✗]${C_RESET}" } "loading" { "${C_MAG}[…]${C_RESET}" } default { " " } } Write-Host " $icon $msg" } function PS.UI([string]$title) { Clear-Host Write-Host "" Write-Host " ${C_BOLD}${C_CYAN}$title${C_RESET}" Write-Host " ${C_GRAY}$('─' * ([Math]::Max($title.Length + 2, 40)))${C_RESET}" Write-Host "" } function Ask-Menu { param( [string] $Title, [string[]] $Items, [string] $Color = "Cyan" ) $colorCode = switch ($Color) { "Cyan" { $C_CYAN } "Green" { $C_GREEN } "Yellow" { $C_YEL } "Magenta" { $C_MAG } default { $C_CYAN } } $idx = 0 $count = $Items.Count $first = $true # linesDrawn = title + blank line + N items $linesDrawn = 2 + $count [Console]::CursorVisible = $false try { while ($true) { # On redraw: move cursor up and erase each previous line if (-not $first) { for ($l = 0; $l -lt $linesDrawn; $l++) { Write-Host -NoNewline "$ESC[1A$ESC[2K" } } $first = $false # Draw menu Write-Host " ${C_BOLD}${colorCode}$Title${C_RESET}" Write-Host "" for ($i = 0; $i -lt $count; $i++) { if ($i -eq $idx) { Write-Host " ${colorCode}●${C_RESET} $($Items[$i])" } else { Write-Host " ${C_GRAY}○${C_RESET} $($Items[$i])" } } $key = [Console]::ReadKey($true) switch ($key.Key) { "UpArrow" { if ($idx -gt 0) { $idx-- } else { $idx = $count - 1 } } "DownArrow" { if ($idx -lt $count - 1) { $idx++ } else { $idx = 0 } } "Enter" { [Console]::CursorVisible = $true; return $idx } "Escape" { [Console]::CursorVisible = $true; return $null } "Q" { [Console]::CursorVisible = $true; return $null } } } } finally { [Console]::CursorVisible = $true } } # -- Draw header -- # PS.UI "TG-Sort [github.com/ztd38f]" # ───────────────────────────────────────── # DATE PATTERN DETECTION # Extracts a DateTime from a filename # supporting all universal patterns (no prefix dependency) # ───────────────────────────────────────── function Get-DateFromFilename([string]$filename) { # Strip directory / extension for matching $name = [System.IO.Path]::GetFileNameWithoutExtension($filename) # --- Telegram photo: photo_N@DD-MM-YYYY_HH-MM-SS --- if ($name -match '@(\d{2})-(\d{2})-(\d{4})_(\d{2})-(\d{2})-(\d{2})$') { try { $dt = [DateTime]::new( [int]$Matches[3], [int]$Matches[2], [int]$Matches[1], [int]$Matches[4], [int]$Matches[5], [int]$Matches[6] ) if ($dt.Year -ge 2000 -and $dt.Year -le 2035) { return $dt } } catch {} } # --- Epoch ms (13 digits) --- if ($name -match '(? # contains: