Beginner Twitch Chatbot Using Python

Every once in a while, I enjoy playing a few video games. One of the cool things I’ve seen are chatbots such as Nightbot. I thought to myself, I wonder how hard this would be to create. After creating one, I thought I would make a blog post about a beginner Twitch chatbot using python From what I’ve seen, Twitch’s chat is just an IRC channel. But then I stumbled upon a tutorial showing how to connect to Twitch’s chat using the twitchio module for python. Now python is not my natural language for programming, but it’s fairly easy enough to use. I am going to link the original, as this tutorial I am doing is more of an extension of the one I originally saw here: https://dev.to/ninjabunny9000/let-s-make-a-twitch-bot-with-python-2nd8 .

I am going to expand upon it slightly to include a random number generator that users in my chat can use by typing “!roll” in my chat channel. I also changed the code to not use pipenv, and instead uses a config.py script to store variables. I found this important for a big reason. When defining the initial channel to connect to, I can use a list of multiple channels that the bot will join. If you’d like to see this same tutorial using NodeJS, it can be found in my post about it here: https://www.richwerks.com/index.php/2019/nodejs-twitch-chatbot/

Preparing our environment for our project

First thing we want to do is prepare an environment. make sure the environment you are using has python 3.6 or later installed. This can be installed on all operating systems. I am choosing to use an Ubuntu 19.0.1 distribution on the Google Cloud Platform. I’m using an f1-micro instance. This is totally sufficient for this chatbot.

The next thing you need to do is set up a chatbot account at twitch and create the credentials for it.

The next thing you want to do is install the twitchio module using pip:

pip install twitchio

Creating our configuration file

Now that we’ve got an OS environment with Python and a twitch account created, we can start creating the code for the chat bot. First we are going to create our config.py script that will hold the configuration information for our chatbot. Having it in a separate file allows you to keep your OAuth code and client ID for your Twitch chatbot app separate from your code, as these need to be kept secret. It is the identity for authenticating your app with Twitch. Keep it private, or someone could create a malicious app with your credentials. Below is the the code in the config.py

#config.py
TMI_TOKEN="oauth:ENTER YOUR OATH TOKEN HERE"
CLIENT_ID="ENTER YOUR CLIENT ID HERE"
BOT_NICK="THIS IS YOUR Twitch ID of your Chatbot"
BOT_PREFIX="!" #The prefix that you want to use to denote a chatbot command.
CHANNEL=["SOMECHANNELHERE"] #This can be a list with multiple channels to connect to.

Creating the main application for our chatbot

Now that we’ve got config.py script created, we want to create our chatbot script. Firstly we will want to import all variables from our config.py, as this is used to define the connection for twitch.io. Next we’ll import commands from the twitchio module. Finally, we’ll import random for our random number generator for our “!roll” commands.

#chatbot.py
from config import *
from twitchio.ext import commands
import random

Next we want to create the bot object. This will connect to a Twitch channel (or channels) and process events from the IRC chat.

bot = commands.Bot(
    irc_token=TMI_TOKEN,
    client_id=CLIENT_ID,
    nick=BOT_NICK,
    prefix=BOT_PREFIX,
    initial_channels=CHANNEL
)

We now need to define functions to handle the ready event and message event for the IRC channel. These function use the @bot.event decorator to pass the function to the twitchio bot.event event. Our function defines what we are going to do with the event.

The event_ready function runs when the bot connects to the IRC channel. What we are doing is printing to the console that the bot is online. We then create a websocket object from the bot.

@bot.event
async def event_ready():
    print(f"{BOT_NICK} is online!")
    ws = bot._ws

Next we need to handle the messages event with our event_message() function. This function receives a context (ctx) argument. This function fires every time we receive a message from the IRC chat. Within this function, we tell the bot to handle commands from the IRC message. We can also do something like print the message to the console. This is what I did. I also included the messages channel, author and message. This was to help see where messages came from if I were to connect to multiple channels.

@bot.event
async def event_message(ctx):
    if ctx.author.name.lower() == BOT_NICK.lower():
        return
    await bot.handle_commands(ctx)
    print(f'{ctx.channel} - {ctx.author.name}: {ctx.content}')

Next is the meat and potatoes of it. We will define a function that handles a chat command. We are going to define a command for “!roll” which will take some text after the roll command that will determine what number we roll or help context for the roll command itself. The function uses an @bot.command decorator with a “name ” argument passed to it. This name is the name of our command, sans prefix. SO our name will be “roll”. Next we will define a function for the roll. It does not have to have the same name as the argument in the decorator, but it will make it easier to keep your functions easy to identify.

Our roll function will take the command, perform a split on spaces and check a few things. One, we’ll check if the length of our say our command, after we split it, if it is greater than one. This let’s us know that the user wanted to pass an argument to it. We then check if the second command in the list is numeric. If it is, this is the number we will use as the upper bound of our random number, else if the second command is not numeric, we check to see if it is the word “help”. If so, we send out what the user can do with our “!roll command”. Otherwise it is not “help”, we will send to the user that the command is invalid. If there is no second command, we know they typed only “!roll” and will default our upper bound to 100.

@bot.command(name='roll')
async def roll(ctx):
    rollNum = ctx.content.split(' ')
    print(rollNum)
    if len(rollNum) > 1 and rollNum[1].isnumeric():
        print(rollNum[1])
        await ctx.channel.send(f"{ctx.author.name} rolled a {random.randrange(0,int(rollNum[1]),1)}!")
    elif len(rollNum) > 1 and rollNum[1].lower() == 'help':
        await ctx.channel.send(f"{ctx.author.name}, you can specify a number to roll between 0 and the number specified. If you do not specify a number, you will roll 100.")
    elif len(rollNum) > 1 and rollNum[1].lower() != 'help':
        await ctx.channel.send(f"{ctx.author.name}, you did not select a number to roll")
    else:
        await ctx.channel.send(f"{ctx.author.name} rolled a {random.randrange(0,100,1)}!")

That’s it. We now have a command our chatbot will listen for. The last step is to tell our script to start the bot.

if __name__ == "__main__":
    bot.run()

Putting it all together

Below will be the entire contents of the config.py and chatbot.py scripts.

#config.py
TMI_TOKEN=""
CLIENT_ID=""
BOT_NICK="OmegaDroid"
BOT_PREFIX="!"
CHANNEL=["xerousplays"]
#chatbot.py
from twitchio.ext import commands
import random
from config import *

bot = commands.Bot(
    irc_token=TMI_TOKEN,
    client_id=CLIENT_ID,
    nick=BOT_NICK,
    prefix=BOT_PREFIX,
    initial_channels=CHANNEL
    #initial_channels=["drjayfisto","Asmongold"]
)

@bot.event
async def event_ready():
    print(f"{BOT_NICK} is online!")
    ws = bot._ws


@bot.event
async def event_message(ctx):
    if ctx.author.name.lower() == BOT_NICK.lower():
        return
    await bot.handle_commands(ctx)
    print(f'{ctx.channel} - {ctx.author.name}: {ctx.content}')
    
@bot.command(name='roll')
async def roll(ctx):
    rollNum = ctx.content.split(' ')
    print(rollNum)
    if len(rollNum) > 1 and rollNum[1].isnumeric():
        print(rollNum[1])
        await ctx.channel.send(f"{ctx.author.name} rolled a {random.randrange(0,int(rollNum[1]),1)}!")
    elif len(rollNum) > 1 and rollNum[1].lower() == 'help':
        await ctx.channel.send(f"{ctx.author.name}, you can specify a number to roll between 0 and the number specified. If you do not specify a number, you will roll 100.")
    elif len(rollNum) > 1 and rollNum[1].lower() != 'help':
        await ctx.channel.send(f"{ctx.author.name}, you did not select a number to roll")
    else:
        await ctx.channel.send(f"{ctx.author.name} rolled a {random.randrange(0,100,1)}!")

if __name__ == "__main__":
    bot.run()
console output of twitch chatbot in Ubuntu
twitch chat showing chatbot in action

7 thoughts on “Beginner Twitch Chatbot Using Python”

  1. Hi there! I’m at work surfing around your blog from
    my new iphone 4! Just wantfed to say I love reading through your
    blog and look forward to all your posts! Keep up the great work!

  2. Its like you read my mind! You appear to know so much about this,
    like you wrote the book in it or something. I think that you can do
    with some pics to drive the message home a little bit, but instead of that, this is great
    blog. A great read. I’ll definitely be back.

  3. My brother recommended I might llike this web site.
    He was entirely right. This post actually made my day. You can not
    imagine just hoow much time I hadd spent for this info!
    Thanks!

  4. Exceklent post. I was checking constantly thos blog and I am impressed!
    Extremely useful info specifically the lqst part :
    ) I care for uch information a lot. I was seeking this certain info
    for a very long time. Thank you and best of luck.

  5. Neat blog! Is your theme custom made or did you download it from somewhere?
    A theme like yours with a few simple adjustements would
    really make my blog shine. Please let me know where you got your
    theme. Appreciate it

  6. Thanks for the post (not a bot here like the other comments) When I try to run the bot I receive a KeyError: (mychannelname) Task exception was never retrieved:

    I feel I created TMI and APP auth as instructed but I cannot get it to run without error. Thanks!

  7. Hello there! Do you know if they make any plugins to assist with Search Engine Optimization? I’m trying to get my blog
    to rank for some targeted keywords but I’m not seeing very good success.

    If you know of any please share. Appreciate it!

Leave a Reply

Your email address will not be published. Required fields are marked *