Telegram bot to warm up Claude Code sessions and track session timing.
Claude Code Pro's 5-hour session cooldown means hours of dead time between sessions. If you exhaust your session at 3pm, you can't use Claude Code again until 8pm. That's half a workday gone.
This bot starts sessions while you sleep. Schedule /schedule tomorrow 9am and the bot warms up at 6am — by 9am you still have 2 hours of usage left, enough for a productive morning. By 11am the session expires, the cooldown starts, and by the afternoon you have a fresh session ready to go.
| Command | Description |
|---|---|
/warmup [account] |
Start a session now via claude -p (all warms every account) |
/session [account] |
Show active session info (time remaining, tokens, expiry) |
/schedule <datetime> [hours] |
Schedule a warmup. At <datetime>, you'll have [hours] remaining (default: 2) |
/schedules |
List pending scheduled warmups |
/cancel <id> |
Cancel a scheduled warmup |
/daily <time[, time...]> [hours] |
Schedule daily warmups. At each <time>, you'll have [hours] remaining |
/workday <start>-<end> [lead] |
Plan a day's warmups around your working hours ([lead] hours left at <start>, default: 2) |
/dailies |
List daily scheduled warmups |
/cancel_daily <id> |
Cancel a daily scheduled warmup |
/history [account] |
Show recent session history |
/accounts |
List configured accounts and their current windows |
/schedule tomorrow 9am # 2h remaining at 9am → warmup at 6am
/schedule monday 14:00 3 # 3h remaining at 14:00 → warmup at 12:00
/schedule jan 30 8:00 4h # 4h remaining at 8:00 → warmup at 7:00
/daily 7:00 AM 5h # warm up every day at 7:00am
/daily 9:00 AM 2h # warm up every day at 6:00am
/daily 7:00, 13:00, 18:00 5h # warm up three times a day
/workday 9am-6pm # plan a 9-to-6 day (warmups at 06:00, 11:05, 16:10)
/workday 9:00-18:00 1.5h # same day, 1.5h left on the clock at 9:00
The bot computes: warmup_time = target - (5h - hours_remaining).
A daily schedule can hold several times of day, separated by commas. All of its
times share the same [hours] value, and the whole group is one ID — /dailies
lists it on a single line and /cancel_daily <id> cancels every time in it. The
bot always arms a timer for the soonest upcoming time, then re-arms for the next
one after each warmup.
/workday plans a whole day for you instead of making you pick times by hand:
/workday 9am-6pm
→ Warmups: 06:00, 11:05, 16:10 (3 per day)
It works backwards from two facts about the 5-hour window. First, a window
opened by a "ready" prompt has essentially untouched quota, so the best state
to start work in is a window that is already running with a couple of hours
left — you spend that quota on your first stretch of work, and the next reset
lands inside your day rather than after it. [lead] is how much of that window
is left when you clock in (default 2h), so the first warmup goes at
start - (5h - lead).
Second, each later warmup has to land just past the previous window's expiry.
A warmup fired exactly on the boundary lands inside the window that is still
live — it opens nothing and is silently wasted — so /workday adds a 5-minute
handover margin and keeps chaining until another window would start after your
day ends.
For a 9-to-6 day that yields three separate quota allowances covering 8.8 of
your 9 working hours, versus two if you simply warm up at 9:00. Ranges can be
written 9am-6pm, 9:00-18:00, 9-18, 9-6, or 9am to 6pm; overnight
shifts (21:00-06:00) work too.
One bot can drive several Claude accounts. Set CLAUDE_ACCOUNTS to a
comma-separated list of names — the first is the primary:
CLAUDE_ACCOUNTS=work,personal
CLAUDE_ACCOUNTS_DIR=/data/accounts
Each account is isolated by its own CLAUDE_CONFIG_DIR, which the bot passes to
the claude child process. Log each one in once, on the host:
CLAUDE_CONFIG_DIR="$PWD/data/accounts/work" claude auth login
CLAUDE_CONFIG_DIR="$PWD/data/accounts/personal" claude auth logindata/ is already mounted at /data in the container, so no extra mount is
needed. Credentials refresh themselves in place, and each account keeps its own
session transcripts.
Commands then take an optional trailing account name. Listings cover every account; commands that act default to the primary:
/warmup # primary account
/warmup personal # that account
/warmup all # every account
/session # every account's window
/daily 7:00, 13:00 5h work # schedule for one account
/accounts # names, config dirs, current windows
/workday is the exception: with no account named it plans every account and
staggers them, offsetting each by an even fraction of a session so their windows
do not all reset together:
/workday 9am-6pm
→ work: 06:00, 11:05, 16:10
personal: 08:30, 13:35
Read as a single timeline, a fresh window now arrives at 06:00, 08:30, 11:05,
13:35 and 16:10 — every ~2.5 hours instead of every 5, with two live windows at
any moment. Name an account (/workday 9am-6pm work) to plan just that one.
Leaving CLAUDE_ACCOUNTS unset runs a single account exactly as before, with no
account labels in any output. Existing rows are attributed to the primary
account when the database is migrated.
- Warmup: Runs
claude -p "ready" --output-format jsonwhich sends a minimal prompt to Claude, starting the 5-hour session timer. The JSON response includessession_idand token usage. - Session tracking: Each warmup is recorded in a local SQLite database with start time, expiry (start + 5h), and token usage.
- Scheduling: One-time and daily schedules are persisted in SQLite and restored on bot restart using
setTimeout. - Auth: Only Telegram user IDs listed in
TELEGRAM_ALLOWED_USER_IDScan use the bot.
Session tracking is self-managed — the bot records when it starts a session and computes time remaining locally. There is no Anthropic API to query session status, and we intentionally avoid reverse-engineering internal endpoints to prevent account bans.
This means:
- Sessions started outside the bot (e.g. from your dev machine) are not tracked. The bot only knows about sessions it started itself.
- If you start a session manually and then ask the bot, it may show "no active session" or stale data.
- The bot is designed to be the sole session starter — use it from a dedicated VPS for best results.
- Claude Code CLI must be installed and authenticated on the host machine (or inside the Docker container).
# edit .env with your values
cp .env.example .env
pnpm install
pnpm build
pnpm start| Variable | Description |
|---|---|
TELEGRAM_BOT_TOKEN |
Bot token from @BotFather |
TELEGRAM_ALLOWED_USER_IDS |
Comma-separated Telegram user IDs |
TIMEZONE |
Display timezone (default: UTC) |
DB_PATH |
SQLite database path (default: data/bot.db) |
First copy your local Claude CLI auth files into the ignored data/ directory:
mkdir -p data/claude-home
cp -a ~/.claude data/claude-home/.claude
cp ~/.claude.json data/claude-home/.claude.jsonThen start the container:
docker compose up -d --buildDo not commit data/claude-home or .env; they contain local credentials and are ignored by git.

