Created
March 1, 2026 14:17
-
-
Save chrismdp/64c2947fbd1e8b640aa3036412995209 to your computer and use it in GitHub Desktop.
Claude Code Heartbeat: automated reminders via Telegram
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Claude Code Heartbeat — check reminders and ping Telegram | |
| # | |
| # This script runs Claude Code every 15 minutes via cron to check | |
| # a reminders file and send Telegram notifications when something | |
| # needs attention. The server never sleeps, so reminders always fire. | |
| # | |
| # Setup: | |
| # 1. Create a Telegram bot: message @BotFather, run /newbot, save the token | |
| # 2. Get your chat ID: message @userinfobot on Telegram | |
| # 3. Save credentials to ~/.secret_env: | |
| # export TELEGRAM_BOT_TOKEN="your-bot-token" | |
| # export TELEGRAM_CHAT_ID="your-chat-id" | |
| # 4. Create ~/REMINDERS.md with your reminders (see format below) | |
| # 5. Ask Claude Code to add this to your crontab: | |
| # "Run heartbeat.sh every 15 minutes between 6am and 11pm via cron" | |
| # | |
| # Reminder format (~/REMINDERS.md): | |
| # 2026-03-15 09:00 - Call the dentist | |
| # every day 08:00 - Check email | |
| # monday 09:00 - Weekly planning | |
| # after 17:00 - Wind down, stop coding | |
| # | |
| # How it works: | |
| # Cron runs this script → it spawns Claude Code with a prompt → | |
| # Claude reads REMINDERS.md, checks the time, and calls send_telegram.sh | |
| # for anything that matches. A lock file prevents overlapping runs. | |
| set -euo pipefail | |
| LOG=/tmp/heartbeat.log | |
| LOCKFILE=/tmp/heartbeat.lock | |
| # Ensure only one heartbeat runs at a time | |
| exec 9>"$LOCKFILE" | |
| if ! flock -n 9; then | |
| # Check if the holder is stuck (>10 min) | |
| HOLDER_PID=$(pgrep -f 'claude -p.*heartbeat' | head -1) | |
| if [ -n "$HOLDER_PID" ]; then | |
| ELAPSED=$(ps -p "$HOLDER_PID" -o etimes= 2>/dev/null | tr -d ' ') | |
| if [ -n "$ELAPSED" ] && [ "$ELAPSED" -gt 600 ]; then | |
| kill -9 "$HOLDER_PID" 2>/dev/null | |
| sleep 1 | |
| flock -n 9 || exit 0 | |
| else | |
| exit 0 | |
| fi | |
| else | |
| exit 0 | |
| fi | |
| fi | |
| # The prompt — tell Claude what to check and how to notify you | |
| PROMPT='Check ~/REMINDERS.md against the current time. | |
| For each reminder that matches now (within a 10-minute window), | |
| send a Telegram notification using ~/bin/send_telegram.sh "message". | |
| Skip any reminders outside the current time window.' | |
| OUTPUT=$(claude -p "$PROMPT" --model sonnet 2>&1) | |
| EXIT_CODE=$? | |
| if [ $EXIT_CODE -ne 0 ]; then | |
| echo "$(date '+%H:%M') exit=$EXIT_CODE $OUTPUT" >> "$LOG" | |
| fi | |
| date '+%H:%M' > /tmp/heartbeat-last-run |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Send a message to Telegram | |
| # Usage: send_telegram.sh "Your message here" | |
| # Or: echo "message" | send_telegram.sh | |
| source ~/.secret_env | |
| CHAT_ID="${TELEGRAM_CHAT_ID}" | |
| if [ -z "$1" ]; then | |
| MESSAGE=$(cat) | |
| else | |
| MESSAGE="$1" | |
| fi | |
| if [ -z "$MESSAGE" ]; then | |
| echo "Usage: send_telegram.sh \"message\" or pipe via stdin" | |
| exit 1 | |
| fi | |
| curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ | |
| --data-urlencode "text=$MESSAGE" \ | |
| -d "chat_id=$CHAT_ID" \ | |
| -d "parse_mode=Markdown" > /dev/null |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment