diff --git a/api_server.py b/api_server.py index ec6a089..a7c4aa2 100644 --- a/api_server.py +++ b/api_server.py @@ -4,12 +4,13 @@ import re from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException, Response, BackgroundTasks -from fastapi.responses import HTMLResponse, FileResponse +from fastapi.responses import HTMLResponse, FileResponse, PlainTextResponse from telegram_client import TelegramClient from config import get_settings import mimetypes import os from pyrogram import errors +from feedgen.feed import FeedGenerator logger = logging.getLogger(__name__) client = TelegramClient() @@ -30,12 +31,7 @@ app = FastAPI( if __name__ == "__main__": import uvicorn - uvicorn.run( - "api_server:app", - host=settings["api_host"], - port=settings["api_port"], - reload=False - ) + uvicorn.run( "api_server:app", host=settings["api_host"], port=settings["api_port"], reload=False) @app.get("/html/{channel}/{post_id}", response_class=HTMLResponse) async def get_post_html(channel: str, post_id: int): @@ -123,4 +119,48 @@ async def get_media( raise HTTPException( status_code=500, detail=f"Internal server error: {str(e)}" - ) \ No newline at end of file + ) + +@app.get("/rss/{channel}", response_class=PlainTextResponse) +async def get_rss_feed(channel: str): + try: + # Get last 20 posts + posts = await client.get_channel_posts(channel, limit=20) + + # Create FeedGenerator + fg = FeedGenerator() + fg.title(channel) + fg.link(href=f"{settings['pyrogram_bridge_url']}/html/{channel}", rel='alternate') + fg.description(f'Telegram channel {channel} RSS feed') + fg.language('ru-ru') + + # Add channel metadata + fg.id(f"{settings['pyrogram_bridge_url']}/rss/{channel}") + fg.link(href=f"{settings['pyrogram_bridge_url']}/rss/{channel}", rel='self', type='application/rss+xml') + + # Add posts + for post in posts: + fe = fg.add_entry() + fe.id(f"{settings['pyrogram_bridge_url']}/html/{channel}/{post['id']}") + fe.title(post['title']) + fe.link(href=f"{settings['pyrogram_bridge_url']}/html/{channel}/{post['id']}") + fe.pubDate(post['date'].astimezone(tz=None)) + fe.description(post['html']) + fe.content(content=post['html'], type='CDATA') + + # Add media enclosures + for media in post.get('media', []): + if media.get('url'): + fe.enclosure( + url=f"{settings['pyrogram_bridge_url']}/media/{media['url']}", + type=media.get('type', 'image/jpeg'), + length=str(media.get('size', 0)) + ) + + # Generate RSS + rss = fg.rss_str(pretty=True) + return Response(content=rss, media_type="application/xml") + + except Exception as e: + logger.error(f"RSS generation error: {str(e)}") + raise HTTPException(status_code=500, detail="RSS feed generation failed") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 9bb8c01..49a5239 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,8 @@ fastapi uvicorn +python-multipart kurigram tgcrypto -pillow \ No newline at end of file +pillow +feedgen +python-dateutil \ No newline at end of file diff --git a/telegram_client.py b/telegram_client.py index ccf4cad..1349058 100644 --- a/telegram_client.py +++ b/telegram_client.py @@ -181,12 +181,11 @@ class TelegramClient: return { "id": message.id, - "date": message.date.isoformat(), + "date": message.date, "html": processed_text, - "text": re.sub('<[^<]+?>', '', raw_text).strip(), - "views": message.views or 0, - "media_group_id": message.media_group_id, - "author": author_str + "raw_text": raw_text, + "media": media, + "author": self._get_author_info(message) } async def _parse_media(self, media_obj) -> dict: @@ -496,4 +495,29 @@ class TelegramClient: raise except Exception as e: logger.error(f"Media download failed {file_id}: {type(e).__name__} - {str(e)}") - raise \ No newline at end of file + raise + + async def get_channel_posts(self, channel: str, limit: int = 20) -> list: + try: + if not self.client.is_connected: + await self.start() + + posts = [] + async for message in self.client.get_chat_history( + chat_id=channel, + limit=limit + ): + parsed = await self._parse_message(message) + if parsed: + posts.append({ + "id": message.id, + "date": message.date, + "title": self._generate_title(parsed.get("raw_text", "")), + "html": parsed["html"], + "media": parsed.get("media", []) + }) + return posts + + except Exception as e: + logger.error(f"Channel posts error {channel}: {type(e).__name__} - {str(e)}") + return [] \ No newline at end of file