|
#!/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() |