Async, decorator-driven, deployable in an hour.
/build/project-telegram-summarizer with the starter and the rubric already in it.Build a Telegram bot that listens for URLs in your DM, fetches the page, sends a 3-line summary back via Claude. Async, uses decorators for message routing, and ships on Railway's free tier.
This is your first real AI product — one API you built, one AI API you called, one deploy. It's also the pattern behind 90% of AI startups you'll ever work at.
Build against this exact list — it is the spec your published project is judged on.
Copy this into a new file — or open it in a workspace as bot.py. The TODO parts are yours to fill.
"""
tl;dr bot — summarizes any URL you send.
Env vars:
TELEGRAM_TOKEN — from @BotFather
ANTHROPIC_API_KEY — from console.anthropic.com
Deploy:
Set env vars on Railway/Fly.io, then `python bot.py`.
"""
from __future__ import annotations
import os
import re
import sys
# PyRun's workspace runs Python inside your browser (sys.platform is
# "emscripten" there). A Telegram bot needs a server that stays up, a bot
# token, and packages a browser can't install, so in the browser this file
# explains itself instead of crashing. On your machine it runs normally.
IN_BROWSER = sys.platform == "emscripten"
if not IN_BROWSER:
import httpx
from anthropic import Anthropic
from telegram import Update
from telegram.ext import (
Application,
CommandHandler,
ContextTypes,
MessageHandler,
filters,
)
URL_RE = re.compile(r"https?://[^\s]+")
anthropic = None if IN_BROWSER else Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
async def start(update: Update, _: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text(
"Send me a URL and I'll send you a 3-line summary."
)
async def handle_message(update: Update, _: ContextTypes.DEFAULT_TYPE) -> None:
text = update.message.text or ""
match = URL_RE.search(text)
if not match:
await update.message.reply_text(
"Send me a URL and I'll summarize it. Nothing else works yet."
)
return
url = match.group(0)
# TODO: fetch page (respect content-type, timeout, redirects)
# TODO: extract main text (readability is nice, but text-only is fine)
# TODO: call anthropic.messages.create with a 3-line summary prompt
# TODO: reply with formatted summary + source URL
...
def main() -> None:
app = Application.builder().token(os.environ["TELEGRAM_TOKEN"]).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
app.run_polling()
if __name__ == "__main__":
if IN_BROWSER:
print("This bot runs on a server, not in the browser tab.")
print("1. pip install python-telegram-bot anthropic httpx")
print("2. Set TELEGRAM_TOKEN (from @BotFather) and ANTHROPIC_API_KEY")
print("3. python bot.py (on your machine, or on Railway / Fly.io)")
print("You can write and organise the code here; run it there.")
else:
main()
bot.py and a README.md holding all 9 rubric items as a checklist.pyrun.in/u/<handle>/w/project-telegram-summarizer.pyrun.in/u/<handle> portfolio page.Your workspace saves to this browser only. Sign in to keep it across devices and to publish it.
Deploy first, get a Telegram bot handle (@yourbotname), and paste that + your GitHub repo URL below. If you connect your own API key, the optional AI review reads the code you paste; it does not message your live bot.
A published PyRun workspace URL (pyrun.in/u/<handle>/w/project-telegram-summarizer) works as the public URL too.