add entetaiment features
This commit is contained in:
@@ -36,16 +36,16 @@ Linux_post_install/
|
|||||||
├── bin/ # CLI tools — installed to /usr/local/bin/
|
├── bin/ # CLI tools — installed to /usr/local/bin/
|
||||||
│ ├── pos # Main dispatcher — smart arg matching to pos-* scripts
|
│ ├── pos # Main dispatcher — smart arg matching to pos-* scripts
|
||||||
<!-- GEN:START tree -->
|
<!-- GEN:START tree -->
|
||||||
│ ├── pos-communication-telegram # Send Telegram messages via Bot API (--send, test, config set)
|
│ ├── pos-communication-telegram # Send Telegram messages/files/links/stickers via Bot API (send, test, config set)
|
||||||
│ ├── pos-docker-compose # Docker Compose service manager (ls/up/down/restart/logs/update/config)
|
│ ├── pos-docker-compose # Docker Compose service manager (ls/up/down/restart/logs/update/config)
|
||||||
│ ├── pos-docker-health # One-glance container health dashboard (exits 1 if unhealthy)
|
│ ├── pos-docker-health # One-glance container health dashboard (exits 1 if unhealthy)
|
||||||
│ ├── pos-docker-ps # Enhanced container overview (health, IPs, ports, uptime)
|
│ ├── pos-docker-ps # Enhanced container overview (health, IPs, ports, uptime)
|
||||||
│ ├── pos-docker-vbox # Disposable Docker-based VMs (create/enter/start/stop/rm/ls)
|
│ ├── pos-docker-vbox # Disposable Docker-based VMs (create/enter/start/stop/rm/ls)
|
||||||
│ ├── pos-entertainment-config # Show or edit the entertainment config (ENABLED auto-trigger list, weather location)
|
│ ├── 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-disable # Disable a plugin's auto-trigger (remove it from ENABLED)
|
||||||
│ ├── pos-entertainment-enable # Enable an auto-trigger for a plugin (systemd user timer)
|
│ ├── 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 Telegram (default sender)
|
||||||
│ ├── pos-entertainment-status # Show enabled plugins and their timer state
|
│ ├── pos-entertainment-status # Show enabled plugins and scheduler state
|
||||||
│ ├── pos-media-mp3 # Download audio as MP3 (yt-dlp)
|
│ ├── pos-media-mp3 # Download audio as MP3 (yt-dlp)
|
||||||
│ ├── pos-media-mp4 # Download video as MP4 (interactive format select)
|
│ ├── pos-media-mp4 # Download video as MP4 (interactive format select)
|
||||||
│ ├── pos-network-checkport # Check TCP port connectivity
|
│ ├── pos-network-checkport # Check TCP port connectivity
|
||||||
@@ -212,16 +212,16 @@ All non-interactive `pos` commands log output to `~/.local/share/linux_post_inst
|
|||||||
| Category | Command | Script | Description |
|
| Category | Command | Script | Description |
|
||||||
|----------|---------|--------|-------------|
|
|----------|---------|--------|-------------|
|
||||||
<!-- GEN:START dispatch -->
|
<!-- GEN:START dispatch -->
|
||||||
| communication | telegram | `pos-communication-telegram` | Send Telegram messages via Bot API (--send, test, config set) |
|
| communication | telegram | `pos-communication-telegram` | Send Telegram messages/files/links/stickers via Bot API (send, test, config set) |
|
||||||
| docker | compose | `pos-docker-compose` | Docker Compose service manager (ls/up/down/restart/logs/update/config) |
|
| docker | compose | `pos-docker-compose` | Docker Compose service manager (ls/up/down/restart/logs/update/config) |
|
||||||
| docker | health | `pos-docker-health` | One-glance container health dashboard (exits 1 if unhealthy) |
|
| docker | health | `pos-docker-health` | One-glance container health dashboard (exits 1 if unhealthy) |
|
||||||
| docker | ps | `pos-docker-ps` | Enhanced container overview (health, IPs, ports, uptime) |
|
| docker | ps | `pos-docker-ps` | Enhanced container overview (health, IPs, ports, uptime) |
|
||||||
| docker | vbox | `pos-docker-vbox` | Disposable Docker-based VMs (create/enter/start/stop/rm/ls) |
|
| docker | vbox | `pos-docker-vbox` | Disposable Docker-based VMs (create/enter/start/stop/rm/ls) |
|
||||||
| entertainment | config | `pos-entertainment-config` | Show or edit the entertainment config (ENABLED auto-trigger list, weather location) |
|
| 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 | 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 (systemd user timer) |
|
| 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 Telegram (default sender) |
|
||||||
| entertainment | status | `pos-entertainment-status` | Show enabled plugins and their timer state |
|
| entertainment | status | `pos-entertainment-status` | Show enabled plugins and scheduler state |
|
||||||
| media | mp3 | `pos-media-mp3` | Download audio as MP3 (yt-dlp) |
|
| media | mp3 | `pos-media-mp3` | Download audio as MP3 (yt-dlp) |
|
||||||
| media | mp4 | `pos-media-mp4` | Download video as MP4 (interactive format select) |
|
| media | mp4 | `pos-media-mp4` | Download video as MP4 (interactive format select) |
|
||||||
| network | checkport | `pos-network-checkport` | Check TCP port connectivity |
|
| network | checkport | `pos-network-checkport` | Check TCP port connectivity |
|
||||||
@@ -375,7 +375,7 @@ All `.service` files in `systemd/` are automatically copied to `/etc/systemd/sys
|
|||||||
### Runtime Config
|
### Runtime Config
|
||||||
|
|
||||||
- `~/.config/linux_post_install/compose.env` — Docker Compose global defaults
|
- `~/.config/linux_post_install/compose.env` — Docker Compose global defaults
|
||||||
- `~/.config/linux_post_install/entertainment.env` — entertainment plugin defaults: weather location + `ENABLED` auto-trigger list (`plugin, interval` pairs → systemd user timers via `pos entertainment enable/disable`); auto-installed from `config/entertainment.env` by `postinstall.sh` (no clobber, template printed)
|
- `~/.config/linux_post_install/entertainment.env` — entertainment plugin defaults: weather location + `ENABLED` auto-trigger list (`plugin, interval` pairs scheduled via `pos entertainment enable/disable`, systemd user timers or cron auto-detected); auto-installed from `config/entertainment.env` by `postinstall.sh` (no clobber, template printed)
|
||||||
- `~/.bashrc` — Modified by postinstall (PATH, bash completion)
|
- `~/.bashrc` — Modified by postinstall (PATH, bash completion)
|
||||||
|
|
||||||
### Feature Flags
|
### Feature Flags
|
||||||
@@ -490,16 +490,16 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
|||||||
| `features/autostart.sh` | 14 | Boot-time feature (moved from `bin/`, flag-gated service) |
|
| `features/autostart.sh` | 14 | Boot-time feature (moved from `bin/`, flag-gated service) |
|
||||||
<!-- GEN:START filetable -->
|
<!-- GEN:START filetable -->
|
||||||
| `bin/pos` | 213 | CLI dispatcher with smart arg matching + logging + category help |
|
| `bin/pos` | 213 | CLI dispatcher with smart arg matching + logging + category help |
|
||||||
| `bin/pos-communication-telegram` | 150 | Send Telegram messages via Bot API (--send, test, config set) |
|
| `bin/pos-communication-telegram` | 270 | Send Telegram messages/files/links/stickers via Bot API (send, test, config set) |
|
||||||
| `bin/pos-docker-compose` | 364 | Docker Compose service manager (ls/up/down/restart/logs/update/config) |
|
| `bin/pos-docker-compose` | 364 | Docker Compose service manager (ls/up/down/restart/logs/update/config) |
|
||||||
| `bin/pos-docker-health` | 110 | One-glance container health dashboard (exits 1 if unhealthy) |
|
| `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-ps` | 128 | Enhanced container overview (health, IPs, ports, uptime) |
|
||||||
| `bin/pos-docker-vbox` | 157 | Disposable Docker-based VMs (create/enter/start/stop/rm/ls) |
|
| `bin/pos-docker-vbox` | 157 | Disposable Docker-based VMs (create/enter/start/stop/rm/ls) |
|
||||||
| `bin/pos-entertainment-config` | 64 | Show or edit the entertainment config (ENABLED auto-trigger list, weather location) |
|
| `bin/pos-entertainment-config` | 85 | 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-disable` | 32 | Disable a plugin's auto-trigger (remove it from ENABLED) |
|
||||||
| `bin/pos-entertainment-enable` | 49 | Enable an auto-trigger for a plugin (systemd user timer) |
|
| `bin/pos-entertainment-enable` | 50 | 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-send` | 93 | Run a public-API plugin and send its output via Telegram (default sender) |
|
||||||
| `bin/pos-entertainment-status` | 55 | Show enabled plugins and their timer state |
|
| `bin/pos-entertainment-status` | 55 | Show enabled plugins and scheduler state |
|
||||||
| `bin/pos-media-mp3` | 35 | Download audio as MP3 (yt-dlp) |
|
| `bin/pos-media-mp3` | 35 | Download audio as MP3 (yt-dlp) |
|
||||||
| `bin/pos-media-mp4` | 38 | Download video as MP4 (interactive format select) |
|
| `bin/pos-media-mp4` | 38 | Download video as MP4 (interactive format select) |
|
||||||
| `bin/pos-network-checkport` | 45 | Check TCP port connectivity |
|
| `bin/pos-network-checkport` | 45 | Check TCP port connectivity |
|
||||||
@@ -510,7 +510,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
|
|||||||
| `bin/pos-system-backup` | 117 | Encrypted (AES-256) folder snapshots (tar + gpg) |
|
| `bin/pos-system-backup` | 117 | Encrypted (AES-256) folder snapshots (tar + gpg) |
|
||||||
| `bin/pos-system-firewall` | 285 | Interactive UFW management |
|
| `bin/pos-system-firewall` | 285 | Interactive UFW management |
|
||||||
| `bin/pos-usb-server` | 218 | USB Redirector server control (--ls, --share; prompts when args omitted) |
|
| `bin/pos-usb-server` | 218 | USB Redirector server control (--ls, --share; prompts when args omitted) |
|
||||||
| `completions/pos.bash` | 169 | Dynamic bash completion |
|
| `completions/pos.bash` | 188 | Dynamic bash completion |
|
||||||
<!-- GEN:END filetable -->
|
<!-- GEN:END filetable -->
|
||||||
| `apps/install.sh` | 171 | App install/uninstall picker/orchestrator |
|
| `apps/install.sh` | 171 | App install/uninstall picker/orchestrator |
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -183,12 +183,14 @@ The `entertainment` module routes public-API data to Telegram via the single run
|
|||||||
|
|
||||||
### 1. Create the plugin
|
### 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):
|
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`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# POS_PLUGIN: myplugin
|
# POS_PLUGIN: myplugin
|
||||||
|
# POS_KEYS: MYPLUGIN_URL <feed url> (required)
|
||||||
|
# POS_KEYS: MYPLUGIN_TAG <filter tag> (optional)
|
||||||
err() { echo "ERROR: $*" >&2; exit 1; }
|
err() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
command -v curl &>/dev/null || err "curl not found"
|
command -v curl &>/dev/null || err "curl not found"
|
||||||
@@ -201,7 +203,7 @@ printf 'Title: %s\n' "$data"
|
|||||||
|
|
||||||
### 2. Config (if needed)
|
### 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`.
|
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.
|
||||||
|
|
||||||
### 3. Deps
|
### 3. Deps
|
||||||
|
|
||||||
|
|||||||
+25
-8
@@ -188,20 +188,27 @@ Subcommands that need input prompt interactively when args are omitted.
|
|||||||
|
|
||||||
| Command | File | Purpose | Configuration |
|
| Command | File | Purpose | Configuration |
|
||||||
|---------|------|---------|---------------|
|
|---------|------|---------|---------------|
|
||||||
| `pos communication telegram --send "text"` | `bin/pos-communication-telegram` | Send a text message to a Telegram chat via the Bot API | Token + chat ID from `~/.config/linux_post_install/telegram.env` (`TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID`, chmod 600). Precedence: `--token`/`--chat-id` flags > env > config file |
|
| `pos communication telegram send "text"` | `bin/pos-communication-telegram` | Send a message, link, or media file (auto-detects the type) to a Telegram chat via the Bot API | Token + chat ID from `~/.config/linux_post_install/telegram.env` (`TELEGRAM_BOT_TOKEN`, `TELEGRAM_CHAT_ID`, chmod 600). Precedence: `--token`/`--chat-id` flags > env > config file |
|
||||||
|
|
||||||
`pos communication telegram` in detail:
|
`pos communication telegram` in detail:
|
||||||
|
|
||||||
| Command | Behavior |
|
| Command | Behavior |
|
||||||
|---------|----------|
|
|---------|----------|
|
||||||
| `pos communication telegram --send "text"` | POSTs `sendMessage` to the Bot API (20s timeout); prints `[+] Message sent to chat <id>` or fails with a nonzero exit |
|
| `pos communication telegram send "text"` | POSTs `sendMessage` to the Bot API (60s timeout); prints `[+] message sent to chat <id>` or fails with a nonzero exit |
|
||||||
| `pos communication telegram --send "text" --token <t> --chat-id <id>` | One-shot override of token/chat ID |
|
| `pos communication telegram send <value>` | **Auto-detects the type** when `--type` is omitted: existing file → `file` (except `.webp` → sticker, `.gif` → animation, images → photo, video/audio/voice extensions → their type), value starting with `http://`/`https://`/`www.` → `link`, otherwise `message` |
|
||||||
| `pos communication telegram --send "text" --parse-mode <mode>` | Send with Telegram formatting; `<mode>` is `plain` (default), `markdown`, or `html` (passed as `parse_mode` to the API). Markdown/HTML use raw Telegram syntax — unescaped characters may be rejected by the API (400) |
|
| `pos communication telegram send <path> --type file [--caption "…"]` | Uploads a file as a `sendDocument` via multipart (`document=@path`); `--caption` adds a caption. Path must exist and be readable |
|
||||||
|
| `pos communication telegram send <path> [--caption "…"]` | Media uploads via their Bot API endpoint: `--type photo` → `sendPhoto`, `video` → `sendVideo`, `audio` → `sendAudio`, `voice` → `sendVoice`, `animation` → `sendAnimation`, `sticker` (`.webp`) → `sendSticker` (captions not supported for stickers) |
|
||||||
|
| `pos communication telegram send "url" --type link [--no-preview]` | Sends a link as a message (URLs auto-linkify); `--no-preview` adds `disable_web_page_preview=true` |
|
||||||
|
| `pos communication telegram send "text" --parse-mode <mode>` | Send with Telegram formatting; `<mode>` is `plain` (default), `markdown`, or `html` (passed as `parse_mode` to the API — also applies to captions). Markdown/HTML use raw Telegram syntax — unescaped characters may be rejected by the API (400) |
|
||||||
|
| `pos communication telegram send … --token <t> --chat-id <id>` | One-shot override of token/chat ID |
|
||||||
|
| `pos communication telegram --send "text"` | Legacy alias for `send "text"` (kept for the entertainment runner) |
|
||||||
| `pos communication telegram test` | Sends a canned test message using the current config |
|
| `pos communication telegram test` | Sends a canned test message using the current config |
|
||||||
| `pos communication telegram config` | Shows current config (bot token masked) |
|
| `pos communication telegram config` | Shows current config (bot token masked) |
|
||||||
| `pos communication telegram config set TELEGRAM_BOT_TOKEN=...` | Saves a bot token (600 perms) |
|
| `pos communication telegram config set TELEGRAM_BOT_TOKEN=...` | Saves a bot token (600 perms) |
|
||||||
| `pos communication telegram config set TELEGRAM_CHAT_ID=...` | Saves the target chat ID |
|
| `pos communication telegram config set TELEGRAM_CHAT_ID=...` | Saves the target chat ID |
|
||||||
|
|
||||||
|
`send` option validation: `--caption` is only valid with media types (file/photo/video/audio/voice/animation), `--no-preview` only with `--type message`/`link`, and `--type` only accepts `message|file|link|sticker|photo|video|audio|voice|animation`. An explicit `--type` always overrides auto-detection.
|
||||||
|
|
||||||
The bot token is a secret — it is stored only in `~/.config/linux_post_install/telegram.env` and never in the repo. Requires network access to `api.telegram.org`.
|
The bot token is a secret — it is stored only in `~/.config/linux_post_install/telegram.env` and never in the repo. Requires network access to `api.telegram.org`.
|
||||||
|
|
||||||
### entertainment
|
### entertainment
|
||||||
@@ -216,10 +223,10 @@ The bot token is a secret — it is stored only in `~/.config/linux_post_install
|
|||||||
| `pos entertainment send <plugin> --print` | Print the output locally; do not send |
|
| `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`) |
|
| `pos entertainment send <plugin> --markdown` | Send with `--parse-mode markdown` (via `pos communication telegram`) |
|
||||||
| `pos entertainment config` | Show the config file (`~/.config/linux_post_install/entertainment.env`) |
|
| `pos entertainment config` | Show the config file (`~/.config/linux_post_install/entertainment.env`) |
|
||||||
| `pos entertainment config set KEY=VALUE…` | Set keys (e.g. `ENABLED`, `WEATHER_LAT`) and re-sync timers |
|
| `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 enable <plugin> [interval]` | Add plugin to `ENABLED` + create/enable its systemd **user** timer |
|
| `pos entertainment enable <plugin> [interval]` | Add plugin to `ENABLED` + schedule it (systemd user timer or cron, auto-detected) |
|
||||||
| `pos entertainment disable <plugin>` | Remove plugin from `ENABLED` + disable/remove its timer |
|
| `pos entertainment disable <plugin>` | Remove plugin from `ENABLED` + remove its scheduled job |
|
||||||
| `pos entertainment status` | Enabled plugins + timer state + next fire time |
|
| `pos entertainment status` | Enabled plugins + scheduler + schedule state |
|
||||||
|
|
||||||
**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.
|
**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.
|
||||||
|
|
||||||
@@ -235,6 +242,16 @@ Plugins:
|
|||||||
|
|
||||||
**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 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`.
|
||||||
|
|
||||||
|
**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)`:
|
||||||
|
|
||||||
|
```
|
||||||
|
# POS_PLUGIN: myfeed
|
||||||
|
# POS_KEYS: MYFEED_URL <feed url> (required)
|
||||||
|
# POS_KEYS: MYFEED_TAG <filter tag> (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
The plugin itself still reads the keys as plain env vars (`${MYFEED_URL:-}`). The declaration is what `pos entertainment config` uses to print its Keys section and to decide whether `config set` warns about an undeclared key — add the line whenever a plugin gets a new config key.
|
||||||
|
|
||||||
**Automation (auto-trigger):** enable plugins on a schedule via the `ENABLED` key in the config — a comma-separated list of `plugin, interval` pairs:
|
**Automation (auto-trigger):** enable plugins on a schedule via the `ENABLED` key in the config — a comma-separated list of `plugin, interval` pairs:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
+157
-37
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# POS: communication telegram — Send Telegram messages via Bot API (--send, test, config set)
|
# POS: communication telegram — Send Telegram messages/files/links/stickers via Bot API (send, test, config set)
|
||||||
# POS_FLAGS: --send
|
# POS_FLAGS: --send --type --caption --parse-mode --no-preview --token --chat-id
|
||||||
|
|
||||||
CONFIG_DIR="$HOME/.config/linux_post_install"
|
CONFIG_DIR="$HOME/.config/linux_post_install"
|
||||||
CONFIG_FILE="$CONFIG_DIR/telegram.env"
|
CONFIG_FILE="$CONFIG_DIR/telegram.env"
|
||||||
@@ -14,12 +14,29 @@ Usage: pos communication telegram [command] [args]
|
|||||||
Send Telegram messages via the Bot API.
|
Send Telegram messages via the Bot API.
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
--send "text" Send a text message to the configured chat
|
send <value> [options] Send a message, link, or media (auto-detects the type)
|
||||||
--send "text" --token <t> --chat-id <id> Override token/chat for one send
|
test Send a test message using the current config
|
||||||
--send "text" --parse-mode <mode> Format mode: plain (default), markdown, html
|
config Show current config (token masked)
|
||||||
test Send a test message using the current config
|
config set KEY=VALUE Set TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID
|
||||||
config Show current config (token masked)
|
|
||||||
config set KEY=VALUE Set TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID
|
Types (auto-detected when --type is omitted):
|
||||||
|
message Plain text (default)
|
||||||
|
link Clickable link (value starts with http:// https:// www.)
|
||||||
|
file Any document (any other existing file)
|
||||||
|
photo .jpg .jpeg .png .bmp
|
||||||
|
video .mp4 .mkv .webm .mov .avi
|
||||||
|
audio .mp3 .wav .flac .m4a .aac
|
||||||
|
voice .ogg .opus .oga
|
||||||
|
animation .gif
|
||||||
|
sticker .webp
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--type <type> Force a type: message|file|link|sticker|photo|video|audio|voice|animation
|
||||||
|
--caption <text> Caption for file/photo/video/audio/voice/animation
|
||||||
|
--parse-mode <mode> Format mode: plain (default), markdown, html (message/link/caption)
|
||||||
|
--no-preview Disable the link's web page preview (message/link only)
|
||||||
|
--token <t> Override token for one send
|
||||||
|
--chat-id <id> Override chat id for one send
|
||||||
|
|
||||||
Config file: $CONFIG_FILE
|
Config file: $CONFIG_FILE
|
||||||
Keys: TELEGRAM_BOT_TOKEN | TELEGRAM_CHAT_ID
|
Keys: TELEGRAM_BOT_TOKEN | TELEGRAM_CHAT_ID
|
||||||
@@ -27,10 +44,13 @@ Keys: TELEGRAM_BOT_TOKEN | TELEGRAM_CHAT_ID
|
|||||||
Precedence: CLI flags > environment > config file.
|
Precedence: CLI flags > environment > config file.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
pos communication telegram config set TELEGRAM_BOT_TOKEN=123456:ABC-DEF
|
pos communication telegram send "Backup finished"
|
||||||
pos communication telegram config set TELEGRAM_CHAT_ID=987654321
|
pos communication telegram send "/path/to/report.pdf" --caption "Daily report"
|
||||||
pos communication telegram --send "Backup finished"
|
pos communication telegram send "/path/to/photo.jpg" --caption "Sunset"
|
||||||
pos communication telegram --send "*Backup finished*" --parse-mode markdown
|
pos communication telegram send "/path/to/sticker.webp"
|
||||||
|
pos communication telegram send "https://example.com" --no-preview
|
||||||
|
pos communication telegram send "/path/to/video.mp4" --type video
|
||||||
|
pos communication telegram --send "Old style text message"
|
||||||
pos communication telegram test
|
pos communication telegram test
|
||||||
EOF
|
EOF
|
||||||
exit 0
|
exit 0
|
||||||
@@ -63,14 +83,127 @@ mask_token() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_send() {
|
send_request() {
|
||||||
local text="$1" parse_mode="${2:-}"
|
local endpoint="$1"; shift
|
||||||
[ -z "${TELEGRAM_BOT_TOKEN:-}" ] && err "No bot token — run 'pos communication telegram config set TELEGRAM_BOT_TOKEN=...'"
|
[ -z "${TELEGRAM_BOT_TOKEN:-}" ] && err "No bot token — run 'pos communication telegram config set TELEGRAM_BOT_TOKEN=...'"
|
||||||
[ -z "${TELEGRAM_CHAT_ID:-}" ] && err "No chat id — run 'pos communication telegram config set TELEGRAM_CHAT_ID=...'"
|
[ -z "${TELEGRAM_CHAT_ID:-}" ] && err "No chat id — run 'pos communication telegram config set TELEGRAM_CHAT_ID=...'"
|
||||||
local args=(--data-urlencode "chat_id=${TELEGRAM_CHAT_ID}" --data-urlencode "text=${text}")
|
curl -fsS -m 60 -X POST "$API/bot${TELEGRAM_BOT_TOKEN}/${endpoint}" "$@" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
send_message() {
|
||||||
|
local text="$1" parse_mode="${2:-}" no_preview="${3:-}"
|
||||||
|
local args=(--data-urlencode "chat_id=${TELEGRAM_CHAT_ID:-}" --data-urlencode "text=${text}")
|
||||||
[ -n "$parse_mode" ] && args+=(--data-urlencode "parse_mode=${parse_mode}")
|
[ -n "$parse_mode" ] && args+=(--data-urlencode "parse_mode=${parse_mode}")
|
||||||
curl -fsS -m 20 -X POST "$API/bot${TELEGRAM_BOT_TOKEN}/sendMessage" "${args[@]}" >/dev/null
|
[ "$no_preview" = "1" ] && args+=(--data-urlencode "disable_web_page_preview=true")
|
||||||
echo "[+] Message sent to chat ${TELEGRAM_CHAT_ID}"
|
send_request sendMessage "${args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
send_multipart() {
|
||||||
|
local endpoint="$1" file_field="$2" file_path="$3" caption="${4:-}" parse_mode="${5:-}"
|
||||||
|
[ -f "$file_path" ] && [ -r "$file_path" ] || err "File not found or unreadable: $file_path"
|
||||||
|
local args=(-F "chat_id=${TELEGRAM_CHAT_ID:-}" -F "${file_field}=@${file_path}")
|
||||||
|
[ -n "$caption" ] && args+=(-F "caption=${caption}")
|
||||||
|
[ -n "$parse_mode" ] && args+=(-F "parse_mode=${parse_mode}")
|
||||||
|
send_request "$endpoint" "${args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_type() {
|
||||||
|
local value="$1" ext
|
||||||
|
if [ -f "$value" ] && [ -r "$value" ]; then
|
||||||
|
ext="${value##*.}"
|
||||||
|
ext="${ext,,}"
|
||||||
|
case "$ext" in
|
||||||
|
webp) echo sticker ;;
|
||||||
|
gif) echo animation ;;
|
||||||
|
jpg|jpeg|png|bmp) echo photo ;;
|
||||||
|
mp4|mkv|webm|mov|avi|m4v) echo video ;;
|
||||||
|
ogg|opus|oga) echo voice ;;
|
||||||
|
mp3|wav|flac|m4a|aac) echo audio ;;
|
||||||
|
*) echo file ;;
|
||||||
|
esac
|
||||||
|
elif [[ "$value" =~ ^(https?://|www\.) ]]; then
|
||||||
|
echo link
|
||||||
|
else
|
||||||
|
echo message
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_send() {
|
||||||
|
local value="" type="" caption="" parse_mode="" no_preview=0 type_explicit=0 auto=""
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--type)
|
||||||
|
[ $# -ge 2 ] || err "--type needs a value"
|
||||||
|
case "$2" in
|
||||||
|
message|file|link|sticker|photo|video|audio|voice|animation) type="$2" ;;
|
||||||
|
*) err "Unknown type '$2' (allowed: message, file, link, sticker, photo, video, audio, voice, animation)" ;;
|
||||||
|
esac
|
||||||
|
type_explicit=1
|
||||||
|
shift 2 ;;
|
||||||
|
--caption)
|
||||||
|
[ $# -ge 2 ] || err "--caption needs a value"
|
||||||
|
caption="$2"; shift 2 ;;
|
||||||
|
--parse-mode)
|
||||||
|
[ $# -ge 2 ] || err "--parse-mode needs a value"
|
||||||
|
case "$2" in
|
||||||
|
plain|markdown|html) ;;
|
||||||
|
*) err "Unknown parse mode '$2' (allowed: plain, markdown, html)" ;;
|
||||||
|
esac
|
||||||
|
parse_mode="$2"; shift 2 ;;
|
||||||
|
--no-preview)
|
||||||
|
no_preview=1; shift ;;
|
||||||
|
--token)
|
||||||
|
[ $# -ge 2 ] || err "--token needs a value"
|
||||||
|
TELEGRAM_BOT_TOKEN="$2"; shift 2 ;;
|
||||||
|
--chat-id)
|
||||||
|
[ $# -ge 2 ] || err "--chat-id needs a value"
|
||||||
|
TELEGRAM_CHAT_ID="$2"; shift 2 ;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
[ $# -ge 1 ] || err "No value given for send"
|
||||||
|
value="$1"; shift
|
||||||
|
[ $# -eq 0 ] || err "Unexpected argument '$1'"
|
||||||
|
;;
|
||||||
|
-*) err "Unknown option '$1'" ;;
|
||||||
|
*)
|
||||||
|
[ -z "$value" ] || err "Unexpected extra argument '$1'"
|
||||||
|
value="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
[ -n "$value" ] || err "No value given for send"
|
||||||
|
|
||||||
|
if [ "$type_explicit" -eq 0 ]; then
|
||||||
|
type="$(detect_type "$value")"
|
||||||
|
auto=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$type" in
|
||||||
|
message|link)
|
||||||
|
[ -z "$caption" ] || err "--caption only applies to file/photo/video/audio/voice/animation"
|
||||||
|
;;
|
||||||
|
sticker)
|
||||||
|
[ -z "$caption" ] || err "--caption is not supported for stickers"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
case "$type" in
|
||||||
|
message|link) ;;
|
||||||
|
*) [ "$no_preview" -eq 0 ] || err "--no-preview only applies to --type message or link" ;;
|
||||||
|
esac
|
||||||
|
[ "$parse_mode" = "plain" ] && parse_mode=""
|
||||||
|
|
||||||
|
load_config
|
||||||
|
case "$type" in
|
||||||
|
message) send_message "$value" "$parse_mode" "$no_preview" ;;
|
||||||
|
link) send_message "$value" "$parse_mode" "$no_preview" ;;
|
||||||
|
file) send_multipart sendDocument document "$value" "$caption" "$parse_mode" ;;
|
||||||
|
sticker) send_multipart sendSticker sticker "$value" "" "" ;;
|
||||||
|
photo) send_multipart sendPhoto photo "$value" "$caption" "$parse_mode" ;;
|
||||||
|
video) send_multipart sendVideo video "$value" "$caption" "$parse_mode" ;;
|
||||||
|
audio) send_multipart sendAudio audio "$value" "$caption" "$parse_mode" ;;
|
||||||
|
voice) send_multipart sendVoice voice "$value" "$caption" "$parse_mode" ;;
|
||||||
|
animation) send_multipart sendAnimation animation "$value" "$caption" "$parse_mode" ;;
|
||||||
|
esac
|
||||||
|
echo "[+] $type${auto:+ (auto-detected)} sent to chat ${TELEGRAM_CHAT_ID}"
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_config() {
|
cmd_config() {
|
||||||
@@ -108,32 +241,19 @@ cmd="${1:-}"
|
|||||||
|
|
||||||
case "$cmd" in
|
case "$cmd" in
|
||||||
-h|--help) usage ;;
|
-h|--help) usage ;;
|
||||||
|
send)
|
||||||
|
shift
|
||||||
|
cmd_send "$@"
|
||||||
|
;;
|
||||||
--send)
|
--send)
|
||||||
shift
|
shift
|
||||||
[ $# -ge 1 ] || usage
|
[ $# -ge 1 ] || usage
|
||||||
text="$1"
|
cmd_send "$@"
|
||||||
shift
|
|
||||||
parse_mode=""
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
--token) TELEGRAM_BOT_TOKEN="$2"; shift 2 ;;
|
|
||||||
--chat-id) TELEGRAM_CHAT_ID="$2"; shift 2 ;;
|
|
||||||
--parse-mode)
|
|
||||||
case "$2" in
|
|
||||||
plain|markdown|html) ;;
|
|
||||||
*) err "Unknown parse mode '$2' (allowed: plain, markdown, html)" ;;
|
|
||||||
esac
|
|
||||||
parse_mode="$2"; shift 2 ;;
|
|
||||||
*) err "Unknown option '$1'" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
[ "$parse_mode" = "plain" ] && parse_mode=""
|
|
||||||
load_config
|
|
||||||
cmd_send "$text" "$parse_mode"
|
|
||||||
;;
|
;;
|
||||||
test)
|
test)
|
||||||
load_config
|
load_config
|
||||||
cmd_send "Test message from pos $(date '+%Y-%m-%d %H:%M:%S')"
|
send_message "Test message from pos $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo "[+] test message sent to chat ${TELEGRAM_CHAT_ID}"
|
||||||
;;
|
;;
|
||||||
config)
|
config)
|
||||||
shift
|
shift
|
||||||
|
|||||||
@@ -5,6 +5,25 @@ set -euo pipefail
|
|||||||
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
||||||
source "$(dirname "$0")/../lib/entertainment-lib.sh" 2>/dev/null || source "$(dirname "$0")/entertainment-lib.sh"
|
source "$(dirname "$0")/../lib/entertainment-lib.sh" 2>/dev/null || source "$(dirname "$0")/entertainment-lib.sh"
|
||||||
|
|
||||||
|
keys_section() {
|
||||||
|
# Renders the Keys reference: ENABLED + every key declared by installed
|
||||||
|
# plugins (via their # POS_KEYS: headers), aligned by key name.
|
||||||
|
local d="$1" maxw=8 line key plugin desc req
|
||||||
|
while IFS= read -r line; do
|
||||||
|
IFS='|' read -r key _desc _req <<<"$line"
|
||||||
|
[ ${#key} -gt "$maxw" ] && maxw=${#key}
|
||||||
|
done <<< "$(config_keys "$d")"
|
||||||
|
|
||||||
|
printf ' %-*s %s\n' "$maxw" "ENABLED" "Auto-trigger list: comma-separated 'plugin, interval' pairs,"
|
||||||
|
printf ' %-*s %s\n' "$maxw" "" "e.g. ENABLED=\"weather, 5m gold, 1h\""
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -n "$line" ] || continue
|
||||||
|
IFS='|' read -r plugin key desc req <<<"$line"
|
||||||
|
printf ' %-*s %s (%s) [%s]\n' "$maxw" "$key" "$desc" "$req" "$plugin"
|
||||||
|
done <<< "$(config_keys "$d")"
|
||||||
|
printf ' %-*s %s\n' "$maxw" "<any>" "UPPER_SNAKE key settable freely; a warning is shown if no installed plugin declares it"
|
||||||
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: pos entertainment config [set KEY=VALUE ...]
|
Usage: pos entertainment config [set KEY=VALUE ...]
|
||||||
@@ -14,12 +33,8 @@ After 'set', auto-trigger timers are re-synced from ENABLED.
|
|||||||
|
|
||||||
Config file: $CONFIG_FILE
|
Config file: $CONFIG_FILE
|
||||||
|
|
||||||
Keys:
|
Keys (declared by the installed plugins' # POS_KEYS: headers):
|
||||||
ENABLED Auto-trigger list: comma-separated 'plugin, interval' pairs,
|
$(keys_section "$(plugin_dir)")
|
||||||
e.g. ENABLED="weather, 5m gold, 1h"
|
|
||||||
WEATHER_LAT Weather plugin latitude (required for weather)
|
|
||||||
WEATHER_LON Weather plugin longitude (required for weather)
|
|
||||||
WEATHER_CITY Optional city label
|
|
||||||
|
|
||||||
Intervals: 5m 10m 15m 30m 45m hourly 2h 6h 12h daily weekly, or OnCalendar=...
|
Intervals: 5m 10m 15m 30m 45m hourly 2h 6h 12h daily weekly, or OnCalendar=...
|
||||||
|
|
||||||
@@ -32,6 +47,10 @@ EOF
|
|||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
key_known() {
|
||||||
|
config_key_known "$(plugin_dir)" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
-h|--help) usage ;;
|
-h|--help) usage ;;
|
||||||
set)
|
set)
|
||||||
@@ -40,11 +59,10 @@ case "${1:-}" in
|
|||||||
for pair in "$@"; do
|
for pair in "$@"; do
|
||||||
key="${pair%%=*}"
|
key="${pair%%=*}"
|
||||||
val="${pair#*=}"
|
val="${pair#*=}"
|
||||||
case "$key" in
|
[[ "$key" =~ ^[A-Z][A-Z0-9_]*$ ]] || \
|
||||||
ENABLED|WEATHER_LAT|WEATHER_LON|WEATHER_CITY) ;;
|
err "Invalid key '$key' (expected UPPER_SNAKE, e.g. WEATHER_LAT)"
|
||||||
*) err "Unknown key '$key' (allowed: ENABLED, WEATHER_LAT, WEATHER_LON, WEATHER_CITY)" ;;
|
|
||||||
esac
|
|
||||||
[ -z "$val" ] && [ "$key" != "ENABLED" ] && err "No value given (expected $key=...)"
|
[ -z "$val" ] && [ "$key" != "ENABLED" ] && err "No value given (expected $key=...)"
|
||||||
|
key_known "$key" || warn "Key '$key' not referenced by any installed plugin — saving anyway"
|
||||||
write_config_key "$key" "$val"
|
write_config_key "$key" "$val"
|
||||||
ok "$key saved to $CONFIG_FILE"
|
ok "$key saved to $CONFIG_FILE"
|
||||||
done
|
done
|
||||||
@@ -60,5 +78,8 @@ case "${1:-}" in
|
|||||||
echo
|
echo
|
||||||
echo "ENABLED: $(config_value ENABLED)"
|
echo "ENABLED: $(config_value ENABLED)"
|
||||||
echo "Intervals: 5m 10m 15m 30m 45m hourly 2h 6h 12h daily weekly, or OnCalendar=..."
|
echo "Intervals: 5m 10m 15m 30m 45m hourly 2h 6h 12h daily weekly, or OnCalendar=..."
|
||||||
|
echo
|
||||||
|
echo "Plugin keys (declared in each plugin's # POS_KEYS: header):"
|
||||||
|
keys_section "$(plugin_dir)"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Usage: pos entertainment disable <plugin>
|
|||||||
|
|
||||||
Remove a plugin from the ENABLED list in
|
Remove a plugin from the ENABLED list in
|
||||||
$CONFIG_FILE
|
$CONFIG_FILE
|
||||||
and disable/remove its systemd user timer.
|
and remove its scheduled job (systemd user timer or cron line).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
pos entertainment disable weather
|
pos entertainment disable weather
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# POS: entertainment enable — Enable an auto-trigger for a plugin (systemd user timer)
|
# POS: entertainment enable — Enable an auto-trigger for a plugin on a schedule
|
||||||
|
|
||||||
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
||||||
source "$(dirname "$0")/../lib/entertainment-lib.sh" 2>/dev/null || source "$(dirname "$0")/entertainment-lib.sh"
|
source "$(dirname "$0")/../lib/entertainment-lib.sh" 2>/dev/null || source "$(dirname "$0")/entertainment-lib.sh"
|
||||||
@@ -11,8 +11,9 @@ Usage: pos entertainment enable <plugin> [interval]
|
|||||||
|
|
||||||
Enable an auto-trigger: writes '<plugin>, <interval>' into the ENABLED list in
|
Enable an auto-trigger: writes '<plugin>, <interval>' into the ENABLED list in
|
||||||
$CONFIG_FILE
|
$CONFIG_FILE
|
||||||
and creates/enables a systemd user timer that runs 'pos entertainment send <plugin>'
|
and schedules 'pos entertainment send <plugin>' to run (systemd user timer, or
|
||||||
on the schedule. Re-running without an interval keeps the plugin's current one.
|
cron when systemd is unavailable). Re-running without an interval keeps the
|
||||||
|
plugin's current one.
|
||||||
|
|
||||||
Available plugins:
|
Available plugins:
|
||||||
$(list_plugins "$(plugin_dir)" | sed 's/^/ /')
|
$(list_plugins "$(plugin_dir)" | sed 's/^/ /')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# POS: entertainment status — Show enabled plugins and their timer state
|
# POS: entertainment status — Show enabled plugins and scheduler state
|
||||||
|
|
||||||
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
|
||||||
source "$(dirname "$0")/../lib/entertainment-lib.sh" 2>/dev/null || source "$(dirname "$0")/entertainment-lib.sh"
|
source "$(dirname "$0")/../lib/entertainment-lib.sh" 2>/dev/null || source "$(dirname "$0")/entertainment-lib.sh"
|
||||||
@@ -9,8 +9,8 @@ usage() {
|
|||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: pos entertainment status
|
Usage: pos entertainment status
|
||||||
|
|
||||||
Show the enabled plugins (from ENABLED in $CONFIG_FILE)
|
Show the enabled plugins (from ENABLED in $CONFIG_FILE),
|
||||||
and each one's systemd user timer state + next fire time.
|
the detected scheduler, and the schedule for each plugin.
|
||||||
|
|
||||||
Enable plugins with: pos entertainment enable <plugin> [interval]
|
Enable plugins with: pos entertainment enable <plugin> [interval]
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
+20
-1
@@ -3,7 +3,7 @@
|
|||||||
# Install: source this file in ~/.bashrc or place in /etc/bash_completion.d/
|
# Install: source this file in ~/.bashrc or place in /etc/bash_completion.d/
|
||||||
# GEN:START posflags
|
# GEN:START posflags
|
||||||
declare -A _pos_flags
|
declare -A _pos_flags
|
||||||
_pos_flags[communication-telegram]="--send"
|
_pos_flags[communication-telegram]="--send --type --caption --parse-mode --no-preview --token --chat-id"
|
||||||
_pos_flags[entertainment-send]="--print --markdown"
|
_pos_flags[entertainment-send]="--print --markdown"
|
||||||
_pos_flags[network-hotspot]="--foreground"
|
_pos_flags[network-hotspot]="--foreground"
|
||||||
_pos_flags[system-backup]="--service"
|
_pos_flags[system-backup]="--service"
|
||||||
@@ -161,6 +161,25 @@ _pos() {
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
|
communication-telegram)
|
||||||
|
case "${words[3]}" in
|
||||||
|
send|--send) _pos_complete_flags communication-telegram ;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
6)
|
||||||
|
case "${words[1]}-${words[2]}" in
|
||||||
|
communication-telegram)
|
||||||
|
case "${words[3]}:${words[4]}" in
|
||||||
|
send:--type|--send:--type)
|
||||||
|
COMPREPLY=($(compgen -W "message file link sticker photo video audio voice animation" -- "$cur"))
|
||||||
|
;;
|
||||||
|
send:--parse-mode|--send:--parse-mode)
|
||||||
|
COMPREPLY=($(compgen -W "plain markdown html" -- "$cur"))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
# Entertainment plugin: current weather via Open-Meteo (no API key).
|
# Entertainment plugin: current weather via Open-Meteo (no API key).
|
||||||
# POS_PLUGIN: weather
|
# POS_PLUGIN: weather
|
||||||
|
# POS_KEYS: WEATHER_LAT <latitude> (required)
|
||||||
|
# POS_KEYS: WEATHER_LON <longitude> (required)
|
||||||
|
# POS_KEYS: WEATHER_CITY <city label> (optional)
|
||||||
# Contract: stdout is the message sent by 'pos entertainment send weather'.
|
# Contract: stdout is the message sent by 'pos entertainment send weather'.
|
||||||
|
|
||||||
err() { echo "ERROR: $*" >&2; exit 1; }
|
err() { echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|||||||
@@ -80,6 +80,54 @@ resolve_plugin() {
|
|||||||
err "Plugin '$name' not found in $dir — try one of: $(list_plugins "$dir" | tr '\n' ' ')"
|
err "Plugin '$name' not found in $dir — try one of: $(list_plugins "$dir" | tr '\n' ' ')"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Config keys declared by plugins ────────────────────────────────
|
||||||
|
# Pattern: each plugin documents the config keys it reads with
|
||||||
|
# # POS_KEYS: <KEY> <description> (required|optional)
|
||||||
|
# on one or more lines (right after # POS_PLUGIN:). 'pos entertainment
|
||||||
|
# config' prints them in its Keys section; config set warns when a key is
|
||||||
|
# not declared by any installed plugin.
|
||||||
|
|
||||||
|
plugin_keys() {
|
||||||
|
# Usage: plugin_keys <plugin-file> → "KEY|description|required|optional"
|
||||||
|
local file="$1" line key desc req
|
||||||
|
grep '^# POS_KEYS:' "$file" 2>/dev/null | sed 's/^# POS_KEYS:[[:space:]]*//' | while IFS= read -r line; do
|
||||||
|
key="${line%% *}"
|
||||||
|
desc="${line#* }"
|
||||||
|
case "$desc" in
|
||||||
|
*\(required\)) req=required; desc="${desc% (required)}" ;;
|
||||||
|
*\(optional\)) req=optional; desc="${desc% (optional)}" ;;
|
||||||
|
*) req="" ;;
|
||||||
|
esac
|
||||||
|
printf '%s|%s|%s\n' "$key" "$desc" "$req"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
config_keys() {
|
||||||
|
# Usage: config_keys <plugin-dir> → "KEY|plugin|description|required|optional"
|
||||||
|
local dir="$1" f name line
|
||||||
|
for f in "$dir"/*.sh; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
name="$(plugin_marker "$f")"
|
||||||
|
[ -n "$name" ] || continue
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -n "$line" ] || continue
|
||||||
|
printf '%s|%s\n' "$name" "$line"
|
||||||
|
done <<< "$(plugin_keys "$f")"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
config_key_known() {
|
||||||
|
# Usage: config_key_known <plugin-dir> <KEY> → true if ENABLED or declared by a plugin
|
||||||
|
local dir="$1" key="$2"
|
||||||
|
[ "$key" = "ENABLED" ] && return 0
|
||||||
|
local line _plugin _key _rest
|
||||||
|
while IFS= read -r line; do
|
||||||
|
IFS='|' read -r _plugin _key _rest <<<"$line"
|
||||||
|
[ "$_key" = "$key" ] && return 0
|
||||||
|
done <<< "$(config_keys "$dir")"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# ── ENABLED list parsing ──────────────────────────────────────────
|
# ── ENABLED list parsing ──────────────────────────────────────────
|
||||||
# Format: comma/space-separated 'plugin[, interval]' pairs, e.g.
|
# Format: comma/space-separated 'plugin[, interval]' pairs, e.g.
|
||||||
# 'weather, 5m gold, 1h'. Parsed name-aware: a token that resolves to a
|
# 'weather, 5m gold, 1h'. Parsed name-aware: a token that resolves to a
|
||||||
|
|||||||
Reference in New Issue
Block a user