From 2fc26a3e6d0611092a99af1bddb731f1d107a0ff Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 26 Jun 2024 16:00:43 -0400 Subject: [PATCH] FEAT: Added a youtube module that tracks live and/or video uploads REF: Changed code needed for Youtube --- config.py | 4 ++ main.py | 5 +++ modules/social/youtube.py | 90 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 modules/social/youtube.py diff --git a/config.py b/config.py index 82087a7..73d0919 100644 --- a/config.py +++ b/config.py @@ -7,6 +7,7 @@ config = { 'DISCORD_TOKEN': os.getenv('DISCORD_TOKEN'), 'GUILD_ID': int(os.getenv('DISCORD_GUILD_ID')), 'DISCORD_CHANNEL_ID': int(os.getenv('DISCORD_CHANNEL_ID')), + 'YOUTUBE_API_KEY': os.getenv('YOUTUBE_API_KEY'), 'BUNGIE_API_KEY': os.getenv('BUNGIE_API_KEY'), 'OAUTH_URL': os.getenv('OAUTH_URL'), 'OAUTH_CLIENT_ID': os.getenv('OAUTH_CLIENT_ID'), @@ -25,6 +26,9 @@ config = { }, 'music': { 'enabled': True + }, + 'youtube': { + 'enabled': True } } } diff --git a/main.py b/main.py index 4516401..83b21b5 100644 --- a/main.py +++ b/main.py @@ -49,6 +49,11 @@ class Selena(discord.Client): music = Music(self) music.setup(self.tree) + if config['modules']['youtube']['enabled']: + from modules.social.youtube import YouTube + youtube = YouTube(self) + youtube.setup(self.tree) + bot = Selena() diff --git a/modules/social/youtube.py b/modules/social/youtube.py new file mode 100644 index 0000000..dea1d13 --- /dev/null +++ b/modules/social/youtube.py @@ -0,0 +1,90 @@ +import discord +from discord import app_commands +import requests +import logging +import asyncio +import sqlite3 +from datetime import datetime +from config import config + + +class YouTube: + def __init__(self, bot): + self.bot = bot + self.api_key = config['YOUTUBE_API_KEY'] + self.logger = logging.getLogger('YouTube') + self.logger.setLevel(logging.DEBUG) + handler = logging.FileHandler(filename='log/selena.log', encoding='utf-8', mode='w') + handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s:%(message)s')) + self.logger.addHandler(handler) + self.db_path = 'data/selena.db' + + async def fetch_latest_video(self, channel_id): + url = f'https://www.googleapis.com/youtube/v3/search?key={self.api_key}&channelId={channel_id}&part=snippet,id&order=date&maxResults=1' + response = requests.get(url) + if response.status_code == 200: + data = response.json() + if data['items']: + return data['items'][0] + return None + + async def check_channels(self): + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("SELECT channel_id, last_video_id, alert_channel_id FROM youtube_channels") + channels = cursor.fetchall() + for channel_id, last_video_id, alert_channel_id in channels: + latest_video = await self.fetch_latest_video(channel_id) + if latest_video and latest_video['id']['videoId'] != last_video_id: + await self.send_alert(alert_channel_id, latest_video) + cursor.execute("UPDATE youtube_channels SET last_video_id = ? WHERE channel_id = ?", (latest_video['id']['videoId'], channel_id)) + conn.commit() + conn.close() + + async def send_alert(self, alert_channel_id, video): + channel = self.bot.get_channel(alert_channel_id) + if channel: + title = video['snippet']['title'] + description = video['snippet']['description'] + url = f"https://www.youtube.com/watch?v={video['id']['videoId']}" + thumbnail = video['snippet']['thumbnails']['high']['url'] + embed = discord.Embed(title=title, description=description, url=url, color=discord.Color.red()) + embed.set_thumbnail(url=thumbnail) + await channel.send(embed=embed) + + def setup(self, tree: app_commands.CommandTree): + @tree.command(name="add_youtube_channel", description="Add a YouTube channel to monitor") + async def add_youtube_channel_command(interaction: discord.Interaction, channel_id: str, alert_channel: discord.TextChannel): + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("INSERT INTO youtube_channels (channel_id, last_video_id, alert_channel_id) VALUES (?, ?, ?)", (channel_id, '', alert_channel.id)) + conn.commit() + conn.close() + await interaction.response.send_message(embed=discord.Embed(description=f"Added YouTube channel {channel_id} to monitor.", color=discord.Color.green())) + + @tree.command(name="remove_youtube_channel", description="Remove a YouTube channel from monitoring") + async def remove_youtube_channel_command(interaction: discord.Interaction, channel_id: str): + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("DELETE FROM youtube_channels WHERE channel_id = ?", (channel_id,)) + conn.commit() + conn.close() + await interaction.response.send_message(embed=discord.Embed(description=f"Removed YouTube channel {channel_id} from monitoring.", color=discord.Color.green())) + + if not tree.get_command("add_youtube_channel"): + tree.add_command(add_youtube_channel_command) + + if not tree.get_command("remove_youtube_channel"): + tree.add_command(remove_youtube_channel_command) + + async def setup_hook(self): + await self.bot.wait_until_ready() + while not self.bot.is_closed(): + await self.check_channels() + await asyncio.sleep(3600) # Check every hour + + +def setup(bot): + youtube = YouTube(bot) + youtube.setup(bot.tree) + bot.loop.create_task(youtube.setup_hook())