Files
2025-04-14 17:38:21 -04:00

186 lines
5.7 KiB
Python

import requests
import config
import urllib.parse
import os
import webbrowser
from config import CLIENT_ID, CLIENT_SECRET, BOT_CLIENT_ID, BOT_CLIENT_SECRET, REDIRECT_URI
SCOPES = [
"channel:read:subscriptions",
"bits:read",
"moderator:read:followers",
"channel:read:redemptions",
"channel:manage:redemptions",
"channel:manage:raids",
"channel:manage:broadcast",
"user:read:email",
"chat:read",
"chat:edit",
]
BOT_SCOPES = ["chat:read", "chat:edit"] # Bot-only permissions
TOKEN_FILE = "bot_token.txt" # Stores bot token locally
def _request_token(payload, client_id, client_secret):
"""Internal function to request an OAuth token from Twitch."""
url = "https://id.twitch.tv/oauth2/token"
payload.update({
"client_id": client_id,
"client_secret": client_secret
})
response = requests.post(url, data=payload).json()
if "access_token" in response:
return response
else:
print(f"❌ Error getting token: {response}")
return None
### 🔹 MAIN ACCOUNT (BROADCASTER) AUTH ###
def get_oauth_url():
"""Generates OAuth URL for broadcaster authentication (event subscriptions, rewards, etc.)."""
scope_str = " ".join(SCOPES)
encoded_scope = urllib.parse.quote(scope_str)
url = (
f"https://id.twitch.tv/oauth2/authorize"
f"?client_id={CLIENT_ID}"
f"&redirect_uri={urllib.parse.quote(REDIRECT_URI)}"
f"&response_type=code"
f"&scope={encoded_scope}"
)
print(f"🔗 Opening browser for broadcaster authentication: {url}")
webbrowser.open(url)
return url
def get_user_access_token(auth_code):
"""Gets a user access token for the broadcaster account."""
print(f"🔄 Exchanging authorization code for token: {auth_code}")
token_data = _request_token({
"code": auth_code,
"grant_type": "authorization_code",
"redirect_uri": REDIRECT_URI
}, CLIENT_ID, CLIENT_SECRET)
if token_data:
config.USER_ACCESS_TOKEN = token_data["access_token"]
config.REFRESH_TOKEN = token_data.get("refresh_token", "")
print(f"✅ Broadcaster access token retrieved successfully: {config.USER_ACCESS_TOKEN[:10]}...")
return config.USER_ACCESS_TOKEN
else:
print(f"❌ Failed to exchange authorization code. Response: {token_data}")
return None
def refresh_user_access_token():
"""Refreshes the user access token when it expires."""
if not config.REFRESH_TOKEN:
print("❌ No refresh token available. Broadcaster must reauthenticate.")
return None
token_data = _request_token({
"grant_type": "refresh_token",
"refresh_token": config.REFRESH_TOKEN
}, CLIENT_ID, CLIENT_SECRET)
if token_data:
config.USER_ACCESS_TOKEN = token_data["access_token"]
config.REFRESH_TOKEN = token_data.get("refresh_token", "")
print("🔄 Broadcaster access token refreshed.")
return config.USER_ACCESS_TOKEN
def get_app_access_token():
"""Gets an app access token for general API use (not user-specific)."""
token_data = _request_token({"grant_type": "client_credentials"}, CLIENT_ID, CLIENT_SECRET)
return token_data.get("access_token") if token_data else None
### 🔹 BOT ACCOUNT AUTH ###
def get_bot_oauth_url():
"""Generates OAuth URL for bot authentication (chat only)."""
scope_str = " ".join(BOT_SCOPES)
encoded_scope = urllib.parse.quote(scope_str)
url = (
f"https://id.twitch.tv/oauth2/authorize"
f"?client_id={BOT_CLIENT_ID}"
f"&redirect_uri={urllib.parse.quote(REDIRECT_URI)}"
f"&response_type=code"
f"&scope={encoded_scope}"
)
print(f"🔗 Opening browser for bot authentication: {url}")
webbrowser.open(url)
return url
def load_bot_token():
"""Loads the bot token from a local file."""
if os.path.exists(TOKEN_FILE):
with open(TOKEN_FILE, "r") as f:
return f.read().strip()
return None
def save_bot_token(token):
"""Saves the bot token to a local file."""
with open(TOKEN_FILE, "w") as f:
f.write(token)
def get_bot_access_token(auth_code=None):
"""Gets a bot access token for chat authentication."""
if auth_code:
token_data = _request_token({
"code": auth_code,
"grant_type": "authorization_code",
"redirect_uri": REDIRECT_URI
}, BOT_CLIENT_ID, BOT_CLIENT_SECRET)
else:
print("❌ No bot authorization code provided. Re-authentication required.")
get_bot_oauth_url()
return None
if token_data:
config.BOT_OAUTH_TOKEN = token_data["access_token"]
save_bot_token(config.BOT_OAUTH_TOKEN)
print(f"✅ Bot access token retrieved successfully.")
return config.BOT_OAUTH_TOKEN
return None
def ensure_bot_token():
"""Ensures the bot has a valid token before starting chat."""
bot_token = load_bot_token()
if bot_token:
# Validate the token
headers = {
"Authorization": f"Bearer {bot_token}"
}
response = requests.get("https://id.twitch.tv/oauth2/validate", headers=headers)
if response.status_code == 200:
# Token is valid, format it correctly for IRC
if bot_token.startswith("oauth:"):
config.BOT_OAUTH_TOKEN = bot_token
else:
config.BOT_OAUTH_TOKEN = f"oauth:{bot_token}"
print("✅ Using saved bot token.")
return
else:
print("🔄 Existing bot token is invalid. Getting a new one.")
print("🔄 No valid bot token found. Redirecting to Twitch for bot authentication.")
get_bot_oauth_url()