Introduce hate_crack.notify package with a small functional public API
and a CrackTailer thread for polling hashcat output files. Package
layout keeps the HTTP call (_send_pushover) isolated so future backends
(Slack, generic webhooks) can be added as a sibling function rather
than a framework rewrite.
Core pieces:
- settings.py: NotifySettings dataclass plus atomic config persistence
(save_enabled, add_to_allowlist) via read-modify-write + os.replace.
- pushover.py: single _send_pushover() that never raises; network
errors, missing requests, and missing creds all funnel to False.
- _suppress.py: thread-local suppression context manager so
orchestrator attacks can chain primitives without flooding
notifications.
- tailer.py: CrackTailer(threading.Thread) that seeks to EOF on start,
polls at a user-configurable interval, and collapses per-tick bursts
into a single aggregate notification when they exceed the cap.
- __init__.py: public API (init, prompt_notify_for_attack,
notify_job_done, notify_crack, start_tailer, stop_tailer,
toggle_enabled, suppressed_notifications). Privacy guarantee:
notification payloads contain only attack name, counts, and hash
path, never plaintexts.
72 new tests cover dataclass defaults, atomic config writes, idempotent
allowlist updates, HTTP payload privacy, suppression nesting and
thread-locality, tailer EOF seek, burst cap, truncation recovery, and
the per-attack prompt's [y/n/always] flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>