twitchAPI Python Wrapper

repository·master·Indexed 43 days ago

https://github.com/teekeks/pytwitchapi

A Python wrapper for the Twitch API providing high-level abstractions for Twitch services, including the Helix API, Chat, and EventSub. It features built-in support for App and User OAuth authentication, a comprehensive Chat bot interface with command middleware, and integration with the twitch-cli for mocking API calls.

Tokens
10.8K
Snippets
30
Records
32
Agent score
55%

What's inside twitchAPI

  1. Iterate over AsyncIterTwitchObject

    master

    An AsyncIterTwitchObject is used when an API call returns useful data outside of the paginated list. You must use async for to iterate over the segments or items contained within the object.

    # schedule contains metadata like .broadcaster_name
    schedule = await twitch.get_channel_stream_schedule('user_id')
    print(schedule.broadcaster_name)
    async for segment in schedule:
        print(segment.title)
  2. Iterate over IterTwitchObject

    master

    Some API calls return an IterTwitchObject. These objects contain useful metadata outside of the list itself (e.g., totals or counts) but are also directly iterable to access the items within the list.

    # lb contains metadata like .total, but is also iterable
    lb = await twitch.get_bits_leaderboard()
    print(lb.total)
    for e in lb:
        print(f'#{e.rank:02d} - {e.user_name}: {e.score}')
  3. How chat command middleware works

    master

    Command Middleware acts as a set of filters that decide whether a chat command should be executed by a user. This is useful for restricting commands to specific users (e.g., administrators), specific chat rooms, or specific conditions.

    There are two scopes of middleware:

    1. Global command middleware: Applied to all commands registered in the chat instance. Use chat.register_command_middleware() to set these.
    2. Single command middleware: Applied only to a specific command. Use the command_middleware argument within chat.register_command() to set these.
    # Global middleware example
    chat.register_command_middleware(UserRestriction(allowed_users=['user1']))
    
    # Single command middleware example
    chat.register_command('one', command_one, command_middleware=[StreamerOnly()])
  4. Configure App Authentication

    master

    App authentication is the simplest form of authentication and is used for many Helix API calls. It is initialized during the creation of the Twitch instance.

    from twitchAPI.twitch import Twitch
    
    # Initializing with credentials automatically sets up App Auth
    twitch = await Twitch('my_app_id', 'my_app_secret')
  5. Use EventSub to listen for Twitch events

    master
    EventSub allows you to listen for real-time events on Twitch. The EventSub client runs in its own thread and executes a provided callback function whenever a relevant event occurs. Multiple transport types are available depending on your use case.
  6. Use AsyncGenerators for automatic pagination

    master

    Some API calls return an AsyncGenerator. These automatically handle pagination for you, iterating over all possible results.

    Warning: Some endpoints (like stream schedules with repeating entries) may result in an endless stream. Always implement your own exit conditions when using these generators.

    # Automatically iterates through all pages of tags
    async for tag in twitch.get_all_stream_tags():
        print(tag.tag_id)
  7. Configure User Authentication

    master

    User authentication requires the user to explicitly authorize your application via a Twitch login prompt.

    1. Setup Redirect URL: In your Twitch Developer Dashboard, add http://localhost:17563 as an OAuth Redirect URL.
    2. Use UserAuthenticator: Use the UserAuthenticator class to trigger the browser-based authorization flow.
    3. Apply Token: Once the token is obtained, apply it to the Twitch instance using set_user_authentication.
    from twitchAPI.twitch import Twitch
    from twitchAPI.oauth import UserAuthenticator
    from twitchAPI.type import AuthScope
    
    twitch = await Twitch('my_app_id', 'my_app_secret')
    
    target_scope = [AuthScope.BITS_READ]
    auth = UserAuthenticator(twitch, target_scope, force_verify=False)
    
    # Opens browser for Twitch verification
    token, refresh_token = await auth.authenticate()
    
    # Apply the user authentication to the instance
    await twitch.set_user_authentication(token, target_scope, refresh_token)
  8. Set up twitch-cli for mocking

    master

    To mock API calls and EventSub, use the official twitch-cli.

    1. Install and set up twitch-cli following the official Twitch documentation.
    2. Generate mock credentials by running:
      twitch mock-api generate
      Note the generated Client ID, Client Secret, and the User ID provided in the output (e.g., User ID 53100947 has all applicable units).
    3. Start the mock server:
      twitch mock-api start
    twitch mock-api generate
    twitch mock-api start
  9. Migrate from v2 to v3 (Async Transition)

    master

    Version 3 of pytwitchapi is fully asynchronous. This is a breaking change from version 2, which used a mix of sync and async patterns.

    Key changes include:

    • The Twitch object must now be initialized using await.
    • All refresh callbacks (app_auth_refresh_callback, user_auth_refresh_callback) must be async functions.
    • All PubSub callbacks must be async functions.
    • All EventSub methods (including listen_, unsubscribe_, and stop()) are now asynchronous and must be called with await.
    • API responses have switched from raw dictionaries to specialized objects and generators.
    from twitchAPI.twitch import Twitch
    
    # V3 Initialization
    async def user_refresh(token: str, refresh_token: str):
        print(f'my new user token is: {token}')
    
    async def app_refresh(token: str):
        print(f'my new app token is: {token}')
    
    twitch = await Twitch('my_app_id', 'my_app_secret')
    twitch.app_auth_refresh_callback = app_refresh
    twitch.user_auth_refresh_callback = user_refresh
  10. Migrate EventSub to v4 Webhooks

    master

    EventSub has introduced a new transport mechanism. The original EventSub class is now located in twitchAPI.eventsub.webhook and has been renamed to EventSubWebhook.

    Key Changes:

    • Class Name: Use EventSubWebhook instead of EventSub.
    • Callback Data: Topic callbacks no longer receive plain dictionaries (dict). They now receive specific Event objects (e.g., ChannelFollowEvent). You should update your callback function signatures to expect these objects instead of dictionaries.
    # V4 (now)
    from twitchAPI.eventsub.webhook import EventSubWebhook
    from twitchAPI.object.eventsub import ChannelFollowEvent
    import asyncio
    
    EVENTSUB_URL = 'https://url.to.your.webhook.com'
    
    # Note: data is now a ChannelFollowEvent object, not a dict
    async def on_follow(data: ChannelFollowEvent):
        print(f'{data.event.user_name} now follows {data.event.broadcaster_user_name}!')
    
    
    async def eventsub_webhook_example():
        # twitch setup is left out of this example
    
        eventsub = EventSubWebhook(EVENTSUB_URL, 8080, twitch)
        await eventsub.unsubscribe_all()
        eventsub.start()
        await eventsub.listen_channel_follow_v2(user.id, user.id, on_follow)
    
        try:
            input('press Enter to shut down...')
        finally:
            await eventsub.stop()
            await twitch.close()
        print('done')
    
    
    asyncio.run(eventsub_webhook_example())
  11. Write your own Chat Command Middleware

    master

    To implement custom logic for chat commands, extend the BaseCommandMiddleware class. A custom middleware allows you to intercept command execution to decide if a command should run or to perform actions after a command has been executed.

    To create a middleware, you must implement two primary methods:

    1. can_execute(cmd: ChatCommand) -> bool: This method is called before the command runs. Return True if the command should proceed, or False to block it.
    2. was_executed(cmd: ChatCommand): This method is called after the command has been executed. Use this to update internal state or perform post-execution logic.

    It is recommended to include an execute_blocked_handler in your __init__ method to maintain compatibility with default middlewares, though this is optional.

    from typing import Callable, Optional, Awaitable
    from twitchAPI.chat.middleware import BaseCommandMiddleware
    
    class MyOwnCoinFlipMiddleware(BaseCommandMiddleware):
    
       # Recommended for compatibility with default middlewares
       def __init__(self, execute_blocked_handler: Optional[Callable[[ChatCommand], Awaitable[None]]] = None):
         self.execute_blocked_handler = execute_blocked_handler
    
       async def can_execute(self, cmd: ChatCommand) -> bool:
          # Return True if the command should execute and False otherwise
          import random
          return random.choice([True, False])
    
       async def was_executed(self, cmd: ChatCommand):
          # Called whenever a command this Middleware is attached to was executed
          pass
  12. Integrate UserAuthenticator into a headless application

    master

    To implement OAuth authentication in a headless environment (where you don't have a direct browser interface, such as a web server), use the UserAuthenticator class. This involves setting up a web server (like Flask) to handle the redirect from Twitch.

    Workflow

    1. Configure Twitch Dashboard: Add your redirect URL (e.g., http://localhost:5000/login/confirm) as an "OAuth Redirect URL" in the Twitch Developer Console.
    2. Initialize Twitch and Authenticator: Create a Twitch instance and pass it to UserAuthenticator along with your desired AuthScope and your redirect url.
    3. Redirect to Twitch: Create a route (e.g., /login) that redirects the user to the URL generated by auth.return_auth_url().
    4. Handle Callback: Create a callback route (e.g., /login/confirm) to receive the state and code parameters from Twitch.
      • Verify that the state from the request matches auth.state to prevent CSRF.
      • Use await auth.authenticate(user_token=code) to exchange the code for tokens.
      • Use await twitch.set_user_authentication(token, TARGET_SCOPE, refresh) to apply the new authentication to your Twitch instance.

    Implementation Example

    import asyncio
    from twitchAPI.twitch import Twitch
    from twitchAPI.oauth import UserAuthenticator
    from twitchAPI.type import AuthScope, TwitchAPIException
    from flask import Flask, redirect, request
    
    APP_ID = 'my_app_id'
    APP_SECRET = 'my_app_secret'
    TARGET_SCOPE = [AuthScope.CHAT_EDIT, AuthScope.CHAT_READ]
    MY_URL = 'http://localhost:5000/login/confirm'
    
    app = Flask(__name__)
    twitch: Twitch
    auth: UserAuthenticator
    
    @app.route('/login')
    def login():
        return redirect(auth.return_auth_url())
    
    @app.route('/login/confirm')
    async def login_confirm():
        state = request.args.get('state')
        if state != auth.state:
            return 'Bad state', 401
        code = request.args.get('code')
        if code is None:
            return 'Missing code', 400
        try:
            token, refresh = await auth.authenticate(user_token=code)
            await twitch.set_user_authentication(token, TARGET_SCOPE, refresh)
        except TwitchAPIException as e:
            return 'Failed to generate auth token', 400
        return 'Sucessfully authenticated!'
    
    async def twitch_setup():
        global twitch, auth
        twitch = await Twitch(APP_ID, APP_SECRET)
        auth = UserAuthenticator(twitch, TARGET_SCOPE, url=MY_URL)
    
    asyncio.run(twitch_setup())
    import asyncio
    from twitchAPI.twitch import Twitch
    from twitchAPI.oauth import UserAuthenticator
    from twitchAPI.type import AuthScope, TwitchAPIException
    from flask import Flask, redirect, request
    
    APP_ID = 'my_app_id'
    APP_SECRET = 'my_app_secret'
    TARGET_SCOPE = [AuthScope.CHAT_EDIT, AuthScope.CHAT_READ]
    MY_URL = 'http://localhost:5000/login/confirm'
    
    app = Flask(__name__)
    twitch: Twitch
    auth: UserAuthenticator
    
    @app.route('/login')
    def login():
        return redirect(auth.return_auth_url())
    
    @app.route('/login/confirm')
    async def login_confirm():
        state = request.args.get('state')
        if state != auth.state:
            return 'Bad state', 401
        code = request.args.get('code')
        if code is None:
            return 'Missing code', 400
        try:
            token, refresh = await auth.authenticate(user_token=code)
            await twitch.set_user_authentication(token, TARGET_SCOPE, refresh)
        except TwitchAPIException as e:
            return 'Failed to generate auth token', 400
        return 'Sucessfully authenticated!'
    
    async def twitch_setup():
        global twitch, auth
        twitch = await Twitch(APP_ID, APP_SECRET)
        auth = UserAuthenticator(twitch, TARGET_SCOPE, url=MY_URL)
    
    asyncio.run(twitch_setup())