Skip to content

Instantly share code, notes, and snippets.

@coldino
Last active October 3, 2025 10:37
Show Gist options
  • Select an option

  • Save coldino/0dd97c6fb17bbf15f9ab82ebd6c476c8 to your computer and use it in GitHub Desktop.

Select an option

Save coldino/0dd97c6fb17bbf15f9ab82ebd6c476c8 to your computer and use it in GitHub Desktop.
Server Pinger / Monitor

Purpose

Pings a server waiting for it to become accessible, then sends a Windows notification and exits.

Run Directly

uv run https://gist.githubusercontent.com/coldino/0dd97c6fb17bbf15f9ab82ebd6c476c8/raw/pinger.py <host>

Command-line Arguments

usage: pinger.py [-h] [--test] [-4] [-6] [-d DELAY] [-q] [host]

Ping a server until reachable and notify on success.

positional arguments:
  host               Server hostname or IP to ping

options:
  -h, --help         show this help message and exit
  --test             Send a test notification and exit
  -4                 Force IPv4 resolution
  -6                 Force IPv6 resolution
  -d, --delay DELAY  Delay between pings in seconds (default: 5)
  -q, --quiet        Suppress repeat status prints
#!/usr/bin/env python
# /// script
# requires-python = ">=3.9"
# dependencies = [
# "ping3",
# "windows-toasts"
# ]
# ///
import argparse
import time
import socket
import sys
from ping3 import ping
from windows_toasts import WindowsToaster, Toast
def send_notification(title: str, message: str):
toaster = WindowsToaster("ServerNotifier")
toast = Toast()
toast.text_fields = [title, message]
toaster.show_toast(toast)
def resolve_host(host: str, family: socket.AddressFamily):
"""Resolve host to specific IP version (IPv4/IPv6)."""
infos = socket.getaddrinfo(host, None, family, socket.SOCK_STREAM)
return infos[0][4][0]
def main():
parser = argparse.ArgumentParser(description="Ping a server until reachable and notify on success.")
parser.add_argument("host", nargs="?", help="Server hostname or IP to ping")
parser.add_argument("--test", action="store_true", help="Send a test notification and exit")
parser.add_argument("-4", dest="ipv4", action="store_true", help="Force IPv4 resolution")
parser.add_argument("-6", dest="ipv6", action="store_true", help="Force IPv6 resolution")
parser.add_argument("-d", "--delay", type=int, default=5, help="Delay between pings in seconds (default: 5)")
parser.add_argument("-q", "--quiet", action="store_true", help="Suppress repeat status prints")
args = parser.parse_args()
if args.test:
send_notification("Test Notification", "Your notification system is working ✅")
return
if not args.host:
parser.error("You must provide a host unless using --test")
# Resolve host if IPv4/IPv6 forced
host = args.host
if args.ipv4 and args.ipv6:
parser.error("Cannot force both IPv4 and IPv6 at the same time")
if args.ipv4:
host = resolve_host(args.host, socket.AF_INET)
elif args.ipv6:
host = resolve_host(args.host, socket.AF_INET6)
print(f"Pinging {host} every {args.delay} seconds. Waiting for it to become reachable...")
try:
while True:
try:
result = ping(host, timeout=2, unit="ms")
if result is not None:
print(f"{host} is reachable! Sending notification.")
send_notification("Server Reachable", f"{host} is now reachable ✅")
break
else:
if not args.quiet:
print(f"{host} not reachable, retrying...")
except Exception as e:
if not args.quiet:
print(f"Error pinging {host}: {e}")
time.sleep(args.delay)
except KeyboardInterrupt:
print("\nInterrupted by user. Exiting cleanly.")
sys.exit(0)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment