extras: add PowerShell greeting port, Rust gm-switch and Windows installers
- powershell_greeting: PowerShell port of fish_greeting (fastfetch random logo, sixel/kitty-direct per terminal, logs, modes) - win-gm-switch: Rust port of gm-switch.cpp (cross-platform mode file, --status, Start Menu shortcuts installer) - install-windows.ps1 umbrella installer; README pointers
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# J621Greeting.ps1 - show a random J621 library image as the shell greeting.
|
||||
#
|
||||
# PowerShell port of extras/fish_greeting/fish_greeting.fish (the J621 REST
|
||||
# rewrite): asks GET /api/random/?fastfetch=1&rating=X for a random
|
||||
# png/jpg/gif, downloads the signed url and runs fastfetch with the image as
|
||||
# its logo. Based on the original script from
|
||||
# J621-Django/extras/fish_greeting system/fish_greeting.fish.
|
||||
#
|
||||
# Requires: fastfetch and curl.exe (ships with Windows 10 1803+).
|
||||
#
|
||||
# Rating modes (same file name as the fish version):
|
||||
# %APPDATA%\j621Greeting\greeting_mode 0 = NSFW (explicit)
|
||||
# 1 = SFW (safe)
|
||||
# 2 = Questionable (default)
|
||||
#
|
||||
# Configuration: %APPDATA%\j621Greeting\config.ps1 (sets environment
|
||||
# variables; see config.example.ps1):
|
||||
# J621_BASE API origin (default https://j621.rainbow-herring.ts.net)
|
||||
# J621_TOKEN API / scoped greeting token, sent as "Authorization: Token ..."
|
||||
# J621_WEB web origin used in the printed link (defaults to J621_BASE)
|
||||
# J621_FASTFETCH_CONFIG fastfetch config name (default "jake")
|
||||
# J621_MODE_FILE mode file override
|
||||
# J621_LOGO_TYPE force a fastfetch logo type (sixel, kitty-direct, ...)
|
||||
# J621_ALWAYS_FETCH=1 fetch even in terminals without image support
|
||||
# J621_LOGO_WIDTH logo width in columns (default 35)
|
||||
|
||||
#Requires -Version 5.1
|
||||
|
||||
function Get-J621FileKind {
|
||||
param([string]$Path)
|
||||
try {
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
try {
|
||||
$buffer = New-Object byte[] 8
|
||||
$read = $stream.Read($buffer, 0, 8)
|
||||
if ($read -ge 4 -and $buffer[0] -eq 0x89 -and $buffer[1] -eq 0x50 -and $buffer[2] -eq 0x4E -and $buffer[3] -eq 0x47) { return 'png' }
|
||||
if ($read -ge 2 -and $buffer[0] -eq 0xFF -and $buffer[1] -eq 0xD8) { return 'jpeg' }
|
||||
if ($read -ge 3 -and $buffer[0] -eq 0x47 -and $buffer[1] -eq 0x49 -and $buffer[2] -eq 0x46) { return 'gif' }
|
||||
return 'unknown'
|
||||
} finally { $stream.Dispose() }
|
||||
} catch { return 'unknown' }
|
||||
}
|
||||
|
||||
function Format-J621Accent {
|
||||
param([string]$Text)
|
||||
return "$([char]27)[4;2;38;2;138;0;222;49m$Text$([char]27)[0m"
|
||||
}
|
||||
|
||||
function Format-J621Error {
|
||||
param([string]$Text)
|
||||
return "$([char]27)[31m$Text$([char]27)[0m"
|
||||
}
|
||||
|
||||
function Get-J621FastfetchArgs {
|
||||
$configName = if ($env:J621_FASTFETCH_CONFIG) { $env:J621_FASTFETCH_CONFIG } else { 'jake' }
|
||||
$candidates = @(
|
||||
(Join-Path $env:APPDATA "fastfetch\$configName.jsonc"),
|
||||
(Join-Path $env:APPDATA "fastfetch\$configName\config.jsonc")
|
||||
)
|
||||
foreach ($candidate in $candidates) {
|
||||
if (Test-Path -LiteralPath $candidate) { return @('--config', $configName) }
|
||||
}
|
||||
return @()
|
||||
}
|
||||
|
||||
function Show-J621Greeting {
|
||||
param([int]$Depth = 0)
|
||||
|
||||
# --- Configuration ---
|
||||
$configFile = Join-Path $env:APPDATA 'j621Greeting\config.ps1'
|
||||
if (Test-Path -LiteralPath $configFile) { . $configFile }
|
||||
|
||||
$base = if ($env:J621_BASE) { $env:J621_BASE.TrimEnd('/') } else { 'https://j621.rainbow-herring.ts.net' }
|
||||
$webBase = if ($env:J621_WEB) { $env:J621_WEB.TrimEnd('/') } else { $base }
|
||||
$token = $env:J621_TOKEN
|
||||
$logoWidth = if ($env:J621_LOGO_WIDTH) { $env:J621_LOGO_WIDTH } else { '35' }
|
||||
$modeFile = if ($env:J621_MODE_FILE) { $env:J621_MODE_FILE } else { Join-Path $env:APPDATA 'j621Greeting\greeting_mode' }
|
||||
|
||||
# --- Setup logging ---
|
||||
$logDir = Join-Path $env:APPDATA 'j621Logos'
|
||||
$logFile = Join-Path $logDir 'logs.log'
|
||||
if (-not (Test-Path -LiteralPath $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
|
||||
$stamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
|
||||
|
||||
# --- Mode selection ---
|
||||
$mode = '2'
|
||||
if (Test-Path -LiteralPath $modeFile) {
|
||||
$raw = Get-Content -LiteralPath $modeFile -Raw -ErrorAction SilentlyContinue
|
||||
if ($null -ne $raw) { $mode = $raw.Trim() }
|
||||
}
|
||||
switch ($mode) {
|
||||
'0' { $rating = 'e'; $modeName = 'NSFW' }
|
||||
'1' { $rating = 's'; $modeName = 'SFW' }
|
||||
'2' { $rating = 'q'; $modeName = 'Questionable' }
|
||||
default { Write-Output 'Unknown mode, using default NSFW'; $rating = 'e'; $modeName = 'NSFW' }
|
||||
}
|
||||
|
||||
# --- Pick the image protocol for this terminal ---
|
||||
$logoType = $env:J621_LOGO_TYPE
|
||||
if (-not $logoType) {
|
||||
if ($env:WT_SESSION) { $logoType = 'sixel' }
|
||||
elseif ($env:WEZTERM_PANE) { $logoType = 'kitty-direct' }
|
||||
}
|
||||
$fetchImage = [bool]$logoType -or ($env:J621_ALWAYS_FETCH -eq '1')
|
||||
$ffArgs = @(Get-J621FastfetchArgs)
|
||||
|
||||
if (-not $fetchImage) {
|
||||
# Terminal without image support (e.g. VS Code): plain fastfetch.
|
||||
& fastfetch @ffArgs
|
||||
return
|
||||
}
|
||||
|
||||
# --- Ask the API for a random image (JSON with a signed URL) ---
|
||||
$apiUrl = "$base/api/random/?fastfetch=1&rating=$rating"
|
||||
$curlArgs = @('-s', '-L', '--max-time', '20')
|
||||
if ($token) { $curlArgs += @('-H', "Authorization: Token $token") }
|
||||
$json = & curl.exe @curlArgs $apiUrl 2>$null | Out-String
|
||||
|
||||
$data = $null
|
||||
try { $data = $json | ConvertFrom-Json } catch { $data = $null }
|
||||
|
||||
$imageUrl = $null; $jId = $null; $md5 = $null; $filename = $null; $detail = $null; $ext = 'img'
|
||||
if ($data) {
|
||||
if ($data.url) { $imageUrl = [string]$data.url }
|
||||
if ($data.j_id) { $jId = [string]$data.j_id }
|
||||
if ($data.md5) { $md5 = [string]$data.md5 }
|
||||
if ($data.filename) { $filename = [string]$data.filename }
|
||||
if ($data.detail) { $detail = [string]$data.detail }
|
||||
if ($data.extension) { $ext = [string]$data.extension }
|
||||
}
|
||||
if ($ext -notmatch '^[A-Za-z0-9]+$') { $ext = 'img' }
|
||||
|
||||
if (-not $imageUrl) {
|
||||
& fastfetch @ffArgs
|
||||
if ($detail) { Write-Host (Format-J621Accent "No logo ($detail)") } else { Write-Host (Format-J621Accent 'No logo (API error)') }
|
||||
Add-Content -LiteralPath $logFile -Value "$stamp No logo for rating=${rating}: $detail"
|
||||
return
|
||||
}
|
||||
|
||||
# --- Download the signed image ---
|
||||
$tempFile = Join-Path ([IO.Path]::GetTempPath()) ('j621_logo_' + [guid]::NewGuid().ToString('N') + '.' + $ext)
|
||||
$downloadSuccess = $false
|
||||
& curl.exe -s -L --max-time 60 -o $tempFile $imageUrl 2>$null
|
||||
if ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $tempFile) -and (Get-Item -LiteralPath $tempFile).Length -gt 0) { $downloadSuccess = $true }
|
||||
|
||||
if (-not $downloadSuccess) {
|
||||
& fastfetch @ffArgs
|
||||
Write-Host (Format-J621Accent 'No logo (download failed)')
|
||||
Add-Content -LiteralPath $logFile -Value "$stamp Download failed for $jId"
|
||||
Remove-Item -LiteralPath $tempFile -ErrorAction SilentlyContinue
|
||||
return
|
||||
}
|
||||
|
||||
# --- Accept only PNG, JPEG, GIF; anything else causes one more roll ---
|
||||
$kind = Get-J621FileKind -Path $tempFile
|
||||
if ($kind -eq 'unknown') {
|
||||
if ($Depth -lt 3) {
|
||||
Show-J621Greeting -Depth ($Depth + 1)
|
||||
} else {
|
||||
Write-Host (Format-J621Error 'Max recursion depth reached, skipping unsupported file type: unknown')
|
||||
}
|
||||
if ($Depth -eq 0) {
|
||||
Write-Host (Format-J621Error ("Forked (Got unsupported type: unknown - $filename)"))
|
||||
if ($md5) { Write-Output "Link: $webBase/view/$md5" }
|
||||
}
|
||||
Remove-Item -LiteralPath $tempFile -ErrorAction SilentlyContinue
|
||||
return
|
||||
}
|
||||
|
||||
# --- Display result ---
|
||||
if ($logoType) {
|
||||
& fastfetch @ffArgs --logo-type $logoType --logo $tempFile --logo-width $logoWidth
|
||||
} else {
|
||||
& fastfetch @ffArgs
|
||||
}
|
||||
Add-Content -LiteralPath $logFile -Value "$stamp Downloaded: $jId $filename [$modeName]"
|
||||
if ($filename) {
|
||||
Write-Host (Format-J621Accent "Logo from API: $filename ($modeName, $jId)")
|
||||
Write-Output "Link: $webBase/detail/$jId"
|
||||
} else {
|
||||
Write-Host (Format-J621Accent "Logo from API ($modeName)")
|
||||
}
|
||||
|
||||
# --- Keep only the last 1000 lines of the log ---
|
||||
$lines = @(Get-Content -LiteralPath $logFile -Tail 1000)
|
||||
Set-Content -LiteralPath $logFile -Value $lines
|
||||
|
||||
# --- Cleanup ---
|
||||
Remove-Item -LiteralPath $tempFile -ErrorAction SilentlyContinue
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
# PowerShell greeting (J621 Windows port)
|
||||
|
||||
Shows a random J621 library image as your shell greeting, using fastfetch.
|
||||
This is the Windows/PowerShell port of [`../fish_greeting/`](../fish_greeting/) -
|
||||
same API, same modes, same logs, adapted to PowerShell 5.1+ and Windows
|
||||
terminals.
|
||||
|
||||
## What changed from the fish version
|
||||
|
||||
| | fish | PowerShell (this port) |
|
||||
| --- | --- | --- |
|
||||
| Entry point | `fish_greeting` function | `Show-J621Greeting` (loaded from your profile) |
|
||||
| Endpoint | `GET /api/random/?fastfetch=1&rating=X` | same |
|
||||
| Config | `~/.config/j621Greeting/config.fish` | `%APPDATA%\j621Greeting\config.ps1` |
|
||||
| Mode file | `~/.config/fish/greeting_mode` | `%APPDATA%\j621Greeting\greeting_mode` (the file `gm-switch.exe` writes) |
|
||||
| JSON parsing | `jq` / `python3` / grep | `ConvertFrom-Json` (built-in) |
|
||||
| MIME detection | `file --mime-type` | PNG/JPEG/GIF magic bytes |
|
||||
| GIFs | `gifsicle` + `kitten icat` | none: static first frame in Windows Terminal, animated in WezTerm (its `kitty-direct` plays the GIF) |
|
||||
| fastfetch logo | `--logo-type kitty --logo ... --logo-width 35` | `sixel` in Windows Terminal, `kitty-direct` in WezTerm, no image elsewhere |
|
||||
| Logs | `~/.config/j621Logos/logs.log` | `%APPDATA%\j621Logos\logs.log` |
|
||||
|
||||
## Install
|
||||
|
||||
```powershell
|
||||
cd extras\powershell_greeting
|
||||
.\install.ps1
|
||||
```
|
||||
|
||||
The installer copies `J621Greeting.ps1` into `%APPDATA%\j621Greeting\`, asks
|
||||
for your API origin (and an optional scoped token), writes
|
||||
`%APPDATA%\j621Greeting\config.ps1`, wires a marker block into your
|
||||
PowerShell profile (it replaces the previous plain-`fastfetch` hook), checks
|
||||
the tools it needs and probes the backend (`/health` plus a random roll). It
|
||||
exits non-zero when the backend cannot be reached.
|
||||
|
||||
Non-interactive / re-install:
|
||||
|
||||
```powershell
|
||||
.\install.ps1 -Url https://j621.example.ts.net -Token <api-token>
|
||||
.\install.ps1 -NoPrompt # defaults, never asks
|
||||
.\install.ps1 -Force # rewrite an existing config
|
||||
.\install.ps1 -NoProfilePatch # do not touch your PowerShell profile
|
||||
.\install.ps1 -Uninstall # remove the profile block + installed script
|
||||
```
|
||||
|
||||
Test it manually any time with:
|
||||
|
||||
```powershell
|
||||
Show-J621Greeting
|
||||
```
|
||||
|
||||
Requirements: `fastfetch` and `curl.exe` (ships with Windows 10 1803+).
|
||||
Image logos need Windows Terminal 1.22+ (sixel) or WezTerm with kitty
|
||||
graphics enabled; other terminals fall back to a plain fastfetch.
|
||||
|
||||
## No token? That is fine
|
||||
|
||||
The greeting is meant to run **without** a token: it then behaves like a
|
||||
guest of your instance - unsigned image links and only items that are
|
||||
visible to guests. Adding a token to the config unlocks signed links and
|
||||
items that are hidden from guests. The recommended value is a **scoped
|
||||
greeting token**: open the app, go to *Account -> Shell tokens* (or `/tokens`),
|
||||
create one and copy the `j621r_...` key - it only works with `/api/random/`,
|
||||
so it is safe to keep in the config file.
|
||||
|
||||
## Configuration
|
||||
|
||||
`%APPDATA%\j621Greeting\config.ps1` is read on every run (environment
|
||||
variables already set in your session win over it):
|
||||
|
||||
```powershell
|
||||
if (-not $env:J621_BASE) { $env:J621_BASE = 'https://j621.rainbow-herring.ts.net' } # API origin
|
||||
if (-not $env:J621_WEB) { $env:J621_WEB = 'https://j621.example.ts.net' } # link origin (split deploys)
|
||||
if (-not $env:J621_TOKEN) { $env:J621_TOKEN = '<api token>' } # signed URLs + hidden items
|
||||
if (-not $env:J621_FASTFETCH_CONFIG) { $env:J621_FASTFETCH_CONFIG = 'jake' } # fastfetch config name
|
||||
```
|
||||
|
||||
Extra escapes (all optional):
|
||||
|
||||
- `J621_LOGO_TYPE` - force a fastfetch logo type (`sixel`, `kitty-direct`, ...)
|
||||
- `J621_ALWAYS_FETCH=1` - fetch even in terminals without image support (prints the link, ASCII logo)
|
||||
- `J621_LOGO_WIDTH` - logo width in columns (default 35)
|
||||
- `J621_MODE_FILE` - mode file override (shared with `gm-switch`)
|
||||
|
||||
## Rating filters
|
||||
|
||||
| `greeting_mode` | Rating requested | Label |
|
||||
| --- | --- | --- |
|
||||
| `0` | `e` | NSFW |
|
||||
| `1` | `s` | SFW |
|
||||
| `2` (default) | `q` | Questionable |
|
||||
|
||||
The mode file is `%APPDATA%\j621Greeting\greeting_mode`. Use
|
||||
[`../win-gm-switch/`](../win-gm-switch/) (or its Start Menu shortcuts) to
|
||||
switch, or just edit the file.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No image logo in Windows Terminal**: WT 1.22+ is required for sixel;
|
||||
update with `winget upgrade Microsoft.WindowsTerminal`.
|
||||
- **WezTerm**: kitty graphics must be on - `config.enable_kitty_graphics = true`
|
||||
in `~/.wezterm.lua`. If the default looks wrong, try
|
||||
`J621_LOGO_TYPE=kitty`, `sixel`, or `iterm`.
|
||||
- **VS Code / other terminals**: no image protocol, so the greeting prints a
|
||||
plain fastfetch on purpose (no fetch delay). `J621_ALWAYS_FETCH=1` still
|
||||
fetches and prints the link.
|
||||
- `No logo (No image matches those filters.)` - the library has no images
|
||||
for that rating; try another mode or add files.
|
||||
- `No logo (API error)` - check `J621_BASE` and that the deployment is up
|
||||
(`https://<host>/health`).
|
||||
- `No logo (download failed)` - the signed URL expired (they last 24 h) or
|
||||
the item was deleted between the two requests; just run it again.
|
||||
- **The greeting is slow** - the API and image fetch have `--max-time`
|
||||
guards; a slow tailnet link is usually the cause.
|
||||
- Log file: `%APPDATA%\j621Logos\logs.log` (only the last 1000 lines are kept).
|
||||
|
||||
## gm-switch
|
||||
|
||||
The Rust [`../win-gm-switch/`](../win-gm-switch/) tool writes the same mode
|
||||
file, and on Linux it can replace the old C++ `gm-switch` (it writes
|
||||
`~/.config/fish/greeting_mode` there, the same file the fish greeting reads).
|
||||
@@ -0,0 +1,24 @@
|
||||
# Copy to %APPDATA%\j621Greeting\config.ps1 and adjust.
|
||||
# This file runs every time the greeting starts; an environment variable that
|
||||
# is already set in your session wins over the values here.
|
||||
|
||||
# API origin (no trailing slash needed).
|
||||
if (-not $env:J621_BASE) { $env:J621_BASE = 'https://j621.rainbow-herring.ts.net' }
|
||||
|
||||
# Web origin used in the printed link. Only needed when the SPA is served
|
||||
# from a different host than the API (split deployment).
|
||||
# if (-not $env:J621_WEB) { $env:J621_WEB = 'https://j621-frontend.rainbow-herring.ts.net' }
|
||||
|
||||
# API token. Optional: gives you signed image URLs and access to items that
|
||||
# are hidden from guests. The recommended value is a scoped greeting token
|
||||
# (Account -> Shell tokens, or /tokens in the app): those only work with
|
||||
# /api/random/, so they are safe to keep in this file. The full API token
|
||||
# works too, but grants everything.
|
||||
# if (-not $env:J621_TOKEN) { $env:J621_TOKEN = 'j621r_paste-your-token-here' }
|
||||
|
||||
# fastfetch config name used for the greeting logo.
|
||||
# if (-not $env:J621_FASTFETCH_CONFIG) { $env:J621_FASTFETCH_CONFIG = 'jake' }
|
||||
|
||||
# Force a fastfetch logo type instead of the per-terminal default
|
||||
# (Windows Terminal -> sixel, WezTerm -> kitty-direct, others -> no image).
|
||||
# if (-not $env:J621_LOGO_TYPE) { $env:J621_LOGO_TYPE = 'kitty-direct' }
|
||||
@@ -0,0 +1,211 @@
|
||||
# Install the J621 PowerShell greeting:
|
||||
# * copies J621Greeting.ps1 into %APPDATA%\j621Greeting\
|
||||
# * writes %APPDATA%\j621Greeting\config.ps1 (asks for the API origin and an
|
||||
# optional token, or takes them as parameters)
|
||||
# * wires the greeting into your PowerShell profile (marker block, once)
|
||||
# * checks the tools it needs and probes the configured backend
|
||||
#
|
||||
# Usage:
|
||||
# .\install.ps1
|
||||
# .\install.ps1 -Url https://j621.example.ts.net -Token <api-token>
|
||||
# .\install.ps1 -NoPrompt # never ask, keep/derive defaults
|
||||
# .\install.ps1 -Force # rewrite an existing config
|
||||
# .\install.ps1 -NoProfilePatch # do not touch your PowerShell profile
|
||||
# .\install.ps1 -Uninstall # remove the profile block + installed script
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Url,
|
||||
[string]$Token,
|
||||
[switch]$NoPrompt,
|
||||
[switch]$Force,
|
||||
[switch]$NoProfilePatch,
|
||||
[switch]$Uninstall
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$here = $PSScriptRoot
|
||||
if (-not $here) { $here = Split-Path -Parent $MyInvocation.MyCommand.Path }
|
||||
$configDir = Join-Path $env:APPDATA 'j621Greeting'
|
||||
$configFile = Join-Path $configDir 'config.ps1'
|
||||
$installedScript = Join-Path $configDir 'J621Greeting.ps1'
|
||||
$defaultUrl = 'https://j621.rainbow-herring.ts.net'
|
||||
$markerBegin = '# >>> J621 greeting >>>'
|
||||
$markerEnd = '# <<< J621 greeting <<<'
|
||||
$profilePath = $PROFILE
|
||||
|
||||
function Get-InstalledConfigValue {
|
||||
param([string]$Name)
|
||||
if (-not (Test-Path -LiteralPath $configFile)) { return $null }
|
||||
$content = Get-Content -LiteralPath $configFile -Raw
|
||||
$rx = '\$env:' + $Name + ' = ''([^'']*)'''
|
||||
if ($content -match $rx) { return $Matches[1] }
|
||||
return $null
|
||||
}
|
||||
|
||||
function Remove-ProfileBlock {
|
||||
if (-not (Test-Path -LiteralPath $profilePath)) { return }
|
||||
$lines = @(Get-Content -LiteralPath $profilePath)
|
||||
if (-not ($lines -contains $markerBegin)) { return }
|
||||
$out = New-Object System.Collections.Generic.List[string]
|
||||
$inside = $false
|
||||
foreach ($line in $lines) {
|
||||
if ($line.Trim() -eq $markerBegin) { $inside = $true; continue }
|
||||
if ($line.Trim() -eq $markerEnd) { $inside = $false; continue }
|
||||
if (-not $inside) { $out.Add($line) }
|
||||
}
|
||||
while ($out.Count -gt 0 -and [string]::IsNullOrWhiteSpace($out[$out.Count - 1])) { $out.RemoveAt($out.Count - 1) }
|
||||
Set-Content -LiteralPath $profilePath -Value ($out -join "`r`n") -Encoding ASCII
|
||||
}
|
||||
|
||||
# --- Uninstall -----------------------------------------------------------------
|
||||
if ($Uninstall) {
|
||||
Remove-ProfileBlock
|
||||
if (Test-Path -LiteralPath $installedScript) { Remove-Item -LiteralPath $installedScript -Force }
|
||||
Write-Host "Removed the J621 greeting from the profile and $installedScript"
|
||||
Write-Host "Your config and logs were left in place: $configDir"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Install the function ------------------------------------------------------
|
||||
New-Item -ItemType Directory -Path $configDir -Force | Out-Null
|
||||
$source = Join-Path $here 'J621Greeting.ps1'
|
||||
if (-not (Test-Path -LiteralPath $source)) { throw "J621Greeting.ps1 not found next to this installer ($source)" }
|
||||
Copy-Item -LiteralPath $source -Destination $installedScript -Force
|
||||
Write-Host "Installed $installedScript"
|
||||
|
||||
# --- Configuration -------------------------------------------------------------
|
||||
$writeConfig = $false
|
||||
if (-not (Test-Path -LiteralPath $configFile)) {
|
||||
$writeConfig = $true
|
||||
} elseif ($Force -or $PSBoundParameters.ContainsKey('Url')) {
|
||||
$writeConfig = $true
|
||||
} else {
|
||||
Write-Host "Keeping existing $configFile (use -Force to rewrite it)"
|
||||
$current = Get-InstalledConfigValue -Name 'J621_BASE'
|
||||
if ($current) { $Url = $current }
|
||||
if (-not $Token) { $Token = Get-InstalledConfigValue -Name 'J621_TOKEN' }
|
||||
}
|
||||
|
||||
if ($writeConfig) {
|
||||
if (-not $NoPrompt) {
|
||||
if (-not $Url) {
|
||||
$answer = Read-Host "API origin [$defaultUrl]"
|
||||
if ($answer) { $Url = $answer }
|
||||
}
|
||||
if (-not $Token) {
|
||||
$answer = Read-Host 'API token (optional, Enter to run as a guest)'
|
||||
if ($answer) { $Token = $answer }
|
||||
}
|
||||
}
|
||||
if (-not $Url) { $Url = $defaultUrl }
|
||||
$Url = $Url.Trim().TrimEnd('/').Replace("'", "")
|
||||
if ($Token) { $Token = $Token.Trim().Replace("'", "") }
|
||||
|
||||
$lines = New-Object System.Collections.Generic.List[string]
|
||||
$lines.Add('# J621 greeting configuration - written by install.ps1 on ' + (Get-Date -Format 'yyyy-MM-dd'))
|
||||
$lines.Add('# Values here are defaults; an environment variable already set in your')
|
||||
$lines.Add('# session wins over them.')
|
||||
$lines.Add('if (-not $env:J621_BASE) { $env:J621_BASE = ''' + $Url + ''' }')
|
||||
if ($Token) {
|
||||
$lines.Add('if (-not $env:J621_TOKEN) { $env:J621_TOKEN = ''' + $Token + ''' }')
|
||||
} else {
|
||||
$lines.Add('# if (-not $env:J621_TOKEN) { $env:J621_TOKEN = ''paste-a-token-here'' }')
|
||||
}
|
||||
$lines.Add('# if (-not $env:J621_WEB) { $env:J621_WEB = ''' + $Url + ''' }')
|
||||
$lines.Add('# if (-not $env:J621_FASTFETCH_CONFIG) { $env:J621_FASTFETCH_CONFIG = ''jake'' }')
|
||||
Set-Content -LiteralPath $configFile -Value $lines -Encoding ASCII
|
||||
Write-Host "Wrote $configFile"
|
||||
}
|
||||
|
||||
# --- Profile -------------------------------------------------------------------
|
||||
if (-not $NoProfilePatch) {
|
||||
$existing = ''
|
||||
if (Test-Path -LiteralPath $profilePath) { $existing = Get-Content -LiteralPath $profilePath -Raw }
|
||||
if ($existing -match [regex]::Escape($markerBegin)) {
|
||||
Write-Host 'Profile already sources the greeting (marker present)'
|
||||
} else {
|
||||
$normalized = $existing -replace "`r`n", "`n"
|
||||
$legacy = @(
|
||||
'# Fastfetch greeting on interactive PowerShell start',
|
||||
'if ([Environment]::UserInteractive -and -not [Console]::IsOutputRedirected) {',
|
||||
' fastfetch',
|
||||
'}'
|
||||
) -join "`n"
|
||||
if ($normalized.Contains($legacy)) {
|
||||
$normalized = $normalized.Replace($legacy, '').TrimEnd() + "`n"
|
||||
Write-Host 'Replaced the plain fastfetch block in your profile'
|
||||
}
|
||||
|
||||
$block = @(
|
||||
$markerBegin,
|
||||
'# Random J621 library image as the fastfetch logo (extras/powershell_greeting).',
|
||||
'$j621GreetingScript = "$env:APPDATA\j621Greeting\J621Greeting.ps1"',
|
||||
'if ((Test-Path -LiteralPath $j621GreetingScript) -and [Environment]::UserInteractive -and -not [Console]::IsOutputRedirected) {',
|
||||
' . $j621GreetingScript',
|
||||
' Show-J621Greeting',
|
||||
'}',
|
||||
$markerEnd
|
||||
) -join "`r`n"
|
||||
|
||||
$profileDir = Split-Path -Parent $profilePath
|
||||
if (-not (Test-Path -LiteralPath $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force | Out-Null }
|
||||
Set-Content -LiteralPath $profilePath -Value ($normalized.TrimEnd() + "`r`n`r`n" + $block + "`r`n") -Encoding ASCII
|
||||
Write-Host "Added the J621 greeting to $profilePath"
|
||||
}
|
||||
}
|
||||
|
||||
# --- Dependencies --------------------------------------------------------------
|
||||
$problems = 0
|
||||
foreach ($tool in 'fastfetch', 'curl.exe') {
|
||||
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) {
|
||||
$problems = 1
|
||||
Write-Host "Missing required tool: $tool"
|
||||
}
|
||||
}
|
||||
if (-not ($env:WT_SESSION -or $env:WEZTERM_PANE)) {
|
||||
Write-Host 'Note: image logos need Windows Terminal 1.22+ (sixel) or WezTerm (kitty graphics);'
|
||||
Write-Host 'other terminals fall back to a plain fastfetch.'
|
||||
}
|
||||
|
||||
# --- Probe the configured backend -----------------------------------------------
|
||||
Write-Host ''
|
||||
$probeUrl = $Url
|
||||
$probeToken = $Token
|
||||
if (-not $probeUrl) { $probeUrl = $defaultUrl }
|
||||
Write-Host "Checking $probeUrl ..."
|
||||
$health = & curl.exe -s -m 10 ($probeUrl + '/health') 2>$null | Out-String
|
||||
if ($health -match '"status"\s*:\s*"ok"') {
|
||||
Write-Host ' backend: ok'
|
||||
$probeArgs = @('-s', '-m', '10')
|
||||
if ($probeToken) { $probeArgs += @('-H', "Authorization: Token $probeToken") }
|
||||
$probe = & curl.exe @probeArgs ($probeUrl + '/api/random/?fastfetch=1') 2>$null | Out-String
|
||||
$detail = $null
|
||||
try {
|
||||
$parsed = $probe | ConvertFrom-Json
|
||||
if ($parsed.detail) { $detail = [string]$parsed.detail }
|
||||
} catch { }
|
||||
if ($detail) {
|
||||
Write-Host " random: $detail"
|
||||
if (-not $probeToken) { Write-Host ' (a token would also let you see items that are hidden from guests)' }
|
||||
} else {
|
||||
Write-Host ' random: ok'
|
||||
}
|
||||
} else {
|
||||
$problems = 1
|
||||
Write-Host " backend did not answer at $probeUrl/health"
|
||||
Write-Host " got: $health"
|
||||
Write-Host " Fix J621_BASE in $configFile (or pass -Url) and run again."
|
||||
}
|
||||
|
||||
# --- Wrap up --------------------------------------------------------------------
|
||||
Write-Host ''
|
||||
if ($problems -eq 0) {
|
||||
Write-Host 'Done. Test it now with: Show-J621Greeting'
|
||||
} else {
|
||||
Write-Host 'Finished with problems above; the greeting will report them too.'
|
||||
}
|
||||
Write-Host ('Rating mode lives in ' + (Join-Path $configDir 'greeting_mode') + ' (0=NSFW, 1=SFW, 2=Questionable).')
|
||||
Write-Host 'gm-switch.exe and its Start Menu shortcuts write that same file.'
|
||||
exit $problems
|
||||
Reference in New Issue
Block a user