mirror of
https://gitverse.ru/kpa39l/telepost.git
synced 2026-09-29 09:55:09 +00:00
233 lines
9.6 KiB
Python
233 lines
9.6 KiB
Python
from typing import List, Optional
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import and_, or_
|
|
from ..models.post import Post
|
|
from ..schemas.post import PostCreate, PostUpdate
|
|
from datetime import datetime
|
|
from ..integrations.telegram_bot import TelegramBot
|
|
from ..integrations.twitter_api import TwitterAPI
|
|
import os
|
|
|
|
|
|
class PostService:
|
|
def __init__(self, db_session: Session):
|
|
self.db = db_session
|
|
|
|
def get_post(self, post_id: int, owner_id: int) -> Optional[Post]:
|
|
"""Получить пост по ID и владельцу"""
|
|
return self.db.query(Post).filter(
|
|
and_(Post.id == post_id, Post.owner_id == owner_id)
|
|
).first()
|
|
|
|
def get_posts(self, owner_id: int, skip: int = 0, limit: int = 100) -> List[Post]:
|
|
"""Получить список постов пользователя"""
|
|
return self.db.query(Post).filter(Post.owner_id == owner_id)\
|
|
.order_by(Post.created_at.desc())\
|
|
.offset(skip)\
|
|
.limit(limit)\
|
|
.all()
|
|
|
|
def create_post(self, post_data: PostCreate, owner_id: int) -> Post:
|
|
"""Создать новый пост"""
|
|
db_post = Post(
|
|
title=post_data.title,
|
|
content=post_data.content,
|
|
scheduled_time=post_data.scheduled_time,
|
|
status=post_data.status,
|
|
owner_id=owner_id
|
|
)
|
|
self.db.add(db_post)
|
|
self.db.commit()
|
|
self.db.refresh(db_post)
|
|
return db_post
|
|
|
|
def update_post(self, post_id: int, post_data: PostUpdate, owner_id: int) -> Optional[Post]:
|
|
"""Обновить пост"""
|
|
db_post = self.get_post(post_id, owner_id)
|
|
if not db_post:
|
|
return None
|
|
|
|
# Обновляем только те поля, которые предоставлены
|
|
update_data = post_data.dict(exclude_unset=True)
|
|
|
|
for field, value in update_data.items():
|
|
setattr(db_post, field, value)
|
|
|
|
# Обновляем время последнего изменения
|
|
db_post.updated_at = datetime.utcnow()
|
|
|
|
# Если статус меняется на published и published_at еще не установлен, устанавливаем его
|
|
if post_data.status == "published" and not db_post.published_at:
|
|
db_post.published_at = datetime.utcnow()
|
|
|
|
self.db.commit()
|
|
self.db.refresh(db_post)
|
|
return db_post
|
|
|
|
def delete_post(self, post_id: int, owner_id: int) -> bool:
|
|
"""Удалить пост"""
|
|
db_post = self.get_post(post_id, owner_id)
|
|
if not db_post:
|
|
return False
|
|
|
|
self.db.delete(db_post)
|
|
self.db.commit()
|
|
return True
|
|
|
|
def get_scheduled_posts(self) -> List[Post]:
|
|
"""Получить посты, готовые к публикации (статус scheduled и время наступило)"""
|
|
current_time = datetime.utcnow()
|
|
return self.db.query(Post).filter(
|
|
and_(
|
|
Post.status == "scheduled",
|
|
Post.scheduled_time <= current_time
|
|
)
|
|
).all()
|
|
|
|
def get_posts_by_status(self, owner_id: int, status: str) -> List[Post]:
|
|
"""Получить посты по статусу"""
|
|
return self.db.query(Post).filter(
|
|
and_(Post.owner_id == owner_id, Post.status == status)
|
|
).all()
|
|
|
|
def publish_post_to_telegram(self, post_id: int, chat_id: str) -> bool:
|
|
"""
|
|
Опубликовать пост в Telegram
|
|
|
|
Args:
|
|
post_id: ID поста в базе данных
|
|
chat_id: ID чата или канала в Telegram
|
|
|
|
Returns:
|
|
Успешность публикации
|
|
"""
|
|
# Получаем пост из базы данных
|
|
post = self.db.query(Post).filter(Post.id == post_id).first()
|
|
|
|
if not post:
|
|
return False
|
|
|
|
# Получаем токен бота из переменных окружения
|
|
bot_token = os.getenv("TELEGRAM_BOT_TOKEN")
|
|
if not bot_token:
|
|
raise ValueError("TELEGRAM_BOT_TOKEN environment variable is not set")
|
|
|
|
# Создаем экземпляр TelegramBot
|
|
bot = TelegramBot(bot_token)
|
|
|
|
try:
|
|
# Формируем сообщение из заголовка и содержимого поста
|
|
message = f"<b>{post.title}</b>\n\n{post.content}"
|
|
|
|
# Отправляем сообщение в Telegram
|
|
result = bot.send_message(chat_id=chat_id, text=message)
|
|
|
|
# Если публикация успешна, обновляем статус поста и время публикации
|
|
if result.get("ok"):
|
|
post.status = "published"
|
|
post.published_at = datetime.utcnow()
|
|
self.db.commit()
|
|
return True
|
|
else:
|
|
return False
|
|
except Exception as e:
|
|
print(f"Error publishing post to Telegram: {str(e)}")
|
|
return False
|
|
|
|
def publish_scheduled_posts(self, chat_id: str) -> int:
|
|
"""
|
|
Опубликовать все запланированные посты, время которых наступило
|
|
|
|
Args:
|
|
chat_id: ID чата или канала в Telegram
|
|
|
|
Returns:
|
|
Количество опубликованных постов
|
|
"""
|
|
scheduled_posts = self.get_scheduled_posts()
|
|
published_count = 0
|
|
|
|
for post in scheduled_posts:
|
|
try:
|
|
# Пытаемся опубликовать пост в Telegram
|
|
success = self.publish_post_to_telegram(post.id, chat_id)
|
|
|
|
if success:
|
|
published_count += 1
|
|
# Обновляем статус поста на published
|
|
post.status = "published"
|
|
post.published_at = datetime.utcnow()
|
|
self.db.commit()
|
|
else:
|
|
# Если публикация не удалась, помечаем пост как cancelled
|
|
post.status = "cancelled"
|
|
self.db.commit()
|
|
except Exception as e:
|
|
print(f"Error publishing scheduled post {post.id}: {str(e)}")
|
|
# Если возникла ошибка, помечаем пост как cancelled
|
|
post.status = "cancelled"
|
|
self.db.commit()
|
|
|
|
return published_count
|
|
|
|
def publish_post_to_twitter(self, post_id: int) -> bool:
|
|
"""
|
|
Опубликовать пост в Twitter
|
|
|
|
Args:
|
|
post_id: ID поста в базе данных
|
|
|
|
Returns:
|
|
Успешность публикации
|
|
"""
|
|
# Получаем пост из базы данных
|
|
post = self.db.query(Post).filter(Post.id == post_id).first()
|
|
|
|
if not post:
|
|
return False
|
|
|
|
# Получаем параметры для Twitter API из переменных окружения
|
|
twitter_consumer_key = os.getenv("TWITTER_CONSUMER_KEY")
|
|
twitter_consumer_secret = os.getenv("TWITTER_CONSUMER_SECRET")
|
|
twitter_access_token = os.getenv("TWITTER_ACCESS_TOKEN")
|
|
twitter_access_token_secret = os.getenv("TWITTER_ACCESS_TOKEN_SECRET")
|
|
|
|
if not all([twitter_consumer_key, twitter_consumer_secret, twitter_access_token, twitter_access_token_secret]):
|
|
raise ValueError("Twitter API credentials are not fully configured in environment variables")
|
|
|
|
# Создаем экземпляр TwitterAPI
|
|
twitter_api = TwitterAPI(
|
|
consumer_key=twitter_consumer_key,
|
|
consumer_secret=twitter_consumer_secret,
|
|
access_token=twitter_access_token,
|
|
access_token_secret=twitter_access_token_secret
|
|
)
|
|
|
|
try:
|
|
# Формируем текст твита из заголовка и содержимого поста
|
|
# Учитываем ограничение на 280 символов
|
|
combined_text = f"{post.title}\n\n{post.content}"
|
|
if len(combined_text) > 280:
|
|
# Обрезаем текст до 280 символов, возможно сокращая содержимое
|
|
truncated_title = post.title[:int(280*0.3)] # используем 30% на заголовок
|
|
remaining_chars = 280 - len(truncated_title) - 3 # -3 для "..."
|
|
|
|
if len(post.content) > remaining_chars:
|
|
combined_text = f"{truncated_title}...\n\n{post.content[:remaining_chars-4]}..."
|
|
else:
|
|
combined_text = f"{truncated_title}...\n\n{post.content}"
|
|
|
|
# Отправляем твит
|
|
result = twitter_api.post_tweet(combined_text)
|
|
|
|
# Если публикация успешна, обновляем статус поста и время публикации
|
|
if result.get("success"):
|
|
post.status = "published"
|
|
post.published_at = datetime.utcnow()
|
|
self.db.commit()
|
|
return True
|
|
else:
|
|
return False
|
|
except Exception as e:
|
|
print(f"Error publishing post to Twitter: {str(e)}")
|
|
return False |