No description
  • Python 64.4%
  • Nix 18%
  • Shell 17.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Nimmo 9e344d3c91
All checks were successful
Build and Push Attic Cache / validate (push) Successful in 10s
Build and Push Attic Cache / push (push) Successful in 4s
Allow custom Nova runner in workflow lint
2026-07-29 08:06:32 +01:00
.forgejo Allow custom Nova runner in workflow lint 2026-07-29 08:06:32 +01:00
.envrc Port daily briefing to Nix and harden delivery 2026-07-04 08:09:53 +01:00
.gitignore Expand .gitignore with standard Python entries 2026-07-21 23:22:21 +01:00
AGENTS.md Port daily briefing to Nix and harden delivery 2026-07-04 08:09:53 +01:00
daily-briefing.py Improve news summaries and add dry-run timing 2026-07-29 06:53:06 +01:00
daily-briefing.sh Package daily briefing with Nix and Attic CI 2026-07-29 08:00:17 +01:00
flake.lock Package daily briefing with Nix and Attic CI 2026-07-29 08:00:17 +01:00
flake.nix Package daily briefing with Nix and Attic CI 2026-07-29 08:00:17 +01:00
nixos-module.nix Package daily briefing with Nix and Attic CI 2026-07-29 08:00:17 +01:00
package.nix Package daily briefing with Nix and Attic CI 2026-07-29 08:00:17 +01:00
quotes.txt Add daily thought for the day sign-off from quotes pool 2026-03-06 13:07:37 +00:00
README.md Package daily briefing with Nix and Attic CI 2026-07-29 08:00:17 +01:00
setup.sh Port daily briefing to Nix and harden delivery 2026-07-04 08:09:53 +01:00
test-media.sh Fix Sonarr/Radarr Python parsing: heredoc overrides pipe so pass data via env var instead 2026-03-06 12:07:04 +00:00

daily-briefing

A personal morning briefing script that assembles weather, news, and media download summaries into a spoken TTS audio clip and pushes everything to ntfy.

What it does

Each run:

  1. Weather — fetches current conditions and today's forecast from open-meteo, then generates a conversational report via Ollama
  2. News — pulls yesterday's unread articles from FreshRSS (per category/label), summarises each category using a local Ollama model, and assembles a conversational spoken summary
  3. Media — checks Sonarr and Radarr history for TV episodes and movies downloaded yesterday
  4. Thought for the day — picks a quote from quotes.txt, rotating through the full list without repeating until all have been used
  5. TTS — generates a WAV file using pocket-tts cloned from a Fern Brady voice sample
  6. ntfy — sends a markdown text notification and a separate audio file attachment to the dailybriefing topic

Requirements

  • Nix with flakes enabled, or Python 3.13 plus requests
  • curl, jq, ffmpeg (for WAV→MP3 conversion before upload)
  • A running Ollama instance with a suitable model (default: qwen3.6:35b-a3b-q8_0)
  • A running FreshRSS instance with the Google Reader API enabled
  • Running Sonarr and Radarr instances
  • An ntfy server
  • For the NixOS module: sops-nix and an age-encrypted secrets file

Setup

direnv allow

This loads the flake-defined development environment with Python, requests, pocket-tts, curl, and jq.

If you prefer not to use direnv:

nix develop

The legacy ./setup.sh path remains available for non-Nix environments and now exits early with usage guidance when a flake-capable Nix install is present.

Voice sample

Place a WAV voice sample at:

voice_samples/fern_brady.wav

Environment file (standalone mode)

Create a single briefing.env file in the project directory:

# ntfy
NTFY_BASE_URL=https://ntfy.example.com
NTFY_API_KEY=your-api-key

# Weather (open-meteo)
WEATHER_POSTCODE=SW1A1AA
WEATHER_LAT=51.5074
WEATHER_LON=-0.1278

# FreshRSS
FRESHRSS_URL=https://freshrss.example.com
FRESHRSS_USER=your-username
FRESHRSS_PASS=your-password

# Sonarr / Radarr
SONARR_URL=http://192.168.x.x:8989
SONARR_API_KEY=your-sonarr-api-key
RADARR_URL=http://192.168.x.x:7878
RADARR_API_KEY=your-radarr-api-key

# Ollama (optional overrides)
# OLLAMA_URL=http://192.168.x.x:11434
# OLLAMA_MODEL=qwen3.6:35b

# Audio (optional overrides)
# FFMPEG_BIN=/path/to/ffmpeg   # default: ffmpeg
# MP3_BITRATE=96k             # default: 96k

The audio attachment is generated as a WAV by pocket-tts, then converted to MP3 with ffmpeg before being uploaded to ntfy. If ffmpeg is unavailable the WAV is sent instead.

NixOS service

The flake exports nixosModules.default, which turns the settings from the environment file into NixOS options and keeps credentials in sops. Import it in a host configuration alongside the flake's other modules:

{
  imports = [ inputs.daily-briefing.nixosModules.default ];

  services.daily-briefing = {
    enable = true;
    sopsFile = ./secrets/daily-briefing.yaml;

    ntfyBaseUrl = "https://ntfy.example.com";
    weatherPostcode = "SW1A1AA";
    weatherLatitude = "51.5074";
    weatherLongitude = "-0.1278";
    freshRssUrl = "https://freshrss.example.com";
    freshRssUser = "briefing";
    sonarrUrl = "http://192.168.1.10:8989";
    radarrUrl = "http://192.168.1.10:7878";
    ollamaUrl = "http://192.168.1.10:11434";
  };
}

The module creates a daily-briefing system user and a persistent timer at 07:00 by default. Put these keys in the encrypted sops YAML (values omitted):

daily-briefing/ntfy-api-key: ...
daily-briefing/freshrss-password: ...
daily-briefing/sonarr-api-key: ...
daily-briefing/radarr-api-key: ...
daily-briefing/hf-token: ...

Copy voice_samples/fern_brady.wav into the module's /var/lib/daily-briefing/voice_samples/ directory and make it readable by the service user. The module copies quotes.txt there on first run and keeps the quote history in the same directory.

Attic cache workflow

.forgejo/workflows/attic-cache.yml validates the flake on pull requests and builds the package closure on main, pushing it to the nixos Attic cache. Configure the repository secret ATTIC_PUSH_TOKEN with permission to push to that cache. Consumers must configure https://attic.nimmog.uk/nixos as a Nix substituter and trust its public signing key before they can use the cached closure.

Running

nix run

You can also run ./daily-briefing.sh, which uses python3 when available and otherwise falls back to nix run. Run those commands from the repository root so the script can read briefing.env, voice_samples/, and the quote history files. On Debian media1, ./daily-briefing.sh prefers nix run when Nix is installed, so it uses the flake's pinned Python/TTS/runtime dependencies before the host migrates to NixOS. nix run also works directly on any other non-NixOS machine with flakes enabled.

Use ./daily-briefing.sh --dry-run to generate the briefing locally without posting text or audio notifications to ntfy.

Typically scheduled as a cron job or systemd timer each morning.

TTS Notes

  • The script prefers the local voice_samples/fern_brady.wav cloning sample when available.
  • On Nix, pocket-tts may require accepted Hugging Face terms and local auth for voice-cloning weights.
  • If cloned voice generation is unavailable, the script falls back to a built-in Pocket TTS voice.
  • Override the fallback voice with POCKET_TTS_FALLBACK_VOICE, or point directly at a binary with POCKET_TTS_BIN.

Current Operational Checks

When debugging missing briefings, verify these first:

  • FreshRSS credentials in briefing.env still work against the Google Reader API.
  • Ollama at the configured host responds quickly enough for both weather and news summarisation.
  • pocket-tts can either access the cloned-voice weights or produce audio with the configured fallback voice.
  • A systemd timer or other scheduler actually exists on the target machine.

Files

File Description
daily-briefing.py Main script
daily-briefing.sh Wrapper that runs the script directly or via nix run
flake.nix Nix runtime and development shell
.envrc direnv entrypoint for the flake shell
setup.sh Legacy non-Nix setup script
test-media.sh Standalone test for Sonarr/Radarr fetching
quotes.txt Pool of quotes for "thought of the day"
unused_quotes.txt Remaining quotes not yet used (auto-managed)
quote_history.json Log of which quote was used on which date (auto-managed)
voice_samples/ Voice WAV samples for pocket-tts