Want to automatically post latest news to your Telegram channel every hour — without running any server or paying for hosting? In this guide, you will learn how to build a fully automated news bot using Python, Google News RSS, Telegram, and GitHub Actions.

By the end, you will have a bot that fetches fresh news on any topic and delivers it straight to your Telegram channel — completely free, no server required



👉 Live Project: You can view the complete working example on GitHub: https://github.com/aiocrafters/telegram-channel


🚀 What You Are Building

A bot that:

  • Fetches news from Google News RSS (based on a topic like AI News)
  • Scrapes article text and generates AI summaries using OpenRouter
  • Sends formatted updates to your Telegram channel
  • Runs automatically every hour via GitHub Actions
  • Tracks sent articles to avoid duplicate posts

🧠 Why This Approach is Powerful

Most bots require external infrastructure. This approach uses GitHub Actions as a free scheduler, giving you everything you need at zero cost.

Requirement Traditional Bot This Method
Hosting / VPS Required ❌ Not needed ✅
Background server Required ❌ Not needed ✅
Monthly cost $5–$20+ 💸 $0 ✅
Maintenance High Minimal ✅
Scalability Manual Easy ✅

1 Create a Telegram Bot

The first step is to create a bot using Telegram's official BotFather.

  1. Open Telegram on your phone or desktop
  2. Search for BotFather and open the chat
  3. Send the following commands one by one:
/start
/newbot
  1. Enter a display name for your bot (e.g., AI News Bot)
  2. Enter a username ending with bot (e.g., ainews_bot)
  3. Copy the Bot Token that BotFather provides — you will need this later

⚠ Keep your Bot Token private. This token gives full control over your bot. Never share it publicly or commit it directly to your repository.


2 Create a Telegram Channel

  1. Open Telegram and tap the pencil icon to start a New Channel
  2. Give your channel a name (e.g., AI News Updates)
  3. Set the channel as Public and choose a username
  4. Open channel settings → Administrators → Add Administrator
  5. Search for your bot username and add it as an admin with Post Messages permission
  6. Copy your channel username — it looks like: @your_channel_name

💡 Tip: Your Telegram Chat ID for a public channel is simply its username (e.g., @ainewsupdates). For private channels, you will need the numeric ID.


3 Set Up API Keys & Config

To enable AI summaries, you will need an OpenRouter API key. Go to OpenRouter.ai, sign up, and create an API key.

Create a file named config.py in your repository to separate the configuration:

# config.py
TOPIC = "AI News"
MODEL = "openai/gpt-4o-mini"
MAX_ARTICLES = 10
RSS_URL = f"https://news.google.com/rss/search?q={TOPIC.replace(' ', '%20')}"
SEEN_FILE = "seen_news.json"
SUMMARY_CACHE_FILE = "summary_cache.json"

Now, create prompt_template.py to instruct the AI on how to summarize:

# prompt_template.py
SUMMARY_PROMPT_TEMPLATE = """
You are a professional journalist writing crisp news briefs.

Your task:
- Extract the MOST important facts
- Focus on WHAT happened, WHERE, and WHY it matters

Write output in this format:
🧠 <One-line headline>

🔹 Point 1  
🔹 Point 2  
🔹 Point 3  

Rules:
- Max 5 points
- Each point short (8–15 words)
- Simple English
- No opinions

News:
{content}
"""

4 Write the Main Python Script

Create a file named main.py. This script uses newspaper3k to scrape the text and calls the OpenRouter API to summarize it.

import requests, feedparser, json, os, time
from datetime import datetime
from dotenv import load_dotenv
from newspaper import Article
from config import TOPIC, MODEL, RSS_URL, MAX_ARTICLES, SEEN_FILE, SUMMARY_CACHE_FILE
from prompt_template import SUMMARY_PROMPT_TEMPLATE

load_dotenv()
BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY")

def load_json_file(file):
    return json.load(open(file, "r")) if os.path.exists(file) else {}

def save_json_file(file, data):
    json.dump(data, open(file, "w"), indent=2)

def extract_article_text(url):
    try:
        article = Article(url)
        article.download()
        article.parse()
        text = article.text.strip()
        return text[:2000] if len(text) >= 200 else None
    except:
        return None

def generate_summary(news, cache):
    news_id = news["link"]
    if news_id in cache: return cache[news_id]

    text = extract_article_text(news.get("real_link", news["link"]))
    content = text if text else news["title"]

    url = "https://openrouter.ai/api/v1/chat/completions"
    headers = {"Authorization": f"Bearer {OPENROUTER_API_KEY}", "Content-Type": "application/json"}
    payload = {"model": MODEL, "messages": [{"role": "user", "content": SUMMARY_PROMPT_TEMPLATE.format(content=content)}]}

    try:
        res = requests.post(url, headers=headers, json=payload, timeout=30).json()
        summary = res["choices"][0]["message"]["content"].strip()
        cache[news_id] = summary
        return summary
    except:
        return news["title"]

def run():
    seen = set(load_json_file(SEEN_FILE))
    summary_cache = load_json_file(SUMMARY_CACHE_FILE)
    feed = feedparser.parse(RSS_URL)

    for entry in feed.entries[:MAX_ARTICLES]:
        link = entry.get("link", "")
        if not link or link in seen: continue

        news = {"title": entry.get("title", ""), "link": link, "published": entry.get("published", ""), "source": entry.get("source", {}).get("title", ""), "real_link": link}
        summary = generate_summary(news, summary_cache)

        message = f"🚨 {news['title']}\n\n{summary}\n\n📍 {TOPIC}\n📰 {news['source']}\n🕒 {news['published']}\n\n🔗 <a href='{link}'>Read more</a>"

        requests.post(f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage", json={"chat_id": CHAT_ID, "text": message, "parse_mode": "HTML"})
        
        seen.add(link)
        time.sleep(1)

    save_json_file(SEEN_FILE, list(seen))
    save_json_file(SUMMARY_CACHE_FILE, summary_cache)

if __name__ == "__main__":
    run()

Here is what the script does:

  • Fetches the latest articles from Google News RSS
  • Checks seen_news.json to prevent duplicates
  • Scrapes the article content and sends it to OpenRouter API for summarization
  • Caches the summaries in summary_cache.json and posts to Telegram

5 Create the Requirements File

Create a requirements.txt file in the root of your repository:

requests
feedparser
python-dotenv
newspaper3k
lxml_html_clean

This tells GitHub Actions (and your local environment) which packages to install before running the script.


6 Set Up Environment Variables

For local testing, create a .env file in the project root:

TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=@your_channel_name
OPENROUTER_API_KEY=your_openrouter_api_key_here

⚠ Important: Add .env to your .gitignore file to prevent accidentally pushing your credentials to GitHub.

Add this line to .gitignore:

.env

In production (GitHub Actions), these values will be read from encrypted GitHub Secrets — not from the .env file.


7 Automate with GitHub Actions

Create the following file in your repository:

.github/workflows/news.yml

Add this content:

name: Google News to Telegram

on:
  schedule:
    - cron: "0 * * * *"
  workflow_dispatch:

permissions:
  contents: write

jobs:
  run-news-bot:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
          cache: "pip"

      - run: pip install -r requirements.txt

      - run: |
          [ -f seen_news.json ] || echo "[]" > seen_news.json
          [ -f summary_cache.json ] || echo "{}" > summary_cache.json

      - run: python main.py
        env:
          TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
          TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
          OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}

      - run: |
          git config --global user.name "github-actions"
          git config --global user.email "actions@github.com"
          git add seen_news.json summary_cache.json
          git commit -m "Update seen + summary cache" || echo "No changes"
          git push

💡 workflow_dispatch: This option lets you trigger the workflow manually from the GitHub Actions tab — useful for testing before waiting for the scheduled run.

Here is what each step does:

Step Purpose
actions/checkout@v4 Clones your repository into the runner
actions/setup-python@v5 Installs Python 3.11
pip install Installs the required Python packages
Initialize seen_news.json Creates the file on first run if it does not exist
python main.py Runs the bot script with secrets injected as environment variables
Git commit and push Saves the updated seen_news.json and summary_cache.json back to the repository

8 Add GitHub Secrets

Your bot token and channel ID must be stored as encrypted secrets — never hardcoded in the workflow file.

  1. Open your GitHub repository
  2. Go to Settings → Secrets and variables → Actions
  3. Click New repository secret
  4. Add the following secrets:
Secret Name Value
TELEGRAM_BOT_TOKEN Your bot token from BotFather
TELEGRAM_CHAT_ID Your channel username (e.g., @your_channel_name)
OPENROUTER_API_KEY Your OpenRouter API key

⚠ Secrets are encrypted and never visible in logs. GitHub Actions automatically masks them if they appear in output.


🔄 How the Full System Works

  1. GitHub Actions triggers the workflow every hour (cron schedule)
  2. The runner checks out your repository and installs dependencies
  3. The Python script fetches the latest articles from Google News RSS
  4. It compares each article URL against seen_news.json
  5. For new articles, it scrapes the full text and sends it to the OpenRouter API to generate a summary
  6. These AI-summarized articles are sent to the Telegram channel
  7. The updated seen_news.json and summary_cache.json are committed back to the repository
  8. On the next run, the process repeats seamlessly

⏱ Understanding the Cron Schedule

cron: "30 * * * *"
Field Value Meaning
Minute 30 At 30 minutes past the hour
Hour * Every hour
Day * Every day
Month * Every month
Weekday * Every day of the week

💡 Note: GitHub Actions uses UTC time. The 30 minute offset helps align runs more closely with IST (UTC+5:30) timing, so news lands on the hour in Indian Standard Time.


🎯 Customization Ideas

🔹 Change the News Topic

Simply update the TOPIC variable in main.py:

TOPIC = "Artificial Intelligence"

This will fetch news about AI instead. You can use any keyword or phrase that Google News supports.

🔹 Run Multiple Topics

Create separate workflow files or loop over a list of topics:

TOPICS = ["AI News", "Government Jobs", "Stock Market"]

for topic in TOPICS:
    fetch_and_send(topic)

🔹 Add Custom Hashtags

Customize the message footer with relevant hashtags for discoverability:

f"#AiNews #BreakingNews #India"

🔹 Adjust the Posting Frequency

Modify the cron expression to post every 2 hours, 6 hours, or daily:

# Every 2 hours
cron: "0 */2 * * *"

# Twice a day (9am and 9pm UTC)
cron: "0 9,21 * * *"

🚀 Advanced Upgrade Ideas

Once your basic bot is running, here are powerful extensions you can build on top of it:

  • 🎛️ Multiple Channels — Route technical news to one Telegram channel and general news to another
  • 🌐 Store in Supabase — Replace seen_news.json with a Supabase table for better scalability and a browsable history
  • 📡 Public API — Expose the news data through a REST API so your website can consume it
  • 📊 Admin Dashboard — Build a simple web interface to view sent articles, manage topics, and monitor bot health
  • 🧵 Thread-style Posts — Group related articles into a single Telegram thread for cleaner channel presentation
  • 🌍 Multi-language Support — Use Google Translate API to post news in multiple languages

💡 Pro Tips

  • Keep bot admin in channel: If the bot loses admin status, it will fail silently. Check channel admin settings periodically.
  • Rate limiting: Telegram allows ~30 messages per second per bot. Add a small delay between sends if posting many articles:
    import time
    time.sleep(1)  # 1 second between messages
  • Use RSS over scraping: Google News RSS is official and stable. Scraping HTML pages is fragile and can break without warning.
  • Test with workflow_dispatch first: Before relying on the cron schedule, trigger the workflow manually to confirm everything works.
  • Monitor GitHub Actions logs: Go to the Actions tab in your repository to see the output of each run and debug any failures.

🏁 Final Result

You have now built a fully automated, serverless, free news bot that:

  • Fetches fresh news from Google News RSS on any topic
  • Posts formatted updates to your Telegram channel automatically
  • Runs every hour using GitHub Actions at zero cost
  • Tracks sent articles to prevent duplicate posts

This is one of the simplest yet most powerful automation systems you can build as a developer. You can expand it into a full News SaaS, alert system, or content automation engine.


Start small, run it for a week, then layer on the advanced features — your Telegram channel will grow on autopilot. 🔥


⭐ If you found this guide useful, consider starring the project on GitHub and sharing it with others:

https://github.com/aiocrafters/telegram-channel


Built with ❤️ by AIOCrafters