rftdgsh
This commit is contained in:
@@ -5,36 +5,28 @@ import models
|
||||
import security
|
||||
|
||||
class TwitchBot(twitchio.Client):
|
||||
def __init__(self, access_token: str, refresh_token: str, client_id: str, client_secret: str, channel_name: str, websocket_manager, db_user_id: int):
|
||||
def __init__(self, websocket_manager, db_user_id: int):
|
||||
self.websocket_manager = websocket_manager
|
||||
# Store our application's database user ID to avoid conflict with twitchio's internal 'owner_id'
|
||||
self.db_user_id = db_user_id
|
||||
self.is_initialized = False # Health check flag
|
||||
|
||||
async def start(self, access_token: str, refresh_token: str, client_id: str, client_secret: str, channel_name: str):
|
||||
"""
|
||||
A custom start method that also handles initialization. This makes the
|
||||
entire setup process an awaitable, atomic operation.
|
||||
"""
|
||||
print(f"DIAGNOSTIC: Initializing and connecting for user {self.db_user_id}...")
|
||||
|
||||
# Per twitchio documentation, the token must be prefixed with 'oauth:'.
|
||||
# This is a common cause of silent authentication failures.
|
||||
formatted_token = f"oauth:{access_token}"
|
||||
|
||||
super().__init__(
|
||||
token=formatted_token,
|
||||
# The client_id and client_secret are required for the client to
|
||||
# identify itself with Twitch's services.
|
||||
client_id=client_id,
|
||||
client_secret=client_secret,
|
||||
refresh_token=refresh_token,
|
||||
initial_channels=[channel_name],
|
||||
ssl=True # Mandate: Explicitly use SSL for the IRC connection.
|
||||
)
|
||||
|
||||
# The sensitive __init__ call is now inside the awaitable task.
|
||||
super().__init__(token=formatted_token, client_id=client_id, client_secret=client_secret,
|
||||
refresh_token=refresh_token, initial_channels=[channel_name], ssl=True)
|
||||
self.channel_name = channel_name
|
||||
# Health Check: If __init__ completes successfully, this flag will exist.
|
||||
# If super().__init__ fails silently, this line is never reached.
|
||||
self.is_initialized = True
|
||||
|
||||
async def start(self):
|
||||
"""
|
||||
A custom start method to bypass the default start() which can
|
||||
unpredictably start a webserver. This gives us direct control.
|
||||
"""
|
||||
print(f"DIAGNOSTIC: Initiating secure connection for user {self.db_user_id}...")
|
||||
await self.connect()
|
||||
await self.join_channels([self.channel_name])
|
||||
|
||||
|
||||
@@ -32,22 +32,20 @@ class ListenerManager:
|
||||
access_token = tokens['access_token']
|
||||
refresh_token = tokens['refresh_token']
|
||||
|
||||
# Initialize the bot object without credentials first. It's just a lightweight container.
|
||||
bot = TwitchBot(
|
||||
access_token=access_token,
|
||||
refresh_token=refresh_token,
|
||||
channel_name=user.username,
|
||||
client_id=settings.TWITCH_CLIENT_ID,
|
||||
client_secret=settings.TWITCH_CLIENT_SECRET,
|
||||
websocket_manager=websocket_manager,
|
||||
db_user_id=user.id
|
||||
)
|
||||
|
||||
# Verify that the bot was initialized correctly before proceeding.
|
||||
# This catches silent failures from the twitchio library.
|
||||
if not getattr(bot, 'is_initialized', False):
|
||||
raise Exception("TwitchBot failed to initialize, likely due to an invalid token.")
|
||||
|
||||
task = asyncio.create_task(bot.start())
|
||||
# Create a task that runs our new start method with all credentials.
|
||||
# If super().__init__ fails inside bot.start(), the exception will be
|
||||
# caught by our try/except block here, preventing hollow objects.
|
||||
task = asyncio.create_task(bot.start(
|
||||
access_token=access_token, refresh_token=refresh_token,
|
||||
client_id=settings.TWITCH_CLIENT_ID, client_secret=settings.TWITCH_CLIENT_SECRET,
|
||||
channel_name=user.username
|
||||
))
|
||||
# Store both the task and the bot instance for graceful shutdown
|
||||
self.active_listeners[user.id] = {"task": task, "bot": bot}
|
||||
except Exception as e:
|
||||
@@ -66,7 +64,8 @@ class ListenerManager:
|
||||
bot = listener_info["bot"]
|
||||
|
||||
# Gracefully close the bot's connection
|
||||
if bot and not bot.is_closed():
|
||||
# The getattr check prevents the shutdown crash if the bot was never initialized.
|
||||
if bot and getattr(bot, 'is_initialized', False) and not bot.is_closed():
|
||||
await bot.close()
|
||||
|
||||
# Cancel the asyncio task
|
||||
|
||||
Reference in New Issue
Block a user