Assistant
A private Telegram assistant that runs on my homelab. It answers questions about my schedule and courses, messages me 10 minutes before every lecture with a recap of where the last one stopped, and can search my university email read-only. The rule it's built on: code decides the facts, the AI only writes the words — and every reply is checked against the source data.
Timeline
Click a stop to open the chaptern8n on the homelab
Stood up n8n in its own container behind the reverse proxy, firewalled so only the proxy and dashboard can reach it. Instead of a paid API key, n8n reaches Claude Code in the AI dev container over SSH, using a key that only works from n8n's address and can't open a shell session.
Facts in code
The first version let the model read my schedule, and it dropped an event once in eight runs. I redesigned it: plain Python parses my timetable and deadlines, the model only turns that list into a friendly reply, and the script checks that every event title made it into the answer before sending it.
Telegram bot
Connected it to a Telegram bot that answers only me. It polls Telegram rather than using a webhook, because nothing on the homelab is exposed to the internet. Commands cover the next hour, today, tomorrow and the week; free text goes to a general assistant that can read my project notes and read or edit my uni and personal notes — and nothing else.
Lecture reminders
An n8n workflow checks my timetable during university hours and sends a reminder 10 minutes before each lecture, with the course's week topics and a recap of the previous lecture. Code picks which slides were covered from a progress log I update by message; the model only writes 3–4 bullets about them, with slide titles as the fallback.
Uni email, read-only
My university account doesn't allow app passwords, so instead of IMAP the assistant reaches Gmail through an MCP connector. Only the search and read tools are allowed; every write tool — send, reply, forward, draft, delete, label — is explicitly denied. Email access only switches on when my message is actually about email.
Daily use
It's in daily use. The same bot also carries gym reminders generated from a single plan file, and the homelab's uptime and update alerts. Next on the list is a morning brief.
Milestones
What broke
From the repo
# code decides the facts, the AI only writes the words
events = agenda(window) # parsed from timetable.md
if not events:
return "Nothing scheduled." # no AI call at all
for writer in (claude_haiku, codex):
reply = writer(prompt(events, question))
# every real event title must survive into the reply
if reply and all(norm(e.title) in norm(reply) for e in events):
return reply
return plain_list(events) + "\n(AI unavailable, raw list)"