Introduction: Twitch.tv Moderator Bot

In this tutorial, you'll implement a bot that moderates the chat for a channel on Twitch.tv. The bot will be able to connect to the Twitch IRC server, read the channel chat, search for unwanted messages and spam, and timeout or ban the user who sent the message.

Required Resources

  • A basic understanding of Python. I use Python 3 in this tutorial, but Python 2 is similar enough that the tutorial can be easily translated.
  • A Python installation. You'll need this to run the bot.
  • A separate Twitch.tv account. You should not use your main account, as incorrectly implementing the bot can cause you to be (temporarily) banned.

Step 1: Create a Configuration Module

Values that will differ between executions of the bot should be easy to view and modify. For this purpose, we'll create a configuration module that will store these values. Get your Twitch.tv OAuth token from twitchapps.com/tmi, and then create a new file in your source directory called 'cfg.py' and add these values:

# cfg.py
HOST = "irc.twitch.tv" # the Twitch IRC server
PORT = 6667 # always use port 6667!
NICK = "twitch_username" # your Twitch username, lowercase
PASS = "oauth:xxxxxxxxxxxxxxxxxxxx" # your Twitch OAuth token
CHAN = "#channel" # the channel you want to join

Step 2: Write the Network Functions

Twitch uses IRC as its chat protocol, which makes most communication trivial. Still, the code will end up much cleaner if we define some utility functions first.

We use sockets as the network abstraction for this bot. Sockets provide a very clear send-and-receive interface for network communications, so these functions won't be very complicated.

I've provided docstrings in compliance with PEP 257 which will provide brief explanation of the functions.

# bot.py
def chat(sock, msg):
"""
Send a chat message to the server.
Keyword arguments:
sock -- the socket over which to send the message
msg -- the message to be sent
"""
sock.send("PRIVMSG #{} :{}".format(cfg.CHAN, msg))

def ban(sock, user):
"""
Ban a user from the current channel.
Keyword arguments:
sock -- the socket over which to send the ban command
user -- the user to be banned
"""
chat(sock, ".ban {}".format(user))

def timeout(sock, user, secs=600):
"""
Time out a user for a set period of time.
Keyword arguments:
sock -- the socket over which to send the timeout command
user -- the user to be timed out
secs -- the length of the timeout in seconds (default 600)
"""
chat(sock, ".timeout {}".format(user, secs))

Step 3: Connect to the Twitch IRC Server

You'll need to get your Twitch OAuth token here in order to log in. You should send your OAuth token, your username, and the channel you wish to join (in that order) as shown below.

# bot.py

import cfg
import socket

# network functions go here

s = socket.socket()
s.connect((HOST, PORT))
s.send("PASS {}\r\n".format(PASS).encode("utf-8"))
s.send("NICK {}\r\n".format(NICK).encode("utf-8"))
s.send("JOIN {}\r\n".format(CHAN).encode("utf-8"))

Joining a channel will give us a huge amount of text and also connect us to the channel chat, so we need to handle responses from the server continually. We'll do this with an infinite loop; you can interrupt the program with CTRL-C.

while True:
response = s.recv(1024).decode("utf-8") print(response) sleep(0.1)

Sleeping for a tenth of a second is barely noticeable to humans, but it drastically decreases the CPU time of the program without decreasing performance.

Step 4: Make Sure the Server Knows You're Connected

IRC servers will "ping" your bot at regular intervals to make sure you're still connected: [PING :tmi.twitch.tv]. The bot should respond as soon as possible with [PONG :tmi.twitch.tv].

# bot.py
while True:
response = s.recv(1024).decode("utf-8") if response == "PING :tmi.twitch.tv\r\n": s.send("PONG :tmi.twitch.tv\r\n".encode("utf-8")) else: print(response)

Step 5: Limit Your Messages to the Server

In order to prevent users from spamming commands, Twitch limits how quickly a user can send IRC messages to the server. For regular users, this limit is set at 20 messages per 30 seconds; moderators may send up to 100 messages per 30 seconds. Disobeying this limit will earn you an 8-hour ban from the IRC server.

The simple way to handle this limit is to define the maximum send rate in your configuration module:

# cfg.py
RATE = (20/30) # messages per second

and then change the sleep time to the inverse of that rate:

# bot.py
while True: response = s.recv(1024).decode("utf-8") if response == "PING :tmi.twitch.tv\r\n": s.send("PONG :tmi.twitch.tv\r\n".encode("utf-8")) else: print(response) sleep(1 / cfg.RATE)

Step 6: Recognize Messages to the Chat

Not all messages from the server are worthy of the bot's attention. We don't need to know who's joining or leaving the channel, and it's not very important who the other moderators are. We do, however, want to see chat messages, which look like this:

:nickname!nickname@nickname.tmi.twitch.tv PRIVMSG #channel :message

IRC's formatting of a chat message is rather complicated, so we'll use a regular expression to pick out messages that match this pattern. Import Python's regular expressions module:

# bot.py
import re

and define the pattern we're looking for.

# Make sure you prefix the quotes with an 'r'!
CHAT_MSG=re.compile(r"^:\w+!\w+@\w+\.tmi\.twitch\.tv PRIVMSG #\w+ :")

Once we've determined that this is a chat message, we can strip out most of the text, since we only need one copy of the username and their message. First, we need to pick the username out of the string. The username will be the first substring made up solely of regex word characters, which consist of alphanumeric characters and underscores. Once we find the username, we can replace the ugly prefix with just the username and a colon.

# bot.py
while True: response = s.recv(1024).decode("utf-8") if response == "PING :tmi.twitch.tv\r\n": s.send("PONG :tmi.twitch.tv\r\n".encode("utf-8")) else: username = re.search(r"\w+", line).group(0) # return the entire match message = CHAT_MSG.sub("", line) print(username + ": " + message) sleep(1 / cfg.RATE)

Step 7: Register Patterns and Drop the Banhammer

Once again, we'll use regex to match messages that contain unwanted patterns. The most obvious of these patterns are individual words or phrases, which can simply be typed as they are into the regex. For more complicated or variable patterns, you'll need to look into more advanced regex; this can be used to ban links, phone numbers, addresses (physical and email), and other information that follows a particular pattern.

Add a list of patterns to your config file:

# cfg.py
PATT = [
r"swear",
# ...
r"some_pattern"
]

Now, in your source file, add a loop that checks each message for the pattern you (don't) want:

# bot.py
while True: response = s.recv(1024).decode("utf-8") if response == "PING :tmi.twitch.tv\r\n": s.send("PONG :tmi.twitch.tv\r\n".encode("utf-8")) else: username = re.search(r"\w+", line).group(0) # return the entire match message = CHAT_MSG.sub("", line) print(username + ": " + message)
for pattern in cfg.PATT:
if re.match(pattern, message):
ban(s, username)
break
sleep(1 / cfg.RATE)

Step 8: Congratulations!

You've successfully implemented a moderator bot for Twitch.tv. Of course, this tutorial only walks you through a fairly minimal implementation; in order to have functionality like that of Nightbot or Moobot, you'll have to do a bit more work. However, this bot provides a solid foundation for expansion and customization. Good luck!