pytwitcherapi

PyPI version Build Status Downloads per month Coverage License Documentation

This project is not developed anymore!

Twitch is a trademark or registered trademark of Twitch Interactive, Inc. in the U.S. and/or other countries. “pytwitcher” and “pytwitcherapi” is not operated by, sponsored by, or affiliated with Twitch Interactive, Inc. in any way.

Python API for interacting with twitch.tv.

Features

  • Easy-to-use object oriented high-level API
  • Search and query information about games, channels, streams and users
  • Get the livestream playlist
  • OAauth Authentication. Can retrieve followed streams and more...
  • Good documentation and test coverage
  • IRC client for the chat (with IRC v3 tag support)

Welcome to pytwitcherapi‘s documentation!

Contents:

Installation

At the command line either via easy_install or pip:

$ easy_install pytwitcherapi
$ pip install pytwitcherapi

Or, if you have virtualenvwrapper installed:

$ mkvirtualenv pytwitcherapi
$ pip install pytwitcherapi

Usage

This is a litte quickstart guide. For more information go to the Usermanual.

API requests

pytwitcherapi.TwitchSession class is the central class for interacting with twitch.tv:

1
2
3
import pytwitcherapi

ts = pytwitcherapi.TwitchSession()

To query all top games use:

5
topgames = ts.top_games()

Get streams and playlist for every game:

 7
 8
 9
10
11
for game in topgames:
    streams = ts.get_streams(game=game)
    for stream in streams:
        channel = stream.channel
        playlist = ts.get_playlist(channel)

As you can see games, channels, streams are wrapped into objects. See pytwitcherapi.Game, pytwitcherapi.Channel, pytwitcherapi.Stream, pytwitcherapi.User.

You can use your own Client-ID for twitch by setting the environment variable PYTWITCHER_CLIENT_ID.

Usermanual

In here you find help for using the pytwitcherapi. There are also some simple examples.

Requests

API requests

pytwitcherapi.TwitchSession class is the central class for interacting with twitch.tv:

1
2
3
import pytwitcherapi

ts = pytwitcherapi.TwitchSession()

To query all top games use:

5
topgames = ts.top_games()

Get streams and playlist for every game:

 7
 8
 9
10
11
for game in topgames:
    streams = ts.get_streams(game=game)
    for stream in streams:
        channel = stream.channel
        playlist = ts.get_playlist(channel)

As you can see games, channels, streams are wrapped into objects. See pytwitcherapi.Game, pytwitcherapi.Channel, pytwitcherapi.Stream, pytwitcherapi.User.

You can use your own Client-ID for twitch by setting the environment variable PYTWITCHER_CLIENT_ID.

Custom requests

You can also issue custom requests. The pytwitcherapi.TwitchSession is actually a subclass of requests.Session. So basically you can use pytwitcherapi.TwitchSession.request() to issue arbitrary requests. To make it easier to use the different twitch APIs there are a few helpers.

You can get easy access to three different twitch APIs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import pytwitcherapi

ts = pytwitcherapi.TwitchSession()

topgames = ts.top_games()

for game in topgames:
    streams = ts.get_streams(game=game)
    for stream in streams:
        channel = stream.channel
        playlist = ts.get_playlist(channel)

Authentication

For some methods of pytwitcherapi.TwitchSession, the user needs to grant pytwitcher authorization. Twitch Authentication is based on OAuth. We use the implicit grant workflow. In short, the user visits a website. Has to login, and allow pytwitcher. Twitch will redirect him to pytwitcherapi.constants.REDIRECT_URI. In the url fragment of that redirection, one can find the token we need. To make it simple for the user, here is what should be done for authentication:

  • Call pytwitcherapi.TwitchSession.start_login_server(). This will create a thread that serves a server on pytwitcherapi.constants.LOGIN_SERVER_ADRESS. Once the user gets redirected, this server will pick up the request and extract the token:

    1
    2
    3
    4
    import pytwitcherapi
    
    ts = pytwitcherapi.TwitchSession()
    ts.start_login_server()
    
  • Get the url pytwitcherapi.TwitchSession.get_auth_url() and send the user to visit that url in his favorite webbrowser. He might have to login, and allow pytwitcher, if he did not already:

    6
    7
    8
    import webbrowser
    url = ts.get_auth_url()
    webbrowser.open(url)
    
  • Wait until the user finished login in. Then call pytwitcherapi.TwitchSession.shutdown_login_server() to shutdown the server and join the thread:

    10
    11
    raw_input("Press ENTER when finished")
    ts.shutdown_login_server()
    
  • Check if the user authorized the session with pytwitcherapi.TwitchSession.authorized():

    13
    14
    assert ts.authorized, "Authorization failed! Did the user allow it?"
    print "Login User: %s" % ts.current_user
    
  • Now you can call methods that require authentication:

    16
    streams = ts.followed_streams()
    

Chat

The twitch chat is based on the IRC protocol RFC1459. The official documentation on the twitch chat is here: Twitch IRC Doc. The irc python lib might also be useful, because we use it as backend.

pytwitcherapi.IRCClient is a simple client, which can only connect to one channel/server at a time. When building applications, you probably wanna run the IRCClient in another thread, so it doesn’t block your application. The client is thread safe and has quite a few methods to send IRC commands, if the client is running in another thread. They are wrapped versions of methods from irc.client.ServerConnection. E.g. you can simply call pytwitcherapi.IRCClient.quit() from another thread. Here is a simple example, where we send messages to the channel. Change input to raw_input for python 2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import threading

import pytwitcherapi

session = ...  # we assume an authenticated TwitchSession
channel = session.get_channel('somechannel')
client = pytwitcherapi.IRCClient(session, channel)
t = threading.Thread(target=client.process_forever)
t.start()

try:
    while True:
        m = input('Send Message:')
        if not m: break;
        # will be processed in other thread
        # sends a message to the server
        client.send_msg(m)
finally:
    client.shutdown()
    t.join()

Important

The connection will wait/block if you send more messages than twitch allows. See pytwitcherapi.chat.ServerConnection3.

You can make the client handle different IRC events. Subclass the client and create a method on_<eventtype>. For example to greet everyone who joins an IRC channel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import pytwitcherapi

class MyIRCClient(pytwitcherapi.IRCClient):

    def on_join(self, connection, event):
        """Handles the join event and greets everone

        :param connection: the connection with the event
        :type connection: :class:`irc.client.ServerConnection`
        :param event: the event to handle
        :type event: :class:`irc.client.Event`
        :returns: None
        """
        target = event.source
        self.privmsg(target, 'Hello %s!' % target)

If you override pytwitcherapi.IRCClient.on_pubmsg() or pytwitcherapi.IRCClient.on_privmsg() make sure to call the super method:

1
2
3
4
5
6
7
8
from pytwitcherapi import chat

class MyIRCClient(pytwitcherapi.IRCClient):

    def on_privmsg(self, connection, event):
        super(MyIRCClient, self).on_privmsg(connection, event)

        print chat.Message3.from_event(event)

But printing out messages is not really useful. You probably want to access them in another thread. All private and public messages are stored in a thread safe message queue. By default the queue stores the last 100 messages. You can alter the queuesize when creating a client. 0 will make the queue store all messages.

Note

The Client is using two connections. One for sending messages (pytwitcherapi.IRCClient.in_connection) and one for receiving (pytwitcherapi.IRCClient.in_connection) them. With one message, you wouldn’t revceive your own messages processed from the server (with tags).

Here is a little example. To quit press CTRL-C:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import threading
import queue  # Queue for python 2

import pytwitcherapi

session = ...  # we assume an authenticated TwitchSession
channel = session.get_channel('somechannel')
client = pytwitcherapi.IRCClient(session, channel, queuesize=0)
t = threading.Thread(target=client.process_forever)
t.start()

try:
    while True:
        try:
            m = client.messages.get(block=False)
        except queue.Empty:
            pass
        else:
            # Now you have the message in the main thread
            # and can display the message in the
            # GUI or wherever you want
            print "Message from %s to %s: %s" % (m.source, m.target, m.text)
finally:
    client.shutdown()
    t.join()
Tags and metadata

Twitch does support tags. Tags store metadata about a message, like the color of the user, whether he is a subscriber, the pytwichterapi.chat.Emote etc. These messages get safed id the message queue: pytwitcherapi.IRCClient.messages. See the pytwitcherapi.chat.Message3 documentation for the additional metadata.

Here is a little example. To quit press CTRL-C:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import threading
import queue  # Queue for python 2

import pytwitcherapi

session = ...  # we assume an authenticated TwitchSession
channel = session.get_channel('somechannel')
client = pytwitcherapi.IRCClient(session, channel, queuesize=0)
t = threading.Thread(target=client.process_forever)
t.start()

try:
    while True:
        try:
            m = client.messages.get(block=False)
        except queue.Empty:
            pass
        else:
            print m.color
            print m.subscriber
            print m.turbo
            print m.emotes
            print m.user_type
finally:
    client.shutdown()
    t.join()

Developer’s Documentation

Welcome to the developer’s documenation. All necessary information for contributors who want to extend the project.

Documentation

Build

To build the documentation locally, follow these instructions:

  1. Go to the root directory of this project.

  2. Install the requirements:

    $ pip install -r docs/requirements.txt
    

    This will install sphinx, some required packages and the project in development mode (pip install -e .). That’s why you have to be in the root dir.

  3. Go to the docs dir:

    $ cd docs
    
  4. Invoke sphinx build:

    $ sphinx-build -b html -d _build/doctrees   source _build/html
    

    or alternativly with make:

    $ make html
    

    on windows:

    $ make.bat html
    

Reference

Automatic generated Documenation by apidoc and autodoc.

pytwitcherapi

Subpackages
pytwitcherapi.chat
Submodules
pytwitcherapi.chat.client

IRC client for interacting with the chat of a channel.

Classes
ChatServerStatus(server[, ip, port, status, ...]) Useful for comparing the performance of servers.
IRCClient(session, channel[, queuesize]) Simple IRC client which can connect to a single pytwitcherapi.Channel.
Reactor([on_connect, on_disconnect]) Reactor that can exit the process_forever loop.
Reactor3([on_connect, on_disconnect]) Reactor that uses irc v3 connections
Functions
add_serverconnection_methods(cls) Add a bunch of methods to an irc.client.SimpleIRCClient to send commands and messages.
Data
absolute_import
log Instances of the Logger class represent a single logging channel.
pytwitcherapi.chat.client.add_serverconnection_methods(cls)[source]

Add a bunch of methods to an irc.client.SimpleIRCClient to send commands and messages.

Basically it wraps a bunch of methdos from irc.client.ServerConnection to be irc.schedule.IScheduler.execute_after(). That way, you can easily send, even if the IRCClient is running in IRCClient.process_forever in another thread.

On the plus side you can use positional and keyword arguments instead of just positional ones.

Parameters:cls (irc.client.SimpleIRCClient) – The class to add the methods do.
Returns:None
pytwitcherapi.chat.client.absolute_import = _Feature((2, 5, 0, 'alpha', 1), (3, 0, 0, 'alpha', 0), 16384)
pytwitcherapi.chat.client.log = <logging.Logger object>[source]

Instances of the Logger class represent a single logging channel. A “logging channel” indicates an area of an application. Exactly how an “area” is defined is up to the application developer. Since an application can have any number of areas, logging channels are identified by a unique string. Application areas can be nested (e.g. an area of “input processing” might include sub-areas “read CSV files”, “read XLS files” and “read Gnumeric files”). To cater for this natural nesting, channel names are organized into a namespace hierarchy where levels are separated by periods, much like the Java or Python package namespace. So in the instance given above, channel names might be “input” for the upper level, and “input.csv”, “input.xls” and “input.gnu” for the sub-levels. There is no arbitrary limit to the depth of nesting.

pytwitcherapi.chat.connection
Classes
Event3(type, source, target[, arguments, tags]) An IRC event with tags
ServerConnection3(reactor[, msglimit, ...]) ServerConncetion that can handle irc v3 tags
Data
log Instances of the Logger class represent a single logging channel.
pytwitcherapi.chat.connection.log = <logging.Logger object>[source]

Instances of the Logger class represent a single logging channel. A “logging channel” indicates an area of an application. Exactly how an “area” is defined is up to the application developer. Since an application can have any number of areas, logging channels are identified by a unique string. Application areas can be nested (e.g. an area of “input processing” might include sub-areas “read CSV files”, “read XLS files” and “read Gnumeric files”). To cater for this natural nesting, channel names are organized into a namespace hierarchy where levels are separated by periods, much like the Java or Python package namespace. So in the instance given above, channel names might be “input” for the upper level, and “input.csv”, “input.xls” and “input.gnu” for the sub-levels. There is no arbitrary limit to the depth of nesting.

pytwitcherapi.chat.message
Classes
Chatter(source) A chat user object
Emote(emoteid, occurences) Emote from the emotes tag
Message(source, target, text) A messag object
Message3(source, target, text[, tags]) A message which stores information from irc v3 tags
Tag(name[, value, vendor]) An irc v3 tag
Module contents

Package for interacting with the IRC chat of a channel.

The main client for connecting to the channel is IRCClient.

Classes
IRCClient(session, channel[, queuesize]) Simple IRC client which can connect to a single pytwitcherapi.Channel.
Data
absolute_import
pytwitcherapi.chat.absolute_import = _Feature((2, 5, 0, 'alpha', 1), (3, 0, 0, 'alpha', 0), 16384)
Submodules
pytwitcherapi.constants

Collection of constants

These constants might be needed in multiple modules, so we pull them together here.

Data
LOGIN_SERVER_ADRESS Server adress of server that catches the redirection and the oauth token.
REDIRECT_URI The redirect url of pytwitcher.
pytwitcherapi.constants.LOGIN_SERVER_ADRESS = ('', 42420)

Server adress of server that catches the redirection and the oauth token.

pytwitcherapi.constants.REDIRECT_URI = 'http://localhost:42420'

The redirect url of pytwitcher. We do not need to redirect anywhere so localhost is set in the twitch prefrences of pytwitcher

pytwitcherapi.exceptions

Collection exceptions

Exceptions
NotAuthorizedError Exception that is raised, when the session is not authorized.
PytwitcherException Base exception for pytwitcher
pytwitcherapi.models

Contains classes that wrap the jsons returned by the twitch.tv API

Classes
Channel(name, status, displayname, game, ...) Channel on twitch.tv
Game(name, box, logo, twitchid[, viewers, ...]) Game on twitch.tv
Stream(game, channel, twitchid, viewers, preview) A stream on twitch.tv
User(usertype, name, logo, twitchid, ...) A user on twitch.tv
pytwitcherapi.oauth

Twitch.tv uses OAuth2 for authorization. We use the Implicit Grant Workflow. The user has to visit an authorization site, login, authorize PyTwitcher. Once he allows PyTwitcher, twitch will redirect him to pytwitcherapi.REDIRECT_URI. In the url fragment, there is the access token.

This module features a server, that will respond to the redirection of the user. So if twitch is redirecting to pytwitcherapi.REDIRECT_URI, the server is gonna send a website, which will extract the access token, send it as a post request and give the user a response, that everything worked.

Classes
LoginServer(session) This server responds to the redirection of the user after he granted authorization.
RedirectHandler(request, client_address, server) This request handler will handle the redirection of the user when he grants authorization to PyTwitcher and twitch redirects him.
TwitchOAuthClient(client_id[, ...]) This is a client needed for oauthlib.oauth2.OAuth2Session.
Data
log Instances of the Logger class represent a single logging channel.
pytwitcherapi.oauth.log = <logging.Logger object>[source]

Instances of the Logger class represent a single logging channel. A “logging channel” indicates an area of an application. Exactly how an “area” is defined is up to the application developer. Since an application can have any number of areas, logging channels are identified by a unique string. Application areas can be nested (e.g. an area of “input processing” might include sub-areas “read CSV files”, “read XLS files” and “read Gnumeric files”). To cater for this natural nesting, channel names are organized into a namespace hierarchy where levels are separated by periods, much like the Java or Python package namespace. So in the instance given above, channel names might be “input” for the upper level, and “input.csv”, “input.xls” and “input.gnu” for the sub-levels. There is no arbitrary limit to the depth of nesting.

pytwitcherapi.session

API for communicating with twitch

Classes
OAuthSession() Session with oauth2 support.
TwitchSession() Session for making requests to the twitch api
Functions
needs_auth(meth) Wraps a method of TwitchSession and raises an exceptions.NotAuthorizedError if before calling the method, the session isn’t authorized.
Data
AUTHORIZATION_BASE_URL Authorisation Endpoint
CLIENT_ID The client id of pytwitcher on twitch.
SCOPES The scopes that PyTwitcher needs
TWITCH_APIURL The baseurl for the old twitch api
TWITCH_HEADER_ACCEPT The header for the Accept key to tell twitch which api version it should use
TWITCH_KRAKENURL The baseurl for the twitch api
TWITCH_STATUSURL str(object=’‘) -> string
TWITCH_USHERURL The baseurl for the twitch usher api
absolute_import
log Instances of the Logger class represent a single logging channel.
pytwitcherapi.session.needs_auth(meth)[source]

Wraps a method of TwitchSession and raises an exceptions.NotAuthorizedError if before calling the method, the session isn’t authorized.

Parameters:meth
Returns:the wrapped method
Return type:Method
Raises:None
pytwitcherapi.session.AUTHORIZATION_BASE_URL = 'https://api.twitch.tv/kraken/oauth2/authorize'

Authorisation Endpoint

pytwitcherapi.session.CLIENT_ID = '642a2vtmqfumca8hmfcpkosxlkmqifb'

The client id of pytwitcher on twitch. Use environment variable PYTWITCHER_CLIENT_ID or pytwitcher default value.

pytwitcherapi.session.SCOPES = ['user_read', 'chat_login']

The scopes that PyTwitcher needs

pytwitcherapi.session.TWITCH_APIURL = 'http://api.twitch.tv/api/'

The baseurl for the old twitch api

pytwitcherapi.session.TWITCH_HEADER_ACCEPT = 'application/vnd.twitchtv.v3+json'

The header for the Accept key to tell twitch which api version it should use

pytwitcherapi.session.TWITCH_KRAKENURL = 'https://api.twitch.tv/kraken/'

The baseurl for the twitch api

pytwitcherapi.session.TWITCH_STATUSURL = 'http://twitchstatus.com/api/status?type=chat'

str(object=’‘) -> string

Return a nice string representation of the object. If the argument is a string, the return value is the same object.

pytwitcherapi.session.TWITCH_USHERURL = 'http://usher.twitch.tv/api/'

The baseurl for the twitch usher api

pytwitcherapi.session.absolute_import = _Feature((2, 5, 0, 'alpha', 1), (3, 0, 0, 'alpha', 0), 16384)
pytwitcherapi.session.log = <logging.Logger object>[source]

Instances of the Logger class represent a single logging channel. A “logging channel” indicates an area of an application. Exactly how an “area” is defined is up to the application developer. Since an application can have any number of areas, logging channels are identified by a unique string. Application areas can be nested (e.g. an area of “input processing” might include sub-areas “read CSV files”, “read XLS files” and “read Gnumeric files”). To cater for this natural nesting, channel names are organized into a namespace hierarchy where levels are separated by periods, much like the Java or Python package namespace. So in the instance given above, channel names might be “input” for the upper level, and “input.csv”, “input.xls” and “input.gnu” for the sub-levels. There is no arbitrary limit to the depth of nesting.

Module contents
Data
absolute_import
pytwitcherapi.absolute_import = _Feature((2, 5, 0, 'alpha', 1), (3, 0, 0, 'alpha', 0), 16384)

Contributing

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

You can contribute in many ways:

Types of Contributions

Report Bugs

Report bugs at https://github.com/Pytwitcher/pytwitcherapi/issues.

If you are reporting a bug, please include:

  • Your operating system name and version.
  • Any details about your local setup that might be helpful in troubleshooting.
  • Detailed steps to reproduce the bug.
Fix Bugs

Look through the GitHub issues for bugs. Anything tagged with “bug” is open to whoever wants to implement it.

Implement Features

Look through the GitHub issues for features. Anything tagged with “feature” is open to whoever wants to implement it.

Write Documentation

pytwitcherapi could always use more documentation, whether as part of the official pytwitcherapi docs, in docstrings, or even on the web in blog posts, articles, and such.

Submit Feedback

The best way to send feedback is to file an issue at https://github.com/Pytwitcher/pytwitcherapi/issues.

If you are proposing a feature:

  • Explain in detail how it would work.
  • Keep the scope as narrow as possible, to make it easier to implement.
  • Remember that this is a volunteer-driven project, and that contributions are welcome :)

Get Started!

Ready to contribute? Here’s how to set up pytwitcherapi for local development.

  1. Fork the pytwitcherapi repo on GitHub.

  2. Clone your fork locally:

    $ git clone git@github.com:your_name_here/pytwitcherapi.git
    
  3. Create a branch for local development:

    $ git checkout -b name-of-your-bugfix-or-feature
    

Now you can make your changes locally.

  1. When you’re done making changes, check that your changes pass style and unit tests, including testing other Python versions with tox:

    $ tox
    

To get tox, just pip install it.

  1. Commit your changes and push your branch to GitHub:

    $ git add .
    $ git commit -m "Your detailed description of your changes."
    $ git push origin name-of-your-bugfix-or-feature
    
  2. Submit a pull request through the GitHub website.

Pull Request Guidelines

Before you submit a pull request, check that it meets these guidelines:

  1. The pull request should include tests.
  2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.
  3. The pull request should work for Python 2.6, 2.7, and 3.3, and for PyPy. Check https://travis-ci.org/Pytwitcher/pytwitcherapi under pull requests for active pull requests or run the tox command and make sure that the tests pass for all supported Python versions.

Tips

To run a subset of tests:

$ py.test test/test_pytwitcherapi.py

Credits

Development Lead

History

0.1.1 (2015-03-15)

  • First release on PyPI.
  • Pulled pytwitcherapi out of main project pytwitcher

0.1.2 (2015-03-15)

  • Fix wrapping search stream results due to incomplete channel json

0.1.3 (2015-03-23)

  • Refactor twitch module into models and session module

0.1.4 (2015-03-23)

  • Fix wrap json using actual class instead of cls

0.2.0 (2015-04-12)

  • Authentication: User can login and TwitchSession can retrieve followed streams.

0.3.0 (2015-05-08)

  • Easier imports. Only import the package for most of the cases.
  • Added logging. Configure your logger and pytwitcher will show debug messages.

0.3.1 (2015-05-09)

  • Fix login server shutdown by correctly closing the socket

0.4.0 (2015-05-12)

  • IRC client for twitch chat

0.5.0 (2015-05-13)

  • IRC v3 Tags for messages

0.5.1 (2015-05-13)

  • Fix coverage reports via travis

0.6.0 (2015-05-16)

  • Add limit for sending messages

0.7.0 (2015-05-16)

  • IRCCLient manages two connections. Receives own messages from the server (with tags).
  • Improved test thread saftey

0.7.1 (2015-05-22)

  • IRCClient shutdown is now thread-safe through events

0.7.2 (2015-05-30)

  • Add TwitchSession.get_emote_picture(emote, size).
  • Capabilities for chat: twitch.tv/membership, twitch.tv/commands, twitch.tv/tags

0.8.0 (2015-05-31)

  • Replace context managers for apis with dedicated methods. The context managers made it difficult to use a session thread-safe because they relied (more heavily) on the state of the session.

0.9.0 (2016-09-16)

  • Remove on_schedule argument for irc client. irc >=15.0 required.
  • #17: Always submit a client id in the headers. Credits to Coriou.
  • Client ID can be provided via environment variable PYTWITCHER_CLIENT_ID.

0.9.1 (2016-09-18)

  • Make example chat client python 3 compatible
  • #16: Ignore unknown arguments from twitchstatus
  • Use Client ID for old api requests as well

0.9.2 (2017-08-27)

  • Fix compatibility to irc>=16.0. Thanks to crey4fun.

0.9.3 (2017-08-27)

  • Re-release of 0.9.2

Feedback

If you have any suggestions or questions about pytwitcherapi feel free to email me at zuber.david@gmx.de.

If you encounter any errors or problems with pytwitcherapi, please let me know! Open an Issue at the GitHub https://github.com/Pytwitcher/pytwitcherapi main repository.

Indices and tables