Refactor code structure for improved readability and maintainability

This commit is contained in:
MaddoScientisto 2026-02-09 23:46:11 +01:00
commit e078cada3b
11 changed files with 1640 additions and 1184 deletions

140
modules/stream_monitor.py Normal file
View file

@ -0,0 +1,140 @@
"""
Stream monitoring and API interaction for Twitch Archive.
"""
import os
import sys
from typing import Dict, Optional, Any
import requests
from colorama import Fore, Style
from .constants import TWITCH_OAUTH_URL, TWITCH_API_URL, TWITCH_GQL_URL, TWITCH_GQL_CLIENT_ID
class StreamMonitor:
"""Handles Twitch API interactions for monitoring stream status."""
def __init__(self, username: str):
"""
Initialize the stream monitor.
Args:
username: Twitch username to monitor
"""
self.username = username
self._oauth_token = None
def get_oauth_token(self) -> str:
"""
Get OAuth token from Twitch API.
Uses CLIENT-ID and CLIENT-SECRET from environment variables.
Returns:
str: OAuth access token
Raises:
SystemExit: If authentication fails
"""
if self._oauth_token:
return self._oauth_token
try:
url = f"{TWITCH_OAUTH_URL}?client_id={os.getenv('CLIENT-ID')}&client_secret={os.getenv('CLIENT-SECRET')}&grant_type=client_credentials"
response = requests.post(url, timeout=15)
response.raise_for_status()
self._oauth_token = response.json()['access_token']
return self._oauth_token
except requests.exceptions.RequestException as e:
print(f'{Fore.RED}✗ ERROR: Failed to authenticate with Twitch API{Style.RESET_ALL}')
print(f'{Fore.YELLOW} {str(e)}{Style.RESET_ALL}')
print(f'{Fore.CYAN} → Check your CLIENT-ID and CLIENT-SECRET in the .env file{Style.RESET_ALL}')
sys.exit(1)
except KeyError:
print(f'{Fore.RED}✗ ERROR: Invalid response from Twitch API{Style.RESET_ALL}')
print(f'{Fore.CYAN} → Verify your CLIENT-ID and CLIENT-SECRET are correct{Style.RESET_ALL}')
sys.exit(1)
def validate_username(self) -> bool:
"""
Validate that the configured Twitch username exists.
Returns:
bool: True if username exists, False otherwise
Raises:
SystemExit: If username is invalid or doesn't exist
"""
try:
url = f'{TWITCH_API_URL}/users?login={self.username}'
headers = {
"Authorization": f"Bearer {self.get_oauth_token()}",
"Client-ID": os.getenv('CLIENT-ID')
}
response = requests.get(url, headers=headers, timeout=15)
response.raise_for_status()
data = response.json()
if not data.get('data'):
print(f'{Fore.RED}✗ ERROR: Twitch user "{self.username}" not found{Style.RESET_ALL}')
print(f'{Fore.CYAN} → Check the username in your config file{Style.RESET_ALL}')
sys.exit(1)
print(f'{Fore.GREEN}✓ Username "{self.username}" validated{Style.RESET_ALL}')
return True
except requests.exceptions.RequestException as e:
print(f'{Fore.RED}✗ ERROR: Could not validate username{Style.RESET_ALL}')
print(f'{Fore.YELLOW} {str(e)}{Style.RESET_ALL}')
sys.exit(1)
def check_stream_status(self) -> Optional[Dict[str, Any]]:
"""
Check if the configured user is currently live.
Returns:
dict: Stream information if live, None if offline
Raises:
SystemExit: If API request fails
"""
query = f'query{{user(login: "{self.username}") {{stream{{archiveVideo{{id}}title createdAt}}}}}}'
try:
response = requests.post(
TWITCH_GQL_URL,
json={'query': query},
headers={"Client-ID": TWITCH_GQL_CLIENT_ID},
timeout=15
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f'{Fore.RED}✗ ERROR: Failed to check stream status{Style.RESET_ALL}')
print(f'{Fore.YELLOW} {str(e)}{Style.RESET_ALL}')
sys.exit(1)
def get_latest_vod(self) -> Optional[Dict[str, Any]]:
"""
Get the most recent VOD for the configured user.
Returns:
dict: VOD information, or None if no VODs found
"""
query = f'query {{user(login: "{self.username}") {{videos(first: 1) {{edges {{node {{id title description recordedAt lengthSeconds animatedPreviewURL previewThumbnailURL(height: 1280, width: 720) thumbnailURLs(height: 1280, width: 720)}}}}}}}}}}'
try:
response = requests.post(
TWITCH_GQL_URL,
json={'query': query},
headers={"Client-ID": TWITCH_GQL_CLIENT_ID},
timeout=15
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f'{Fore.YELLOW}⚠ Warning: Could not fetch latest VOD{Style.RESET_ALL}')
print(f'{Fore.YELLOW} {str(e)}{Style.RESET_ALL}')
return None