Telegram filter bot and Bluesky poster A developer created a three-bot system that monitors Telegram OSINT and geopolitics channels, filters messages through keyword and LLM evaluation, and automatically posts approved stories to Bluesky with AI-generated summaries and image analysis. A three-bot system that monitors Telegram OSINT/geopolitics channels, filters messages through a keyword pre-filter and LLM evaluation, and automatically posts approved stories to Bluesky with AI-generated summaries, image analysis, and automatic story threading. ┌─────────────────────────────────────────────────────────┐ │ controlbot.py │ │ Telegram bot that manages the other two │ │ /start, /stop, /restart, /startall, /logs, etc. │ └──────────────┬──────────────────────┬───────────────────┘ │ manages │ manages ▼ ▼ ┌──────────────────────────┐ ┌──────────────────────────┐ │ filterbot.py │ │ posterbot.py │ │ │ │ │ │ Monitors TG channels │ │ Watches filterbot output │ │ Keyword pre-filter │──│ AI-formats for Bluesky │ │ Semantic deduplication │ │ Vision: logo detection │ │ Auto-translation │ │ Vision: auto-captioning │ │ LLM evaluation Groq │ │ Semantic deduplication │ │ Urgency tagging │ │ Story threading │ │ Forwards to destination │ │ Posts to Bluesky │ └──────────────────────────┘ └──────────────────────────┘ | File | Description | |---|---| controlbot.py | Telegram bot supervisor. Runs as the parent process and manages filterbot and posterbot as child processes. Send commands from your phone to start/stop/restart bots, view logs, and check heartbeat stats. | filterbot.py | The core intelligence filter. Connects to your Telegram user account via Telethon, monitors source channels, runs a three-tier keyword pre-filter override → instant-reject → must-match , detects semantic duplicates, auto-translates non-English messages, and evaluates importance via Groq LLM. Approved messages are forwarded to a destination Telegram channel with urgency tags 🔴 FLASH / 🟡 NOTABLE . | posterbot.py | Bluesky publisher. Watches the destination channel that filterbot writes to, batches incoming messages, uses Groq to rewrite them as concise Bluesky posts ≤300 chars , runs vision analysis on images logo detection + auto-captioning , detects story updates and posts them as threaded replies, and publishes to Bluesky. | TOOL export telegram channels.py | Utility script. Connects to your Telegram account and exports all channels you're subscribed to into a channels.json config file. This is the first thing you run when setting up the project. | channels.json | Your personal channel configuration gitignored . Contains channel IDs, names, credibility tiers, and the destination channel. Generated by the export tool. | channels.example.json | Template showing the channels.json format. Copy this and fill it in if you prefer manual setup over the export tool. | filters.json | Your keyword filters and LLM prompt gitignored . Defines what topics the bot looks for, what it rejects, and how the AI evaluates messages. | filters.example.json | Template showing the filters.json format with placeholder keywords. Copy and customize for your use case. | private.env | Environment variables file containing all API keys and tokens. Never commit this to Git. | You need credentials from four services: | Service | What you need | Where to get it | |---|---|---| Telegram API | TELEGRAM API ID and TELEGRAM API HASH | | Telegram Bot CONTROL BOT TOKEN @BotFather https://t.me/BotFather on Telegram → /newbot Telegram User ID ADMIN TELEGRAM ID @userinfobot https://t.me/userinfobot on Telegram Groq GROQ API KEY console.groq.com/keys https://console.groq.com/keys — free tier available Bluesky BLUESKY HANDLE and BLUESKY APP PASSWORD bsky.app/settings/app-passwords https://bsky.app/settings/app-passwords | Software | Download | |---|---| Python 3.11+ | | pip install telethon python-telegram-bot python-dotenv deep-translator groq pydantic langdetect sentence-transformers torch atproto git clone https://github.com/bananaosint/bsky-poster.git cd bsky-poster pip install telethon python-telegram-bot python-dotenv deep-translator groq pydantic langdetect sentence-transformers torch atproto Create a file called private.env in the project root with the following contents: Telegram TELEGRAM API ID=your api id TELEGRAM API HASH=your api hash Bluesky BLUESKY HANDLE=yourhandle.bsky.social BLUESKY APP PASSWORD=xxxx-xxxx-xxxx-xxxx Groq GROQ API KEY=gsk xxxxxxxxxxxx Control Bot CONTROL BOT TOKEN=1234567890:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ADMIN TELEGRAM ID=your numeric telegram id You have two options: The export tool automatically finds all Telegram channels you're subscribed to and generates a channels.json config file: python " TOOL export telegram channels.py" On first run, Telethon will ask for your phone number and a login code sent to Telegram. This creates a local session file so you only need to authenticate once. The tool will create channels.json with all your channels. Then open it and: Set — Change destination channel -100000000000 to the Telegram channel ID where filterbot should forward approved messages. This must be a channel you own or have admin access to. Remove channels you don't want monitored. Set channel tiers — Rate each source's credibility: "high" — Official accounts, verified outlets IDF, Al Jazeera, etc. "medium" or "" — General news aggregators, unverified "low" — Propaganda outlets, known bias sources Add rapid-update channels — Channels that post many updates per minute e.g., air raid alert bots . These get a lower dedup threshold so legitimate rapid-fire updates aren't dropped. Copy the template and fill it in manually: cp channels.example.json channels.json Then edit channels.json — see the format in channels.example.json . You can find channel IDs by forwarding a message from the channel to @userinfobot https://t.me/userinfobot . Copy the example filter config: cp filters.example.json filters.json Then edit filters.json to define what topics your bot monitors . The file has four sections: Messages containing any of these words bypass ALL other checks and go straight to the LLM. Use for your most critical, unambiguous signals. Messages containing any of these are dropped immediately. Use for spam, ads, and off-topic noise. Messages must contain at least one of these to proceed to the LLM. This is your main topic gate — add keywords for whatever you're monitoring . The system prompt that tells the LLM what "important" means for your use case. Customize this to match your topic. Examples for different use cases: Cryptocurrency / DeFi monitoring { "override keywords": { "keywords": "hack", "exploit", "rug pull", "flash loan attack", "bridge drained", "sec charges" }, "instant reject keywords": { "keywords": "airdrop", "giveaway", "join our group", "buy now", "100x gem", "not financial advice" }, "must match keywords": { "keywords": "bitcoin", "ethereum", "defi", "hack", "exploit", "sec", "regulation", "whale", "liquidation", "stablecoin", "depeg", "exchange", "binance", "coinbase" }, "llm prompt": { "system prompt": "You are a crypto intelligence filter. Approve messages about: security exploits, major price movements 5% , regulatory actions, exchange issues, whale movements. Reject: shilling, price predictions, memes, influencer opinions. Return JSON with 'important' bool , 'urgency' 1-3 , 'reason' string ." } } Sports / Football scores { "override keywords": { "keywords": "goal ", "red card", "penalty", "injury time", "transfer confirmed" }, "instant reject keywords": { "keywords": "bet now", "odds", "prediction", "fantasy", "subscribe" }, "must match keywords": { "keywords": "goal", "score", "match", "transfer", "signed", "injury", "lineup", "suspended", "champions league", "premier league", "red card", "var" }, "llm prompt": { "system prompt": "You are a football news filter. Approve: live match events goals, cards, substitutions , confirmed transfers, injuries, official announcements. Reject: rumours, opinions, betting, fantasy football. Return JSON with 'important' bool , 'urgency' 1-3 , 'reason' string ." } } Natural disaster / Weather alerts { "override keywords": { "keywords": "tsunami warning", "earthquake", "tornado warning", "hurricane", "evacuation order" }, "instant reject keywords": { "keywords": "donate", "pray for", "climate change debate", "subscribe" }, "must match keywords": { "keywords": "earthquake", "tsunami", "tornado", "hurricane", "wildfire", "flood", "evacuation", "magnitude", "category", "storm surge", "landslide", "volcanic" }, "llm prompt": { "system prompt": "You are a disaster alert filter. Approve: active natural disaster reports, official warnings, evacuation orders, casualty reports, damage assessments. Reject: general weather forecasts, climate opinions, historical events. Return JSON with 'important' bool , 'urgency' 1-3 , 'reason' string ." } } python controlbot.py Then on Telegram, message your control bot: /startall This starts both filterbot and posterbot. On first run, filterbot will prompt in the terminal for your Telegram phone number and a login code. | Command | Description | |---|---| /status | Show running status of all bots | /start