Implement phase 3 and 4: Telegram and Twitter integration

This commit is contained in:
2026-01-18 22:42:48 +03:00
parent 836e8df344
commit f4725a5506
11 changed files with 726 additions and 7 deletions
+4
View File
@@ -0,0 +1,4 @@
from .telegram_bot import TelegramBot
from .twitter_api import TwitterAPI
__all__ = ["TelegramBot", "TwitterAPI"]
+127
View File
@@ -0,0 +1,127 @@
import requests
from typing import Dict, Optional
from fastapi import HTTPException
import logging
logger = logging.getLogger(__name__)
class TelegramBot:
def __init__(self, bot_token: str):
self.bot_token = bot_token
self.base_url = f"https://api.telegram.org/bot{bot_token}"
def send_message(self, chat_id: str, text: str, parse_mode: str = "HTML") -> Dict:
"""
Отправить текстовое сообщение в Telegram
Args:
chat_id: ID чата или канала
text: Текст сообщения
parse_mode: Режим парсинга ('HTML' или 'MarkdownV2')
Returns:
Ответ от Telegram API
"""
url = f"{self.base_url}/sendMessage"
payload = {
"chat_id": chat_id,
"text": text,
"parse_mode": parse_mode,
"disable_web_page_preview": True
}
try:
response = requests.post(url, json=payload)
response.raise_for_status()
result = response.json()
if not result.get("ok"):
raise HTTPException(status_code=400, detail=f"Telegram API error: {result.get('description')}")
return result
except requests.exceptions.RequestException as e:
logger.error(f"Error sending message to Telegram: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to send message to Telegram: {str(e)}")
def send_photo(self, chat_id: str, photo_url: str, caption: str = "", parse_mode: str = "HTML") -> Dict:
"""
Отправить фото в Telegram
Args:
chat_id: ID чата или канала
photo_url: URL фото
caption: Подпись к фото
parse_mode: Режим парсинга ('HTML' или 'MarkdownV2')
Returns:
Ответ от Telegram API
"""
url = f"{self.base_url}/sendPhoto"
payload = {
"chat_id": chat_id,
"photo": photo_url,
"caption": caption,
"parse_mode": parse_mode
}
try:
response = requests.post(url, json=payload)
response.raise_for_status()
result = response.json()
if not result.get("ok"):
raise HTTPException(status_code=400, detail=f"Telegram API error: {result.get('description')}")
return result
except requests.exceptions.RequestException as e:
logger.error(f"Error sending photo to Telegram: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to send photo to Telegram: {str(e)}")
def get_me(self) -> Dict:
"""
Получить информацию о боте
Returns:
Информация о боте от Telegram API
"""
url = f"{self.base_url}/getMe"
try:
response = requests.get(url)
response.raise_for_status()
result = response.json()
if not result.get("ok"):
raise HTTPException(status_code=400, detail=f"Telegram API error: {result.get('description')}")
return result
except requests.exceptions.RequestException as e:
logger.error(f"Error getting bot info from Telegram: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to get bot info from Telegram: {str(e)}")
def get_chat(self, chat_id: str) -> Dict:
"""
Получить информацию о чате
Args:
chat_id: ID чата или канала
Returns:
Информация о чате от Telegram API
"""
url = f"{self.base_url}/getChat"
payload = {"chat_id": chat_id}
try:
response = requests.post(url, json=payload)
response.raise_for_status()
result = response.json()
if not result.get("ok"):
raise HTTPException(status_code=400, detail=f"Telegram API error: {result.get('description')}")
return result
except requests.exceptions.RequestException as e:
logger.error(f"Error getting chat info from Telegram: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to get chat info from Telegram: {str(e)}")
+126
View File
@@ -0,0 +1,126 @@
import tweepy
from typing import Dict, Optional
from fastapi import HTTPException
import logging
logger = logging.getLogger(__name__)
class TwitterAPI:
def __init__(self, consumer_key: str, consumer_secret: str, access_token: str, access_token_secret: str):
"""
Инициализация Twitter API клиента
Args:
consumer_key: Ключ приложения Twitter
consumer_secret: Секрет приложения Twitter
access_token: Токен доступа
access_token_secret: Секрет токена доступа
"""
try:
# Создаем объект аутентификации
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
# Создаем API клиент
self.api = tweepy.API(auth, wait_on_rate_limit=True)
# Проверяем валидность аутентификации
self.api.verify_credentials()
except Exception as e:
logger.error(f"Error initializing Twitter API: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to initialize Twitter API: {str(e)}")
def post_tweet(self, text: str) -> Dict:
"""
Опубликовать твит
Args:
text: Текст твита (до 280 символов)
Returns:
Информация о созданном твите
"""
try:
# Публикуем твит
tweet = self.api.update_status(text)
return {
"success": True,
"tweet_id": tweet.id,
"tweet_text": tweet.text,
"created_at": tweet.created_at.isoformat() if tweet.created_at else None
}
except tweepy.TweepyException as e:
logger.error(f"Error posting tweet: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to post tweet: {str(e)}")
except Exception as e:
logger.error(f"Unexpected error posting tweet: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to post tweet: {str(e)}")
def post_thread(self, tweets: list) -> Dict:
"""
Опубликовать тред (цепочку твитов)
Args:
tweets: Список текстов твитов в порядке публикации
Returns:
Информация о созданном треде
"""
try:
previous_tweet_id = None
tweet_ids = []
for i, text in enumerate(tweets):
if previous_tweet_id:
# Отвечаем на предыдущий твит, чтобы создать тред
tweet = self.api.update_status(
status=text,
in_reply_to_status_id=previous_tweet_id,
auto_populate_reply_metadata=True
)
else:
# Первый твит в треде
tweet = self.api.update_status(text)
tweet_ids.append(tweet.id)
previous_tweet_id = tweet.id
return {
"success": True,
"tweet_ids": tweet_ids,
"thread_length": len(tweets)
}
except tweepy.TweepyException as e:
logger.error(f"Error posting thread: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to post thread: {str(e)}")
except Exception as e:
logger.error(f"Unexpected error posting thread: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to post thread: {str(e)}")
def get_user_info(self) -> Dict:
"""
Получить информацию о текущем пользователе
Returns:
Информация о пользователе
"""
try:
user = self.api.verify_credentials()
return {
"id": user.id,
"screen_name": user.screen_name,
"name": user.name,
"followers_count": user.followers_count,
"friends_count": user.friends_count,
"statuses_count": user.statuses_count,
"description": user.description
}
except tweepy.TweepyException as e:
logger.error(f"Error getting user info: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to get user info: {str(e)}")
except Exception as e:
logger.error(f"Unexpected error getting user info: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to get user info: {str(e)}")