DEV Community

Cover image for Let's make a Twitch bot with Python!
πŸ’Ύ bun9000
πŸ’Ύ bun9000

Posted on • Updated on

Let's make a Twitch bot with Python!

This tutorial gets you up and running with a simple chat bot for Twitch channel.

Who's this tutorial for?

Beginners to coding and experienced coders new to Python.

Contents

We'll start out by setting up the accounts, getting the secrets, and installing the softywares. Then we'll setup and code the bot. By the end of this tutorial you should be ready to start adding your own custom commands.

BUT FIRST... we need to make sure we have our credentials in order. πŸ‘

Papers, please!

πŸ“’ Glory to Arstotzka!

  1. Make an account on Twitch (for your bot) or use the one you stream with. Make it something cool like RealStreamer69 😎
  2. Request an oauth code. You'll need to login and give the app permissions to generate it for you.
  3. Register your app with Twitch dev and request a client-id (so you can interface with Twitch's API)

Keep the oauth and client id somewhere handy but not public. You'll need them later to configure the bot.

πŸ’‘ PROTIPβ„’ -- Keep it secret. Keep it safe.

Install all the things! 🧹

  1. Install Python 3.6 or 3.7 -- Windows // Linux // OS X
  2. Install PIPENV. In the console, run β‡’ pip install pipenv

Create a cozy home for the bot to live in

Virtual environments require a couple extra steps to set up but make developing Python apps a breeze. For this tutorial, we'll use PIPENV which marries pip and venv into a single package.

  • In the console, navigate to your working directory
  • Run β‡’ pipenv --python 3.6 or pipenv --python 3.7
    • This is going to create pipfile and piplock. They hold venv info like Python version and libraries you install.
  • Then run β‡’ pipenv install twitchio

Configuring and authorizing the bot

Create 2 files in your working directory. One called bot.py and another called .env (no file name, just the extension - it's weird, I know).

/.env

Your secrets will live inside the .env file. Add the oauth token and client-id from above after the = in the file. Fill in the other vars as well.

# .env
TMI_TOKEN=oauth:
CLIENT_ID=
BOT_NICK=
BOT_PREFIX=!
CHANNEL=
Enter fullscreen mode Exit fullscreen mode

/bot.py

Inside bot.py, import the libraries we'll need and create the bot obj that we'll start in the next step.

# bot.py
import os # for importing env vars for the bot to use
from twitchio.ext import commands

bot = commands.Bot(
    # set up the bot
    irc_token=os.environ['TMI_TOKEN'],
    client_id=os.environ['CLIENT_ID'],
    nick=os.environ['BOT_NICK'],
    prefix=os.environ['BOT_PREFIX'],
    initial_channels=[os.environ['CHANNEL']]
)
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ PROTIPβ„’ -- When we run bot.py using PIPENV, it first loads the variables from the .env file into the virtual environment and then runs bot.py. So, inside this venv, we have acess (on an instance-basis) to these variables. We're going to use python's os module to import them, just like we would import environment variables on our native environment.

At the bottom of the file, we need to make sure the bot runs when we call bot.py directly using if __name__ == "__main__":

# bot.py
if __name__ == "__main__":
    bot.run()
Enter fullscreen mode Exit fullscreen mode

WAKE BOT UP (wake bot up inside!)

Let's test the bot and make sure it can connect to Twitch.

  • In the console, run β‡’ pipenv run python bot.py

If it worked, you shouldn't get any errors - that means the environment variables loaded correctly and your bot successfully connected to Twitch!

If you got errors, check out the next section before moving on.

Error: Can't wake up. [save me]

A wild Request to join the channel has timed out. Make sure the channel exists. appears. You evade with..

  1. Make sure you have the right tokens (oauth and client-id) in the .env file and that they're in the same directory/folder as bot.py

  2. Your directory structure at this point should look like this...

    working-directory/
    β”œβ”€ .env
    β”œβ”€ bot.py
    β”œβ”€ Pipfile
    └─ Pipfile.lock
    
  3. If that still doesn't fix it, comment below and we'll sort it out for ya!

Adding some functionality to the bot

Greet the chat room!

Back to bot.py.... Below the bot object, let's create a function called event_ready with the decorator @bot.event. This function will run once when we start the bot. It then reports to terminal and chat that it successfully connected to Twitch.

# bot.py, below bot object
@bot.event
async def event_ready():
    'Called once when the bot goes online.'
    print(f"{os.environ['BOT_NICK']} is online!")
    ws = bot._ws  # this is only needed to send messages within event_ready
    await ws.send_privmsg(os.environ['CHANNEL'], f"/me has landed!")
Enter fullscreen mode Exit fullscreen mode

Go ahead and test the bot again. It should greet chat when it comes online now.

landed

Respond to messages in chat

Next up, we're going to add a function that's run every time a message is sent in your channel. You can add all sorts of logic here later, but we'll start out with making sure the bot ignores itself.

# bot.py, below event_ready
@bot.event
async def event_message(ctx):
    'Runs every time a message is sent in chat.'

    # make sure the bot ignores itself and the streamer
    if ctx.author.name.lower() == os.environ['BOT_NICK'].lower():
        return
Enter fullscreen mode Exit fullscreen mode

After that, we'll drop in a line of code that will annoyingly echo back every message sent in chat. ᴷᡃᡖᡖᡃ

# bot.py, in event_message, below the bot-ignoring stuff
await ctx.channel.send(ctx.content)
Enter fullscreen mode Exit fullscreen mode

Restart the bot and check it out!

rip

πŸ’‘ PROTIPβ„’ -- Comment out that line now cuz it's actually really annoying.

Making a chat command

Any command you make needs to follow this format when defining them..

  • Decorated with @bot.command(name='whatever')
  • Be asynchronous functions with names that match the name variable in the decorator
  • Pass the message context in through the function

How the function works and what it does is all up to you. For this example, we'll create a command called !test that says test passed! in chat when we call it.

# bot.py, below event_message function
@bot.command(name='test')
async def test(ctx):
    await ctx.send('test passed!')
Enter fullscreen mode Exit fullscreen mode

Before this can work, we need to make sure that the bot knows to listen for commands coming through.

Add this just below the ignore bot code in event_message:

    #bot.py, in event_message, below the bot ignore stuffs
    await bot.handle_commands(ctx)
Enter fullscreen mode Exit fullscreen mode

Alright! Time to test it out. Reboot the bot and send !test in chat!

test passed

Responding to specific messages

Tell my bot I said... "Hello."

You can respond to specific messages in your chat too, they don't have to be !commands. Let's write some code that says hi when people say hello.

    # bot.py, at the bottom of event_message
    if 'hello' in ctx.content.lower():
        await ctx.channel.send(f"Hi, @{ctx.author.name}!")
Enter fullscreen mode Exit fullscreen mode

Go ahead and test it out! You've got the framework to start buildng your bot and adding commands.

tell my bot i said "hello."

Here's what you should have when you're done

/bot.py

import os
from twitchio.ext import commands

# set up the bot
bot = commands.Bot(
    irc_token=os.environ['TMI_TOKEN'],
    client_id=os.environ['CLIENT_ID'],
    nick=os.environ['BOT_NICK'],
    prefix=os.environ['BOT_PREFIX'],
    initial_channels=[os.environ['CHANNEL']]
)

@bot.event
async def event_ready():
    'Called once when the bot goes online.'
    print(f"{os.environ['BOT_NICK']} is online!")
    ws = bot._ws  # this is only needed to send messages within event_ready
    await ws.send_privmsg(os.environ['CHANNEL'], f"/me has landed!")


@bot.event
async def event_message(ctx):
    'Runs every time a message is sent in chat.'

    # make sure the bot ignores itself and the streamer
    if ctx.author.name.lower() == os.environ['BOT_NICK'].lower():
        return

    await bot.handle_commands(ctx)

    # await ctx.channel.send(ctx.content)

    if 'hello' in ctx.content.lower():
        await ctx.channel.send(f"Hi, @{ctx.author.name}!")


@bot.command(name='test')
async def test(ctx):
    await ctx.send('test passed!')


if __name__ == "__main__":
    bot.run()
Enter fullscreen mode Exit fullscreen mode

And ofc your .env with your secrets and a pipfile and Piplock.

I've uploaded the files to a github repo too, if that's your thing.

Congrats!! πŸ₯³πŸŽ‰

You've made it this far.. You know what that means? Time to celebrate by clicking this GitKraken referral link and signing up so I can get free socks (or maybe a Tesla? 😎).

Also, feel free to check out the Live Coding Stream recap where we developed this tutorial. Shoutouts to everyone in chat that collaborated!

What do you want to do next?

Questions? Comments? Ideas? Let me know in the comments below!

I'll be following up to this post soon with some tips on how to get the most out of the TwitchIO library -- more stuff that's not really well-documented and was a PITA to figure out, like how to get the author, using badges for permissions, etc.

Latest comments (57)

Collapse
 
realfollowersau profile image
harry

The videos a user has viewed on TikTok are those they've marked as "loved." These favored videos are stored in a dedicated "likes" section on the user's profile, which can be set as either public or private. buy tiktok likes real

Collapse
 
ericeberhart profile image
Eric_Eberhart

Creating a Twitch Bot with Python

To create a Twitch bot using Python, we'll utilize the twitchio library, which provides easy-to-use functionality for interacting with the Twitch API. Follow these steps to get started:

Install Required Packages:
First, install the twitchio library using pip:

Copy code
pip install twitchio
Create a Twitch Account:
If you haven't already, create a Twitch account that you'll use for your bot.

Generate OAuth Token:
Generate an OAuth token for your bot by visiting the Twitch Chat OAuth Password Generator (twitchapps.com/tmi/). Log in with your bot's Twitch account and copy the OAuth token generated.

Write the Bot Code:
Now, let's write the Python code for our Twitch bot:

python
Copy code
import twitchio
import os

Twitch Bot Configuration

BOT_NICK = 'your_bot_username'
CHANNEL = 'channel_to_join'
TOKEN = 'your_oauth_token'

Bot Class

class Bot(twitchio.Client):
async def event_ready(self):
print(f'Bot is ready! Joining channel {CHANNEL}')
await self.join_channels(CHANNEL)

async def event_message(self, message):
print(f'Received message in {message.channel.name}: {message.content}')
Enter fullscreen mode Exit fullscreen mode




Run the Bot

bot = Bot(
irc_token=TOKEN,
nick=BOT_NICK,
prefix='!',
initial_channels=[CHANNEL]
)
bot.run()
Replace 'your_bot_username', 'channel_to_join', and 'your_oauth_token' with your bot's Twitch username, the channel you want the bot to join, and the OAuth token you generated, respectively.

Run the Bot:
Save the Python code to a file (e.g., twitch_bot.py) and run it using Python:

Copy code
python twitch_bot.py
Test Your Bot:
Your Twitch bot should now be running and connected to the specified channel. Test it out by sending messages in the Twitch chat and observing the bot's responses in the console.

Extend Functionality:
You can extend your bot's functionality by adding event handlers for specific Twitch events, such as new messages, subscriptions, or channel raids. Refer to the twitchio documentation for more information on available events and methods.

By following these steps, you can create a simple Twitch bot using Python and start experimenting with custom commands and interactions in your Twitch channel.

Collapse
 
beardfacefacebeard profile image
beardfacefacebeard • Edited

when i run the bot.py my terminal prompt replies:
Loading .env environment variables...

and thats it. never says anything else. it DOES join the channel, it DOES respond to the !test command (and i made a couple dopey simple commands to test out as well). it feels like if i try to run !test back to back, it wont issue the command the second time but if i use a second command then !test again, it will show up. same thing with another command. the bot never says it has landed in the chatroom, it doesnt display any chat text within the terminal window. the article is slightly older at this point so my python version is 3.10 but im not sure how much that would have an effect.. i even added to the imports asyncio just in case to no avail.

any help would be marvelous.. if there is anything anyone would like to see that may be helpful let me know but im not seeing any errors or anything else to really have something to show other than the inital 'loading .env etc..'

thanks everyone

Collapse
 
newbiesaurus profile image
Newbiesaurus

So I'm able to connect to twitch and see the bot in chat but when it comes to the @bot.event nothing happens. It doesn't print in the terminal or in the twitch chat.

Collapse
 
wilverengame profile image
Wilveren

I'm at the "Greet the chat room!" step in the tutorial, and I'm not getting anything out of it. The terminal says "loading .env environment variables..." and that's all. Nothing is printed in either the terminal, or my chat. My .env file looks like this, with the token and ID masked, of course:

TMI_TOKEN=oauth:***************
CLIENT_ID=**************
BOT_NICK=SapphireHerald
BOT_PREFIX=!
CHANNEL=Wilveren

My bot.py looks almost exactly the same, with the exception that, per the comments below, "irc_token" has been replaced with "token"

I also don't have a redirect URL, just localhost
I was told I have to build a website, but does that not apply with this method?

Collapse
 
mimmz86 profile image
Mimmz86 • Edited

So, I've had to make a few minor edits to my code to make it work and it feels appropriately held together with duct tape and bubblegum, but I'm recieving an "AttributeError: 'funtion' object has no attribute 'run'" when I go to run my code. I take it as a good sign its running into an error so far into the code, but I'm unsure how to proceed. Currently my run code reads

def bot():
@bot.event
async def event_ready():
print(f"{os.environ['BOT_NICK']} is online!")
ws = bot._ws
await ws.send_privmsg(os.environ['CHANNEL'], f'/me has landed!')

if __ name __ == "__ main __":
bot.run()

(Spaces where needed for clarity)

If anyone can help, I'd be sincerely appreciative.

Collapse
 
jarelef profile image
Jarelef • Edited

Greetings
Cool bot.
The question is how to receive commands in private messages?
example "/w @botname !command"
There is an idea to create a mafia game for twitch chat, but there some players have to write commands to the bot in private messages so that their roles are not easily revealed

Collapse
 
jarelef profile image
Jarelef

Nobody knows?

Collapse
 
restingbeardface profile image
RestingBeardFace

Hey there, I just found this now and am trying to get it to work. I am getting an error: TypeError: Bot.init() missing 1 required positional argument: 'token'

I have seen a couple of the posts / people experiencing it and have tried some of the comments associated on the chain but been unable to fix, please help!

Collapse
 
doctorpizzamd profile image
DoctorPizzaMD • Edited

Inside your bot = commands.Bot() function the "tmi_token" variable needs to be changed to "token". My guess is that twitchio probably changed something since this post was made, seeing as it's a couple years old at this point. But this is just a guess.

The commands.Bot() function call is expecting the "token" argument to be present, and since it's not, due to the instructions saying "tmi_token" the call is failing.

Edit: I'm trying to upload an image, but it's not letting me.

It should look like this: imgur.com/oaAMC1t

Collapse
 
nisha550png profile image
nisha550-png

Traceback (most recent call last):
File "bot.py", line 11, in
initial_channels=[os.environ['CHANNEL']]
TypeError: init() missing 1 required positional argument: 'token'

Facing pipenv run python bot.py

Collapse
 
xtimemaster profile image
Carlos Castro

It was working properly an then got this error
Traceback (most recent call last):
File "bot.py", line 11, in
initial_channels=[os.environ['CHANNEL']]
TypeError: init() missing 1 required positional argument: 'token'

Collapse
 
runneypo profile image
runneypo

I'm having trouble registering an app on the dev.twitch.tv/console/apps/create, using localhost gives me Cartman.GetAuthorizationToken: 401: {"code":401,"status":"invalid oauth token"}, any idea what I'm supposed to do to get it to work?

Collapse
 
sive profile image
Sive

Got this working today, wow. This is beautiful. Thank you!

Collapse
 
seanlodhammar profile image
Sean Lodhammar

Just wanted to ask, what do you fill in for the 'CHANNEL' variable in the .env file?

Collapse
 
airplanegobrr profile image
AirplaneGoBrr

How do you add more then 1 channel?

Collapse
 
jacouby profile image
EkoDoesntDev

Keep getting this error, have looked at the comments and everything people say seems to be correct. Please help cause this is confusing me very much.

Task exception was never retrieved
future: exception=TimeoutError('Request to join the "josh_liv3" channel has timed out. Make sure the channel exists.')>
Traceback (most recent call last):
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/twitchio/websocket.py", line 280, in _join_channel
await asyncio.wait_for(fut, timeout=10)
File "/usr/lib/python3.8/asyncio/tasks.py", line 501, in wait_for
raise exceptions.TimeoutError()
asyncio.exceptions.TimeoutError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/twitchio/websocket.py", line 228, in auth_seq
await self.join_channels(channels)
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/twitchio/websocket.py", line 271, in join_channels
await asyncio.gather(
[self._join_channel(x) for x in channels])
File "/opt/virtualenvs/python3/lib/python3.8/site-packages/twitchio/websocket.py", line 284, in _join_channel
raise asyncio.TimeoutError(
asyncio.exceptions.TimeoutError: Request to join the "josh_liv3" channel has timed out. Make sure the channel exists.