Building a Telegram Ping-Pong Keep-Alive Monitor with Gradio
π€ Telegram Ping-Pong Keep-Alive Monitor
A lightweight, self-contained uptime monitoring system using two Telegram bots, a background watchdog, and a real-time Gradio web dashboard β all running concurrently in pure Python.
π Table of Contents
- Overview
- How It Works
- Project Structure
- Architecture Deep Dive
- Setup & Installation
- Configuration
- Running the Monitor
- Dashboard Features
- URL-Triggered Pings
- Watchdog Logic
- Download Source Files
Overview
When youβre running services on free-tier platforms like Render, Railway, or Hugging Face Spaces, they often spin down after inactivity. The classic workaround is a βkeep-aliveβ ping β a periodic HTTP or message-based signal to keep the service awake.
This project takes that idea further: instead of just sending a fire-and-forget ping, it verifies that the ping was received and responded to. It does this with a two-bot Telegram system and a watchdog that alerts you when a pong never comes back.
The entire system runs in a single Python process with three concurrent threads and exposes a live Gradio dashboard to monitor activity.
How It Works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Python Process β
β β
β Thread 1: Ping Scheduler β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Every 5 min β Send "ping" to Telegram channel β β
β β After 12s β Check if pong was received β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Thread 2: Pong Bot β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Listens on channel β Sees "ping" β β
β β Replies "pong" β Records timestamp β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Thread 3: Gradio UI (Main Thread) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Displays ping/pong logs and missed pongs β β
β β Accepts manual ping trigger via button or URL β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
Telegram Channel Public Gradio URL
@keep_alive_ping (share=True)
The Ping Bot sends "ping" to a public Telegram channel. The Pong Bot listens to the same channel, sees the message, and replies "pong". A watchdog timer fires 12 seconds after each ping to verify the pong arrived on time.
Project Structure
ping-pong-monitor/
βββ main.py # Entry point β starts all threads
βββ ping_scheduler.py # Sends pings, runs watchdog
βββ pong_bot.py # Receives pings, sends pongs
βββ gradio_interface_v2.py # Web UI dashboard
βββ README.md # This file
Architecture Deep Dive
main.py β The Orchestrator
import threading
from ping_scheduler import start_ping_loop
from pong_bot import run_pong_bot
from gradio_interface import launch_ui
if __name__ == "__main__":
threading.Thread(target=start_ping_loop, daemon=True).start()
threading.Thread(target=run_pong_bot, daemon=True).start()
launch_ui()
This is elegantly simple. Both background threads are marked as daemon threads, which means they automatically terminate when the main thread (the Gradio UI) exits. The UI itself runs on the main thread β this matters because Gradioβs server loop is blocking.
Key design decision: Using daemon=True means you donβt need explicit shutdown logic. When the user closes the app or the process ends, the bots stop cleanly.
ping_scheduler.py β The Sender
This module is responsible for three things:
- Sending periodic pings to a Telegram channel
- Scheduling a watchdog check 12 seconds after each ping
- Logging missed pongs when no response arrives in time
def send_ping():
global last_ping
url = f"https://api.telegram.org/bot{PING_BOT_TOKEN}/sendMessage"
params = {"chat_id": CHAT_ID, "text": "ping"}
res = requests.get(url, params=params)
if res.ok:
last_ping = datetime.now()
ping_log.append(("ping", last_ping.strftime("%H:%M:%S")))
if len(ping_log) > 10:
ping_log.pop(0)
# Schedule watchdog 12 seconds later
threading.Timer(12, check_pong_watchdog).start()
The ping is sent using the Telegram Bot API directly over HTTP β no library needed for the sender side, just requests. After a successful send, a threading.Timer is created to fire check_pong_watchdog() 12 seconds later. This gives the pong bot enough time to receive and respond.
The Watchdog
def check_pong_watchdog():
if last_ping is None or last_pong_timestamp is None:
missed_pongs.append(...)
return
if last_pong_timestamp < last_ping:
missed_pongs.append(last_ping.strftime("%H:%M:%S"))
The logic is straightforward:
- If
last_pong_timestampisNone, no pong has ever come β missed - If
last_pong_timestamp < last_ping, the most recent pong is older than the most recent ping β missed
Both conditions result in the timestamp being logged to missed_pongs.
Ping Loop
def start_ping_loop(interval_sec=300): # 5 minutes
while True:
send_ping()
time.sleep(interval_sec)
A simple infinite loop with a configurable interval (default 5 minutes). Change interval_sec to adjust frequency.
pong_bot.py β The Responder
import telebot
from datetime import datetime
bot = telebot.TeleBot(PONG_BOT_TOKEN)
@bot.channel_post_handler(func=lambda msg: msg.text and msg.text.lower() == "ping")
def respond_pong(message):
global last_pong, last_pong_timestamp
bot.send_message(message.chat.id, "pong")
last_pong_timestamp = datetime.now()
last_pong = last_pong_timestamp.strftime("%H:%M:%S")
pong_log.append(("pong", last_pong))
if len(pong_log) > 10:
pong_log.pop(0)
def run_pong_bot():
bot.infinity_polling()
This bot uses pyTelegramBotAPI (telebot) for its clean decorator-based message handling. The @bot.channel_post_handler decorator means it only triggers on channel posts (not direct messages), which is exactly what we need since the ping bot posts to a channel.
The handler checks that the message is exactly "ping" (case-insensitive) and replies with "pong" to the same chat. Timestamps and logs are updated in globals, which the watchdog and UI read from.
Why two separate bots? Telegram bots cannot read their own messages. If we used one bot to send the ping, it couldnβt also detect it to send the pong. Two separate bot tokens solve this cleanly.
gradio_interface_v2.py β The Dashboard
The UI is built with Gradio, which lets you create web interfaces in pure Python with minimal boilerplate.
MAX_LOG_ENTRIES = 20
def get_logs():
pings = get_ping_log()[-MAX_LOG_ENTRIES:]
pongs = get_pong_log()[-MAX_LOG_ENTRIES:]
missed = get_missed_pongs()
# ... formats into a readable text block
The get_logs() function pulls from all three data sources and formats them into a single markdown-style text block. Entries are capped at 20 to keep the display clean.
Layout
with gr.Blocks() as ui:
gr.Markdown("# π€ Telegram Ping Pong Monitor with Watchdog")
with gr.Row():
refresh_btn = gr.Button("π Refresh Logs")
ping_btn = gr.Button("π€ Send Ping Now")
output = gr.Textbox(lines=20, interactive=False, label="Ping-Pong Logs + Watchdog")
refresh_btn.click(get_logs, outputs=output)
ping_btn.click(manual_ping, outputs=output)
ui.load(fn=on_load, inputs=None, outputs=output)
Two buttons: one to refresh the log view, one to manually fire a ping. The ui.load handler runs on page load, enabling URL-based triggering.
Public Sharing
def launch_ui():
ui.launch(share=True)
share=True generates a public gradio.live URL β no deployment required. This is perfect for sharing the dashboard with teammates or embedding in external monitoring tools.
Setup & Installation
Prerequisites
- Python 3.8+
- Two Telegram bot tokens (create via @BotFather)
- A public Telegram channel
Install Dependencies
pip install requests pyTelegramBotAPI gradio
Create Your Telegram Setup
- Go to @BotFather on Telegram
- Create Bot 1 (the Ping Bot) β copy its token
- Create Bot 2 (the Pong Bot) β copy its token
- Create a public Telegram channel (e.g.
@your_keep_alive_channel) - Add both bots as administrators to the channel
Configuration
Open ping_scheduler.py and pong_bot.py and update the tokens and channel:
ping_scheduler.py
PING_BOT_TOKEN = "YOUR_PING_BOT_TOKEN_HERE"
CHAT_ID = "@your_channel_username"
pong_bot.py
PONG_BOT_TOKEN = "YOUR_PONG_BOT_TOKEN_HERE"
Security tip: For production use, store tokens in environment variables and load them with
os.environ.get("PING_BOT_TOKEN")instead of hardcoding.
Running the Monitor
python main.py
On startup youβll see:
- The ping loop begins (first ping fires immediately)
- The pong bot starts polling Telegram
- Gradio launches and prints a local URL (and a public
gradio.liveURL ifshare=True)
Open the URL in your browser to see the live dashboard.
Dashboard Features
| Feature | Description |
|---|---|
| π’ Ping Log | Last 20 pings with timestamps |
| π΅ Pong Log | Last 20 pongs with timestamps |
| β οΈ Missed Pongs | Up to 5 most recent missed pong events |
| π Last Ping | Timestamp of the most recent ping sent |
| π Last Pong | Timestamp of the most recent pong received |
| π Refresh Button | Manually refresh the log view |
| π€ Send Ping Button | Immediately fire a manual ping |
URL-Triggered Pings
One of the more powerful features is the ability to trigger a ping via a URL query parameter. This enables integration with external schedulers like UptimeRobot, cron jobs, or CI/CD pipelines.
def on_load(request: gr.Request):
query = request.query_params or {}
if query.get("ping", "false").lower() == "true":
return manual_ping()
return get_logs()
To trigger a ping externally, simply visit:
https://your-gradio-url.gradio.live/?ping=true
Use cases:
- Set UptimeRobot to hit
?ping=trueevery 5 minutes - Add it as a cron webhook to keep Render services alive
- Use it in GitHub Actions as a scheduled health check step
Watchdog Logic
The watchdog is the heart of the monitoring system. It runs 12 seconds after every ping and checks two conditions:
Ping sent at T
β
βΌ
T + 12 seconds
β
ββ last_pong_timestamp is None? β MISSED (never received any pong)
β
ββ last_pong_timestamp < last_ping? β MISSED (pong is stale)
(no new pong came after this ping)
If either condition is true, the pingβs timestamp is appended to the missed_pongs list. The UI shows the last 5 missed pong events at a glance.
The 12-second window is intentional β it accounts for Telegram API latency, pyTelegramBotAPI polling intervals, and processing time, while still being fast enough to detect genuine failures.
Download Source Files
You can download all source files as a zip archive here:
π¦ Download ping-pong-monitor.zip
The archive includes:
main.pyping_scheduler.pypong_bot.pygradio_interface_v2.py
Built with Python, Gradio, and the Telegram Bot API.