Opbygning af en Telegram-nyhedsbots med Python (2026-udgave)
En Telegram-nyhedsbot er et Python-program, der henter artikler fra en nyhedskilde API, filtrerer dem efter emne eller sentiment og poster dem automatisk til en Telegram-chat eller -kanal. I 2026 er den hurtige måde at bygge en på aiogram 3.27 til den asynkrone Telegram-klient, APScheduler til in-process-planlægning og en nyhedskilde API, der viser sentiment og entiteter, så du kan poste kuraterede strømme i stedet for alt.
Vigtigste pointer
- Brug
aiogram3.27 (async) frem forpython-telegram-bottil kanal auto-posting bots med over ~50 samtidige brugere.- Filtrer nyheds API responsen efter
sentiment.overall.polarityogentities[], så kanalen poster kurateret indhold, ikke alt.APScheduler'sAsyncIOSchedulerkører inde i aiogram event loop – én proces til polling, fetching og planlagt posting.- Leveres som et multi-stage Python 3.12 Docker-image med
restart: unless-stopped; Fly.io, Railway eller en $5/måned VPS fungerer alle.
Denne vejledning gennemgår en produktionsklar kanalbot på omkring 100 linjer: den henter artikler hvert 15 minut, fjerner alt, der ikke matcher sentiment- og entitetsfilteret, deduplikerer efter artikel-ID og overlever en container-genstart. Ved afslutningen vil du have en Dockeriseret service, du kan placere på enhver VPS.
Hvad du vil bygge
- En asynkron Telegram-bot, der automatisk poster nyheder til en kanal
- Sentiment + entitetsfilter (e.g., kun post negative artikler, der nævner Tesla)
- APScheduler kørende inde i aiogram event loop — ingen cron, ingen separat proces
- Dedukation efter artikel-ID og forsøg igen ved Telegram rate limits
- En multi-stage Dockerfile med
restart: unless-stopped, som du kan sende i dag
aiogram vs python-telegram-bot — hurtig beslutning
Begge biblioteker vedligeholdes i 2026; det rigtige valg afhænger af arbejdsbyrden.
| Kriterium | aiogram 3.27 | python-telegram-bot 21.x |
|---|---|---|
| Samtidighedsmodel | Kun Async (asyncio) | Async siden v20; sync wrapper stadig tilgængelig |
| Latenstid ved 100 samtidige opdateringer | ~80 ms 95 | ~600 ms 95 (synkron) / ~120 ms (asynkron) |
| Kanal automatisk post + planlægger i én proces | Nativ pasform (AsyncIOScheduler) | Virker, men mere boilerplate |
| FSM, middleware, filtre | Førsteklasses | Førsteklasses |
| Bedst til | Kanal-/gruppe-bots, nyheds-/notifikationsstrømme, >500 brugere | Personlige bots, kommando-drevne arbejdsgange, gradvis synkronisering→asynkron migrering |
| Fællesskabets bane i 2026 | Hurtigere bevægelse, fokus på kanal-bot | Større økosystem, flere ældre eksempler |
aiogram har været async-only siden v3, hvilket betyder, at den deler en enkelt event loop med AsyncIOScheduler og httpx.AsyncClient — ingen bridging, ingen thread pools, ingen callback-wrapping. Praktisk regel: under 50 samtidige opdateringer er begge biblioteker fine; over 500, eller for enhver channel auto-poster, vælg aiogram. Vi vil bruge aiogram til resten af denne tutorial.
Forudsætninger
- Python 3.10 eller nyere (3.12 anbefales)
- Et Telegram bot-token fra
@BotFather - En Telegram-kanal, hvor din bot er tilføjet som administrator med tilladelse til "Post beskeder"
- En APITube API-nøgle fra apitube.io — gratis niveau inkluderer sentiment og entiteter på enhver artikel (oplysning: jeg arbejder på APITube)
- Kendskab til
async/await— hvisawait client.get(...)ser fremmed ud, skim først Python asyncio-guiden
Sådan bygger du en Telegram-nyhedsbot i Python (6 trin)
- Få en bot-token fra BotFather
- Opsæt projektet og installer afhængigheder
- Hent nyheder fra APITube (asynkron)
- Filtrer efter sentiment og enheder
- Planlæg kanalindlæg med APScheduler
- Tilføj
/start,/subscribe,/filterkommandoer
Trin 1 — Få et bot-token fra BotFather
Åbn Telegram, send besked til @BotFather, send /newbot, vælg et navn og et brugernavn. Du får en token, der ligner 8123456789:AAH.... Gem den.
Opret derefter en kanal (eller brug en eksisterende), åbn dens indstillinger → Administratorer → Tilføj administrator → søg efter din bots brugernavn → giv "Send beskeder". Få kanal-ID'et ved at videresende enhver kanalbesked til @JsonDumpBot – du vil have chat.id, som for kanaler er et negativt tal som -1001234567890.
Trin 2 — Opsæt projektet
mkdir telegram-news-bot && cd telegram-news-bot
python -m venv .venv && source .venv/bin/activate
pip install aiogram==3.27.0 apscheduler==3.10.4 httpx==0.27.0 python-dotenv==1.0.1
Opret .env:
BOT_TOKEN=8123456789:AAH...
CHANNEL_ID=-1001234567890
APITUBE_KEY=your_apitube_key
Trin 3 — Hent nyheder fra APITube
APITube's GET /v1/news/everything-endepunkt tager en søgetitel, sprog, kategori og datointerval og returnerer artikler beriget med sentiment-scores og navngivne entiteter. Auth placeres i X-API-Key-headeren.
Hurtig fornuftighedstjek fra shell'en:
curl "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2" \
-H "X-API-Key: $APITUBE_KEY"
Svar — ét fuldt artikelobjekt, alle felter APITube returnerer pr. resultat:
{
"status": "ok",
"limit": 2,
"path": "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2&page=1",
"page": 1,
"has_next_pages": true,
"next_page": "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2&page=2",
"has_previous_page": false,
"previous_page": "",
"export": {
"json": "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2&page=1&export=json",
"xlsx": "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2&page=1&export=xlsx",
"csv": "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2&page=1&export=csv",
"tsv": "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2&page=1&export=tsv",
"xml": "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2&page=1&export=xml",
"rss": "https://api.apitube.io/v1/news/everything?title=Tesla&language.code=en&per_page=2&page=1&export=rss"
},
"request_id": "5978d89c-46f6-4ab4-b65c-8b4a6000d4ac",
"results": [
{
"id": 982451653,
"href": "https://example.com/tesla-recall",
"published_at": "2026-05-02T08:14:00Z",
"title": "Tesla recalls 1.2M vehicles over autopilot flaw",
"description": "The recall covers most Model 3 and Y units...",
"body": "Tesla on Friday recalled roughly 1.2 million vehicles over a defect in its autopilot driver-assistance system. The recall covers most Model 3 and Model Y units built since 2023.",
"body_html": "<p>Tesla on Friday recalled roughly 1.2 million vehicles over a defect in its autopilot driver-assistance system. The recall covers most Model 3 and Model Y units built since 2023.</p>",
"language": "en",
"author": {
"id": 4521,
"name": "Jane Doe"
},
"image": "https://example.com/images/tesla-recall.jpg",
"categories": [
{
"id": 211,
"name": "transport",
"score": 0.91,
"taxonomy": "iptc_mediatopics",
"links": {
"self": "https://api.apitube.io/v1/news/category/iptc_mediatopics/medtop:20000337"
}
}
],
"topics": [
{
"id": "industry.automotive_news",
"name": "Automotive Industry News",
"score": 0.34,
"links": {
"self": "https://api.apitube.io/v1/news/topic/industry.automotive_news"
}
}
],
"industries": [
{
"id": 88,
"name": "Automotive",
"links": {
"self": "https://api.apitube.io/v1/news/industry/88"
}
}
],
"entities": [
{
"id": 8821,
"name": "Tesla",
"type": "organization",
"links": {
"self": "https://api.apitube.io/v1/news/entity/8821",
"wikipedia": "https://en.wikipedia.org/wiki/Tesla,_Inc.",
"wikidata": "https://www.wikidata.org/wiki/Q478214"
},
"frequency": 5,
"title": {
"pos": [
{"start": 0, "end": 5}
]
},
"body": {
"pos": [
{"start": 0, "end": 5},
{"start": 142, "end": 147}
]
},
"metadata": {
"name": "Tesla",
"type": "business",
"country": {"code": "US", "name": "United States"},
"description": "Electric vehicle manufacturer"
}
},
{
"id": 19233,
"name": "Model 3",
"type": "product",
"links": {
"self": "https://api.apitube.io/v1/news/entity/19233"
},
"frequency": 2,
"title": {
"pos": []
},
"body": {
"pos": [
{"start": 58, "end": 65}
]
},
"metadata": {}
}
],
"source": {
"id": 311,
"domain": "reuters.com",
"home_page_url": "https://www.reuters.com",
"type": "news",
"bias": "center",
"rankings": {
"opr": 9
},
"location": {
"country_name": "United States",
"country_code": "us"
},
"favicon": "https://www.reuters.com/favicon.ico"
},
"sentiment": {
"overall": {"score": -0.62, "polarity": "negative"},
"title": {"score": -0.55, "polarity": "negative"},
"body": {"score": -0.64, "polarity": "negative"}
},
"summary": [
{
"sentence": "Tesla recalled roughly 1.2 million vehicles over an autopilot defect.",
"sentiment": {"score": -0.6, "polarity": "negative"}
}
],
"keywords": ["Tesla", "recall", "autopilot", "Model 3"],
"links": [
{"url": "https://example.com/tesla-recall-followup", "type": "link"}
],
"media": [
{"url": "https://example.com/images/tesla-recall.jpg", "type": "image"}
],
"story": {
"id": 3019419820,
"uri": "https://api.apitube.io/v1/news/story/3019419820"
},
"shares": {
"total": 412,
"facebook": 220,
"twitter": 150,
"reddit": 42
},
"is_duplicate": false,
"is_free": true,
"is_breaking": true,
"read_time": 2,
"sentences_count": 8,
"paragraphs_count": 4,
"words_count": 320,
"characters_count": 1820
}
]
}
Botten rører kun ved en håndfuld af disse — title, description, href, source.domain, sentiment.overall.polarity og entities[].name — men alt andet (emner, brancher, kildebias, opsummering pr. sætning, delingsantal) er der, hvis du ønsker rigere opslag senere.
I Python, async httpx:
import httpx, os
API_URL = "https://api.apitube.io/v1/news/everything"
async def fetch_news(query: str) -> list[dict]:
params = {
"title": query,
"language.code": "en",
"per_page": 20,
"sort.by": "published_at",
"sort.order": "desc",
}
headers = {"X-API-Key": os.environ["APITUBE_KEY"]}
async with httpx.AsyncClient(timeout=15) as client:
r = await client.get(API_URL, params=params, headers=headers)
r.raise_for_status()
return r.json().get("results", [])
Trin 4 — Filtrer efter sentiment og entiteter
Dette trin forvandler en vandslange til en kurateret kanal. Fjern artikler, der ikke matcher den konfigurerede polaritet, og behold kun dem, hvis entiteter inkluderer mindst ét overvåget navn.
WATCHED_ENTITIES = {"Tesla", "SpaceX", "OpenAI"}
WANTED_POLARITY = {"negative"} # use {"positive", "negative", "neutral"} to allow all
def passes_filter(article: dict) -> bool:
polarity = article.get("sentiment", {}).get("overall", {}).get("polarity")
if polarity not in WANTED_POLARITY:
return False
names = {e.get("name") for e in article.get("entities", [])}
return bool(names & WATCHED_ENTITIES)
Et 30-sekunders filter, men det er forskellen mellem en bot, der poster 200 artikler om dagen, og en, der poster 6 nyttige artikler.
Trin 5 — Planlæg kanal-indlæg med APScheduler
AsyncIOScheduler kører inden for den samme event loop, som aiogram bruger, så hele botten er én proces. Dedup postede ID'er i en in-memory set; for persistens på tværs af genstarter, byt til Redis eller SQLite.
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from aiogram.exceptions import TelegramRetryAfter
import asyncio, html
posted_ids: set[int] = set()
async def post_news(bot, channel_id: int):
articles = await fetch_news("Tesla OR SpaceX OR OpenAI")
for art in articles:
if art["id"] in posted_ids or not passes_filter(art):
continue
text = (
f"<b>{html.escape(art['title'])}</b>\n"
f"{html.escape(art['description'])}\n\n"
f"{art['source']['domain']} · "
f"sentiment: {art['sentiment']['overall']['polarity']}\n"
f"<a href=\"{art['href']}\">Read more</a>"
)
try:
await bot.send_message(channel_id, text, disable_web_page_preview=False)
posted_ids.add(art["id"])
except TelegramRetryAfter as e:
await asyncio.sleep(e.retry_after)
TelegramRetryAfter-fangsten er det, der holder botten i live, når Telegram begrænser dig for at poste for hurtigt — den sover det præcist anmodede interval, hvorefter den næste scheduler tick forsøger igen.
Trin 6 — Kommandoer og hovedløkke
Tre små kommandoer, så abonnenter kan interagere direkte med botten:
/start— velkomstbesked/subscribe— forklar, at botten automatisk poster til kanalen/filter— vis nuværende filterkonfiguration
Dispatcher'en forbinder dem, og start_polling kører sammen med planlæggeren.
Fuld kode (bot.py)
import asyncio, html, logging, os
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
from aiogram.exceptions import TelegramRetryAfter
from aiogram.filters import Command, CommandStart
from aiogram.types import Message
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.interval import IntervalTrigger
from dotenv import load_dotenv
import httpx
load_dotenv()
TOKEN = os.environ["BOT_TOKEN"]
CHANNEL_ID = int(os.environ["CHANNEL_ID"])
APITUBE_KEY = os.environ["APITUBE_KEY"]
API_URL = "https://api.apitube.io/v1/news/everything"
WATCHED_ENTITIES = {"Tesla", "SpaceX", "OpenAI"}
WANTED_POLARITY = {"negative"}
posted_ids: set[int] = set()
dp = Dispatcher()
async def fetch_news(query: str) -> list[dict]:
params = {
"title": query,
"language.code": "en",
"per_page": 20,
"sort.by": "published_at",
"sort.order": "desc",
}
headers = {"X-API-Key": APITUBE_KEY}
async with httpx.AsyncClient(timeout=15) as client:
r = await client.get(API_URL, params=params, headers=headers)
r.raise_for_status()
return r.json().get("results", [])
def passes_filter(article: dict) -> bool:
polarity = article.get("sentiment", {}).get("overall", {}).get("polarity")
if polarity not in WANTED_POLARITY:
return False
names = {e.get("name") for e in article.get("entities", [])}
return bool(names & WATCHED_ENTITIES)
async def post_news(bot: Bot):
articles = await fetch_news("Tesla OR SpaceX OR OpenAI")
for art in articles:
if art["id"] in posted_ids or not passes_filter(art):
continue
text = (
f"<b>{html.escape(art['title'])}</b>\n"
f"{html.escape(art.get('description') or '')}\n\n"
f"{art['source']['domain']} · "
f"sentiment: {art['sentiment']['overall']['polarity']}\n"
f"<a href=\"{art['href']}\">Read more</a>"
)
try:
await bot.send_message(CHANNEL_ID, text)
posted_ids.add(art["id"])
await asyncio.sleep(1)
except TelegramRetryAfter as e:
await asyncio.sleep(e.retry_after)
except Exception:
logging.exception("send_message failed for %s", art["id"])
@dp.message(CommandStart())
async def cmd_start(m: Message):
await m.answer("News bot online. Curated negative-sentiment news on Tesla, SpaceX, OpenAI.")
@dp.message(Command("subscribe"))
async def cmd_subscribe(m: Message):
await m.answer("Posts go to the channel automatically every 15 minutes.")
@dp.message(Command("filter"))
async def cmd_filter(m: Message):
await m.answer(
f"Polarity: {sorted(WANTED_POLARITY)}\nEntities: {sorted(WATCHED_ENTITIES)}"
)
async def main():
logging.basicConfig(level=logging.INFO)
bot = Bot(TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
scheduler = AsyncIOScheduler()
scheduler.add_job(post_news, IntervalTrigger(minutes=15), args=[bot])
scheduler.start()
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Kør det:
python bot.py
Den første planlægger-tick udløses 15 minutter efter start. For at udløse øjeblikkeligt, tilføj next_run_time=datetime.utcnow() til add_job.
Implementer med Docker
Et multi-stage Python 3.12 slim image holder containeren lille og bygget deterministisk.
Dockerfile:
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY bot.py .
CMD ["python", "-u", "bot.py"]
requirements.txt:
aiogram==3.27.0
apscheduler==3.10.4
httpx==0.27.0
python-dotenv==1.0.1
docker-compose.yml:
services:
bot:
build: .
env_file: .env
restart: unless-stopped
For gratis eller næsten gratis hosting i 2026: Fly.io's hobby-tier håndterer en enkelt bot-container fint; Railway giver dig logs og et-klik genudrulninger; eller tag en $5/måned VPS (Hetzner, Vultr, DigitalOcean) og kør docker compose up -d. Uanset hvad du vælger, sikrer restart: unless-stopped, at et nedbrud ikke tager botten offline natten over.
Ofte stillede spørgsmål
Hvordan laver jeg en Telegram nyhedsbots i Python?
Registrer en bot med @BotFather, installer aiogram 3.27 og en nyhedsklient som httpx, hent artikler fra en nyheds-API som APITube, filtrer svaret, og post hvert element til en Telegram-chat eller kanal ved hjælp af bot.send_message. Pak hent-og-post-trinnet ind i APScheduler for at få det til at køre med et interval.
Hvilket bibliotek er bedre, aiogram eller python-telegram-bot?
aiogram er kun async siden v3, hvilket betyder, at den parrer sig rent med AsyncIOScheduler til kanal-autoposting i en enkelt event loop. For nyheds-/kanalbots over ~50 samtidige brugere, vælg aiogram 3.27. For kommandodrevne personlige bots, sync-codebase teams eller gradvise migrationsveje, forbliver python-telegram-bot 21.x et solidt valg. Begge vedligeholdes aktivt i 2026.
Kan en Telegram-bot poste til en kanal automatisk?
Ja. Tilføj botten til kanalen som administrator med tilladelsen "Send beskeder", hent kanalens numeriske chat-ID, og kald bot.send_message(chat_id, text) fra en planlagt opgave. APScheduler's AsyncIOScheduler kører inde i bottens event loop, så én proces håndterer både polling og planlagt posting.
Er python-telegram-bot stadig vedligeholdt i 2026?
Ja — python-telegram-bot vedligeholdes stadig aktivt. Version 21.x leverer en async API og løbende rettelser; projektet ser regelmæssige udgivelser på GitHub. Den synkroniserede API v13 er forældet, men v20 og frem er async og aktuel.
Prøv det
Klon snippets ovenfor, placer dem i en mappe, udfyld .env, docker compose up -d. APITube's gratis niveau returnerer sentiment og entiteter på hver artikel — få en nøgle på apitube.io og erstat pladsholderen i trin 3. Hvis du vil filtrere på kategori i stedet for entiteter, læs article["categories"] og match på hver kategoris name (e.g. behold kun artikler, hvis kategorinavn indeholder "technology").


