2024-06-26 22:00:44 -04:00
import discord
from discord import app_commands
import requests
import logging
import asyncio
import sqlite3
from config import config
2024-06-30 15:54:13 -04:00
2024-06-26 22:00:44 -04:00
class Twitch :
def __init__ ( self , bot ) :
self . bot = bot
self . client_id = config [ ' TWITCH_CLIENT_ID ' ]
self . client_secret = config [ ' TWITCH_CLIENT_SECRET ' ]
self . logger = logging . getLogger ( ' Twitch ' )
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 '
self . token = None
self . token_expiry = None
self . channel_alerts = { }
2024-07-10 12:10:30 -04:00
self . ensure_table_exists ( )
def ensure_table_exists ( self ) :
conn = sqlite3 . connect ( self . db_path )
cursor = conn . cursor ( )
cursor . execute ( """
CREATE TABLE IF NOT EXISTS twitch_channels (
channel_name TEXT PRIMARY KEY ,
alert_channel_id TEXT
) ;
""" )
conn . commit ( )
conn . close ( )
self . logger . info ( ' Twitch channels table ensured in database ' )
2024-06-26 22:00:44 -04:00
async def get_token ( self ) :
url = " https://id.twitch.tv/oauth2/token "
params = {
' client_id ' : self . client_id ,
' client_secret ' : self . client_secret ,
' grant_type ' : ' client_credentials '
}
response = requests . post ( url , params = params )
if response . status_code == 200 :
data = response . json ( )
self . token = data [ ' access_token ' ]
self . token_expiry = asyncio . get_event_loop ( ) . time ( ) + data [ ' expires_in ' ]
self . logger . info ( ' Successfully obtained Twitch token ' )
else :
self . logger . error ( f ' Failed to obtain Twitch token: { response . status_code } - { response . text } ' )
async def ensure_token ( self ) :
if not self . token or asyncio . get_event_loop ( ) . time ( ) > = self . token_expiry :
await self . get_token ( )
async def fetch_channel_info ( self , channel_name ) :
await self . ensure_token ( )
url = " https://api.twitch.tv/helix/streams "
headers = {
' Authorization ' : f ' Bearer { self . token } ' ,
' Client-Id ' : self . client_id
}
params = {
' user_login ' : channel_name
}
response = requests . get ( url , headers = headers , params = params )
self . logger . debug ( f ' Response status code: { response . status_code } ' )
self . logger . debug ( f ' Response content: { response . content } ' )
if response . status_code == 200 :
data = response . json ( )
if data [ ' data ' ] :
return data [ ' data ' ] [ 0 ] # Return the first stream (should only be one)
return None
2024-06-30 15:54:13 -04:00
async def fetch_user_info ( self , user_id ) :
2024-06-26 22:00:44 -04:00
await self . ensure_token ( )
2024-06-30 15:54:13 -04:00
url = f " https://api.twitch.tv/helix/users?id= { user_id } "
2024-06-26 22:00:44 -04:00
headers = {
' Authorization ' : f ' Bearer { self . token } ' ,
' Client-Id ' : self . client_id
}
response = requests . get ( url , headers = headers )
if response . status_code == 200 :
data = response . json ( )
if data [ ' data ' ] :
return data [ ' data ' ] [ 0 ]
return None
async def check_channels ( self ) :
conn = sqlite3 . connect ( self . db_path )
cursor = conn . cursor ( )
2024-06-30 15:54:13 -04:00
cursor . execute ( " SELECT channel_name, alert_channel_id FROM twitch_channels " )
2024-06-26 22:00:44 -04:00
channels = cursor . fetchall ( )
2024-06-30 15:54:13 -04:00
for channel_name , alert_channel_id in channels :
2024-06-26 22:00:44 -04:00
channel_info = await self . fetch_channel_info ( channel_name )
if channel_info and not self . channel_alerts . get ( channel_name ) :
2024-06-30 15:54:13 -04:00
await self . send_alert ( alert_channel_id , channel_info )
2024-06-26 22:00:44 -04:00
self . channel_alerts [ channel_name ] = True
elif not channel_info and self . channel_alerts . get ( channel_name ) :
self . channel_alerts [ channel_name ] = False
conn . close ( )
2024-06-30 15:54:13 -04:00
async def send_alert ( self , alert_channel_id , channel_info ) :
user_info = await self . fetch_user_info ( channel_info [ ' user_id ' ] )
2024-06-26 22:00:44 -04:00
channel = self . bot . get_channel ( alert_channel_id )
if channel :
title = channel_info [ ' title ' ]
url = f " https://www.twitch.tv/ { channel_info [ ' user_login ' ] } "
thumbnail = channel_info [ ' thumbnail_url ' ] . replace ( ' {width} ' , ' 320 ' ) . replace ( ' {height} ' , ' 180 ' )
2024-06-30 15:54:13 -04:00
logo = user_info [ ' profile_image_url ' ] if user_info else None
2024-06-26 22:00:44 -04:00
embed = discord . Embed ( title = title , url = url , color = discord . Color . purple ( ) )
2024-06-30 15:54:13 -04:00
embed . set_thumbnail ( url = logo if logo else thumbnail )
2024-06-26 22:00:44 -04:00
embed . add_field ( name = " Channel " , value = channel_info [ ' user_name ' ] , inline = True )
embed . add_field ( name = " Game " , value = channel_info [ ' game_name ' ] , inline = True )
await channel . send ( embed = embed )
2024-07-03 11:43:28 -04:00
async def is_channel_followed ( self , channel_name ) :
conn = sqlite3 . connect ( self . db_path )
cursor = conn . cursor ( )
cursor . execute ( " SELECT 1 FROM twitch_channels WHERE channel_name = ? " , ( channel_name , ) )
result = cursor . fetchone ( )
conn . close ( )
return result is not None
2024-06-26 22:00:44 -04:00
def setup ( self , tree : app_commands . CommandTree ) :
@tree.command ( name = " add_twitch_channel " , description = " Add a Twitch channel to monitor " )
async def add_twitch_channel_command ( interaction : discord . Interaction , channel_name : str , alert_channel : discord . TextChannel ) :
2024-07-03 11:43:28 -04:00
if await self . is_channel_followed ( channel_name ) :
await interaction . response . send_message ( embed = discord . Embed ( description = f " Twitch channel { channel_name } is already being monitored. " , color = discord . Color . orange ( ) ) )
return
2024-06-30 15:54:13 -04:00
conn = sqlite3 . connect ( self . db_path )
cursor = conn . cursor ( )
cursor . execute ( " INSERT INTO twitch_channels (channel_name, alert_channel_id) VALUES (?, ?) " , ( channel_name , alert_channel . id ) )
conn . commit ( )
conn . close ( )
await interaction . response . send_message ( embed = discord . Embed ( description = f " Added Twitch channel { channel_name } to monitor. " , color = discord . Color . green ( ) ) )
2024-06-26 22:00:44 -04:00
@tree.command ( name = " remove_twitch_channel " , description = " Remove a Twitch channel from monitoring " )
async def remove_twitch_channel_command ( interaction : discord . Interaction , channel_name : str ) :
2024-07-03 11:43:28 -04:00
if not await self . is_channel_followed ( channel_name ) :
await interaction . response . send_message ( embed = discord . Embed ( description = f " Twitch channel { channel_name } is not being monitored. " , color = discord . Color . red ( ) ) )
return
2024-06-26 22:00:44 -04:00
conn = sqlite3 . connect ( self . db_path )
cursor = conn . cursor ( )
cursor . execute ( " DELETE FROM twitch_channels WHERE channel_name = ? " , ( channel_name , ) )
conn . commit ( )
conn . close ( )
await interaction . response . send_message ( embed = discord . Embed ( description = f " Removed Twitch channel { channel_name } from monitoring. " , color = discord . Color . green ( ) ) )
@tree.command ( name = " check_twitch_channel " , description = " Check if a Twitch channel is live " )
async def check_twitch_channel_command ( interaction : discord . Interaction , channel_name : str ) :
channel_info = await self . fetch_channel_info ( channel_name )
2024-06-30 15:54:13 -04:00
user_info = await self . fetch_user_info ( channel_info [ ' user_id ' ] ) if channel_info else None
2024-06-26 22:00:44 -04:00
if channel_info :
thumbnail = channel_info [ ' thumbnail_url ' ] . replace ( ' {width} ' , ' 320 ' ) . replace ( ' {height} ' , ' 180 ' )
2024-06-30 15:54:13 -04:00
logo = user_info [ ' profile_image_url ' ] if user_info else None
2024-06-26 22:00:44 -04:00
embed = discord . Embed ( title = f " { channel_info [ ' user_name ' ] } is live! " , url = f " https://www.twitch.tv/ { channel_info [ ' user_login ' ] } " , color = discord . Color . purple ( ) )
2024-06-30 15:54:13 -04:00
embed . set_thumbnail ( url = logo if logo else thumbnail )
2024-06-26 22:00:44 -04:00
embed . add_field ( name = " Title " , value = channel_info [ ' title ' ] , inline = False )
embed . add_field ( name = " Game " , value = channel_info [ ' game_name ' ] , inline = False )
await interaction . response . send_message ( embed = embed )
else :
2024-06-30 15:54:13 -04:00
await interaction . response . send_message ( embed = discord . Embed ( description = f " { channel_name } is not live. " , color = discord . Color . red ( ) ) )
2024-06-26 22:00:44 -04:00
if not tree . get_command ( " add_twitch_channel " ) :
tree . add_command ( add_twitch_channel_command )
if not tree . get_command ( " remove_twitch_channel " ) :
tree . add_command ( remove_twitch_channel_command )
if not tree . get_command ( " check_twitch_channel " ) :
tree . add_command ( check_twitch_channel_command )
async def setup_hook ( self ) :
await self . bot . wait_until_ready ( )
await self . get_token ( )
while not self . bot . is_closed ( ) :
await self . check_channels ( )
await asyncio . sleep ( 300 ) # Check every 5 minutes
def setup ( bot ) :
twitch = Twitch ( bot )
twitch . setup ( bot . tree )
bot . loop . create_task ( twitch . setup_hook ( ) )