Skip to main content

Pluggable Python notification utility with Discord webhook support

Project description

🚀 PingCast

CI License: MIT Python 3.8+ Code style: black Docs

PingCast is a lightweight Python library that sends status updates from your scripts (e.g., ML training, data pipelines, ETL jobs) to external channels. It starts with Discord webhooks and is built to be plugin‑friendly for future channels.

  • 🔧 Simple API: from pingcast import Discord, NotifierNotifier(Discord(WEBHOOK_URL)).everything()
  • ✅ Covers the basics: success, failure (uncaught exceptions), periodic heartbeats, and optional log forwarding
  • 🔁 Sync & async: Notifier (sync) and AsyncNotifier (async)
  • 🔌 Extensible: clean plugin interface; future discovery via entry points

🧭 Table of contents


❓ Why

Training LLMs and running heavy experiments takes hours. People don’t babysit terminals the whole time, and then return to discover the job failed hours ago. PingCast keeps you in the loop:

  • Completion notices when the script exits normally
  • Crash alerts when an uncaught exception terminates your program (via sys.excepthook) ([Python documentation][2])
  • ⏱️ Periodic heartbeats (default every 15 minutes) so you know it’s still running
  • 🧾 Optional log forwarding using the standard logging handlers (no invasive framework) ([Python documentation][3])

📦 Install

pip install pingcast

🚀 Quickstart

  1. Create a Discord webhook in your channel (Server Settings → Integrations → Webhooks), then copy the URL.

  2. Use it in code:

from pingcast import Discord, Notifier

discord = Discord("https://discord.com/api/webhooks/XXXXXXXX/XXXXXXXX")
notify = Notifier(discord)

def main():
    # one line to wire everything:
    notify.everything(filter={"level": "INFO"}, interval=900)  # 15 min heartbeats

    # your long job
    import logging, time
    logging.info("training started")
    for epoch in range(5):
        time.sleep(5)
        logging.info("epoch %d done", epoch + 1)

    # if an uncaught exception happens, you'll get a ❌ alert

if __name__ == "__main__":
    main()

This sends:

  • periodic “still running” messages,
  • your logging messages (respecting the filter),
  • a final ✅ on normal exit, or ❌ on crash.

🧰 Usage

✉️ Manual sends

notify.send("Checkpoint saved", {"epoch": 12, "val_loss": 0.217})

🔭 Track “everything”

notify.everything()  # start log capture, heartbeats, success/exception hooks

This wires:

  • a logging.Handler to forward records,
  • a heartbeat thread for periodic pings,
  • atexit hook for success on normal interpreter termination,
  • sys.excepthook to report uncaught exceptions before exit. (Functions registered with atexit run automatically on normal interpreter termination.) ([Python documentation][5])

⏱️ Filtering & heartbeat interval

# only ERROR+ logs, heartbeat every 10 minutes
notify.everything(filter={"level": "ERROR"}, interval=600)

Supported filter keys (initially):

  • level: "DEBUG"|"INFO"|"WARNING"|"ERROR"|"CRITICAL"

⚙️ Async usage

import asyncio
from pingcast import AsyncNotifier, Discord

async def run():
    discord = Discord("https://discord.com/api/webhooks/…")
    notify = AsyncNotifier(discord)
    await notify.send("Job kicked off")
    await notify.everything(filter={"level": "WARNING"}, interval=900)
    # … your async workload …

asyncio.run(run())

📚 Documentation

See the docs site for a brief overview and getting started guide:

docs/index.md

🛠️ How it works (under the hood)

  • Logging: attaches a logging.Handler to forward formatted records; you keep using Python’s standard logging API. ([Python documentation][3])
  • Success on exit: registers an atexit handler that fires on normal termination. (Not triggered on hard kills, fatal internal errors, or os._exit().) ([Python documentation][5])
  • Crash reporting: installs a custom sys.excepthook so uncaught exceptions send a ❌ message before the interpreter exits. You can always restore the original hook. ([Python documentation][2])

🔌 Plugins & extensibility

PingCast ships with a Discord plugin first. The architecture is intentionally simple:

  • A tiny BasePlugin interface with .send() / .send_async()
  • Notifier holds a list of plugins and broadcasts to each
  • New channels (Slack, Email, Telegram, etc.) can be added with small plugin classes

For automatic plugin discovery later, we’ll adopt entry points so third-party packages can register plugins that PingCast discovers at runtime—this is the recommended approach in the Packaging User Guide. ([Python Packaging][9])

Example entry point (future):

# pyproject.toml of a third-party plugin package
[project.entry-points."pingcast.plugins"]
"slack" = "pingcast_slack:SlackPlugin"

Consumers can then do:

notify = Notifier(slack="xoxb-…", discord="https://discord.com/api/webhooks/…")

⚙️ Configuration

Minimal constructor:

from pingcast import Discord, Notifier

notify = Notifier(Discord("https://discord.com/api/webhooks/…"))

Common options:

  • filter={"level": "INFO"} — limit forwarded logs by level
  • interval=900 — heartbeat interval in seconds (default 15 min)
  • notify.send(message, details: dict | None) — manual push
  • Legacy keyword-style initialization (Notifier(discord="…")) still works, but new plugin-style construction is preferred so multiple channels can be combined.

Treat webhook URLs as secrets. Don’t commit them; prefer env vars or secret managers.


🐍 Supported Python versions

We target Python 3.8+. (Python 3.7 reached end-of-life on 2023-06-27; upgrading is strongly recommended.) ([Python Developer's Guide][10])


🛟 Troubleshooting

  • No messages arrive • Double-check the webhook URL and channel permissions. • Look for HTTP 429s (rate limit) and back off before retrying. ([Discord][7])

  • Too many log messages • Raise your filter level: filter={"level": "ERROR"}. • Consider sending only summary metrics with notify.send(...).

  • Script was killed (SIGKILL / machine reboot)atexit only runs on normal termination; if the process is killed, the success hook won’t fire. ([Python documentation][5])

  • I want fewer heartbeats • Increase interval (e.g., interval=3600 for hourly).


🗺️ Roadmap

  • Additional plugins (Slack/Telegram/Email)
  • Optional batching & backoff for rate limits (429) ([Discord][7])
  • Automatic plugin discovery via entry points ([Python Packaging][11])
  • Structured embeds for richer Discord messages ([Discord][8])

🤝 Contributing

Issues and PRs welcome! Please:

  • Open an issue to discuss larger changes before submitting a PR.
  • Follow modern packaging practices (PEP 621 / pyproject.toml) and ensure tests pass.
  • Keep changes focused and add/adjust tests where appropriate.

If you’re adding a new plugin, start with a tiny class implementing .send() and consider registering via entry points. Useful references: Packaging Projects tutorial and Writing your pyproject.toml. ([Python Packaging][12])

See CONTRIBUTING.md for details.


🔐 Security

If you believe you’ve found a security issue, please follow the process in SECURITY.md rather than opening a public issue.


📜 Code of Conduct

This project adheres to a Contributor Code of Conduct. By participating, you agree to uphold its terms. See CODE_OF_CONDUCT.md.


📝 License

This project is licensed under the MIT License - see the LICENSE file for details.


Citation

If you use this library in your research, please cite:

@article{pingcast,
  title={PingCast: A lightweight Python library for sending status updates to Discord},
  author={Islam, MD. Tarikul}
  year={2025}
  url={https://github.com/Tarikul-Islam-Anik/PingCast}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pingcast-0.1.0a0.tar.gz (20.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pingcast-0.1.0a0-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file pingcast-0.1.0a0.tar.gz.

File metadata

  • Download URL: pingcast-0.1.0a0.tar.gz
  • Upload date:
  • Size: 20.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pingcast-0.1.0a0.tar.gz
Algorithm Hash digest
SHA256 147fcf56478089029cdbf1552b97f74c363e05edd03537b84b927a179c8c24da
MD5 b295d7fb7d64b086d1a6957cf0da85bd
BLAKE2b-256 f93e365b2f3720d421acae8ba0884de0ce244816e38e5995f45acac28fb92c8b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pingcast-0.1.0a0.tar.gz:

Publisher: publish.yml on Tarikul-Islam-Anik/PingCast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pingcast-0.1.0a0-py3-none-any.whl.

File metadata

  • Download URL: pingcast-0.1.0a0-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pingcast-0.1.0a0-py3-none-any.whl
Algorithm Hash digest
SHA256 1e9b054490ef29da974782ac0ea4083de0dc6ac960d1057288c48aa2bc67af1b
MD5 2f4c1a6d240625fe26ea14328dbe1bc0
BLAKE2b-256 759a34bbbeb2a600f32f736a097fb8b2bfa2a4b364b02a663e8568d979f35325

See more details on using hashes here.

Provenance

The following attestation bundles were made for pingcast-0.1.0a0-py3-none-any.whl:

Publisher: publish.yml on Tarikul-Islam-Anik/PingCast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page