From f6526e00e3720405cee91cda4b28b57dec28d964 Mon Sep 17 00:00:00 2001
From: Dan <daniel.sapelli@gmail.com>
Date: Fri, 21 Jun 2024 20:30:41 -0400
Subject: [PATCH] FEAT: Added Twitch Live Alerts REF: Changed Twitch module to
 use the DB

---
 modules/data/db.py              |   8 +++
 modules/social/twitch_module.py | 111 +++++++++++++++++++++++++++++++-
 2 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/modules/data/db.py b/modules/data/db.py
index 7210e9c..c313e16 100644
--- a/modules/data/db.py
+++ b/modules/data/db.py
@@ -22,6 +22,14 @@ def initialize_db():
         )
     ''')
 
+    # Followed channels table
+    cursor.execute('''
+        CREATE TABLE IF NOT EXISTS followed_channels (
+            twitch_name TEXT PRIMARY KEY,
+            discord_channel_id INTEGER
+        )
+    ''')
+
     conn.commit()
     conn.close()
 
diff --git a/modules/social/twitch_module.py b/modules/social/twitch_module.py
index f9f041f..64bf3e5 100644
--- a/modules/social/twitch_module.py
+++ b/modules/social/twitch_module.py
@@ -4,6 +4,8 @@ from twitchAPI.twitch import Twitch
 from twitchAPI.helper import first
 import logging
 import config
+import asyncio
+from modules.data.db import get_connection
 
 logger = logging.getLogger(__name__)
 
@@ -14,12 +16,117 @@ class TwitchModule:
         self.twitch = Twitch(config.TWITCH_CLIENT_ID,
                              config.TWITCH_CLIENT_SECRET)
         self.bot.loop.create_task(self.authenticate_twitch())
+        self.bot.loop.create_task(self.check_live_streams())
         self.add_commands()
 
     async def authenticate_twitch(self):
-        await self.twitch.authenticate_app([])
+        await self.twitch.authenticate_app([])  # Authenticate without scopes
+
+    async def check_live_streams(self):
+        while True:
+            conn = get_connection()
+            c = conn.cursor()
+            c.execute('''
+                CREATE TABLE IF NOT EXISTS live_status (
+                    twitch_name TEXT PRIMARY KEY,
+                    is_live BOOLEAN
+                )
+            ''')
+            conn.commit()
+
+            c.execute('SELECT twitch_name, discord_channel_id FROM followed_channels')
+            followed_channels = c.fetchall()
+
+            for twitch_name, discord_channel_id in followed_channels:
+                try:
+                    user_info = await first(self.twitch.get_users(logins=[twitch_name]))
+                    if not user_info:
+                        continue
+
+                    user_id = user_info.id
+                    streams = await first(self.twitch.get_streams(user_id=[user_id]))
+                    is_live = streams is not None
+
+                    c.execute('SELECT is_live FROM live_status WHERE twitch_name = ?', (twitch_name,))
+                    row = c.fetchone()
+                    was_live = row[0] if row else False
+
+                    if is_live and not was_live:
+                        channel = self.bot.get_channel(discord_channel_id)
+                        if channel:
+                            embed = discord.Embed(
+                                title=f"{twitch_name} is Live!",
+                                description=(
+                                    f"**Title:** {streams.title}\n"
+                                    f"**Game:** {streams.game_name}\n"
+                                    f"**Viewers:** {streams.viewer_count}"
+                                ),
+                                color=discord.Color.green()
+                            )
+                            embed.set_thumbnail(
+                                url=streams.thumbnail_url.replace('{width}', '320').replace('{height}', '180')
+                            )
+                            await channel.send(embed=embed)
+
+                    c.execute('INSERT OR REPLACE INTO live_status (twitch_name, is_live) VALUES (?, ?)', (twitch_name, is_live))
+                    conn.commit()
+
+                except Exception as e:
+                    logger.error(f"Error checking live status for {twitch_name}: {e}", exc_info=True)
+
+            conn.close()
+            await asyncio.sleep(120)  # Check every 2 minutes for testing purposes
 
     def add_commands(self):
+        @app_commands.command(
+            name="follow_twitch",
+            description="Follow a Twitch channel to get live alerts"
+        )
+        async def follow_twitch(interaction: discord.Interaction, twitch_name: str, channel: discord.TextChannel = None):
+            channel = channel or interaction.channel
+            conn = get_connection()
+            c = conn.cursor()
+            c.execute(
+                'INSERT OR REPLACE INTO followed_channels (twitch_name, discord_channel_id) VALUES (?, ?)',
+                (twitch_name, channel.id)
+            )
+            conn.commit()
+            conn.close()
+            await interaction.response.send_message(
+                embed=discord.Embed(
+                    title="Followed Twitch Channel",
+                    description=f"Now following {twitch_name}. Alerts will be sent to {channel.mention}.",
+                    color=discord.Color.green()
+                )
+            )
+            logger.info(f"Now following {twitch_name} for alerts in {channel.name}")
+
+        @app_commands.command(
+            name="unfollow_twitch",
+            description="Unfollow a Twitch channel"
+        )
+        async def unfollow_twitch(interaction: discord.Interaction, twitch_name: str):
+            conn = get_connection()
+            c = conn.cursor()
+            c.execute(
+                'DELETE FROM followed_channels WHERE twitch_name = ?',
+                (twitch_name,)
+            )
+            c.execute(
+                'DELETE FROM live_status WHERE twitch_name = ?',
+                (twitch_name,)
+            )
+            conn.commit()
+            conn.close()
+            await interaction.response.send_message(
+                embed=discord.Embed(
+                    title="Unfollowed Twitch Channel",
+                    description=f"No longer following {twitch_name}.",
+                    color=discord.Color.red()
+                )
+            )
+            logger.info(f"No longer following {twitch_name}")
+
         @app_commands.command(
             name="twitch_live",
             description="Check if a Twitch streamer is live"
@@ -71,6 +178,8 @@ class TwitchModule:
                 logger.error(f"Error in twitch_live command: {e}", exc_info=True)
                 await interaction.followup.send(f"An error occurred: {e}")
 
+        self.bot.tree.add_command(follow_twitch)
+        self.bot.tree.add_command(unfollow_twitch)
         self.bot.tree.add_command(twitch_live)