Every morning I used to open 5 tabs: weather, news, crypto prices, calendar, and email. That's 10 minutes of scattered attention before I even start working.
So I built a bot that sends me everything in one Telegram message at 7 AM. Here's the entire thing.
The Stack
- Python 3.10+ (no external frameworks)
- Open-Meteo API — weather (free, no key)
- CoinGecko API — crypto prices (free, no key)
- NewsAPI — headlines (free tier, key required)
- Telegram Bot API — delivery
Total cost: $0/month. Runs on any machine with cron.
Step 1: Weather (Open-Meteo)
No API key. No signup. Just works.
\`python
import requests
from datetime import datetime
def get_weather(lat=55.75, lon=37.62): # Moscow
"""Get current weather + today's forecast"""
r = requests.get('https://api.open-meteo.com/v1/forecast', params={
'latitude': lat, 'longitude': lon,
'current_weather': True,
'daily': 'temperature_2m_max,temperature_2m_min,precipitation_sum',
'timezone': 'auto', 'forecast_days': 1
}).json()
cw = r['current_weather']
daily = r['daily']
return (
f"🌡 Weather: {cw['temperature']}°C "
f"(high {daily['temperature_2m_max'][0]}°, low {daily['temperature_2m_min'][0]}°)\n"
f"💧 Rain: {daily['precipitation_sum'][0]}mm"
)
`\
Step 2: Crypto Prices (CoinGecko)
Also free, also no key needed.
\`python
def get_crypto():
"""Get top 3 crypto prices with 24h change"""
r = requests.get('https://api.coingecko.com/api/v3/simple/price', params={
'ids': 'bitcoin,ethereum,solana',
'vs_currencies': 'usd',
'include_24hr_change': True
}).json()
lines = []
symbols = {'bitcoin': 'BTC', 'ethereum': 'ETH', 'solana': 'SOL'}
for coin, sym in symbols.items():
price = r[coin]['usd']
change = r[coin]['usd_24h_change']
arrow = '📈' if change > 0 else '📉'
lines.append(f"{arrow} {sym}: ${price:,.0f} ({change:+.1f}%)")
return '\n'.join(lines)
`\
Step 3: News Headlines (NewsAPI)
Free tier gives you 100 requests/day — more than enough for a daily bot.
\`python
def get_news(api_key, country='us', count=5):
"""Get top headlines"""
r = requests.get('https://newsapi.org/v2/top-headlines', params={
'country': country, 'pageSize': count, 'apiKey': api_key
}).json()
lines = []
for i, article in enumerate(r.get('articles', [])[:count], 1):
lines.append(f"{i}. {article['title']}")
return '📰 Headlines:\n' + '\n'.join(lines)
`\
Step 4: Telegram Delivery
\python
def send_telegram(bot_token, chat_id, text):
"""Send message via Telegram"""
requests.post(
f'https://api.telegram.org/bot{bot_token}/sendMessage',
json={'chat_id': chat_id, 'text': text, 'parse_mode': 'HTML'}
)
\\
Putting It All Together
\`python
!/usr/bin/env python3
"""Morning briefing bot — runs daily via cron"""
import requests
from datetime import datetime
=== CONFIG ===
TELEGRAM_TOKEN = 'your-bot-token'
TELEGRAM_CHAT_ID = 'your-chat-id'
NEWS_API_KEY = 'your-newsapi-key'
LAT, LON = 40.71, -74.01 # New York
def get_weather():
r = requests.get('https://api.open-meteo.com/v1/forecast', params={
'latitude': LAT, 'longitude': LON, 'current_weather': True,
'daily': 'temperature_2m_max,temperature_2m_min,precipitation_sum',
'timezone': 'auto', 'forecast_days': 1
}).json()
cw = r['current_weather']
d = r['daily']
return f"🌡 {cw['temperature']}°C (↑{d['temperature_2m_max'][0]}° ↓{d['temperature_2m_min'][0]}°) 💧{d['precipitation_sum'][0]}mm"
def get_crypto():
r = requests.get('https://api.coingecko.com/api/v3/simple/price', params={
'ids': 'bitcoin,ethereum,solana', 'vs_currencies': 'usd', 'include_24hr_change': True
}).json()
lines = []
for coin, sym in [('bitcoin','BTC'),('ethereum','ETH'),('solana','SOL')]:
p, c = r[coin]['usd'], r[coin]['usd_24h_change']
lines.append(f"{'📈' if c>0 else '📉'} {sym}: ${p:,.0f} ({c:+.1f}%)")
return '\n'.join(lines)
def get_news():
r = requests.get('https://newsapi.org/v2/top-headlines', params={
'country': 'us', 'pageSize': 5, 'apiKey': NEWS_API_KEY
}).json()
return '📰 ' + '\n'.join(f"{i}. {a['title']}" for i,a in enumerate(r.get('articles',[])[:5],1))
def main():
msg = f"☀️ Morning Briefing — {datetime.now().strftime('%B %d, %Y')}\n\n"
msg += get_weather() + '\n\n'
msg += get_crypto() + '\n\n'
msg += get_news()
requests.post(f'https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage',
json={'chat_id': TELEGRAM_CHAT_ID, 'text': msg, 'parse_mode': 'HTML'})
print('Briefing sent!')
if name == 'main':
main()
`\
That's ~50 lines of actual logic. Save it, add a cron job, done.
Setting Up Cron
\`bash
Run every day at 7:00 AM
crontab -e
0 7 * * * /usr/bin/python3 /path/to/briefing.py
`\
Extending It
I've added more modules over time:
- Calendar — Google Calendar API shows today's events
- GitHub — shows overnight stars/issues on my repos
- Exchange rates — USD/EUR/GBP via exchangerate.host
Each module is just a function that returns a string. The architecture stays dead simple.
What I Learned
- Free APIs are incredibly powerful — most developers underuse them
- Telegram bots are the simplest delivery mechanism — beats building a UI
- Cron + Python + free APIs = infinite possibilities with zero infrastructure cost
- Start with 3 data sources, not 10 — you can always add more
For a list of 300+ free APIs to build your own version, check out my curated API directory.
What would you add to your morning briefing? I'm curious what data other developers check first thing.
Follow for more Python automation tutorials.
Top comments (0)