diff --git a/api/trakt/models/rating.py b/api/trakt/models/rating.py index bedd95a..c50115b 100644 --- a/api/trakt/models/rating.py +++ b/api/trakt/models/rating.py @@ -1,20 +1,30 @@ from dateutil.parser import parse from dateutil.tz import tzlocal +from src.tmdb.client import TMDb + class Rating: def __init__(self, data): self.type = data['type'] self.rated = data['rating'] self.rated_at = parse(data['rated_at']).astimezone(tzlocal()) # Convert to local timezone + self.date = self.rated_at.strftime("%d/%m/%Y %H:%M:%S") # Convert to human readable format self.show_title = None self.season_id = None self.episode_id = None + self.trakt_id = None + self.imdb_id = None + self.tmdb_id = None + self.tvdb_id = None + self.poster = None + self.slug = None media = create_media(data) self.set_media_attributes(media) def set_media_attributes(self, media): self.title = media.title self.year = media.year + self.poster = media.poster if isinstance(media, (Episode, Season)): self.show_title = media.show_title @@ -36,6 +46,11 @@ class Movie: def __init__(self, data): self.title = data['movie']['title'] self.year = data['movie']['year'] + self.slug = data['movie']['ids']['slug'] + self.trakt_id = data['movie']['ids']['trakt'] + self.imdb_id = data['movie']['ids']['imdb'] + self.tmdb_id = data['movie']['ids']['tmdb'] + self.poster = TMDb.movie_poster_path(self.tmdb_id) class Episode: def __init__(self, data): @@ -44,6 +59,12 @@ def __init__(self, data): self.season_id = "{:02}".format(data['episode']['season']) # Format as two-digit number self.episode_id = "{:02}".format(data['episode']['number']) # Format as two-digit number self.year = data['show']['year'] + self.slug = data['show']['ids']['slug'] + self.trakt_id = data['show']['ids']['trakt'] + self.imdb_id = data['show']['ids']['imdb'] + self.tmdb_id = data['show']['ids']['tmdb'] + self.tvdb_id = data['show']['ids']['tvdb'] + self.poster = TMDb.show_poster_path(self.tvdb_id) class Season: def __init__(self, data): @@ -51,8 +72,20 @@ def __init__(self, data): self.show_title = data['show']['title'] self.season_id = "{:02}".format(data['season']['number']) # Format as two-digit number self.year = data['show']['year'] + self.slug = data['show']['ids']['slug'] + self.trakt_id = data['show']['ids']['trakt'] + self.imdb_id = data['show']['ids']['imdb'] + self.tmdb_id = data['show']['ids']['tmdb'] + self.tvdb_id = data['show']['ids']['tvdb'] + self.poster = TMDb.show_poster_path(self.tvdb_id) class Show: def __init__(self, data): self.title = data['show']['title'] - self.year = data['show']['year'] \ No newline at end of file + self.year = data['show']['year'] + self.slug = data['show']['ids']['slug'] + self.trakt_id = data['show']['ids']['trakt'] + self.imdb_id = data['show']['ids']['imdb'] + self.tmdb_id = data['show']['ids']['tmdb'] + self.tvdb_id = data['show']['ids']['tvdb'] + self.poster = TMDb.show_poster_path(self.tvdb_id) \ No newline at end of file diff --git a/src/discord/bot.py b/src/discord/bot.py index aa8d41d..42666d5 100644 --- a/src/discord/bot.py +++ b/src/discord/bot.py @@ -1,7 +1,5 @@ import discord -from discord.ext import commands, tasks - -from config.config import DELAY_START +from discord.ext import commands from utils.custom_logger import logger from utils.datetime import get_times @@ -38,7 +36,12 @@ async def on_ready(self): logger.info( f'Logged in as {self.bot.user.name} ({self.bot.user.id}) and is ready!' ) - + + # Load the tasks cog + logger.info('Loading tasks cog') + await self.bot.load_extension('src.discord.cogs.tasks') + + # Load commands @self.bot.command() async def favorite(ctx, username: str = None): if username is None: diff --git a/src/discord/cogs/__init__.py b/src/discord/cogs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/discord/cogs/tasks.py b/src/discord/cogs/tasks.py new file mode 100644 index 0000000..1887c42 --- /dev/null +++ b/src/discord/cogs/tasks.py @@ -0,0 +1,21 @@ +from discord.ext import tasks, commands + +from utils.custom_logger import logger +from src.trakt.functions import process_ratings + +class TasksCog(commands.Cog): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + self.trakt_ratings.start() + + @tasks.loop(minutes=60) + async def trakt_ratings(self): + ratings_channel = self.bot.get_channel(1052967176828616724) + try: + await process_ratings(ratings_channel, 'desiler') + except Exception as e: + logger.error(f'Error processing recent Trakt ratings: {e}') + +async def setup(bot): + logger.info('Cogs have been loaded') + await bot.add_cog(TasksCog(bot)) \ No newline at end of file diff --git a/src/discord/embed.py b/src/discord/embed.py index 0aab7f9..0eb0a2d 100644 --- a/src/discord/embed.py +++ b/src/discord/embed.py @@ -15,6 +15,10 @@ def add_field(self, name, value, inline=True): def set_footer(self, text, icon_url=None): self.embed.set_footer(text=text, icon_url=icon_url) return self + + def set_thumbnail(self, url): + self.embed.set_thumbnail(url=url) + return self def build(self): return self.embed diff --git a/src/tmdb/client.py b/src/tmdb/client.py new file mode 100644 index 0000000..59852ee --- /dev/null +++ b/src/tmdb/client.py @@ -0,0 +1,42 @@ +import requests + +from config.globals import TMDB_API_KEY +from utils.custom_logger import logger + +class TMDb: + BASE_URL = "https://api.themoviedb.org/3" + API_KEY = TMDB_API_KEY + + # Fetches the show poster path from TMDb + @classmethod + def show_poster_path(cls, tvdb_id): + try: + response = requests.get( + f"{cls.BASE_URL}/find/{tvdb_id}", + params={ + "api_key": cls.API_KEY, + "external_source": "tvdb_id" + } + ) + response.raise_for_status() + data = response.json() + if (tv_results := data.get('tv_results', [])): + return f"https://image.tmdb.org/t/p/original{tv_results[0].get('poster_path')}" + except Exception as e: + logger.error(f"Failed to fetch show poster path from TMDb: {e}") + return None + + # Fetches the movie poster path from TMDb + @classmethod + def movie_poster_path(cls, tmdb_id): + try: + response = requests.get( + f"{cls.BASE_URL}/movie/{tmdb_id}", + params={"api_key": cls.API_KEY} + ) + response.raise_for_status() + data = response.json() + return f"https://image.tmdb.org/t/p/original{data.get('poster_path')}" + except Exception as e: + logger.error(f"Failed to fetch movie poster path from TMDb: {e}") + return None \ No newline at end of file diff --git a/src/trakt/functions.py b/src/trakt/functions.py new file mode 100644 index 0000000..1560826 --- /dev/null +++ b/src/trakt/functions.py @@ -0,0 +1,18 @@ +from datetime import datetime, timedelta + +from src.discord.embed import EmbedBuilder +from api.trakt.client import TraktClient + +async def process_ratings(ratings_channel, username): + client = TraktClient() + user = client.user(username) + now = datetime.now() + one_hour_ago = now - timedelta(hours=30) + ratings = user.get_ratings(start_time=one_hour_ago, end_time=now) + + for rating in ratings: + description = f"**{rating.show_title} (S{rating.season_id}E{rating.episode_id})**\n{rating.title}\n\n {username} rated {rating.rated} :star:" + embed_builder = EmbedBuilder(description=description, color=0xFF0000) + embed_builder.set_footer(text=rating.date) + embed_builder.set_thumbnail(url=rating.poster) + await embed_builder.send_embed(ratings_channel) \ No newline at end of file