ARTICLE AD BOX
In my quest to integrate my twitch chat into a useable format I have spent hours going over why my bot won't connect.
I use Asyncio websockets as I have multiple task needing to run side-by-side, and my twitch bot is showing no errors, but nothing that is typed into the twitch chat is being registered on the bot. After mulling over it for awhile, I discovered that the self._websockets variable is showing up as defaultdict(<class 'dict'>, {}). I looked it up and sources said it meant my bot wasn't actually connecting to my channel.
Without revealing too much information, let me go through the process that I set this up as along with revealing my code:
1. I set up a new application under the "Chat Bot" category in Twitch Dev Tools.
- The Chat:Read and Chat:Write options are both checked off.
- the OAuth URL Callback is set to "http://localhost:4343/oauth/callback"
2. My "Bot" uses the ClientID and the generated SECRET from that, and the bot's ID is just my account's ID as I prefer to not create an entirely separate account just to control my OBS browser source. I double checked the OAUTH token and it should be correct, I even attempted regenerating the code, the chat:read/write options are both checked off.
3. The initial channels is just a single element list using only my channel's name in lowercase.
I am unsure what I am doing wrong, I can show code so you might see my strife.
main.py
import asyncio, json, websockets from classes import ws_logic from classes.twitchintegration import TwitchChatBot # Config --- def get_config() -> dict: with open(f"config/settings.json", 'r') as conffile: return json.load(conffile) async def main(): config = get_config() server = await websockets.serve( ws_logic.handler, config["websocket"]["host"], config["websocket"]["port"] ) print(f"WebSocket Server started on ws://{config['websocket']['host']}:{config['websocket']['port']}") dispatcher_task = asyncio.create_task(ws_logic.dispatcher()) twitchchat_bot = TwitchChatBot( token=f"oauth:{config["twitch"]["oauth_token"]}", client_id=config["twitch"]["client_id"], client_secret=config["twitch"]["client_secret"], bot_id=config["twitch"]["bot_id"], channel=config["twitch"]["channel"] ) twitchchat_task = await asyncio.create_task(twitchchat_bot.start()) try: await asyncio.Future() # Run forever except KeyboardInterrupt: print("Shutting down server...") except Exception as e: print(f"Unexpected exception in main: {e}") finally: twitchchat_task.cancel() dispatcher_task.cancel() server.close() await server.wait_closed() await asyncio.gather( twitchchat_task, dispatcher_task, return_exceptions=True ) print("Server shut down successfully.") if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: passws_logic.py
import asyncio, websockets from classes.messagetypes import Base_Message clients = set() message_queue: asyncio.Queue[Base_Message] = asyncio.Queue(maxsize=200) async def handler(websocket: websockets): clients.add(websocket) print(f"Client connected. Total clients: {len(clients)}") try: await websocket.wait_closed() except Exception as e: print(f"Unexpected exception in handler: {e}") finally: clients.discard(websocket) print(f"Client disconnected. Total clients: {len(clients)}") async def dispatcher(): """ Waits for Messages in the Queue then dispatches them to the websocket clients. Sleeps Automatically when the queue is empty. """ print("Dispatcher started.") while True: message = await message_queue.get() if not clients: # No Clients -> Drop Message continue disconnected = set() for client in clients: try: await client.send(message.model_dump_json()) except (websockets.ConnectionClosedOK, websockets.ConnectionClosedError): disconnected.add(client) except KeyboardInterrupt: print("Server stopping...") return for d in disconnected: # Removes Clients in one large batch to avoid modifying the set while iterating. clients.remove(d) async def publish_message(message: Base_Message): """ Publishes a Message to the Message Queue for dispatching to all connected clients. """ print(f"Publishing message of type: {message.model_dump()}") await message_queue.put(message)twitchintegration.py
import logging import twitchio from twitchio.ext import commands from classes.messagetypes import Base_Message from classes import ws_logic handler = logging.FileHandler(filename='twitchio.log', encoding='utf-8', mode='w') twitchio.utils.setup_logging(level=logging.DEBUG, handler=handler) class TwitchChatBot(commands.Bot): def __init__(self, token: str, channel: str, client_id: str, client_secret: str, bot_id: str, nick: str = "CloverlyTwitchBot"): """ Initialize the Twitch chat bot. :param token: OAuth token (must include 'oauth:' prefix) :param channel: Twitch channel to join :param nick: Bot nickname (optional) """ self.initial_channels = [channel] super().__init__( token=token, client_id=client_id, client_secret=client_secret, bot_id=bot_id, owner_id=bot_id, prefix='!', initial_channels=self.initial_channels, nick=nick ) async def event_ready(self): print(f"[READY] Connected as {self.user.name} (ID: {self.user.id})") print(f"[READY] Initial channels: {self.initial_channels}") print(f"[READY] Websockets: {self._websockets}") # contains joined channels @commands.command(name='ping') async def ping_command(self, ctx: commands.Context): await ctx.send('Pong!') async def event_message(self, message): print(f"[DEBUG] Received message from {message.author.name}: {message.content}") chat_message = Base_Message( type="twitch_chat", message="New chat message received", data={ "author": message.author.name, "content": message.content, "timestamp": str(message.timestamp) } ) await ws_logic.publish_message(chat_message) async def event_follow(self, user): follow_message = Base_Message( type="twitch_follow", message="New follower detected", data={ "follower": user.name, "timestamp": str(user.followed_at) } ) await ws_logic.publish_message(follow_message)Any help would be appreciated as I have been stuck on this for a few hours.
