A collection of scripts that push notifications to ntfy (and optionally Telegram)
- Python 96.4%
- Just 2.2%
- Shell 1.4%
|
|
||
|---|---|---|
| bin | ||
| docs | ||
| just | ||
| lib | ||
| scripts | ||
| .env.example | ||
| .gitignore | ||
| .python-version | ||
| justfile | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
🍌 bananify
A collection of notification scripts that push useful info to ntfy (and optionally Telegram). Named after ntfy's banana mascot.
Scripts
| Script | Topic | Schedule | Source |
|---|---|---|---|
weather.py |
weather |
7am daily | Open-Meteo (free, no key) |
apod.py |
apod |
7am daily | NASA APOD (free) |
news.py |
news |
7am daily | Configurable RSS feeds |
quote.py |
quote |
7am daily | zenquotes.io (free) |
jokes.py |
jokes |
7am daily | JokeAPI v2 / personal file |
memes.py |
memes |
12pm daily | Reddit JSON API |
system_health.py |
system-health |
8am daily | Local /proc / psutil |
adguard_stats.py |
adguard |
8am daily | AdGuard Home API |
ssl_expiry.py |
ssl-expiry |
Mon 9am | TLS cert check (stdlib) |
ssh_login.py |
ssh-login |
SSH login (PAM) | PAM environment |
Setup
git clone <repo-url> bananify && cd bananify
cp .env.example .env # fill in your values
uv sync # install deps
Configuration
All configuration lives in .env. See .env.example for all available options.
Required:
NTFY_URL— your ntfy server URL (e.g.https://ntfy.sh)NTFY_TOKEN— ntfy access token
Optional:
WEATHER_LANG— geocoding/description language (enores, defaulten)HEALTH_MOUNT_LABELS— display names for mounts, e.g./=Root,/mnt/media=MediaNTFY_TOPIC_<NAME>— override default topic per script; comma-separate to publish to multiple topics (e.g.bananify,news)- Telegram, AdGuard credentials, city list, RSS feeds, SSL domains, thresholds
News feeds (NEWS_FEEDS)
Each feed entry is comma-separated and supports three formats:
| Format | Example |
|---|---|
url |
https://hnrss.org/frontpage |
name|url |
HN|https://hnrss.org/frontpage |
name|url|max |
HN|https://hnrss.org/frontpage|3 |
- name — overrides the RSS feed title in the notification
- max — per-feed headline limit; falls back to
NEWS_MAX_HEADLINESif omitted - Each feed sends as a separate notification to avoid ntfy's 4096-byte attachment threshold
Jokes (JOKES_SOURCE, JOKES_FILE)
JOKES_SOURCE=internet(default) — fetches a Spanish joke from JokeAPI v2 (free, no key)JOKES_SOURCE=personal— picks a random joke from a local markdown file atJOKES_FILE- Personal file format: jokes separated by
---delimiters, or as a bullet list (- joke text)
Memes (MEMES_SOURCES)
Each entry is comma-separated and supports two formats:
| Format | Example |
|---|---|
subreddit |
me_irl |
subreddit|count |
me_irl|3 |
- count — number of image posts to send (default: 3)
- NSFW posts are filtered by default; set
MEMES_ALLOW_NSFW=trueto include them - Each image post sends as a separate notification with inline image and click-through link
Running
just run weather # run a single script
just run jokes
just run memes
just run-all # run all scripts once
Or call the wrapper directly:
bin/bananify weather
Scheduling (cron)
0 7 * * * /path/to/bananify/bin/bananify weather
0 7 * * * /path/to/bananify/bin/bananify apod
0 7 * * * /path/to/bananify/bin/bananify news
0 7 * * * /path/to/bananify/bin/bananify quote
0 7 * * * /path/to/bananify/bin/bananify jokes
0 8 * * * /path/to/bananify/bin/bananify system_health
0 8 * * * /path/to/bananify/bin/bananify adguard_stats
0 9 * * 1 /path/to/bananify/bin/bananify ssl_expiry
0 12 * * * /path/to/bananify/bin/bananify memes
For SSH login alerts, see docs/cron.md.
Adding a new script
- Create
scripts/my_thing.py— import fromlib.notify:import sys, os sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from lib.notify import load_config, send_ntfy def main(): cfg = load_config() # Optional: markdown=True, attach="https://...", click="https://..." send_ntfy(cfg, "my-topic", "Title", "Body") if __name__ == "__main__": main() - Test:
just run my_thing - Add a cron entry using
bin/bananify my_thing