feat: entertainment hardening — notify_send delivery, plugin lib, last-run state, config edit; shared user-timers lib
This commit is contained in:
@@ -10,19 +10,19 @@
|
||||
|
||||
<!-- GEN:START docmap -->
|
||||
| ## 1. Project Overview | 28–43 |
|
||||
| ## 2. Directory Structure | 44–189 |
|
||||
| ## 3. Installation Flow | 190–241 |
|
||||
| ## 4. The `pos` CLI System | 242–311 |
|
||||
| ## 5. Shared Library — `lib/common.sh` | 312–343 |
|
||||
| ## 6. Docker Compose / ScaleTail | 344–386 |
|
||||
| ## 7. Optional Apps (`apps/`) | 387–416 |
|
||||
| ## 8. Entertainment Module | 417–430 |
|
||||
| ## 9. Systemd Services | 431–442 |
|
||||
| ## 10. Configuration Files | 443–469 |
|
||||
| ## 11. Coding Conventions | 470–502 |
|
||||
| ## 12. Development Workflow | 503–555 |
|
||||
| ## 13. Key File Quick Reference | 556–613 |
|
||||
| ## 14. Common Tasks for Agents | 614–643 |
|
||||
| ## 2. Directory Structure | 44–192 |
|
||||
| ## 3. Installation Flow | 193–244 |
|
||||
| ## 4. The `pos` CLI System | 245–314 |
|
||||
| ## 5. Shared Library — `lib/common.sh` | 315–346 |
|
||||
| ## 6. Docker Compose / ScaleTail | 347–389 |
|
||||
| ## 7. Optional Apps (`apps/`) | 390–419 |
|
||||
| ## 8. Entertainment Module | 420–433 |
|
||||
| ## 9. Systemd Services | 434–445 |
|
||||
| ## 10. Configuration Files | 446–472 |
|
||||
| ## 11. Coding Conventions | 473–505 |
|
||||
| ## 12. Development Workflow | 506–558 |
|
||||
| ## 13. Key File Quick Reference | 559–618 |
|
||||
| ## 14. Common Tasks for Agents | 619–648 |
|
||||
<!-- GEN:END docmap -->
|
||||
|
||||
## 1. Project Overview
|
||||
@@ -53,7 +53,10 @@ Linux_post_install/
|
||||
│ ├── common.sh # Shared library (colors, logging, spinner, timer, run, load_system_env)
|
||||
│ ├── flags.sh # Feature flag store (flag_set/clear/is_set/value/list/status)
|
||||
│ ├── notify.sh # Multi-platform alerting (notify_send) — sourced opt-in, silent-fails
|
||||
│ └── entertainment-lib.sh # Entertainment module lib (ENABLED list, scheduler sync)
|
||||
│ ├── entertainment-lib.sh # Entertainment module lib (ENABLED list, last-run state, scheduler sync)
|
||||
│ ├── entertainment-plugin-lib.sh # Message-safe helpers for plugins (config load, require, fetch+retry)
|
||||
│ ├── scheduler-lib.sh # System scheduler lib (job parsing, notify policies, per-job user timers)
|
||||
│ └── user-timers-lib.sh # Shared systemd **user** timer machinery (interval→OnCalendar, unit pair, linger)
|
||||
│
|
||||
├── bin/ # CLI tools — installed to /usr/local/bin/
|
||||
│ ├── pos # Main dispatcher — smart arg matching to pos-* scripts
|
||||
@@ -71,7 +74,7 @@ Linux_post_install/
|
||||
│ ├── pos-entertainment-config # Show or edit the entertainment config (ENABLED auto-trigger list, weather location)
|
||||
│ ├── pos-entertainment-disable # Disable a plugin's auto-trigger (remove it from ENABLED)
|
||||
│ ├── pos-entertainment-enable # Enable an auto-trigger for a plugin on a schedule
|
||||
│ ├── pos-entertainment-send # Run a public-API plugin and send its output via Telegram (default sender)
|
||||
│ ├── pos-entertainment-send # Run a public-API plugin and send its output via the configured notify platforms
|
||||
│ ├── pos-entertainment-status # Show enabled plugins and scheduler state
|
||||
│ ├── pos-media-mp3 # Download audio as MP3 (yt-dlp)
|
||||
│ ├── pos-media-mp4 # Download video as MP4 (smart/interactive format select)
|
||||
@@ -271,7 +274,7 @@ All non-interactive `pos` commands log output to `~/.local/share/linux_post_inst
|
||||
| entertainment | config | `pos-entertainment-config` | Show or edit the entertainment config (ENABLED auto-trigger list, weather location) |
|
||||
| entertainment | disable | `pos-entertainment-disable` | Disable a plugin's auto-trigger (remove it from ENABLED) |
|
||||
| entertainment | enable | `pos-entertainment-enable` | Enable an auto-trigger for a plugin on a schedule |
|
||||
| entertainment | send | `pos-entertainment-send` | Run a public-API plugin and send its output via Telegram (default sender) |
|
||||
| entertainment | send | `pos-entertainment-send` | Run a public-API plugin and send its output via the configured notify platforms |
|
||||
| entertainment | status | `pos-entertainment-status` | Show enabled plugins and scheduler state |
|
||||
| media | mp3 | `pos-media-mp3` | Download audio as MP3 (yt-dlp) |
|
||||
| media | mp4 | `pos-media-mp4` | Download video as MP4 (smart/interactive format select) |
|
||||
@@ -416,13 +419,13 @@ ScaleTail provides 119+ Docker Compose templates with a Tailscale sidecar patter
|
||||
|
||||
## 8. Entertainment Module
|
||||
|
||||
Public-API "entertainment" plugins (weather, joke, gold) that can auto-send their output to Telegram on a schedule.
|
||||
Public-API "entertainment" plugins (weather, joke, gold) that can auto-send their output to the configured notify platforms (default Telegram) on a schedule.
|
||||
|
||||
- **CLI:** `pos entertainment {config|enable|disable|send|status}` — see the dispatch table in §4 and POS.md [entertainment](#entertainment).
|
||||
- **Library:** `lib/entertainment-lib.sh` — config-file helpers, ENABLED-list parsing, plugin lookup, and scheduler sync (systemd user timers, crontab fallback).
|
||||
- **Plugins:** `entertainment/*.sh` — standalone scripts that fetch a public API and **print the message to stdout** (what gets sent). Each declares its name with a `# POS_PLUGIN: <name>` header; a new plugin is auto-discovered.
|
||||
- **Library:** `lib/entertainment-lib.sh` — config-file helpers, ENABLED-list parsing, plugin lookup, per-plugin last-run state, and scheduler sync. Timer machinery (interval→OnCalendar, unit pair writer, linger) is shared via `lib/user-timers-lib.sh` with the system scheduler.
|
||||
- **Plugins:** `entertainment/*.sh` — standalone scripts that fetch a public API and **print the message to stdout** (what gets sent). Each declares its name with a `# POS_PLUGIN: <name>` header; a new plugin is auto-discovered. Plugins may source `lib/entertainment-plugin-lib.sh` (message-safe: no stdout chatter).
|
||||
- **Config:** `~/.config/linux_post_install/entertainment.env` (ENABLED auto-trigger list, weather location). Template: `config/entertainment.env`, auto-installed by postinstall.
|
||||
- **Sending:** `pos entertainment send <plugin> [--print] [--markdown]` runs the plugin and delivers via `pos communication telegram sender send`.
|
||||
- **Sending:** `pos entertainment send <plugin> [--print] [--markdown]` runs the plugin and delivers via `notify_send` (follows `NOTIFY_PLATFORM`, default Telegram). Last-run rc/timestamp is recorded per plugin and shown by `status`; a timer-context failure also notifies.
|
||||
- **Auto-trigger:** `pos entertainment enable <plugin> <interval>` writes the plugin into ENABLED and syncs a systemd user timer (allowed intervals: `5m 10m 15m 30m 45m hourly 2h 6h 12h daily weekly`, or `OnCalendar=…`); `disable` removes it.
|
||||
- **Docs:** DEV.md "Adding an Entertainment Plugin" (§1 step list) and POS.md [entertainment](#entertainment).
|
||||
|
||||
@@ -563,8 +566,10 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
||||
| `lib/common.sh` | 144 | Shared library (log/warn/err/run/spawn, dry-run aware, `load_system_env`) |
|
||||
| `lib/flags.sh` | 60 | Feature flag store (set/clear/is_set/value/list/status) |
|
||||
| `lib/notify.sh` | 76 | Multi-platform alerting (`notify_send`) — opt-in source, silent-fails |
|
||||
| `lib/entertainment-lib.sh` | 354 | Entertainment module lib (ENABLED parsing, scheduler sync) |
|
||||
| `lib/scheduler-lib.sh` | 830 | Scheduler lib (job parsing, notify policies, per-job user timers, legacy migrate) |
|
||||
| `lib/entertainment-lib.sh` | 311 | Entertainment module lib (ENABLED parsing, last-run state, scheduler sync via user-timers-lib) |
|
||||
| `lib/entertainment-plugin-lib.sh` | 67 | Message-safe helpers for plugins (config load, require, fetch+retry) — plugins MAY source it |
|
||||
| `lib/scheduler-lib.sh` | 760 | Scheduler lib (job parsing, notify policies, per-job user timers via user-timers-lib, legacy migrate) |
|
||||
| `lib/user-timers-lib.sh` | 112 | Shared systemd **user** timer machinery (interval→OnCalendar, unit pair writer, linger) |
|
||||
| `bin/flag-reader` | 58 | Inspect flags (list/status/`--raw`) |
|
||||
| `bin/flag-set` | 21 | Set a flag (optionally with a value) |
|
||||
| `bin/flag-clear` | 21 | Unset a flag |
|
||||
@@ -582,11 +587,11 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
||||
| `bin/pos-docker-health` | 110 | One-glance container health dashboard (exits 1 if unhealthy) |
|
||||
| `bin/pos-docker-ps` | 128 | Enhanced container overview (health, IPs, ports, uptime) |
|
||||
| `bin/pos-docker-vbox` | 158 | Disposable Docker-based VMs (create/enter/start/stop/rm/ls) |
|
||||
| `bin/pos-entertainment-config` | 99 | Show or edit the entertainment config (ENABLED auto-trigger list, weather location) |
|
||||
| `bin/pos-entertainment-config` | 143 | Show or edit the entertainment config (ENABLED auto-trigger list, weather location) |
|
||||
| `bin/pos-entertainment-disable` | 32 | Disable a plugin's auto-trigger (remove it from ENABLED) |
|
||||
| `bin/pos-entertainment-enable` | 49 | Enable an auto-trigger for a plugin on a schedule |
|
||||
| `bin/pos-entertainment-send` | 93 | Run a public-API plugin and send its output via Telegram (default sender) |
|
||||
| `bin/pos-entertainment-status` | 49 | Show enabled plugins and scheduler state |
|
||||
| `bin/pos-entertainment-send` | 95 | Run a public-API plugin and send its output via the configured notify platforms |
|
||||
| `bin/pos-entertainment-status` | 62 | Show enabled plugins and scheduler state |
|
||||
| `bin/pos-media-mp3` | 80 | Download audio as MP3 (yt-dlp) |
|
||||
| `bin/pos-media-mp4` | 126 | Download video as MP4 (smart/interactive format select) |
|
||||
| `bin/pos-network-checkport` | 496 | Check TCP/UDP port reachability (nmap, or bash/nc fallback) + local interface view |
|
||||
|
||||
+18
-12
@@ -20,7 +20,7 @@ How this repo works, how to add features, and what to keep in mind when editing.
|
||||
| Phase | Script | Responsibility |
|
||||
|-------|--------|----------------|
|
||||
| Pre | `preinstall.sh` | System packages, apt repos, global binaries (yt-dlp) |
|
||||
| Install | `install.sh` | Copies `bin/*` → `/usr/local/bin/` (chmod 755), `lib/common.sh` + `lib/flags.sh` + `lib/entertainment-lib.sh` → `/usr/local/bin/` (chmod 644) |
|
||||
| Install | `install.sh` | Copies `bin/*` → `/usr/local/bin/` (chmod 755), all `lib/*.sh` → `/usr/local/bin/` (chmod 644) |
|
||||
| Post | `postinstall.sh` | User config (SSH keys, PATH, bash completion), systemd services |
|
||||
|
||||
Each phase is independent and runs only if the corresponding script exists.
|
||||
@@ -31,7 +31,7 @@ Each phase is independent and runs only if the corresponding script exists.
|
||||
|-----------|---------|-------------|
|
||||
| `bin/` | Daily-use CLI tools and wrappers | `/usr/local/bin/` |
|
||||
| `apps/<category>/` | Optional desktop app installers | run on demand |
|
||||
| `lib/` | Shared libraries: `common.sh` (helpers), `flags.sh` (feature flags), `notify.sh` (multi-platform alerting), `entertainment-lib.sh` (entertainment scheduling) | sourced at build time |
|
||||
| `lib/` | Shared libraries: `common.sh` (helpers), `flags.sh` (feature flags), `notify.sh` (multi-platform alerting), `entertainment-lib.sh` (entertainment scheduling + last-run state), `entertainment-plugin-lib.sh` (message-safe plugin helpers), `scheduler-lib.sh` (system scheduler), `user-timers-lib.sh` (shared systemd user timer machinery), `config-ui.sh` (interactive config UI) | sourced at build time |
|
||||
| `config/` | Gitignored user config files | `~/.config/<app>/` (via postinstall) |
|
||||
| `entertainment/` | Public-API plugins for the entertainment module | `/usr/local/bin` (via install.sh Phase 2) |
|
||||
| `compose/` | ScaleTail templates (git submodule) | `/usr/local/share/linux_post_install/scale-tail` |
|
||||
@@ -203,11 +203,13 @@ Stub harnesses are **throwaway by design**: no `tests/` dir and no CI in this re
|
||||
|
||||
## Adding an Entertainment Plugin
|
||||
|
||||
The `entertainment` module routes public-API data to Telegram via the single runner `pos entertainment send <plugin>` (`bin/pos-entertainment-send`). Auto-triggering is config-driven: `ENABLED` in `entertainment.env` holds `plugin, interval` pairs; the tools `pos entertainment config|enable|disable|status` (`bin/pos-entertainment-*`) reconcile the schedule. All shared logic (ENABLED parsing, interval→schedule mapping, scheduler sync) lives in `lib/entertainment-lib.sh` — sourced by the `pos-entertainment-*` tools (never by plugins). The scheduler is **systemd user timers** — the only backend (requires a reachable user systemd manager).
|
||||
The `entertainment` module routes public-API data to the configured notify platforms via the single runner `pos entertainment send <plugin>` (`bin/pos-entertainment-send`). Auto-triggering is config-driven: `ENABLED` in `entertainment.env` holds `plugin, interval` pairs; the tools `pos entertainment config|enable|disable|status` (`bin/pos-entertainment-*`) reconcile the schedule. Shared logic (ENABLED parsing, plugin lookup, last-run state, scheduler sync) lives in `lib/entertainment-lib.sh` — sourced by the `pos-entertainment-*` tools (never by plugins). The scheduler is **systemd user timers** — the only backend (requires a reachable user systemd manager); the timer machinery itself is shared with the system scheduler via `lib/user-timers-lib.sh`.
|
||||
|
||||
### 1. Create the plugin
|
||||
|
||||
Drop an executable script in `entertainment/<name>.sh` with a `# POS_PLUGIN: <name>` marker on line 3 (this is what makes it a plugin — the installed runner lists plugins by this marker, not by `.sh` files, since `/usr/local/bin` is shared with other tooling). Declare every config key the plugin reads with `# POS_KEYS: <KEY> <description> (required|optional)` lines right after it — `pos entertainment config` prints these in its Keys section and uses them to warn/not-warn on `config set`:
|
||||
Drop an executable script in `entertainment/<name>.sh` with a `# POS_PLUGIN: <name>` marker (this is what makes it a plugin — the installed runner lists plugins by this marker, not by `.sh` files, since `/usr/local/bin` is shared with other tooling). Declare every config key the plugin reads with `# POS_KEYS: <KEY> <description> (required|optional)` lines right after it — `pos entertainment config` prints these in its Keys section and uses them to warn/not-warn on `config set`.
|
||||
|
||||
Plugins may source `lib/entertainment-plugin-lib.sh` — message-safe helpers (config load, dep guards, JSON fetch with retry) that never write to stdout. Template:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
@@ -215,23 +217,27 @@ set -euo pipefail
|
||||
# POS_PLUGIN: myplugin
|
||||
# POS_KEYS: MYPLUGIN_URL <feed url> (required)
|
||||
# POS_KEYS: MYPLUGIN_TAG <filter tag> (optional)
|
||||
err() { echo "ERROR: $*" >&2; exit 1; }
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/../lib/entertainment-plugin-lib.sh" 2>/dev/null \
|
||||
|| source "$(dirname "${BASH_SOURCE[0]}")/entertainment-plugin-lib.sh" 2>/dev/null \
|
||||
|| source "$(dirname "$0")/../lib/entertainment-plugin-lib.sh" 2>/dev/null \
|
||||
|| source "$(dirname "$0")/entertainment-plugin-lib.sh"
|
||||
|
||||
command -v curl &>/dev/null || err "curl not found"
|
||||
|
||||
data="$(curl -fsS --max-time 20 https://api.example.com/foo)"
|
||||
printf 'Title: %s\n' "$data"
|
||||
plugin_load_config
|
||||
plugin_require MYPLUGIN_URL
|
||||
tag="${MYPLUGIN_TAG:-}"
|
||||
json="$(plugin_http_json --key '.title' "${MYPLUGIN_URL}${tag:+/?tag=$tag}")"
|
||||
printf 'Title: %s\n' "$json"
|
||||
```
|
||||
|
||||
**Contract:** plugins are **self-contained** — do **not** source `lib/common.sh`. Its `log`/`warn`/`ok` helpers print to **stdout**, and the runner captures stdout as the message to send (helper chatter would be sent to Telegram). All stdout is the message; errors go to stderr and exit nonzero. Plugins must be non-interactive (no prompts) — the module is designed for systemd user timers.
|
||||
**Contract:** plugins are **self-contained** — do **not** source `lib/common.sh` or `lib/entertainment-lib.sh`. Their `log`/`warn`/`ok` helpers print to **stdout**, and the runner captures stdout as the message to send (helper chatter would be sent to the notify platforms). All stdout is the message; errors go to stderr and exit nonzero. `lib/entertainment-plugin-lib.sh` is the *only* lib plugins may source (it defines just `plugin_*` and writes nothing to stdout). Plugins must be non-interactive (no prompts) — the module is designed for systemd user timers.
|
||||
|
||||
### 2. Config (if needed)
|
||||
|
||||
Read runtime values from `~/.config/linux_post_install/entertainment.env` (chmod 600, env precedence) — same pattern as `telegram.env`. Example: `weather.sh` uses `WEATHER_LAT`/`WEATHER_LON`. Declare each key with a `# POS_KEYS:` header line (see step 1) so `pos entertainment config` lists it and `config set` recognizes it.
|
||||
Read runtime values from `~/.config/linux_post_install/entertainment.env` (chmod 600, env precedence) via `plugin_load_config` — same pattern as `telegram.env`. Example: `weather.sh` uses `WEATHER_LAT`/`WEATHER_LON`. Declare each key with a `# POS_KEYS:` header line (see step 1) so `pos entertainment config` lists it and `config set` recognizes it.
|
||||
|
||||
### 3. Deps
|
||||
|
||||
`curl` and `jq` are already in `preinstall.sh` PACKAGES. Anything else: guard with `command -v … || err "…"` and, if apt-available, add to PACKAGES.
|
||||
`curl` and `jq` are already in `preinstall.sh` PACKAGES (and are what `plugin_have curl`/`plugin_have jq` check). Anything else: guard with `plugin_have <cmd>` and, if apt-available, add to PACKAGES.
|
||||
|
||||
### 4. Done
|
||||
|
||||
|
||||
+12
-7
@@ -325,19 +325,23 @@ The daemon long-polls `/sync` (30s timeout, per-sync `since` token, compact filt
|
||||
### entertainment
|
||||
|
||||
**File:** `bin/pos-entertainment-send`
|
||||
**Purpose:** run a public-API plugin and send its output to Telegram by default. Plugins are standalone scripts in `entertainment/` that fetch a public API and **print the message to stdout** — that stdout is what gets sent.
|
||||
**Purpose:** run a public-API plugin and send its output via `notify_send` — the platform follows `NOTIFY_PLATFORM` (default Telegram, silent-fail when none configured). Plugins are standalone scripts in `entertainment/` that fetch a public API and **print the message to stdout** — that stdout is what gets sent.
|
||||
|
||||
| Command | Behavior |
|
||||
|---------|----------|
|
||||
| `pos entertainment send` | List available plugins + usage |
|
||||
| `pos entertainment send <plugin> [--print] [--markdown] [args…]` | Run the plugin, send its output to Telegram (silent) |
|
||||
| `pos entertainment send <plugin> [--print] [--markdown] [args…]` | Run the plugin, send its output via `notify_send` (silent) |
|
||||
| `pos entertainment send <plugin> --print` | Print the output locally; do not send |
|
||||
| `pos entertainment send <plugin> --markdown` | Send with `--parse-mode markdown` (via `pos communication telegram sender`) |
|
||||
| `pos entertainment send <plugin> --markdown` | Send with Markdown parse_mode (via the notify senders) |
|
||||
| `pos entertainment config` | Show the config file (`~/.config/linux_post_install/entertainment.env`) |
|
||||
| `pos entertainment config get KEY` | Print one key's current value (`(not set)` if absent) |
|
||||
| `pos entertainment config set KEY=VALUE…` | Set keys (any UPPER_SNAKE key; warns if no installed plugin uses it) and re-sync the schedule |
|
||||
| `pos entertainment config unset KEY` | Remove a key from the config file |
|
||||
| `pos entertainment config ls` | Declared keys with their current values, aligned |
|
||||
| `pos entertainment config edit` | Interactive editor for the scope (via `pos config` UI) |
|
||||
| `pos entertainment enable <plugin> [interval]` | Add plugin to `ENABLED` + schedule it as a systemd user timer |
|
||||
| `pos entertainment disable <plugin>` | Remove plugin from `ENABLED` + remove its scheduled job |
|
||||
| `pos entertainment status` | Enabled plugins + scheduler + schedule state |
|
||||
| `pos entertainment status` | Enabled plugins (each with interval + last run), installed-but-not-enabled plugins, scheduler + timers |
|
||||
|
||||
**Plugin lookup order:** `$ENTERTAINMENT_DIR` → repo `entertainment/` → `/usr/local/bin/` (installed by `install.sh` Phase 2, beside the runner). A plugin name matches the file name with or without the `.sh` suffix.
|
||||
|
||||
@@ -351,7 +355,7 @@ Plugins:
|
||||
|
||||
**Config auto-install:** `postinstall.sh` copies the repo's `config/entertainment.env` (a commented template showing each key's syntax) to `~/.config/linux_post_install/entertainment.env` on install — but only if you haven't already created your own (no clobber), and prints the template so you can fill in your location. Fill in `WEATHER_LAT`/`WEATHER_LON` (and optionally `WEATHER_CITY`) to enable the weather plugin.
|
||||
|
||||
**Adding a plugin:** drop an executable script in `entertainment/` (e.g. `myfeed.sh`) with a `# POS_PLUGIN: <name>` marker on line 3 — the runner lists and validates plugins by this marker, so non-plugin `.sh` files in the shared `/usr/local/bin` are ignored. The plugin must be non-interactive and print the message to stdout; errors go to stderr (exit nonzero). If it needs coordinates/tokens, read them from `~/.config/linux_post_install/entertainment.env` (chmod 600, env precedence). No registration needed. Dependencies beyond `curl`/`jq` (both in `preinstall.sh` PACKAGES) should be guarded with `command -v … || exit 1`.
|
||||
**Adding a plugin:** drop an executable script in `entertainment/` (e.g. `myfeed.sh`) with a `# POS_PLUGIN: <name>` marker — the runner lists and validates plugins by this marker, so non-plugin `.sh` files in the shared `/usr/local/bin` are ignored. The plugin must be non-interactive and print the message to stdout; errors go to stderr (exit nonzero). Source `lib/entertainment-plugin-lib.sh` for the standard helpers — `plugin_load_config` (reads `entertainment.env`, env precedence), `plugin_have <cmd>`, `plugin_require KEY <desc>`, `plugin_http_json <url> [--key <jq>] [-H <header>]` (curl, 2 retries, timeout) — it never writes to stdout, so the message stays clean. No registration needed. Dependencies beyond `curl`/`jq` (both in `preinstall.sh` PACKAGES) should be guarded with `plugin_have`.
|
||||
|
||||
**Declaring config keys (pattern):** document every key the plugin reads with one `# POS_KEYS:` line right after `# POS_PLUGIN:` — `KEY`, a description, and `(required)`/`(optional)`:
|
||||
|
||||
@@ -373,15 +377,16 @@ ENABLED="weather, 5m gold, 1h joke, daily"
|
||||
|
||||
- One **user timer** per enabled plugin (`~/.config/systemd/user/pos-entertainment-<plugin>.{service,timer}`), running `pos entertainment send <plugin>` as your user on that schedule (`OnCalendar` + `Persistent=true`). `pos entertainment enable` also tries `sudo loginctl enable-linger $USER` once so timers fire without login.
|
||||
|
||||
The job runs as you, so it reads your `$HOME` configs (weather location, Telegram token) natively — no `Environment=HOME=` hacks.
|
||||
The job runs as you, so it reads your `$HOME` configs (weather location, notify platform) natively — no `Environment=HOME=` hacks.
|
||||
|
||||
Intervals: `5m 10m 15m 30m 45m hourly 2h 6h 12h daily weekly`, or a raw `OnCalendar=…` spec. Default when omitted: `daily`.
|
||||
|
||||
`pos entertainment status` shows the enabled plugins, the scheduler, and each plugin's interval + next fire time (`systemctl --user list-timers`).
|
||||
`pos entertainment status` shows the enabled plugins (each with interval + last run), the installed-but-not-enabled plugins, the scheduler, and each plugin's next fire time (`systemctl --user list-timers`). Last run is recorded by `pos entertainment send` on every non-`--print` run (`~/.local/share/linux_post_install/entertainment/last/<plugin>`); a run that fails while fired by a timer also notifies the configured platforms.
|
||||
|
||||
Notes:
|
||||
- The runner is headless/timer-friendly — no TTY prompts, exit 0 on success / 1 on failure.
|
||||
- Scheduling is per-user for the user who runs `enable`; if you manage a different machine's user (e.g. via `runuser`/`sudo -u`), run the `enable`/`disable` commands as that user.
|
||||
- The unit template (`TimeoutStopSec=5s`, `Persistent=true`, network-online deps) is shared with the system scheduler via `lib/user-timers-lib.sh`.
|
||||
|
||||
### flags
|
||||
|
||||
|
||||
+10
-3
@@ -38,7 +38,7 @@ The phases:
|
||||
| # | Phase | Script/action |
|
||||
|---|-------|----------------|
|
||||
| 1 | preinstall | `preinstall.sh` — apt packages + yt-dlp |
|
||||
| 2 | scripts | Copies `bin/*` → `/usr/local/bin/` (755), `lib/common.sh` + `lib/flags.sh` + `lib/notify.sh` + `lib/entertainment-lib.sh` → `/usr/local/bin/` (644). Copies precompiled arch binaries from `x64_bin/` (or `arm64_bin/`) → `/usr/local/bin/`. With `--feature`: also installs `features/*` (see below) |
|
||||
| 2 | scripts | Copies `bin/*` → `/usr/local/bin/` (755), `lib/common.sh` + `lib/flags.sh` + `lib/notify.sh` + `lib/entertainment-lib.sh` + `lib/entertainment-plugin-lib.sh` + `lib/scheduler-lib.sh` + `lib/config-ui.sh` + `lib/user-timers-lib.sh` → `/usr/local/bin/` (644). Copies precompiled arch binaries from `x64_bin/` (or `arm64_bin/`) → `/usr/local/bin/`. With `--feature`: also installs `features/*` (see below) |
|
||||
| 3 | postinstall | `postinstall.sh` — PATH, completion, SSH keys, systemd |
|
||||
| 4 | scalepoint | Shallow-clones ScaleTail templates to `/usr/local/share/linux_post_install/scale-tail` |
|
||||
| 5 (opt) | apps | `apps/install.sh` when `--apps` (interactive) or `--full` (all, non-interactive) |
|
||||
@@ -194,9 +194,16 @@ Platform selection: `~/.config/linux_post_install/notify.env` (`NOTIFY_PLATFORM=
|
||||
## lib/entertainment-lib.sh — entertainment module
|
||||
|
||||
**File:** `lib/entertainment-lib.sh` (installed to `/usr/local/bin/entertainment-lib.sh`)
|
||||
**Purpose:** shared logic for the `pos entertainment` tools — config (`entertainment.env`), `ENABLED` auto-trigger list parsing (`plugin, interval` pairs), plugin lookup by `# POS_PLUGIN:` marker, interval→schedule mapping, and scheduler reconciliation (systemd **user** timers — the only backend; requires a reachable user manager, `ensure_linger()` enables linger if needed).
|
||||
**Purpose:** shared logic for the `pos entertainment` tools — config (`entertainment.env`), `ENABLED` auto-trigger list parsing (`plugin, interval` pairs), plugin lookup by `# POS_PLUGIN:` marker, per-plugin last-run state (`~/.local/share/linux_post_install/entertainment/last/<plugin>`), and scheduler reconciliation. Timer machinery (interval→OnCalendar, unit pair writer, linger) comes from `lib/user-timers-lib.sh`, shared with the system scheduler.
|
||||
|
||||
Sourced by `bin/pos-entertainment-send|config|enable|disable|status` (after `lib/common.sh`). **Plugins must not source it** — their stdout is the sent message.
|
||||
Sourced by `bin/pos-entertainment-send|config|enable|disable|status` (after `lib/common.sh`). **Plugins must not source it** — their stdout is the sent message; they may instead source `lib/entertainment-plugin-lib.sh` (message-safe helpers: config load, `plugin_have`/`plugin_require`, `plugin_http_json` with retry).
|
||||
|
||||
---
|
||||
|
||||
## lib/user-timers-lib.sh — shared systemd user timers
|
||||
|
||||
**File:** `lib/user-timers-lib.sh` (installed to `/usr/local/bin/user-timers-lib.sh`)
|
||||
**Purpose:** the one copy of the systemd **user** timer machinery used by both the entertainment module and the system scheduler — `ut_interval_to_oncalendar` (interval→`OnCalendar`, incl. raw `OnCalendar=…` passthrough), `ut_interval_label`, `ut_unit_name`, `ut_write_unit_pair` (oneshot service + `Persistent=true` timer, `TimeoutStopSec=5s`, `network-online` deps), and `ut_ensure_linger`. Sourced by `lib/entertainment-lib.sh` and `lib/scheduler-lib.sh`; defines only `ut_*` so it never collides with either.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+20
-10
@@ -8,8 +8,8 @@ Tools: `config`, `enable`, `disable`, `send`, `status`.
|
||||
| `pos entertainment send` | Fetch a plugin's message to stdout (dry-run) |
|
||||
| `pos entertainment enable <plugin> [interval]` | Schedule auto-sends |
|
||||
| `pos entertainment disable <plugin>` | Remove the schedule |
|
||||
| `pos entertainment status` | List plugins + their active schedules |
|
||||
| `pos entertainment config set|get` | Plugin keys in `entertainment.env` |
|
||||
| `pos entertainment status` | List plugins + active schedules + last run |
|
||||
| `pos entertainment config set|get|unset|ls|edit` | Plugin keys in `entertainment.env` |
|
||||
|
||||
### Plugins
|
||||
|
||||
@@ -19,8 +19,8 @@ Tools: `config`, `enable`, `disable`, `send`, `status`.
|
||||
| `joke` | icanhazdadjoke (no key) | — |
|
||||
| `gold` | goldprice.dev (no key) | — |
|
||||
|
||||
Interval (systemd time): `5m`, `10m`, `30m`, `1h`, `2h`, `6h`, `12h`, `daily`,
|
||||
`weekly`.
|
||||
Interval (systemd time): `5m`, `10m`, `15m`, `30m`, `45m`, `1h`, `2h`, `6h`,
|
||||
`12h`, `daily`, `weekly`.
|
||||
|
||||
---
|
||||
|
||||
@@ -28,6 +28,7 @@ Interval (systemd time): `5m`, `10m`, `30m`, `1h`, `2h`, `6h`, `12h`, `daily`,
|
||||
|
||||
```bash
|
||||
pos entertainment config set WEATHER_LAT=36.51 WEATHER_LON=40.75 WEATHER_CITY="Berlin"
|
||||
pos entertainment config get WEATHER_CITY
|
||||
|
||||
pos entertainment send weather # stdout test — message for Telegram
|
||||
pos entertainment send joke
|
||||
@@ -37,38 +38,47 @@ pos entertainment enable weather daily # once a day
|
||||
pos entertainment enable joke 2h # every 2 hours
|
||||
pos entertainment disable gold
|
||||
|
||||
pos entertainment status # plugins + active schedules
|
||||
pos entertainment status # plugins + schedules + last run
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
- **Scheduling** uses systemd **user** timers only (unit
|
||||
`pos-entertainment-<plugin>.timer` in `~/.config/systemd/user/`). Interval is
|
||||
resolved through the same systemd-time parser used by `.timer` units —
|
||||
invalid values are rejected with a clear message.
|
||||
resolved through the same `OnCalendar` mapper the system scheduler uses
|
||||
(`lib/user-timers-lib.sh`) — invalid values are rejected with a clear message.
|
||||
- **Delivery** goes through `notify_send`, so the platform follows
|
||||
`NOTIFY_PLATFORM` (default Telegram). If no platform is configured, enable
|
||||
still works — the plugin fetches and tries to notify, silently no-ops when
|
||||
unconfigured.
|
||||
- **Last run** — every send (except `--print`) records the plugin's exit code
|
||||
and timestamp to `~/.local/share/linux_post_install/entertainment/last/`;
|
||||
`pos entertainment status` shows it per plugin, and a failed timer run also
|
||||
notifies the configured platforms.
|
||||
- **Interactive editing** — `pos entertainment config edit` opens the same
|
||||
numbered-menu editor as `pos config` (already registered for `pos
|
||||
entertainment`).
|
||||
|
||||
**Recipes:**
|
||||
- **Morning weather + joke:** `enable weather daily`, `enable joke daily`; the
|
||||
08:00 health digest plus these make a nice wake-up.
|
||||
- **Add a plugin:** it's a `POS_PLUGIN` script in `entertainment/`; stdout is
|
||||
the message, `POS_KEYS` lines declare config keys. See
|
||||
the message, `POS_KEYS` lines declare config keys, and the message-safe
|
||||
helpers in `lib/entertainment-plugin-lib.sh` remove the boilerplate. See
|
||||
[DOC/DEV.md → "Adding a plugin"](../DEV.md).
|
||||
|
||||
**Troubleshooting:**
|
||||
- `send weather` prints an error about coordinates → set `WEATHER_LAT`/
|
||||
`WEATHER_LON` (required keys) via `config set`.
|
||||
- `enable` fails on the interval → the value isn't valid systemd time; use one
|
||||
of: `5m 10m 30m 1h 2h 6h 12h daily weekly`.
|
||||
of: `5m 10m 15m 30m 45m 1h 2h 6h 12h daily weekly`.
|
||||
- Nothing arrives even though `status` shows the timer → check `notify.env`
|
||||
(`NOTIFY_PLATFORM`) and that `telegram.env` is configured (see
|
||||
[communication](communication.md)); confirm the timer fired:
|
||||
`systemctl --user list-timers pos-ent-*`.
|
||||
- Public-API flakiness → the plugin outputs a clear failure; the notify call is
|
||||
silent-fail by design.
|
||||
silent-fail by design. `pos entertainment status` shows the last failed run
|
||||
(rc ≠ 0) so a missed timer is visible.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user